一,硬件需求:
1,STM32F103C8T6核心板;2,舵机模块
二、实现目标
1,按键控制舵机转动,分别是0度---30度----60度----90度-----........-180度;每次按键后自增30度,在到达180度后,再有一次按键输入后,回到0度;
三、前导知识梳理:
1,PWM知识:
①PWM是具有一定占空比的输出方波;
②什么是占空比:高电平占整个周期的时间比,Ton/Ts
③PWM波的作用:在一个波形周期内,会有高电平和低电平状态,合理控制PWM占空比,在具有惯性的系统中,可以得到不同输入电压比例;进而对外部单元进行控制
补充:在无刷三相电机的控制,就是通过在不同时期输出不同比例占空比,输出不同电压,从而实现交流电压的输出;
2,舵机控制
舵机控制的内部原理,不太理解。我这边理解,对应这种控制模块,仅需要知道如何控制即可;
控制原理:输出周期为20ms的pwm波,控制不同的占空比,实现不同转动角的控制;
3,通用定时器对应PWM的输出控制
①通用定时器的输出比较模块
1-输出模块控制器,可以实现CNT与CCR的比较,在CNT<CCR或者CNT>CCR输出不同的REF,具体由控制模式决定;
2-第二个框,实现对与REF的二轮控制,即REF的高低电平,可以再经过一轮控制;最终输出OC对应电平
②输出比较模块-pwm输出模式的控制原理
向上计数:CNT<CCR时,REF置有效电平,CNT≥CCR时,REF置无效电平
向下计数:CNT>CCR时,REF置无效电平,CNT≤CCR时,REF置有效电平
③通用定时器整体控制逻辑
1-配置时基单元,PSC,ARR
2-配置输出比较单元CCR,并选择PWM1模式
3-看情况动态调整CCR的数值,进而实现不同pwm输出
到此,前导知识梳理完毕;
四、代码模块
1,思路梳理:
①PWM控制模块(PWM初始化模块、动态调整配置PWMCCR模块)
②舵机转向模块(舵机初始化、转向角控制模块)
③按键检测模块
④OLED显示模式
2,PWM控制模块
1-定时器2模块时钟开启、GPIO-PA1时钟开启
//配置时钟RCC
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
2-配置定时器2的时钟源--为系统时钟72mhz
//配置时钟源为RCC -72mhz
TIM_InternalClockConfig(TIM2);
3-配置时基单元,主要是预分频PSC、自动重装ARR的数值
计算公式:
PWM频率: Freq = CK_PSC / (PSC + 1) / (ARR + 1)
PWM占空比: Duty = CCR / (ARR + 1) PWM
分辨率: Reso = 1 / (ARR + 1)
目标是形成20ms的周期pwm,设置PSC为72-1,设置ARR为20000-1;
//配置时基单元数值
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
TIM_TimeBaseInitStruct.TIM_ClockDivision=TIM_CKD_DIV1 ;
TIM_TimeBaseInitStruct.TIM_CounterMode=TIM_CounterMode_Up ;
TIM_TimeBaseInitStruct.TIM_Period=20000-1; //ARR
TIM_TimeBaseInitStruct.TIM_Prescaler=72-1; //psc
TIM_TimeBaseInitStruct.TIM_RepetitionCounter=0;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStruct);
4-输出比较模块配置
①定义结构体TIM_OCInitStruct;
②由于部分数据属于高级定时器内容,不需要配置,但是不配置容易出现bug。先使用TIM_OCStructInit(&TIM_OCInitStruct)进行一次默认初始化
③输出模式PWM1
④有效模式设定:在有效状态下,输出高电平
⑤输出使能开启
⑥输出CCR的数值
⑦初始化指令
//输出比较单元配置
TIM_OCInitTypeDef TIM_OCInitStruct;
TIM_OCStructInit(&TIM_OCInitStruct);
TIM_OCInitStruct.TIM_OCMode=TIM_OCMode_PWM1 ;
TIM_OCInitStruct.TIM_OCPolarity=TIM_OCPolarity_High ;
TIM_OCInitStruct.TIM_OutputState=TIM_OutputState_Enable;
TIM_OCInitStruct.TIM_Pulse=0;//设置CCR数值
TIM_OC2Init(TIM2, &TIM_OCInitStruct);
5-初始化GPIO-PA1
①初始化结构体GPIO_InitStruct;
②初始化为复用推挽输出GPIO_Mode_AF_PP;(其中,配置复用推挽输出的原因是的,只有在配置复用模式下,TIM才能控制GPIO的输出)
// 配置PA1端口
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP;
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_1;
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init( GPIOA, &GPIO_InitStruct);
6-启动定时器
//启动定时器
TIM_Cmd(TIM2,ENABLE);
7-动态调整CCR的数值
void pwm_setcompare(uint16_t compare)
{
TIM_SetCompare2(TIM2, compare);
}
至此,核心的PWM模块已经完毕
3,舵机转向模块
舵机转向模块,主要是对PWM模块进行二次封装,需要关注的是:
配置angle,需要配置为float类型,不然运算时刻容易出bug(强制类型转化带来的问题)
计算公式:Angle / 180 * 2000 + 500
#include "stm32f10x.h" // Device header
#include "pwm.h"
//舵机初始化
void servo_init(void)
{
pwm_init();
}
void Servo_SetAngle(float Angle)
{
pwm_setcompare(Angle / 180 * 2000 + 500);
}
4,在主函数中实现简单设置
#include "stm32f10x.h" // Device header
#include "delay.h"
#include "OLED.H"
#include "service.H"
#include "key.h"
/*
1,初始化时钟
2,定义GPIO端口,并配置输出端口,输出速度和输出方式
3,控制端口输出
*/
uint8_t keynum=0;
float angle=0.0;
int main()
{
OLED_Init();
keyinit();
OLED_ShowString(1,1,"Angle:");
servo_init();
while(1)
{
keynum=key_getnum();
if (keynum == 1)
{
angle += 30;
if (angle > 180)
{
angle = 0;
}
}
Servo_SetAngle(angle);
OLED_ShowNum(1,7,angle,3);
}
}
至此,已经完成代码模块配置
实验状态截屏
四、总结
1,pwm模块配置:开启TIM2时钟、开启GIPIOA时钟----配置时钟源-----配置时基模块----配置输出比较模块---配置GPIO-PA1端口---开启时钟TIM2
2,调用TIM_SetCompare2(TIM2, compare),配置CCR,控制占空比