单片机与上位机通信协议的制定
单片机和上位机的串口通信协议分为上行协议和下行协议,要分别制定!
上行协议,即由 单片机向上位机发送数据。
下行协议,即由上位机向单片机发送数据。
而通信协议又要分固定长度和不定长度两种
本文所介绍的协议属于简单的固定字长的通信协议!
下行协议由四个字节构成
起始字
命令字 ORD
值 VAL
结束字
附注
PRE
END
1byte
1byte
1byte
1byte
BBH
AAH(ORD_SATA)
单片机状态转换命令标识
01H
EEH
模式 1:空闲
02H
EEH
模式 2:温度采集
03H
EEH
模式 3:温度采集向上位机传送采集值
04H
EEH
模式 4:PWM测试
BBH
DDH(ORD_PWM)
value
EEH
PWM占空比值修改指令
BBH
FFH(ORD_TEM)
value
EEH
目标温度控制指令
上表是简单的上位机对 单片机的控制指令
下述函数是 C#中封装的串口通信类中的发送函数的封装
public void SerSendCommu(byte orderDef, byte data)//参
数 1 为命令字,参数二为要发送的数
//据,需要时可直接
调用
{
Byte[] BSendTemp = new Byte[SEND_LENTH];
BSendTemp[0] = PRE;
BSendTemp[1] = orderDef;
BSendTemp[2] = data;
BSendTemp[3] = END;
this.serialPort1.Write(BSendTemp, 0,
SEND_LENTH);
}
下位机中用中断方式接收字符,本文用的是 GCC语言,下面是串口接收数据中断
ISR(USART_RXC_vect)//串口接收中断
{
unsigned char status,data;
status= UCSRA;
//**首先读取
UCSRA的值,再读取
UDR值,顺序不
能颠倒,否则读取UDR后的 UCSRA的
值即会改变 **
data = UDR;
if(!Uart_RecvFlag)//判断缓存中的数据是否读完,读完则接收指令
{
if((status&((1<
{
rx_buffer[rx_counter]=data;
rx_counter++;
switch(rx_counter)
{
case 1:
if(data!=USART_BEGIN_STX)
rx_counter=0;
break;
case 4:
rx_counter=0;
if(data==USART_END_STX)
Uart_RecvFlag=1;
break;
}
}
}
}
在单片机主循环程序的最前部分进行指令译码
if(Uart_RecvFlag)//接收到命令
{
switch(rx_buffer[1])
{
case 0xAA://单片机状态命令控制;
ucWorkStatue=rx_buffer[2];//指令数据
break;
case 0xDD://PWM 值修改指令
OCR2=rx_buffer[2];
break;
case 0xFF://初始温度设定
break;
}
Uart_RecvFlag=0;
}
随后进行执行指令
switch(ucWorkStatue)
{
case 1://空闲模式
break;
case 2://测量模式,但不输出
break;
case 3://测量模式,由串口输出
break;
case 4://PWM 输出测试
break;
default:
break;
}
这样就可以利用串口对 单片机进行在线命令控制了;
上行协议的制定!
和下行协议基本一致!
在 AVR单片机程序中定义了串口通信输出缓冲区, 缓冲区的字长正好为协议的长度 ;
串口发送缓冲区变量声明
volatile unsigned char tx_buffer[TX_BUFFER_SIZE];//定义串口发送缓冲区
volatile unsigned char
tx_wr_index=0,tx_rd_index=0,tx_counter=0;//rx_wr_index写指
针 ,rx_rd_index 读指针 ,rx_counter 缓冲区数据个数
//USART发送函数
void USART_Transmit(unsigned char data)//发送数据函数
{
while(tx_counter==TX_BUFFER_SIZE);// 输出缓冲区满,等待 asm("cli");
if(tx_counter||((UCSRA &