内容介绍
内容介绍
项目需求
使用板卡上的触摸按键,实现点按和左右滑动,实现传感器选择和切换,并将数据发送到上位机,功能选择的可视化也在上位机完成。
比如:能够选择加速度传感器,开启X轴数据发送,然后关闭加速度显示,选择温度等相似的功能。
需求分析
- 实现传感器数据的读取。
- 读取触摸按键的数据,并且判断点击和滑动的事件。
- 完成上位机可视化的设计。
板卡介绍
X-NUCLEO-IKS4A1板卡集成了一系列环境和动作传感器,并且配备Qvar触摸/滑动电极,如下:
- LSM6DSO16IS:MEMS 3D加速度计 (±2/±4/±8/±16 g) + 3D陀螺仪 (±125/±250/±500/±1000/±2000 dps) 与ISPU(智能处理单元)
- LIS2MDL:MEMS 3D磁力计 (±50 gauss)
- LIS2DUXS12:超低功耗MEMS 3轴加速度计 (±2/±4/±8/±16 g),具有Qvar、AI,以及抗混叠功能
- LPS22DF:低功率和高精度MEMS压力传感器,260-1260 hPa绝对数字输出气压计
- SHT40AD1B:湿度和温度传感器
- STTS22H:低电压,超低功耗,0.5°C精度的温度传感器(–40 °C到+125 °C)
- LSM6DSV16X:MEMS 3D加速度计 (±2/±4/±8/±16 g) + 3D陀螺仪 (±125/±250/±500/±1000/±2000/±4000 dps),内嵌传感器融合、AI、Qvar
搭配使用的STM32开发板为NUCLEO-G0B1RE,这是一款STM32 Nucleo-64板,为用户提供了一种可负担的灵活方法,具有以下及更多的功能:
- 采用LQFP64封装的STM32 微控制器
- 1个用户LED
- 1个用户按钮和1个复位按钮
- 32.768 kHz晶体振荡器
- 板连接器:ARDUINO® Uno V3扩展连接器意法半导体的morpho延长引脚头,用于完全访问所有STM32 I/O
- 灵活的供电选项:ST-LINK、USB VBUS或外部电源
- 具有USB重新枚举功能的板上ST-LINK调试器/编程器:大容量存储器、虚拟COM端口和调试端口
X-NUCLEO-IKS4A1和NUCLEO-G0B1RE组合的照片如下:
功能实现
主要分为两个部分,MCU开发和QT界面开发。
下位机
采用STM32G0芯片,使用官方的CubeIDE图形化界面开发。
吐槽一下,可能是Keil用吸习惯了,感觉现在IDE并不是很好用,需要图形化配置还是用CubeMX吧...
在图形界面中主要配置的有以下个部分:
- I2C1
- USART2
- GPIO_EXTI13
- GPIO_Output
- GPIO_Input
- TIM6
- X-CUBE-MEMS1
在图形化配置的时候,有时候经过会报错,提示XXX文件没有,XXX定义没有,这一般估计都是在图形化界面配置的时候,提示了Error,根据提示的错误进行修改,
主要的程序流程如下:
主要代码
main函数
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_TIM6_Init();
MX_MEMS_Init();
HAL_TIM_Base_Start_IT(&htim6);
int flag;
char* TouchState = "IDLE";
int i = 0;
while (1)
{
flag = TouchResult();
switch(flag){
case LEFT_PRESSED:
TouchState = "LEFT_PRESSED";
break;
case RIGHT_PRESSED:
TouchState = "RIGHT_PRESSED";
break;
case SLIDING_LEFT:
TouchState = "SLIDING_LEFT";
break;
case SLIDING_RIGHT:
TouchState = "SLIDING_RIGHT";
break;
default:
TouchState = "IDLE";
}
HAL_Delay(250);
MX_MEMS_Process();
}
}
触摸读取
主要是在定时器种,不断读取并且进行处理判断触摸状态
enum TouchState touchState = IDLE; //触摸状态
const int PRESS_THRESHOLD = 25000; //按下阈值
const int RELEASE_THRESHOLD = 10000; //释放阈值
const int SLIDE_START_THRESHOLD = 10000; //滑动阈值
static int16_t currentTouchValue = 0; //当前触摸状态
static enum TouchState LastTouch = 0; //上次触摸状态
static enum TouchState LastLastTouch = 0; //上上次触摸状态
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
static int lastTouchValue = 0;
if (htim->Instance == TIM6)
{
LastLastTouch = LastTouch;
LastTouch = touchState;
BSP_SENSOR_QVAR_GetValue(¤tTouchValue);
if (touchState == IDLE) { //空闲状态下,判断是否有按下
if (abs(currentTouchValue) > PRESS_THRESHOLD) {
if (currentTouchValue < 0)
touchState = LEFT_PRESSED;
else if (currentTouchValue > 0)
touchState = RIGHT_PRESSED;
}
}
else if (touchState == LEFT_PRESSED || touchState == RIGHT_PRESSED) {//左按或者右按
if (abs(currentTouchValue) <= RELEASE_THRESHOLD) {
touchState = IDLE;
}
else if ((touchState == LEFT_PRESSED && currentTouchValue > lastTouchValue + SLIDE_START_THRESHOLD)
|| (touchState == RIGHT_PRESSED && currentTouchValue < lastTouchValue - SLIDE_START_THRESHOLD)) {
touchState = (touchState == LEFT_PRESSED) ? SLIDING_RIGHT : SLIDING_LEFT;
}
}
else if (touchState == SLIDING_LEFT || touchState == SLIDING_RIGHT) {//左滑或者右滑
if (abs(currentTouchValue) <= RELEASE_THRESHOLD) {
touchState = IDLE;
}
}
lastTouchValue = currentTouchValue;
}
}
int TouchResult(void)
{
enum TouchState flag = 0;
//防误触发
if(touchState == LastLastTouch){
flag = IDLE;
}
else if((touchState == LEFT_PRESSED || touchState == RIGHT_PRESSED) && LastTouch == IDLE){
flag = IDLE;
}
else if((touchState == SLIDING_LEFT && LastTouch == SLIDING_LEFT) || (touchState == SLIDING_RIGHT && LastTouch == SLIDING_RIGHT)){
flag = IDLE;
}
else{
flag = touchState;
}
//printf("touchState = %d, LastTouch = %d, LastLastTouch = %d\r\n",touchState, LastTouch, LastLastTouch);
return flag;
}
传感器数据读取
主要是通过CubeMX直接生成的
void MX_IKS4A1_DataLogTerminal_Process(void)
{
int i;
if (PushButtonDetected != 0U)
{
/* Debouncing */
HAL_Delay(50);
/* Wait until the button is released */
while ((BSP_PB_GetState( BUTTON_KEY ) == PushButtonState));
/* Debouncing */
HAL_Delay(50);
/* Reset Interrupt flag */
PushButtonDetected = 0;
MX_IKS4A1_DataLogTerminal_Init();
}
snprintf(dataOut, MAX_BUF_SIZE, "\r\n__________________________________________________________________________\r\n");
printf("%s", dataOut);
for(i = 0; i < IKS4A1_MOTION_INSTANCES_NBR; i++)
{
if(MotionCapabilities[i].Acc)
{
Accelero_Sensor_Handler(i);
}
if(MotionCapabilities[i].Gyro)
{
Gyro_Sensor_Handler(i);
}
if(MotionCapabilities[i].Magneto)
{
Magneto_Sensor_Handler(i);
}
}
for(i = 0; i < IKS4A1_ENV_INSTANCES_NBR; i++)
{
if(EnvCapabilities[i].Humidity)
{
Hum_Sensor_Handler(i);
}
if(EnvCapabilities[i].Temperature)
{
Temp_Sensor_Handler(i);
}
if(EnvCapabilities[i].Pressure)
{
Press_Sensor_Handler(i);
}
}
HAL_Delay( 1000 );
}
上位机
采用QT进行开发。
在串口打开的时候,成功或者失败都会弹框进行提示,默认不显示任务数据,并且选择框为红色。
当右触摸点击的时候,选择框切换为紫色,并且显示数据【右图】。当左触摸点击的时候,变为缺省值【左图】
可以通选择框选择不同的传感器,也可以通过下位机上的触摸传感器,左滑或者右滑来切换。
主要代码
监听串口数据
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
ShowleLable(0);
// 初始化串口设置
SerialLoad();
connect(&serial, &QSerialPort::readyRead, this, &Widget::readData); // 当串口有数据可读时触发readData槽函数
}
开关控制串口的开或关
/* 打开和关闭串口,默认115200 */
void Widget::on_SerialOpen_But_clicked()
{
if(ui->SerialOpen_But->text() == "open"){
currentPortName = ui->SerialNum_Box->currentText();
if (!currentPortName.isEmpty()) {
serial.setPortName(currentPortName);
serial.setBaudRate(QSerialPort::Baud115200);
serial.setDataBits(QSerialPort::Data8);
serial.setParity(QSerialPort::NoParity);
serial.setStopBits(QSerialPort::OneStop);
serial.setFlowControl(QSerialPort::NoFlowControl);
if (serial.open(QIODevice::ReadWrite)) {
ui->SerialOpen_But->setText("close");
ui->SerialNum_Box->setEnabled(false);
ui->SerialRefresh_But->setEnabled(false);
QMessageBox::information(this,"提示","successful");
} else {
QMessageBox::critical(this,"提示","successful");
}
}
}
else{
serial.close();
ui->SerialNum_Box->setEnabled(true);
ui->SerialRefresh_But->setEnabled(true);
ui->SerialOpen_But->setText("open");
}
}
处理主要的数据
/* 串口接受的数据 */
void Widget::readData()
{
int index = 0;
QByteArray data = serial.readAll();
serbuf.append(data);
//提取单片机完整的一帧,以XXX-TOUCH\r\n结尾
if(serbuf.indexOf("-touch\r\n") != -1){
// 提取一个完整的消息
while ((index = serbuf.indexOf("\r\n")) != -1) {
QByteArray completeMessage = serbuf.left(index);
serbuf.remove(0, index + 2); // 移除已处理的消息和分隔符
// 处理完整的消息
QString message = QString::fromUtf8(completeMessage);
//qDebug() << "Received message: " << message;
if(message.indexOf("MAG_X[0]") != -1){
// 使用逗号作为分隔符分割字符串
QStringList parts = message.split(", ");
LIS2MDL_data[0] = parts[0].mid(parts[0].indexOf(": ") + 2).toInt(); // 提取x
LIS2MDL_data[1] = parts[1].mid(parts[1].indexOf(": ") + 2).toInt(); // 提取y
LIS2MDL_data[2] = parts[2].mid(parts[2].indexOf(": ") + 2).toInt(); // 提取z
SensorData[0] = message;
}
else if(message.indexOf("ACC_X[1]") != -1){
// 使用逗号作为分隔符分割字符串
QStringList parts = message.split(", ");
LSM6DSO16IS_data[0] = parts[0].mid(parts[0].indexOf(": ") + 2).toInt(); // 提取x
LSM6DSO16IS_data[1] = parts[1].mid(parts[1].indexOf(": ") + 2).toInt(); // 提取y
LSM6DSO16IS_data[2] = parts[2].mid(parts[2].indexOf(": ") + 2).toInt(); // 提取z
SensorData[1] = message;
}
else if(message.indexOf("GYR_X[1]") != -1){
// 使用逗号作为分隔符分割字符串
QStringList parts = message.split(", ");
LSM6DSO16IS_data[3] = parts[0].mid(parts[0].indexOf(": ") + 2).toInt(); // 提取x
LSM6DSO16IS_data[4] = parts[1].mid(parts[1].indexOf(": ") + 2).toInt(); // 提取y
LSM6DSO16IS_data[5] = parts[2].mid(parts[2].indexOf(": ") + 2).toInt(); // 提取z
SensorData[2] = message;
}
else if(message.indexOf("ACC_X[2]") != -1){
// 使用逗号作为分隔符分割字符串
QStringList parts = message.split(", ");
LIS2DUXS12_data[0] = parts[0].mid(parts[0].indexOf(": ") + 2).toInt(); // 提取x
LIS2DUXS12_data[1] = parts[1].mid(parts[1].indexOf(": ") + 2).toInt(); // 提取y
LIS2DUXS12_data[2] = parts[2].mid(parts[2].indexOf(": ") + 2).toInt(); // 提取z
SensorData[3] = message;
}
else if(message.indexOf("ACC_X[3]") != -1){
// 使用逗号作为分隔符分割字符串
QStringList parts = message.split(", ");
LSM6DSV16X_data[0] = parts[0].mid(parts[0].indexOf(": ") + 2).toInt(); // 提取x
LSM6DSV16X_data[1] = parts[1].mid(parts[1].indexOf(": ") + 2).toInt(); // 提取y
LSM6DSV16X_data[2] = parts[2].mid(parts[2].indexOf(": ") + 2).toInt(); // 提取z
SensorData[4] = message;
}
else if(message.indexOf("GYR_X[3]") != -1){
QStringList parts = message.split(", ");
LSM6DSV16X_data[3] = parts[0].mid(parts[0].indexOf(": ") + 2).toInt(); // 提取x
LSM6DSV16X_data[4] = parts[1].mid(parts[1].indexOf(": ") + 2).toInt(); // 提取y
LSM6DSV16X_data[5] = parts[2].mid(parts[2].indexOf(": ") + 2).toInt(); // 提取z
SensorData[5] = message;
}
else if(message.indexOf("Temp[0]") != -1){
QString tempValueStr = message.mid(message.indexOf(": ") + 2, message.indexOf(" degC") - message.indexOf(": ") - 2);
STTS22H_data = tempValueStr.toDouble();
SensorData[6] = message;
}
else if(message.indexOf("Press[1]") != -1){
QString tempValueStr = message.mid(message.indexOf(": ") + 2, message.indexOf(" hPa") - message.indexOf(": ") - 2);
LPS22DF_data = tempValueStr.toDouble();
SensorData[7] = message;
}
else if(message.indexOf("Hum[2]") != -1){
QString tempValueStr = message.mid(message.indexOf(": ") + 2, message.indexOf(" %") - message.indexOf(": ") - 2);
SHT40AD1B_data[0] = tempValueStr.toDouble();
SensorData[8] = message;
}
else if(message.indexOf("Temp[2]") != -1){
QString tempValueStr = message.mid(message.indexOf(": ") + 2, message.indexOf(" degC") - message.indexOf(": ") - 2);
SHT40AD1B_data[1] = tempValueStr.toDouble();
SensorData[9] = message;
}
else if(message.indexOf("-touch") != -1){
Touch = message;
}
}
DateProcess();
}
}
成果展示
致谢
感谢官方大力提供帮助在此活动中,也感谢帮助我的所有人,感谢!
附件下载
QT_Projct.zip
上位机QT源代码
G0B1_ALL.zip
下位机STM32G0源代码
团队介绍
个人
评论
0 / 100
查看更多
猜你喜欢
Funpack3-3使用X-NUCLEO-IKS4A1触摸功能实现传感器选择和切换该项目使用了stm32cubeide、matlab,实现了使用X-NUCLEO-IKS4A1触摸功能实现传感器选择和切换的设计,它的主要功能为:使用板卡上的触摸按键,实现点按和左右滑动,实现传感器选择和切换,并将数据发送到上位机,功能选择的可视化也在上位机完成。。
sdgdrdfc
29
Funpack3-3 基于X-NUCLEO-IKS4A1 的数据显示及切换该项目使用了X-NUCLEO-IKS4A1扩展版和NUCLEO-G0B1RE开发板,C语言,实现了传感器数据显示切换的设计,它的主要功能为:读取开发板上自带的传感器,将其发送给上位机,显示出来。
反正都一样
33
Funpack3-3活动——实现X-NUCLEO-IKS4A1数据切换及可视化该项目使用了STM32CubeIDE、Matlab,实现了X-NUCLEO-IKS4A1数据切换及可视化的设计,它的主要功能为:使用板卡上的触摸按键,识别出点按和左右滑动,以此切换单片机发送数据类型,通过上位机将数据进行可视化展示。
且听风吟123
36