在定时器中断中处理多通道数据采集

原创 云深之无迹 2025-01-02 07:15
在做这个多通道的数据采集的时候,数据的处理是个难点,如果有蓝牙或者BLE做媒介的时候就更难搞了。
我平时喜欢定时器中断做处理。   一般定时器中断适合的场景是:
需要定时触发:任务必须在固定时间间隔内执行。
实时性要求高:任务需要精确控制时间,不能有太多延迟。
轻量级处理:中断中执行的任务应尽量短小,复杂任务可以通过信号量或标志位移到主循环中完成。
中断虽好,但是不要贪杯哦~
如果 ISR 执行时间过长,可能会导致错过下一个中断(特别是在高频率触发的情况下)。在这种情况下,需要检查中断的处理效率。
给一个demo:
#include "stm32f4xx_hal.h"
// 定义一个标志变量volatile uint8_t timer_flag = 0;
// 定时器中断回调函数void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if (htim->Instance == TIM2) { // 判断是否是定时器2 timer_flag = 1; // 设置标志,表示定时器触发 }}
int main(void) { HAL_Init(); // 初始化 HAL 库 SystemClock_Config(); // 配置系统时钟 MX_TIM2_Init(); // 初始化定时器2
HAL_TIM_Base_Start_IT(&htim2); // 启动定时器并使能中断
while (1) { if (timer_flag) { // 检查标志是否被设置 timer_flag = 0; // 清除标志 // 定时触发的代码 HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5); // 切换 LED 状态 } }}

定时器来了,一般是来了一个小小的好算的数

比如1S来一次,中断内部有个数值来记录进入的次数。一次又一次,我们就可以判断次数来执行代码。

HAL库启动以前需要手动开启IT一次,后面就在执行触发代码前要清楚我们的标志,这个标志是一个全局变量,作用就是提醒这次的代码。
检查中断频率:确保定时器频率(中断周期)合适,不要让 ISR 执行时间过长。
在中断中设置标志或将任务放入队列,在主循环中处理,从而避免中断中运行复杂代码。
合理分配中断优先级,避免多个中断之间互相影响。这些是我给的编写中断的建议。
下面这个代码就是一个中断函数,但是比较典型。典型在这个中断函数太长了,所以在最下面有一些整改建议。
  1. 定时触发 ADC 数据读取:读取 4 个通道的 ADC 数据并进行累加和平均处理。
  2. 滤波处理:对采集的 ADC 数据进行滤波,包括 50Hz陷波滤波和 IIR 滤波。
  3. 数据打包与发送:将处理后的数据以 BLE(蓝牙低功耗)数据包格式进行封装,并通过 DMA 发送。
  4. CRC 校验:为每个通道的封装数据生成校验码,确保数据完整性。
一般就是要使用一个定时器

判断是否是定时器 2 触发了中断。如果是,则执行后续逻辑。因为是按时进去,那每一次进来都会记一下,然后就可以实现比如5ms,10ms,15ms执行任务。

增加 tim_counter,用于控制每隔 4 次中断保存一次数据。

定时保存数据:
  • 每 4 次中断(tim_counter == 3)将处理后的 ADC 数据保存到 BLE_Packet_to_Send。
  • ADC_Sample_Counter 递增,记录当前采样次数。
满数据发送:
  • 当采样次数达到 DMA_MAX_SEND 时,生成 CRC 校验值。
  • 调用 HAL_UART_Transmit_DMA 通过 UART 的 DMA 模块发送封装的数据。

线保存在BLE的封包里面,当封包里面的DMA满了,就直接使用UART穿出去,这个代码框架可以当做一个模板使用。

我也一直在学习,编程的时候我们在关注什么?我回答是其实是数据。外设都是固定的,无非也是抽象的读写。

但是数据却是我们一直关注,一个数据来了,它是什么样的?现在完整了吗?接下来要干嘛?给下一级?下一级要什么样的?应该怎么修改?其实都是对数据做操作而已。

赶紧进来获得来自ADC的数据

调用 AD7682_Read_4_ADC_Value 采集 ADC 的 4 通道数据,并累加两次。
sum_1[] 是 4 个通道的累加值。

将累加的值右移 1 位(除以 2),得到平均值。
sum_1[] 清零,为下一次累加做准备。

然后滤波

IIR_50HZ_Norch_Filter 是一个 50Hz 陷波滤波器,去除电源噪声。
applyIIRFilter 是一个通用的 IIR 滤波器,对数据进一步平滑处理。

将 16 位 ADC 数据分解成两个 8 位字节,便于通过 BLE 通信协议传输(BLE 通信通常以字节为单位传输数据)。

这是完整的一组

ADC_Value_Receive_1[ADC_Sample_Counter * 2] = (uint8_t)((ADC_Value_u16[2] & 0XFF00) >> 8);ADC_Value_u16[2]
是一个 16 位(uint16_t)的 ADC 数据。
  1. & 0xFF00:保留高 8 位(清除低 8 位)。
  2. >> 8:将高 8 位右移到低 8 位的位置。
  3. (uint8_t):将 16 位数据截断为 8 位(高字节)。
  4. ADC_Sample_Counter * 2:表示数据在 ADC_Value_Receive_1 数组中的位置。
  5. 存储结果:将高 8 位存入 ADC_Value_Receive_1 的当前位置。
ADC_Value_Receive_1[ADC_Sample_Counter * 2 + 1] = (uint8_t)(ADC_Value_u16[2] & 0X00FF);& 0x00FF
  1. 保留低 8 位(清除高 8 位)。
  2. (uint8_t):将低 8 位数据截断为 8 位(低字节)。
  3. ADC_Sample_Counter * 2 + 1:表示数据在 ADC_Value_Receive_1 数组中的位置。
  4. 存储结果:将低 8 位存入 ADC_Value_Receive_1 的下一个位置。

详细分析

BLE_Packet_to_Send[Channel1_Data_Start_Counter + ADC_Sample_Counter * 2] = ADC_Value_Receive_1[ADC_Sample_Counter * 2];
  1. 从 ADC_Value_Receive_1 中读取高字节数据。
  2. 将高字节数据写入 BLE_Packet_to_Send 数组的对应位置。
  3. Channel1_Data_Start_Counter:是 Channel 1 数据在 BLE_Packet_to_Send 中的起始位置。
  4. ADC_Sample_Counter * 2:计算当前样本的偏移。
BLE_Packet_to_Send[Channel1_Data_Start_Counter + ADC_Sample_Counter * 2 + 1] = ADC_Value_Receive_1[ADC_Sample_Counter * 2 + 1];
  1. 从 ADC_Value_Receive_1 中读取低字节数据。
  2. 将低字节数据写入 BLE_Packet_to_Send 数组的对应位置。
  3. 数据偏移计算同上,但存储到紧接的位置。

从MPU6050看传感器原始数据的处理方式-位运算  看不懂?就先复习一下我的文章。

清醒一点,我们的要求就是对原始数据重新塑造然后传到下个封包里面。

分解 16 位数据:
ADC_Value_u16[2] 分解为两个 8 位字节,高位和低位分别存储到 ADC_Value_Receive_1 数组中。
组织 BLE 数据包,将高字节和低字节从:
ADC_Value_Receive_1 
复制到 BLE_Packet_to_Send 数组中,准备通过 BLE 发送。

可以封装一个函数:

void Pack_ADC_Data(uint16_t adc_value, uint8_t *receive_buffer, uint8_t *ble_buffer, int sample_counter, int start_counter) {    receive_buffer[sample_counter * 2] = (uint8_t)((adc_value & 0xFF00) >> 8);    receive_buffer[sample_counter * 2 + 1] = (uint8_t)(adc_value & 0x00FF);    ble_buffer[start_counter + sample_counter * 2] = receive_buffer[sample_counter * 2];    ble_buffer[start_counter + sample_counter * 2 + 1] = receive_buffer[sample_counter * 2 + 1];}

调用:

Pack_ADC_Data(ADC_Value_u16[2]ADC_Value_Receive_1BLE_Packet_to_SendADC_Sample_CounterChannel1_Data_Start_Counter);

CRC和发送

什么时候不适合使用中断?

任务执行时间过长:如果中断内代码耗时过长,会影响其他任务的执行,甚至导致系统崩溃。
如不需要精确时间间隔,主循环或事件触发可能是更好的选择。
中断优先级竞争:当系统中有多个高优先级中断时,可能导致定时器中断被延迟。

然后就批判一下上面这个函数,

第一杀,多次调用函数:
AD7682_Read_4_ADC_Value():多次调用,可能涉及 SPI/I²C 等通信操作,通常较耗时。
这个ADC我还写过:精密小体积ADC-AD7682 16位4通道
IIR_50HZ_Norch_Filter() 和 applyIIRFilter():滤波运算较为复杂,尤其是涉及浮点或大量运算时。
crc16_ibm():用于生成 CRC 校验,处理大量数据时会占用 CPU 资源。可以使用外设CRC好一些。
第二杀,复杂数据处理:采集的 ADC 数据进行求和、平均计算,以及数据封包。BLE 数据填充与打包。  需要大量的执行计算过程来分发新包。
怎么做?
其实很简单,就是剥离耗时操作。

在中断中只执行简单的采样操作,将数据存入缓冲区。主循环读取缓冲区的数据并进行滤波、平均计算和 BLE 打包。
还有什么呢?就是这个频率问题,因为不同的函数,不同的外设,大家的步调不一样
ADC 采样频率过高:如果定时器触发频率较高(如 1 ms),而 BLE 数据发送的需求较低(如每 10 ms),会导致不必要的数据处理。除非你有一个缓存区存这个东西,还要发的快。
BLE 打包延迟:BLE 通信的频率通常低于 ADC 采样频率。如果在每次中断中都执行 BLE 打包,会导致发送延迟和资源浪费。

然后,中断中只采样 ADC 数据并存入一个环形缓冲区。在中断中设置标志位,主循环中根据标志位执行滤波和通信操作。其实就是在较长的时间后开始处理数据。

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {    if (htim == &htim2) {        // 简单采样操作        AD7682_Read_4_ADC_Value(ADC_Value_Buffer);        // 将数据存入环形缓冲区        ring_buffer_write(&adc_ring_buffer, ADC_Value_Buffer);        // 设置标志位        data_ready_flag = 1;    }}

然后写个循环代码:

void main_loop() {    while (1) {        if (data_ready_flag) {            data_ready_flag = 0;
// 从环形缓冲区读取数据 ring_buffer_read(&adc_ring_buffer, Processed_Data);
// 执行滤波操作 IIR_50HZ_Norch_Filter(Processed_Data, Filtered_Data); applyIIRFilter(Filtered_Data, Final_Data);
// 数据打包与 BLE 发送 BLE_Packet_to_Send(...); } }}

记住判断和清空,下面就是大家干活,读数据,滤波和发送

最后就简单了, ADC 采样10 ms 触发一次。BLE 数据发送频率较低,可以通过一个较慢的定时器(100 ms)单独触发 BLE 打包和发送。

以前觉得这些东西很难,但是到现在我觉得这些东西分外的清晰,我想就是流程清晰,也能知道各种方法。

大概总结一下,先梳理流程,分割各种任务快,把和外部交互的放在中断,保持实时性,处理放在循环里面。如果一个快一个慢,就搞个缓存区。

评论
  • 车身域是指负责管理和控制汽车车身相关功能的一个功能域,在汽车域控系统中起着至关重要的作用。它涵盖了车门、车窗、车灯、雨刮器等各种与车身相关的功能模块。与汽车电子电气架构升级相一致,车身域发展亦可以划分为三个阶段,功能集成愈加丰富:第一阶段为分布式架构:对应BCM车身控制模块,包含灯光、雨刮、门窗等传统车身控制功能。第二阶段为域集中架构:对应BDC/CEM域控制器,在BCM基础上集成网关、PEPS等。第三阶段为SOA理念下的中央集中架构:VIU/ZCU区域控制器,在BDC/CEM基础上集成VCU、
    北汇信息 2025-01-03 16:01 115浏览
  • 【工程师故事】+半年的经历依然忧伤,带着焦虑和绝望  对于一个企业来说,赚钱才是第一位的,对于一个人来说,赚钱也是第一位的。因为企业要活下去,因为个人也要活下去。企业打不了倒闭。个人还是要吃饭的。企业倒闭了,打不了从头再来。个人失业了,面对的不仅是房贷车贷和教育,还有找工作的焦虑。企业说,一个公司倒闭了,说明不了什么,这是正常的一个现象。个人说,一个中年男人失业了,面对的压力太大了,焦虑会摧毁你的一切。企业说,是个公司倒闭了,也不是什么大的问题,只不过是这些公司经营有问题吧。
    curton 2025-01-02 23:08 199浏览
  • 影像质量应用于多个不同领域,无论是在娱乐、医疗或工业应用中,高质量的影像都是决策的关键基础。清晰的影像不仅能提升观看体验,还能保证关键细节的准确传达,例如:在医学影像中,它对诊断结果有着直接的影响!不仅如此,影像质量还影响了:▶ 压缩技术▶ 存储需求▶ 传输效率随着技术进步,影像质量的标准不断提高,对于研究与开发领域,理解并提升影像质量已成为不可忽视的重要课题。在图像处理的过程中,硬件与软件除了各自扮演着不可或缺的基础角色,有效地协作能够确保图像处理过程既高效又具有优异的质量。软硬件各扮演了什么
    百佳泰测试实验室 2025-01-03 10:39 79浏览
  • Matter加持:新世代串流装置如何改变智能家居体验?随着现在智能家庭快速成长,串流装置(Streaming Device,以下简称Streaming Device)除了提供更卓越的影音体验,越来越多厂商开始推出支持Matter标准的串流产品,使其能作为智能家庭中枢,连结多种智能家电。消费者可以透过Matter的功能执行多样化功能,例如:开关灯、控制窗帘、对讲机开门,以及操作所有支持Matter的智能家电。此外,再搭配语音遥控器与语音助理,打造出一个更加智能、便捷的居家生活。支持Matter协议
    百佳泰测试实验室 2025-01-03 10:29 94浏览
  • 物联网(IoT)的快速发展彻底改变了从智能家居到工业自动化等各个行业。由于物联网系统需要高效、可靠且紧凑的组件来处理众多传感器、执行器和通信设备,国产固态继电器(SSR)已成为满足中国这些需求的关键解决方案。本文探讨了国产SSR如何满足物联网应用的需求,重点介绍了它们的优势、技术能力以及在现实场景中的应用。了解物联网中的固态继电器固态继电器是一种电子开关设备,它使用半导体而不是机械触点来控制负载。与传统的机械继电器不同,固态继电器具有以下优势:快速切换:确保精确快速的响应,这对于实时物联网系统至
    克里雅半导体科技 2025-01-03 16:11 115浏览
  • 本文继续介绍Linux系统查看硬件配置及常用调试命令,方便开发者快速了解开发板硬件信息及进行相关调试。触觉智能RK3562开发板演示,搭载4核A53处理器,主频高达2.0GHz;内置独立1Tops算力NPU,可应用于物联网网关、平板电脑、智能家居、教育电子、工业显示与控制等行业。查看系统版本信息查看操作系统版本信息root@ido:/# cat /etc/*releaseDISTRIB_ID=UbuntuDISTRIB_RELEASE=20.04DISTRIB_CODENAME=focalDIS
    Industio_触觉智能 2025-01-03 11:37 82浏览
  • 光耦合器,也称为光隔离器,是一种利用光在两个隔离电路之间传输电信号的组件。在医疗领域,确保患者安全和设备可靠性至关重要。在众多有助于医疗设备安全性和效率的组件中,光耦合器起着至关重要的作用。这些紧凑型设备经常被忽视,但对于隔离高压和防止敏感医疗设备中的电气危害却是必不可少的。本文深入探讨了光耦合器的功能、其在医疗应用中的重要性以及其实际使用示例。什么是光耦合器?它通常由以下部分组成:LED(发光二极管):将电信号转换为光。光电探测器(例如光电晶体管):检测光并将其转换回电信号。这种布置确保输入和
    腾恩科技-彭工 2025-01-03 16:27 104浏览
  • 在快速发展的能源领域,发电厂是发电的支柱,效率和安全性至关重要。在这种背景下,国产数字隔离器已成为现代化和优化发电厂运营的重要组成部分。本文探讨了这些设备在提高性能方面的重要性,同时展示了中国在生产可靠且具有成本效益的数字隔离器方面的进步。什么是数字隔离器?数字隔离器充当屏障,在电气上将系统的不同部分隔离开来,同时允许无缝数据传输。在发电厂中,它们保护敏感的控制电路免受高压尖峰的影响,确保准确的信号处理,并在恶劣条件下保持系统完整性。中国国产数字隔离器经历了重大创新,在许多方面达到甚至超过了全球
    克里雅半导体科技 2025-01-03 16:10 71浏览
  • 自动化已成为现代制造业的基石,而驱动隔离器作为关键组件,在提升效率、精度和可靠性方面起到了不可或缺的作用。随着工业技术不断革新,驱动隔离器正助力自动化生产设备适应新兴趋势,并推动行业未来的发展。本文将探讨自动化的核心趋势及驱动隔离器在其中的重要角色。自动化领域的新兴趋势智能工厂的崛起智能工厂已成为自动化生产的新标杆。通过结合物联网(IoT)、人工智能(AI)和机器学习(ML),智能工厂实现了实时监控和动态决策。驱动隔离器在其中至关重要,它确保了传感器、执行器和控制单元之间的信号完整性,同时提供高
    腾恩科技-彭工 2025-01-03 16:28 118浏览
  • 在测试XTS时会遇到修改产品属性、SElinux权限、等一些内容,修改源码再编译很费时。今天为大家介绍一个便捷的方法,让OpenHarmony通过挂载镜像来修改镜像内容!触觉智能Purple Pi OH鸿蒙开发板演示。搭载了瑞芯微RK3566四核处理器,树莓派卡片电脑设计,支持开源鸿蒙OpenHarmony3.2-5.0系统,适合鸿蒙开发入门学习。挂载镜像首先,将要修改内容的镜像传入虚拟机当中,并创建一个要挂载镜像的文件夹,如下图:之后通过挂载命令将system.img镜像挂载到sys
    Industio_触觉智能 2025-01-03 11:39 78浏览
我要评论
0
点击右上角,分享到朋友圈 我知道啦
请使用浏览器分享功能 我知道啦