基于ADUC841的电机控制软件设计与流程分析
0赞1.主程序工作过程
基于ADUC841的电机控制系统要求能产生规定的典型的3种力矩波形,分别定义位状态0、状态1、状态2。控制系统主程序流程图如图所示,系统的工作过程为,上电后首先进行系统初始化,包括设置堆栈首地址,上电后将DAC输出2.5V电压,来平衡硬件电路中的2.5V的偏置电压,从而电机在商店以后控制率未投入以前保持静止状态。另外初始化定时器的控制字及定时市场的初始化,另外初始化中断设置,打开系统中断和定时器中断。在这里我们采用3.3ms的采样时间,它可以通过设置低能提供时器计数初值来完成。

由于单片机在上电之后的一段时间之内,需要对系统内部的一些设置进行初始化,这样单片机将处于失控状态,而在这段时间内,模拟电路中的2.5V已经投入运行,在这种情况下2.5V的电压将加在了电机电枢的两端,从而使电机进行转动,而这种转动时我们所不希望见到的。因此,在这里我们采用了时间继电器来消除单片机的失控时间,采用常闭继电器,使得在上电后电机电枢两端处于短接状态,延时十秒后,等单片机系统的初始化过程结束后,见常闭结点打开,电机处于正常运行状态,就可消除失控时间的问题。延时10s后,单片机系统等待上位机发送命令指令,当上位机发送0x30到下位机时,电机投入运转状态0,当下位机接受到0x31的指令时,电机投入运转状态1,当下位机接受到0x32的指令时,电机投入运转状态2。下位机接受到上位机的命令指令以后,则触发定时器0开始计时,定时器溢出后,则系统进入定时器中断子程序,进行相应的控制和操作,当中断子程序执行完后,在主程序中,让程序在一个死循环中运转,第一次进入死循环时,下位机向上位机上传电机误差的初始值,然后再进入死循环时,每隔0.1s将电机的误差值上传到上位机进行运算和处理。
2. 定时器中断子程序工作过程
定时器中断子程序流程图如图所示,其工作过程如下,首先定义一些在中断子程序中使用到的变量,另外还要进行一些定时器的初始化处理。紧接着法出控制指令,控制光电码盘对电机的当前位置信息进行采集,获得三十二位的关于电机位置的计数值f。按照指标要求,给出电机运转的指令曲线u,将u和f相减得系统的偏差e(e = u - f),根据e的大小,将设计好的二型PID控制器投入,经过运算后得到系统的控制参数,为了防止系统溢出,加入限幅指令,然后根据上位机下达的控制指令,将不同的电机控制参数以不同的方式投入运转,从而实现不同的控制。

#includeunsigned int xdata data_f_h[2];//电机0f当前和上一次的高16位(0-上一次,1-当前) unsigned int xdata data_f_l[2]; //电机0f当前和上一次的低16位(0-上一次,1-当前) float xdata x[2]; float xdata u[2]; unsigned long xdata midsx; float xdata r[2]; unsigned int xdata time; unsigned int xdata flag; unsigned char xdata receivedata;//,flagsx,flagsx0,flagsx1; unsigned int xdata uploaddata; void receivechar() { while(RI==0){;} RI = 0; receivedata = SBUF; } void sendflag() { while(TI==0){;} TI = 0; SBUF = 10; while(TI==0){;} TI = 0; SBUF = 13; } void sendchar(unsigned char senddata) { unsigned char data temp,tempH,tempL//数据转换 temp = senddata; senddata = (senddata>>4); senddata = (senddata&0x0F); if(senddata < 0x0A) { senddata = senddata + '0'; //0x30; } else { senddata = senddata + '0'+7; //0x37; } tempH = senddata; senddata = temp; senddata = (senddata&0x0F); if(senddata < 0x0A) { senddata = senddata + '0'; //0x30; } else { senddata = senddata + '0'+7; //0x37; } tempL = senddata;//发送 while(TI==0){;} TI = 0; SBUF = tempH; while(TI==0){;} TI = 0; SBUF = tempL; } unsigned int receivePosi16() { unsigned int data countersx; unsigned char data i; unsigned char data countersx1,countersx2; P07 = 0; P06 = 1; for(i=0;i<3;i++) { } countersx1 = P1;//读取高八位计数值 P06 = 0; for(i=0;i<3;i++) { } countersx2 = P1;//高八位计数值送入itemp,低八位计数值送入temp for(i=0;i<3;i++) { } P07 = 1;//关闭cpld输出 countersx = countersx1; countersx = (countersx<<8); countersx= countersx + countersx2; return countersx; } unsigned long receivePosi32(unsigned int data itempsx) { unsigned long xdata re1; data_f_h[0]=data_f_h[1]; data_f_l[0]=data_f_l[1]; if((data_f_l[0]<1000)&&(itempsx>64536)) //当反转时,data_f_h[1]减。条件由 { //一次控制期间的最大计数决定 data_f_h[1] = data_f_h[1] - 1; } else if((data_f_l[0]>64536)&&(itempsx<1000)) { data_f_h[1] = data_f_h[1] + 1; } else{;} data_f_l[1] = itempsx; re1 = data_f_h[1]; re1 = (re1<<16); re1 = re1 + data_f_l[1]; return re1; } void IT0P()interrupt 1 { float data ltempsx; long data ltempsx1; unsigned long data receive32; unsigned int data receivesx16; TR0 = 0; TL0=0xBC; TH0=0x27;//更新本计数器计数器值 TR0 = 1; flag = 0; receivesx16=receivePosi16();//读取光电码盘计数值 receive32=receivePosi32(receivesx16);//读取光电码盘三十二位计数值 time++; r[0]=r[1];//更新偏差值 u[0]=u[1]; u[1]=0x00010000; if(time==3001) { time=1; } else{;} if(time<=150) { u[1] = u[0]+0.0354958813*time+0.0177479407; signsx = 1; } else if(time<=1350) { u[1] = u[0]+5.3781638370; signsx = 2; } else if(time<=1500) { u[1] = u[0]-0.0354958813*time+53.2798556836; signsx = 3; } else if(time<=1650) { u[1] = u[0]-0.0354958813*time+53.2798556836; signsx = 4; } else if(time<=2850) { u[1] = u[0]-5.3781638370; signsx = 5; } else { u[1] = u[0]-106.5236776695+0.0354958813*time; signsx = 6; }*/ if(receive32>0x90000) { u[1] = u[1]-0x90000; receive32 = receive32 - 0x90000; data_f_h[1] = data_f_h[1] - 9; } else {;}*/ r[1]=u[1]-receive32;//更新当前的偏差值 x[0] = x[1]; x[1] = (498.95 * r[1])/2+ (0.362245 * x[0]) - (471.6344833 * r[0])/2; x[1]=x[1]+0x0800; if(signsx ==1) { x[1] = x[1] + 0x088C; } else if(signsx ==2) { x[1] = x[1] + 0x087C; } else if(signsx ==3) { x[1] = x[1] + 0x086C; } else if(signsx ==4) { x[1] = x[1] + 0x0774; } else if(signsx ==5) { x[1] = x[1] + 0x0784; } else if(signsx ==6) { x[1] = x[1] + 0x0794; } else{;}*/ ltempsx = x[1]; //ltempsx为浮点数 if(ltempsx>0) { ltempsx = ltempsx + 0.5; } else { ltempsx = ltempsx - 0.5; } ltempsx1 = ltempsx; //ltempsx1为长整形 if(ltempsx1<0) { ltempsx1 = 0; } else if(ltempsx1>0x00000FFF) { ltempsx1 = 0x00000FFF; } else{;} ltempsx1 = ltempsx1&0x00000FFF;//取低十二位DA值 DAC0H = (ltempsx1>>8); DAC0L = ltempsx1; } / if(flagsx0 ==0) { if(flagsx == 0 ) { DAC0H = (ltempsx01>>8);//取高四位DA值 DAC0L = ltempsx01;//启动DA0//T0=0x7F98; DAC1H = (ltempsx11>>8);//取高四位DA值 DAC1L = ltempsx11;//启动DA0//T0=0x7F98; P37=0; spi_w(0x08); spi_w(0x00); P37=1; // 上电后DAC2给出2.5V电压 } else if(flagsx == 1) { DAC0H = (ltempsx01>>8);//取高四位DA值 DAC0L = ltempsx01;//启动DA0//T0=0x7F98; DAC1H = 0x08; DAC1L = 0x00;//上电后DAC1给出2.5V电压 P37=0; spi_w(0x08); spi_w(0x00); P37=1; // 上电后DAC2给出2.5V电压 } else if(flagsx ==2) { DAC0H = (ltempsx01>>8);//取高四位DA值 DAC0L = ltempsx01;//启动DA0//T0=0x7F98; DAC0H = 0x08; DAC0L = 0x00;//上电后DAC0给出2.5V电压 DAC1H = 0x08; DAC1L = 0x00;//上电后DAC1给出2.5V电压 P37=0; spi_w(0x08); spi_w(0x00); P37=1; // 上电后DAC2给出2.5V电压 } else{;} } else if(flagsx0 ==1) { if(time0 ==1500) { flagsx1 = 1; } else { flagsx1 = 0; } if((flagsx1==1)&&(flagsx==0)) { DAC0H = (ltempsx01>>8);//取高四位DA值 DAC0L = ltempsx01;//启动DA0//T0=0x7F98; DAC1H = (ltempsx11>>8);//取高四位DA值 DAC1L = ltempsx11;//启动DA0//T0=0x7F98; P37=0; spi_w(0x08); spi_w(0x00); P37=1; // 上电后DAC2给出2.5V电压 } else if((flagsx1==0)&&(flagsx==0)) { DAC0H = (ltempsx01>>8);//取高四位DA值 DAC0L = ltempsx01;//启动DA0//T0=0x7F98; DAC1H = 0x08; DAC1L = 0x00;//上电后DAC1给出2.5V电压 P37=0; spi_w(0x08); spi_w(0x00); P37=1; // 上电后DAC2给出2.5V电压 } else if (flagsx ==1) { DAC0H = (ltempsx01>>8);//取高四位DA值 DAC0L = ltempsx01;//启动DA0//T0=0x7F98; DAC1H = 0x08; DAC1L = 0x00;//上电后DAC1给出2.5V电压 P37=0; spi_w(0x08); spi_w(0x00); P37=1; // 上电后DAC2给出2.5V电压 } else if(flagsx ==2) { DAC0H = 0x08; DAC0L = 0x00;//上电后DAC0给出2.5V电压 DAC1H = 0x08; DAC1L = 0x00;//上电后DAC1给出2.5V电压 P37=0; spi_w(0x08); spi_w(0x00); P37=1; // 上电后DAC2给出2.5V电压 } else{;} } else if(flagsx0 ==2) { if(flagsx==0) { DAC0H = (ltempsx01>>8);//取高四位DA值 DAC0L = ltempsx01;//启动DA0//T0=0x7F98; DAC1H = (ltempsx11>>8);//取高四位DA值 DAC1L = ltempsx11;//启动DA0//T0=0x7F98; P37=0; spi_w(0x08); spi_w(0x00); P37=1; // 上电后DAC2给出2.5V电压 } else if(flagsx ==1) { DAC0H = (ltempsx01>>8);//取高四位DA值 DAC0L = ltempsx01;//启动DA0//T0=0x7F98; DAC1H = 0x08; DAC1L = 0x00;//上电后DAC1给出2.5V电压 P37=0; spi_w(0x08); spi_w(0x00); P37=1; // 上电后DAC2给出2.5V电压 } else if(flagsx ==2) { DAC0H = 0x08; DAC0L = 0x00;//上电后DAC0给出2.5V电压 DAC1H = 0x08; DAC1L = 0x00;//上电后DAC1给出2.5V电压 P37=0; spi_w(0x08); spi_w(0x00); P37=1; // 上电后DAC2给出2.5V电压 } else{;} } else{;} void main( void ) { unsigned int data receive16; SP = 0x30; SCON = 0x52; T3CON = 0x86; T3FD = 0x2D; CFG841 = 0x01; PLLCON = 0x50; DACCON = 0x7F; DAC0H = 0x08; DAC0L = 0x00;//上电后DAC0给出2.5V电压 DAC1H = 0x08; DAC1L = 0x00;//上电后DAC1给出2.5V电压 P0 = 0xFF; IE = 0x82;//开系统中断和定时期0中断 TMOD = 0x11;//设置计数器控制字 TL0=0xBC; TH0=0x27;//定时期计数值赋初值 data_f_h[0]=1; data_f_h[1]=1;//返回值高十六位赋初始值 receive16=receivePosi16();//读取码盘初始值 data_f_l[0] =receive16; data_f_l[1] =receive16;//返回值低十六位赋初始值为码盘上电初始值 u[0] = data_f_h[0]; midsx = u[0]; midsx = (midsx<<16); u[0] = midsx; u[0] = u[0] + data_f_l[0]; u[1] = u[0]; x[0] = 0; x[1] = 0; r[0] = 0; r[1] = 0; time = 0; flag = 0;//显示数据的间隔标志 sendflag(); sendchar(0x00); sendchar(0x00); sendflag(); /* receivechar(); if(receivedata == 0x00) { while(1); } else if(receivedata == 0x30) { flagsx = 0; flagsx0 = flagsx; } else if (receivedata == 0x31) { flagsx = 1; flagsx0 = flagsx; } else if (receivedata == 0x32) { flagsx = 2; flagsx0 = flagsx;} else{;}*/ TR0 = 1; while(1) { /* flagsx0 = SBUF-0x30; if(RI ==1) { RI =0; if(SBUF == 0x30) {flagsx=0;} else if(SBUF == 0x31) {flagsx = 1;} else if (SBUF == 0x32) {flagsx = 2;} else {;} } else{;}*/ if((flag==0)&&(time%30==0)) { flag = 1; uploaddata = r[1]; sendflag(); sendchar(0x00); sendchar(uploaddata); sendflag(); } else{;} } } 2. 逻辑电路程序(4 倍频程序): module ab(clk,oe,out,ina,inb,sel); input clk,oe,ina,inb,sel; output[7:0] out; reg[1:0] prestate,state; reg[15:0] counter; reg[15:0] counter1; wire[7:0] counter2; reg lock; wire [7:0]out; always @(posedge clk) begin state[0]<=inb; state[1]<=ina; begin if((prestate===2'b11)&&(state===2'b01)) //判向计数 begin counter<=counter+16'b1; end else if((prestate===2'b01)&&(state===2'b00)) begin counter<=counter+16'b1; end else if((prestate===2'b00)&&(state===2'b10)) begin counter<=counter+16'b1; end else if((prestate===2'b10)&&(state===2'b11)) begin counter<=counter+16'b1; end else if((prestate===2'b00)&&(state===2'b01)) begin counter<=counter-16'b1; end else if((prestate===2'b01)&&(state===2'b11)) begin counter<=counter-16'b1; end else if((prestate===2'b11)&&(state===2'b10)) begin counter<=counter-16'b1; end else if((prestate===2'b10)&&(state===2'b00)) begin counter<=counter-16'b1; end else counter<=counter; end prestate<=state; end always@(negedge clk) begin lock<=oe; if((lock==1)&&(oe==0)) begin counter1<=counter; end else begin counter1<=counter1; end end assign counter2=(sel)?counter1[15:8]:counter1[7:0]; assign out=(lock)?8'bz:counter2; endmodule
