STM32学习之路——通用定时器库函数分析(为产生PWM)
程序员文章站
2022-03-13 17:18:53
...
产生PWM的必要流程
一、使能GPIOA和TIM2外设。
两个外设别挂接在APB1和APB2总线上,所以需要分别调用下面两条库函数指令。
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO,DISABLE);
分析:该指令的作用是APB总线上的外设的使能
二、对GPIOA进行配置。
我使用的是TIM2,TIM2有四个输出通道(TIM2_CH1,TIM2CH2,TIM2_CH3,TIM2_CH4)分别于PA.0,PA.1,PA.2,PA.3引脚复用,所以对GPIOA进行配置如下:
GPIO_Structure.GPIO_Mode=GPIO_Mode_AF_PP;
GPIO_Structure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Structure.GPIO_Pin=GPIO_Pin_0;
GPIO_Init(GPIOA,&GPIO_Structure);
这里只对TIM2_CH1通道进行输出,所以把PA.0配置成为复用输出
三、讲TIM2进行初始化。
TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV4;
TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;
TimeBaseStructure.TIM_Period=899;
TimeBaseStructure.TIM_Prescaler=0;
TIM_TimeBaseInit(TIM2,&TimeBaseStructure);
分析:下面是TIM_TimeBaseInit函数定义
void TIM_TimeBaseInit(TIM_TypeDef* TIMx, TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct)
{
uint16_t tmpcr1 = 0;
/* Check the parameters */
assert_param(IS_TIM_ALL_PERIPH(TIMx)); //
assert_param(IS_TIM_COUNTER_MODE(TIM_TimeBaseInitStruct->TIM_CounterMode));
assert_param(IS_TIM_CKD_DIV(TIM_TimeBaseInitStruct->TIM_ClockDivision));
//检验以上三个变量是否符合规定,符合就往下执行,不符合就报错
tmpcr1 = TIMx->CR1;//控制寄存器1,主要控制着计数器的工作方式
if((TIMx == TIM1) || (TIMx == TIM8)|| (TIMx == TIM2) || (TIMx == TIM3)||
(TIMx == TIM4) || (TIMx == TIM5))
{
/* Select the Counter Mode */
tmpcr1 &= (uint16_t)(~((uint16_t)(TIM_CR1_DIR | TIM_CR1_CMS)));//控制寄存器1上的DIR于CMS位,分别控制计数器的计数方向和对齐方式
tmpcr1 |= (uint32_t)TIM_TimeBaseInitStruct->TIM_CounterMode;//把我们定义的TIM_CounterMode_Up实际上就是0x0000,与其相或,进而使DIR和CMS位达到我们的想要的设定。此时的设定为位边缘对齐模式和向上计数
}
if((TIMx != TIM6) && (TIMx != TIM7))
{
/* Set the clock division */
tmpcr1 &= (uint16_t)(~((uint16_t)TIM_CR1_CKD));//控制寄存器1上的CKD位,是选择时钟分频因子
tmpcr1 |= (uint32_t)TIM_TimeBaseInitStruct->TIM_ClockDivision;//对CKD位进行赋值,
}
TIMx->CR1 = tmpcr1;//将设定的值赋值到控制寄存器1中
/* Set the Autoreload value */
TIMx->ARR = TIM_TimeBaseInitStruct->TIM_Period ;//ARR为自动重装载寄存器,将值赋予其中,在这里的值为899,功能:计数器从零开始计数,在达到899是归零,在重新计数,以此往复
/* Set the Prescaler value */
TIMx->PSC = TIM_TimeBaseInitStruct->TIM_Prescaler;//PSC为预分频器,功能:控制计数器计数的频率,计数频率=fCK_PSC/(PSC存储的数值+1)
if ((TIMx == TIM1) || (TIMx == TIM8)|| (TIMx == TIM15)|| (TIMx == TIM16) || (TIMx == TIM17))
{
/* Set the Repetition Counter value */
TIMx->RCR = TIM_TimeBaseInitStruct->TIM_RepetitionCounter;
}//我们的定时器选择的是TIM2,所以这段语句直接跳过
/* Generate an update event to reload the Prescaler and the Repetition counter
values immediately */
TIMx->EGR = TIM_PSCReloadMode_Immediate;//EGR为时间产生寄存器,这里使用的是默认值,也就是0x0000,没有使用 。
}
四、设置 TIM2_CH1 的 PWM 模式及通道方向, 使能 TIM1 的 CH1 输出。
Tim_OCInitypeStructure.TIM_OCMode=TIM_OCMode_PWM2;
Tim_OCInitypeStructure.TIM_OCPolarity=TIM_OCPolarity_High;
Tim_OCInitypeStructure.TIM_OutputState=TIM_OutputState_Enable;
TIM_OC1Init(TIM2,&Tim_OCInitypeStructure);
分析:下面是TIM_OC1Init函数定义。
void TIM_OC1Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct)
{
uint16_t tmpccmrx = 0, tmpccer = 0, tmpcr2 = 0;
/* Check the parameters */
assert_param(IS_TIM_LIST8_PERIPH(TIMx));
assert_param(IS_TIM_OC_MODE(TIM_OCInitStruct->TIM_OCMode));
assert_param(IS_TIM_OUTPUT_STATE(TIM_OCInitStruct->TIM_OutputState));
assert_param(IS_TIM_OC_POLARITY(TIM_OCInitStruct->TIM_OCPolarity));
/* Disable the Channel 1: Reset the CC1E Bit */
TIMx->CCER &= (uint16_t)(~(uint16_t)TIM_CCER_CC1E);//将CCER(捕获/比较使能寄存器)上的CC1E位置1,开启- OC1信号输出到对应的输出引脚。
/* Get the TIMx CCER register value */
tmpccer = TIMx->CCER;
/* Get the TIMx CR2 register value */
tmpcr2 = TIMx->CR2;//CR2为控制寄存器2
/* Get the TIMx CCMR1 register value */
tmpccmrx = TIMx->CCMR1;//CCMR1为捕获/比较模式寄存器1
/* Reset the Output Compare Mode Bits */
tmpccmrx &= (uint16_t)(~((uint16_t)TIM_CCMR1_OC1M));
tmpccmrx &= (uint16_t)(~((uint16_t)TIM_CCMR1_CC1S));
//CCMR1上的OC1M三位控制的是输出比较模式,CC1S两位控制的是通道的方向(输入/输出),及输入脚的选择
/* Select the Output Compare Mode */
tmpccmrx |= TIM_OCInitStruct->TIM_OCMode;
/* Reset the Output Polarity level */
tmpccer &= (uint16_t)(~((uint16_t)TIM_CCER_CC1P));
/* Set the Output Compare Polarity */
tmpccer |= TIM_OCInitStruct->TIM_OCPolarity;
/* Set the Output State */
tmpccer |= TIM_OCInitStruct->TIM_OutputState;
//将我们的设定值分别赋值给这几个变量
if((TIMx == TIM1) || (TIMx == TIM8)|| (TIMx == TIM15)||
(TIMx == TIM16)|| (TIMx == TIM17))
{
assert_param(IS_TIM_OUTPUTN_STATE(TIM_OCInitStruct->TIM_OutputNState));
assert_param(IS_TIM_OCN_POLARITY(TIM_OCInitStruct->TIM_OCNPolarity));
assert_param(IS_TIM_OCNIDLE_STATE(TIM_OCInitStruct->TIM_OCNIdleState));
assert_param(IS_TIM_OCIDLE_STATE(TIM_OCInitStruct->TIM_OCIdleState));
/* Reset the Output N Polarity level */
tmpccer &= (uint16_t)(~((uint16_t)TIM_CCER_CC1NP));
/* Set the Output N Polarity */
tmpccer |= TIM_OCInitStruct->TIM_OCNPolarity;
/* Reset the Output N State */
tmpccer &= (uint16_t)(~((uint16_t)TIM_CCER_CC1NE));
/* Set the Output N State */
tmpccer |= TIM_OCInitStruct->TIM_OutputNState;
/* Reset the Output Compare and Output Compare N IDLE State */
tmpcr2 &= (uint16_t)(~((uint16_t)TIM_CR2_OIS1));
tmpcr2 &= (uint16_t)(~((uint16_t)TIM_CR2_OIS1N));
/* Set the Output Idle state */
tmpcr2 |= TIM_OCInitStruct->TIM_OCIdleState;
/* Set the Output N Idle state */
tmpcr2 |= TIM_OCInitStruct->TIM_OCNIdleState;
}//我们选择的是TIM2,所以该条件跳过
/* Write to TIMx CR2 */
TIMx->CR2 = tmpcr2;
/* Write to TIMx CCMR1 */
TIMx->CCMR1 = tmpccmrx;
/* Set the Capture Compare Register value */
TIMx->CCR1 = TIM_OCInitStruct->TIM_Pulse;
/* Write to TIMx CCER */
TIMx->CCER = tmpccer;
//分别将变量里的值赋值给这几寄存器,完成设定
}
五、前面只是完成了计数器的计数模式的配置,输出通道的输出模式配置,此时需要对计数器进行使能操作,开始进行计数。
TIM_Cmd(TIM2,ENABLE);
分析:下面是TIM_Cmd函数定义
void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState)
{
/* Check the parameters */
assert_param(IS_TIM_ALL_PERIPH(TIMx));
assert_param(IS_FUNCTIONAL_STATE(NewState));
if (NewState != DISABLE)
{
/* Enable the TIM Counter */
TIMx->CR1 |= TIM_CR1_CEN;//将控制寄存器1上的CEN为置1,功能:使能计数器。
}
else
{
/* Disable the TIM Counter */
TIMx->CR1 &= (uint16_t)(~((uint16_t)TIM_CR1_CEN));
}
}
六、需要了解的PWM输出原理。
在这里,我们设定的TIM2_CH1输出模式为PWM模式2-,在该模式下就有,当计数器内的值<CCR1(捕获/比较寄存器1)内的值是,输出为低电平,当计数器内的值>=CCR1(捕获/比较寄存器1)内的值是,输出为高电平。
所以此时我们想要得到脉冲输出的话,就需要对CCR1进行赋值,此时我选择的是计数器设定值得一半,也就是450,调用的函数如下:
TIM_SetCompare1(TIM2,450);
七、得到的PWM输出
上一篇: STM32 定时器中断的使用
下一篇: STM32 定时器中断周期计算