51单片机控制舵机程序精度,数量,占用时间优化方案及程序
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
#include
#include
//本程序经软仿真调试在机器周期为1us时理论误差为0,不需要占用太多的cpu运行时间就可以控制8路舵机,精度为1ms-2ms 平均分成100份,在时间消耗和舵机数量上
//明显优于网上常见的舵机控制程序,keil3使用9(最高)编译器优化时达到理论误差为0,编译器优化级别过低时无法使用unsigned char gai;
unsigned char nt[8];
unsigned char nw[8];
unsigned char pwmbuffer[8] = {50,50,50,50,50,50,50,50};
void set(unsigned char m,unsigned char n){
if((m<8)&&(n<101)){ //如果输入合法则记录新数据并将状态改变标志置位pwmbuffer[m] = n;
gai = 1;
}
}
void tim(void){
unsigned char a1,a2,tempt,tempw; //a1,a2作为循环变量,tempt,tempw作为排序交换用临时变量
for(a1 = 0; a1 < 8; a1++){ //由舵机控制数据设置用于排序的表(两行八列)nt[a1] = pwmbuffer[a1]; //第几个舵机所需的高电平时长
nw[a1] = 1 << a1; //用第几位置一来表示第几个舵机
}
for(a1 = 0; a1 < 7; a1++){ //简单排序算法,找出最小的与第一个交换,在从剩余的中找出最小的与第二个交换,以此类推
unsigned char min = a1; //用于记录哪一个是最小的
for(a2 = a1 + 1; a2 < 8; a2++){ //从剩余项中找出最小的
if(nt[a2] < nt[min]){
min = a2;
}
}
tempt = nt[a1]; //交换
tempw = nw[a1];
nt[a1] = nt[min];
nw[a1] = nw[min];
nt[min] = tempt;
nw[min] = tempw;
}
for(a1 = 1; a1 < 8; a1++){ //之前记录应该变成低电平的输出口,之后记录应该是低电平的输出口nw[a1] |= nw[a1-1];
}
a2 = 0;
for(a1 = 0; a1 < 7; a1++){ //去掉重复
if(nt[a1] != nt[a1 + 1]){
nt[a2] = nt[a1];
nw[a2] = nw[a1];
a2++;
}
}
nt[a2] = nt[7];
nw[a2] = nw[7];
for(a2++; a2 < 8; a2++){
nt[a2] = 0;
nw[a2] = 0xFF;
}
for(a1 = 7; a1 > 0; a1--){ //之前记录每个输出口应持续高电平的时间,之后记录与上一次变化间隔时间if(nt[a1]){
nt[a1] = nt[a1] - nt[a1-1] - 1;
}
}
for(a1 = 0; a1 < 8; a1++){ //避免对0减1发生溢出跳到255,因为已经去掉重复所以不影响经度nw[a1] = ~nw[a1];
if(nt[a1] < 2){
nt[a1] = 1;
}
else{
nt[a1] -= 1;
}
}
}
void Timer0(void)
{
TMOD &= 0xF0;
TMOD |= 0x01;
TL0 = 0xE0;
TH0 = 0xB1;
TR0 = 1;
ET0 = 1;
}
void setuart(unsigned int a){ SCON = 0x50; //设置串口为模式1 TMOD &= 0x0F; //设置定时器T1为自动重装模式TMOD |= 0x20; //设置定时器T1为自动重装模式TH1 = 256 -(12000000/12/2/16)/a; //计算T1重载值TL1 = TH1; //手动初次重装ET1 = 0; //定时器中断关闭ES = 1; //串口中断启动EA = 1; //总中断启动TR1 = 1; //定时器T1启动}
void delayus(void){ //延时函数,加上外层的loop指令正好十个机器周期unsigned char i;
_nop_();
i = 1;
while (--i);
}
void delaypw(unsigned char a){ //可调延时函数产生误差多7个机器周期do{
delayus();
}while(--a);
}
void pwm(void) interrupt 1{
P0 = 0xFF; //所有输出口输出高电平
delaypw(99); //原定1000个机器周期减去10加7加3正好是1000
_nop_();_nop_();_nop_();
delaypw(nt[0]); //从数组取数需要两个周期输出需要一个周期加误差7个周期正好是10个周期
P0 = nw[0]; //nw记录了当前哪些输出口应该置为低电平
delaypw(nt[1]); //nt记录了到下一次输出变化应间隔的时间
P0 = nw[1];
delaypw(nt[2]);
P0 = nw[2];
delaypw(nt[3]);
P0 = nw[3];
delaypw(nt[4]);
P0 = nw[4];
delaypw(nt[5]);
P0 = nw[5];
delaypw(nt[6]);
P0 = nw[6];
delaypw(nt[7]);
P0 = nw[7];
if(gai){ //如果舵机位置被改变标志位置位则重新计算舵机参数nt,nw tim();
}
}
void uart(void) interrupt 4 {
static unsigned char z = 0; //静态变量用于储存舵机序号
unsigned char y; //临时变量暂存SBUF
if(RI){
RI = 0; //中断标志位清空
y = SBUF; //读取SBUF
if(y == 101){ //判断是否是清除信号
z = 0;
}
else if((z != 0)&&(y < 101)){ //如果以输入了舵机序号且新输入的角度信号没有溢出则更新数据
set(z,y);
}
else if((z == 0)&&(y < 8)){ //如果未输入舵机序号且新输入的舵机序号合法则记录舵机序号
z = y;
}
}
if(TI){
TI = 0;
}
}
void main(void){
tim(); //计算舵机所需参数nt,nw
Timer0(); //定时器初始化
setuart(600); //串口初始化,因为需要正好1us的的延时所以晶振必须用12MHz,通讯速率无法在高P0 = nt[0]; //查看参数计算结果,用于调试
P0 = nw[0];
P0 = nt[1];
P0 = nw[1];
P0 = nt[2];