|
程序使用数据包传送指令,有一个固定的协议,只有接收到固定格式的数据包后,才对其中的命令位进行解析,再执行动作指令。虽然协议很简单,但是对于我们这种没有大量数据传输的模型来说,干扰数据正好就是指令的概率几乎不可能发生,发生了就去买彩票吧~首先给出协议格式:
/********************************************************************
协议规定:
包头 类型位 数据位 结束位
0XFF 1 1 0XFF
各命令说明:
类型位 数据位 功能
0X00 0X01 前进
0X00 0X02 后退
0X00 0X03 左转
0X00 0X04 右转
0X00 0X00 停止
0X01 0X01 舵机上
0X01 0X02 舵机下
0X02 0X01 车灯亮
0X02 0X02 车灯灭
0X03 雷达数据 发送雷达数据
***********************************************************************/
这是个超级简单的协议,总共就4位,包头为0XFF,包尾也是,第二位是类型位,0X00代表动作指令,0X01代表舵机,0X02代表车灯,0X03代表启动雷达。
做过串口通信的车友可能会奇怪了:校验位哪去了?是的,其实应该有个校验位,作用就是校验收到的数据是否完整,如果不完整则抛弃,这一点我考虑过,但是我们的通信数据量并不大,加上校验位有点画蛇添足了,所以我就省去了。有兴趣者可以度娘一下“CRC校验”。
那么,上位机怎么发指令呢?以本文中的协议为例,假设我们要让车子前进,则发送类型位:0X00,命令位:0X01,加上包头包尾,就是0XFF0X000X010XFF,所以只需要在上位机那边发送FF0001FF即可,但是这次不能像以前那样发送ASCII格式了,而是以byte格式发送,这样在下位机那边,一个字符占4位,所以串口都是两个两个地接收的,每两个字符就组成一个位,放到相应的数组里,就实现了指令的接收。指令的解析就用switch case语句完成。
下面是源代码,单片机的引脚接线从代码里就可以看出来,大家可以根据自己的车子配置增加类似的指令与解析即可,与之配套的上位机程序正在抽空写。
#include<reg52.h>
#include<math.h>
#include<intrins.h>
#define uchar unsigned char
#define uint unsigned int
uchar Buffer =0; //从串口接收的数据
uint i=0,j,URTAReceivedCount=0,n=1;
uchar flag=0;
uchar data Tempdatatable[4],CommandDatatable[4];//数据包
uchar key_stime_counter,hight_votage=15,timeT_counter; //舵机的变量
bit key_stime_ok;
sbit control_signal=P3^6; //舵机控制信号
sbit StatusLight=P0^0; //状态灯
sbit MainLight=P0^1; //主大灯
sbit Trig = P3^4; //雷达产生脉冲引脚
sbit Echo = P3^5; //接收雷达回波引脚
/********************************************************************
* 名称 : Delay_1ms()
* 功能 : 延时子程序,延时时间为 1ms * x
* 输入 : x (延时一毫秒的个数)
* 输出 : 无
***********************************************************************/
void Delay_1ms(uint i)//1ms延时
{
uchar x,j;
for(j=0;j<i;j++)
for(x=0;x<=148;x++);
}
void TurnOnStatusLight()
{
StatusLight=0;
}
/********************************************************************
* 名称 : Send_Data()
* 功能 : 向上位机传送字符
* 输入 : 无
* 输出 : 无
***********************************************************************/
void Send_Data(uchar data type,uchar data cmd)
{
uchar data Buffer[4];
//构建数据包
uchar *p;
uint Send_Count=0;
p = Buffer;
Buffer[0]=0XFF;
Buffer[1]=type;
Buffer[2]=cmd;
Buffer[3]=0XFF;
while(1)
{
if(*p==0XFF)
{
Send_Count++; //0XFF标志统计位
}
SBUF = *p; //发送
while(!TI) //如果发送完毕,硬件会置位TI,等待发送完毕
{
_nop_();
}
p++;
TI = 0;
if(Send_Count == 2) //当统计到两次出现0XFF,则认为一个数据包发送完毕,跳出循环
{
TI = 0;
break;
}
}
}
/********************************************************************
协议规定:
包头 类型位 数据位 结束位
0XFF 1 1 0XFF
各命令说明:
类型位 数据位 功能
0X00 0X01 前进
0X00 0X02 后退
0X00 0X03 左转
0X00 0X04 右转
0X00 0X00 停止
0X01 0X01 舵机上
0X01 0X02 舵机下
0X02 0X01 车灯亮
0X02 0X02 车灯灭
0X03 雷达数据 发送雷达数据
***********************************************************************/
/********************************************************************
* 名称 : Com_Int()
* 功能 : 串口中断子函数
* 输入 : 无
* 输出 : 无
***********************************************************************/
void Com_Int(void) interrupt 4
{
uchar temp;
ES=0; //关串口中断
RI=0; //软件清除接收中断
temp=SBUF;
if(temp==0XFF && URTAReceivedCount<2)
{
Tempdatatable[0]==0XFF; //包头
URTAReceivedCount++;
}
else
{
Tempdatatable[n]=temp;
n++;
}
if(URTAReceivedCount==2)//包尾
{
Tempdatatable[0]=0XFF;
Tempdatatable[3]=0XFF;
n=1;
URTAReceivedCount=0; //组包完毕
temp="";
//Send_Data(Tempdatatable[1],Tempdatatable[2]); //发送组成的数据包回去
}
CommandDatatable[0]=Tempdatatable[0];
CommandDatatable[1]=Tempdatatable[1];
CommandDatatable[2]=Tempdatatable[2];
CommandDatatable[3]=Tempdatatable[3];
ES=1;//开串口中断
}
/********************************************************************
* 名称 : Com_Init()
* 功能 : 串口初始化,晶振11.0592,波特率9600,使能了串口中断
* 输入 : 无
* 输出 : 无
***********************************************************************/
void Com_Init(void)
{
TMOD = 0x21;
PCON = 0x00;
SCON = 0x50;
TH1 = 0xFd; //设置波特率 9600
TL1 = 0xFd;
TR1 = 1; //启动定时器1
ES = 1; //开串口中断
EA = 1; //开总中断
IT0=0;
EX0=1;
}
void TimerInit()
{
control_signal=0;
EA=1; //开总中断
ET0=1; //定时器0中断允许
TH0 = 0xFF; //定时器装初值
TL0 = 0xA3;
TR0=0;
}
/********************************************************************
* 名称 :Moto_Forward()
* 功能 : 电机1、2启动,都是前进,整车表现为前进。
* 输入 : 无
* 输出 : 无
***********************************************************************/
void Moto_Forward()
{
P1= 0x06;
Delay_1ms(100);
}
/********************************************************************
* 名称 :Moto_Backward()
* 功能 : 电机1、2启动,都是后退,整车表现为后退。
* 输入 : 无
* 输出 : 无
***********************************************************************/
void Moto_Backward()
{
P1= 0x05;
Delay_1ms(100);
}
/********************************************************************
* 名称 :Moto_TurnLeft()
* 功能 : 电机1后退,电机2前进,整车表现为左转。
* 输入 : 无
* 输出 : 无
***********************************************************************/
void Moto_TurnLeft()
{
P1= 0x09;
Delay_1ms(100);
}
/********************************************************************
* 名称 :Moto_TurnRight()
* 功能 : 电机1前进,电机2后退,整车表现为右转。
* 输入 : 无
* 输出 : 无
***********************************************************************/
void Moto_TurnRight()
{
P1= 0x0A;
Delay_1ms(100);
}
/********************************************************************
* 名称 :Moto_Stop()
* 功能 : 电机1停止,电机2停止,整车表现为停止。
* 输入 : 无
* 输出 : 无
***********************************************************************/
void Moto_Stop()
{
P1= 0x00;
Delay_1ms(100);
}
/********************************************************************
* 名称 :SteerEngineTurningUp()
* 功能 : 舵机向上转。
* 输入 : 无
* 输出 : 无
***********************************************************************/
void SteerEngineTurningUp()
{
ET0=1;
TR0=1;
Delay_1ms(40);
hight_votage-=1;
if(hight_votage<5)
hight_votage=5;
}
/********************************************************************
* 名称 :SteerEngineTurningDown()
* 功能 : 舵机向下转。
* 输入 : 无
* 输出 : 无
***********************************************************************/
void SteerEngineTurningDown()
{
ET0=1;
TR0=1;
Delay_1ms(40);
hight_votage+=1;
if(hight_votage>20)
hight_votage=20;
}
/********************************************************************
* 名称 : SteerEngine()
* 功能 : 舵机PWM中断
* 输入 : 无
* 输出 : 无
***********************************************************************/
void SteerEngine() interrupt 1
{
TH0=0xFF;
TL0=0xA3;
if (++key_stime_counter>=200)
{
key_stime_counter=0;
control_signal=1;
key_stime_ok = 1; // 20ms到
timeT_counter=0;
}
if (key_stime_ok&&(++timeT_counter>=hight_votage))
{
key_stime_ok=0;
timeT_counter=0;
control_signal=0; // hight_votage*0.1ms到
}
}
void main()
{
MainLight=0;
Delay_1ms(200);
Com_Init();//串口初始化
TimerInit();//舵机初始化
while(1)
{
if(CommandDatatable[0]==0XFF && CommandDatatable[3]==0XFF)
{
switch (CommandDatatable[1]) //根据键值不同,执行不同的内容
{
case 0X00: //类型位0X00,表明是控制数据包,进入控制数据case
switch(CommandDatatable[2]) //根据数据位的值来进行选择执行不同的动作
{
case 0X00:
Moto_Stop();
break;
case 0X01:
Moto_Forward();
break;
case 0X02:
Moto_Backward();
break;
case 0X03:
Moto_TurnLeft();
break;
case 0X04:
Moto_TurnRight();
break;
}
break;
case 0X01: //类型位0X01,表明是舵机数据包,进入舵机case
switch(CommandDatatable[2])
{
case 0X01:
SteerEngineTurningUp();
break;
case 0X02:
SteerEngineTurningDown();
break;
}
break;
case 0X02: //类型位0X02,表明是大灯数据包,进入大灯case
switch(CommandDatatable[2])
{
case 0X01:
MainLight=1;
break;
case 0X02:
MainLight=0;
break;
}
break;
default : TR0=0;TR2=0;
break;
}
}
}
}
/********************************************************************
* 名称 : extern_int0()
* 功能 : 外部中断0,红外中断函数
* 输入 : 无
* 输出 : 无
***********************************************************************/
void extern_int0(void) interrupt 0 using 0
{
EX0=0;
Moto_TurnLeft();
Buffer=0;
EX0=1;
IE0=0;
}
|
|