高級時鍾控制定時器TIM1&TIM8簡介:
STM32F4的高級控制定時器包含一個自動重裝載計數器,計數器的輸入是一個被預分頻的系統時鍾。
這個定時器有多種用途,包括車輛輸入信號長度(輸入捕獲模式)或者產生波形輸出(輸出捕獲,PWM,帶死區插入的互補PWM輸出等)
脈沖長度和波形周期可在通過定時器的預分頻器或者RCC的預分頻器在幾個微秒時鍾內調整。
高級控制定時器和通用定時器完全獨立,不共享任何資源。
高級時鍾控制定時器TIM1&TIM8的主要特性:
1、16位向上、向下、雙向自動重裝載計數器2、16位預分頻器,分頻值從1打655353、4個獨立通道4、帶死去輸出的互補輸出5、控制外部信號的同步電路6、剎車輸入7、產生中斷和DMA強求8、可外部觸發
等等。。
TIM定時器確實很強大。至於怎么用,ST的手冊不出奇的難看,完全沒有條理可言。昨天看一天,都沒明白是在說什么。配套的固件庫也是,各種函數的介紹,函數名結構體定義完全沒有邏輯可言。於是只能參照網友的介紹,從最基礎的部分弄起。
參考資料見:STM32入門篇之通用定時器徹底研究
【實驗1、TIM1的計時功能】
【實驗描述】
利用TIM1的技術功能,產生2Hz的中斷每次中斷LED1反轉,LED1反轉頻率為1Hz。
根據時鍾配置,系統時鍾為168MHz,APB2時鍾為84MHz。TIM1掛接在APB2上,所以APB2 時鍾為84MHz。
因此預分頻系數設置成了10000即0x2710,自動重裝載計數器ARR(TIM_Period)設置成了4200即0x1068。每次計數滿產生中斷。
中斷頻率f= 84MHz /4200 / 10000 = 2Hz
【代碼實現】
1、首先開啟TIM1的時鍾
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1,ENABLE);
2、時基單元的初始化
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1,ENABLE);
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_Period = 0x1068;
TIM_TimeBaseInitStructure.TIM_Prescaler = 0x2710;
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0x00;
TIM_TimeBaseInit(TIM1,&TIM_TimeBaseInitStructure);
TIM_TimeBaseInit(TIM1,&TIM_TimeBaseInitStructure);
TIM_ClearFlag(TIM1,TIM_FLAG_Update); //必須先清除配置時候產生的更新標志
TIM_ITConfig(TIM1,TIM_IT_Update,ENABLE); //使能中斷,中斷事件為定時器工薪事件
TIM_Cmd(TIM1,ENABLE); //使能定時器
3、中斷處理函數
沒什么可說的,反轉LED燈而已。每次中斷反轉一次,2Hz的中斷產生1Hz的閃爍。
中斷名字是庫里邊定義的,跟TIM10全局中斷公用。
void TIM1_UP_TIM10_IRQHandler(void)
{
TIM_ClearFlag(TIM1,TIM_FLAG_Update);//進入中斷先清除更新標志
LEDTog(LED1);
}
之后我們就可以看到LED以大約1Hz的頻率在閃爍了。
【實驗2、強制輸出模式實驗】
百度來的強制輸出模式的定義:在程序編程中,IO口一般都可以作為輸入輸出的。而有些數據要在讓其執行時候必須執行,所以讓其強制性的輸出。這是IO口只能做一件事。
看完之后還是一頭霧水。
簡單 點說,就是不管當時IO輸出的是什么,都能強制將其設為0或者為1.
【實驗描述】
為了實驗方便,這個實驗使用TIM4的強制輸出功能,點亮與GPIOD Pin13引腳相連的LED3。對於強制輸出功能,高級定時器和通用定時器是完全一樣的。
TIM4的CH2被復用在GPIOD 的Pin13。所以可以將這個輸出強制為高,將LED點亮。
【代碼實現】
1、首先將GPIO初始化為AF復用功能。
CM4的引腳復用功能和CM3的實現方法不同,要特別注意。按照CM3的寫法將不會有輸出
void TIM4_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_initStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD,ENABLE);
GPIO_initStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_initStructure.GPIO_OType = GPIO_OType_PP;
GPIO_initStructure.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
GPIO_initStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_initStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_Init(GPIOD,&GPIO_initStructure);
GPIO_PinAFConfig(GPIOD,GPIO_PinSource12,GPIO_AF_TIM4);
GPIO_PinAFConfig(GPIOD,GPIO_PinSource13,GPIO_AF_TIM4);
GPIO_PinAFConfig(GPIOD,GPIO_PinSource14,GPIO_AF_TIM4);
GPIO_PinAFConfig(GPIOD,GPIO_PinSource15,GPIO_AF_TIM4);
}
2、TIM4的初始化
這里的時鍾我沒有計算,因為這個實驗不太關注這個。
void TIM4_Config1(void)
{
TIM4_GPIO_Config();
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4,ENABLE);
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_Period =0x1068;
TIM_TimeBaseInitStructure.TIM_Prescaler = 0x2710;
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0x00;
TIM_TimeBaseInit(TIM4,&TIM_TimeBaseInitStructure);
TIM_ARRPreloadConfig(TIM4,ENABLE);
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Active;; //設置成什么模式都行。
TIM_OCInitStructure.TIM_Pulse= 1000;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OC2Init(TIM4,&TIM_OCInitStructure);
TIM_Cmd(TIM4,ENABLE);
}
3、在主函數中強制輸出。
初始化完成之后,在任何時候都能強制引腳電平,只需要一個函數即可:
TIM_ForcedOC2Config(TIM4,TIM_ForcedAction_Active);
這個函數設置TIM的CCMR1的OCxM位為101或者100實現輸出的拉高或拉低
【實驗三、比較輸出】
一直不理解這里的比較是哪兩個東西在進行比較。今天翻ST參考手冊 reference manual發現這么句話:
當OCxM位為000時:The comparison between the output compare register TIMx_CCR1 and the counter TIMx_CNT has no effect on the outputs.
即這里的“比較”是TIMx_CCR1和TIMx_CNT的比較。兩個相等時,觸發事件。這個事件發生時,TIm根據CCMRx寄存器的OCxM位進行輸出。
OCxM不同設置的不同功能如下表所示:
OCxM[2..0]值 | 功能 |
000 | 對輸出不影響 |
001 | 相等時輸出強制為1 |
010 | 相等時輸出強制為0 |
011 | 輸出反轉 |
100 | 不管是否相等,強制為0 |
101 | 不管是否相等,強制為1 |
110 | PWM模式1(先正后負) |
111 | PWM模式2(先負后正) |
結合庫中的定義,可以很方便地改變輸出方式:
#define TIM_OCMode_Timing ((uint16_t)0x0000)
#define TIM_OCMode_Active ((uint16_t)0x0010)
#define TIM_OCMode_Inactive ((uint16_t)0x0020)
#define TIM_OCMode_Toggle ((uint16_t)0x0030)
#define TIM_OCMode_PWM1 ((uint16_t)0x0060)
#define TIM_OCMode_PWM2 ((uint16_t)0x0070)
【實驗現象】
LED周期閃爍
【代碼實現】
只需要將上邊的代碼中的TIM_OCMode改成PWM即可。
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Toggle;
參數TIM_Pulse對這種模式沒影響,TIM_OCPolarity只影響先輸出的是低電平還是高電平。
【實驗4、PWM輸出】
這是輸出部分的傳統了。所有的開發板的TIM例子都是一個PWM輸出。
時基單元好了,設置一下輸出模式,反轉時機(TIM_Pulse)。然后開啟哥哥通道的OC即可。對於每個Tim的所有通道,由於時基配置是一樣的所以只能改變各個通道的占空比。
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;//PWM2也行
TIM_OCInitStructure.TIM_Pulse= 2000;//CCR,設置占空比。反轉模式時候無效
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_ClearFlag(TIM4,TIM_FLAG_CC2);
TIM_OC2Init(TIM4,&TIM_OCInitStructure);
當然,引腳初始化不能少的。
【實驗5、單脈沖方式】
只需要在上邊的代碼之后加一句:
TIM_SelectOnePulseMode(TIM4,TIM_OPMode_Single);
這樣講產生一個負脈沖,效果是LED滅一下之后保持常亮。如果要要讓LED亮一下,輸出正脈沖還需要改下輸出的極性:
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;
到這里,TIM控制輸出的實驗基本就做完了。馬上開始TIM控制輸入部分,包括輸入捕獲、PWM輸入等。