一、定時器功能之高精度定時(微秒級)
我們常用的延時函數中無論是HAL_Delay還是vTaskDelay()函數都是毫秒級的定時,我們可以借助定時器實現一個微秒級更高精度的延時函數。這個定時器不會影響FreeRtos的任務切換
這里就是用定時器的計數功能來實現定時
前面我們設置了Tim2的分頻值為72/(71+1) = 1MHz,也就是1000000Hz,那么頻率就是1/1000000 = 1us,也就是1us(1微秒)計數一次,前面我們設置的ARR自動重載的值為999,也就是999+1 = 1000us會重新加載CNT的值為0
1、原理:
t_old原計數值 與 t_now當下計數值之間不可以超過一個溢出周期的前提下。
因為原計數與當下計數值之間在不存在延時操作的情況下,不可能出現超過一個溢出周期的。
上面就是兩種延時周期的運算方式
配置TIM2時鐘,用它的基本計數功能就可以,不需要去管通道,因為通道是做PWM或者輸入捕獲的,我們這里只需要它最基本的定時計數,前面的章節我們用TIM2還做了PWM,占用了通道,但是這不影響基本計數定時功能
//實現更高精度的延時函數(微秒級)
void tim2_u_delay(uint32_t us)
{HAL_TIM_Base_Start(&htim2);//啟用定時器基本計數功能。//1.獲取ARR自動裝載寄存器中的值:uint32_t load_ARR = __HAL_TIM_GET_AUTORELOAD(&htim2);//2.獲取一個起始檢測點的計數值:uint32_t t_start_CNT = __HAL_TIM_GET_COUNTER(&htim2);//3.檢測周期:uint32_t t_current_CNT = 0;uint32_t ticks = 0;while (true){/* 檢測 */t_current_CNT = __HAL_TIM_GET_COUNTER(&htim2);if(t_current_CNT >= t_start_CNT){ticks += (t_current_CNT - t_start_CNT);}else{ticks += (load_ARR) - t_start_CNT + 1 + t_current_CNT;}//更新起始點start:t_start_CNT = t_current_CNT;if(ticks >= us){break;}}}
二、輸入捕獲
1、輸入捕獲原理
當定時器啟動輸入捕獲模式,當外部信號出現變化時,此時會記錄計數器CNT的值,并保存到CCR1,此時會發生中斷,可以在中斷回調函數中讀取出CCR1此時的值,也就是讀取出CNT計數器的值,這個時間和ARR的值相比看一下占用周期的比例,就可以完整的得出外部信號的波形
舉例使用:監測脈沖寬度,也就是記錄脈沖的時間
這里將一個信號分兩次接入chn1和chn2,chn1捕獲上升沿,chn2捕獲下降沿,這樣分別捕獲的時間會再CCR1和CCR2中,上面的計算公式中的分辨率就是計數器CNT的頻率,這里假設PSC出來的是1MHZ,也就是1秒1000000次,設置ARR為1000,CNT只是記錄前面的PSC頻率的計數,此時1MHZ,1/1000000 = 1us,也就是1us記錄一次,這里的分辨率就是1us
通道1和通道2是一對,通道3和通道4是一對,這樣一對中,一個通道監測上升沿,一個通道監測下降沿,就可以判斷一個高電平的時長
2、典型應用
1、 脈寬測量:通過捕獲信號的上升沿和下降沿,可以測量信號的高電平或低電平持續時間。
3、周期測量:通過兩次上升沿或下降沿之間的時間差,可以測量信號的周期。
4、頻率測量:通過周期測量,可以計算出信號的頻率。
3、輸入捕獲HAL庫接口函數API:
1.啟動輸入捕獲:HAL_TIM_IC_Start
HAL_StatusTypeDef HAL_TIM_IC_Start(TIM_HandleTypeDef *htim, uint32_t Channel);
HAL_StatusTypeDef HAL_TIM_IC_Start_IT(TIM_HandleTypeDef *htim, uint32_t Channel);
1功能:啟動定時器通道的輸入捕獲功能:
2功能:啟動定時器通道的輸入捕獲功能并且開啟捕獲中斷。
參數:
htim:即要啟動的定時器的句柄。
Channel:啟動輸入捕獲的通道是哪個。返回值:HAL_OK: 操作成功。HAL_ERROR: 操作失敗。HAL_BUSY: 定時器資源忙
2.停止輸入捕獲:HAL_TIM_IC_Stop
HAL_StatusTypeDef HAL_TIM_IC_Stop(TIM_HandleTypeDef *htim, uint32_t Channel);
HAL_StatusTypeDef HAL_TIM_IC_Stop_IT(TIM_HandleTypeDef *htim, uint32_t Channel);
1.功能:停止定時器通道的輸入捕獲功能:
2功能:停止定時器通道的輸入捕獲功能并且關閉捕獲中斷。
參數:
htim:即要停止的定時器的句柄。
Channel:停止輸入捕獲的通道是哪個。
返回值:HAL_OK: 操作成功。HAL_ERROR: 操作失敗。HAL_BUSY: 定時器資源忙
3.輸入捕獲配置通道:HAL_TIM_IC_ConfigChannel,相應的輸入參數也可直接在CubMX直接配置。
HAL_StatusTypeDef HAL_TIM_IC_ConfigChannel(TIM_HandleTypeDef *htim, TIM_IC_InitTypeDef *sConfig, uint32_t Channel);
功能:配置輸入捕獲通道
參數:
htim: 指向 TIM_HandleTypeDef 結構體的指針,該結構體包含了定時器的配置信息。
sConfig: 指向 TIM_IC_InitTypeDef 結構體的指針,該結構體用于配置輸入捕獲通道的參數。
Channel: 要配置的定時器通道。可以是 TIM_CHANNEL_1, TIM_CHANNEL_2, TIM_CHANNEL_3, 或 TIM_CHANNEL_4。TIM_IC_InitTypeDef 結構體用于設置輸入捕獲通道的參數,主要包括:
ICPolarity: 輸入捕獲極性。可以是 TIM_ICPOLARITY_RISING(上升沿)或 TIM_ICPOLARITY_FALLING(下降沿),或者 TIM_ICPOLARITY_BOTHEDGE(兩邊緣)。
ICSelection: 輸入捕獲選擇。可以是 TIM_ICSELECTION_DIRECTTI(直接輸入)或 TIM_ICSELECTION_INDIRECTTI(間接輸入)。
ICPrescaler: 輸入捕獲預分頻器。可以是 TIM_ICPSC_DIV1(無分頻),TIM_ICPSC_DIV2(除2),TIM_ICPSC_DIV4(除4),或 TIM_ICPSC_DIV8(除8)。
ICFilter: 輸入捕獲濾波器值,用于過濾輸入信號中的噪聲。
返回值HAL_OK: 配置成功。HAL_ERROR: 配置失敗。
4.當捕獲成功之后的中斷回調函數,直接重寫這個函數就可以:
__weak void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
//此函數是捕獲之后的中斷回調函數,是一個弱函數。
//當捕獲發生后,可以重新定義相應的邏輯。
//注意:此中斷回調函數,需要要執行HAL_TIM_IC_Start_IT時才會被調用。
5、超聲波測距驅動(輸入捕獲實例)
原理:啟動超聲波就是給Trig引腳通10us的高電平,然后超聲波左側就會發出,這個超聲波的頻率是40KHZ,發出8個周期,也就是0.2ms,超聲波發出之后,Echo引腳就會自動拉高電平,當超聲波回來以后,也就是右側的接收超聲波的8個周期完成,echo就會把電平拉低
實際就是超聲波發出后,echo拉高電平,超聲波接收完成,echo拉低電平,此時只需要監測echo處于高電平的時間就可以計算出距離
距離=聲速(340m/s)*傳播時間 / 2
echo可以維持最大38ms,也就是超出了量程,飛了
通道1選擇上升沿直接監測,通道2選擇下降沿間接監測,就可以獲取出Echo處于高電平的周期
1、選擇通道捕獲模式
通道1選擇輸入直接捕獲模式,通道2選擇輸入間接捕獲模式
2、選擇邊沿監測模式
通道1選擇上升沿監測,通道2選擇下降沿監測
3、選擇分頻系數
4、整體
Input Filter (4 bits value)是輸入濾波,設置的最大值15
配置PD15為輸出模式,接Trig
PD14是TIM4的chnl3,接Echo
前面說了這個超聲波測距模塊的Echo維持高電平的時間最大為38ms,所以這里就要設置一個周期要大于38ms,這里設置了分頻為72/(71+1) = 1MHz,也就是1/1000000 = 1us,也就是1us的時候CNT計數一次,38ms = 38000us,所以只要ARR大于38000就可以了,這里我設置了65535,完全可以包含38ms