|
发表于 2009-4-1 10:05:08
|
显示全部楼层
fantaq :
看了你的叙述,
1. 我是在串口中断里用timer做了个timeout, 就是收完一帧数据之后就会timeout, 这样就可以判断一帧收完了。atmel的arm上面 uart硬件有这个功能,很好用,想在avr上实现一下。
2. 每帧数据想算一下校验和,还想进一步地判断一下本机地址,如果不是立即丢掉。
这样中断里的动作就多了。
3. 说是中断里的一些处理,实际上就几行而已,居然就用了那么多寄存器。
如果不放在中端里判断,怕是没等到主程序去处理,AVR那点可怜的ram已经装满了。
本以为串口是个低速设备,8位的怎么也能处理得来,结果还有点难度,呵呵。
看了你的叙述1,好像你借用了MODBUS协议的规则,其实MODBUS协议接收到一个字符之后,就开启定时器开始计时,当3.5个字符的静止时间内还未接收到“下一个字符”,就认为已经接收到完整的一帧数据。
我的所有数据帧分析全部放在主程序中,编写了MODBUS协议2年来,从没有出现过问题,从波特率1200-115200,PC机定时每隔50ms
不停的发送,通讯质量都非常好。
超时可以在定时中断服务程序中判断,每来一次定时中断,就将超时时间-1,当超时时间减到0,就认为已经接收到一帧完整的数据,然后再判断接收指针是否合法。
下面是我的接收中断服务程序:
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 可靠地判断帧结束,防止通信停滞
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 利用单独的软件定时器,来判断一帧接收报文结束,可以防止若报文接收不完整,该帧通信任务无法结束而影响下一帧的接收。
//由于一帧报文中字节与字节之间的时间间隔和帧与帧之间的时间间隔相比要小得多,因此每当接收一个新字节,就启动软件定时器
//开始计时,定时器的时间设定为帧与帧的最小时间间隔。波特率不同,该时间间隔也不同。
// (1).若不到预定的时间内又接收到下一个字节,则说明一帧报文未结束,定时器重新计时;若定时器顺利计数到预定时间,就
//会触发相应的中断号,在该定时器中断子程序中设定帧结束标志字节,表明一帧报文接收完毕。
// (2).当主程序内检测到一帧报文接收完毕后,会通过核查从方地址及循环冗余校验字节是否正确来判断该帧的有效性。若确定
//接收到的是一帧发送给已方的正确报文,则会根据报文内的功能码对该帧命令进行相应的处理,并准备发送帧。
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma interrupt_handler USART1_RI_ISR:iv_USART1_RX
void USART1_RI_ISR(void)
{
INT8U ch;
INT8U status;
status = UCSR1A;
ch = UDR1;
if (USART1_receCount < (MSCOMM_BUFFER_LENGTH)
USART1_mscomm_buffer[USART1_receCount++] = ch;
USART1_receTimeOut = 2;//设置超时计时
}
下面是我的发送中断服务程序:
#pragma interrupt_handler USART1_UDRE_ISR:iv_USART1_UDRE//“数据空中断”发送数据
void USART1_UDRE_ISR(void)
{
UDR1 = USART1_send_buffer[USART1_sendPosi++];
if (USART1_sendPosi >= USART1_sendCount)
{
UCSR1B &= ~BIT(5);
UCSR1B |= BIT(6);
}
}
//发送完成中断发送最后一个字节(因为是RS485,因此必须确保最后一个发送到PC机后,再将总线置为接收状态)
#pragma interrupt_handler USART1_TX_ISR:iv_USART1_TX
void USART1_TX_ISR(void)
{
USART1_checkoutError = 0;
USART1_receCount = 0;
USART1_RECIVE();
} |
|