1 前言
前幾期我們介紹了STM32CubeMX中關(guān)于定時器的各個配置參數(shù)及其功能,本期我們來介紹各個功能的具體代碼實現(xiàn)。
分別是:定時器中斷,PWM模式,輸出捕獲模式,比較輸出模式,強制輸出模式。
進行時鐘配置,這里主頻是170MHZ,定時器時鐘為170MHZ。
2 定時器中斷
打開CubeMX,配置定時器分頻系數(shù)為170-1,周期計數(shù)值為1000,主頻為170MHZ。由此我們可以計算一次溢出的為:
170M/(170+1)/1000 = 1000即1ms一次。
開啟定時器中斷以確保我們的定時器中斷回調(diào)函數(shù)能被正常觸發(fā)。
接著選擇好編譯器和文件路徑和文件名( 不要用中文路徑)生成工程。
/* USER CODE BEGIN 2 */
HAL_TIM_Base_Start_IT(&htim1);
/* USER CODE END 2 */
添加啟動定時器并啟用中斷的函數(shù),參數(shù)為我們開啟的定時器句柄。
//開啟定時器并開啟中斷
HAL_TIM_Base_Start_IT(&htim1);
//開啟定時器不開啟中斷
//HAL_TIM_Base_Start(&htim1);
當然也有不啟用中斷的,后者僅僅開啟定時器計數(shù)功能,但是會發(fā)生定時器溢出產(chǎn)生事件更新,不過不會觸發(fā)中斷請求。
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
static int number = 0;
if(htim->Instance == TIM1) // 判斷是否是 TIM1 觸發(fā)的中斷
{
// 1ms觸發(fā)一次
number++;
if(number==1000)
{
//1ms*1000 = 1s
HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_5);
number = 0;//清零
}
}
}
接著我們需要重寫定時器中斷回調(diào)函數(shù)來實現(xiàn)我們的功能,由于我們定時器是1ms觸發(fā)一次,因此我們使用一個計數(shù)器來判斷觸發(fā)的次數(shù)。當我們定時時間到達1s時,使我們的LED燈翻轉(zhuǎn)。
我們可以看到,中斷回調(diào)函數(shù)在底層被一個虛函數(shù)定義,這段注釋告訴用戶,不要修改這個函數(shù)的默認實現(xiàn)。如果需要自定義的回調(diào)行為,應該在用戶的代碼中重新實現(xiàn)這個回調(diào)函數(shù)。
當我們在程序中需要修改定時器觸發(fā)時間的時候,我們可以重新修改定時器的參數(shù)并進行初始化。
htim1.Init.Prescaler = new_prescaler;//新的分頻系數(shù)
htim1.Init.Period = new_arr;//新的周期值
HAL_TIM_Base_Init(&htim1); // 重新初始化定時器
HAL_TIM_Base_Start_IT(&htim1); // 重新啟動定時器
下面要用。
3 PWM模式
上面我們設(shè)置了定時器的單周期參數(shù)為1000,接著我們設(shè)置PWM的比較值(Output compare preload)為500,那么占空比就是:500/1000 = 50%。
接著生成我們的代碼。
/* USER CODE BEGIN 2 */
HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_1);
/* USER CODE END 2 */
啟動定時器PWM,參數(shù)分別為定時器句柄和通道。
這樣子我們通道一對應的IO就可以輸出方波了。
htim1.Init.Prescaler = new_prescaler;//新的分頻系數(shù)
htim1.Init.Period = new_arr;//新的周期值
HAL_TIM_Base_Init(&htim1); // 重新初始化定時器
HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_1); // 重新啟動PWM
通過修改定時器的計數(shù)頻率來修改PWM的頻率。
/* USER CODE BEGIN 2 */
__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, duty); //修改占空比
/* USER CODE END 2 */
通過修改通道的比較值CCR來改變PWM的占空比 = CCR/ARR.
4 輸入捕獲
通道配置為輸入捕獲之后,需要注意捕獲模式是上升沿捕獲、下降沿捕獲還是雙邊捕獲。
這里要開啟中斷,我們需要使用中斷回調(diào)函數(shù)。
HAL_TIM_IC_Start_IT(&htim2, TIM_CHANNEL_1); // 啟動定時器并使能中斷
調(diào)用開啟輸入捕獲的函數(shù),接著重寫輸入捕獲的回調(diào)函數(shù)。
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
static uint32_t last_capture = 0; // 上次捕獲的計數(shù)值
uint32_t capture_value = 0;
if (htim->Instance == TIM1) // 判斷是否是定時器1觸發(fā)的中斷
{
capture_value = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1); // 讀取捕獲
// 計算捕獲值之間的時間差(信號周期或脈沖寬度)
if (capture_value > last_capture)
{
uint32_t pulse_width = capture_value - last_capture; // 計算脈沖寬度
last_capture = capture_value; // 更新上次捕獲值
}
}
}
當外部信號來臨的時候,我們可以讀取通道值來獲得當前的計數(shù)值。通過比較兩次的計數(shù)值可以知道兩個信號之間的觸發(fā)事件。
這種方法我們也可以用來測量PWM的高低電平時間。通過修改觸發(fā)方式,當上升沿觸發(fā)的時候,修改觸發(fā)方式為下降沿,就可以知道高電平的時間。反過來就可以知道低電平的時間。
// 輸入捕獲中斷回調(diào)函數(shù)
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
capture_value = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1); // 讀取捕獲值
if (htim->Instance == TIM1) // 判斷是否是定時器2觸發(fā)的中斷
{
if (capture_value > last_capture) // 如果是上升沿
{
// 處理上升沿捕獲邏輯
printf("上升沿時間: %lun", capture_value);
// 修改下一次捕獲為下降沿觸發(fā)
TIM_IC_InitTypeDef sConfigIC = {0};
sConfigIC.ICPolarity = TIM_ICPOLARITY_FALLING; // 設(shè)置為下降沿觸發(fā)
sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI; // 直接從輸入端口獲取信號
sConfigIC.ICPrescaler = TIM_ICPSC_DIV1; // 不進行預分頻
sConfigIC.ICFilter = 0; // 無輸入濾波器
HAL_TIM_IC_ConfigChannel(&htim2, &sConfigIC, TIM_CHANNEL_1); // 配置通道1為下降沿觸發(fā)
}
else // 如果是下降沿
{
// 處理下降沿捕獲邏輯
printf("下降沿時間: %lun", capture_value);
// 修改下一次捕獲為上升沿觸發(fā)
TIM_IC_InitTypeDef sConfigIC = {0};
sConfigIC.ICPolarity = TIM_ICPOLARITY_RISING; // 設(shè)置為上升沿觸發(fā)
sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI; // 直接從輸入端口獲取信號
sConfigIC.ICPrescaler = TIM_ICPSC_DIV1; // 不進行預分頻
sConfigIC.ICFilter = 0; // 無輸入濾波器
HAL_TIM_IC_ConfigChannel(&htim2, &sConfigIC, TIM_CHANNEL_1); // 配置通道1為上升沿觸發(fā)
}
last_capture = capture_value; // 更新上次捕獲值
}
}
5 比較輸出
比較輸出的配置和PWM模式的差不多。
HAL_TIM_OC_Start(&htim1, TIM_CHANNEL_1); // 啟動通道1的輸出比較
利用該函數(shù)開啟通道的比較輸出的功能,它除了能輸出PWM波之外,還可以在比較值的時候觸發(fā)中斷回調(diào)函數(shù)。
void HAL_TIM_OC_DelayElapsedCallback(TIM_HandleTypeDef *htim)
{
if (htim->Instance == TIM1) // 檢查是否是TIM1的比較輸出中斷
{
}
}
并且,當我們把模式修改為電平翻轉(zhuǎn)之后,可以讓通道實現(xiàn)方波輸出。
6 強制輸出
利用如下函數(shù)實現(xiàn)強制輸出。
// 配置強制輸出模式為強制低電平
__HAL_TIM_OC_SET_FORCED_ACTION(&htim1, TIM_CHANNEL_1, TIM_OCFORCE_LOW);
// 或者配置強制輸出模式為強制高電平
// __HAL_TIM_OC_SET_FORCED_ACTION(&htim1, TIM_CHANNEL_1, TIM_OCFORCE_HIGH);
通過在定時器溢出、比較事件或外部觸發(fā)事件發(fā)生時,強制定時器輸出高電平或低電平,可以靈活地控制STM32微控制器的輸出行為。