搜索
bottom↓
回复: 6

请教马潮老师:从站发送一帧数据包(至少14个字节),为什么我的模块99.999%的时间接收正

[复制链接]

出0入0汤圆

发表于 2010-8-23 11:00:58 | 显示全部楼层 |阅读模式
问题如下:  
     
     主站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        00  0B         00  05       CRCHi  CRCLo
                              设备地址  功能码    起始地址=12   寄存器数量       CRC16

                  中转站接收到上述请求包后, 翻译成如下的包,然后发送给从站      
                                 1        1        00  00         00  05       CRCHi  CRCLo
                              设备地址  功能码    起始地址=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个字节。
                                   这种原因,如何解决?

出0入0汤圆

 楼主| 发表于 2010-8-23 11:02:32 | 显示全部楼层
经过连续长时间运行,发现错误是固定的,即:从站给中转站发送的应答包(至少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个字节
                              

(原文件名:未命名.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[USART0_receCount++] = 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[USART1_receCount++] = 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();
        }        
     }
}

出0入0汤圆

 楼主| 发表于 2010-8-26 13:10:49 | 显示全部楼层
问题已经解决。

出75入4汤圆

发表于 2010-8-26 14:37:20 | 显示全部楼层
怎么解决的?原因在哪里?让其他人也学习下。

出0入0汤圆

 楼主| 发表于 2010-8-27 16:16:49 | 显示全部楼层
1、程序代码没有任何问题。
2、问题出在硬件上
   问题出在“中转站”模块上没有启用终端电阻。
   启用终端电阻后,通讯百分百完全正常。


            |------------------    RS232      ----------------------        RS422         ----------------------   
            |     设备A       | --------->   |       设备X         |  --------------->   |       设备B       |   
            |                 | <----------- |                     |  <--------------    |                   |   
            |------------------              -----------------------                     ----------------------                                     通讯模式

出0入0汤圆

发表于 2010-8-27 16:20:43 | 显示全部楼层
为何不移植FREE MODBUS协议呢?很成熟的代码啊

出0入0汤圆

发表于 2012-11-9 09:34:36 | 显示全部楼层
谢谢楼主分享....
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-4-20 06:42

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表