请大家讨论一下串口通讯的架构问题,以提高通讯效率和可
<br />下面是我一个项目里的通讯程序,没有用环形缓冲区,可能会出现没及时处理的命令被覆盖的问题,如果我改用环形缓冲区的话,怎样存取和判断处理比较方便呢,下面是通讯协议:<br /><B>通讯格式</B>:<br /><table class=ubb cellspacing=0><TR><td class=ubb><br />帧起始苻</td><td class=ubb><br />帧长度</td><td class=ubb><br />命令字</td><td class=ubb><br />数据项</td><td class=ubb><br /> </td></TR><TR><td class=ubb><br />68H<br />单字节</td><td class=ubb><br />单字节</td><td class=ubb><br />xxH<br />单字节</td><td class=ubb><br />DATA<br />不定</td><td class=ubb><br /> </td></TR></table><br /> <br /> <br />因为每帧命令的字节数不一样,中断里网环形缓冲区里存的时候没什么问题,但通讯处理函数从缓冲区里取命令的时候就会有一定问题,指针不容易维护,而且一旦某次读出错,那以后的全部都错,我只是说一下我这个程序容易出现的问题,请大家在这里谈论一个通用性强的,结构清晰的通讯程序架构<br />/*------------------------------------------------------------------------------------------------**<br />****************************************************************************************************<br />**------------------------------------------------------------------------------------------------**<br />** com.c 通讯相关程序<br />**------------------------------------------------------------------------------------------------**<br />****************************************************************************************************<br />**------------------------------------------------------------------------------------------------*/<br /><br />//----------------------------<br />//通讯处理<br />//----------------------------<br />void com_cl() <br />{ <br /> if(recv_end!=1)return;<br /> recv_end=0;<br /> switch(recv_buf)<br /> {<br /><br />/*------------------------------------------------------------------------------------------------*/<br /> //调试命令区<br /> case 0xD0: <br /> if(recv_buf==0x00)<br /> {<br /> getdatm();<br /> send0();<br /> }<br /> else<br /> {<br /> switch(recv_buf&0xc0)<br /> { case 0x00:get7028(recv_buf,recv_buf); <br /> send0(); break;<br /> case 0x80:write7028(recv_buf,recv_buf,recv_buf+3);break;<br /> case 0xc0: break;<br /> default: break;<br /> }<br /> }<br /> break;<br /> case 0xD1:<br /> switch(recv_buf&0x80)<br /> {<br /> case 0x00:geteeprom();<br /> send0(); break;<br /> case 0x80:writeeeprom(recv_buf,recv_buf+3,recv_buf&0x7f);break;<br /> default : break;<br /> }<br /> break;<br /> case 0xD2:<br /> dysdz=bcdtoint(recv_buf+1);<br /> <br /> <br /> break;<br /> case 0xD3:<br /> tiaoya();<br /> break;<br /> case 0xD8:<br /> if(recv_buf==0x88)<br /> reset485();<br /> else<br /> {<br /> send1_add(recv_buf);<br /> Delay(1000);<br /> xxzz();<br /> send1(); <br /> }<br /> break;<br /> <br />/*------------------------------------------------------------------------------------------------*/<br /> //电源控制命令区<br /> case 0xA0: //复位电源<br /> reset_dy();<br /> break;<br /> <br /> case 0xA1: //设置电压电流输入输出状态 <br /> <br /> if(recv_buf==0)<br /> kg_flag=0x77;<br /> else<br /> {<br /> if(recv_buf==1)<br /> kg_flag=1;<br /> else if(recv_buf==2)<br /> kg_flag=2;<br /> else if(recv_buf==3)<br /> kg_flag=4;<br /> else if(recv_buf==4)<br /> kg_flag=16;<br /> else if(recv_buf==5)<br /> kg_flag=32;<br /> else if(recv_buf==6)<br /> kg_flag=64;<br /> <br /> }<br /> if(recv_buf==1)<br /> {<br /> u_out(kg_flag,u_fd);<br /> i_out();<br /> }<br /> else if(recv_buf==0)<br /> {<br /> u_shut();<br /> i_shut();<br /> }<br /> <br /> break;<br /> case 0xA2: //设置相位 <br /> xw=recv_buf;<br /> xw=recv_buf;<br /> xw=recv_buf;<br /> break; <br /> <br /> case 0xA3: //设置电压档位和幅度 <br /> u_dw=recv_buf;<br /> u_fd=recv_buf;<br /> u_fd=recv_buf; <br /> dydw();<br /> break;<br /> case 0xA4: //设置电流档位和幅度 <br /> i_dw=recv_buf;<br /> i_fd=recv_buf;<br /> i_fd=recv_buf;<br /> dldw();<br /> break;<br /> <br /> case 0xA5: //对称度调整 <br /> dcd_adjust();<br /> break;<br /> case 0xA6: //电压粗调<br /> break;<br /> <br /> case 0xA7: //电流粗调<br /> break;<br /> case 0xA8: //电压细调<br /> break;<br /> <br /> case 0xA9: //电流细调<br /> break;<br /><br />/*------------------------------------------------------------------------------------------------*/<br /> //信息索取命令区<br /> case 0xB0: //索取低压信息<br /> getdatm();<br /> send0();<br /> break;<br /> case 0xB4: //索取通讯校验状态<br /> break;<br />/*------------------------------------------------------------------------------------------------*/<br /> //返回信息区<br /> case 0xB1: //返回高压信息<br /> <br /> switch(recv_buf)<br /> {<br /> case 1:u_bz=ctof(recv_buf+3);<br /> u_bz=ctof(recv_buf+7);<br /> u_bz=ctof(recv_buf+11);<br /> u_bz_new=1;<br /> break;<br /> case 2:i_bz=ctof(recv_buf+3);<br /> i_bz=ctof(recv_buf+7);<br /> i_bz=ctof(recv_buf+11);<br /> i_bz_new=1;<br /> break;<br /> case 3:xw_bz=ctof(recv_buf+3);<br /> xw_bz=ctof(recv_buf+7);<br /> xw_bz=ctof(recv_buf+11);<br /> xw_bz=ctof(recv_buf+15);<br /> xw_bz=ctof(recv_buf+19);<br /> xw_bz_new=1;<br /> break;<br /> default:<br /> break; <br /> }<br /><br /> break;<br />/*------------------------------------------------------------------------------------------------*/<br /> //辅助命令区<br /> case 0xC4: //信息重新发送命令<br /> send0();<br /> break;<br /> default : break;<br /> }<br />}<br />//----------------------------<br />//初始化串口及定时器<br />//----------------------------<br />void init(void)<br />{<br />TMOD=0x21;<br />TH0=0x00;<br />TL0=0x00;<br />TH1=0xfd;<br />TL1=0xfd;<br />PCON=0x00;<br />TR1=1;<br />SCON0=0x50;<br />SCON1=0xd0; //UART1工作于方式3,用于多机通讯<br />ES0=1;<br />ES1=1;<br />ET0=1;<br />EA=1;<br />}<br />//----------------------------<br />//串口0发送<br />//----------------------------<br />void send0()<br />{ <br /> uchar i;<br /> EA=0;<br /> SBUF0=0x68;<br /> while(TI==0);TI=0; <br /> for(i=0;i<=send0_buf;i++)<br /> {<br /> SBUF0=send0_buf;<br /> while(TI==0);TI=0;<br /> }<br /> EA=1;<br />}<br />//----------------------------<br />//串口0中断<br />//----------------------------<br /> void int232_0(void) interrupt 4 using 0<br /> {<br /> uchar a;<br /> ES0=0;<br /> if(RI==1)<br /> {<br /> RI=0;<br /> a=SBUF0;<br /> if(recv_long==0)<br /> {<br /> if(a==0x68)<br /> recv_start=1;<br /> else<br /> if(recv_start==1)recv_long=a;<br /> }<br /> else<br /> {<br /> if(recv_start==1)<br /> {<br /> recv_buf=a;<br /> recv_zz=recv_zz+1;<br /> recv_long=recv_long-1;<br /> if(recv_long==0)<br /> {<br /> recv_end=1;<br /> recv_zz=0;<br /> recv_start=0;<br /> }<br /> }<br /> else<br /> recv_long=recv_long-1;<br /> } <br /> } <br /> ES0=1;<br />}Re
帧结构中可以考虑加一个校验码(简单和校验)。<br /><br />我一般在接收中断中检查帧结构。检查正确的帧数据,才会放到环行缓冲区中。<br />这样,保证环行缓冲中的数据总是有效数据。但写入环行缓冲区前,要检查是否会溢出。<br />从环行缓冲读数据时,只要程序正确,就不会出现“一旦某次读出错,那以后的全部都错”的问题。通讯
大家看一下这个问题怎么解决:<br /> 我的通讯程序中断接收和通讯处理是完全分开的,中断处理函数是在主程序里循环检测的,检测到接收完标志,就取缓冲区的数据进行解析。这样的话受到的命令是依次处理的,就是一次命令处理完后,退出通讯处理函数,再重新检测是否受到数据。这样会出现一个问题:<br /><br />mcu已经接收到一条命令,通讯处理函数判断后进入一函数执行(此函数执行时间较长),这时如果再接收一条命令,接收中断结束后还会回到尚未执行完的函数中,而不会去执行通讯处理函数,从而不会及时的执行新命令,怎样才能终止尚未处理完的函数来执行新命令啊是不是可以考虑开辟新任务来处理哦
是不是可以考虑开辟新任务来处理哦你的问题应该这样描述:
从串口上接收命令,<br />但每个命令的优先级不同,<br /><br />需要接到高优先级命令时终止未执行完的低优先级任务;<br /><br />给你一个参考:将低优先级的任务A分段,每个段作一个状态,<br /><br />遇到高优先级的命令时直接将A的状态设为结束,从而在下一循环结束掉该任务;<br /><br />有一个缺点:不能够及时响应,但可以做到接收命令的xx ms内结束任务硬件流控
从可靠性来讲,最好加上硬件流量控制,如RTS/CTS;<br />因为使用大的缓冲区,也有溢出的可能。walnutcy的意思是不是这样
在中断里设一个标志,如果高优先级的中断来了指标值,然后在处理函数里检测这个标志,检测到直接退出?这样只要函数循环时间不要太长就可以了。我用的是51,没用操作系统
我用的是51,没用操作系统51就老老实实一帧一帧处理好了!
环的话,那点地方还不够,除非帧小。51跟用环形缓冲区有什么必然联系么
用环只是为了不让命令丢失,因为有些处理函数需要时间比较长,而我的片外存储器也足够用环形队列比较好用。两个函数一个写一个读轻松搞定
页:
[1]