- 硬件:stm32f103c8t6
- 開發工具:Keil uVision4
- 下載調試工具:ARM仿真器
如果第一次接觸定時器,可以先看基本定時器。本篇內容較多,如果想直接動手操作,可以跳到後面的實驗代碼。
stm32標準庫對定時器外設建立了4個初始化結構體,定時器分爲基本定時器、通用定時器、高級定時器,針對不用的定時器要使用不同初始化結構體。下面是4個初始化結構體的適用分類:
TIM_TimeBaseInitTypeDef (基本定時器、通用定時器、高級定時器)
TIM_OCInitTypeDef (通用定時器、高級定時器)
TIM_ICInitTypeDef (通用定時器、高級定時器)
TIM_BDTRInitTypeDef (高級定時器)
使用哪一個定時器,就配置所有相應的初始化結構體,然後調用初始化函數。下面先理解每個結構體成員的含義和其取值範圍。
定時器基本初始化結構體 >> TIM_TimeBaseInitTypeDef
typedef struct
{
uint16_t TIM_Prescaler; /*定時器預分頻設置。 value:(0~0xFFFF)*/
uint16_t TIM_CounterMode;/*選擇了計數器模式。Value:
#define TIM_CounterMode_Up ((uint16_t)0x0000) //TIM 向上計數模式
#define TIM_CounterMode_Down ((uint16_t)0x0010) //TIM 向下計數模式
#define TIM_CounterMode_CenterAligned1 ((uint16_t)0x0020) //TIM 中央對齊模式 1 計數模式
#define TIM_CounterMode_CenterAligned2 ((uint16_t)0x0040) //TIM 中央對齊模式 2 計數模式*/
uint16_t TIM_Period; /*設置了在下一個更新事件裝入活動的自動重裝載寄存器週期的值。value:0x0000~0xFFFF*/
uint16_t TIM_ClockDivision; /*設置定時器時鐘CK_INT頻率與死區發生器以及數字濾波器採樣時鐘頻率分頻化。Value:
#define TIM_CKD_DIV1 ((uint16_t)0x0000)
#define TIM_CKD_DIV2 ((uint16_t)0x0100)
#define TIM_CKD_DIV4 ((uint16_t)0x0200)*/
uint8_t TIM_RepetitionCounter; /*是否使用重複定時器,當該值不爲0的時候,計數器計數值達到週期數時,該值減1,計數器重新計數,當該值減到0的時候纔會產生事件。*/
} TIM_TimeBaseInitTypeDef;
備註: 若是主頻72MHz、TIM_Prescaler的值設置爲(72-1),則定時器時鐘頻率=72MHz/72=1MHz。知道頻率就可以算週期了,用1/1MHz 乘以TIM_Period的值就是定時的時間(週期),如果加入了重複定時器,那麼還要乘以TIM_RepetitionCounter的值纔是定時的時間(週期)。
定時器比較輸出初始化結構體 >> TIM_OCInitTypeDef
typedef struct
{
uint16_t TIM_OCMode;/*比較輸出模式選擇,共8種。value:
#define TIM_OCMode_Timing ((uint16_t)0x0000) // TIM 輸出比較時間模式
#define TIM_OCMode_Active ((uint16_t)0x0010) //TIM 輸出比較主動模式
#define TIM_OCMode_Inactive ((uint16_t)0x0020) //TIM 輸出比較非主動模式
#define TIM_OCMode_Toggle ((uint16_t)0x0030) //TIM 輸出比較觸發模式
#define TIM_OCMode_PWM1 ((uint16_t)0x0060) //TIM 脈衝寬度調製模式 1
#define TIM_OCMode_PWM2 ((uint16_t)0x0070) //TIM 脈衝寬度調製模式 2*/
uint16_t TIM_OutputState;/*比較輸出使能,決定信號是否通過外部引腳輸出。value:0(Disable)、1(Enable)。 */
uint16_t TIM_OutputNState; /*比較互補輸出使能,決定互補信號是否通過外部引腳輸出。value:0(Disable)、1(Enable)。*/
uint16_t TIM_Pulse; /*比較輸出的脈衝寬度,設置佔空比。Value:0x0000~0xFFFF*/
uint16_t TIM_OCPolarity; /*比較輸出極性,決定定時器通道有效電平的極性。Value:
#define TIM_OCPolarity_High ((uint16_t)0x0000)
#define TIM_OCPolarity_Low ((uint16_t)0x0002)*/
uint16_t TIM_OCNPolarity; /*比較互補輸出極性,可選高電平有效、低電平有效。Value:
#define TIM_OCNPolarity_High ((uint16_t)0x0000)
#define TIM_OCNPolarity_Low ((uint16_t)0x0008)*/
uint16_t TIM_OCIdleState; /*空閒狀態時通道輸出電平設置,可選高電平、低電平。Value:
#define TIM_OCNPolarity_High ((uint16_t)0x0000)
#define TIM_OCNPolarity_Low ((uint16_t)0x0008)*/
uint16_t TIM_OCNIdleState; /*空閒狀態時互補通道輸出電平設置,可選高電平、低電平,設定值必須跟TIM_OCIdleState相反。Value:
#define TIM_OCNIdleState_Set ((uint16_t)0x0200)
#define TIM_OCNIdleState_Reset ((uint16_t)0x0000)*/
} TIM_OCInitTypeDef;
備註:設置TIM_Pulse的值就可以改變輸出波形的佔空比了,如果不使用重複定時器,那麼佔空比=(TIM_Pulse+1)/(TIM_Period+1)x100%。
定時器輸入捕獲初始化結構體 >> TIM_ICInitTypeDef
typedef struct
{
uint16_t TIM_Channel; /*輸入通道選擇,共4個通道。Value:
#define TIM_Channel_1 ((uint16_t)0x0000) //使用 TIM 通道 1
#define TIM_Channel_2 ((uint16_t)0x0004) //使用 TIM 通道 2
#define TIM_Channel_3 ((uint16_t)0x0008) //使用 TIM 通道 3
#define TIM_Channel_4 ((uint16_t)0x000C) //使用 TIM 通道 4*/
uint16_t TIM_ICPolarity; /*輸入捕獲邊沿觸發選擇,可選上升沿觸發、下降沿觸發。Value:
#define TIM_ICPolarity_Rising ((uint16_t)0x0000) //TIM 輸入捕獲上升沿
#define TIM_ICPolarity_Falling ((uint16_t)0x0002) //TIM 輸入捕獲下降沿*/
uint16_t TIM_ICSelection; /*輸入通道選擇,共3個通道。Value:
#define TIM_ICSelection_DirectTI ((uint16_t)0x0001) //TIM 輸入 2,3 或 4 選擇對應地與 IC1 或 IC2 或IC3 或 IC4 相連
#define TIM_ICSelection_IndirectTI ((uint16_t)0x0002) //TIM 輸入 2,3 或 4 選擇對應地與 IC2 或 IC1 或IC4 或 IC3 相連
#define TIM_ICSelection_TRC ((uint16_t)0x0003) //TIM 輸入 2,3 或 4 選擇與 TRC 相連*/
uint16_t TIM_ICPrescaler; /*輸入捕獲通道預分頻,共(1、2、4、8)種。Value:
#define TIM_ICPSC_DIV1 ((uint16_t)0x0000) //TIM 捕獲在捕獲輸入上每探測到一個邊沿執行一次
#define TIM_ICPSC_DIV2 ((uint16_t)0x0004) // TIM 捕獲每 2 個事件執行一次
#define TIM_ICPSC_DIV4 ((uint16_t)0x0008) //TIM 捕獲每 4 個事件執行一次
#define TIM_ICPSC_DIV8 ((uint16_t)0x000C) //TIM 捕獲每 8 個事件執行一次*/
uint16_t TIM_ICFilter; /*輸入捕獲濾波器設置,value:0x0~0x0F。一般不用,設置爲0*/
} TIM_ICInitTypeDef;
備註:定時器捕獲信號,可以測量輸入信號的脈寬和測量PWM輸入信號的頻率和佔空比;信號的來源可以來自其他的定時器或者外部引腳;濾波器的作用是排除高頻的干擾,採樣的頻率必須大於等於兩倍的輸入信號。
斷路和死區初始化結構體 >>TIM_BDTRInitTypeDef
typedef struct
{
uint16_t TIM_OSSRState;/*運行模式下關閉狀態選擇。Value:
#define TIM_OSSRState_Enable ((uint16_t)0x0800)
#define TIM_OSSRState_Disable ((uint16_t)0x0000)*/
uint16_t TIM_OSSIState;/*空閒模式下關閉狀態選擇。Value:
#define TIM_OSSIState_Enable ((uint16_t)0x0400)
#define TIM_OSSIState_Disable ((uint16_t)0x0000)*/
uint16_t TIM_LOCKLevel;/*鎖定配置。Value:
#define TIM_LOCKLevel_OFF ((uint16_t)0x0000)
#define TIM_LOCKLevel_1 ((uint16_t)0x0100)
#define TIM_LOCKLevel_2 ((uint16_t)0x0200)
#define TIM_LOCKLevel_3 ((uint16_t)0x0300)*/
uint16_t TIM_DeadTime;/*死區時間。Vlaue:0x0~0xFF*/
uint16_t TIM_Break;/*短路輸入使能控制。Value:
#define TIM_Break_Enable ((uint16_t)0x1000)
#define TIM_Break_Disable ((uint16_t)0x0000)*/
uint16_t TIM_BreakPolarity;/*斷路輸出極性。Value:
#define TIM_BreakPolarity_Low ((uint16_t)0x0000)
#define TIM_BreakPolarity_High ((uint16_t)0x2000)*/
uint16_t TIM_AutomaticOutput;/*自動輸出使能。Value:
#define TIM_AutomaticOutput_Enable ((uint16_t)0x4000)
#define TIM_AutomaticOutput_Disable ((uint16_t)0x0000)*/
} TIM_BDTRInitTypeDef;
備註:
上圖的黑影部分是死區時間,死區時間是根據與輸出信號相連接的器件及其特性來調整的,爲了避免OCx和OCxN同時改變時,造成外部器來不及反應所造成的短路問題。
下面通過幾個實例,進一步理解初始化結構體成員的含義,熟悉定時器的使用的流程。
實驗一.PWM互補輸出實驗(帶死區和斷路功能)
一. 設計要求:
雙通道互補PWM,帶死區和剎車功能。
週期:100ms
佔空比:通道一爲25%,通道二爲60%
死區持續時間:12.8ms
二. 硬件設計:
採用TIM1的通道一和通道二。
PA8、PB13引腳是TIM1通道一的互補輸出。PA9、PB14引腳是TIM1通道二的互補輸出。爲增加斷路功能,需要用到TIM1_BKIN引腳,PB12。
三. 設計步驟:
- 定時器IO口配置。
- 配置時基結構體:TIM_TimeBaseInitTypeDef
- 配置輸出比較結構體:TIM_OCInitTypeDef
- 配置斷路和死區結構體:TIM_BDTRInitTypeDef
- 啓動定時器,輸出使能。
>>死區持續時間的計算:
TIM_DeadTime的就是配置寄存器DTG[7:0]的值,是定時器時鐘,是死區發生器的時鐘,DT是死區持續時間。
本實驗定時器時鐘配置爲10kHz,死區持續時間需要配置成12.8ms,我這裏選用第二條公式,具體選用那一條公式,由DTG[7:5]這幾個bit位決定,如上圖。
根據公式 DTG[7:5]=10x => DT=(64+DTG[5:0]) × Tdtg,Tdtg = 2 × TDTS
那麼先計算 =2=2*(1/10kHz)=0.2ms
把參數代入公式 DT=(64+DTG[5:0]) × Tdtg,可算出DTG[5:0]=0,即DTG[7:0]=0x80.
創建TIM_test.h
#ifndef __TIM_TEST_H
#define __TIM_TEST_H
#include "stm32f10x.h"
void GPIO_Configuration(void);
void TIME_Configuration(void);
#endif
創建TIM_test.c
void GPIO_Configuration(void)
{
/*定義一個GPIO_InitTypeDef類型的結構體*/
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOA| \
RCC_APB2Periph_GPIOC, ENABLE);//開啓GPIOC的外設時鐘
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //設置引腳模式爲通用推輓輸出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14;
GPIO_Init(GPIOB, &GPIO_InitStructure); //調用庫函數,初始化GPIOC
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9;
GPIO_Init(GPIOA, &GPIO_InitStructure); //調用庫函數,初始化GPIOC
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
GPIO_Init(GPIOB, &GPIO_InitStructure); //調用庫函數,初始化GPIOC
GPIO_SetBits(GPIOB,GPIO_Pin_12);
}
#define ADVANCE_TIM TIM1
void TIME_Configuration(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_BDTRInitTypeDef TIM_BDTRInitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE); //開啓TIM1時鐘
//時基結構體配置
TIM_TimeBaseStructure.TIM_Period = 1000-1; //從0開始計數 一個週期1000次
TIM_TimeBaseStructure.TIM_Prescaler =(7200-1); //計數器頻率爲10kHz 定時器時鐘:72MHz/7200=10kHz 週期:(1/10kHz)*1000=100ms
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //不需要分頻
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //計數方式 向上計數
TIM_TimeBaseStructure.TIM_RepetitionCounter=0; //重複計數器
TIM_TimeBaseInit(ADVANCE_TIM, &TIM_TimeBaseStructure); //調用庫函數,初始化TIM1
//輸出比較結構體配置
TIM_OCInitStructure.TIM_OCMode=TIM_OCMode_PWM1;//選擇PWM1模式
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;//信號輸出使能
TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable;//互補信號輸出使能
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_High;
TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set;
TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCNIdleState_Reset;
//初始化TIM1的通道一
TIM_OCInitStructure.TIM_Pulse = 250-1; //佔空比=250/1000=25%
TIM_OC1Init(ADVANCE_TIM,&TIM_OCInitStructure);
TIM_OC1PreloadConfig(ADVANCE_TIM,TIM_OCPreload_Enable);
//初始化TIM1通道二
TIM_OCInitStructure.TIM_Pulse = 600-1; //佔空比=600/1000=60%
TIM_OC2Init(ADVANCE_TIM,&TIM_OCInitStructure);
TIM_OC2PreloadConfig(ADVANCE_TIM,TIM_OCPreload_Enable);
//死區和短路結構體配置
TIM_BDTRInitStructure.TIM_OSSRState = TIM_OSSRState_Enable;
TIM_BDTRInitStructure.TIM_OSSIState = TIM_OSSIState_Enable;
TIM_BDTRInitStructure.TIM_LOCKLevel = TIM_LOCKLevel_1;
TIM_BDTRInitStructure.TIM_DeadTime = 0x80;
TIM_BDTRInitStructure.TIM_Break = TIM_Break_Enable;
TIM_BDTRInitStructure.TIM_BreakPolarity = TIM_BreakPolarity_Low;//低電平有效,如果引腳檢測到高電平則會停止PWM的輸出,不會產生任何波形
TIM_BDTRInitStructure.TIM_AutomaticOutput = TIM_AutomaticOutput_Enable;
TIM_BDTRConfig(ADVANCE_TIM,&TIM_BDTRInitStructure);
//使能定時器,計數器開始計數
TIM_Cmd(ADVANCE_TIM, ENABLE);
//主動輸出使能
TIM_CtrlPWMOutputs(ADVANCE_TIM,ENABLE);
}
創建main.c
#include "stm32f10x.h"
#include “TIM_test.h”
int main(void)
{
GPIO_Configuration(); //IO口配置
TIME_Configuration(); //定時器配置
while(1){
}
}
四. 實驗結果:
把程序編譯燒錄到開發板裏面出,再將PA8,PB13引腳和PA9,PB14引腳接到示波器,並把短路輸入引腳PB12拉低,示波器和開發板共地。也可以先在keil裏面進行軟件仿真。用keil4 模擬仿真顯示引腳波形輸出分析的步驟
軟件仿真:
週期:100ms
死區持續時間:12.8ms
佔空比(通道一):25%
佔空比(通道二):60%
硬件調試:
信號1接的是PB13引腳,信號2接的是PA8引腳,調整示波器到合適的參數。
調試短路功能可以將PB12引腳處接高電平,那麼定時器就會停止產生波形,以達到剎車的功能。高級定時器的PWM輸出的功能特性天生就是用來控制電機的。
一個高級定時器可以輸出多路PWM,但是他們的週期是一樣的,佔空比可以不一樣,在運行過程中每個通道的佔空比是可以通過調用函數來改變。通用定時器跟高級定時器的區別是前者沒有死區和斷路功能,其他都是一樣。
水平有限,僅供參考,錯誤之處以及不足之處還望多多指教。