源码连接:https://gitee.com/HL12334/stm32-learning-code
前提知识:
1.定时器中断
1.关键概念
1.1pwm输出
1.常用术语
- OC(output compare)输出比较
- CNT(counter),定时器中用于计数的寄存器:当前定时器计数值
- CCR(Capture/Compare),捕获比较寄存器:与CNT寄存器的值进行比较,用于确定PWM占空比
- PSC(Prescaler),预分频器:降低定时器输入频率,可以进行更长时间的定时
- ARR(Auto-Reload Register),自动重装寄存器: 用于确定PWM周期, 当CNT==ARR时,计数器重新开始计数
2.每个高级定时器和通用定时器都拥有4个输出比较通道。
3.高级定时器的前三个通道额外拥有死区生成和互补输出的功能,用于驱动三相无刷电机。
4.有效电平: 自行设置有效电平为高电平还是低电平,反正则为无效电平
5.输出比较模式,常用模式
- PWM模式1
向上计数时(一般使用向上计数):CNT<CCR时输出有效电平,CNT>=CCR时输出无效电平
向下计数时:CNT>CCR时输出无效电平,CNT<=CCR时输出无效电平 - PWM模式2
与PWM模式1相反
6.参数计算(有效电平为高电平,向上计数模式,计数模式为PWM模式1,其他一样的道理)
- PWM频率:Freq=CK_PSC/(PSC+1)/(ARR+1)
- PWM占空比Duty=CCR/(ARR+1)
示例:ARR=99,CCR=30,使用向上计数,有效电平设置为高电平,使用PWM模式1。输出占空比为30%的PWM波。
向上计数在小于30时(CNT<CCR),由于计数模式是PWM模式1,所以输出有效电平(高电平),大于等于30时(CNT>=CCR)输出无效电平(低电平),当计数为99(CNT==ARR)时,计数器又重新开始计数(开始下一个脉冲)
1.2sg90舵机
驱动是直流电机,内部通过电位器确定当前角度,再通过内部电板控制使其运动到pwm波计算所得位置。
输入PWM要求,周期为20ms,高电平宽度为0.5ms~2.5ms。中间其他角度自行按比例计算。控制的是绝对角度。
接线,一般橙色是PWM控制线,黑色是地线,红色为vcc。
2.配置流程
1.GPIO配置:PWM输出引脚配置,根据所选定时器硬件确定的引脚,查询《STM32中文参考手册_V10》8.3.7章节
2.定时器配置:设置pwm周期
3.输出比较配置:
- 创建输出比较配置结构体TIM_OCInitTypeDef
- 给结构体赋初始值(增加代码通用性,高级定时器参数很多,未初始化的值使用时可能会导致其他问题)-TIM_OCStructInit
- 配置关键参数:输出比较模式(一般选择为PWM模式1)、有效电平、输出使能、CCR值
- 初始化结构体:TIM_OCYInit(TIMX, &TIM_OCInitStructure),配置定时器X的第Y个通道输出PWM
3.使用流程
1.初始化
2.修改占空比(修改CCR值):TIM_SetCompareX(TIMY),修改定时器Y的第X通道的CCR值。
4.代码
使用通用定时器2的通道1(PA0)驱动舵机转动,
pwm.c
#include "pwm.h"
//通过通用定时器2通道1输出PWM控制舵机
void PWMInit(int psc,int arr){
//1.GPIO初始化配置,定时器2通道1对应IO口为PA0
GPIO_InitTypeDef GPIO_InitStructure;//创建gpio初始化结构体
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//使能PA端口对应时钟
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;//配置为复用推挽输出
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0;//PA0
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;//输出速度为50mhz
GPIO_Init(GPIOA,&GPIO_InitStructure);//初始化端口PA0
//2.定时器配置
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;//创建定时器配置结构体
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);//使能通用定时器2时钟
TIM_TimeBaseStructInit(&TIM_TimeBaseInitStructure);//初始化结构体参数,提高代码通用性
TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up;//向上计数
TIM_TimeBaseInitStructure.TIM_Prescaler=psc-1;//设置预分频值
TIM_TimeBaseInitStructure.TIM_Period=arr-1;//设置自动重装值
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);//初始化定时器
//3.输出比较配置
TIM_OCInitTypeDef TIM_OCInitStructure;//创建输出比较配置结构体
TIM_OCStructInit(&TIM_OCInitStructure);//初始化比较配置结构体,方便配置其他定时器
TIM_OCInitStructure.TIM_OCMode=TIM_OCMode_PWM1;//设置输出比较模式为PWM1模式
TIM_OCInitStructure.TIM_OCPolarity=TIM_OCPolarity_High;//有效电平为高电平
TIM_OCInitStructure.TIM_Pulse=0;//设置比较寄存器值为0,后续在程序中动态修改
TIM_OCInitStructure.TIM_OutputState=TIM_OutputState_Enable;//启用比较输出通道
TIM_OC1Init(TIM2,&TIM_OCInitStructure);//初始化比较输出配置
TIM_Cmd(TIM2, ENABLE); //使能TIM2
}
pwm.h
#ifndef __PWM_H
#define __PWM_H
#include "sys.h"
void PWMInit(int psc,int arr);//初始化函数
#endif
main.c
#include "stm32f10x.h"
#include "pwm.h"
#include "delay.h"
int main(void)
{
delay_init();//延时函数初始化
PWMInit(7200,200);//初始化定时器,频率为72mhz/7200/200=50hz
while(1)
{
delay_ms(1000);//延迟1s
TIM_SetCompare1(TIM2,5);//占空比为5/200,舵机角度为0°
delay_ms(1000);//延时1s
TIM_SetCompare1(TIM2,15);//占空比为15/200,舵机角度为90°
delay_ms(1000);//延时1s
TIM_SetCompare1(TIM2,25);//占空比为25/200,舵机角度为180°
}
}
5.问题总结
1.同一定时器输出多路PWM波形都是一样的吗
同一个定时器所以频率、相位一样,占空比由各自输出比较配置确定
2.输出PWM错误
1.端口输出模式要配置为复用推挽输出。 2.检查定时器是否选择错误,通道端口号是否正确
3.使用定时器高级定时器无法输出PWM
高级定时器还要进行主输出使能, TIM_CtrlPWMOutputs
实验效果
stm32输出PWM驱动舵机