ba_wang_mao 发表于 2010-8-19 17:38:58

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)、其它原因
                               这种原因,如何解决?

               注:中转站接收“主站”的请求包完全正确。

bynce 发表于 2010-8-19 18:40:59

这个问题很不好查。   需要注意的地方
1.中转站接收数据。把接收到的数据一个一个移到MCU中处理也需要时间。 这些是硬件必须使用的时间。需要考虑。

3.5T只是推荐值。建议使用5ms左右。
150ms一帧建议增大到500ms
注意连接线的距离。

ba_wang_mao 发表于 2010-8-20 14:36:23

主站是一台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();
      }      
   }
}

ba_wang_mao 发表于 2010-10-6 13:24:53

俺已经解决了。
   现在双向通讯没有任何问题。 连续通电3天,通讯次数达到1000000(1百万)次,通讯成功率100%。

plc_avr 发表于 2010-10-6 15:09:35

请LZ应该分享一下解决问题的方法,谢谢!
页: [1]
查看完整版本: MODBUS 中转模块的通讯问题(主站发送请求包给“中转”模块,中转模块翻译后送给从站)。