普通的輸入捕獲,可使用定時器的四個通道,一路捕獲占用一個捕獲寄存器.
PWM輸入,只能使用兩個通道,通道1和通道2。
一路PWM輸入占用兩個捕獲寄存器,一個捕獲周期,一個捕獲占空比。
這里,用通用定時器產生一路PWM信號,用高級定時器的通道1或通道2捕獲。
通用定時器TIM3的通道1,PA6,用於輸出PWM信號。
高級控制定時器TIM1的通道1,PA8,用於PWM輸入捕獲。
bsp_ AdvanceTim.c文件,高級定時器PWM輸入捕獲驅動程序。
bsp_ GeneralTim.c文件,通用定時器PWM信號輸出驅動程序。
通用定時器產生PWM配置
高級定時器PWM輸入配置
中斷服務程序,計算測量的頻率和占空比。
關鍵,PWM信號輸出,PWM信號輸入捕獲。
通用定時器宏定義:
PWM 輸出,就是對外輸出脈寬(即占空比)可調的方波信號,信號頻率由自動重裝寄存器ARR的值決定,占空比由比較寄存器CCR的值決定。可看之前寫的高級定時器基礎知識。
可算出PWM信號的頻率F:72M/( 10*72 )=100KHZ。
PWM 信號的周期 T = (ARR+1) * (1/CLK_cnt) = (ARR+1) * (PSC+1) / 72M
注釋:
定時器時鍾經過PSC預分頻器后,即CK_CNT,用來驅動計數器計數。
PSC是16位的預分頻器,可以對定時器時鍾TIMxCLK進行1~65536之間的任何一個數進行分頻。CK_CNT=TIMxCLK/(PSC+1)。
定時器時鍾TIMxCLK,即內部時鍾CK_INT,經APB1預分頻器分頻提供。
APB1預分頻系數等於1,頻率不變。
庫函數中APB1預分頻的系數是2,即PCLK1=36M,所以定時器時鍾TIMxCLK=36*2=72M。
計一個數的時間乘上一個波形的計數次數(ARR+1),就是整個波形的所需時間。
時間取倒數就是PWM信號的頻率。
以CNT工作在遞增模式為例,上圖,ARR=8,CCR=4,CNT從0開始計數.
當CNT<CCR,OCxREF為有效的高電平,同時,比較中斷寄存器CCxIF置位。
CCR<=CNT<=ARR,OCxREF為無效的低電平。CNT又從0開始計數,並生成計數器上溢事件,循環往復。
占空比:GENERAL_TIM_CCR1/(GENERAL_TIM_PERIOD+1)= 50%
// 自動重裝載寄存器的值,累計TIM_Period+1個頻率后產生一個更新或者中斷 TIM_TimeBaseStructure.TIM_Period=GENERAL_TIM_PERIOD; #define GENERAL_TIM_PERIOD 0XFFFF
/************通用定時器TIM參數定義,只限TIM2、3、4、5************/ // 當使用不同的定時器的時候,對應的GPIO是不一樣的,這點要注意 // 這里默認使用TIM3 #define GENERAL_TIM TIM3 #define GENERAL_TIM_APBxClock_FUN RCC_APB1PeriphClockCmd #define GENERAL_TIM_CLK RCC_APB1Periph_TIM3 // 輸出PWM的頻率為 72M/{ (ARR+1)*(PSC+1) } #define GENERAL_TIM_PERIOD (10-1) #define GENERAL_TIM_PSC (72-1) #define GENERAL_TIM_CCR1 5 #define GENERAL_TIM_CCR2 4 #define GENERAL_TIM_CCR3 3 #define GENERAL_TIM_CCR4 2 // TIM3 輸出比較通道1 #define GENERAL_TIM_CH1_GPIO_CLK RCC_APB2Periph_GPIOA #define GENERAL_TIM_CH1_PORT GPIOA #define GENERAL_TIM_CH1_PIN GPIO_Pin_6
通用定時器引腳初始化,初始化了通用定時器PWM輸出用到的GPIO,使用不同的GPIO時,只需修改頭文件里的宏定義。
/** * @brief 通用定時器PWM輸出用到的GPIO初始化 * @param 無 * @retval 無 */ static void GENERAL_TIM_GPIO_Config(void) { GPIO_InitTypeDef GPIO_InitStructure; // 輸出比較通道1 GPIO 初始化 RCC_APB2PeriphClockCmd(GENERAL_TIM_CH1_GPIO_CLK, ENABLE); GPIO_InitStructure.GPIO_Pin = GENERAL_TIM_CH1_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GENERAL_TIM_CH1_PORT, &GPIO_InitStructure); // 輸出比較通道2 GPIO 初始化 RCC_APB2PeriphClockCmd(GENERAL_TIM_CH2_GPIO_CLK, ENABLE); GPIO_InitStructure.GPIO_Pin = GENERAL_TIM_CH2_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GENERAL_TIM_CH2_PORT, &GPIO_InitStructure); // 輸出比較通道3 GPIO 初始化 RCC_APB2PeriphClockCmd(GENERAL_TIM_CH3_GPIO_CLK, ENABLE); GPIO_InitStructure.GPIO_Pin = GENERAL_TIM_CH3_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GENERAL_TIM_CH3_PORT, &GPIO_InitStructure); // 輸出比較通道4 GPIO 初始化 RCC_APB2PeriphClockCmd(GENERAL_TIM_CH4_GPIO_CLK, ENABLE); GPIO_InitStructure.GPIO_Pin = GENERAL_TIM_CH3_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GENERAL_TIM_CH3_PORT, &GPIO_InitStructure); }
通用定時器PWM輸出初始化。
GENERAL_TIM_Mode_Config()函數中初始化了兩個結構體,時基結構體和輸出比較結構體。
若修改PWM的周期和占空比,需修改頭文件里面的GENERAL_TIM_PERIOD、GENERAL_TIM_PSC和GENERAL_TIM_CCR1這三個宏。
PWM信號的頻率的計算公式為:F=TIM_CLK/{(ARR+1)*(PSC+1)},TIM_CLK=72MHZ,ARR是自動重裝載寄存器的值,對應GENERAL_TIM_PERIOD,PSC是計數器時鍾的分頻因子,對應GENERAL_TIM_PSC。
/** * @brief 通用定時器PWM輸出初始化 * @param 無 * @retval 無 * @note */ static void GENERAL_TIM_Mode_Config(void) { // 開啟定時器時鍾,即內部時鍾CK_INT=72M GENERAL_TIM_APBxClock_FUN(GENERAL_TIM_CLK,ENABLE); /*--------------------時基結構體初始化-------------------------*/ // 配置周期,這里配置為100K TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; // 自動重裝載寄存器的值,累計TIM_Period+1個頻率后產生一個更新或者中斷 TIM_TimeBaseStructure.TIM_Period=GENERAL_TIM_PERIOD; // 驅動CNT計數器的時鍾 = Fck_int/(psc+1) TIM_TimeBaseStructure.TIM_Prescaler= GENERAL_TIM_PSC; // 時鍾分頻因子 ,配置死區時間時需要用到 TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1; // 計數器計數模式,設置為向上計數 TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up; // 重復計數器的值,沒用到不用管 TIM_TimeBaseStructure.TIM_RepetitionCounter=0; // 初始化定時器 TIM_TimeBaseInit(GENERAL_TIM, &TIM_TimeBaseStructure); /*--------------------輸出比較結構體初始化-------------------*/ TIM_OCInitTypeDef TIM_OCInitStructure; // 配置為PWM模式1 TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; // 輸出使能 TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; // 輸出通道電平極性配置 TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; // 輸出比較通道 1 TIM_OCInitStructure.TIM_Pulse = GENERAL_TIM_CCR1; TIM_OC1Init(GENERAL_TIM, &TIM_OCInitStructure); TIM_OC1PreloadConfig(GENERAL_TIM, TIM_OCPreload_Enable); // 輸出比較通道 2 TIM_OCInitStructure.TIM_Pulse = GENERAL_TIM_CCR2; TIM_OC2Init(GENERAL_TIM, &TIM_OCInitStructure); TIM_OC2PreloadConfig(GENERAL_TIM, TIM_OCPreload_Enable); // 輸出比較通道 3 TIM_OCInitStructure.TIM_Pulse = GENERAL_TIM_CCR3; TIM_OC3Init(GENERAL_TIM, &TIM_OCInitStructure); TIM_OC3PreloadConfig(GENERAL_TIM, TIM_OCPreload_Enable); // 輸出比較通道 4 TIM_OCInitStructure.TIM_Pulse = GENERAL_TIM_CCR4; TIM_OC4Init(GENERAL_TIM, &TIM_OCInitStructure); TIM_OC4PreloadConfig(GENERAL_TIM, TIM_OCPreload_Enable); // 使能計數器 TIM_Cmd(GENERAL_TIM, ENABLE); }
調用函數GENERAL_TIM_Init()后,相應引腳就輸出PWM信號。
/** * @brief 通用定時器PWM輸出用到的GPIO和PWM模式初始化 * @param 無 * @retval 無 */ void GENERAL_TIM_Init(void) { GENERAL_TIM_GPIO_Config(); GENERAL_TIM_Mode_Config(); }
輸入到高級定時器捕獲引腳的PWM信號,來自通用定時器的輸出。
宏定義里面,可以算出計數器的計數周期為T=72M/(1000*72)=1MS。
這是定時器在不溢出的情況下的最大計數周期,也就是說,周期小於1ms的PWM信號都可以被捕獲到。轉換成頻率就是能捕獲到的最小的頻率為1KHZ。
要根據捕獲的PWM信號,調節ADVANCE_TIM_PERIOD和ADVANCE_TIM_PSC這兩個宏。
#ifndef __BSP_ADVANCETIME_H #define __BSP_ADVANCETIME_H #include "stm32f10x.h" /************高級定時器TIM參數定義,只限TIM1和TIM8************/ // 當使用不同的定時器的時候,對應的GPIO是不一樣的,這點要注意 // 這里我們使用高級控制定時器TIM1 #define ADVANCE_TIM TIM1 #define ADVANCE_TIM_APBxClock_FUN RCC_APB2PeriphClockCmd #define ADVANCE_TIM_CLK RCC_APB2Periph_TIM1 // 輸入捕獲能捕獲到的最小的頻率為 72M/{ (ARR+1)*(PSC+1) } #define ADVANCE_TIM_PERIOD (1000-1) #define ADVANCE_TIM_PSC (72-1) // 中斷相關宏定義 #define ADVANCE_TIM_IRQ TIM1_CC_IRQn #define ADVANCE_TIM_IRQHandler TIM1_CC_IRQHandler // TIM1 輸入捕獲通道1 #define ADVANCE_TIM_CH1_GPIO_CLK RCC_APB2Periph_GPIOA #define ADVANCE_TIM_CH1_PORT GPIOA #define ADVANCE_TIM_CH1_PIN GPIO_Pin_8 #define ADVANCE_TIM_IC1PWM_CHANNEL TIM_Channel_1 #define ADVANCE_TIM_IC2PWM_CHANNEL TIM_Channel_2 /**************************函數聲明********************************/ void ADVANCE_TIM_Init(void); #endif /* __BSP_ADVANCETIME_H */
高級定時器PWM輸入模式:
ADVANCE_TIM_Mode_Config()函數中初始化了兩個結構體。
TIM_TimeBaseInitTypeDef,時基結構體,用於定時器基礎參數設置。
TIM_OCInitTypeDef,輸出比較結構體,用於輸出比較模式。
PWM輸入模式,只能使用通道1和通道2。
使用通道1,即TI1,輸入的PWM信號被分成兩路,分別是TI1FP1和TI1FP2,兩路都可以是觸發信號。
選擇TI1FP1為觸發信號,IC1捕獲到的是PWM信號的周期,IC2捕獲到的是占空比。
這種輸入通道TI和捕獲通道IC的映射關系叫直連,輸入捕獲結構體TIM_ICSelection配置為TIM_ICSelection_DirectTI。
選擇TI1FP2為觸發信號,則IC2捕獲到的是周期,IC1捕獲到的是占空比。
這種輸入通道TI和捕獲通道IC的映射關系叫非直連,輸入捕獲結構體的TIM_ICSelection要配置為TIM_ICSelection_IndirectTI。
輸入通道TI和捕獲通道IC的具體映射關系有直連和非直連兩種。
/** * @brief 高級定時器PWM輸入初始化和用到的GPIO初始化 * @param 無 * @retval 無 */ static void ADVANCE_TIM_Mode_Config(void) { // 開啟定時器時鍾,即內部時鍾CK_INT=72M ADVANCE_TIM_APBxClock_FUN(ADVANCE_TIM_CLK,ENABLE); /*--------------------時基結構體初始化-------------------------*/ TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; // 自動重裝載寄存器的值,累計TIM_Period+1個頻率后產生一個更新或者中斷 TIM_TimeBaseStructure.TIM_Period=ADVANCE_TIM_PERIOD; // 驅動CNT計數器的時鍾 = Fck_int/(psc+1) TIM_TimeBaseStructure.TIM_Prescaler= ADVANCE_TIM_PSC; // 時鍾分頻因子 ,配置死區時間時需要用到 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); /*--------------------輸入捕獲結構體初始化-------------------*/ // 使用PWM輸入模式時,需要占用兩個捕獲寄存器,一個測周期,另外一個測占空比 TIM_ICInitTypeDef TIM_ICInitStructure; // 捕獲通道IC1配置 // 選擇捕獲通道 TIM_ICInitStructure.TIM_Channel = ADVANCE_TIM_IC1PWM_CHANNEL; // 設置捕獲的邊沿 TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; // 設置捕獲通道的信號來自於哪個輸入通道,有直連和非直連兩種 TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; // 1分頻,即捕獲信號的每個有效邊沿都捕獲 TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; // 不濾波 TIM_ICInitStructure.TIM_ICFilter = 0x0; // 初始化PWM輸入模式 TIM_PWMIConfig(ADVANCE_TIM, &TIM_ICInitStructure); // 當工作做PWM輸入模式時,只需要設置觸發信號的那一路即可(用於測量周期) // 另外一路(用於測量占空比)會由硬件自帶設置,不需要再配置 // 捕獲通道IC2配置 // TIM_ICInitStructure.TIM_Channel = ADVANCE_TIM_IC1PWM_CHANNEL; // TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Falling; // TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_IndirectTI; // TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; // TIM_ICInitStructure.TIM_ICFilter = 0x0; // TIM_PWMIConfig(ADVANCE_TIM, &TIM_ICInitStructure); // 選擇輸入捕獲的觸發信號 TIM_SelectInputTrigger(ADVANCE_TIM, TIM_TS_TI1FP1); // 選擇從模式: 復位模式 // PWM輸入模式時,從模式必須工作在復位模式,當捕獲開始時,計數器CNT會被復位 TIM_SelectSlaveMode(ADVANCE_TIM, TIM_SlaveMode_Reset); TIM_SelectMasterSlaveMode(ADVANCE_TIM,TIM_MasterSlaveMode_Enable); // 使能捕獲中斷,這個中斷針對的是主捕獲通道(測量周期那個) TIM_ITConfig(ADVANCE_TIM, TIM_IT_CC1, ENABLE); // 清除中斷標志位 TIM_ClearITPendingBit(ADVANCE_TIM, TIM_IT_CC1); // 使能高級控制定時器,計數器開始計數 TIM_Cmd(ADVANCE_TIM, ENABLE); }
高級定時器中斷優先級:只有一個中斷源,優先級可以隨便配置
// 中斷相關宏定義 #define ADVANCE_TIM_IRQ TIM1_CC_IRQn #define ADVANCE_TIM_IRQHandler TIM1_CC_IRQHandler
/** * @brief 高級控制定時器 TIMx,x[1,8]中斷優先級配置 * @param 無 * @retval 無 */ static void ADVANCE_TIM_NVIC_Config(void) { NVIC_InitTypeDef NVIC_InitStructure; // 設置中斷組為0 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0); // 設置中斷來源 NVIC_InitStructure.NVIC_IRQChannel = ADVANCE_TIM_IRQ; // 設置搶占優先級 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; // 設置子優先級 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); }
高級定時器中斷服務函數:
捕獲到PWM信號的第一個上升沿時,產生中斷,計數器被復位,鎖存到捕獲寄存器IC1和IC2的值都為0。
下降沿到來時,IC2會捕獲,對應的是占空比,會產生中斷。
當捕獲到第二個上升沿時,IC1會捕獲,對應的是周期,再次進入中斷。
可以根據IC1和IC2的值計算出頻率和占空比。
中斷復位函數中,獲取輸入捕獲寄存器CCR1和CCR2寄存器中的值。
CCR1的值不為0,說明有效捕獲到了一個周期,然后計算出頻率和占空比。
計算時,CCR1和CCR2的值都必須加1,因為計數器從0開始計數。
/* * 如果是第一個上升沿中斷,計數器會被復位,鎖存到CCR1寄存器的值是0,CCR2寄存器的值也是0 * 無法計算頻率和占空比。當第二次上升沿到來的時候,CCR1和CCR2捕獲到的才是有效的值。其中 * CCR1對應的是周期,CCR2對應的是占空比。 */ void ADVANCE_TIM_IRQHandler(void) { /* 清除中斷標志位 */ TIM_ClearITPendingBit(ADVANCE_TIM, TIM_IT_CC1); /* 獲取輸入捕獲值 */ IC1Value = TIM_GetCapture1(ADVANCE_TIM); IC2Value = TIM_GetCapture2(ADVANCE_TIM); // 注意:捕獲寄存器CCR1和CCR2的值在計算占空比和頻率的時候必須加1 if (IC1Value != 0) { /* 占空比計算 */ DutyCycle = (float)((IC2Value+1) * 100) / (IC1Value+1); /* 頻率計算 */ Frequency = (72000000/(ADVANCE_TIM_PSC+1))/(float)(IC1Value+1); printf("占空比:%0.2f%% 頻率:%0.2fHz\n",DutyCycle,Frequency); } else { DutyCycle = 0; Frequency = 0; } }
main
通用定時器初始化完之后,輸出PWM信號,高級定時器初始化完之后,捕獲通用定時器輸出的PWM信號。
通用定時器TIM3的通道1,PA6,用於輸出PWM信號。
高級控制定時器TIM1的通道1,PA8,用於PWM輸入捕獲。
用杜邦線短接PA6和PA8,用USB線連電腦,打開串口調試助手,可以看到捕獲到的PWM信號的頻率和占空比。
同時,可以通過仿真模擬的方法看到PA6,輸出的信號波形。
// TIM—高級定時器-PWM輸入捕獲應用,通用定時器產生PWM波,高級定時器則捕獲這個PWM,並測量周期和占空比 #include "stm32f10x.h" #include "bsp_led.h" #include "bsp_AdvanceTim.h" #include "bsp_GeneralTim.h" #include "bsp_usart.h" /** * @brief 主函數 * @param 無 * @retval 無 */ int main(void) { /* 串口初始化 */ USART_Config(); /* 通用定時器初始化,用於生成PWM信號 */ GENERAL_TIM_Init(); /* 高級定時器初始化 ,用戶捕獲PWM信號*/ ADVANCE_TIM_Init(); while(1) { } }