MODBUS 中转模块的通讯问题(主站发送请求包给“中转”模块,中转模块翻译后送给从站)。
问题如下:主站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
T0中断用做“中转站”接收到“主站”请求包的3.5字符溢出中断,中断时间=1.25ms(1.25ms对应38400波特率的3.5字符静止时间足够了)
T2中断用做“中转站”接收到“从站”应答包的3.5字符溢出中断,中断时间=1.25ms
(2)、串口0
中转站的“串口0”接收“主站”的请求包
中转站的“串口0”发送“从站”的应答包
串口0 发送采用“空中断”方式,接收采用“中断”方式
当进入串口0接收中断后,开启T0中断,当T0溢出后,“中转接”收到“主站”一帧完整的请求数据包。
(3)、串口1
中转站的“串口1”发送“中断站”接收到的请求包
中转站的“串口1”接收“从站”发送的应答包
串口1 发送采用“空中断”方式,接收采用“中断”方式
当进入串口1接收中断后,开启T2中断,当T2溢出后,,“中转接”收到“从站”一帧完整的应答数据包。
现在存在的问题是:
中转站接收“从站”的应答包偶而有问题,大约30分钟左右才会出现。 即“中转站”偶而接收到1个字节后,即进入T2溢出中断。
为什么会出现:中转站接收从站返回的应答包导致T2溢出后,接收计数 = 1 呢?
(1)、确实是从站发送了一个字节(这种情况不可能,因为主站和从站直连时,当设置主站和从站的寄存器地址一一对应时,没有任何问题)
(2)、从站正确给主转站返回的应答包,但是由于其它原因,导致中转站只接收了一个字节后,T2就溢出了。
这种原因,如何解决?
(3)、其它原因
这种原因,如何解决?
注:中转站接收“主站”的请求包完全正确。 这个问题很不好查。 需要注意的地方
1.中转站接收数据。把接收到的数据一个一个移到MCU中处理也需要时间。 这些是硬件必须使用的时间。需要考虑。
3.5T只是推荐值。建议使用5ms左右。
150ms一帧建议增大到500ms
注意连接线的距离。 主站是一台PC机上的组态监控软件,上面显示的有:请求次数和应答次数和超时次数。
请求次数 = 应答次数 + 超时次数
在程序中跟踪超时时产生的原因,代码片段如下:
经过长期运行发现,当上位机组态监控软件发生超时时,是因为中转站接收的当前应答包的个数=1时,产生了T2溢出中断。即这个应答包中转站只收到了1个字节。(面板上有两个指示灯用于跟踪错误)
http://cache.amobbs.com/bbs_upload782111/files_32/ourdev_576508.JPG
(原文件名:未命名.JPG)
#define TIMER0_START()(TCCR0 = (0 << WGM00) | (0 << WGM01) | (1 << CS02) | (1 << CS01) | (0 << CS00)) // 256 分频
#define TIMER0_STOP() (TCCR0 = 0x00)
#define TIMER2_START()(TCCR2 = (0 << WGM20) | (0 << WGM21) | (1 << CS22) | (0 << CS21) | (0 << CS20)) // 256 分频
#define TIMER2_STOP() (TCCR2 = 0x00)
//====================================================================================================
//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);
}
//====================================================================================================
//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);
}
#pragma interrupt_handler timer0_ovf_isr:iv_TIM0_OVF
void timer0_ovf_isr(void)
{
TIMER0_STOP();
USART0_OK_mark = TRUE;
}
#pragma interrupt_handler timer2_ovf_isr:iv_TIM2_OVF
void timer2_ovf_isr(void)
{
TIMER2_STOP();
USART1_OK_mark = TRUE;
}
#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;
TIMER0_START();
}
#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;
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();
}
}
} 俺已经解决了。
现在双向通讯没有任何问题。 连续通电3天,通讯次数达到1000000(1百万)次,通讯成功率100%。 请LZ应该分享一下解决问题的方法,谢谢!
页:
[1]