标题:
STC单片机关于舵机pwm控制源程序(单个与多个速度控制都有)
[打印本页]
作者:
k.leonard
时间:
2019-12-21 20:28
标题:
STC单片机关于舵机pwm控制源程序(单个与多个速度控制都有)
具体内容见附件
51hei.png
(7.33 KB, 下载次数: 30)
下载附件
2019-12-21 20:33 上传
单片机源程序如下:
/*****************************************************************************
** 文件 : main.c
** 作者 : Zheng23
** 日期 :
** 功能 : 产生8路PWM输出控制8路舵机,方法:将20毫秒分成8个2500微秒,
即8个单元,先计算出第一个单元的舵机PWM控制线的脉冲宽度,因
为控制舵机的脉冲宽度最大为2500微秒,剩余的(20000 - 2500)
毫秒就是剩下的7个单元时间,第一个单元的舵机PWM控制线全部为
低电平了,这时就可以安心的处理剩下的7个单元的事情了,同理
第二个单元也是一样,这样循环下去就可以控制8路PWM输出了。
/*****************************************************************************/
//#include <STC15.h>
//#include"STC89C5xRC.H"
//一定要用12M的晶振
//一定要用12M的晶振
//一定要用12M的晶振
//一定要用12M的晶振
//一定要用12M的晶振
//一定要用12M的晶振
#include "reg51.h"
#define bool bit
#define uint8 unsigned char
#define uint16 unsigned short int
#define uint32 unsigned long
sbit SERVO0=P0^0; //控制舵机的PWM输出口
sbit SERVO1=P0^1;
sbit SERVO2=P0^2;
sbit SERVO3=P0^3;
sbit SERVO4=P0^4;
sbit SERVO5=P0^5;
sbit SERVO6=P0^6;
sbit SERVO7=P0^7;
sbit LED=P2^7; //调试用
uint16 ServoPwmDuty[8] = {1500,1500,1500,1500,1500,1500,1500,1500}; //PWM脉冲宽度
uint16 ServoPwmDutySet[8] = {1500,1500,1500,1500,1500,1500,1500,1500}; //PWM脉冲宽度
double ServoPwmDutyInc[8]; //为了速度控制,当PWM脉宽发生变化时,每2.5ms或20ms递增的PWM脉宽
bool Flag_20ms = 0; //20毫秒标识位,在定时中断里面置位
bool Flag_2_5ms = 0; //2.5毫秒标识位,在定时中断里面置位
bool ServoPwmDutyHaveChange = 0; //脉宽有变化标志位
uint16 ServoTime = 2000; //舵机从当前角度运动到指定角度的时间,也就是控制速度
/***********************************************************
* 名 称: DelayMs(uint16 ms)
* 功 能: 延时ms毫秒
* 入口参数: ms 毫秒
* 出口参数: 无
* 说 明:
/**********************************************************/
void DelayMs(uint16 ms)
{
uint16 i,j;
for(i=0;i<800;i++) //89单片机用85,12系列单片机用800
for(j=0;j<ms;j++);
}
/***********************************************************
* 名 称:InitTimer0()
* 功 能:时钟0初始化
* 入口参数:无
* 出口参数:无
* 说 明:12M晶振,12分频,所以计数器每递增一个数就是1微秒,完全满足舵机控制的精度要求
因为定时器是TH0,TL0都要全部计数到0xFF后在计1个数就会产生中断,所以要想产生
x毫秒的中断,那么TH0,TL0就应该赋值(0xFFFF-x) 从这个值开始计数产生定时中断
/**********************************************************/
void InitTimer0(void)
{
// AUXR &= 0x7F; //定时器时钟12T模式
// AUXR |= 0x00; //T0,T1工作在12T
TMOD &= 0xF0; //设置定时器模式
TMOD |= 0x01; //设置定时器模式
TL0 = 0x00; //设置定时初值
TH0 = 0x00; //设置定时初值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
ET0 = 1; //开定时器0中断
}
/***********************************************************
* 名 称:Timer0Value(uint16 pwm)
* 功 能:给定时器0计数器赋值产生定时中断
* 入口参数:pwm 控制舵机的PWM脉冲宽度值(范围:500~2500)
* 出口参数:无
* 说 明:12M晶振,12分频,所以计数器每递增一个数就是1微秒,完全满足舵机控制的精度要求
因为定时器是TH0,TL0都要全部计数到0xFF后在计1个数就会产生中断,所以要想产生
pwm毫秒的中断,那么TH0,TL0就应该赋值(0xFFFF-pwm) 从这个值开始计数产生定时中断
/**********************************************************/
void Timer0Value(uint16 pwm)
{
uint16 value;
value=0xffff-pwm;
TR0 = 0;
TL0=value; //16位数据给8位数据赋值默认将16位数据的低八位直接赋给八位数据
TH0=value>>8; //将16位数据右移8位,也就是将高8位移到低八位,再赋值给8位数据
TR0 = 1;
}
/***********************************************************
* 名 称: ServoPwmDutyCompare()
* 功 能: 脉宽变化比较及速度控制
* 入口参数: 无
* 出口参数: 无
* 说 明:
/**********************************************************/
void ServoPwmDutyCompare()
{
uint8 i;
static uint16 ServoPwmDutyIncTimes; //需要递增的次数
static bool ServoRunning = 0; //舵机正在以指定速度运动到指定的脉宽对应的位置
if(ServoRunning == 0 && ServoPwmDutyHaveChange)//停止运动并且脉宽发生变化时才进行计算
{
ServoPwmDutyHaveChange = 0;
// ServoPwmDutyIncTimes = ServoTime*2/5; //ServoTime/(20/8) //当每2.5ms调用一次ServoPwmDutyCompare()函数时用此句
ServoPwmDutyIncTimes = ServoTime/20; //当每20ms调用一次ServoPwmDutyCompare()函数时用此句
for(i=0;i<8;i++)
{
//if(ServoPwmDuty[i] != ServoPwmDutySet[i])
{
if(ServoPwmDutySet[i] > ServoPwmDuty[i])
{
ServoPwmDutyInc[i] = ServoPwmDutySet[i] - ServoPwmDuty[i];
ServoPwmDutyInc[i] = -ServoPwmDutyInc[i];
}
else
{
ServoPwmDutyInc[i] = ServoPwmDuty[i] - ServoPwmDutySet[i];
}
ServoPwmDutyInc[i] /= ServoPwmDutyIncTimes;//每次递增的脉宽
}
}
ServoRunning = 1; //舵机开始动作
}
if(ServoRunning)
{
ServoPwmDutyIncTimes--;
for(i=0;i<8;i++)
{
if(ServoPwmDutyIncTimes == 0)
{ //最后一次递增就直接将设定值赋给当前值
ServoPwmDuty[i] = ServoPwmDutySet[i];
ServoRunning = 0; //到达设定位置,舵机停止运动
}
else
{
ServoPwmDuty[i] = ServoPwmDutySet[i] +
(signed short int)(ServoPwmDutyInc[i] * ServoPwmDutyIncTimes);
}
}
}
}
/***********************************************************
* 名 称: main()
* 功 能: 入口函数
* 入口参数: 无
* 出口参数: 无
* 说 明:
/**********************************************************/
void main(void)
{
uint8 i;
uint16 Time;
InitTimer0(); //定时器0初始化
EA = 1; //开总中断
while(1) //大循环
{
if(Flag_20ms) // Flag_2_5ms 2.5毫秒调用一次ServoPwmDutyCompare()或20毫秒调用一次
{
Flag_20ms = 0;
Flag_2_5ms = 0;
Time++;
ServoPwmDutyCompare();
}
if(Time > 110)
{
Time = 0;
LED = ~LED;
if(ServoPwmDutySet[0] == 500)
{
ServoPwmDutySet[0] = 2500; //脉冲宽度在2500微秒,对应90°
for(i = 1;i<8;i++)
ServoPwmDutySet[i] = 500; //脉冲宽度在500微秒,对应-90°
}
else
{
ServoPwmDutySet[0] = 500; //脉冲宽度在500微秒,对应-90°
for(i = 1;i<8;i++)
ServoPwmDutySet[i] = 1500; //脉冲宽度在1500微秒,对应0°
}
ServoPwmDutyHaveChange = 1;
}
}
}
/***********************************************************
* 名 称: Timer0_isr() interrupt 1 using 1
* 功 能: 时钟0中断处理
* 入口参数: 无
* 出口参数: 无
* 说 明:
/**********************************************************/
void Timer0_isr(void) interrupt 1 using 1
{
static uint16 i = 1; //静态变量:每次调用函数时保持上一次所赋的值,
//跟全局变量类似,不同是它只能用于此函数内部
switch(i)
{
case 1:
SERVO0 = 1; //PWM控制脚高电平
//给定时器0赋值,计数Pwm0Duty个脉冲后产生中断,下次中断会进入下一个case语句
Timer0Value(ServoPwmDuty[0]);
Flag_20ms = 1;
Flag_2_5ms = 1;
break;
case 2:
SERVO0 = 0; //PWM控制脚低电平
//此计数器赋值产生的中断表示下一个单元要进行任务的开始
Timer0Value(2500-ServoPwmDuty[0]);
break;
case 3:
SERVO1 = 1;
Timer0Value(ServoPwmDuty[1]);
Flag_2_5ms = 1;
break;
case 4:
SERVO1 = 0; //PWM控制脚低电平
Timer0Value(2500-ServoPwmDuty[1]);
break;
case 5:
SERVO2 = 1;
Timer0Value(ServoPwmDuty[2]);
Flag_2_5ms = 1;
break;
case 6:
SERVO2 = 0; //PWM控制脚低电平
Timer0Value(2500-ServoPwmDuty[2]);
break;
case 7:
SERVO3 = 1;
Timer0Value(ServoPwmDuty[3]);
Flag_2_5ms = 1;
break;
case 8:
SERVO3 = 0; //PWM控制脚低电平
Timer0Value(2500-ServoPwmDuty[3]);
break;
case 9:
SERVO4 = 1;
Timer0Value(ServoPwmDuty[4]);
Flag_2_5ms = 1;
break;
case 10:
SERVO4 = 0; //PWM控制脚低电平
Timer0Value(2500-ServoPwmDuty[4]);
break;
case 11:
SERVO5 = 1;
Timer0Value(ServoPwmDuty[5]);
Flag_2_5ms = 1;
break;
case 12:
SERVO5 = 0; //PWM控制脚低电平
Timer0Value(2500-ServoPwmDuty[5]);
break;
case 13:
SERVO6 = 1;
Timer0Value(ServoPwmDuty[6]);
Flag_2_5ms = 1;
break;
case 14:
SERVO6 = 0; //PWM控制脚低电平
Timer0Value(2500-ServoPwmDuty[6]);
break;
case 15:
SERVO7 = 1;
Timer0Value(ServoPwmDuty[7]);
Flag_2_5ms = 1;
break;
case 16:
SERVO7 = 0; //PWM控制脚低电平
Timer0Value(2500-ServoPwmDuty[7]);
i = 0;
break;
}
i++;
}
复制代码
/*****************************************************************************
** 文件 : main.c
** 作者 : Zheng23
** 日期 :
** 功能 :
/*****************************************************************************/
//#include <STC15.h>
//#include"STC89C5xRC.H"
//一定要用12M的晶振
//一定要用12M的晶振
//一定要用12M的晶振
//一定要用12M的晶振
//一定要用12M的晶振
//一定要用12M的晶振
#include "reg51.h"
#define bool bit
#define uint8 unsigned char
#define uint16 unsigned short int
#define uint32 unsigned long
sbit SERVO0=P0^0; //控制舵机的PWM输出口
sbit LED=P2^7; //调试用
uint16 ServoPwmDuty=1500; //PWM脉冲宽度
uint16 ServoPwmDutySet=1500;
double ServoPwmDutyInc;
bool Flag_20ms = 0; //20毫秒标识位,在定时中断里面置位
bool ServoPwmDutyHaveChange = 0; //脉宽有变化标志位
uint16 ServoTime = 2000;
/***********************************************************
* 名 称: DelayMs(uint16 ms)
* 功 能: 延时ms毫秒
* 入口参数: ms 毫秒
* 出口参数: 无
* 说 明:
/**********************************************************/
void DelayMs(uint16 ms)
{
uint16 i,j;
for(i=0;i<85;i++) //89单片机用85,12系列单片机用800
for(j=0;j<ms;j++);
}
/***********************************************************
* 名 称:InitTimer0()
* 功 能:时钟0初始化
* 入口参数:无
* 出口参数:无
* 说 明:12M晶振,12分频,所以计数器每递增一个数就是1微秒,完全满足舵机控制的精度要求
因为定时器是TH0,TL0都要全部计数到0xFF后在计1个数就会产生中断,所以要想产生
x毫秒的中断,那么TH0,TL0就应该赋值(0xFFFF-x) 从这个值开始计数产生定时中断
/**********************************************************/
void InitTimer0(void)
{
// AUXR &= 0x7F; //定时器时钟12T模式
// AUXR |= 0x00; //T0,T1工作在12T
TMOD &= 0xF0; //设置定时器模式
TMOD |= 0x01; //设置定时器模式
TL0 = 0x00; //设置定时初值
TH0 = 0x00; //设置定时初值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
ET0 = 1; //开定时器0中断
}
/***********************************************************
* 名 称:Timer0Value(uint16 pwm)
* 功 能:给定时器0计数器赋值产生定时中断
* 入口参数:pwm 控制舵机的PWM脉冲宽度值(范围:500~2500)
* 出口参数:无
* 说 明:12M晶振,12分频,所以计数器每递增一个数就是1微秒,完全满足舵机控制的精度要求
因为定时器是TH0,TL0都要全部计数到0xFF后在计1个数就会产生中断,所以要想产生
pwm毫秒的中断,那么TH0,TL0就应该赋值(0xFFFF-pwm) 从这个值开始计数产生定时中断
/**********************************************************/
void Timer0Value(uint16 pwm)
{
uint16 value;
value=0xffff-pwm;
TR0 = 0;
TL0=value; //16位数据给8位数据赋值默认将16位数据的低八位直接赋给八位数据
TH0=value>>8; //将16位数据右移8位,也就是将高8位移到低八位,再赋值给8位数据
TR0 = 1;
}
/***********************************************************
* 名 称: ServoPwmDutyCompare()
* 功 能: 脉宽变化比较及速度控制
* 入口参数: 无
* 出口参数: 无
* 说 明:
/**********************************************************/
void ServoPwmDutyCompare()
{
static uint16 ServoPwmDutyIncTimes; //需要递增的次数
static bool ServoRunning = 0; //舵机正在以指定速度运动到指定的脉宽对应的位置
if(ServoRunning == 0 && ServoPwmDutyHaveChange)//停止运动并且脉宽发生变化时才进行计算
{
ServoPwmDutyHaveChange = 0;
ServoPwmDutyIncTimes = ServoTime/20;
if(ServoPwmDutySet > ServoPwmDuty)
{
ServoPwmDutyInc = ServoPwmDutySet - ServoPwmDuty;
ServoPwmDutyInc = -ServoPwmDutyInc;
}
else
{
ServoPwmDutyInc = ServoPwmDuty - ServoPwmDutySet;
}
ServoPwmDutyInc /= ServoPwmDutyIncTimes;//每次递增的脉宽
ServoRunning = 1; //舵机开始动作
}
if(ServoRunning)
{
ServoPwmDutyIncTimes--;
if(ServoPwmDutyIncTimes == 0)
{ //最后一次递增就直接将设定值赋给当前值
ServoPwmDuty = ServoPwmDutySet;
ServoRunning = 0; //到达设定位置,舵机停止运动
}
else
{
ServoPwmDuty = ServoPwmDutySet +
(signed short int)(ServoPwmDutyInc * ServoPwmDutyIncTimes);
}
}
}
/***********************************************************
* 名 称: main()
* 功 能: 入口函数
* 入口参数: 无
* 出口参数: 无
* 说 明:
/**********************************************************/
void main(void)
{
uint16 Time;
InitTimer0(); //定时器0初始化
EA = 1; //开总中断
while(1) //大循环
{
if(Flag_20ms)
{
Time++;
Flag_20ms = 0;
ServoPwmDutyCompare();
}
if(Time > 110)
{
Time = 0;
LED = ~LED;
if(ServoPwmDutySet == 500)
{
ServoPwmDutySet = 2500; //脉冲宽度在2500微秒,对应90°
}
else
{
ServoPwmDutySet = 500; //脉冲宽度在500微秒,对应-90°
}
ServoPwmDutyHaveChange = 1;
}
}
}
/***********************************************************
* 名 称: Timer0_isr() interrupt 1 using 1
* 功 能: 时钟0中断处理
* 入口参数: 无
* 出口参数: 无
* 说 明:
/**********************************************************/
void Timer0_isr(void) interrupt 1 using 1
{
static uint16 i = 1; //静态变量:每次调用函数时保持上一次所赋的值,
//跟全局变量类似,不同是它只能用于此函数内部
switch(i)
{
case 1:
SERVO0 = 1; //PWM控制脚高电平
//给定时器0赋值,计数Pwm0Duty个脉冲后产生中断,下次中断会进入下一个case语句
Timer0Value(ServoPwmDuty);
Flag_20ms = 1;
break;
case 2:
SERVO0 = 0; //PWM控制脚低电平
//高脉冲结束后剩下的时间(20000-Pwm0Duty)全是低电平了,Pwm0Duty + (20000-Pwm0Duty) = 20000个脉冲正好为一个周期20毫秒
Timer0Value(20000-ServoPwmDuty);
i = 0;
break;
}
i++;
}
复制代码
所有资料51hei提供下载:
STC单片机舵机PWM控制教程.zip
(284.19 KB, 下载次数: 113)
2019-12-21 20:27 上传
点击文件名下载附件
下载积分: 黑币 -5
作者:
你消失的踪迹
时间:
2019-12-21 22:05
求详细控制多舵机程序
作者:
yang305228226
时间:
2020-2-24 15:47
看看,多谢分享!!!!
作者:
yang305228226
时间:
2020-2-24 15:48
多谢分享!!!!!!!!!!!
作者:
y123chiang
时间:
2020-2-26 19:28
謝謝大大無私分享.....
作者:
快乐眼泪
时间:
2020-2-27 07:44
正在学习,谢谢楼主无私奉献!
作者:
独行者stc
时间:
2020-2-27 10:54
要是能用在机器狗上面多好啊!谢谢楼主分享。
作者:
你消失的踪迹
时间:
2020-3-8 08:13
感谢楼主分享
作者:
gdatgb
时间:
2020-6-3 16:21
看看,多谢分享!!!
作者:
nhwhg
时间:
2020-6-5 15:33
学习中,感谢楼主无私奉献!
欢迎光临 (http://www.51hei.com/bbs/)
Powered by Discuz! X3.1