#include <STC89C5xRC.h>
char tcount = 0;
char step = 0;//记录主电机走过的步数(走过的步数=step/2)
char buf[30];//存放命令
int en = 0;//en = 1 -> 系统工作, en = 0 -> 系统停止
int k = 0;
int len = 0;
int re = 0;//复位信号, 1 -> 复位, 0 -> 不复位
int direction = 0;//0 -> 顺时针, 1 -> 逆时针
int step_B = 0;//命令要求主电机转的步数
int motorRun = 0;// 1 -> 按命令转相应步数, 0 -> 无动作
void delay()
{
int i,j;
for(i = 0; i < 40; i++)
for(j = 0; j < 40 ; j++)
{
;//为针臂步进电机提供时延
}
}
void delay2()//针臂在底部停留时延
{
int i,j;
for(i=0;i<800;i++)
for(j=0;j<800;j++)
{
;
}
}
void motor_Control(int steps)//控制主转盘电机转动指定步数
{
int i;
for(i = 0; i < steps; i++)
{
P20 = 0;
delay();
P20 = 1;
delay();
}
}
void timer0() interrupt 1
{
int i = 0;
if(step == 0)
{
if(direction == 0)
{
P21 = 0;//顺时针
}
else if(direction == 1)
{
P21 = 1;//逆时针
}
}
TR0 = 0;//关闭定时器0
TH0 = 0x3c;
TL0 = 0xB0;//50ms
if(re == 1)//复位信号起作用
{
re = 0;
//针臂移至上限位, 转盘转回0号瓶
P03 = 0;//往上转
while(P01 == 1)//若未到上限位置,P0.1接单片机P01
{
P22 = 0;
delay();
P22 = 1;
delay();
}
while(P00 == 1)//若未到0号瓶位置
{
P20 = 0;
delay();
P20 = 1;
delay();
}
//霍尔传感器有误差, 还差2.5格左右, 再发20个脉冲
if(P21 == 1)//转盘逆时针转过来
{
while(i++ < 20)
{
P20 = 0;
delay();
P20 = 1;
delay();
}
}
else if(P21 == 0)//转盘顺时针转过来
{
while(i++ < 22)
{
P20 = 0;
delay();
P20 = 1;
delay();
}
}
tcount = 0;
en = 0;
step = 0;
goto END;
}
if(motorRun == 1)//电机按要求转命令起作用
{
motorRun = 0;
step = 0;
tcount = 0;
en = 0;
motor_Control(step_B);
step_B = 0;
goto END;
}
tcount++;
if(tcount == 2)//100ms = 0.1s
{
P20 = ~P20;//P20翻转,相当于发送脉冲
tcount = 0;
step ++;
if(step / 2 == 8)//电机走了8步(经历了8次高低电平的转换)
{
step = 0;//将步数归零
//P1.3=1代表向下,P1.3=0代表向上
P03 = 1;//往下转,P13接单片机P03
while(P02 == 1)//若未到下限位置,P0.2接单片机P02
{
P22 = 0;
delay();
P22 = 1;
delay();
}
delay2();//针臂在底部停留片刻
P03 = 0;//往上转
while(P01 == 1)//若未到上限位置,P0.1接单片机P01
{
P22 = 0;
delay();
P22 = 1;
delay();
}
}
}//1000ms = 1s 发送一个完整的脉冲
END: ;
TR0 = 1;//开启定时器0
}
void UART_INT() interrupt 4//串行通信中断处理程序
{
int len = 0;
int j = 0;
if(RI == 1)//如果收到数据
{
if(k == 30)
{
k = 0;//防止越界访问
}
RI = 0;
buf[k++] = SBUF;//将数据接收下来
if(buf[k - 1] == 'E')//到达结尾
{
len = k - 1;//记录数据长度
k = 0;
if(len == 6)
{
if(buf[0] == 'S' && buf[1] == 't' && buf[2] == 'a' && buf[3] == 'r' && buf[4] == 't' && buf[5] == '.')
{
//Start命令(开机)
en = 1;
P27 = 0;//D8亮
P26 = 1;
P25 = 1;
P24 = 1;
//再将en的值传回去
SBUF = en + 48;
}
else if(buf[0] == 'R' && buf[1] == 'e' && buf[2] == 's' && buf[3] == 'e' && buf[4] == 't' && buf[5] == '.')//主转盘回到原始位置,针臂抬到最高位置
{
//Reset命令(复位命令)
en = 1;
P27 = 1;
P26 = 0;//D7亮
P25 = 1;
P24 = 1;
SBUF = en + 48;
re = 1;//复位信号
}
}
else if(len == 5)//停机命令
{
if(buf[0] == 'S' && buf[1] == 't' && buf[2] == 'o' && buf[3] == 'p' && buf[4] == '.')
{
//Stop命令(停机命令)
en = 0;
P27 = 1;
P26 = 1;
P25 = 0;//D6亮
P24 = 1;
SBUF = en + 48;
}
}
else if(len == 19)//要求主转盘转动相应格数(>=10)
{
if(buf[0] == 'M' && buf[1] == 'a' && buf[2] == 'i' && buf[3] == 'n' && buf[4] == ' ' && buf[5] == 'm' && buf[6] == 'o' && buf[7] == 't' && buf[8] == 'o' && buf[9] == 'r' && buf[10] == ' ' && buf[11] == 'r' && buf[12] == 'u' && buf[13] == 'n' && buf[14] == 's' && buf[15] == ' ' && buf[18] == '.')
{
//Main motor runs命令,比如Main motor runs 22.
if(buf[16] >= '1' && buf[16] <= '9' && buf[17] >= '1' && buf[17] <= '9')//确定发来的步数是数字
{
en = 1;
P27 = 1;
P26 = 1;
P25 = 1;
P24 = 0;//D5亮
SBUF = en + 48;
step_B = 8 * (((buf[16] - 48) * 10 + (buf[17] - 48)));
motorRun = 1;
}
}
}
else if(len == 18)//要求主转盘转动相应格数(<=9)
{
if(buf[0] == 'M' && buf[1] == 'a' && buf[2] == 'i' && buf[3] == 'n' && buf[4] == ' ' && buf[5] == 'm' && buf[6] == 'o' && buf[7] == 't' && buf[8] == 'o' && buf[9] == 'r' && buf[10] == ' ' && buf[11] == 'r' && buf[12] == 'u' && buf[13] == 'n' && buf[14] == 's' && buf[15] == ' ' && buf[17] == '.')
{
//Main motor runs命令,比如Main motor runs 22.
if(buf[16] >= '1' && buf[16] <= '9')//确定发来的步数是数字
{
en = 1;
P27 = 1;
P26 = 1;
P25 = 1;
P24 = 0;//D5亮
SBUF = en + 48;
step_B = 8 * (buf[16] - 48);
motorRun = 1;
}
}
}
else if(len == 9)//要求主转盘转动的方向
{
//Rotate 0/Rotate 1命令
if(buf[0] == 'R' && buf[1] == 'o' && buf[2] == 't' && buf[3] == 'a' && buf[4] == 't' && buf[5] == 'e' && buf[6] == ' ' && buf[8] == '.')
{
if(buf[7] == '0')
{
direction = 0;//顺时针
}
else if(buf[7] == '1')
{
direction = 1;//逆时针
}
}
}
}
}
else if(TI == 1)//如果发送数据完毕
{
TI = 0;
}
}
int main()
{
TMOD = 0x21;
TH0 = 0x3C;
TL0 = 0xB0;
IE = 0x92;
P20 = 0;//给虚拟接口卡P1.0发脉冲,以驱动主转盘步进电机
P22 = 0;//给虚拟接口卡P1.2发脉冲,以驱动针臂步进电机
TR0 = 1;//启动定时器0
//
SCON = 0x50;
TH1 = 0xe6;
TL1 = 0xe6;
TR1 = 1;//启动定时器1
RI = 0;
TI = 0;
IP = 0x10;//提升串口中断的优先级
P21 = 0;//主转盘电机方向, 默认为顺时针
while(1)
{
//如果en = 1开启timer0的中断, 否则将timer0的中断关闭
if(en == 1)
{
IE = 0x92;
}
else if(en == 0)
{
IE = 0x90;
}
}
}
/*
P1.0:转盘电机Pulse信号
P1.1:转盘电机Dir -> P21
P1.2:针臂电机Pulse
P1.3:针臂电机Dir
P0口:输出口,连接虚拟软件中的传感器信号
P0.0:霍尔传感器信号,平时为高电平,有效时为低电平 -> P00
P0.1:针臂上限位信号
P0.2:针臂下限位信号
*/
硬件环境: 8051MCU + Lenovo Y7000(Intel i5 - 8300H @2.30GHz + 16G 2667MB memory) + 虚拟接口卡(学校老师提供).
软件环境: Keil + AutoExampler.exe(学校老师提供) + stc-isp.
-------------------------------------------------------------------------------------------------------------------
虚拟接口卡与8051MCU接口连接情况:
P1.0:转盘电机Pulse信号 -> P20
P1.1:转盘电机Dir -> P21
P1.2:针臂电机Pulse -> P22
P1.3:针臂电机Dir -> P03
P0.1:针臂上限位信号 -> P01
P0.2:针臂下限位信号 -> P02
P0.0: 霍尔传感器信号 ->P00
-------------------------------------------------------------------------------------------------------------------
通信协议处理:
在每条命令后,必须加".E", 这将作为数据包尾处理.
Start.E -> 系统开始工作.
Stop.E -> 在系统已经开始工作的基础上, 待主转盘走完一整格 ,针臂抬至上限位, 系统停止工作.
Reset.E ->针臂抬至上限位处, 主转盘转至0号瓶位置
Rotate 0/1.E ->只有当完整的一次序列动作后, 才可改变主转盘旋转方向
Main motor runs ××.E命令 -> 主转盘根据要求旋转N格, 之后系统停止工作, 等待下一命令的到来.
-------------------------------------------------------------------------------------------------------------------
一次完整的序列动作:
主转盘旋转8格 -> 针臂从上限位移动至下限位 -> 针臂静止一段时间 -> 针臂从下限位移动至上限位
-------------------------------------------------------------------------------------------------------------------
心得体会:
完成这样一个程序的设计并不是件容易的事情. 在程序实现前, 须分别实现各功能模块的功能-----只有这样, 才能在技术上做到畅通无阻. 表面上看一次完整的序列动作十分简单, 但涉及到两个中断源的处理时, 问题便会变得棘手. 在提高串口中断优先级前, 我遇到了很多目前无法合理解释的问题: 这是因为我没有足够的硬件知识储备, 所以对8051MCU的使用仅仅停留在软件层面. 这是不可取的. 希望各位像我一样的同学们多多加强对硬件知识的学习, 做到"软硬皆通". 另外也十分欢迎各位同仁对我的程序进行多角度的测试 并对我的错误进行指正, 感谢各位.
-------------------------------------------------------------------------------------------------------------------
联系方式: QQ: 377539288 好梦成真
-------------------------------------------------------------------------------------------------------------------
备注: 定时器中断处理序列动作的问题可参考我的博文"二自由度自动进样器动作序列实现".
串口接收复杂命令的问题可参考我的博文"8051单片机串口复杂命令接收与解析".