// 定义一个标志变量
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来一次,中断内部有个数值来记录进入的次数。一次又一次,我们就可以判断次数来执行代码。
判断是否是定时器 2 触发了中断。如果是,则执行后续逻辑。因为是按时进去,那每一次进来都会记一下,然后就可以实现比如5ms,10ms,15ms执行任务。
增加 tim_counter,用于控制每隔 4 次中断保存一次数据。
线保存在BLE的封包里面,当封包里面的DMA满了,就直接使用UART穿出去,这个代码框架可以当做一个模板使用。
我也一直在学习,编程的时候我们在关注什么?我回答是其实是数据。外设都是固定的,无非也是抽象的读写。
但是数据却是我们一直关注,一个数据来了,它是什么样的?现在完整了吗?接下来要干嘛?给下一级?下一级要什么样的?应该怎么修改?其实都是对数据做操作而已。
赶紧进来获得来自ADC的数据
然后滤波
将 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]
ADC_Value_Receive_1[ADC_Sample_Counter * 2 + 1] = (uint8_t)(ADC_Value_u16[2] & 0X00FF);& 0x00FF
详细分析
BLE_Packet_to_Send[Channel1_Data_Start_Counter + ADC_Sample_Counter * 2] = ADC_Value_Receive_1[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];
从MPU6050看传感器原始数据的处理方式-位运算 看不懂?就先复习一下我的文章。
清醒一点,我们的要求就是对原始数据重新塑造然后传到下个封包里面。
ADC_Value_Receive_1
可以封装一个函数:
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_1, BLE_Packet_to_Send, ADC_Sample_Counter, Channel1_Data_Start_Counter);
CRC和发送
什么时候不适合使用中断?
然后就批判一下上面这个函数,
然后,中断中只采样 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 打包和发送。
以前觉得这些东西很难,但是到现在我觉得这些东西分外的清晰,我想就是流程清晰,也能知道各种方法。
大概总结一下,先梳理流程,分割各种任务快,把和外部交互的放在中断,保持实时性,处理放在循环里面。如果一个快一个慢,就搞个缓存区。