定時器簡述
Stm32f10x系列最多有8個定時器,其中通用定時器有4個,進階定時器有2個,基礎定時器2個。下面這張圖簡要說明三種定時器的主要差別:
STM3 的通用 TIMx :TIM2、TIM3、TIM4 和 TIM5,在低速的APB1總線上
進階定時器:TIM1與TIM8在高速的APB2總線上
基礎定時器:TIM6與TIM7,也在APB1低速的總線上
進階定時器主要功能:
通用定時器功能:
基礎定時器:
需要注意的是基礎定時器隻有累加的功能,産生的中斷也是累加的溢出中斷。進階的定時器與通用定時器的差別是進階定時器多出一個死區控制程式設計。
通用定時器内部結
時鐘發生器:1.來自系統時鐘RCC的TIM_CLK
2.TIMx_ETR外部時鐘輸入端,這引腳隻有定時器2,3,4才有,定時器5沒有。
3.内部觸發輸入口,ITR0、ITR1、ITR2、ITR3是來自定時器級聯的時鐘信号
4.來自TIMx_CH3/TIMx_CH4通道的時鐘輸入(輸入到TI1FP1,TI1FP2端)
時基單元:CK_PSC 的時基經過PSC預分頻器分頻後得到CK_CNT 時基然後給CNT計 數器
輸入捕獲:通過TIMx_CH3通道經過邊沿檢查、濾波器後給捕獲寄存器,其中并不進行預分頻器進行分頻。
輸出比較:也是TIMx_CH3/TIMx_CH4通道,是以要麼輸入捕獲或者輸出比較,隻能其中一種。也就是計數器的值跟捕獲寄存器的值進行比較,達到了捕獲寄存器的值相等時輸出信号。
使用定時器預分頻器和RCC時鐘控制器預分頻器,脈沖長度和波形周期可以在幾個微秒到幾個毫秒間調整。
通用定時器的定時中斷
前面說過定時器的時鐘來源有4個:
1.内部時鐘(CK_INT)
2.外部時鐘模式1:外部輸入腳(TIx)
3.外部時鐘模式2:外部觸發輸入(ETR)
4.内部觸發輸入(ITRx):使用一個定時器作為另一個定時器的預分頻器,如可以配置一個5.定時器Timer1而作為另一個定時器Timer2的預分頻器。
一般主要用到内部時鐘CK_INT
注意:當APB1的分頻系數是1,CK_INT才為36MHz,否則通用定時器的時鐘等于APB1時鐘的2倍。也就是72MHz,還有一點需要注意的是如果你是使用庫函數版本編寫程式,那麼庫函數預設情況下把APB1的時鐘設定為36MHz,也就是說預設分頻系數是2分頻。
上圖中CK_INT等于CK_PSC的時鐘,CK_CNT=(CK_PSC+1)/N
庫函數有個的SystemInit函數用來設定系統時鐘,他在startup_stm32f10x_hd.s彙編檔案中被複位中斷函數調用,也就是在單片機重新開機時,預設設定系統時鐘。
溢出時間:
APB1分頻得到的CK_INT÷(預分頻寄存器TIMx_PSC的值+1)=Tclk
溢出時間計算:
Tout(溢出時間)=(ARR+1)(PSC+1)/Tclk
其中Tclk是APB1的分頻CK_INT,PSC為預分頻系數,ARR為預裝載值
定時器計數模式有多種:向上、向下、中央對齊計數。具體的内容stm32的手冊有詳細的講解。
需要注意的寄存器:
TIMx_CR1寄存器的ARPE位:定時器中的ARR自動重裝載寄存器其實存在兩個寄存器,1個是自動裝載緩存寄存器和1個自動裝載影子寄存器。所謂的影子寄存器是我們使用者無法操作的寄存器,卻又是存在的。加載進CNT計數器的正是影子寄存器。當ARPE=0時,數值直接在寫入自動裝載緩存寄存器的同時也寫入影子寄存器中;當ARPE=1時,當數值寫入自動裝載緩存器後,隻有發生中斷更新事件才更新到影子寄存器中。
設定TIM定時中斷的步驟:
1.使能定時器時鐘APB1的TIMx時鐘
2.初始化定時器,配置ARR,PSC寄存器
3.開啟定時器中斷,配置NVIC中斷管理器
4.使能定時器
5.編寫中斷函數(中斷入口函數在startup_stm32f10x_hd.s中找)
void TIM_Init(u16 arr,u16 psc)
{
NVIC_InitTypeDef NVIC_InitStructure;
RCC->APB1ENR|=0x01; //打開APB1的定時器時鐘
TIM2->CR1|=0x01; //使能計數器,設定為向上計數,從0計數到預裝載值
TIM2->ARR=arr; //自動裝載值
TIM2->DIER|=0x01; //打開UEV中斷更新事件
TIM2->PSC=psc; //預分頻系數
NVIC_InitStructure.NVIC_IRQChannel=TIM2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;
NVIC_Init(&NVIC_InitStructure);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
}
void TIM2_IRQHandler()
{
if(TIM2->SR&0X0001)//溢出中斷
{
LED0=!LED0;
LED1=!LED1;
}
TIM2->SR&=~(1<<0);//清除中斷标志位
}
上面代碼中TIM2->ARR寄存器指派時不能用“|=”,寄存器的值初始值非0,同樣PSC寄存器設定時也不能用“|=”。 TIM2_IRQn在stm32f10x.h中找。
對于庫函數初始化函數中需要解釋地方:
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //設定時鐘分割:TDTS = Tck_tim 其實這句話就是說預分頻CK_PSC時鐘。
PWM模式
PWM的工作過程:
CCR1:捕獲比較(值)寄存器(x=1,2,3,4):設定比較值。
CCMR1: OC1M[2:0]位:
對于PWM方式下,用于設定PWM模式1【110】或者PWM模式2【111】
CCER:CC1P位:輸入/捕獲1輸出極性。0:高電平有效,1:低電平有效。
CCER:CC1E位:輸入/捕獲1輸出使能。0:關閉,1:打開。
PWM模式:PWM輸出有兩種模式:
110:PWM模式1- 在向上計數時,一旦TIMx_CNT<TIMx_CCR1時通道1為有效電平,否則為無效電平;在向下計數時,一旦TIMx_CNT>TIMx_CCR1時通道1為無效電平(OC1REF=0),否則為有效電平(OC1REF=1)。
111:PWM模式2- 在向上計數時,一旦TIMx_CNT<TIMx_CCR1時通道1為無效電平,否則為有效電平;在向下計數時,一旦TIMx_CNT>TIMx_CCR1時通道1為有效電平,否則為無效電平。
注1:一旦LOCK級别設為3(TIMx_BDTR寄存器中的LOCK位)并且CC1S=’00’(該通道配置成輸出)則該位不能被修改。
注2:在PWM模式1或PWM模式2中,隻有當比較結果改變了或在輸出比較模式中從當機模式切換到PWM模式時,OC1REF電平才改變。
注意:
TIMx的輸出通道重映射:
可以檢視資料手冊,中文手冊也有,下面部分截圖
程式代碼:
void TIM_PWM_Init(u16 arr ,u16 psc)
{
RCC->APB1ENR|=1<<1; //打開定時器
RCC->APB2ENR|=1<<0|1<<3; //打開GPIOB、AFIO的時鐘
GPIOB->CRL&=0xff0fffff; //清零CRL寄存器
GPIOB->CRL|=0x00b00000; //設定為複用功能推挽輸出
AFIO->MAPR&=0xfffff3ff; //清零對應位
AFIO->MAPR|=1<<11; //TIM3部分功能重映射
TIM3->ARR=arr; //設定周期
TIM3->PSC=psc; //設定分頻系數
TIM3->CCMR1|=7<<12; //通道2 PWM模式2輸出
TIM3->CCMR1|=1<<11; //CH2預裝載使能
TIM3->CCER|=1<<4; //OC2 輸出使能,高電平有效
TIM3->CR1=0x0080; //ARPE使能
TIM3->CR1|=0x01; //使能定時器3
TIM3->CCR2 =499; //設定比較寄存器的值
}
庫函數版:
GPIO_InitTypeDef GPIO_InitStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE); //打開TIM3的時鐘
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_AFIO,ENABLE); //打開GPIOB、AFIO的時鐘
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP; //複用輸出
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_5; //GPIOB第5腳
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStructure);
//初始化TIM3
TIM_TimeBaseStructure.TIM_Period = arr; //設定在下一個更新事件裝入活動的自動重裝載寄存器周期的值
TIM_TimeBaseStructure.TIM_Prescaler =psc; //設定用來作為TIMx時鐘頻率除數的預分頻值
TIM_TimeBaseStructure.TIM_ClockDivision = 0; //設定時鐘分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上計數模式
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根據TIM_TimeBaseInitStruct中指定的參數初始化TIMx的時間基數機關
GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3,ENABLE); //TIM3端口部分重映射
TIM_OCInitStructure.TIM_OCMode=TIM_OCMode_PWM2; //PWM模式2
TIM_OCInitStructure.TIM_OutputState=TIM_OutputState_Enable; //輸出使能
TIM_OCInitStructure.TIM_OCPolarity=TIM_OCPolarity_High; //高電平有效
TIM_OC3Init(TIM3,&TIM_OCInitStructure);
TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable); //預裝載使能
TIM_Cmd(TIM3, ENABLE); //使能TIM3
未設定比較比對寄存器的值。
定時器輸入捕獲極性
内部結構:
一部分是輸入捕獲通道,可以看到連接配接了一個濾波器,由ICF[3:0]位控制,如果ICF設定為2則表示通道的信号必須出現2次高或者低電平(由寄存器設定具體捕獲的電平變化)後還保持為高或者低則輸入到邊沿檢測器中。
邊沿檢測器的後面緊跟的是通道映射控制器,CC1S[1:0]可以控制IC1具體映射到TI1F或者TI2F或TRC中。通道映射寄存器緊跟着分頻器,用以控制多少個檢測的信号後作為IC1PS通過。
此中f(DTS)采樣頻率由CK_INT輸入,由TIMx_CR1寄存器的CKD[1:0]控制分頻輸入。
輸入捕獲寄存器工作過程概述:通過檢測TIMx_CHx上的邊沿信号,在邊沿信号發生跳變(比如上升沿/下降沿)的時候,将目前定時器的值(TIMx_CNT)存放到對應的捕獲/比較寄存器(TIMx_CCRx)裡面,完成一次捕獲。
代碼:
NVIC_InitTypeDef NVIC_InitStructure;
RCC->APB1ENR|=1<<3; //打開TIM5的時鐘
RCC->APB2ENR|=1<<1; //打開PA口的時鐘
GPIOA->CRL&=0xfffffff0; //清除之前的設定
GPIOA->CRL|=0x00000008; //設定為輸入模式
GPIOA->IDR&=0xfffffffe; //清除之前的設定,同是設定為下拉設定
TIM5->CR1|=0x01; //使能定時器5
TIM5->DIER|=1<<0|1<<1; //開始
TIM5->CCMR1|=0x01; //設定為不濾波,不分頻,映射到Tl1上
TIM5->CCER|=0x01; //使能輸入捕獲,上升沿捕獲不反相
TIM5->ARR=arr; //設定計數周期
TIM5->PSC=psc; //設定預分頻系數
TIM5->DIER|=1<<0|1<<1; //允許更新中斷,允許捕獲中斷
NVIC_InitStructure.NVIC_IRQChannel=TIM5_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;
NVIC_Init(&NVIC_InitStructure);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
void TIM5_IRQHandler()
{
u16 sta;
sta=TIM5->SR; //讀取中斷标志位,判斷是什麼中斷
if((TIM5CH1_CAPTURE_STA&0x80)==0)
{
if(sta&0x01) //溢出中斷
{
if(TIM5CH1_CAPTURE_STA&0x40)
{
if((TIM5CH1_CAPTURE_STA&0x3f)==0x3f) //如果高電平時間太長時間
{
TIM5CH1_CAPTURE_STA|=0x80; //強制标志為捕獲一次
TIM5CH1_CAPTURE_VAL=0xffff;
}
else TIM5CH1_CAPTURE_STA++; //捕獲高電平後還未捕獲低電平,時間溢出加1
}
}
if(sta&0x02)
{
if(TIM5CH1_CAPTURE_STA&0x40) //上次捕獲一個高電平上升沿
{
TIM5CH1_CAPTURE_STA|=0X80; //現在又捕獲一個下降沿電平,标志位設定1
TIM5CH1_CAPTURE_VAL=TIM5->CCR1; //擷取目前的捕獲值.
TIM5->CCER&=~(1<<1); //CC1P=0 設定為上升沿捕獲
}
else{
TIM5CH1_CAPTURE_STA=0;
TIM5CH1_CAPTURE_STA=0x40; //設定捕獲到高電平
TIM5CH1_CAPTURE_VAL=0;
TIM5->CNT=0; //清空計數器
TIM5->CCER|=1<<1; //CC1P=1 設定為下降沿捕獲
}
}
}
TIM5->SR=0;//清除中斷标志位
}