PWM捕獲
目的就是測量輸入到特定管腳上的PWM波的頻率和占空比。
下面是PWM部分的電路圖:
PWM由XL555芯片產(chǎn)生,由滑動變阻器R40連接到PA15,滑動變阻器不同的阻值對應(yīng)不同的PWM波的頻率。下面一個(gè)也是一樣的原理。
可以看到板子上的PA15引腳的功能分別有:TIM2_CH1和TIM8_CH1,我們在板子上就用TIM_CH1來捕獲PWM。PB4引腳我們使用TIM3_CH1來捕獲PWM。(都是使用基礎(chǔ)定時(shí)器)
通過用示波器觀察兩個(gè)XL555產(chǎn)生的波形我們得到:其產(chǎn)生的可用波形的頻率范圍大致在:700HZ-23KHZ。占空比為百分之五十左右。
單路PWM捕獲編程
編程思路:
將管腳設(shè)置為上升沿中斷,也就是每次碰到方波的上升沿就產(chǎn)生中斷。
產(chǎn)生中斷就會進(jìn)入到中斷處理函數(shù),從而進(jìn)入到回調(diào)函數(shù)。
在回調(diào)函數(shù)中應(yīng)該做什么呢?要獲取CNT值。CNT其實(shí)就是一個(gè)時(shí)鐘的走時(shí)。我們在上一個(gè)上升沿獲取這個(gè)時(shí)間,然后將其清零,下一次上升沿再獲取這個(gè)時(shí)間,就是兩次上升沿相隔的時(shí)長。所以CNT值就是一個(gè)PWM波的周期,通過周期可以獲得其頻率。
為了方便我們計(jì)算周期,我們可以設(shè)置CNT為1微秒加一次。那么CNT的值就代表多少微秒,相應(yīng)的就是多少HZ的頻率。
步驟:
- 【模板】作為STM32CUBEMX生成代碼的工程;
- 配置TIM2的PA15作為TIM2 CH1輸入捕獲;
- 根據(jù)需求,配置TIM2_CH1的分頻值,推薦配置成1us計(jì)數(shù)一次;
- 將tim.c和tim.h移植到【編程工程】
4.1 main.c包含#include" tim.h"
4.2添加tim.c和stm32g4的HAL庫函數(shù)到工程中;
4.3 stm32g4xx hal_conf.h中啟動TIM模塊;
4.4 stm32g4xx it.c中,移植TIM2_IRQHandler中斷服務(wù)函數(shù);
4.5在主函數(shù)調(diào)用MX TIM2 lnit)定時(shí)器初始化函數(shù)和HAL_TIM_IC_Start_IT&htim2,TIM_CHANNEL 1)啟動定時(shí)器2捕獲功能;
4.6在HAL_TIM_IC_CaptureCallback回調(diào)函數(shù)里,獲取CNT值,計(jì)算PWM的頻率!
按照以下方法配置引腳:
然后配置其預(yù)分頻值:
如果想要配置預(yù)分頻值使其頻率等于1MHZ,首先要知道TIMER2的頻率是多少。
在STM32G431的數(shù)據(jù)手冊中我們可以找到:
由此可見TIMER2是掛載在APB1總線下的定時(shí)器。
那么在CUBE的時(shí)鐘樹里面我們可以看到:
對于APB1總線下的定時(shí)器,其頻率都是80MHZ。
所以我們設(shè)置預(yù)分頻值為79,即可將其分頻為1MHZ,就是1微秒記一次時(shí)。
Counter Period:是定時(shí)周期,這個(gè)決定的事CNT一直遞增到多少的時(shí)候溢出,然后自動歸零。
所以我們盡量應(yīng)該設(shè)置這個(gè)周期盡可能的大,讓CNT不要自動溢出歸零。由于是32位的,所以其最大值就是0xffffffff,所以配置為0xffffffff就可以了。
最后生成代碼即可(由于TIMER2是掛載在總線上的,所以也不用初始化他的時(shí)鐘了,因?yàn)橹耙呀?jīng)初始化過了)
先在主函數(shù)開啟pwm輸入捕獲中斷:
HAL_TIM_IC_Start_IT(&htim2,TIM_CHANNEL_1); //開啟pwm輸入捕獲中斷
在模板工程編寫中斷回調(diào)函數(shù):
//PWM捕獲中斷的回調(diào)函數(shù)
u32 tim2_cnt1;
u32 f40=0;
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
//獲取CNT的值
tim2_cnt1=__HAL_TIM_GetCounter(&htim2);
__HAL_TIM_SetCounter(&htim2,0);//獲取cnt值之后就把他清零
f40=1000000/tim2_cnt1;//周期的倒數(shù)就是頻率
HAL_TIM_IC_Start_IT(&htim2,TIM_CHANNEL_1); //開啟pwm輸入捕獲中斷
}
把F40的值顯示在LCD屏幕上即可觀察到不斷變化的方波頻率了。
雙路PWM捕獲編程
再設(shè)置一路PWM捕獲
就是把PB4設(shè)置好:
基本設(shè)置都是一樣的,唯一需要注意的一點(diǎn)就是PB4的Counter Period是16位的,最大數(shù)是65535,也就是0xffff,這個(gè)不要和上面那個(gè)一樣了,一樣的話就設(shè)置錯(cuò)了,其他就沒什么需要注意的了,直接生成代碼即可。
然后進(jìn)行代碼移植,把模板工程的代碼移植到編程工程去。
在主函數(shù)中添加timer3的初始化函數(shù)并且打開timer3channel1的pwm輸入捕獲中斷之后
改寫一下回調(diào)函數(shù)即可:
//PWM捕獲中斷的回調(diào)函數(shù)
u32 tim2_cnt1,tim3_cnt1;
u32 f40=0,f30;
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
//獲取CNT的值
if(htim==&htim2)
{
tim2_cnt1=__HAL_TIM_GetCounter(&htim2);
__HAL_TIM_SetCounter(&htim2,0);//獲取cnt值之后就把他清零
f40=1000000/tim2_cnt1;//周期的倒數(shù)就是頻率
HAL_TIM_IC_Start_IT(&htim2,TIM_CHANNEL_1); //開啟pwm輸入捕獲中斷
}
if(htim==&htim3)
{
tim3_cnt1=__HAL_TIM_GetCounter(&htim3);
__HAL_TIM_SetCounter(&htim3,0);//獲取cnt值之后就把他清零
f30=1000000/tim3_cnt1;//周期的倒數(shù)就是頻率
HAL_TIM_IC_Start_IT(&htim2,TIM_CHANNEL_1); //開啟pwm輸入捕獲中斷
}
}
測量單路PWM的頻率和占空比
測量頻率其實(shí)就是測量周期。
測量占空比就是測量高電平時(shí)長占整個(gè)周期的多長時(shí)間。
編程思路:
- 第一次上升沿中斷,開始計(jì)時(shí)(清零計(jì)數(shù)器),并改成下降沿中斷。
- 下降沿中斷,獲取計(jì)數(shù)器的CNT值T1,并改成上升沿中斷。
- 第二次上升沿中斷,獲取計(jì)數(shù)器的CNT值T2,通過T2可以獲得PWM的頻率。通過T1/T2可以獲得PWM的占空比
所以一套操作下來我們其實(shí)就是獲得了兩個(gè)時(shí)間,一個(gè)是高電平的時(shí)間,一個(gè)是整個(gè)周期的時(shí)間。
這就足以讓我們計(jì)算占空比了。
那么現(xiàn)在編程的難點(diǎn)就在于如何改變中斷的極性:
可以看到如下的他的tim初始化函數(shù)里面::
void MX_TIM3_Init(void)
{
TIM_MasterConfigTypeDef sMasterConfig = {0};
TIM_IC_InitTypeDef sConfigIC = {0};
htim3.Instance = TIM3;
htim3.Init.Prescaler = 79;
htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
htim3.Init.Period = 65535;
htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_IC_Init(&htim3) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim3, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_RISING;
sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI;
sConfigIC.ICPrescaler = TIM_ICPSC_DIV1;
sConfigIC.ICFilter = 0;
if (HAL_TIM_IC_ConfigChannel(&htim3, &sConfigIC, TIM_CHANNEL_1) != HAL_OK)
{
Error_Handler();
}
}
定義了一個(gè)初始化tim的結(jié)構(gòu)體:TIM_IC_InitTypeDef sConfigIC = {0};
點(diǎn)開這個(gè)結(jié)構(gòu)體的定義可以看到:
typedef struct
{
uint32_t ICPolarity; /*!< Specifies the active edge of the input signal.
This parameter can be a value of @ref TIM_Input_Capture_Polarity */
uint32_t ICSelection; /*!< Specifies the input.
This parameter can be a value of @ref TIM_Input_Capture_Selection */
uint32_t ICPrescaler; /*!< Specifies the Input Capture Prescaler.
This parameter can be a value of @ref TIM_Input_Capture_Prescaler */
uint32_t ICFilter; /*!< Specifies the input capture filter.
This parameter can be a number between Min_Data = 0x0 and Max_Data = 0xF */
} TIM_IC_InitTypeDef;
這個(gè)結(jié)構(gòu)體里面第一個(gè)就是中斷的極性,第二個(gè)是輸入的通道,后面兩個(gè)分別是分頻和濾波。
初始化函數(shù)后面用了HAL_TIMEx_MasterConfigSynchronization(&htim3, &sMasterConfig)
函數(shù)引用了這個(gè)結(jié)構(gòu)體,然后對tim進(jìn)行了配置。那么我們看下這個(gè)函數(shù)的定義:
HAL_StatusTypeDef HAL_TIM_IC_ConfigChannel(TIM_HandleTypeDef *htim, TIM_IC_InitTypeDef *sConfig, uint32_t Channel)
{
/* Check the parameters */
assert_param(IS_TIM_CC1_INSTANCE(htim->Instance));
assert_param(IS_TIM_IC_POLARITY(sConfig->ICPolarity));
assert_param(IS_TIM_IC_SELECTION(sConfig->ICSelection));
assert_param(IS_TIM_IC_PRESCALER(sConfig->ICPrescaler));
assert_param(IS_TIM_IC_FILTER(sConfig->ICFilter));
/* Process Locked */
__HAL_LOCK(htim);
if (Channel == TIM_CHANNEL_1)
{
/* TI1 Configuration */
TIM_TI1_SetConfig(htim->Instance,
sConfig->ICPolarity,
sConfig->ICSelection,
sConfig->ICFilter);
/* Reset the IC1PSC Bits */
htim->Instance->CCMR1 &= ~TIM_CCMR1_IC1PSC;
/* Set the IC1PSC value */
htim->Instance->CCMR1 |= sConfig->ICPrescaler;
}
else if (Channel == TIM_CHANNEL_2)
{
/* TI2 Configuration */
assert_param(IS_TIM_CC2_INSTANCE(htim->Instance));
TIM_TI2_SetConfig(htim->Instance,
sConfig->ICPolarity,
sConfig->ICSelection,
sConfig->ICFilter);
/* Reset the IC2PSC Bits */
htim->Instance->CCMR1 &= ~TIM_CCMR1_IC2PSC;
/* Set the IC2PSC value */
htim->Instance->CCMR1 |= (sConfig->ICPrescaler << 8U);
}
else if (Channel == TIM_CHANNEL_3)
{
/* TI3 Configuration */
assert_param(IS_TIM_CC3_INSTANCE(htim->Instance));
TIM_TI3_SetConfig(htim->Instance,
sConfig->ICPolarity,
sConfig->ICSelection,
sConfig->ICFilter);
/* Reset the IC3PSC Bits */
htim->Instance->CCMR2 &= ~TIM_CCMR2_IC3PSC;
/* Set the IC3PSC value */
htim->Instance->CCMR2 |= sConfig->ICPrescaler;
}
else
{
/* TI4 Configuration */
assert_param(IS_TIM_CC4_INSTANCE(htim->Instance));
TIM_TI4_SetConfig(htim->Instance,
sConfig->ICPolarity,
sConfig->ICSelection,
sConfig->ICFilter);
/* Reset the IC4PSC Bits */
htim->Instance->CCMR2 &= ~TIM_CCMR2_IC4PSC;
/* Set the IC4PSC value */
htim->Instance->CCMR2 |= (sConfig->ICPrescaler << 8U);
}
__HAL_UNLOCK(htim);
return HAL_OK;
}
里面寫了如果是通道1,執(zhí)行:
if (Channel == TIM_CHANNEL_1)
{
/* TI1 Configuration */
TIM_TI1_SetConfig(htim->Instance,
sConfig->ICPolarity,
sConfig->ICSelection,
sConfig->ICFilter);
/* Reset the IC1PSC Bits */
htim->Instance->CCMR1 &= ~TIM_CCMR1_IC1PSC;
/* Set the IC1PSC value */
htim->Instance->CCMR1 |= sConfig->ICPrescaler;
}
如果是通道2或者3執(zhí)行后面的程序。
我們只看通道1的即可:
這里面調(diào)用了TIM_TI1_SetConfig
函數(shù),把sConfigIC
結(jié)構(gòu)體的各個(gè)參數(shù)傳了進(jìn)去。
所以我們看下TIM_TI1_SetConfig
函數(shù)內(nèi)部是如何操作,然后把sConfigIC
結(jié)構(gòu)體的參數(shù)傳入的:
void TIM_TI1_SetConfig(TIM_TypeDef *TIMx, uint32_t TIM_ICPolarity, uint32_t TIM_ICSelection,
uint32_t TIM_ICFilter)
{
uint32_t tmpccmr1;
uint32_t tmpccer;
/* Disable the Channel 1: Reset the CC1E Bit */
TIMx->CCER &= ~TIM_CCER_CC1E;
tmpccmr1 = TIMx->CCMR1;
tmpccer = TIMx->CCER;
/* Select the Input */
if (IS_TIM_CC2_INSTANCE(TIMx) != RESET)
{
tmpccmr1 &= ~TIM_CCMR1_CC1S;
tmpccmr1 |= TIM_ICSelection;
}
else
{
tmpccmr1 |= TIM_CCMR1_CC1S_0;
}
/* Set the filter */
tmpccmr1 &= ~TIM_CCMR1_IC1F;
tmpccmr1 |= ((TIM_ICFilter << 4U) & TIM_CCMR1_IC1F);
/* Select the Polarity and set the CC1E Bit */
tmpccer &= ~(TIM_CCER_CC1P | TIM_CCER_CC1NP);
tmpccer |= (TIM_ICPolarity & (TIM_CCER_CC1P | TIM_CCER_CC1NP));
/* Write to TIMx CCMR1 and CCER registers */
TIMx->CCMR1 = tmpccmr1;
TIMx->CCER = tmpccer;
}
可以看到這個(gè)里面就是對各種寄存器進(jìn)行操作了。在后面幾行是對中斷極性的寄存器的位進(jìn)行配置的:
tmpccer |= (TIM_ICPolarity & (TIM_CCER_CC1P | TIM_CCER_CC1NP));
TIMx->CCER = tmpccer;
TIM_ICPolarity
就是中斷極性。這句話是對tmpccer
這個(gè)變量進(jìn)行操作的,然后最后把tmpccer
賦值給了TIMx->CCER
這個(gè)寄存器。
所以我們只要操作TIMx->CCER
這個(gè)寄存器就可以操作中斷極性了。
點(diǎn)擊看一下TIMx->CCER
的說明:
__IO uint32_t CCER; /*!< TIM capture/compare enable register, Address offset: 0x20 */
可以看到官方給出的注釋就是CCER就是輸入捕獲使能的寄存器。
我們查閱手冊看到:
可以看到CCER是一個(gè)十六位的寄存器(雖然STM32是32位的,但有些時(shí)候?yàn)榱斯?jié)省內(nèi)存有些寄存器也設(shè)置為16位的)。
我們需要配置的是哪一位呢?
手冊上寫明了bit1這一位,也就是倒數(shù)第二位是配置中斷極性(Polarity)的。
這里說的是CC1NP是0的話是PWM輸入模式,然后在輸入模式下,CC1P如果是0,就是上升沿中斷。CC1P為1的話就是下降沿中斷。
所以我們只需要改變CC1P這一位就可以了(因?yàn)槲覀冇玫木褪荘WM輸入模式,CC1NP已經(jīng)在初始化的時(shí)候初始為0了)。
開始編程:
回調(diào)函數(shù)里面的TIM2可以這樣寫:文章來源:http://www.zghlxwxcb.cn/news/detail-410666.html
if(htim==&htim2)
{
if(tim2_state==0)//第一個(gè)上升沿產(chǎn)生,開始計(jì)時(shí)
{
__HAL_TIM_SetCounter(&htim2,0);//把CNT清零
TIM2->CCER|=0x02; //下降沿中斷,就是要把CC1P置為1
tim2_state=1;//等待下降沿產(chǎn)生
}
else if(tim2_state==1)//第一個(gè)下降沿產(chǎn)生,獲取T1的值,就是高電平的時(shí)長
{
tim2_cnt1=__HAL_TIM_GetCounter(&htim2);//獲取CNT的值
TIM2->CCER &=~0x02;
tim2_state=2;
}
else if(tim2_state==2)//第二個(gè)上升沿產(chǎn)生,獲取T2的值,就是整個(gè)周期
{
tim2_cnt2=__HAL_TIM_GetCounter(&htim2);//獲取CNT的值
f40=1000000/tim2_cnt2;//周期的倒數(shù)就是頻率
d40=tim2_cnt1*100.0f/tim2_cnt2;//計(jì)算占空比
tim2_state=0;
}
HAL_TIM_IC_Start_IT(&htim2,TIM_CHANNEL_1); //開啟pwm輸入捕獲中斷
}
測量兩路PWM的頻率和占空比
就是把上面的代碼應(yīng)用到TIMER3上面重新寫一遍就好了:文章來源地址http://www.zghlxwxcb.cn/news/detail-410666.html
//PWM捕獲中斷的回調(diào)函數(shù)
u32 tim2_cnt1,tim3_cnt1,tim2_cnt2,tim3_cnt2;
u32 f40=0,f30=0;
float d40=0,d30=0;
u8 tim2_state=0,tim3_state=0;
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
//獲取CNT的值
if(htim==&htim2)
{
if(tim2_state==0)//第一個(gè)上升沿產(chǎn)生,開始計(jì)時(shí)
{
__HAL_TIM_SetCounter(&htim2,0);//把CNT清零
TIM2->CCER|=0x02; //下降沿中斷,就是要把CC1P置為1
tim2_state=1;//等待下降沿產(chǎn)生
}
else if(tim2_state==1)//第一個(gè)下降沿產(chǎn)生,獲取T1的值,就是高電平的時(shí)長
{
tim2_cnt1=__HAL_TIM_GetCounter(&htim2);//獲取CNT的值
TIM2->CCER &=~0x02;
tim2_state=2;
}
else if(tim2_state==2)//第二個(gè)上升沿產(chǎn)生,獲取T2的值,就是整個(gè)周期
{
tim2_cnt2=__HAL_TIM_GetCounter(&htim2);//獲取CNT的值
f40=1000000/tim2_cnt2;//周期的倒數(shù)就是頻率
d40=tim2_cnt1*100.0f/tim2_cnt2;
tim2_state=0;
}
HAL_TIM_IC_Start_IT(&htim2,TIM_CHANNEL_1); //開啟pwm輸入捕獲中斷
}
if(htim==&htim3)
{
if(tim3_state==0)//第一個(gè)上升沿產(chǎn)生,開始計(jì)時(shí)
{
__HAL_TIM_SetCounter(&htim3,0);//把CNT清零
TIM3->CCER|=0x02; //下降沿中斷,就是要把CC1P置為1
tim3_state=1;//等待下降沿產(chǎn)生
}
else if(tim3_state==1)//第一個(gè)下降沿產(chǎn)生,獲取T1的值,就是高電平的時(shí)長
{
tim3_cnt1=__HAL_TIM_GetCounter(&htim3);//獲取CNT的值
TIM3->CCER &=~0x02;
tim3_state=2;
}
else if(tim3_state==2)//第二個(gè)上升沿產(chǎn)生,獲取T2的值,就是整個(gè)周期
{
tim3_cnt2=__HAL_TIM_GetCounter(&htim3);//獲取CNT的值
f30=1000000/tim3_cnt2;//周期的倒數(shù)就是頻率
d30=tim3_cnt1*100.0f/tim3_cnt2;
tim3_state=0;
}
HAL_TIM_IC_Start_IT(&htim3,TIM_CHANNEL_1); //開啟pwm輸入捕獲中斷
}
}
到了這里,關(guān)于【藍(lán)橋杯】【嵌入式組別】第十三節(jié):PWM輸入捕獲編程的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!