Funpack3-3基于Zephyr的Qvar滑动检测实现
本项目在ST Nucleo G0B1RE开发板和X-NUCLEO-IKS4A1扩展板的基础上结合Zephyr RTOS,实现Qvar传感器的检测功能传感器数据采集功能。并通过Python上位机实现对数据的可视化以及传感器选择。
标签
嵌入式系统
Funpack活动
maskmoo
更新2024-07-08
65


1 项目介绍

本项目是在Zephyr RTOS的基础之上,以ST Nucleo G0B1RE为开发板完成对LSM6DSV16X驱动Qvar操作部分的增加和适配,并在应用部分实现对Qvar滑动感应的电极的检测功能以及 对LIS2MDL,LIS2MDL: Temperature,LSM6DSO16IS,LSM6DSO16IS,LSM6DSO16IS,LSM6DSV16X,LSM6DSV16X,LPS22DF和LPS22DF的传感器数据采集功能。并通过Python编写上位机实现对传感器数据的可视化以及检测Qvar实现传感器的选择和关闭。

2 设计思路

本项目利用Zephyr RTOS和ST Nucleo G0B1RE开发板,结合X-NUCLEO-IKS4A1扩展板,实现Qvar传感器的检测功能。首先选择在zephyr RTOS的基础之上进行开发。其次,通过修改和优化LSM6DSV16X传感器的驱动程序,新增Qvar检测功能并实现数据处理和中断处理的适配。最后,基于Python开发了上位机程序,实现了传感器数据的实时监控和控制,确保整个系统能够稳定运行并提供准确的检测结果。



3 系统设计框图


image.png



4 项目实现

4.1 JLINK-OB更新

本人更习惯于使用Jlink相关工具,所以拿到板子后第一件事情先将ST-LINK刷成J-Link-OB。J-Link-OB可以使用大多数J-Link功能,如快速的闪存下载和调试,免费使用的GDBServer

ST-LINK On-Board (segger.com)

  1. 安装 ST-LINK USB 驱动程序: 下载
  2. 安装 J-Link 软件包: 下载
  3. 下载 SEGGER STLinkReflash 工具: 下载
  4. 启动 STLinkReflash 实用程序
  5. 同意许可条款,按照提示命令进行操作即可

image.png

通过Jlink Commander连接查看确认更新状态。

image.png

通过开头信息可以确认已经成功更新。

image.png

连接STM32G0B1RE可以正常halt住内核并查看到相关寄存器等信息。


4.2 Zephyr基本使用测试

4.2.1 ST Nucleo G0B1RE

Zephyr的介绍和环境安装可以参照官方文档。

  1. Introduction — Zephyr Project Documentation
  2. Getting Started Guide — Zephyr Project Documentation


编译Zephyr  Hello World a应用程序测试

# From the root of the zephyr repository
west build -b nucleo_g0b1re samples/hello_world

烧写运行

west flash --runner jlink

可以添加 -v参数查看更多信息,遇到问题是方便进行定位。

west -v flash --runner jlink

image.png

通过Jlink CDC UART接口查看打印信息,表明Zephyr3.6已经可以正常启动。也证明了使用Zephyr的工具链通过刷机的Jlink-OB对STM32G0B1RE的Flash更新是没问题的。

image.png

4.2.2 X-NUCLEO-IKS4A1

以samples/shields/x_nucleo_iks4a1/standard/作为测试程序

该示例启用 X-NUCLEO-IKS4A1 扩展板的以下四个传感器,然后定期读取和显示传感器的数据:

  • LSM6DSV16X 6 轴加速度和角速度传感器
  • LSM6DSO16IS 6 轴加速度和角速度传感器
  • LPS22DF环境温度和大气压传感器
  • LIS2MDL 3轴磁场强度传感器

该模式下为标准的I2C模式,所有设备都挂在I2C总线上。

image.png

构建应用程序

 west build -b nucleo_g0b1re samples/shields/x_nucleo_iks4a1/standard/ -p

烧写运行

west flash --runner jlink

image.png


4.3 Qvar检测测功能实现

功能实现是在samples/shields/x_nucleo_iks4a1/standard/代码基础上进行修改的。Zephyr 的lsm6dsv16x的驱动是不支持qvar的,所以我这里适配了qvar的基本驱动以及中断功能。同时也在Zephyr的 sensor框架中增加针对Qvar的SENSOR_CHAN_QVAR通道,方便应用程序的访问。整体上代码的改动如下所示。

image.png


Qvar检测代码是在中断中实现的,Qvar 数据以 240 Hz 的固定 ODR 存储在变量 qvar_out 中,我这里每24次进行一次均值滤波处理,相当于最终的逻辑采样率为10Hz。并通过模式检测状态机来实现TOUCH_QP_ACTIVE,TOUCH_QN_ACTIVE ,TOUCH_QP2N_ACTIVE ,TOUCH_QN2P_ACTIVE的4种状态的检测,算法的主要逻辑是分别设置 Q+ 和 Q- 的阈值,检测是否按下了相应的按钮。当信号输出先后超出这两个阈值时,算法应检测到一个方向上的滑动;当 信号输出以相反顺序先后超出这两个阈值时,算法应检测到另一个方向上的滑动。具体代码实现如下所示:


static int lsm6dsv16x_qvar_trig_cnt;
int32_t avr_qvar;
static int qvar_pval = 20;
static int qvar_nval = -20;

typedef enum
{
TOUCH_IDLE = 0x0,
TOUCH_QP_ACTIVE = 0x1,
TOUCH_QN_ACTIVE = 0x2,
TOUCH_QP2N_ACTIVE = 0x3,
TOUCH_QN2P_ACTIVE = 0x4,
} touch_mode_t;
int check_state = 0;
touch_mode_t check_result = TOUCH_IDLE;

static void lsm6dsv16x_qvar_trig_handler(const struct device *dev,
const struct sensor_trigger *trig)
{
sensor_sample_fetch_chan(dev, SENSOR_CHAN_ALL);
sensor_channel_get(dev, SENSOR_CHAN_QVAR, &lsm6dsv16x_qvar);
lsm6dsv16x_qvar_trig_cnt++;
avr_qvar += lsm6dsv16x_qvar.val1;
if(lsm6dsv16x_qvar_trig_cnt % 24 == 0){
lsm6dsv16x_qvar.val1 = avr_qvar/24;
// printf("{%d}Qvar:%d\n",
// lsm6dsv16x_qvar_trig_cnt, lsm6dsv16x_qvar.val1);
if(check_state == 0){
if(lsm6dsv16x_qvar.val1>qvar_pval){
check_state = 1;
}
else if(lsm6dsv16x_qvar.val1<qvar_nval){
check_state = 2;
}
else{
check_state = 0;
// check_result = TOUCH_IDLE;
}
}
else if(check_state == 1){
if(lsm6dsv16x_qvar.val1>qvar_pval){
check_state = 1;
}
else if(lsm6dsv16x_qvar.val1<qvar_nval){
check_result = TOUCH_QP2N_ACTIVE;
check_state = 3;
}
else{
check_result = TOUCH_QP_ACTIVE;
check_state = 0;
}
}
else if(check_state == 2){
if(lsm6dsv16x_qvar.val1>qvar_pval){
check_result = TOUCH_QN2P_ACTIVE;
check_state = 3;
}
else if(lsm6dsv16x_qvar.val1<qvar_nval){
check_state = 2;
}
else{
check_result = TOUCH_QN_ACTIVE;
check_state = 0;
}
}
else /**/
{
if(lsm6dsv16x_qvar.val1>qvar_pval){
check_state = 3;
}
else if(lsm6dsv16x_qvar.val1<qvar_nval){
check_state = 3;
}
else{
check_state = 0;
}
}
avr_qvar = 0;
}
}


驱动部分的修改:

具体的修改可以查看附件的patch文件。主要涉及到Kconfig配置选项增加,Sensor框架SENSOR_CHAN_QVAR通道增加,基本操作函数lsm6dsv16x_qvar_config,lsm6dsv16x_sample_fetch_qvar,lsm6dsv16x_qvar_channel_get的实现,以及中断相关的lsm6dsv16x_trigger_set和lsm6dsv16x_handle_interrupt函数适配。

diff --git a/drivers/sensor/lsm6dsv16x/Kconfig b/drivers/sensor/lsm6dsv16x/Kconfig
index 79463a0f4e..eaa756d1e0 100644
--- a/drivers/sensor/lsm6dsv16x/Kconfig
+++ b/drivers/sensor/lsm6dsv16x/Kconfig
@@ -64,6 +64,11 @@ config LSM6DSV16X_ENABLE_TEMP
help
Enable/disable temperature

+config LSM6DSV16X_AH_QVAR
+ bool "Qvar"
+ help
+ Enable/disable qvar
+
config LSM6DSV16X_SENSORHUB
bool "I2C sensorhub feature"
help
diff --git a/drivers/sensor/lsm6dsv16x/lsm6dsv16x.c b/drivers/sensor/lsm6dsv16x/lsm6dsv16x.c
index d4f9a2c850..585cbb96f5 100644
--- a/drivers/sensor/lsm6dsv16x/lsm6dsv16x.c
+++ b/drivers/sensor/lsm6dsv16x/lsm6dsv16x.c
@@ -258,6 +258,38 @@ static int lsm6dsv16x_accel_config(const struct device *dev,
return 0;
}

+static int lsm6dsv16x_qvar_config(const struct device *dev,
+ enum sensor_channel chan,
+ enum sensor_attribute attr,
+ const struct sensor_value *val)
+{
+ const struct lsm6dsv16x_config *cfg = dev->config;
+ stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
+ lsm6dsv16x_ah_qvar_mode_t mode;
+
+ // lsm6dsv16x_ah_qvar_zin_set(ctx, LSM6DSV16X_255MOhm);
+ switch (attr) {
+ case SENSOR_ATTR_CONFIGURATION:
+ switch (val->val1) {
+ case 0: /* High Performance */
+ mode.ah_qvar_en = 0;
+ break;
+ case 1: /* High Accuracy */
+ mode.ah_qvar_en = 1;
+ break;
+ default:
+ return -EIO;
+ }
+
+ return lsm6dsv16x_ah_qvar_mode_set(ctx, mode);
+ default:
+ LOG_DBG("Qvar attribute not supported.");
+ return -ENOTSUP;
+ }
+
+ return 0;
+}
+
static int lsm6dsv16x_gyro_odr_set(const struct device *dev, uint16_t freq)
{
int odr;
@@ -353,6 +385,10 @@ static int lsm6dsv16x_attr_set(const struct device *dev,
return lsm6dsv16x_accel_config(dev, chan, attr, val);
case SENSOR_CHAN_GYRO_XYZ:
return lsm6dsv16x_gyro_config(dev, chan, attr, val);
+#if defined(CONFIG_LSM6DSV16X_AH_QVAR)
+ case SENSOR_CHAN_QVAR:
+ return lsm6dsv16x_qvar_config(dev, chan, attr, val);
+#endif /* CONFIG_LSM6DSV16X_AH_QVAR */
#if defined(CONFIG_LSM6DSV16X_SENSORHUB)
case SENSOR_CHAN_MAGN_XYZ:
case SENSOR_CHAN_PRESS:
@@ -416,6 +452,22 @@ static int lsm6dsv16x_sample_fetch_temp(const struct device *dev)
}
#endif

+#if defined(CONFIG_LSM6DSV16X_AH_QVAR)
+static int lsm6dsv16x_sample_fetch_qvar(const struct device *dev)
+{
+ const struct lsm6dsv16x_config *cfg = dev->config;
+ stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
+ struct lsm6dsv16x_data *data = dev->data;
+
+ if (lsm6dsv16x_ah_qvar_raw_get(ctx, &data->qvar_sample) < 0) {
+ LOG_DBG("Failed to read sample");
+ return -EIO;
+ }
+
+ return 0;
+}
+#endif
+
#if defined(CONFIG_LSM6DSV16X_SENSORHUB)
static int lsm6dsv16x_sample_fetch_shub(const struct device *dev)
{
@@ -446,6 +498,11 @@ static int lsm6dsv16x_sample_fetch(const struct device *dev,
case SENSOR_CHAN_DIE_TEMP:
lsm6dsv16x_sample_fetch_temp(dev);
break;
+#endif
+#if defined(CONFIG_LSM6DSV16X_AH_QVAR)
+ case SENSOR_CHAN_QVAR:
+ lsm6dsv16x_sample_fetch_qvar(dev);
+ break;
#endif
case SENSOR_CHAN_ALL:
lsm6dsv16x_sample_fetch_accel(dev);
@@ -453,6 +510,9 @@ static int lsm6dsv16x_sample_fetch(const struct device *dev,
#if defined(CONFIG_LSM6DSV16X_ENABLE_TEMP)
lsm6dsv16x_sample_fetch_temp(dev);
#endif
+#if defined(CONFIG_LSM6DSV16X_AH_QVAR)
+ lsm6dsv16x_sample_fetch_qvar(dev);
+#endif
#if defined(CONFIG_LSM6DSV16X_SENSORHUB)
if (data->shub_inited) {
lsm6dsv16x_sample_fetch_shub(dev);
@@ -579,6 +639,22 @@ static void lsm6dsv16x_gyro_channel_get_temp(struct sensor_value *val,
}
#endif

+#if defined(CONFIG_LSM6DSV16X_AH_QVAR)
+static void lsm6dsv16x_qvar_channel_get(struct sensor_value *val,
+ struct lsm6dsv16x_data *data)
+{
+ int32_t micro_c;
+
+ /* convert units to micro Celsius. Raw temperature samples are
+ * value[mV] = value[LSB] / Qvar_Gain
+ */
+ // micro_c = () / 78;
+ micro_c = lsm6dsv16x_from_lsb_to_mv(data->qvar_sample);
+ val->val1 = micro_c ;
+ val->val2 = micro_c ;
+}
+#endif
+
#if defined(CONFIG_LSM6DSV16X_SENSORHUB)
static inline void lsm6dsv16x_magn_convert(struct sensor_value *val, int raw_val,
uint16_t sensitivity)
@@ -728,6 +804,11 @@ static int lsm6dsv16x_channel_get(const struct device *dev,
lsm6dsv16x_gyro_channel_get_temp(val, data);
break;
#endif
+#if defined(CONFIG_LSM6DSV16X_AH_QVAR)
+ case SENSOR_CHAN_QVAR:
+ lsm6dsv16x_qvar_channel_get(val, data);
+ break;
+#endif
#if defined(CONFIG_LSM6DSV16X_SENSORHUB)
case SENSOR_CHAN_MAGN_X:
case SENSOR_CHAN_MAGN_Y:
diff --git a/drivers/sensor/lsm6dsv16x/lsm6dsv16x.h b/drivers/sensor/lsm6dsv16x/lsm6dsv16x.h
index 2b3819b306..096fada7be 100644
--- a/drivers/sensor/lsm6dsv16x/lsm6dsv16x.h
+++ b/drivers/sensor/lsm6dsv16x/lsm6dsv16x.h
@@ -82,6 +82,9 @@ struct lsm6dsv16x_data {
#if defined(CONFIG_LSM6DSV16X_ENABLE_TEMP)
int16_t temp_sample;
#endif
+#if defined(CONFIG_LSM6DSV16X_AH_QVAR)
+ int16_t qvar_sample;
+#endif
#if defined(CONFIG_LSM6DSV16X_SENSORHUB)
uint8_t ext_data[LSM6DSV16X_SHUB_MAX_NUM_TARGETS][6];
uint16_t magn_gain;
@@ -112,7 +115,8 @@ struct lsm6dsv16x_data {
const struct sensor_trigger *trig_drdy_gyr;
sensor_trigger_handler_t handler_drdy_temp;
const struct sensor_trigger *trig_drdy_temp;
-
+ sensor_trigger_handler_t handler_drdy_qvar;
+ const struct sensor_trigger *trig_drdy_qvar;
#if defined(CONFIG_LSM6DSV16X_TRIGGER_OWN_THREAD)
K_KERNEL_STACK_MEMBER(thread_stack, CONFIG_LSM6DSV16X_THREAD_STACK_SIZE);
struct k_thread thread;
diff --git a/drivers/sensor/lsm6dsv16x/lsm6dsv16x_trigger.c b/drivers/sensor/lsm6dsv16x/lsm6dsv16x_trigger.c
index 52bd7d6c0d..fd018200fb 100644
--- a/drivers/sensor/lsm6dsv16x/lsm6dsv16x_trigger.c
+++ b/drivers/sensor/lsm6dsv16x/lsm6dsv16x_trigger.c
@@ -121,6 +121,9 @@ int lsm6dsv16x_trigger_set(const struct device *dev,
const struct lsm6dsv16x_config *cfg = dev->config;
struct lsm6dsv16x_data *lsm6dsv16x = dev->data;

+ stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
+ lsm6dsv16x_ah_qvar_mode_t mode;
+
if (!cfg->trig_enabled) {
LOG_ERR("trigger_set op not supported");
return -ENOTSUP;
@@ -142,6 +145,19 @@ int lsm6dsv16x_trigger_set(const struct device *dev,
} else {
return lsm6dsv16x_enable_g_int(dev, LSM6DSV16X_DIS_BIT);
}
+ } else if (trig->chan == SENSOR_CHAN_QVAR) {
+ lsm6dsv16x->handler_drdy_qvar = handler;
+ lsm6dsv16x->trig_drdy_qvar = trig;
+ if (handler) {
+ lsm6dsv16x_ah_qvar_mode_get(ctx, &mode);
+ mode.ah_qvar_int_en = LSM6DSV16X_EN_BIT;
+ LOG_INF("lsm6dsv16x_ah_qvar_mode_set %d-%d \n",mode.ah_qvar_en, mode.ah_qvar_int_en );
+ return lsm6dsv16x_ah_qvar_mode_set(ctx, mode);
+ } else {
+ lsm6dsv16x_ah_qvar_mode_get(ctx, &mode);
+ mode.ah_qvar_int_en = LSM6DSV16X_DIS_BIT;
+ return lsm6dsv16x_ah_qvar_mode_set(ctx, mode);
+ }
}

return -ENOTSUP;
@@ -163,8 +179,8 @@ static void lsm6dsv16x_handle_interrupt(const struct device *dev)
LOG_DBG("failed reading status reg");
return;
}
-
- if ((status.drdy_xl == 0) && (status.drdy_gy == 0)) {
+
+ if ((status.drdy_xl == 0) && (status.drdy_gy == 0)&& (status.drdy_qvar == 0)) {
break;
}

@@ -175,7 +191,11 @@ static void lsm6dsv16x_handle_interrupt(const struct device *dev)
if ((status.drdy_gy) && (lsm6dsv16x->handler_drdy_gyr != NULL)) {
lsm6dsv16x->handler_drdy_gyr(dev, lsm6dsv16x->trig_drdy_gyr);
}
-
+
+ if ((status.drdy_qvar) && (lsm6dsv16x->handler_drdy_qvar != NULL)) {
+ // LOG_DBG("lsm6dsv16x_handle_interrupt drdy_qvar");
+ lsm6dsv16x->handler_drdy_qvar(dev, lsm6dsv16x->trig_drdy_qvar);
+ }
}

gpio_pin_interrupt_configure_dt(lsm6dsv16x->drdy_gpio,
diff --git a/include/zephyr/drivers/sensor.h b/include/zephyr/drivers/sensor.h
index 7097236109..d1fdf6eb07 100644
--- a/include/zephyr/drivers/sensor.h
+++ b/include/zephyr/drivers/sensor.h
@@ -189,6 +189,9 @@ enum sensor_channel {
/** Desired charging current in mA */
SENSOR_CHAN_GAUGE_DESIRED_CHARGING_CURRENT,

+ /** Touch Sensor Qvar. */
+ SENSOR_CHAN_QVAR,
+
/** All channels. */
SENSOR_CHAN_ALL,


应用程序部分:

工程配置上打开CONFIG_LSM6DSV16X_AH_QVAR配置,关闭了LIS2MDL,LPS2XDF和LSM6DSO16IS的中断处理。

CONFIG_LOG=y
CONFIG_STDOUT_CONSOLE=y
CONFIG_I2C=y
CONFIG_SENSOR=y
CONFIG_SENSOR_LOG_LEVEL_DBG=y
# CONFIG_LIS2MDL_TRIGGER_OWN_THREAD=y
# CONFIG_LPS2XDF_TRIGGER_OWN_THREAD=y
# CONFIG_LSM6DSO16IS_TRIGGER_OWN_THREAD=y
CONFIG_LSM6DSO16IS_ENABLE_TEMP=y
CONFIG_LSM6DSV16X_TRIGGER_OWN_THREAD=y
CONFIG_LSM6DSV16X_AH_QVAR=y
CONFIG_CBPRINTF_FP_SUPPORT=y

主程序部分

lsm6dsv16x_config配置函数增加Qvar使能和中断使能。

static void lsm6dsv16x_config(const struct device *lsm6dsv16x)
{
struct sensor_value odr_attr, fs_attr, mode_attr;

mode_attr.val1 = 0; /* HP */

if (sensor_attr_set(lsm6dsv16x, SENSOR_CHAN_ACCEL_XYZ,
SENSOR_ATTR_CONFIGURATION, &mode_attr) < 0) {
printk("Cannot set mode for LSM6DSV16X accel\n");
return;
}

/* set LSM6DSV16X accel sampling frequency to 208 Hz */
odr_attr.val1 = 208;
odr_attr.val2 = 0;

if (sensor_attr_set(lsm6dsv16x, SENSOR_CHAN_ACCEL_XYZ,
SENSOR_ATTR_SAMPLING_FREQUENCY, &odr_attr) < 0) {
printk("Cannot set sampling frequency for LSM6DSV16X accel\n");
return;
}

sensor_g_to_ms2(16, &fs_attr);

if (sensor_attr_set(lsm6dsv16x, SENSOR_CHAN_ACCEL_XYZ,
SENSOR_ATTR_FULL_SCALE, &fs_attr) < 0) {
printk("Cannot set full scale for LSM6DSV16X accel\n");
return;
}

/* set LSM6DSV16X gyro sampling frequency to 208 Hz */
odr_attr.val1 = 208;
odr_attr.val2 = 0;

if (sensor_attr_set(lsm6dsv16x, SENSOR_CHAN_GYRO_XYZ,
SENSOR_ATTR_SAMPLING_FREQUENCY, &odr_attr) < 0) {
printk("Cannot set sampling frequency for LSM6DSV16X gyro\n");
return;
}

sensor_degrees_to_rad(250, &fs_attr);

if (sensor_attr_set(lsm6dsv16x, SENSOR_CHAN_GYRO_XYZ,
SENSOR_ATTR_FULL_SCALE, &fs_attr) < 0) {
printk("Cannot set full scale for LSM6DSV16X gyro\n");
return;
}

mode_attr.val1 = 1; /* Enable AH Qvar */

if (sensor_attr_set(lsm6dsv16x, SENSOR_CHAN_QVAR,
SENSOR_ATTR_CONFIGURATION, &mode_attr) < 0) {
printk("Cannot set mode for LSM6DSV16X qvar\n");
return;
}

#ifdef CONFIG_LSM6DSV16X_TRIGGER
// struct sensor_trigger trig;

// trig.type = SENSOR_TRIG_DATA_READY;
// trig.chan = SENSOR_CHAN_ACCEL_XYZ;
// sensor_trigger_set(lsm6dsv16x, &trig, lsm6dsv16x_acc_trig_handler);
struct sensor_trigger trig;

trig.type = SENSOR_TRIG_DATA_READY;
trig.chan = SENSOR_CHAN_QVAR;
sensor_trigger_set(lsm6dsv16x, &trig, lsm6dsv16x_qvar_trig_handler);
#endif
}


main函数主要包含在标准模式下各个传感器的初始化配置,然后进入主循环进行传感器数据访问并通过串口发出,同时检测Qvar的状态判断,发生事件后通过串口进行上报。

int main(void)
{
struct sensor_value lis2mdl_magn[3], lis2mdl_temp, lps22df_press, lps22df_temp;
struct sensor_value lsm6dso16is_xl[3], lsm6dso16is_gy[3];
#ifdef CONFIG_LSM6DSO16IS_ENABLE_TEMP
struct sensor_value lsm6dso16is_temp;
#endif
struct sensor_value lsm6dsv16x_xl[3], lsm6dsv16x_gy[3];

const struct device *const lis2mdl = DEVICE_DT_GET_ONE(st_lis2mdl);
const struct device *const lsm6dso16is = DEVICE_DT_GET_ONE(st_lsm6dso16is);
const struct device *const lsm6dsv16x = DEVICE_DT_GET_ONE(st_lsm6dsv16x);
const struct device *const lps22df = DEVICE_DT_GET_ONE(st_lps22df);
int cnt = 1;

if (!device_is_ready(lsm6dso16is)) {
printk("%s: device not ready.\n", lsm6dso16is->name);
return 0;
}
if (!device_is_ready(lsm6dsv16x)) {
printk("%s: device not ready.\n", lsm6dsv16x->name);
return 0;
}
if (!device_is_ready(lis2mdl)) {
printk("%s: device not ready.\n", lis2mdl->name);
return 0;
}
if (!device_is_ready(lps22df)) {
printk("%s: device not ready.\n", lps22df->name);
return 0;
}

lis2mdl_config(lis2mdl);
lsm6dso16is_config(lsm6dso16is);
lsm6dsv16x_config(lsm6dsv16x);
lps22df_config(lps22df);

while (1) {
/* Get sensor samples */

#ifndef CONFIG_LIS2MDL_TRIGGER
if (sensor_sample_fetch(lis2mdl) < 0) {
printf("LIS2MDL Magn Sensor sample update error\n");
return 0;
}
#endif
#ifndef CONFIG_LSM6DSO16IS_TRIGGER
if (sensor_sample_fetch(lsm6dso16is) < 0) {
printf("LSM6DSO16IS Sensor sample update error\n");
return 0;
}
#endif
#ifndef CONFIG_LSM6DSV16X_TRIGGER
if (sensor_sample_fetch(lsm6dsv16x) < 0) {
printf("LSM6DSV16X Sensor sample update error\n");
return 0;
}
#endif
#ifndef CONFIG_LPS2XDF_TRIGGER
if (sensor_sample_fetch(lps22df) < 0) {
printf("LPS22DF pressure sample update error\n");
return 0;
}
#endif

/* Get sensor data */
sensor_channel_get(lis2mdl, SENSOR_CHAN_MAGN_XYZ, lis2mdl_magn);
sensor_channel_get(lis2mdl, SENSOR_CHAN_DIE_TEMP, &lis2mdl_temp);
sensor_channel_get(lsm6dso16is, SENSOR_CHAN_ACCEL_XYZ, lsm6dso16is_xl);
sensor_channel_get(lsm6dso16is, SENSOR_CHAN_GYRO_XYZ, lsm6dso16is_gy);
#ifdef CONFIG_LSM6DSO16IS_ENABLE_TEMP
sensor_channel_get(lsm6dso16is, SENSOR_CHAN_DIE_TEMP, &lsm6dso16is_temp);
#endif
sensor_channel_get(lsm6dsv16x, SENSOR_CHAN_ACCEL_XYZ, lsm6dsv16x_xl);
sensor_channel_get(lsm6dsv16x, SENSOR_CHAN_GYRO_XYZ, lsm6dsv16x_gy);
#ifdef CONFIG_LSM6DSV16X_AH_QVAR
// sensor_channel_get(lsm6dsv16x, SENSOR_CHAN_QVAR, &lsm6dsv16x_qvar);
#endif
sensor_channel_get(lps22df, SENSOR_CHAN_PRESS, &lps22df_press);
sensor_channel_get(lps22df, SENSOR_CHAN_AMBIENT_TEMP, &lps22df_temp);

/* Display sensor data */

/* Erase previous */
// printf("\0033\014");

// printf("X-NUCLEO-IKS4A1 sensor dashboard\n\n");

/* lis2mdl */
printf("LIS2MDL: Magn (gauss): x: %.3f, y: %.3f, z: %.3f\n",
sensor_value_to_double(&lis2mdl_magn[0]),
sensor_value_to_double(&lis2mdl_magn[1]),
sensor_value_to_double(&lis2mdl_magn[2]));

printf("LIS2MDL: Temperature: %.1f C\n",
sensor_value_to_double(&lis2mdl_temp));

printf("LSM6DSO16IS: Accel (m.s-2): x: %.3f, y: %.3f, z: %.3f\n",
sensor_value_to_double(&lsm6dso16is_xl[0]),
sensor_value_to_double(&lsm6dso16is_xl[1]),
sensor_value_to_double(&lsm6dso16is_xl[2]));

printf("LSM6DSO16IS: Gyro (dps): x: %.3f, y: %.3f, z: %.3f\n",
sensor_value_to_double(&lsm6dso16is_gy[0]),
sensor_value_to_double(&lsm6dso16is_gy[1]),
sensor_value_to_double(&lsm6dso16is_gy[2]));
#ifdef CONFIG_LSM6DSO16IS_ENABLE_TEMP /* temperature */
printf("LSM6DSO16IS: Temperature: %.1f C\n",
sensor_value_to_double(&lsm6dso16is_temp));
#endif

printf("LSM6DSV16X: Accel (m.s-2): x: %.3f, y: %.3f, z: %.3f\n",
sensor_value_to_double(&lsm6dsv16x_xl[0]),
sensor_value_to_double(&lsm6dsv16x_xl[1]),
sensor_value_to_double(&lsm6dsv16x_xl[2]));

printf("LSM6DSV16X: GYro (dps): x: %.3f, y: %.3f, z: %.3f\n",
sensor_value_to_double(&lsm6dsv16x_gy[0]),
sensor_value_to_double(&lsm6dsv16x_gy[1]),
sensor_value_to_double(&lsm6dsv16x_gy[2]));
#ifdef CONFIG_LSM6DSV16X_AH_QVAR
// TOUCH_IDLE = 0x0,
// TOUCH_QP_ACTIVE = 0x1,
// TOUCH_QN_ACTIVE = 0x2,
// TOUCH_QP2N_ACTIVE = 0x3,
// TOUCH_QN2P_ACTIVE = 0x4
//
if(check_result != TOUCH_IDLE){
if(check_result==TOUCH_QP_ACTIVE){
printf("TOUCH_QP_ACTIVE\n");
}
else if(check_result==TOUCH_QN_ACTIVE){
printf("TOUCH_QN_ACTIVE\n");
}
else if(check_result==TOUCH_QP2N_ACTIVE){
printf("TOUCH_QP2N_ACTIVE\n");
}
else if(check_result==TOUCH_QN2P_ACTIVE){
printf("TOUCH_QN2P_ACTIVE\n");
}
check_result = TOUCH_IDLE;
}
#endif
printf("LPS22DF: Temperature: %.1f C\n", sensor_value_to_double(&lps22df_temp));
printf("LPS22DF: Pressure:%.3f kpa\n", sensor_value_to_double(&lps22df_press));

cnt++;
k_sleep(K_MSEC(100));
}
}


5 上位机实现

上位机代码通过Python实现,代码使用Python中的Tkinter库构建了一个GUI应用程序,用于实时显示和控制通过串口接收的多个传感器数据。程序通过线程处理串口数据的接收和解析,并根据特定命令来控制传感器的状态和显示。界面包括多个传感器标签,可以通过命令切换高亮显示的传感器和控制传感器的开关状态。

import tkinter as tk
import serial
import threading

# Define sensor identifiers
sensor_identifiers = [
    'LIS2MDL: Magn (gauss)',
    'LIS2MDL: Temperature:',
    'LSM6DSO16IS: Accel (m.s-2):',
    'LSM6DSO16IS: Gyro (dps):',
    'LSM6DSO16IS: Temperature:',
    'LSM6DSV16X: Accel (m.s-2):',
    'LSM6DSV16X: GYro (dps):',
    'LPS22DF: Temperature:',
    'LPS22DF: Pressure:'
]

sensor_sw = [1,1,1,1,1,1,1,1,1]

        # // {
        # // TOUCH_IDLE = 0x0,
        # // TOUCH_QP_ACTIVE = 0x1,
        # // TOUCH_QN_ACTIVE = 0x2,
        # // TOUCH_QP2N_ACTIVE = 0x3,
        # // TOUCH_QN2P_ACTIVE = 0x4,
        # // } touch_mode_t;
cmd_list =      [
         'TOUCH_QP_ACTIVE' ,
         'TOUCH_QN_ACTIVE' ,
         'TOUCH_QP2N_ACTIVE' ,
         'TOUCH_QN2P_ACTIVE' ,
         ]
class SensorDisplayApp:
    def __init__(self, root):
        self.root = root
        self.root.title("Sensor Display")
       
        # Initialize UI components
        self.sensor_labels = []
        for i in range(9):
            label = tk.Label(self.root, text=f"Sensor {i+1}", padx=20, pady=20, relief=tk.RAISED)
            label.grid(row=i // 3, column=i % 3, padx=5, pady=5)
            self.sensor_labels.append(label)
       
        # Serial communication setup
        self.serial_port = serial.Serial('COM28', 115200, timeout=1)  # Update with your port and baud rate
       
        # Thread for receiving and processing data
        self.receive_thread = threading.Thread(target=self.receive_data_thread, daemon=True)
        self.receive_thread.start()
       
        # Highlight the first sensor by default
        self.current_highlighted = 0
        self.highlight_sensor(self.current_highlighted)
       
    def receive_data_thread(self):
        while True:
            if self.serial_port.in_waiting > 0:
                data = self.serial_port.readline().decode().strip()
                # Parse received data (assuming it's in a certain format)
                # try:
                #     sensor_index, sensor_value = map(int, data.split(':'))
                #     if 1 <= sensor_index <= 9:
                #         # Update corresponding sensor label with value
                #         self.sensor_labels[sensor_index - 1].config(text=f"Sensor {sensor_index}: {sensor_value}")
                # except ValueError:
                #     print(f"Invalid data received: {data}")
                # Process each line in the data
                print(f"data received: {data}")
                for line in data.strip().split('\n'):
                    for sensor_index in range(len(sensor_identifiers)):
                        if sensor_sw[sensor_index] == 0:
                            self.sensor_labels[sensor_index].config(text=f"XXX")
                            continue
                        if sensor_identifiers[sensor_index] in line:
                            self.sensor_labels[sensor_index - 1].config(text=f"Sensor {sensor_index}: {line}")
                            break  # Move to the next line after finding a match
                        # self.sensor_labels[sensor_index].config(text=f"Sensor {sensor_index}") # Test sensor sw
                    for sensor_index in range(len(cmd_list)):
                        if cmd_list[sensor_index] in line:
                            self.process_command(cmd_list[sensor_index])
    def highlight_sensor(self, index):
        # Highlight the selected sensor
        for i, label in enumerate(self.sensor_labels):
            if i == index:
                label.config(bg='yellow')  # Highlighted color
            else:
                label.config(bg='white')   # Normal color
   
    def process_command(self, command):
        if command == "TOUCH_QP2N_ACTIVE":
            self.current_highlighted = (self.current_highlighted - 1) % 9
            self.highlight_sensor(self.current_highlighted)
        elif command == "TOUCH_QN2P_ACTIVE":
            self.current_highlighted = (self.current_highlighted + 1) % 9
            self.highlight_sensor(self.current_highlighted)
        elif command == "TOUCH_QN_ACTIVE":
            sensor_sw[self.current_highlighted] = 0
        elif command == "TOUCH_QP_ACTIVE":
            sensor_sw[self.current_highlighted] = 1

if __name__ == "__main__":
    root = tk.Tk()
    app = SensorDisplayApp(root)
    root.mainloop()



6 项目总结

本次项目集成了Nucleo G0B1RE开发板、X-NUCLEO-IKS4A1扩展板,实现了触摸按键,识别点按和左右滑动,来对传感器进行选择和切换,并将数据发送到上位机一开始由于市电的干扰导致Qvar一直不能正常识别,后来通过加入滤波算法以及优化识别算法实现了也可以在接入市电电脑上的识别。本项目使用的是软件运行的算法来进行对Qvar触摸的识别,LSM6DSV16X也支持运行的FSM有限状态机来进行识别,后面有机会可以再尝试下这种方式。

参考链接

  1. ST Nucleo G0B1RE — Zephyr Project Documentation
  2. X-NUCLEO-IKS4A1: MEMS Inertial and Environmental Multi sensor shield — Zephyr Project Documentation
  3. Qvar 感应 - 应用笔记 (st.com.cn)
附件下载
0001-Funpack3-3.patch
zephyr.bin
standard_qvar.zip
ui.py
团队介绍
1
团队成员
maskmoo
评论
0 / 100
查看更多
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号