实现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

物联沃分享整理
物联沃-IOTWORD物联网 » 实现STM32与STC51舵机任意角度控制

发表回复