首先從定時器的功能框圖上介紹定時器的基本功能,然后介紹輸出比較模式(輸出PWM)和輸入捕獲模式。最后使用定時器的輸入捕獲功能測量一個脈沖的寬度。
通用定時器框圖如下:( STM32中文參考手冊_V10.pdf P254)
圖1
1.輸出模式
從圖1可以看到通用定時器的基本框圖。框圖比較復雜,把定時器的功能單獨拆分成下圖,首先分析下定時器的輸出比較模式用到的部分。
圖2
(1)左上角第1部分,是定時器的時鍾選擇部分,默認使用內部時鍾(CK_INT),就是使用來自APB1的時鍾,頻率是72M。注意一點,APB1是低速時鍾,最高36M,但是定時器可以到72M。
(2)右上角第2部分,暫時不了解。
(3)第3部分,是定時器的基本配置,叫 時基。這一部分包括了PSC預分頻寄存器、ARR自動重裝寄存器和CNT計數寄存器。無論是使用定時器的哪個功能,這個時都要配置的,所以叫時基。時基部分決定了定時器的頻率、周期。時基和第4部分決定輸出PWM的占空比(輸出模式下)。
(4)第4部分,有4個捕獲\比較寄存器。分別對應定時器的4個通道。這個寄存器在輸出模式和輸入模式下是公用的,輸出模式下,可讀可寫,輸入模式下只讀。輸出pwm時,使用CNT計數寄存器和這個寄存器進行比較,控制pwm的占空比。在第4、5部分之間有一個CCxI和OCxREF(x can be 1 2 3 4 ,對應4個通道),CCxI是產生比較中斷(當CNT寄存器和捕獲\比較寄存器的值相等時),並反轉電平,OCxREF是參考信號(高低電平)。
(5)第5部分,輸出控制。低4部分的OCxREF的參考信號到達本部分的輸出控制時,輸出控制主要做兩部分操作,1:確定有效和無效電平(OCxREF在輸出時就確定了是否是有效電平) 2:確定有效時高電平還是低電平。
(6)第6部分,低5部分輸出的高低電平通過OCx通道輸出最終的信號。
1.1輸出比較模式下的工作流程以及框圖和寄存器對應關系
(1) 對應圖2中 第1部分:72M內部時鍾給定時器提供時鍾,讓定時器工作起來。
(2.1)對應圖2中第3部分:PSC預分頻器進行分頻,定時器可能用不到72M,將72M分頻。PSC是16位的,分頻系數為1-2^16。需要注意的是,對寫入該寄存器的值-1,比如分頻100,寫入99即可,防止此寄存器寫入0。
(2.2)對應圖2中第3部分:自動重裝載寄存器(TIMx_ARR),和計數寄存器。當我們打開定時器時,計數器(TIMx_CNT)的值從0開始自增(向上計數模式),當自增的數值等於自動重裝載寄存器(TIMx_ARR)中的值時,TIMx_CNT將清零並重新自增。PSC預分頻只是確定了定時器的時鍾頻率,想要確定定時器一個脈沖的頻率還要ARR的參與,計算公式如下:
比如 PSC=71+1,ARR=999+1 ,則F = 1KHz。知道了頻率。則周期=1/F = 0.001s (周期*頻率=1)
(3)對應圖2中第4部分: 捕獲/比較寄存器(TIMx_CCRx,x can be 1 2 3 4,對應4個通道)。通過上一步知道了一個脈沖的持續時間是0.001s。那這個脈沖中高電平持續的時間和第電平持續的時間怎么划分,(占空比怎么分),這時候就要捕獲/比較寄存器1(TIMx_CCRx)的參與了。通過上一步,我們知道計數器(TIMx_CNT)的值從0開始自增(向上計數模式),當自增的數值等於自動重裝載寄存器(TIMx_ARR)中的值時,TIMx_CNT將清零並重新自增。現在CNT寄存器的值每自增一次,就和捕獲/比較寄存器(TIMx_CCRx)的值進行比較,當CNT的值<CCRx的值時OCxREF輸出有效或者無效電平。當CNT的值>CCRx的值時OCxREF輸出有效或者無效電平(可配置),並且會產生比較中斷 CCxI,相應的標志位 CCxIF(SR 寄存器中)會置位。那么誰來決定什么時候是有效還是無效電平,對應寄存器如下:
(3.1)補充:pwm周期和占空比的關系如下:ARR=1000,CCRx=600,占空比=60%(高電平持續時間),想要改變占空比就修改CCR,想要修改周期(一個高電平+低電平的持續時間就修改ARR和PSC)
(4)圖2中,第4部分輸出的OCREFx電壓(有效or無效)到第5部分的輸出控制部分。輸出控制部分決定有效電平是高電平or低電平。
(5)圖2中,第5部分把最終的信號輸出到通道CHx where x can be 1 2 3 4。
2.輸入捕獲
輸入捕獲框圖
輸入捕獲模式和上面的輸出pwm模式都是定時器的具體功能,所以在定時器功能框圖上有些是共用的,比如途中的紅色框圖1屬於定時器的基本部分,無論那種模式都是必須要配置的,這部分覺得定時器的頻率和周期。剩下的2,3,4,5再來具體說一下。
(1)左邊紅色框圖2部分是,用來作為輸入捕獲的引腳。定時器想要捕獲外部的信號,需要有個通道,也就是對應的IO引腳。具體對應關系在數據手冊的pin map and description中。
(2)左邊紅色框圖3部分是,用來設置被捕獲的信號的檢測方式和濾波方式。當輸入的信號存在高頻干擾的時候,我們需要對輸入信號進行濾波,即進行重新采樣,根據采樣定律,采樣的頻率必須大於等於兩倍的輸入信號。比如輸入的信號為 1M,又存在高頻的信號干擾,那么此時就很有必要進行濾波,我們可以設置采樣頻率為 2M,這樣可以在保證采樣到有效信號的基礎上把高於 2M 的高頻干擾信號過濾掉。
14.4.1 控制寄存器 1(TIMx_CR1) 位9:8
14.4.7 捕獲/比較模式寄存器1(TIMx_CCMR1) 位7:4
疑惑:圖中第3.4部分中間的TF1FP1 TF1FP2是什么意思,舉個例子:輸入捕獲通道1叫TI1,從這個信號捕捉到的信號經過通道1的濾波和邊沿檢測器之后,輸出2個完全相同的信號,分別叫TF1FP1、 TF1FP2。TF1FP1輸入到第4部分的通道1(IC1),TF1FP2輸入到第4部分的通道2(IC2)。這個是用來作為定時器的捕獲PWM的功能。詳細介紹https://blog.csdn.net/guosir_/article/details/78407063
(3)第4部分。輸入捕獲的通道ICx。ICx通道和TI通道的區別:TI通道是直接輸入的信號,TI通道直接輸入的信號進行濾波后到達ICx通道。最終ICx通道的信號到達定時器內部。在第4部分可以進行預分頻設置。ICx 的輸出信號會經過一個預分頻器,用於決定發生多少個事件時進行一次捕獲。具體的由寄存器 CCMRx 的位 ICxPSC 配置,如果希望捕獲信號的每一個邊沿,則不分頻。
14.4.7 捕獲/比較模式寄存器1(TIMx_CCMR1) 位3:2
(4)第4部分進行分頻之后就會得到最終的信號,叫ICxPS,如果使能了捕獲中斷則會進入中斷。
14.4.4 DMA/中斷使能寄存器(TIMx_DIER)
(5)經過預分頻器的信號 ICxPS 是最終被捕獲的信號,當發生捕獲時(第一次),計數器CNT 的值會被鎖存到捕獲寄存器 CCR 中,還會產生 CCxI 中斷,相應的中斷位 CCxIF(在SR 寄存器中)會被置位,通過軟件或者讀取 CCR 中的值可以將 CCxIF 清 0。如果發生第
二次捕獲(即重復捕獲: CCR 寄存器中已捕獲到計數器值且 CCxIF 標志已置 1),則捕獲溢出標志位 CCxOF(在 SR 寄存器中)會被置位, CCxOF 只能通過軟件清零。
第5部分的CCRx寄存器是輸出模式和輸入捕獲模式共用的。
3.輸入捕獲測量一個脈沖的寬度。
3.1輸出占空比為60%的PWM
使用定時器輸出一個10KHz,占空比為60%的PWM
定時器2的ch2對應PA1
1 TIM2_PWM_Init(1000-1,71);//頻率1khz 周期0.001s->1ms 占空比60%,高電平0.6ms,低電平0.4ms
函數實現如下:
1 void TIM2_PWM_Init(u16 arr,u16 psc) 2 { 3 GPIO_InitTypeDef GPIO_InitStructure; 4 TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; 5 TIM_OCInitTypeDef TIM_OCInitStructure; 6 7 8 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //使能定時器3時鍾 9 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE); //使能GPIO外設和AFIO復用功能模塊時鍾 10 11 //GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE); //Timer3部分重映射 TIM3_CH2->PB5 12 13 //設置該引腳為復用輸出功能,輸出TIM3 CH2的PWM脈沖波形 GPIOB.5 14 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; //TIM_CH2 15 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //復用推挽輸出 16 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; 17 18 GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIO 19 20 21 //初始化TIM3 22 TIM_TimeBaseStructure.TIM_Period = arr; //設置在下一個更新事件裝入活動的自動重裝載寄存器周期的值 23 TIM_TimeBaseStructure.TIM_Prescaler =psc; //設置用來作為TIMx時鍾頻率除數的預分頻值 24 TIM_TimeBaseStructure.TIM_ClockDivision = 0; //設置時鍾分割:TDTS = Tck_tim 25 TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上計數模式 26 TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); //根據TIM_TimeBaseInitStruct中指定的參數初始化TIMx的時間基數單位 27 28 //初始化TIM3 Channel2 PWM模式 29 TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //選擇定時器模式:TIM脈沖寬度調制模式2 30 TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比較輸出使能 31 TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //輸出極性:TIM輸出比較極性高 32 TIM_OC2Init(TIM2, &TIM_OCInitStructure); //根據T指定的參數初始化外設TIM3 OC2 33 //TIM_OC1Init(TIM5, &TIM_OCInitStructure); //根據T指定的參數初始化外設TIM3 OC2 34 35 TIM_OC2PreloadConfig(TIM2, TIM_OCPreload_Enable); //使能TIM3在CCR2上的預裝載寄存器 36 37 TIM_SetCompare2(TIM2,600-1); 38 39 TIM_Cmd(TIM2, ENABLE); //使能TIM3 40 41 42 }
結果:
先輸出高電平還是先輸出低電平受到這三個參數的影響
TIM_CounterMode_Up
TIM_OCMode_PWM1
TIM_OCPolarity_High
這里遇到了一個問題,先記錄一下,stm32f103zet6芯片,測試TIM2 TIM3 TIM4 都可以輸出4路PWM,但是TIM5始終沒有輸出。 我以為是TIM5的引腳連接了其他硬件導致的。
但是TIM2 ch2和TIM5的ch2是同一個引腳,查看數據手冊,這幾個定時器都在APB2上,時鍾沒有問題。TIM5的更新中斷可CC中斷都可以進,就是引腳沒有pwm輸出。先記錄下吧。后來測試TIM5 ch4 pa3的輸入捕獲可以正常使用。。。再后來測試,使用HAL庫,TIM5正常工作。
3.2捕獲脈沖時間
在開始捕獲時,清空CNT計數器。在捕獲結束時,記錄CCRx寄存器的數值,計數器CNT中間可能會多次溢出,要計算一下溢出的次數*每次溢出的最大值,再加上當前CCRx寄存器的數值。這么多數值,再*每次計數需要的時間(1/定時器頻率),以下代碼取自野火資料。
定時器更新中斷和捕獲中斷處理函數:
1 void GENERAL_TIM_INT_FUN(void) 2 { 3 // 當要被捕獲的信號的周期大於定時器的最長定時時,定時器就會溢出,產生更新中斷 4 // 這個時候我們需要把這個最長的定時周期加到捕獲信號的時間里面去 5 if ( TIM_GetITStatus ( GENERAL_TIM, TIM_IT_Update) != RESET ) 6 { 7 TIM_ICUserValueStructure.Capture_Period ++; 8 TIM_ClearITPendingBit ( GENERAL_TIM, TIM_FLAG_Update ); 9 } 10 11 // 上升沿捕獲中斷 12 if ( TIM_GetITStatus (GENERAL_TIM, GENERAL_TIM_IT_CCx ) != RESET) 13 { 14 // 第一次捕獲 15 if ( TIM_ICUserValueStructure.Capture_StartFlag == 0 ) 16 { 17 // 計數器清0 18 TIM_SetCounter ( GENERAL_TIM, 0 ); 19 // 自動重裝載寄存器更新標志清0 20 TIM_ICUserValueStructure.Capture_Period = 0; 21 // 存捕獲比較寄存器的值的變量的值清0 22 TIM_ICUserValueStructure.Capture_CcrValue = 0; 23 24 // 當第一次捕獲到上升沿之后,就把捕獲邊沿配置為下降沿 25 GENERAL_TIM_OCxPolarityConfig_FUN(GENERAL_TIM, TIM_ICPolarity_Falling); 26 // 開始捕獲標准置1 27 TIM_ICUserValueStructure.Capture_StartFlag = 1; 28 } 29 // 下降沿捕獲中斷 30 else // 第二次捕獲 31 { 32 // 獲取捕獲比較寄存器的值,這個值就是捕獲到的高電平的時間的值 33 TIM_ICUserValueStructure.Capture_CcrValue = GENERAL_TIM_GetCapturex_FUN (GENERAL_TIM); 34 35 // 當第二次捕獲到下降沿之后,就把捕獲邊沿配置為上升沿,好開啟新的一輪捕獲 36 GENERAL_TIM_OCxPolarityConfig_FUN(GENERAL_TIM, TIM_ICPolarity_Rising); 37 // 開始捕獲標志清0 38 TIM_ICUserValueStructure.Capture_StartFlag = 0; 39 // 捕獲完成標志置1 40 TIM_ICUserValueStructure.Capture_FinishFlag = 1; 41 } 42 43 TIM_ClearITPendingBit (GENERAL_TIM,GENERAL_TIM_IT_CCx); 44 } 45 }
1 // 定時器輸入捕獲用戶自定義變量結構體聲明 2 typedef struct 3 { 4 uint8_t Capture_FinishFlag; // 捕獲結束標志位 5 uint8_t Capture_StartFlag; // 捕獲開始標志位 6 uint16_t Capture_CcrValue; // 捕獲寄存器的值 7 uint16_t Capture_Period; // 自動重裝載寄存器更新標志 8 }TIM_ICUserValueTypeDef;
再測試時發現,tim5的捕獲功能可用。可以排除不是 時鍾,引腳,通道錯誤導致tim5 pwm不能用的問題。先記錄下吧。
參考資料