请教马潮老师:从站发送一帧数据包(至少14个字节),为什么我的模块99.999%的时间接收正
问题如下:主站A和从站B之间通过标准的MODBUS协议进行通讯。
我目前要做的工作是:
在主站和从站之间强行接入一个“自己开发的MODBUS设备X”, 称之为中转站。(由于特殊原因,主站的寄存器地址和从站的寄存器“不是一一对应的”,因此中转站主要的工作是翻译寄存器地址)
(a)、主站到从站的请求包,必须通过设备X(中转站)翻译后,再送给(从站)
(b)、从站到主站的应答包,必须通过设备X(中转站)翻译后,再送给(主站)
主站 自己开发的中转站 从站
|------------------ 翻译 ---------------------- ----------------------
| 设备A | ---------> | 设备X |---------------> | 设备B |
| | <----------- | |<-------------- | |
|------------------ 翻译 ----------------------- ----------------------- 数据流向示意图
例如:主站上0X寄存器数量 = 5 , 地址为 0:0012,0:0013,0:0014,0:0015,0:0016
从站上0X寄存器数量 = 5 , 地址为 0:0001,0:0002,0:0003,0:0004,0:0005
主站(组态软件)读取0X寄存器的请求包为:
1 1 000B 0005 CRCHiCRCLo
设备地址功能码 起始地址=12 寄存器数量 CRC16
中转站接收到上述请求包后, 翻译成如下的包,然后发送给从站
1 1 0000 0005 CRCHiCRCLo
设备地址功能码 起始地址=1 寄存器数量 CRC16
程序比较简单:
使用外部晶体振荡器=11.0592MHZ,波特率=38400,主站每隔150ms给中转站发送一帧请求包。
我的模块的接收中断和发送中断采用中断+缓冲区方式,汇编代码非常短。
溢出中断用于判断是否接收完成一帧数据包,只有两条赋值语句,汇编代码也非常短。
主程序只是用于处理接收到的数据包,没有其它代码。
(1)、两个溢出中断 T0,T2
进入串口0中断服务程序后,开启T0;进入T0溢出中断服务程序后,关闭T0。
进入串口1中断服务程序后,开启T2;进入T2溢出中断服务程序后,关闭T2。
注:MODBUS 协议依据3.5字符静止时间,判断是否一帧数据包接收完成。即进入串口接收中断后,开启定时器,当定时器溢出后,认为一帧数据包接收完成。
(2)、两个接收中断
采用中断+缓存方式接收数据。
(3)、两个发送中断
采用中断+缓存方式发送数据。
(4)、两个数据包解析函数
(5)、两个接收计数器
USART0_RECV = 串口0接收指针
USART1_RECV = 串口1接收指针
现在存在的问题是:
中转站接收“主站”的请求包完全正确。
中转站接收“从站”的应答包(至少14个字节),99.999%的时间接收正常,只是0.001%的时候接收错误出现如下问题,而且原因是固定的。
错误原因: 进入T2溢出中断后,USART1_RECV = 1; 说明这个应答包“中转站”只接收了1个字节。
这种原因,如何解决? 经过连续长时间运行,发现错误是固定的,即:从站给中转站发送的应答包(至少14个字节),中转站只接收到1个字节后,T2就溢出了。
程序比较简单:
(1)、两个溢出中断 T0,T2
进入串口0中断服务程序后,开启T0;进入T0溢出中断服务程序后,关闭T0。
进入串口1中断服务程序后,开启T2;进入T2溢出中断服务程序后,关闭T2。
(2)、两个接收中断
采用中断+缓存方式接收数据。
(3)、两个发送中断
采用中断+缓存方式发送数据。
(4)、两个数据包解析函数
(5)、两个接收计数器
USART0_RECV = 串口0接收指针
USART1_RECV = 串口1接收指针
单片机上面有两个指示灯LED1和LED2,我用来跟踪错误原因:
LED1 亮 LED2 熄灭 ---->表示中转站接收从站数据包时,产生错误,USART1_RECV =1 表示中转站应答包只接收到1个字节。
LED1 熄灭LED2 亮 ---->表示中转站接收主站数据包时,产生错误,表示中转站应答包只接收到2-4个字节
http://cache.amobbs.com/bbs_upload782111/files_32/ourdev_577123.JPG
(原文件名:未命名.JPG)
#define TIMER0_START()(TCCR0 = (0 << WGM00) | (0 << WGM01) | (1 << CS02) | (1 << CS01) | (0 << CS00)) // T0启动
#define TIMER0_STOP() (TCCR0 = 0x00) // T0停止
#define TIMER2_START()(TCCR2 = (0 << WGM20) | (0 << WGM21) | (1 << CS22) | (0 << CS21) | (0 << CS20)) // T2启动
#define TIMER2_STOP() (TCCR2 = 0x00) //T2停止
//====================================================================================================
//TIMER0 initialize - prescale:256
// WGM: Normal
// desired value: 1.25mSec
// actual value:1.250mSec (0.0%)
//====================================================================================================
void TIMER0_Init(void)
{
TCCR0 = 0x00;
ASSR= 0x00;
TCNT0 = 0xCA;
OCR0 = 0x35;
TIMSK |= (1 << TOIE0); // 允许T0溢出中断
}
//====================================================================================================
//TIMER2 initialize - prescale:256
// WGM: Normal
// desired value: 1.25mSec
// actual value:1.250mSec (0.0%)
//====================================================================================================
void TIMER2_Init(void)
{
TCCR2 = 0x00;
ASSR= 0x00;
TCNT2 = 0xCA;
OCR0 = 0x35;
TIMSK |= (1 << TOIE2); // 允许T2溢出中断
}
//T0溢出中断服务程序
#pragma interrupt_handler timer0_ovf_isr:iv_TIM0_OVF
void timer0_ovf_isr(void)
{
TIMER0_STOP(); // T0停止
USART0_OK_mark = TRUE;// 3.5字符静止时间到时,置中转站已经接收到“主站”一帧完整的请求包
}
//T2溢出中断服务程序
#pragma interrupt_handler timer2_ovf_isr:iv_TIM2_OVF
void timer2_ovf_isr(void)
{
TIMER2_STOP(); // T2停止
USART1_OK_mark = TRUE; // 3.5字符静止时间到时,置中转站已经接收到“从站”一帧完整的应答包
}
//串口0接收中断 (串口0接收中断---中转站接收主站的请求包)
#pragma interrupt_handler USART0_RI_ISR:iv_USART0_RX
void USART0_RI_ISR(void)
{
INT8U ch;
ch = UDR0;
if (USART0_receCount < 255)
USART0_mscomm_buffer = ch;
TCNT0 = 0xCA; // 开启T0溢出中断
TIMER0_START();
}
//串口1接收中断 (串口10接收中断---中转站接收从站的应答包)
#pragma interrupt_handler USART1_RI_ISR:iv_USART1_RX
void USART1_RI_ISR(void)
{
INT8U ch;
ch = UDR1;
if (USART1_receCount < 255)
USART1_mscomm_buffer = ch;
TCNT2 = 0xCA; // 开启T2溢出中断
TIMER2_START();
}
void main(void)
{
CLI();
PORT_Init();
TIMER0_Init();
TIMER2_Init();
USART0_Init();
USART1_Init();
SEI();
while (1)
{
if (USART0_OK_mark) // 如果T0溢出,则认为中转站已经完整的接收了一帧“主站”的请求包
{
USART0_OK_mark = FALSE;
USART0_Modbus_Analyze();
}
if (USART1_OK_mark) // 如果T2溢出,则认为中转站已经完整的接收了一帧“从站”的应答包
{
USART1_OK_mark = FALSE;
USART1_Modbus_Analyze();
}
}
} 问题已经解决。 怎么解决的?原因在哪里?让其他人也学习下。 1、程序代码没有任何问题。
2、问题出在硬件上
问题出在“中转站”模块上没有启用终端电阻。
启用终端电阻后,通讯百分百完全正常。
|------------------ RS232 ---------------------- RS422 ----------------------
| 设备A | ---------> | 设备X |---------------> | 设备B |
| | <----------- | |<-------------- | |
|------------------ ----------------------- ---------------------- 通讯模式 为何不移植FREE MODBUS协议呢?很成熟的代码啊 谢谢楼主分享....
页:
[1]