本人是大一的学生,学习了一段时间的stm32,此系列博客为个人的学习笔记,方便个人复习,如有错误或问题,非常非常欢迎大家来大力指正。
本篇默认有51单片机基础,所以pwm原理就不再赘述
与51单片机不同,stm32实现pwm的方法是通过定时器的“输出比较output compare(简称oc,不是超频)”来实现的。通过CNT(计时单元)与CRR(输入/捕获寄存器)的比较实现电平的反转。
先说CRR输入/捕获寄存器,顾名思义,他是有输入和输出的功能,输出功能就是输出pwm,输入功能就是对输入的pwm进行识别。而想要驱动舵机,就要用pwm输出来控制。
先讲pwm输出
stm32实现pwm的方法是CNT计数,然后和CRR比较,当CNT=CCR时,对电平产生变化。然后当CNT=ARR时,触发计数器中断,CNT置0,恢复以前的电平,重新计数。这样就是现实电平在一个周期内的有规律变化,通过调整CRR,就可以调整pwm的占空比。
CNT=CRR的电平变化由模式所决定,下面程序说
PWM freq=ck psc(单片机频率)/(psc+1)/(ARR+1)
pwm占空比 duty=ccr/(arr+1)
pwm分辨率 reso=1/(arr+1)
②标准库的代码实现
pwm的基本结构如图,要用到上节定时器的功能,所以要开启RCC时钟,定义时基单元,配置CCR。前两部像上一节一样配就行,但是不用设定NVIC中断。而定义CCR首先要看TIM_OCInitTypeDef structure结构体中所要定义的东西。
有很多都是高级定时器要用的,我们只需要写出我们需要用的进行,看下面代码
输出极性可以理解为“是否要将电平信号反转”
下面4个都是输出极性的定义,死区生成是防止互补输出时两个电路一起导通产生损耗,延迟下一个端口导通的时间
TIM_ocmode中有几个模式,配置输出比较控制器,当CCR=CNT时输出不同形式的电平
第一个为保持原状态
有效电平和无效电平就可以看作高电平和低电平,电平反转可以输出一个固定平衡率的波形
强制电平和第一个差不多,就是暂停后输出特定的电平
pwm模式是可以输出频率可变的波形.
void TIM_CtrlPWMOutputs(TIM_TypeDef* TIMx, FunctionalState NewState);高级寄存器要用这条使能。
第一步开启RCC,定义好TIM时基单元
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //开启GPIOA的时钟
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//因为一般的GPIO控制权是在寄存器上的,看原理图,OC1通道要接到引脚才能输出pwm,所以要用复用推挽,让引脚接入片上外设
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
TIM_InternalClockConfig(TIM2);
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_Prescaler=720-1; //psc预分频器
TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up;//计数器模式
TIM_TimeBaseInitStructure.TIM_Period=100-1; //ARR自动重装器的初始值(周期)
TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1; //滤波分频
TIM_TimeBaseInitStructure.TIM_RepetitionCounter=0; //重复计数器
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);
第二步用结构体定义OC
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_OCStructInit(&TIM_OCInitStructure);//注意事项,初始值赋值,防止未定义的函数产生错误
TIM_OCInitStructure.TIM_OCMode=TIM_OCMode_PWM1;//配置OC的输出模式
TIM_OCInitStructure.TIM_OCPolarity=TIM_OCPolarity_High;//比较极性
TIM_OCInitStructure.TIM_OutputState=TIM_OutputState_Enable;//输出使能
TIM_OCInitStructure.TIM_Pulse=50 ;//ccr的值
TIM_OC1Init(TIM2, &TIM_OCInitStructure);//注意OC后面是通道编号
TIM_Cmd(TIM2, ENABLE);
PWM已经定义完毕。
实现pwm的占空比变化不能像51单片机这样写,要用到专门改变占空比的
TIM_SetCompare1(TIM2, num);函数来改变CCR的值,从而改变占空比。
改变num的值,就是改变CCR,从而输出PWM。用上面的公式计算。
100hz:ARR:100-1.PSC:720-1
50hz(sg50):ARR:20000-1,PSC:72-1.接上对应接上通道的引脚,就可以输出pwm信号
③HAL库的实现:
我们可以按照标准库的定义开启选项
①开启RCC
②开启TIM,打开内部时钟,选择通道和输出模式,打开NVIC中断
③配置OC(跟着标准库数据配置就好)
然后生成代码
写上下面两条代码
HAL_TIM_Base_Init(&htim2);//初始化tim2
HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_1);//打开pwm输出
下面那条是比较输出的代码
__HAL_TIM_SetCompare(&htim2,TIM_CHANNEL_1,CCR的值);
和TIM_SetCompare1(TIM2, num);一样
驱动舵机:
根据上面来输出合适的pwm使电机转动特定的角度,通过上面几条公式,进行计算即可。
重映射复用端口的小知识:要看手册找到相应的复用端口,先开启RCC时钟(将gpio换为AFIO就可以)然后用gpio_pinremapconfig();来重映射引脚