单片机与上位机通信协议的制定
单片机和上位机的串口通信协议分为上行协议和下行协议,要分别制定!
上行协议,即由单片机向上位机发送数据。
下行协议,即由上位机向单片机发送数据。
而通信协议又要分固定长度和不定长度两种
本文所介绍的协议属于简单的固定字长的通信协议!
下行协议由四个字节构成
上表是简单的上位机对单片机的控制指令
下述函数是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 & DATA_REGISTER_EMPTY)==0)) { tx_buffer[tx_wr_index]=data; if(++tx_wr_index==TX_BUFFER_SIZE) tx_wr_index=0; ++tx_counter; } else UDR = data; asm("sei"); } //发送中断服务程序 ISR(USART_TXC_vect)//USART发送数据中断 { if(tx_counter) { --tx_counter; UDR=tx_buffer[tx_rd_index]; if(++tx_rd_index==TX_BUFFER_SIZE) { tx_rd_index=0; } } } 在C#编写的上位机中,利用串口接收事件响应方法定义 serialPort1.ReceivedBytesThreshold = RECEIVE_LENTH; 在时间响应事件中调用协议分析处理函数serialPortCaculate()来分析协议 private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e) {