// 定義一個(gè)標(biāo)志變量volatile uint8_t timer_flag = 0;// 定時(shí)器中斷回調(diào)函數(shù)void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {if (htim->Instance == TIM2) { // 判斷是否是定時(shí)器2timer_flag = 1; // 設(shè)置標(biāo)志,表示定時(shí)器觸發(fā)}}int main(void) {HAL_Init(); // 初始化 HAL 庫SystemClock_Config(); // 配置系統(tǒng)時(shí)鐘MX_TIM2_Init(); // 初始化定時(shí)器2HAL_TIM_Base_Start_IT(&htim2); // 啟動(dòng)定時(shí)器并使能中斷while (1) {if (timer_flag) { // 檢查標(biāo)志是否被設(shè)置timer_flag = 0; // 清除標(biāo)志// 定時(shí)觸發(fā)的代碼HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5); // 切換 LED 狀態(tài)}}}

定時(shí)器來了,一般是來了一個(gè)小小的好算的數(shù)
比如1S來一次,中斷內(nèi)部有個(gè)數(shù)值來記錄進(jìn)入的次數(shù)。一次又一次,我們就可以判斷次數(shù)來執(zhí)行代碼。



判斷是否是定時(shí)器 2 觸發(fā)了中斷。如果是,則執(zhí)行后續(xù)邏輯。因?yàn)槭前磿r(shí)進(jìn)去,那每一次進(jìn)來都會(huì)記一下,然后就可以實(shí)現(xiàn)比如5ms,10ms,15ms執(zhí)行任務(wù)。

增加 tim_counter,用于控制每隔 4 次中斷保存一次數(shù)據(jù)。
線保存在BLE的封包里面,當(dāng)封包里面的DMA滿了,就直接使用UART穿出去,這個(gè)代碼框架可以當(dāng)做一個(gè)模板使用。
我也一直在學(xué)習(xí),編程的時(shí)候我們?cè)陉P(guān)注什么?我回答是其實(shí)是數(shù)據(jù)。外設(shè)都是固定的,無非也是抽象的讀寫。
但是數(shù)據(jù)卻是我們一直關(guān)注,一個(gè)數(shù)據(jù)來了,它是什么樣的?現(xiàn)在完整了嗎?接下來要干嘛?給下一級(jí)?下一級(jí)要什么樣的?應(yīng)該怎么修改?其實(shí)都是對(duì)數(shù)據(jù)做操作而已。

趕緊進(jìn)來獲得來自ADC的數(shù)據(jù)


然后濾波
將 16 位 ADC 數(shù)據(jù)分解成兩個(gè) 8 位字節(jié),便于通過 BLE 通信協(xié)議傳輸(BLE 通信通常以字節(jié)為單位傳輸數(shù)據(jù))。

這是完整的一組
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
詳細(xì)分析
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看傳感器原始數(shù)據(jù)的處理方式-位運(yùn)算? 看不懂?就先復(fù)習(xí)一下我的文章。
清醒一點(diǎn),我們的要求就是對(duì)原始數(shù)據(jù)重新塑造然后傳到下個(gè)封包里面。
ADC_Value_Receive_1 可以封裝一個(gè)函數(shù):
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];}
調(diào)用:
Pack_ADC_Data(ADC_Value_u16[2],?ADC_Value_Receive_1,?BLE_Packet_to_Send,?ADC_Sample_Counter,?Channel1_Data_Start_Counter);
CRC和發(fā)送
什么時(shí)候不適合使用中斷?
然后就批判一下上面這個(gè)函數(shù),
然后,中斷中只采樣 ADC 數(shù)據(jù)并存入一個(gè)環(huán)形緩沖區(qū)。在中斷中設(shè)置標(biāo)志位,主循環(huán)中根據(jù)標(biāo)志位執(zhí)行濾波和通信操作。其實(shí)就是在較長的時(shí)間后開始處理數(shù)據(jù)。
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {if (htim == &htim2) {// 簡(jiǎn)單采樣操作AD7682_Read_4_ADC_Value(ADC_Value_Buffer);// 將數(shù)據(jù)存入環(huán)形緩沖區(qū)ring_buffer_write(&adc_ring_buffer, ADC_Value_Buffer);// 設(shè)置標(biāo)志位data_ready_flag = 1;}}
然后寫個(gè)循環(huán)代碼:
void main_loop() {while (1) {if (data_ready_flag) {data_ready_flag = 0;// 從環(huán)形緩沖區(qū)讀取數(shù)據(jù)ring_buffer_read(&adc_ring_buffer, Processed_Data);// 執(zhí)行濾波操作IIR_50HZ_Norch_Filter(Processed_Data, Filtered_Data);applyIIRFilter(Filtered_Data, Final_Data);// 數(shù)據(jù)打包與 BLE 發(fā)送BLE_Packet_to_Send(...);}}}

記住判斷和清空,下面就是大家干活,讀數(shù)據(jù),濾波和發(fā)送
最后就簡(jiǎn)單了,?ADC 采樣10 ms 觸發(fā)一次。BLE 數(shù)據(jù)發(fā)送頻率較低,可以通過一個(gè)較慢的定時(shí)器(100 ms)單獨(dú)觸發(fā) BLE 打包和發(fā)送。
以前覺得這些東西很難,但是到現(xiàn)在我覺得這些東西分外的清晰,我想就是流程清晰,也能知道各種方法。
大概總結(jié)一下,先梳理流程,分割各種任務(wù)快,把和外部交互的放在中斷,保持實(shí)時(shí)性,處理放在循環(huán)里面。如果一個(gè)快一個(gè)慢,就搞個(gè)緩存區(qū)。