经过各种折腾,各种实验,总结出一个结论:中断代码必须要尽可能的简短,要尽快的返回,否则的话,影响下一次中断,任何中断都应该这样,串口中断尤甚!!比如,要做一个复杂点的通信协议,如果把协议解析放到中断里来,代码倒是容易写了:
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 );
}
}
}