一、任务要求
- 使用板卡上的触摸按键,实现点按和左右滑动,实现传感器选择和切换,并将数据发送到上位机
- 功能选择的可视化也在上位机完成,在上位机端实现传感器的切换和通道使能
二、设计思路
- 4 个运动传感器和 3 个环境传感器的数据读取可以直接使用 ST 官方提供的驱动和示例配合,完成读取。
- QVAR 触摸板的手势判断可以使用简单的状态机完成左右单击、滑动、长按压这三种手势的判断
- 交互的上位机可以使用 QT 进行快速开发,甚至最后可以实现飞控那样酷炫的效果
三、实现过程
1、开发环境的搭建与软件流程
使用 ST 官方的 CUBE 套件,cubemx 进行外设功能的初始化和添加传感器驱动。然后就可以快乐的开始我们的任务完成了~~~
在单片机程序中的流程框图如下。
在QT上位机中的功能如下。
2、NUCLEO 板的驱动对接
板载传感器:
LSM6DSO16IS:MEMS 3D 加速度计 + 3D 陀螺仪 与 ISPU
LIS2MDL:MEMS 3D 磁力计
LIS2DUXS12:超低功耗 MEMS 3 轴加速度计
LPS22DF:低功率和高精度 MEMS 绝对数字输出气压计
SHT40AD1B:高精度超低功耗的温湿度传感器
STTS22H:低电压,超低功耗,0.5°C 精度的温度传感器
LSM6DSV16X:MEMS 3D 加速度计+ 3D 陀螺仪
Motion | ID | ACCELEROMETER | GYROSCOPE | MAGNETOMETER |
---|---|---|---|---|
LIS2MDL | 0 | ✖ | ✖ | ✔ |
LSM6DSV16X | 1 | ✔ | ✔ | ✖ |
LIS2DUXS12 | 2 | ✔ | ✖ | ✖ |
LSM6DSO16IS | 3 | ✔ | ✔ | ✖ |
Environmental | ID | TEMPERATURE | PRESSURE | HUMIDITY |
---|---|---|---|---|
STTS22H | 0 | ✔ | ✖ | ✖ |
LPS22DF | 1 | ✔ | ✔ | ✖ |
SHT40AD1B | 2 | ✔ | ✖ | ✔ |
我本次任务所使用的 NUCLEO 板是 H743Z。超高的主频加上大容量 FLASH 和 RAM,用起来就是任性。本次的传感器通讯接口全是 I2C,并且挂在同一条总线上。再加上串口与上位机交互,这些就是任务所需要的所有底层外设了。但是,本着积极学习的想法,以及不浪费我这块板子上的百兆以太网口,所以我把 LWIP 协议栈加了进去,并使用 MQTT 进行与上位机的交互(最后因为时间没安排好,只是把 LWIP 的 MQTT 测试调通,并没有完成 MQTT 的上位机交互)。
3、QVAR 读取状态机的实现
官方的驱动中提供了直接读取 QVAR 值的函数
/**
* @brief Get the LSM6DSV16X QVAR sensor data
* @param pObj the device pObj
* @param Data pointer where the value is written
* @retval 0 in case of success, an error code otherwise
*/
int32_t LSM6DSV16X_QVAR_GetData(LSM6DSV16X_Object_t *pObj, int16_t *Data)
{
if (lsm6dsv16x_ah_qvar_raw_get(&(pObj->Ctx), Data) != LSM6DSV16X_OK)
{
return LSM6DSV16X_ERROR;
}
return LSM6DSV16X_OK;
}
并且 lsm6dsv16x 内部还有状态机,可通过一些命令设置,让内部直接进行状态逻辑决策,直接输出最后结果给我们。但是,个人感觉其比较复杂,并且不具备太多
的可复用性,于是我通过观察 QVAR 值的波形,来设计一个状态机,可简单的将题目要求的手势判断出来。
如图中所示,不按下时,其一般在 67 左右。当其中一边按下时,其值会突变到最大值或最小值。简单归纳如下:
ΔΔt 大概为 30ms,[左单击
:0-> 1 ->0],[右单击
:0-> -1 ->0].
ΔΔt 大概为 20ms,[右滑
:0->1->-1->0],[左滑
:0->-1->1->0].
然后,定时将 QVAR 值传入到如下的状态机判断里面,就能够返回手势判断。下面的状态机中,我虽然写了双击的判断,但经测试发现,效果很差,所以这个手势暂时搁置了。
uint8_t QVAR_action_check_statemachine(const int16_t qvar_value)
{
/*省略部分*/
tick_cur = HAL_GetTick();
switch (cur_state)
{
case ACTION_IDLE:
if (qvar_value > JUDGE_UP_VALUE)
{
tick_prev = tick_cur;
cur_state = ACTION_FIRST_UP;
// printf("\r\n----ACTION_IDLE->ACTION_FIRST_UP---\r\n");
}
else if (qvar_value < JUDGE_DOWN_VALUE)
{
tick_prev = tick_cur;
cur_state = ACTION_FIRST_DOWN;
// printf("\r\n----ACTION_IDLE->ACTION_FIRST_DOWN---\r\n");
}
else
{
cur_state = ACTION_IDLE;
prev_state = ACTION_IDLE;
}
tick_prev = tick_cur;
break;
case ACTION_FIRST_UP:
if ((qvar_value > JUDGE_UP_VALUE) && LEFT_PRESSED_flag == 0)
{
LEFT_PRESSED_TICK++;
if (LEFT_PRESSED_TICK > 3)
{
LEFT_PRESSED_flag = 1;
LEFT_PRESSED_TICK = 0;
// printf("\r\n----LEFT_PRESSED_TICK>3---\r\n");
}
}
if ((tick_cur - tick_prev) > 500)
{
if ((qvar_value < JUDGE_MIDDLE_UP) && (qvar_value > JUDGE_MIDDLE_DOWN))
{
// printf("\r\n----RESULT_LEFT_LONG_PRESSED---\r\n");
cur_state = ACTION_IDLE;
prev_state = ACTION_IDLE;
return RESULT_LEFT_LONG_PRESSED;
}
}
else if ((tick_cur - tick_prev) > 50)
{
if ((qvar_value < JUDGE_MIDDLE_UP) && (qvar_value > JUDGE_MIDDLE_DOWN))
{
// printf("\r\n----ACTION_FIRST_UP->ACTION_IDLE---\r\n");
cur_state = ACTION_IDLE;
prev_state = ACTION_IDLE;
}
}
if (LEFT_PRESSED_flag)
{
if ((qvar_value < JUDGE_MIDDLE_UP) && (qvar_value > JUDGE_MIDDLE_DOWN))
{
LEFT_PRESSED_flag = 0;
cur_state = ACTION_END_MIDDLE;
tick_prev = tick_cur;
prev_state = ACTION_FIRST_UP;
// printf("\r\n----ACTION_FIRST_UP->ACTION_END_MIDDLE---\r\n");
}
else if (qvar_value < JUDGE_DOWN_VALUE)
{
RIGHT_SLIP_BEFORE_TICK++;
if (RIGHT_SLIP_BEFORE_TICK > 3)
{
RIGHT_SLIP_BEFORE_TICK = 0;
RIGHT_SLIP_BEFORE_FLAG = 1;
}
}
if (RIGHT_SLIP_BEFORE_FLAG)
{
RIGHT_SLIP_BEFORE_FLAG = 0;
LEFT_PRESSED_flag = 0;
tick_prev = tick_cur;
cur_state = ACTION_SECOND_DOWN;
prev_state = ACTION_FIRST_UP;
// printf("\r\n----ACTION_FIRST_UP->ACTION_SECOND_DOWN---\r\n");
}
}
break;
case ACTION_FIRST_DOWN:
if ((qvar_value < JUDGE_DOWN_VALUE) && RIGHT_PRESSED_flag == 0)
{
RIGHT_PRESSED_TICK++;
if (RIGHT_PRESSED_TICK > 3)
{
RIGHT_PRESSED_flag = 1;
RIGHT_PRESSED_TICK = 0;
// printf("\r\n----RIGHT_PRESSED_TICK>3---\r\n");
}
}
if ((tick_cur - tick_prev) > 500)
{
if ((qvar_value < JUDGE_MIDDLE_UP) && (qvar_value > JUDGE_MIDDLE_DOWN))
{
// printf("\r\n----RESULT_RIGHT_LONG_PRESSED---\r\n");
cur_state = ACTION_IDLE;
prev_state = ACTION_IDLE;
return RESULT_RIGHT_LONG_PRESSED;
}
}
else if ((tick_cur - tick_prev) > 50)
{
if ((qvar_value < JUDGE_MIDDLE_UP) && (qvar_value > JUDGE_MIDDLE_DOWN))
{
// printf("\r\n----ACTION_FIRST_UP->ACTION_IDLE---\r\n");
cur_state = ACTION_IDLE;
prev_state = ACTION_IDLE;
}
}
if (RIGHT_PRESSED_flag)
{
if ((qvar_value < JUDGE_MIDDLE_UP) && (qvar_value > JUDGE_MIDDLE_DOWN))
{
RIGHT_PRESSED_flag = 0;
cur_state = ACTION_END_MIDDLE;
tick_prev = tick_cur;
prev_state = ACTION_FIRST_DOWN;
// printf("\r\n----ACTION_FIRST_DOWN->ACTION_END_MIDDLE---\r\n");
}
else if (qvar_value > JUDGE_UP_VALUE)
{
LEFT_SLIP_BEFORE_TICK++;
if (LEFT_SLIP_BEFORE_TICK > 3)
{
LEFT_SLIP_BEFORE_TICK = 0;
LEFT_SLIP_BEFORE_FLAG = 1;
}
}
if (LEFT_SLIP_BEFORE_FLAG)
{
LEFT_SLIP_BEFORE_FLAG = 0;
RIGHT_PRESSED_flag = 0;
tick_prev = tick_cur;
cur_state = ACTION_SECOND_UP;
prev_state = ACTION_FIRST_DOWN;
// printf("\r\n----ACTION_FIRST_DOWN->ACTION_SECOND_UP---\r\n");
}
}
break;
case ACTION_FIRST_MIDDLE:
if ((tick_cur - tick_prev) < 15)
break;
if ((tick_cur - tick_prev) < 25)
{
if ((qvar_value < JUDGE_MIDDLE_UP) && (qvar_value > JUDGE_MIDDLE_DOWN))
{
cur_state = ACTION_IDLE;
prev_state = ACTION_IDLE;
// printf("\r\n----ACTION_FIRST_MIDDLE->ACTION_IDLE---\r\n");
}
else if (qvar_value > JUDGE_UP_VALUE)
{
cur_state = ACTION_SECOND_UP;
tick_prev = tick_cur;
prev_state = ACTION_FIRST_MIDDLE;
// printf("\r\n----ACTION_FIRST_MIDDLE->ACTION_SECOND_UP---\r\n");
}
else if (qvar_value < JUDGE_DOWN_VALUE)
{
cur_state = ACTION_SECOND_DOWN;
tick_prev = tick_cur;
prev_state = ACTION_FIRST_MIDDLE;
// printf("\r\n----ACTION_FIRST_MIDDLE->ACTION_SECOND_DOWN---\r\n");
}
}
else
{
if ((qvar_value < JUDGE_MIDDLE_UP) && (qvar_value > JUDGE_MIDDLE_DOWN))
{
cur_state = ACTION_IDLE;
prev_state = ACTION_IDLE;
// printf("\r\n----ACTION_FIRST_DOWN->ACTION_IDLE---\r\n");
}
}
break;
case ACTION_SECOND_UP:
LEFT_SLIP_TICK++;
if (LEFT_SLIP_TICK > 3 && LEFT_SLIP_FLAG == 0)
{
LEFT_SLIP_TICK = 0;
LEFT_SLIP_FLAG = 1;
}
if (LEFT_SLIP_FLAG)
{
if ((qvar_value < JUDGE_MIDDLE_UP) && (qvar_value > JUDGE_MIDDLE_DOWN))
{
LEFT_SLIP_FLAG = 0;
LEFT_SLIP_TICK = 0;
if (ACTION_FIRST_MIDDLE == prev_state)
{
// //printf("\r\n----RESULT_LEFT_DOUBLE_CLICK---\r\n");
// cur_state = ACTION_IDLE;
// /*右双击判断成功*/
// return RESULT_LEFT_DOUBLE_CLICK;
}
else if (ACTION_FIRST_DOWN == prev_state)
{
// printf("\r\n----RESULT_LEFT_SLIP---\r\n");
cur_state = ACTION_IDLE;
/*左滑判断成功*/
return RESULT_LEFT_SLIP;
}
}
}
if ((tick_cur - tick_prev) > 100)
{
if ((qvar_value < JUDGE_MIDDLE_UP) && (qvar_value > JUDGE_MIDDLE_DOWN))
{
cur_state = ACTION_IDLE;
}
LEFT_SLIP_TICK = 0;
}
break;
case ACTION_SECOND_DOWN:
RIGHT_SLIP_TICK++;
if (RIGHT_SLIP_TICK > 3 && RIGHT_SLIP_FLAG == 0)
{
RIGHT_SLIP_TICK = 0;
RIGHT_SLIP_FLAG = 1;
}
if (RIGHT_SLIP_FLAG)
{
if ((qvar_value < JUDGE_MIDDLE_UP) && (qvar_value > JUDGE_MIDDLE_DOWN))
{
RIGHT_SLIP_FLAG = 0;
RIGHT_SLIP_TICK = 0;
if (ACTION_FIRST_MIDDLE == prev_state)
{
// printf("\r\n----RESULT_LEFT_DOUBLE_CLICK---\r\n");
cur_state = ACTION_IDLE;
/*右双击判断成功*/
return RESULT_RIGHT_DOUBLE_CLICK;
}
else if (ACTION_FIRST_UP == prev_state)
{
// printf("\r\n----RESULT_RIGHT_SLIP---\r\n");
cur_state = ACTION_IDLE;
/*右滑判断成功*/
return RESULT_RIGHT_SLIP;
}
}
}
if ((tick_cur - tick_prev) > 100)
{
if ((qvar_value < JUDGE_MIDDLE_UP) && (qvar_value > JUDGE_MIDDLE_DOWN))
{
cur_state = ACTION_IDLE;
}
RIGHT_SLIP_TICK = 0;
}
break;
case ACTION_END_MIDDLE:
if ((qvar_value < JUDGE_MIDDLE_UP) && (qvar_value > JUDGE_MIDDLE_DOWN))
{
if (ACTION_FIRST_UP == prev_state)
{
// printf("\r\n----RESULT_LEFT_SINGEL_CLICK---\r\n");
cur_state = ACTION_IDLE;
/*左单击判断成功*/
return RESULT_LEFT_SINGEL_CLICK;
}
else if (ACTION_FIRST_DOWN == prev_state)
{
// printf("\r\n----RESULT_RIGHT_SINGEL_CLICK---\r\n");
cur_state = ACTION_IDLE;
/*右单击成功*/
return RESULT_RIGHT_SINGEL_CLICK;
}
}
else
{
cur_state = ACTION_IDLE;
}
break;
default:
break;
}
return 0;
}
4、数据收发处理
因为加入上位机显示和控制,所以需要做串口的发送和接收处理。这里我的串口接收使用了循环队列,用的是一个开源组件CherryRB,真的超级好用,强烈推荐!
如下是通过选择的传感器和通道,发送相应的数据,为了上位机好提取数据,我中间加入了:
,便于做字符串分割处理。
/*
通过传入的指令进行控制操作
*/
uint8_t instruct_sensor_Handler(Sensor_Type type,uint8_t *_sensor_channel_enable,uint8_t sensor_channel_nums,uint8_t *data)
{
uint16_t i=0;
uint8_t length=0;
__IO UserFtoCtoI temp_fdata;
static uint8_t acc_flag1=1,acc_flag2=1;
switch(type)
{
case LIS2MDL:
if (IKS4A1_MOTION_SENSOR_GetAxes(0, MOTION_MAGNETO, &LIS2MDL_magnetic_field))
{
}
IKS4A1_MOTION_SENSOR_Axes_2_int32(LIS2MDL_mag,LIS2MDL_magnetic_field);
for(i=0;i<sensor_channel_nums;i++)
{
if(_sensor_channel_enable[i])
{
printf("LIS2MDL:%d:%d:\r\n",i,LIS2MDL_mag[i]);
length +=4;
}
}
break;
case LSM6DSV16X:
if(acc_flag1)
{
if (IKS4A1_MOTION_SENSOR_GetAxes(1, MOTION_ACCELERO, &LSM6DSV16X_acceleration))
{
}
IKS4A1_MOTION_SENSOR_Axes_2_int32(LSM6DSV16X_acc,LSM6DSV16X_acceleration);
}
else
{
if (IKS4A1_MOTION_SENSOR_GetAxes(1, MOTION_GYRO, &LSM6DSV16X_angular_velocity))
{
}
IKS4A1_MOTION_SENSOR_Axes_2_int32(LSM6DSV16X_ang,LSM6DSV16X_angular_velocity);
}
for(i=0;i<sensor_channel_nums;i++)
{
if(_sensor_channel_enable[i])
{
if(i<3)
{
if(acc_flag1)
printf("LSM6DSV16X:%d:%d:\r\n",i,LSM6DSV16X_acc[i]);
}
else
{
if(0==acc_flag1)
printf("LSM6DSV16X:%d:%d:\r\n",i,LSM6DSV16X_ang[i]);
}
length +=4;
}
}
acc_flag1=!acc_flag1;
break;
case LIS2DUXS12:
if (IKS4A1_MOTION_SENSOR_GetAxes(2, MOTION_ACCELERO, &LIS2DUXS12_acceleration))
{
}
IKS4A1_MOTION_SENSOR_Axes_2_int32(LIS2DUXS12_acc,LIS2DUXS12_acceleration);
for(i=0;i<sensor_channel_nums;i++)
{
if(_sensor_channel_enable[i])
{
printf("LIS2DUXS12:%d:%d:\r\n",i,LIS2DUXS12_acc[i]);
length +=4;
}
}
break;
case LSM6DSO16IS:
if(acc_flag2)
{
if (IKS4A1_MOTION_SENSOR_GetAxes(3, MOTION_ACCELERO, &LSM6DSO16IS_acceleration))
{
}
IKS4A1_MOTION_SENSOR_Axes_2_int32(LSM6DSO16IS_acc,LSM6DSO16IS_acceleration);
}
else
{
if (IKS4A1_MOTION_SENSOR_GetAxes(3, MOTION_GYRO, &LSM6DSO16IS_angular_velocity))
{
}
IKS4A1_MOTION_SENSOR_Axes_2_int32(LSM6DSO16IS_ang,LSM6DSO16IS_angular_velocity);
}
for(i=0;i<sensor_channel_nums;i++)
{
if(_sensor_channel_enable[i])
{
if(i<3)
{
if(acc_flag2)
printf("LSM6DSO16IS:%d:%d:\r\n",i,LSM6DSO16IS_acc[i]);
}
else
{
if(0==acc_flag2)
printf("LSM6DSO16IS:%d:%d:\r\n",i,LSM6DSO16IS_ang[i]);
}
length +=4;
}
}
acc_flag2=!acc_flag2;
break;
case STTS22H:
if (IKS4A1_ENV_SENSOR_GetValue(0, ENV_TEMPERATURE, &STTS22H_temperature))
{
}
if(0==_sensor_channel_enable[0])
break;
printf("STTS22H:%.2f:\r\n",STTS22H_temperature);
length +=4;
break;
case LPS22DF:
if (IKS4A1_ENV_SENSOR_GetValue(1, ENV_TEMPERATURE, &LPS22DF_temperature))
{
}
if (IKS4A1_ENV_SENSOR_GetValue(1, ENV_PRESSURE, &LPS22DF_pressure))
{
}
if(_sensor_channel_enable[0])
{
printf("LPS22DF:%d:%.2f:\r\n",0,LPS22DF_temperature);
length +=4;
}
if(0==_sensor_channel_enable[1])
break;
printf("LPS22DF:%d:%.2f:\r\n",1,LPS22DF_pressure);
length +=4;
break;
case SHT40AD1B:
if (IKS4A1_ENV_SENSOR_GetValue(2, ENV_TEMPERATURE, &SHT40AD1B_temperature))
{
}
if (IKS4A1_ENV_SENSOR_GetValue(2, ENV_HUMIDITY, &SHT40AD1B_humidity))
{
}
if(_sensor_channel_enable[0])
{
printf("SHT40AD1B:%d:%.2f:\r\n",0,SHT40AD1B_temperature);
length +=4;
}
if(0==_sensor_channel_enable[1])
break;
printf("SHT40AD1B:%d:%.2f:\r\n",1,SHT40AD1B_humidity);
length +=4;
break;
default:
break;
}
return length;
}
这里一开始的时候理解错了题目意思,我将所有的传感器全部往上位机发,上位机从里面挑出需要显示的部分。后来,再仔细看题目才发现需要每一部分分段控制上发。
因为之前配置了 LWIP 的 mqtt(因为没有全部完成,因此不展开说明,等我完善后再补充),引入了 freertos。因此我为了方便,直接创建一个新的线程专门用来处理串口收到的数据。
uint8_t analysis_receivedData_thread(void const * argument)
{
uint8_t data[128];
uint8_t len=0;
uint8_t *ptr_index;
uint8_t temp_sensor=0;
uint8_t channel_index=0;
while(1){
len =chry_ringbuffer_read(&rb, data, 20);
if (len){
ptr_index=strchr(data,':');
if(NULL !=strstr(data,"SENSOR"))
{
temp_sensor=*(ptr_index+1)-'1';
printf("temp_sensor=%d\n",temp_sensor);
cur_sensor=temp_sensor;
}
else if(NULL !=strstr(data,"channel"))
{
if(NULL !=strstr(data,"ON"))
{
channel_index=*(ptr_index+4)-'1';
printf("channel_index=%d\n",channel_index);
sensor_channel_enable[cur_sensor][channel_index]=1;
}
else if(NULL !=strstr(data,"OFF"))
{
channel_index=*(ptr_index+5)-'1';
printf("channel_index=%d\n",channel_index);
sensor_channel_enable[cur_sensor][channel_index]=0;
}
}
}
else
{
// printf("[C] read faild, no data in ringbuffer\r\n");
}
osDelay(100);
}
}
最后则是主线程的处理,这里主要是通过QVAR检测状态机返回的手势,进行传感器的切换,通道的打开和关闭处理。
for(;;)
{
send_tick++;
if(send_tick>=200)
{
send_tick=0;
sensor_send_length=instruct_sensor_Handler(cur_sensor,&sensor_channel_enable[cur_sensor],sensor_channel_nums[cur_sensor],sensor_send_buff);
// printf("sensor_send_length=%d\n",sensor_send_length);
printf("channel:");
for ( i = 0; i < 6; i++)
{
printf("%d:",sensor_channel_enable[cur_sensor][i]);
}
printf("\r\n");
// read_all_sensor_data();
// sensor_send_length=get_one_sensor_data(cur_sensor,sensor_send_buff,c_status);
}
osDelay(5);
BSP_SENSOR_QVAR_GetValue(&QvarValue);
state_back = QVAR_action_check_statemachine((int)QvarValue / 78);
if(0 ==state_back)
continue;
switch (state_back) {
case RESULT_LEFT_SINGEL_CLICK:
//切换传感器的通道
if(channel_index<sensor_channel_nums[cur_sensor]-1)
channel_index++;
else
channel_index=0;
break;
case RESULT_RIGHT_SINGEL_CLICK:
//打开或关闭传感器的通道
sensor_channel_enable[cur_sensor][channel_index]=!sensor_channel_enable[cur_sensor][channel_index];
break;
case RESULT_LEFT_SLIP:
//切换传感器
if(cur_sensor<SHT40AD1B)
cur_sensor++;
else
cur_sensor=LIS2MDL;
channel_index=0;
break;
case RESULT_RIGHT_SLIP:
//切换传感器
if(cur_sensor>LIS2MDL)
cur_sensor--;
else
cur_sensor=SHT40AD1B;
channel_index=0;
break;
case RESULT_LEFT_LONG_PRESSED:
break;
case RESULT_RIGHT_LONG_PRESSED:
break;
default:
break;
}
printf("[QVAR state-> %d]\r\n",state_back);
printf("[cur_sensor -> %d] \n",cur_sensor);
}
5、QT 上位机的实现
QT 使用 C++进行编程,实现我们想要的功能。如下是在 widget.ui 中拖控件,组合的界面
创建一个 data_frame 类进行串口接收数据的解析处理,每当解析完成就触发信号,更新传感器的选择,如下是主要的解析函数
int data_frame::parse_2(const QByteArray &bytes)
{
QString t_data= QString(bytes);
// qDebug() << "开始填充传感器数据";
int channel_index=0;
bool get_Data_en=false;
if(-1==t_data.indexOf(":"))
return -1;
QStringList strs= t_data.split(":");
foreach (QString s, strs)
{
qDebug() << s;
}
qDebug() <<"\r\n";
if(t_data.contains("channel") && strs.size()>6)
{
for(int j=0;j<6;j++)
{
channel_status[j]=strs.at(j+1).toInt()==1?true:false;
}
return 1;
}
for(int i=0;i<7;i++)
{
if(strs.at(0)==sensor_compare.at(i))
{
qDebug() << "sensor:"<<i;
tmp_command=i;
get_Data_en=true;
}
}
if(false==get_Data_en)
return -1;
channel_index=strs.at(1).toInt();
switch (tmp_command) {
case 0:
if(channel_index<3)
LIS2MDL_Magneto[channel_index]=strs.at(2).toInt();
break;
case 1:
if(channel_index<3)
LSM6DSV16X_Acc[channel_index]=strs.at(2).toInt();
else
LSM6DSV16X_Gyro[channel_index-3]=strs.at(2).toInt();
break;
case 2:
if(channel_index<3)
LIS2DUXS12_Acc[channel_index]=strs.at(2).toInt();
break;
case 3:
if(channel_index<3)
LSM6DSO16IS_Acc[channel_index]=strs.at(2).toInt();
else
LSM6DSO16IS_Gyro[channel_index-3]=strs.at(2).toInt();
break;
case 4:
if(channel_index<3)
STTS22H_temperature=strs.at(1).toFloat();
break;
case 5:
if(channel_index==0)
LPS22DF_temperature=strs.at(2).toFloat();
else
LPS22DF_pressure=strs.at(2).toFloat();
break;
case 6:
if(channel_index==0)
SHT40AD1B_temperature=strs.at(2).toFloat();
else
SHT40AD1B_humidity=strs.at(2).toFloat();
break;
default:
break;
}
emit cmd_changed(tmp_command+1);
length=tmp_length;
command=tmp_command;
return 0;
}
在 widget 构函数中进行一些信号量和槽函数的连接
Widget::Widget(QWidget *parent) : QWidget(parent), ui(new Ui::Widget), serial(new QSerialPort(this))
{
ui->setupUi(this);
/*省略*/
group_radiobutton = new QButtonGroup(this);
group_radiobutton->addButton(ui->radioButton_all, 0);
group_radiobutton->addButton(ui->radioButton_1, 1);
group_radiobutton->addButton(ui->radioButton_2, 2);
group_radiobutton->addButton(ui->radioButton_3, 3);
group_radiobutton->addButton(ui->radioButton_4, 4);
group_radiobutton->addButton(ui->radioButton_5, 5);
group_radiobutton->addButton(ui->radioButton_6, 6);
group_radiobutton->addButton(ui->radioButton_7, 7);
group_radiobutton->setExclusive(true);
ui->radioButton_all->setChecked(true);
connect(ui->radioButton_all, SIGNAL(clicked(bool)), this, SLOT(radiobutton_handler()));
connect(ui->radioButton_1, SIGNAL(clicked(bool)), this, SLOT(radiobutton_handler()));
connect(ui->radioButton_2, SIGNAL(clicked(bool)), this, SLOT(radiobutton_handler()));
connect(ui->radioButton_3, SIGNAL(clicked(bool)), this, SLOT(radiobutton_handler()));
connect(ui->radioButton_4, SIGNAL(clicked(bool)), this, SLOT(radiobutton_handler()));
connect(ui->radioButton_5, SIGNAL(clicked(bool)), this, SLOT(radiobutton_handler()));
connect(ui->radioButton_6, SIGNAL(clicked(bool)), this, SLOT(radiobutton_handler()));
connect(ui->radioButton_7, SIGNAL(clicked(bool)), this, SLOT(radiobutton_handler()));
connect(ui->checkBox, SIGNAL(stateChanged(int)), this, SLOT(onCheckBoxState_changed(int)));
connect(ui->checkBox_2, SIGNAL(stateChanged(int)), this, SLOT(onCheckBoxState_changed(int)));
connect(ui->checkBox_3, SIGNAL(stateChanged(int)), this, SLOT(onCheckBoxState_changed(int)));
connect(ui->checkBox_4, SIGNAL(stateChanged(int)), this, SLOT(onCheckBoxState_changed(int)));
connect(ui->checkBox_5, SIGNAL(stateChanged(int)), this, SLOT(onCheckBoxState_changed(int)));
connect(ui->checkBox_6, SIGNAL(stateChanged(int)), this, SLOT(onCheckBoxState_changed(int)));
connect(&m_frame, &data_frame::cmd_changed, this, &Widget::serial_comand_refresh);
connect(serial, &QSerialPort::readyRead, this, &Widget::readData);
ui->mqttButton->setText("CONNECT MQTT SERVER");
// M_client.start();
tim = new QTimer();
tim->setInterval(500);
connect(tim, SIGNAL(timeout()), this, SLOT(onTimeOut()));
tim->start();
/*省略*/
}
为了让数据更直观,我还把 customPlot 加进来了,但是实际上数据显示的很乱,还需要进一步研究才能显示出比较好的效果。
数据的刷新是使用定时器定时刷新
void Widget::onTimeOut()
{
QString str;
QStringList strs;
double key = QDateTime::currentDateTime().toMSecsSinceEpoch() / 1000.0;
//改变6个数值显示的状态
ui->checkBox->setChecked( m_frame.channel_status[0]);
/*省略*/
sensor_data_refresh();
if (ui->checkBox->isChecked())
{
str = ui->checkBox->text();
strs = str.split(":");
// qDebug()<<"data="<<strs.at(1).toDouble();
ui->customPlot->graph(0)->addData(key, strs.at(1).toDouble());
ui->customPlot->graph(0)->rescaleValueAxis();
}
/*省略*/
QSharedPointer<QCPAxisTickerDateTime> timeTicker(new QCPAxisTickerDateTime); // 时间日期作为X轴
timeTicker->setDateTimeFormat("hh:mm:ss");
ui->customPlot->xAxis->setTicker(timeTicker);
ui->customPlot->xAxis->setRange(key, 3, Qt::AlignRight); // 设定x轴的范围
ui->customPlot->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom |
QCP::iSelectPlottables); // 可以缩放可以滚动点击选择
for(int i=0;i<6;i++)
{
ui->customPlot->graph(i)->data().data()->removeBefore(key-10);
}
ui->customPlot->replot();
}
如下是运行时的效果
四、效果展示与遇到的问题
效果展示
遇到的问题
- QVAR 值受天气影响很大,之前天晴时,测试基本没有波动,但是最近梅雨天时,经常出现我之前写的状态机识别不准的情况。使用吹风机加热后,效果才好一些。
- 当我加热后,发现两个传感器的 GYRO 的 3 个轴数据,都会出现读取失败的情况,导致上位机数据并没有更新。总不可能吹风机吹一下,就坏了,估计还是环境影响。
- QT 上位机的串口数据读取出现不连续的情况,串口的 readyRead 信号,只要串口一收到数据,就会触发槽函数,导致数据不连续,在加入回车符截取,才将之前读到的数据传入数据解析函数中,这样才解决这个问题。
五、感想与未来计划
这次终于能玩上 ST 的板子了,玩了这么多的板子,发现还是 ST 的用的最习惯。这次我完成的任务只是都传感器的数据,感觉都没有把这些传感器的功能发挥出来,所以,下一个目标是,帅气的飞控系统!
