Funpack3-3 针对X-NUCLEO-IKS4A1的传感器数据采集系统设计
该项目使用了IKS4A1传感器板、STM32开发板,实现了传感器数据采集系统的设计,它的主要功能为:采集并在上位机显示IKS4A1传感器板上的全部数据。。
标签
嵌入式系统
Funpack活动
开发板
obrulviser
更新2024-07-08
68

Funpack3-3

——任务一:针对X-NUCLEO-IKS4A1板卡的可视化数据采集系统

一、项目介绍

本项目依托于Funpack3-3活动,采用NUCLEO-STM32L476作为主控板卡,读取X-NUCLEO-IKS4A1上各个传感器的数值,并根据QVAR的数据完成单、双击及滑动识别。最后,设计了一款基于Matlab的可视化上位机,使其能实时显示各个传感器的数据。


项目内容:

1、采集IKS4A1板卡上各传感器数据。

2、根据QVAR数据识别单、双击及滑动。

3、发送数据至上位机并完成可视化。


二、系统架构

系统整体分为受控端要求和上位机两部分。由于本设计仅触摸部分需要数据处理,对运算性能要求不高,因此将数据处理任务全部分配给受控端完成。上位机仅负责数据的可视化部分。系统框图如下。


演示文稿1.png

三、受控端设计

受控端设计硬件采用板卡默认即可,软件主要以STM32的程序设计为中心,可以分成三个部分,即,传感器驱动、数据处理以及与上位机通信。此外,主控的调度方式也是个需要注意的问题。

3.1 传感器驱动

官方提供了大量针对与IKS4A1板卡的Demo,虽然没有包含全部的STM32芯片,但是得益于STM32CubeIDE强大的图形化配置功能,我们可以直接生成适用于各个STM32芯片的示例程序。如果正确生成了示例程序,可以在MEMS-Studio观察到相应的现象。生成示例程序的教程请参见如下网址。

基于CubeIDE/MX的IKS4A1配置示例教程

获取到示例程序后,第一步显然是查看示例的硬件配置,这部分在基于CubeIDE/MX的IKS4A1配置示例教程提到了,此处不再赘述。之后就要查看示例程序的main函数,查看有那些函数是专门为了IKS4A1服务的,都有什么作用。以下是某个示例程序的main函数部分。

int main(void)
{
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();

/* Configure the system clock */
SystemClock_Config();

/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_DMA_Init();
MX_RTC_Init();
MX_TIM3_Init();
MX_CRC_Init();
MX_MEMS_Init();

while (1)
{
MX_MEMS_Process();
}

}

显然能注意到 MX_MEMS_Init(); 以及 MX_MEMS_Process(); 是为了 IKS4A1 服务的,并能猜到 MX_MEMS_Init(); 用于 IKS4A1 的初始化, MX_MEMS_Process(); 用于传感器数值的更新。分别阅读两个函数,提取其中的调用过程,就能得到正确的传感器驱动程序。以下是一个根据官方示例,改编部分官方函数,驱动IKS4A1板载的全部传感器的示例。

IKS4A1读取传感器数值教程

以下是MX_MEMS_Init(); 的内容,其中封装了一层调用MX_DataLogFusion_Init(),此处直接显示调用的函数。

static void MX_DataLogFusion_Init(void)
{
float ans_float;

/* Initialize button */
BSP_PB_Init(BUTTON_KEY, BUTTON_MODE_EXTI);

/* Check what is the Push Button State when the button is not pressed. It can change across families */
PushButtonState = (BSP_PB_GetState(BUTTON_KEY)) ? 0 : 1;

/* Initialize LED */
BSP_LED_Init(LED2);

/* Initialize Virtual COM Port */
BSP_COM_Init(COM1);

/* Initialize Timer */
BSP_IP_TIM_Init();

/* Configure Timer to run with desired algorithm frequency */
TIM_Config(ALGO_FREQ);

/* Initialize (disabled) sensors */
Init_Sensors();

/* Sensor Fusion API initialization function */
MotionFX_manager_init();

/* OPTIONAL */
/* Get library version */
MotionFX_manager_get_version(LibVersion, &LibVersionLen);

/* Enable magnetometer calibration */
MotionFX_manager_MagCal_start(ALGO_PERIOD);

/* Test if calibration data are available */
MFX_MagCal_output_t mag_cal_test;
MotionFX_MagCal_getParams(&mag_cal_test);

/* If calibration data are available load HI coefficients */
if (mag_cal_test.cal_quality == MFX_MAGCALGOOD)
{
ans_float = (mag_cal_test.hi_bias[0] * FROM_UT50_TO_MGAUSS);
MagOffset.x = (int32_t)ans_float;
ans_float = (mag_cal_test.hi_bias[1] * FROM_UT50_TO_MGAUSS);
MagOffset.y = (int32_t)ans_float;
ans_float = (mag_cal_test.hi_bias[2] * FROM_UT50_TO_MGAUSS);
MagOffset.z = (int32_t)ans_float;
MagCalStatus = 1;
}

DWT_Init();
BSP_LED_On(LED2);
HAL_Delay(500);
BSP_LED_Off(LED2);

/* Start receiving messages via DMA */
UART_StartReceiveMsg();

}

注意到函数Init_Sensors(); 猜测用于传感器初始化,下面是该函数定义。

static void Init_Sensors(void)
{
BSP_SENSOR_ACC_Init();
BSP_SENSOR_GYR_Init();
BSP_SENSOR_MAG_Init();
BSP_SENSOR_PRESS_Init();
BSP_SENSOR_TEMP_Init();
BSP_SENSOR_HUM_Init();
BSP_SENSOR_ACC_SetOutputDataRate(ACC_ODR);
BSP_SENSOR_ACC_SetFullScale(ACC_FS);
}

该函数调用的任意一个函数都是无法直接跳转的,需要 Crtl+H,在工作空间搜索,才能找到该函数的定义。下面展示其中某一函数的代码。

void BSP_SENSOR_ACC_Init(void)
{
(void)IKS4A1_MOTION_SENSOR_Init(IKS4A1_LSM6DSV16X_0, MOTION_ACCELERO);
}

该函数调用的函数过长,此处不再展示,仅需要知道内部是通用的传感器初始化函数,MotionCompObj是指向初始化后的传感器读值等操作所需的结构体的指针即可。读到这里,我们知道 MX_MEMS_Init(); 以及 Init_Sensors(); 所在的 app_mems.c 和文件名一样是应用层的文件,BSP_SENSOR_ACC_Init(); 等所在的iks4a1_mems_control.c 文件是将底层的函数封装的文件,IKS4A1_MOTION_SENSOR_Init();所在的 iks4a1_motion_sensors.c 则是较为底层的传感器驱动文件,其会直接调用各传感器的.c和.h文件。由于 iks4a1_motion_sensors.c 是不需要选择示例,直接包含在IKS4A1的板级驱动里的,因此我们自制驱动时需要复制、修改 iks4a1_mems_control.c 使其能支持板上的各个传感器,并添加到自己的项目中。至于app_mems.c ,则是为了方便可以直接部分修改为自己的应用。

根据这个思路,我们就得到了前文的IKS4A1读取传感器数值教程


3.2 数据处理

3.2.1 QVAR数据处理与识别

由于QVAR电极部分出现极度不稳定的情况,噪声极大,根据阈值判定的话,容易在阈值附近反复跨越,导致程序错判。采用迟滞、延时组合,来判定当前的触摸状态(左、无、右)。首先利用迟滞方式将输入的传感器数据量化为±1或0,因为利用C语言难以体现连续上升,此处将没有达到阈值的全部点都设定为当前状态的标准值,以便于下次循环的判断。此处为节省计算资源,没有将传感器直出的数据转化为mv为单位的数据,如果单位为mv,将该部分阈值除以78即可。

	int value_deviation=value-value_last;
int act=0;
if (value_deviation>=54600)
value_state_n=1;
else if(value_deviation>=23400)
value_state_n=value_state_l+1;
else if(value_deviation<=-54600)
value_state_n=-1;
else if(value_deviation<=-23400)
value_state_n=value_state_l-1;
else
value_state_n=value_state_l;

value_last= value_state_n * 32760;

然后,利用延时去抖的思想,只有当状态连续变化为同一个数值时,才认为当前状态改变。同时,将过长的状态持续时间量化为两次状态改变。藉此,可以判断点按以及长按。

	if (value_state_n==value_state_l)
{
value_count+=1;
if ((value_count>=40)&&(value_state_n!=0)&&(value_state_n!=value_state_storage[0]))
{
value_count=0;
value_state_valid=1;
value_state_storage[2]=value_state_storage[1];
value_state_storage[1]=value_state_storage[0];
value_state_storage[0]=value_state_n;
}
else if((value_count>=55)&&(value_state_n==0))
{
value_count=0;
value_state_valid=1;
value_state_storage[2]=value_state_storage[1];
value_state_storage[1]=value_state_storage[0];
value_state_storage[0]=value_state_n;
}
}
else
value_count=0;

最后,利用序列检测器思想,在输入的状态序列中检测单击、双击及滑动的信号序列,至此,完成手势识别。

3.2.2 其他数据处理

对于除了QVAR的数据,我们获取它们仅仅为了可视化,其实没有处理的必要。但是为了节约串口通信的带宽,将部分数据精度降低。


四、上位机设计

上位机采用Matlab AppDesigner设计,Matlab语言本身就较为简单,AppDesigner采用的设计方法也比较简单,此处仅简单描述。系统采用定时器更新界面加串口中断更新数据,一定程度上分离了界面更新与数据接受。但是由于Matlab AppDesigner本身羸弱的多线程能力,导致数据更新速度只能限制到每秒5次左右。同时,即使限制到每秒1次甚至更少,也会存在一个至少0.5秒的固定延迟。详细代码请参见附件。


五、效果展示

以下是部分上位机图片展示。

image.png

主界面

image.png

连接成功

image.png

选中数据

image.png

禁用某个数据








附件下载
eight2.zip
STM32L476的CubeIDE工程,版本为1.15.1。
one.mlapp
Matlab上位机源码,版本为2024A。
团队介绍
团队成员
obrulviser
评论
0 / 100
查看更多
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号