用TIM函数配置PWM输出比较模块的函数
TIM_OCxInit();//OC就是Output Compare输出比较
TIM_OCStructInit();//初始化输出比较单元,给输出比较结构体赋一个默认值
TIM_SetCompare1();//单独更改CCR寄存器值的函数,在运行的时候更改占空比
(下面是一些小功能和运行时更改参数的函数)
TIM_ForcedOCxConfig();//配置强制输出模式,在运行中需要暂停输出波形并且强制输出高或低电平
(没啥用,因为强制输出高电平==设置100%占空比,强制输出低电平==设置0%占空比)
TIM_OCxPreloadConfig();//配置CCR寄存器的预装功能(影子寄存器——写入值不会立即生效,而是在更新事件后再生效)
TIM_CtrlPWMOutputs();//仅高级定时器使用,其输出PWM时,需要调用这个函数使能主输出,否则PWM不能正常输出
初始化PWM时从左至右参考该图流程
驱动舵机的关键
-
PWM 频率: Freq = CK_PSC / (PSC + 1) / (ARR + 1)
PWM周期对应计数器的一个溢出更新周期——PWM频率等于计数器的更新频率
-
PWM 占空比: Duty = CCR / (ARR + 1)
-
PWM 分辨率: Reso = 1 / (ARR + 1)
由图可知周期为20ms,时钟频率为72MHz(默认),分别可以解得PSC、ARR的值及其对应舵机输出轴转角
//PWM频率为【1/20ms=50Hz】
//此处PSC和ARR的值并不固定,可参考设置为【PSC+1】为72,【ARR+1】为20K
-
重点:此处的CCR不与占空比有关,纯粹就是对舵机ms的表示,例如CCR=500时代表0.5ms,CCR=1000时代表1ms
1、完成驱动文件导入操作和编写驱动程序基本代码(参考之前文章)
//将【PWM】配置文件放在【Hardware】文件夹里
2、在PWM.c中初始化函数
PWM
_Init
void PWM_Init(void){//第一步:开启时钟(注意使用APB1的开启时钟函数,因为TIM2是APB1总线的外设)RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);//第二步:选择时基单元的时钟(在stm32f10x_tim.h文件中查找函数)TIM_InternalClockConfig(TIM2);//这里选择为内部时钟//时基单元就由内部时钟驱动了//第三步:配置时基单元(在stm32f10x_tim.h文件中查找函数)TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;//指定时钟分频TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;//指定计数器模式(此处选择向上计数)TIM_TimeBaseInitStructure.TIM_Period = 20000 - 1;//指定要在下一次更新事件时加载到ARR自动重新加载寄存器中的周期值 ARR取值[0,65535]TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1;//指定用于划分TIM时钟的预分频器值 PSC取值[0,65535]TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;//指定重复计数器的值(高级定时器才用得上,本项目给0)TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);//初始化时基单元//第四步:初始化输出比较单元(通道)【引脚定义表】//使用PA1口对应第一个输出比较通道TIM_OCInitTypeDef TIM_OCInitStructure;//为了避免出现不确定因素,PWM需要先把结构体成员完整配置一遍默认初始值,再根据需要更改个别地方TIM_OCStructInit(&TIM_OCInitStructure);//给结构体赋初始值TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;//设置输出比较的模式TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;//设置输出比较的极性TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;//设置输出使能TIM_OCInitStructure.TIM_Pulse = 0;//设置CCR的值TIM_OC2Init(TIM2,&TIM_OCInitStructure);//到此通道初始化完成,可以在TIM2的OC2通道上输出PWM波形//波形需要借用GPIO口进行输出//TIM2的OC2的通道借用了PA1【引脚定义表】//第五步:初始化GPIO口RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//复用推挽输出GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GPIO_InitStructure);//不需要打开中断和配置NVIC//第六步:启动定时器(在stm32f10x_tim.h文件中查找函数)TIM_Cmd(TIM2,ENABLE);//至此PWM波形就能通过PA1输出了//通过PWM的频率、占空比、分辨率计算公式可以得到ARR、PSC、CCR值//当PSC=72-1 ARR=20000-1 CCR=1500时//现象为舵机转到0°位置}
3、在PWM.c中编写更改CCR函数
PWM_SetCompare2
//在运行过程中更改CCR值(改变占空比)CCR在初始化时为0void PWM_SetCompare2(uint16_t Compare)//单独更改通道1的CCR值{TIM_SetCompare2(TIM2,Compare);}//TIM_SetCompare函数设置的是CCR的值,不代表直接设置占空比,占空比是由【CCR】和【ARR+1】共同决定的
4、在PWM.h中声明初始化函数
PWM
_In
it和
更改CCR
函数
PWM_SetCompare2
void PWM_Init(void);void PWM_SetCompare2(uint16_t Compare);
5、在主程序main.c中
#includ
e
"PWM
.h
"
#include "PWM.h"
6、在主循环之前先初始化PWM
7、在主循环中编写程序主体
int main(void){PWM_Init();OLED_Init();PWM_SetCompare2(1500);//舵机转到0°while(1){}}
实现功能:上电后舵机转到0°
最终目标
一、编写函数,功能为舵机设置角度,参数为0°—180°,可以更直观的调整调度
-
建立舵机模块完成驱动文件导入操作和编写驱动程序基本代码
//将【Servo】配置文件放在【Hardware】文件夹里
2、在Servo.c中初始化函数
Servo
_Init
void Servo_Init(void){PWM_Init();//将其PWM底层初始化}
3、在Servo.c中编写更改CCR函数
Servo_SetAngle
/*Angle参数计算Angle=0° CRR=500Angle=180° CRR=2500通过一次函数可以得到Angle关系式为:SetAngle=Angle/180*2000+500也就是用函数设置舵机角度为0°时,给CCR赋值500为180°时,给CCR赋值2500*/void Servo_SetAngle(float Angle){PWM_SetCompare2(Angle/180*2000+500);}
4、在Servo.h中声明初始化函数
Servo
_Init和更改CCR函数
Servo_SetAngle
void Servo_Init(void);void Servo_SetAngle(float Angle);
5、在主程序main.c中
#includ
e
"Servo
.h
"(比之前用PWM更加直观,知道用的是什么外设)
#include "Servo.h"//设置角度全部变量float Anglefloat Angle;
6、在主循环之前先初始化Servo
-
添加按键模块
1)在主程序main.c中
#includ
e
"Key
.h
"
2)设置键码全局变量
uint8_t KeyNum;
3)初始化按键模块
Key_Init();
7、在主循环中编写程序主体
uint8_t KeyNum;float Angle;int main(void){Servo_Init();OLED_Init();Key_Init();OLED_ShowString(1,1,"Angle:");while(1){KeyNum = Key_GetNum();if(KeyNum == 1){Angle += 30;}if(Angle > 180){Angle = 0;}Servo_SetAngle(Angle);OLED_ShowNum(1,7,Angle,3);}}
实现功能:上电后OLED显示Angle:000,每按下一次按键角度加30,加到180时返回0角度