51单片机控制舵机程序精度,数量,占用时间优化方案及程序

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 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];

相关文档
最新文档