給平頂山公安局做網站的公司前端培訓哪個機構靠譜
前言
學習是永無止境的,就算之前學過的東西再次學習一遍也能狗學習到很多東西,輸入捕獲很早之前就用過了,但是僅僅是照搬例程沒有去進行理解。溫故而知新!
定時器
定時器簡介
定時器的分類
高級定時器 通用定時器 基本定時器,針對不同情況可以選擇自己所需要的定時器,以下是這幾種定時器的主要功能。
高級定時器:
- 計數器單元
- 輸入捕獲
- 重復計數器
- PWM模式
- 互補輸出和死區(qū)插入
- 支持針對定位的增量(正交)編碼器和霍爾傳感器電路
- 剎車功能
- DMA功能
通用定時器:
- 計數器單元
- 輸入捕獲
- PWM模式
- 支持針對定位的增量(正交)編碼器和霍爾傳感器電路
- DMA功能
基本定時器:
- 計數器單元
- DMA功能
定時器的功能
我們使用定時器很多時候使用都是用做于計數作用,同樣也可以作為PWM輸出,輸入捕獲(脈寬測量,頻率檢測)等等,有的時候我們還會使用DMA+PWM輸出這樣的功能,總之定時器對于我們進行產品開發(fā)是必不可少的。
定時器的配置
主要寄存器
這里我使用的是航芯ACM32FP0X系列的單片機,不過其他單片機用法也是一樣的。
這里我們主要關注以上六個寄存器,中斷使能寄存器、狀態(tài)寄存器、事件產生寄存器、預分頻寄存器、自動加載寄存器、捕獲/比較寄存器。其他的寄存器不是說不重要,只是在本次實驗中主要理解這幾個寄存器就好。
PWM配置
因為本次實驗沒有使用外置的脈沖發(fā)生器,所以使用單片機上另一個通用定時器來配置PWM,然后使用高級定時器去進行輸入捕獲。
TIM_Handler_PWM.Instance = TIM15;TIM_Handler_PWM.Init.ARRPreLoadEn = TIM_ARR_PRELOAD_ENABLE; TIM_Handler_PWM.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; TIM_Handler_PWM.Init.CounterMode = TIM_COUNTERMODE_UP; TIM_Handler_PWM.Init.RepetitionCounter = 0; TIM_Handler_PWM.Init.Prescaler = (timer_clock/TIM_CLOCK_FREQ)*1 - 1; if (timer_clock%TIM_CLOCK_FREQ > TIM_CLOCK_FREQ/2) {TIM_Handler_PWM.Init.Prescaler = TIM_Handler_PWM.Init.Prescaler + 1; }TIM_Handler_PWM.Init.Period = (TIM_CLOCK_FREQ/100000)*20 - 1; // period = 1ms
首先是常規(guī)的配置,這里我選擇的是定時器15作為PWM口進行輸出,然后不分頻所以APB的時鐘就是定時器的時鐘。然后就是自動重裝載值ARR,和預分配系數Prescaler的配置;這兩個參數共同決定了配置的PWM輸出極限頻率是多少。
PWM輸出頻率 Fre =?。?
這里來解析一下為什么要使用 TIM_CLOCK_FREQ這個參數:
TIM_CLOCK_FREQ 代表的是定時器經過分頻之后能夠達到的極限頻率,我們可以來推導一下,timer_clock(也就是主時鐘APBCLK)分頻之后?就是?,
簡單約去公約數,得到的就是TIM_CLOCK_FREQ,所以當重裝載值為1的時候TIM_CLOCK_FREQ就是最高頻率。再來解釋一下周期是怎么得出的,由于1s之內計數器計數了TIM_CLOCK_FREQ次,所以一次需要的時間是1/TIM_CLOCK_FREQ,所以周期和重裝載值之間的關系就是T= ,這里除以1000的原因是將秒的單位換算成毫秒。OK,也不知道講清楚了沒有,之前也搞不懂這個關系式,后來推導出來的時候還感覺蠻巧妙的。
配置完這些再配置一下模式和占空比(CCR)和引腳就行了
輸入捕獲的配置
TIM_IC_InitTypeDef Tim_IC_Init_Para; TIM_Handler.Instance = TIM1;TIM_Handler.Init.ARRPreLoadEn = TIM_ARR_PRELOAD_ENABLE; TIM_Handler.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; TIM_Handler.Init.CounterMode = TIM_COUNTERMODE_UP; TIM_Handler.Init.RepetitionCounter = 0; TIM_Handler.Init.Prescaler = 0; TIM_Handler.Init.Period = 0xFFFF; // max period TIM1_MSP_Pre_Init(&TIM_Handler); HAL_TIMER_Base_Init(&TIM_Handler); Tim_IC_Init_Para.TIFilter = TIM_TI1_FILTER_LVL(0); // no filter Tim_IC_Init_Para.ICPolarity = TIM_SLAVE_CAPTURE_ACTIVE_RISING_FALLING; Tim_IC_Init_Para.ICPrescaler = TIM_IC1_PRESCALER_1; Tim_IC_Init_Para.ICSelection = TIM_ICSELECTION_DIRECTTI; // TI1FP1 HAL_TIMER_Capture_Config(TIM_Handler.Instance, &Tim_IC_Init_Para, TIM_CHANNEL_1); TIM1_MSP_Post_Init();
上面也是一些很常規(guī)的配置,配置了分頻系數(為1意味著定時器的頻率和APB時鐘一致),重裝載值為0xFFFF,這些數據傳達了一個怎樣的信息呢?說明了使用輸入捕獲的頻率可高達64MHz(APB時鐘為64MHz),說明捕獲精度可以達到1/64000000 s,也就是15ns,最長捕獲時間間隔為1023us(15ns * 0xFFFF)。然后下一個就是設置捕獲觸發(fā)模式,可能不同的單片機的庫設置起來會有細微差別,但是大致意思是相同的。由于我們需要收集這些鞋數據然后在串口上打印出來所以我這里使用了中斷觸發(fā),在中斷里面收集數據。
HAL_TIMER_Clear_Capture_Flag(&TIM_Handler, TIM_CHANNEL_1);NVIC_ClearPendingIRQ(TIM1_CC_IRQn); NVIC_ClearPendingIRQ(TIM1_BRK_UP_TRG_COM_IRQn);NVIC_EnableIRQ(TIM1_CC_IRQn); NVIC_EnableIRQ(TIM1_BRK_UP_TRG_COM_IRQn); HAL_TIMER_Base_Init(&TIM_Handler); HAL_TIM_ENABLE_IT(&TIM_Handler, TIM_IT_CC1);HAL_TIM_ENABLE_IT(&TIM_Handler, TIMER_INT_EN_UPD); //計數器向上溢出/向下溢出 用于記錄更新次數HAL_TIM_Capture_Start(TIM_Handler.Instance, TIM_CHANNEL_1);
當然這里還加了個溢出中斷,用于輔助捕獲計算超出最大捕獲長度的電平時間,具體思路就是當計數器溢出的時候在事件更新中斷里面設置一個變量進行加加。
可以大致看一下捕獲中斷里面的函數,主要是獲取觸發(fā)了捕獲中斷之后將數據寫入事先定義好的數組,然后在定義一個變量進行計數。
void TIM1_CC_IRQHandler(void)
{uint32_t status; status = TIM1->SR; if ( (status & TIMER_SR_CC1IF) && ((TIM1->CCMR1) & (BIT0|BIT1)) ) {if(flag1 == 1)flag2 = 1;flag1 = 1;Capture_data[0][capture_times] = TIM1->CCR1;}if ((status & TIMER_SR_CC2IF) && ((TIM1->CCMR1) & (BIT8|BIT9)) ) {Capture_data[1][capture_times] = TIM1->CCR2; }capture_times++; NVIC_ClearPendingIRQ(TIM1_CC_IRQn);
}
然后在主函數里面獲取標志位之后打印顯示就可以了
這樣在主函數當中我們就可以是去使用到這些數據然后打印到串口。這里要注意一點我使用的是雙邊沿觸發(fā)所以打印出來的不是一個周期,而是兩段占空比的數值。
當然這里我還使用了DMA+PWM進行PWM輸出,如果再講下去就有點多了,這部分內容網上也有很多。
總結
輸入捕獲是高級定時器和通用定時器才有的功能,主要通過設置觸發(fā)邊沿和設置捕獲頻率來對脈寬進行一個測量,得出脈寬之后我們就可以很容易地計算出頻率和周期。