经过各种折腾,各种实验,总结出一个结论:中断代码必须要尽可能的简短,要尽快的返回,否则的话,影响下一次中断,任何中断都应该这样,串口中断尤甚!!比如,要做一个复杂点的通信协议,如果把协议解析放到中断里来,代码倒是容易写了:
onRecvByte() interrupt 4 using 1 { if( RI ) { UCHAR rec = SBUF; // 进入又穷自动状态机解析收到的数据 parseCommand( rec ); // 耗时较长 } } 但是,由于parseCommand耗时较长,导致中断未处理完,下次收不到数据了!!因为上位机发来的数据很快,两次数据发送时间间隔小于中断返回的时间,所以就会丢失数据。。
研究了一阵子网上的代码,没找到什么很实用的,尤其是对缓冲区的使用,真正的大神也不屑于写这些,新手文章倒是不少,没什么参考价值,还是自己动手吧。下面是成果(经测试,最高速率,发送几K字节无一丢失),记录在此以备查:
/** * serial.h * CopyLeft 2017.3 HeXu **/ #ifndef HX_SERIAL #define HX_SERIAL 1 #include "../myInc/hxcomm.h" #define READ_BUFFER_SIZE 32 // 定义缓冲区大小 typedef void ( * HXCALLBACK_ONRECV ) ( BYTE uData ); // 回调函数指针 // 初始化 void hxInitSerial(); // 发送一串数据;pDatas指向一个缓冲区,uCount指明尺寸 void hxSendBytes( LPBYTE pDatas, UINT uCount ); // 发送一个字节 void hxSendByte( BYTE uData ); // 放到循环里,检查是否收到数据,如果收到数据,就会调用回调函数。 // 如果没有数据,或者数据正常,返回true,如果缓冲区满了,返回false BOOL hxLoopReadOneByte( HXCALLBACK_ONRECV lpfnCallBack ); #endif
/** * serial.c * Copy Left 2017.3 HeXu **/ #include "hxserial.h" BYTE g_zBuff[ READ_BUFFER_SIZE ]; char g_nReadIndex; char g_nWriteIndex; bit g_bBusy; bit g_bError; void hxInitSerial() { g_nReadIndex = 0; g_nWriteIndex = 0; g_bBusy = 0; g_bError = 0; SCON = 0x50; //8位数据,可变波特率 AUXR |= 0x01; //串口1选择定时器2为波特率发生器 AUXR |= 0x04; //定时器2时钟为Fosc,即1T T2L = 0xCC; //设定定时初值 T2H = 0xFF; //设定定时初值 AUXR |= 0x10; //启动定时器2 ES = 1; } void hxSendByte( BYTE uData ) { while( g_bBusy ); // 等待上次发送完成 g_bBusy = 1; // 标志正在发送 SBUF = uData; } void hxSendBytes( LPBYTE pDatas, UINT uCount ) { while( uCount > 0 ) { hxSendByte( * pDatas ++ ); --uCount; } } void hxOnSerialRecv() interrupt 4 using 1 { char tIndex; if( RI ) { RI = 0; tIndex = ( g_nWriteIndex + 1 ) % READ_BUFFER_SIZE; if( tIndex == g_nReadIndex ) // 如果缓冲区已满 { g_bError = 1; ES = 0; } else { g_zBuff[ g_nWriteIndex ] = SBUF; g_nWriteIndex = tIndex; } } if( TI ) { TI = 0; g_bBusy = 0; // 标志发送已完成 } } BOOL hxLoopReadOneByte( HXCALLBACK_ONRECV lpfnCallBack ) { if( g_bError ) return FALSE; else { while( g_nReadIndex != g_nWriteIndex ) // 一直处理收到的数据,直到缓冲区为空。 { lpfnCallBack( g_zBuff[ g_nReadIndex ] ); g_nReadIndex = ( g_nReadIndex + 1 ) % READ_BUFFER_SIZE; } return TRUE; } }
测试程序:
#include "hxserial.h" #include <intrins.h> void hxRecv( BYTE uData ) { hxSendByte( uData ); } void main() { hxInitSerial(); EA = 1; while( 1 ) { if( ! hxLoopReadOneByte( hxRecv )) // 如果对方发送数据太快(缓冲区已满) { ES = 0; _nop_(); _nop_(); hxInitSerial(); hxSendBytes( "哥们,你发送太快了。", 20 ); } } }