实现STM32与STC51舵机任意角度控制
本实验用于stm32和51单片机的舵机控制
创作原因?
首先,本人希望舵机平稳控制,可以任意操控角度,并且速度可调。但网上的资料一般都是对舵机0°、45°、90°、135°、180°控制。于是我想自己是否可以写一个驱动舵机的代码。
创作经历
stm32,我是刚接触没多久,对代码编写还是处于学习。但自己接触过51单片机和arduino。在去年暑假我自己买了arduino mage2560开发板,打算学习“太极创客”的mearm,了解舵机的控制(舵机速度和方向控制),但我只局限于调用里面的 <Servo.h> 库。后来查询资料,自己编写了51舵机的驱动代码。
创作思路
舵机控制原理
t = 0.5ms——————-舵机会转到 0 °
t = 1.0ms——————-舵机会转到 45°
t = 1.5ms——————-舵机会转到 90°
t = 2.0ms——————-舵机会转到 135°
t = 2.5ms——————-舵机会转到 180°
由图像可知,舵机的旋转角度与频率无关,角度的小与PWM波高电平持续的时间有关
所以为了控制舵机,我们可以改变高电平持续的时间
51单片机思路
1. 初始化定时器(100us产生一次中断), 利用定时器产生一个PWM波
2. 0°、45°、90°、135°、180°对应着0.5ms、1ms、1.5ms、2ms、2.5ms
因此,我们定义一个Count变量,中断一次Count+1;再定义一个变量Time,用来和Count比较
具体怎么实现,可以参考江科大51单片机直流电机驱动https://www.bilibili.com/video/BV1Mb411e7re?p=33
STM32思路
通过配置定时器2产生一个PWM波
Servos_Init(2000,720);
周期Period=2000;频率Prescaler:720
利用TIM_SetCompare1();来改变PWM高电平的持续时间,从未改变舵机旋转方向
0°、45°、90°、135°、180° 对应函数Angle_config(uint16_t Angle,uint16_t Speed)的值分别为
1950 1900 1850 1800 1750
寻找关系,我们得出他们为二元一次函数 Angle= 1950-(int)(舵机旋转的角度值*10/9);
Now_Angle = 1950-(int)((Angle/9)*10);
if(Now_Angle<Old_Angle) //通过比较前角度和后角度,判断是正传还是逆转
{ //向左转,
for(i=Old_Angle;i>=Now_Angle;i–)
{
TIM_SetCompare1(TIM2,i);
delay_ms(Speed);
}
}
Old_Angle = Now_Angle;//对前一个角度进行存储,然后计较
这几串代码可参考太极创客https://www.bilibili.com/video/BV1k4411J7XD/?p=13&spm_id_from=333.1007.top_right_bar_window_history.content.click
驱动代码
51单片机程序
#include <REGX52.H>
#include <intrins.H>
sbit ServoPin= P2^7;
unsigned char Time;
unsigned int Count;//计数值
void Timer0Init() //100微秒@11.0592MHz
{
TMOD &= 0xF0; //设置定时器模式
TMOD |= 0x01; //设置定时器模式
TL0 = 0xA4; //设置定时初始值
TH0 = 0xFF; //设置定时初始值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
ET0=1;
EA=1;
}
void Servo_Proc(unsigned char Angle)//舵机角度控制
{
Count=0;//每次调用,都对Count初始化
switch(Angle)
{
case 0:Time=5;break;
case 45:Time=10;break;
case 90:Time=15;break;
case 135:Time=20;break;
case 180:Time=25;break;
}
}
void Delay(unsigned int xms)//1ms的延时函数
{
unsigned char i, j;
while(xms--)
{
i = 2;
j = 239;
do
{
while (--j);
} while (--i);
}
}
void main()
{
Timer0Init();//定时器0初始化
ServoPin=0;//引脚初始化
while(1)
{
Servo_Proc(0);
Delay(500);
Servo_Proc(45);
Delay(500);
Servo_Proc(90);
Delay(500);
Servo_Proc(135);
Delay(500);
Servo_Proc(180);
Delay(500);
}
}
void Timer0_Routine() interrupt 1 //从装载值100us
{
TL0 = 0xA4; //设置定时初始值
TH0 = 0xFF; //设置定时初始值
if(Time>Count){ServoPin=1;}//PWM波的占空比
else{ServoPin=0;}
Count++;
Count%=200;
}
STM32程序
main 函数
#include "Delay.h"
#include "LED.h"
#include "Time3.h"
#include "Servos.h"
int main(void)
{
u8 i;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
Servos_Init(2000,720);//周期20ms频率10KHz
delay_init();
Servos_Angle_Init(180);
while(1)
{
for(i=180;i>0;i--)
{
Servos_Turn_Angle(i,20);
}
for(i=0;i<180;i++)
{
Servos_Turn_Angle(i,20);
}
}
}
Servos.c 函数
#include "stm32f10x.h" // Device header
#include "Servos.h"
#include "delay.h"
//变量声明区
uint16_t temp;
u8 Start_Stop=1;
u8 flag,flag1;
uint16_t Now_Angle,Old_Angle;//前一个角度值和后一个角度值
//子函数
/**
Tout= ((arr+1)*(psc+1))/Tclk;
Tclk: TIM3 的输入时钟频率(单位为 Mhz)。72Mhz
Tout: TIM3 溢出时间(单位为 ms)
Period : 自动重装值。
Prescaler : 时钟预分频数
**/
/**
* @brief 舵机IO初始化
* @param Period:自动重装值。
* @param Prescaler:时钟预分频数
* @retval 无
*/
void Servos_Init(u16 Period,u16 Prescaler)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO,ENABLE);//打开GPIOB时钟/AFIO时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);//打开定时器时钟
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//PA0初始化/复用推挽输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
TIM_TimeBaseStructure.TIM_Period = Period - 1;//自动重装载寄存器周期的值
TIM_TimeBaseStructure.TIM_Prescaler = Prescaler - 1;//TIMx 时钟频率除数的预分频值
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;//设置了时钟分割
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;//计数器模式
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseStructure);
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2;//PWM模式2
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;//使能PWM输出
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;//有效为高电平
TIM_OC1Init(TIM2,&TIM_OCInitStructure);
TIM_OC1PreloadConfig(TIM2,TIM_OCPreload_Enable);//使能TIM3在CCR2上的预装载寄存器
TIM_ARRPreloadConfig(TIM2,ENABLE);//开启自动预装载寄存器
TIM_Cmd(TIM2,ENABLE);
}
/** 角度 计数值
X Y
t = 0.5ms——————-舵机会转到 0 ° 1950
t = 1.0ms——————-舵机会转到 45° 1900
t = 1.5ms——————-舵机会转到 90° 1850
t = 2.0ms——————-舵机会转到 135° 1800
t = 2.5ms——————-舵机会转到 180° 1750
**/
/**
* @brief Servos角度初始化
* @param Angle:角度值
* @retval 无
*/
void Servos_Angle_Init(uint16_t Angle)//周期位20ms,不管频率如何
{ //只要确保角度固定的高电平时间一致
temp = 1950-(int)(Angle*10/9); //设角度为“x”计数值为“y”列y=kx+b
TIM_SetCompare1(TIM2,temp); //k=-10/9 b=1950
delay_ms(500);
}
/**
* @brief 把舵机角度初始化数据传到后面函数
* @param 无
* @retval temp
*/
uint16_t return_Angle()
{
return temp;
}
/**
* @brief 舵机旋转方向和速度
* @param Angle :角度
* @param Speed :速度
* @retval 无
*/
void Servos_Turn_Angle(uint16_t Angle,uint16_t Speed)
{
uint16_t i;
if(flag==0){Old_Angle = return_Angle();flag=1;}//void Servos_Angle_Init(u8 Angle)
if(Start_Stop) //争对获取舵机初始化是的值,只操作一次
{
Now_Angle = 1950-(int)((Angle/9)*10);
if(Now_Angle<Old_Angle) //通过比较前角度和后角度,判断是正传还是逆转
{ //向左转,
for(i=Old_Angle;i>=Now_Angle;i--)
{
TIM_SetCompare1(TIM2,i);
delay_ms(Speed);
}
}
if(Now_Angle>Old_Angle) //向右转
{
for(i=Old_Angle;i<=Now_Angle;i++)
{
TIM_SetCompare1(TIM2,i);
delay_ms(Speed);
}
}
Old_Angle = Now_Angle;//对前一个角度进行存储,然后计较
}
}
/**
* @brief 舵机旋转方向和速度,和 Servos_Turn_Angle(uint16_t Angle,uint16_t Speed)功能一样
* @param Angle :角度
* @param Speed :速度
* @retval 无
*/
void Angle_config(uint16_t Angle,uint16_t Speed)
{
Servos_Turn_Angle(Angle,Speed);
}
/**
* @brief 启动舵机运行,一般用于舵机停转后启动
* @param 无
* @retval 无
*/
void Start_Servos(void)
{
Start_Stop = 1;
}
/**
* @brief 停止舵机运行,一般用于舵机旋转结束停止
* @param 无
* @retval 无
*/
void Stop_Servos(void)
{
Start_Stop = 0;
}
Servos.h 函数
#ifndef __Servos_H
#define __Servos_H
#include "sys.h"
void Servos_Init(u16 Period,u16 Prescaler);//初始化IO、定时器
void Servos_Angle_Init(uint16_t Angle);//初始化舵机初始位置
void Servos_Turn_Angle(uint16_t Angle,uint16_t Speed);
//用户操作层
void Start_Servos(void);//开启舵机
void Stop_Servos(void);//停止舵机
void Angle_config(uint16_t Angle,uint16_t Speed);//舵机控制
#endif