STM32C5开发LSM6DSV80X .10--FIFO 水位中断与六轴数据读取概述视频教学样品申请源码下载硬件准备参考程序串口配置IIC配置CS和SA0设置INT设置生成项目导入STM32CubeIDE设置工程编码串口 printf 重定向参考程序CMake 编译配置驱动接口与中断回调配置传感器接口初始化与设备检查配置中断引脚的输出方式设置加速度计与陀螺仪量程配置滤波器稳定阶段的信号屏蔽配置加速度计与陀螺仪低通滤波器清空 FIFO 并设置水位阈值配置 FIFO 数据入队速率将 FIFO 水位中断路由到 INT1开启 FIFO 连续模式启动加速度计与陀螺仪主循环演示.10–FIFO 水位中断与六轴数据读取)概述本章介绍如何使用 STM32C542CCT6 驱动六轴传感器通过 FIFO 水位中断实现三轴加速度和三轴角速度数据的批量读取。六轴传感器内置 FIFO可缓存加速度计和陀螺仪数据当 FIFO 中未读记录达到设定水位阈值时传感器通过 INT1 产生中断通知 STM32。主循环收到中断后读取 FIFO 状态和未读记录数量检查是否发生溢出再逐条读取 FIFO 记录根据 TAG 区分加速度和角速度数据完成单位换算、累加和平均值输出。视频教学样品申请https://www.wjx.top/vm/OhcKxJk.aspx#源码下载硬件准备首先需要准备一个开发板这里我准备的是自己绘制的开发板需要的可以进行申请。主控为STM32C542CCT6陀螺仪为LSM6DSV80X磁力计为LIS2MDL。参考程序https://github.com/CoreMaker-lab/STM32C542_LSM6DSV80X_LIS2MDLhttps://gitee.com/CoreMaker/STM32C542_LSM6DSV80X_LIS2MDL串口配置查看原理图PA9和PA10设置为开发板的串口。选择 Connectivity → USART1设置为 Async 异步模式波特率 115200、8 位数据、无校验、1 位停止位启用收发模式。串口的TX、RX 分别选择 PA9、PA10均设置为复用模式Alternate、无上下拉、推挽输出速度选择 Low。IIC配置STM32C542CCT6通过扩展接口与传感器模块连接其中PB6作为I²C时钟线SCLPB7作为I²C数据线SDA首先在 STM32CubeMX2 中进入 Peripherals 外设配置页面在 Connectivity 分类中选择 I2C1并将 I2C1 外设使能。如图所示本实验的 I2C1 配置步骤如下点击左侧 Peripherals 外设配置入口在 Connectivity 分类中选择 I2C1将 I2C1 Mode 设置为 I2C并确认 I2C1 显示为 ActiveSpeed frequency 设置为 Fast mode - 400 kHz确认 I2C1 的 GPIO 引脚映射SCL 为 PB6SDA 为 PB7CS和SA0设置确认I2C 模式选择和地址选择CS 需要处于 I2C 模式对应电平SA0 需要固定为高电平或低电平将 PA1 和 PB2 配置为普通 GPIO。在 Pinout 页面, set PA1 and PB2 to GPIO → Configured, used for SA0 (I²C address selection) respectively和 CS通信接口选择。进入 System → GPIO将 PA1、PB2 设置为推挽输出、无上下拉、低速由程序控制输出电平。板载 LIS2MDL 的 CS 引脚连接 PA6。在 Pinout 页面将 PA6 设置为 GPIO → Configured。进入 System → GPIO将 PA6 命名为 CS2设置为推挽输出、无上下拉、低速由程序控制输出电平。INT设置IMU 的 INT1Pin4连接 STM32C542 的 PB0用于接收陀螺仪数据就绪中断。将 PB0 配置为输入、无上下拉开启 EXTI0选择下降沿触发Falling edge使能中断及 IRQ Handler 生成。生成项目修改配置后左下角会提示 Click to save需要先保存当前工程配置点击左侧 Project settings进入工程生成设置页面在 IDE Project Generation 中选择工程格式和工具链本例选择 CMake GCC然后点击 Generate IDE project 生成工程导入STM32CubeIDE打开 STM32CubeIDE选择 File → Import导入 STM32CubeMX2 生成的 CMake 工程。展开 Import STM32 Project选择 STM32 CMake Project点击 Next进入工程路径选择页面。Project name填写导入到 STM32CubeIDE 中显示的工程名称Source directory选择 STM32CubeMX2 生成的 CMake 工程目录点击 Next继续完成工程导入Toolchain选择 MCU ARM GCC表示使用 ARM GCC 工具链进行编译MCU确认芯片型号为 STM32C542CCTx与前面 STM32CubeMX2 中选择的 MCU 保持一致CPU/Core确认内核为 Cortex-M33Core 为 0点击 Finish完成 CMake 工程导入设置工程编码在 Project Explorer 中选中工程点击 Project → Properties。选择 Resource将 Text file encoding 设置为 Other → GBK点击 Apply and Close 保存。串口 printf 重定向在 main.c 中添加以下头文件并在 main() 前实现 _write()将 printf() 输出重定向到 USART1用于打印 MEMS 数据和调试信息。#includemx_usart1.h#includestdio.h#includestring.hint_write(intfile,char*ptr,intlen){hal_uart_handle_t*huart1mx_usart1_uart_gethandle();if(huart1!NULL){HAL_UART_Transmit(huart1,ptr,len,1000);}returnlen;}参考程序https://github.com/STMicroelectronics/lsm6dsv80x-pid添加对应驱动程序。CMake 编译配置在 CMakeLists.txt 中将 .c 加入编译并添加 -u _printf_float启用 printf() 浮点输出支持。target_sources(${CMAKE_PROJECT_NAME}PRIVATE lsm6dsv80x_reg.c)target_link_options(${CMAKE_PROJECT_NAME}PUBLIC-u _printf_float)驱动接口与中断回调配置在 main.c 中引入 .h并定义启动等待时间 BOOT_TIME 为 10 ms、读取ID。变量 whoamI 用于保存读取到的设备 IDdev_ctx 用于绑定底层通信接口thread_wake 用于标记中断事件。声明 platform_write()、platform_read() 和 platform_delay()分别实现寄存器写入、读取及延时。同时通过 _write() 将 printf() 重定向至 USART1方便输出检测结果。在外部中断回调 HAL_EXTI_TriggerCallback() 中判断中断是否来自 GPIO EXTI0若匹配则将 thread_wake 置为 1。主循环随后读取传感器状态判断是否发生事件。回调中仅设置标志位寄存器读取和串口输出均在主循环中完成。#includelsm6dsv80x_reg.h#defineBOOT_TIME10U#defineBOARD_EXPECTED_IDLSM6DSV80X_ID#defineFIFO_WATERMARK128Ustaticstmdev_ctx_tdev_ctx;staticvolatileuint8_tthread_wake0;staticint32_tplatform_write(void*handle,uint8_treg,constuint8_t*bufp,uint16_tlen);staticint32_tplatform_read(void*handle,uint8_treg,uint8_t*bufp,uint16_tlen);staticvoidplatform_delay(uint32_tms);/** * brief EXTI trigger callback */voidHAL_EXTI_TriggerCallback(hal_exti_handle_t*hexti,hal_exti_trigger_ttrigger){(void)trigger;if(HAL_EXTI_GetInstance(hexti)HAL_EXTI_GPIO_0){thread_wake1;}}传感器接口初始化与设备检查系统初始化完成后设置 CS1、CS2 为高电平SA0 为低电平并将寄存器读写函数、延时函数及 I²C1 句柄绑定到 dev_ctx建立传感器通信接口。等待 10 ms 后读取并通过串口打印设备 ID。当 ID 不匹配 时程序停止运行检查通过后执行软件复位并再次等待 10 ms随后使能 BDU块数据更新为后续检测配置做好准备。printf(HELLO\r\n);HAL_GPIO_WritePin(CS1_PORT,CS1_PIN,HAL_GPIO_PIN_SET);HAL_GPIO_WritePin(SA0_PORT,SA0_PIN,HAL_GPIO_PIN_RESET);HAL_GPIO_WritePin(CS2_PORT,CS2_PIN,HAL_GPIO_PIN_SET);uint8_twhoamI0;lsm6dsv80x_if_cfg_tif_cfg{0};lsm6dsv80x_pin_int_route_tpin_int{0};lsm6dsv80x_filt_settling_mask_tfilt_settling_mask{0};/* Initialize driver interface. */dev_ctx.write_regplatform_write;dev_ctx.read_regplatform_read;dev_ctx.mdelayplatform_delay;dev_ctx.handlemx_i2c1_i2c_gethandle();platform_delay(BOOT_TIME);/* Check device ID. */lsm6dsv80x_device_id_get(dev_ctx,whoamI);printf(LSM6DSV80X_ID0x%x,id0x%x\r\n,(unsignedint)BOARD_EXPECTED_ID,(unsignedint)whoamI);if(whoamI!BOARD_EXPECTED_ID)while(1);/* Reset device. */lsm6dsv80x_reset_set(dev_ctx,LSM6DSV80X_GLOBAL_RST);platform_delay(BOOT_TIME);/* Enable Block Data Update. */lsm6dsv80x_block_data_update_set(dev_ctx,PROPERTY_ENABLE);配置中断引脚的输出方式根据数据手册IF_CFG 寄存器中的 PP_OD 和 H_LACTIVE 用于设置中断引脚的输出类型和有效电平。代码先读取寄存器修改这两个位后再写回保留其他配置。配置完成后INT1 在中断有效时拉低未触发时通过上拉保持高电平。因此MCU 对应引脚需要配置上拉并采用下降沿触发 EXTI。/* INT1: open-drain active low. */lsm6dsv80x_read_reg(dev_ctx,LSM6DSV80X_IF_CFG,(uint8_t*)if_cfg,1);if_cfg.pp_od1;if_cfg.h_lactive1;lsm6dsv80x_write_reg(dev_ctx,LSM6DSV80X_IF_CFG,(uint8_t*)if_cfg,1);设置加速度计与陀螺仪量程将 Low-G 加速度计量程设置为 ±2 g陀螺仪量程设置为 ±2000 dps。/* Set full scales before starting the sensors. */lsm6dsv80x_xl_full_scale_set(dev_ctx,LSM6DSV80X_2g);lsm6dsv80x_gy_full_scale_set(dev_ctx,LSM6DSV80X_2000dps);配置滤波器稳定阶段的信号屏蔽传感器启动后内部滤波器需要一定时间才能进入稳定状态。这段代码配置稳定阶段的信号屏蔽减少启动瞬态对数据就绪通知和嵌入式功能运行的影响。/* Mask>.drdyPROPERTY_ENABLE;filt_settling_mask.irq_xlPROPERTY_ENABLE;filt_settling_mask.irq_gPROPERTY_ENABLE;lsm6dsv80x_filt_settling_mask_set(dev_ctx,filt_settling_mask);配置加速度计与陀螺仪低通滤波器分别开启陀螺仪的 LPF1 低通滤波器和加速度计的 LPF2 低通滤波器用于抑制高频变化使输出数据更加平滑。/* Configure accelerometer and gyroscope filters. */lsm6dsv80x_filt_gy_lp1_set(dev_ctx,PROPERTY_ENABLE);lsm6dsv80x_filt_gy_lp1_bandwidth_set(dev_ctx,LSM6DSV80X_GY_ULTRA_LIGHT);lsm6dsv80x_filt_xl_lp2_set(dev_ctx,PROPERTY_ENABLE);lsm6dsv80x_filt_xl_lp2_bandwidth_set(dev_ctx,LSM6DSV80X_XL_STRONG);清空 FIFO 并设置水位阈值这段代码先将 FIFO 设置为 Bypass 旁路模式清空已有缓存再设置 FIFO 水位阈值为后续采集建立确定的初始状态。FIFO 工作模式由 FIFO_CTRL40Ah中的 FIFO_MODE 字段控制水位阈值由 FIFO_CTRL107h中的 WTM 字段控制。本例 FIFO_WATERMARK 设置为 128表示 FIFO 中未读取的数据达到 128 条记录时水位条件成立。每条记录包含一个数据类型标签 TAG 和 6 字节数据可以是一组三轴加速度也可以是一组三轴角速度。/* Bypass clears FIFO. Watermark counts TAG 6-byte records, not pairs of accelerometer and gyroscope samples. */lsm6dsv80x_fifo_mode_set(dev_ctx,LSM6DSV80X_BYPASS_MODE);lsm6dsv80x_fifo_watermark_set(dev_ctx,FIFO_WATERMARK);配置 FIFO 数据入队速率这段代码设置 Low-G 加速度计和陀螺仪的数据以 各 60 Hz 的速率写入 FIFO。这里设置的是 FIFO 入队速率传感器本身的输出数据率还需要通过后面的启动代码单独配置。为了持续提供数据传感器输出数据率应不低于所选入队速率。/* Batch Low-G accelerometer and gyroscope at 60 Hz each. */lsm6dsv80x_fifo_xl_batch_set(dev_ctx,LSM6DSV80X_XL_BATCHED_AT_60Hz);lsm6dsv80x_fifo_gy_batch_set(dev_ctx,LSM6DSV80X_GY_BATCHED_AT_60Hz);将 FIFO 水位中断路由到 INT1驱动通过配置 INT1_CTRL0Dh寄存器中的 INT1_FIFO_TH 位使 FIFO 达到水位阈值时能够通知 MCU。FIFO 水位中断由缓存中的数据数量决定不需要使用前面运动事件章节中的基本中断锁存配置。仅仅读取 FIFO 状态寄存器不会解除水位条件需要取出数据使未读取记录数降到水位阈值以下INT1 才能恢复非有效状态。/* Route FIFO watermark to INT1. No basic-event latch is needed. */pin_int.fifo_thPROPERTY_ENABLE;lsm6dsv80x_pin_int1_route_set(dev_ctx,pin_int);开启 FIFO 连续模式这段代码将 FIFO 设置为 Stream 连续模式对应 FIFO_CTRL4 寄存器中 FIFO_MODE 字段的连续模式配置。在该模式下新产生的数据持续写入 FIFO。达到水位阈值后FIFO 仍然继续接收数据MCU 可以在收到中断后批量读取。如果 MCU 没有及时读取导致 FIFO 空间耗尽新的数据会覆盖最早的未读取数据并产生溢出标志。因此主循环需要及时处理水位中断并检查是否发生溢出。/* Continuous FIFO: oldest unread data is overwritten when full. */lsm6dsv80x_fifo_mode_set(dev_ctx,LSM6DSV80X_STREAM_MODE);启动加速度计与陀螺仪完成量程、滤波器、FIFO 和中断配置后这段代码启动 Low-G 加速度计和陀螺仪将两者都设置为 60 Hz 输出数据率、高性能模式。/* Start Low-G accelerometer and gyroscope at 60 Hz. */lsm6dsv80x_xl_mode_set(dev_ctx,LSM6DSV80X_XL_HIGH_PERFORMANCE_MD);lsm6dsv80x_xl_data_rate_set(dev_ctx,LSM6DSV80X_ODR_AT_60Hz);lsm6dsv80x_gy_mode_set(dev_ctx,LSM6DSV80X_GY_HIGH_PERFORMANCE_MD);lsm6dsv80x_gy_data_rate_set(dev_ctx,LSM6DSV80X_ODR_AT_60Hz);主循环主循环检测到 thread_wake 置位后读取 FIFO 状态和未读记录数量并检查是否发生溢出。随后逐条读取数据根据 TAG 区分加速度和角速度完成单位换算与累加最后通过串口输出本批次的样本数量和三轴平均值。while(1){if(thread_wake){lsm6dsv80x_fifo_status_tstatus{0};floatxl_sum[3]{0};floatgy_sum[3]{0};uint16_txl_count0;uint16_tgy_count0;uint16_tnum;thread_wake0;/* Reading status alone does not clear a watermark condition. */if(lsm6dsv80x_fifo_status_get(dev_ctx,status)!0){thread_wake1;platform_delay(100);continue;}if(status.fifo_ovr)printf(FIFO overrun: samples lost\r\n);numstatus.fifo_level;/* Read the records already present in this FIFO snapshot. */while(num0){lsm6dsv80x_fifo_out_raw_tfifo_data{0};int16_traw[3];if(lsm6dsv80x_fifo_out_raw_get(dev_ctx,fifo_data)!0){thread_wake1;platform_delay(100);break;}num--;/* Decode little-endian axes without unaligned pointers. */raw[0](int16_t)((uint16_t)fifo_data.data[0]|((uint16_t)fifo_data.data[1]8));raw[1](int16_t)((uint16_t)fifo_data.data[2]|((uint16_t)fifo_data.data[3]8));raw[2](int16_t)((uint16_t)fifo_data.data[4]|((uint16_t)fifo_data.data[5]8));switch(fifo_data.tag){caseLSM6DSV80X_XL_NC_TAG:xl_sum[0]lsm6dsv80x_from_fs2_to_mg(raw[0]);xl_sum[1]lsm6dsv80x_from_fs2_to_mg(raw[1]);xl_sum[2]lsm6dsv80x_from_fs2_to_mg(raw[2]);xl_count;break;caseLSM6DSV80X_GY_NC_TAG:gy_sum[0]lsm6dsv80x_from_fs2000_to_mdps(raw[0]);gy_sum[1]lsm6dsv80x_from_fs2000_to_mdps(raw[1]);gy_sum[2]lsm6dsv80x_from_fs2000_to_mdps(raw[2]);gy_count;break;default:break;}}/* Print only after reading the batch to limit UART overhead. * These are batch averages, not individual FIFO samples. */if(xl_count0){printf(XL avg (%u) [mg]: %.2f, %.2f, %.2f\r\n,(unsignedint)xl_count,(double)(xl_sum[0]/xl_count),(double)(xl_sum[1]/xl_count),(double)(xl_sum[2]/xl_count));}if(gy_count0){printf(GY avg (%u) [mdps]: %.2f, %.2f, %.2f\r\n\r\n,(unsignedint)gy_count,(double)(gy_sum[0]/gy_count),(double)(gy_sum[1]/gy_count),(double)(gy_sum[2]/gy_count));}/* New samples arrived during reads/printing. If still above * watermark, keep servicing: INT1 may remain active low and * no additional falling edge would be generated. */if(lsm6dsv80x_fifo_status_get(dev_ctx,status)!0){thread_wake1;platform_delay(100);}elseif(status.fifo_th){thread_wake1;}}}演示程序运行后FIFO 达到 128 条记录时触发中断主循环批量读取加速度和角速度数据并输出各自的样本数量及三轴平均值。