[应用方案] 【新唐资料分享月】+基于新唐M051的modbus rtu代码实现

[复制链接]
4862|9
 楼主| Harvard 发表于 2016-12-11 18:05 | 显示全部楼层 |阅读模式
       不多说, 玩工控的对modbus rtu都很熟悉了,本人也是经常使用,一路从51,AVR, 用到新唐, 单片机在变,代码不变.下面要分享的一个简单实用的代码就是基于C的modbus rtu 从机读写的代码, 因为是从老的 8位机上的c代码移植而来,因此,在串口处理和超时处理, CRC校验上,还是力求可移植性 ,并没有过多的与硬件关联,利用上新唐M0的特性.
       后面有时间的话,准备把自己的代码全部修改为适合新唐mcu特点,发灰新唐MCU特性的特有代码;代码如下:
       一, 串口的基本通信部分, 说明: 代码是基于新唐2.0库编写,后期因为3.0库出来以后,部分功能使用到了3.0库,可能有些混乱.




  1. u16  _crc16_update0(u16 crc, u08 a)
  2. {
  3.         int i;
  4.         crc ^= a;
  5.         for (i = 0; i < 8; ++i)
  6.         {
  7.             if (crc & 1)
  8.                 crc = (crc >> 1) ^ 0xA001;
  9.             else
  10.                 crc = (crc >> 1);
  11.         }
  12.         return crc;
  13. }

  14. /*
  15. ** 串行口初始化
  16. ** baudrate取值为: 48 96 192
  17. */

  18. void usart0_init(u16 baudrate)
  19. {
  20.         /* Unlock protected registers */
  21.     SYS_UnlockReg();
  22.    
  23.          /* Reset UART0 module */
  24.     SYS_ResetModule(UART0_RST);
  25.                
  26.         /* Enable UART clock */
  27.      CLK_EnableModuleClock(UART0_MODULE);

  28.         /*---------------------------------------------------------------------------------------------------------*/
  29.         /* Init UART                                                                                               */
  30.         /*---------------------------------------------------------------------------------------------------------*/

  31.     /* 选择uart功能 */
  32.     UART0->FUN_SEL = UART_FUNC_SEL_UART;

  33.     /* Tx FIFO Reset & Rx FIFO Reset & FIFO Mode Enable */
  34.         //_UART_FLUSH_FIFO(UART0,UART_FCR_TFR_Msk | UART_FCR_RFR_Msk);
  35.     UART0->FCR |=UART_FCR_TFR_Msk | UART_FCR_RFR_Msk;

  36.     /* 选择RX FIFO的阀值为1BYTE*/
  37.      _UART_SET_RX_TRG_LEV(UART0,UART_FCR_RFITL_1BYTE);
  38.      
  39.     if( baudrate != 9600 && baudrate != 19200 && baudrate != 4800&&baudrate != 2400 ) baudrate  = 2400;//默认值

  40.     /* Set 115200 baudrate according to 50MHz system clock */
  41.    //UART0->BAUD = UART_BAUD_MODE2 | UART_BAUD_MODE2_DIVIDER(__IRC22M, baudrate);
  42.    UART0->BAUD = UART_BAUD_MODE2 | UART_BAUD_MODE2_DIVIDER(__HXT, baudrate);
  43.    
  44.     /* 设置串口的工作方式 */   
  45.    _UART_SET_DATA_FORMAT(UART0, UART_WORD_LEN_8 | UART_PARITY_NONE | UART_STOP_BIT_1);


  46.         /* Lock protected registers */
  47.     SYS_LockReg();


  48.     /* Enable RDA\RLS\Time-out Interrupt  */
  49.     UART_ENABLE_INT(UART0, (UART_IER_RDA_IEN_Msk ));
  50.    
  51.     /* 开串口中断 */
  52.     NVIC_EnableIRQ(UART02_IRQn);

  53. }
  54. /*
  55. *        通信辅助功能控制开关
  56. */

  57. void usart0_dis_recv(void)
  58. {
  59.     _UART_DISABLE_INT(UART0,UART_IER_RDA_IEN_Msk);
  60.     //_UART_RS485_SET_RXDISABLE(UART0);
  61.     UART0->FCR |= UART_FCR_RX_DIS_Msk; /*  Disable RS485 RX*/
  62.    
  63. }
  64. void usart0_en_recv (void)
  65. {
  66.     _UART_ENABLE_INT(UART0,UART_IER_RDA_IEN_Msk);
  67.     //_UART_RS485_CLEAR_RXDISABLE(UART0);
  68.     UART0->FCR &= ~ UART_FCR_RX_DIS_Msk;  /* Enable RS485 RX */

  69.      //UART0_SEND = 0;
  70. }

  71. /*超时定时器初始化及其开关控制*/
  72. #define T3_TICK_MIN                1000000/(F_CPU/1024)
  73. #define T3_TICK                        1000        //1ms
  74. #define TIMER3_BGN_VAL          (255-T3_TICK/T3_TICK_MIN)

  75. //static u08 CNT_t15=0;                        //1.5字符时间
  76. //static u08 CNT_t35=0;                        //3.5字符时间
  77. static u08 SYS_t3_cnt_frame=0;                //时间间隔计数器,用于1.5 和3.5字符时间判断
  78. u08     SYS_t3_on = OFF ;     //用于1.5 3.5字符判断的软定时器 开关.

  79. void timer3_on(void)
  80. {
  81.     SYS_t3_cnt_frame = 0 ;
  82.     SYS_t3_on = ON;
  83. }
  84. void timer3_off(void)
  85. {
  86.     SYS_t3_on = OFF;
  87.     /** 帧间隔控制 */
  88.     SYS_t3_cnt_frame = 0;
  89. }

  90. /**
  91. * [url=home.php?mod=space&uid=247401]@brief[/url]       Timer-3 IRQ
  92. *
  93. * @param       None
  94. *
  95. * [url=home.php?mod=space&uid=266161]@return[/url]      None
  96. *
  97. * [url=home.php?mod=space&uid=1543424]@Details[/url]     The TIMER2 default IRQ, declared in startup_M051Series.s.
  98. */
  99. void TMR3_IRQHandler(void)
  100. {
  101.     /* Clear TIMER2 Timeout Interrupt Flag */
  102.     _TIMER_CLEAR_CMP_INT_FLAG(TIMER3);   

  103.     /* ***********************1- 串口0:超时处理 ***************************/
  104.     if( SYS_t3_on == ON )
  105.     {
  106.           SYS_t3_cnt_frame++;
  107.          
  108.           if ( SYS_t3_cnt_frame > T35)         //a frame was finished
  109.           {
  110.                usart0_dis_recv();          //禁止接收中断

  111.                timer3_off ()          ;   //tunr off timer;

  112.                SYS_t3_cnt_frame = 0 ;   //reset timer

  113.                RxBufPtr--;                         //调整指针        

  114.                fFrameDone    = OK;   //frame is completed
  115.                
  116.                LED_DTU = 1;
  117.            }      
  118.     }
  119.     /* 定时器关,还未进行帧间隔判断 */
  120.    else
  121.    {
  122.         SYS_t3_cnt_frame = 0;
  123.    }
  124.     /* ***********************2- 串口2:超时处理 ***************************/
  125.    /* 直接调用定时器2的回调函数 */
  126.    usart2_timeout();
  127. }

  128. u08     RxBuf[MAX_BUF_LEN]={0,1,2,3,4};
  129. u08     TxBuf[MAX_BUF_LEN];
  130. u08     RxBufPtr             = RX_BUF_BGN_ADR;
  131. u08     TxBufPtr             = TX_BUF_BGN_ADR1;
  132. BOOL    fIsRXING            = NO;          //is receiving now
  133. BOOL    fIsTXING            = NO;          //is transsending now
  134. BOOL    fRxBufFull            = NO;
  135. BOOL    fTxBufFull      = NO;
  136. BOOL    fFrameDone      = NO;                   //one frame finished
  137. BOOL    fFrameBad       = NO;                   //frame was broken
  138. u16     CRC                        = 0xFFFF;           //CRC初始值       


  139. /*接收中断*/
  140. void UART02_IRQHandler( void )
  141. {
  142.     /* 1-定义临时变量获取中断标志   */
  143.     uint32_t u32IntSts= UART0->ISR;
  144.    
  145.     /* 2- 判断是否是RDA中断        */
  146.     // 2-1:串口0处理
  147.      if( UART_GET_INT_FLAG(UART0,UART_ISR_RDA_INT_Msk) )
  148.     {
  149.         /* 通信指示灯处理 */
  150.          LED_DTU = 0;
  151.         
  152.         /* 查询串口的RDA */
  153.         while( UART_IS_RX_READY(UART0) )
  154.         {
  155.             /* Get the character from UART Buffer
  156.             *   BSP接收数据
  157.             */
  158.            RxBuf[RxBufPtr]  = UART_READ(UART0);
  159.            /* 复位定时器  */
  160.            SYS_t3_cnt_frame = 0 ;   

  161.             /*new frame coming*/
  162.             if( RxBufPtr == RX_BUF_BGN_ADR )//新的一帧开始
  163.             {
  164.                 CRC = 0xffff        ;                    //准备crc计算,
  165.                 timer3_on();                    //开启定时器计时
  166.             }
  167.             /*with a frame and RxBuf is not full*/
  168.             /*when we detect a frame was borken according to t15 ,
  169.             * we keep on receiving in order to synchronize the frame  
  170.             */
  171.             else if( RxBufPtr < MAX_BUF_LEN -1)
  172.             {  
  173.                 if( SYS_t3_cnt_frame > T15 && SYS_t3_cnt_frame < T35 )
  174.                 {
  175.                      SYS_t3_cnt_frame   = 0 ;   //reset timer
  176.                      fFrameBad          = TRUE; //frame is broken
  177.                 }
  178.             }

  179.             /*RxBuf full */
  180.             /*
  181.             **
  182.             */
  183.             else if
  184.             ( RxBufPtr >= MAX_BUF_LEN -1 )
  185.             {
  186.                 usart0_dis_recv()    ;                         //stop receving            
  187.                 timer3_off ()            ;            //tunr off timer;
  188.                 fRxBufFull           = YES ;            //缓冲区已满
  189.                 fFrameBad     = TRUE;            //frame is broken
  190.                 RxBufPtr --;                             //指针保持不变,防止破坏其他数据区
  191.                 LED_DTU = 1;
  192.             }
  193.                 CRC = _crc16_update0( CRC,RxBuf[RxBufPtr] );
  194.                 RxBufPtr++;                     //adjust pointer
  195.         }//end while
  196.         
  197.      }//end if
  198.     // END 2-1:串口0处理
  199.    
  200.     // 2-1:串口2处理
  201.     UART2_IRQHandler();

  202. }

  203. /*查询发送*/
  204. void usart0_send_byte( u08 txbyte )
  205. {
  206.      //UART0_SEND = 1;
  207.     UART_WRITE( UART0,txbyte );
  208.     UART_WAIT_TX_EMPTY( UART0 );
  209. }

   二, MODBUS RTU解析处理部分;

  2.1 首先构造modbus协议的通信协议(相当于用户应用层的 ),就是规定多少号寄存器,是代表什么变量,取值范围等等;
/* Modbus地址与实际内部变量的映射表 */
const   u08 * modbus_tab[]  =
{
    /* 2- 只读区域:  地址: 400001 - 400080 ,协议中规定的各种数据地址 */
    MODBUS_ADDR_TAB,
};

      MODBUS_ADDR_TAB,是一个特殊处理手段, 完成了modbus线性寄存器地址与散落在RAM里面的各个变量的对应工作,适合于应用协议数据量不是特别多的情况.
    具体的构造如下,笨办法,谅解.

  1. /* 通信点表与实际的变量映射 ,空白处用Space16替代  */

  2. #define ADR400001          SYS_longitude_hi              //经度
  3. #define ADR400002          SYS_longitude_low             // 纬度
  4. #define ADR400003          SYS_latitude_hi     
  5. #define ADR400004          SYS_latitude_low        
  6. #define ADR400005          SYS_runstate      
  7. #define ADR400006          SYS_current        
  8. #define ADR400007          SYS_voltage                  
  9. #define ADR400008          SYS_kw            
  10. #define ADR400009          SYS_oil_pressure   
  11. #define ADR400010          SYS_oil_temperatue
  12.                                  
  13. #define ADR400011           SYS_gas_level
  14. #define ADR400012           SYS_current_A
  15. #define ADR400013           SYS_current_B
  16. #define ADR400014           SYS_current_C
  17. #define ADR400015           Space16
  18. #define ADR400016           SYS_total_alarm     
  19. #define ADR400017           SYS_alarm1         
  20. #define ADR400018           SYS_alarm2         
  21. #define ADR400019           SYS_alarm3         
  22. #define ADR400020           SYS_alarm4         
  23. #define ADR400021           SYS_alarm5         
  24. #define ADR400022           SYS_alarm6         
  25. #define ADR400023           SYS_alarm7         
  26. #define ADR400024           SYS_alarm8         
  27. #define ADR400025           SYS_alarm9         
  28. #define ADR400026           SYS_alarm10         
  29. #define ADR400027           SYS_alarm11         
  30. #define ADR400028           SYS_alarm12         
  31. #define ADR400029           SYS_alarm13         
  32. #define ADR400030           SYS_alarm14         
  33.                                       
  34. #define ADR400031           SYS_alarm15
  35. #define ADR400032           SYS_his_ID            
  36. #define ADR400033           SYS_his_begin_year   
  37. #define ADR400034           SYS_his_begin_month   
  38. #define ADR400035           SYS_his_begin_day     
  39. #define ADR400036           SYS_his_begin_hour   
  40. #define ADR400037           SYS_his_begin_minute  
  41. #define ADR400038           SYS_his_begin_second  
  42. #define ADR400039           SYS_his_end_year      
  43. #define ADR400040           SYS_his_end_month     
  44. #define ADR400041           SYS_his_end_day      
  45. #define ADR400042           SYS_his_end_hour      
  46. #define ADR400043           SYS_his_end_minute   
  47. #define ADR400044           SYS_his_end_second   
  48. #define ADR400045           SYS_his_length_hour   
  49. #define ADR400046           SYS_his_length_minute
  50. #define ADR400047           SYS_his_kwh_low  //注意顺序      
  51. #define ADR400048           SYS_his_kwh_hi
  52.       
  53. #define ADR400049           SYS_his_gas_cost   

  54.    
  55. #define ADR400050           SYS_his_longitude_hi
  56. #define ADR400051           SYS_his_longitude_low
  57. #define ADR400052           SYS_his_latitude_hi  
  58. #define ADR400053           SYS_his_latitude_low
  59. #define ADR400054           Space16                 //预留1
  60. #define ADR400055           Space16
  61. #define ADR400056           Space16
  62. #define ADR400057           Space16
  63. #define ADR400058           Space16


  64. #define ADR400059           SYS_his_alarm1  
  65. #define ADR400060           SYS_his_alarm2  
  66. #define ADR400061           SYS_his_alarm3  
  67. #define ADR400062           SYS_his_alarm4  
  68. #define ADR400063           SYS_his_alarm5  
  69. #define ADR400064           SYS_his_alarm6  
  70. #define ADR400065           SYS_his_alarm7  
  71. #define ADR400066           SYS_his_alarm8  
  72. #define ADR400067           SYS_his_alarm9  
  73. #define ADR400068           SYS_his_alarm10
  74. #define ADR400069           SYS_his_alarm11
  75. #define ADR400070           SYS_his_alarm12
  76. #define ADR400071           SYS_his_alarm13
  77. #define ADR400072           SYS_his_alarm14
  78. #define ADR400073           SYS_his_alarm15

  79. #define ADR400074           SYS_his_handshake
  80. #define ADR400075           Space16
  81. #define ADR400076           Space16
  82. #define ADR400077           Space16
  83. #define ADR400078           Space16
  84. #define ADR400079           Space16
  85. #define ADR400080           Space16




  86. /* 2- 利用宏定义通信地址表格: 防止源码中太长,影响c代码可读性
  87. *     也可将本文件通过include 插入到modbus.c文件中
  88. */

  89. #define MODBUS_ADDR_TAB     \
  90.                             \
  91.         (u08 *) &SYS_longitude+1,                     ((u08 *) &SYS_longitude)+0,\
  92.         ((u08 *) &SYS_longitude)+3,                 ((u08 *) &SYS_longitude)+2,\
  93.         (u08 *) &SYS_latitude+1,                     ((u08 *) &SYS_latitude)+0,\
  94.         ((u08 *) &SYS_latitude)+3,                 ((u08 *) &SYS_latitude)+2,\
  95.                 &HIGH_( ADR400005 ),                                      &LOW_( ADR400005 ), \
  96.                 &HIGH_( ADR400006 ),                                      &LOW_( ADR400006 ), \
  97.                 &HIGH_( ADR400007 ),                                      &LOW_( ADR400007 ), \
  98.                 &HIGH_( ADR400008 ),                                      &LOW_( ADR400008 ), \
  99.                 &HIGH_( ADR400009 ),                                      &LOW_( ADR400009 ), \
  100.                 &HIGH_( ADR400010 ),                                      &LOW_( ADR400010 ), \
  101.                 &HIGH_( ADR400011 ),                                      &LOW_( ADR400011 ), \
  102.                 &HIGH_( ADR400012 ),                                      &LOW_( ADR400012 ), \
  103.                 &HIGH_( ADR400013 ),                                      &LOW_( ADR400013 ), \
  104.                 &HIGH_( ADR400014 ),                                      &LOW_( ADR400014 ), \
  105.                 &HIGH_( ADR400015 ),                                      &LOW_( ADR400015 ), \
  106.                 &HIGH_( ADR400016 ),                                      &LOW_( ADR400016 ), \
  107.                 &HIGH_( ADR400017 ),                                      &LOW_( ADR400017 ), \
  108.                 &HIGH_( ADR400018 ),                                      &LOW_( ADR400018 ), \
  109.                 &HIGH_( ADR400019 ),                                      &LOW_( ADR400019 ), \
  110.                 &HIGH_( ADR400020 ),                                      &LOW_( ADR400020 ), \
  111.                 &HIGH_( ADR400021 ),                                      &LOW_( ADR400021 ), \
  112.                 &HIGH_( ADR400022 ),                                      &LOW_( ADR400022 ), \
  113.                 &HIGH_( ADR400023 ),                                      &LOW_( ADR400023 ), \
  114.                 &HIGH_( ADR400024 ),                                      &LOW_( ADR400024 ), \
  115.                 &HIGH_( ADR400025 ),                                      &LOW_( ADR400025 ), \
  116.                 &HIGH_( ADR400026 ),                                      &LOW_( ADR400026 ), \
  117.                 &HIGH_( ADR400027 ),                                      &LOW_( ADR400027 ), \
  118.                 &HIGH_( ADR400028 ),                                      &LOW_( ADR400028 ), \
  119.                 &HIGH_( ADR400029 ),                                      &LOW_( ADR400029 ), \
  120.                 &HIGH_( ADR400030 ),                                      &LOW_( ADR400030 ), \
  121.                 &HIGH_( ADR400031 ),                                      &LOW_( ADR400031 ), \
  122.                 &HIGH_( ADR400032 ),                                      &LOW_( ADR400032 ), \
  123.                 &HIGH_( ADR400033 ),                                      &LOW_( ADR400033 ), \
  124.                 &HIGH_( ADR400034 ),                                      &LOW_( ADR400034 ), \
  125.                 &HIGH_( ADR400035 ),                                      &LOW_( ADR400035 ), \
  126.                 &HIGH_( ADR400036 ),                                      &LOW_( ADR400036 ), \
  127.                 &HIGH_( ADR400037 ),                                      &LOW_( ADR400037 ), \
  128.                 &HIGH_( ADR400038 ),                                      &LOW_( ADR400038 ), \
  129.                 &HIGH_( ADR400039 ),                                      &LOW_( ADR400039 ), \
  130.                 &HIGH_( ADR400040 ),                                      &LOW_( ADR400040 ), \
  131.                 &HIGH_( ADR400041 ),                                      &LOW_( ADR400041 ), \
  132.                 &HIGH_( ADR400042 ),                                      &LOW_( ADR400042 ), \
  133.                 &HIGH_( ADR400043 ),                                      &LOW_( ADR400043 ), \
  134.                 &HIGH_( ADR400044 ),                                      &LOW_( ADR400044 ), \
  135.                 &HIGH_( ADR400045 ),                                      &LOW_( ADR400045 ), \
  136.                 &HIGH_( ADR400046 ),                                      &LOW_( ADR400046 ), \
  137.                 &HIGH_( ADR400047 ),                                      &LOW_( ADR400047 ), \
  138.                 &HIGH_( ADR400048 ),                                      &LOW_( ADR400048 ), \
  139.                 &HIGH_( ADR400049 ),                                      &LOW_( ADR400049 ), \
  140.         (u08 *) &SYS_his_longitude+1,                     ((u08 *) &SYS_his_longitude)+0,\
  141.         ((u08 *) &SYS_his_longitude)+3,                 ((u08 *) &SYS_his_longitude)+2,\
  142.         (u08 *) &SYS_his_latitude+1,                     ((u08 *) &SYS_his_latitude)+0,\
  143.         ((u08 *) &SYS_his_latitude)+3,                 ((u08 *) &SYS_his_latitude)+2,\
  144.                 &HIGH_( ADR400054 ),                                      &LOW_( ADR400054 ), \
  145.                 &HIGH_( ADR400055 ),                                      &LOW_( ADR400055 ), \
  146.                 &HIGH_( ADR400056 ),                                      &LOW_( ADR400056 ), \
  147.                 &HIGH_( ADR400057 ),                                      &LOW_( ADR400057 ), \
  148.                 &HIGH_( ADR400058 ),                                      &LOW_( ADR400058 ), \
  149.                 &HIGH_( ADR400059 ),                                      &LOW_( ADR400059 ), \
  150.                 &HIGH_( ADR400060 ),                                      &LOW_( ADR400060 ), \
  151.                 &HIGH_( ADR400061 ),                                      &LOW_( ADR400061 ), \
  152.                 &HIGH_( ADR400062 ),                                      &LOW_( ADR400062 ), \
  153.                 &HIGH_( ADR400063 ),                                      &LOW_( ADR400063 ), \
  154.                 &HIGH_( ADR400064 ),                                      &LOW_( ADR400064 ), \
  155.                 &HIGH_( ADR400065 ),                                      &LOW_( ADR400065 ), \
  156.                 &HIGH_( ADR400066 ),                                      &LOW_( ADR400066 ), \
  157.                 &HIGH_( ADR400067 ),                                      &LOW_( ADR400067 ), \
  158.                 &HIGH_( ADR400068 ),                                      &LOW_( ADR400068 ), \
  159.                 &HIGH_( ADR400069 ),                                      &LOW_( ADR400069 ), \
  160.                 &HIGH_( ADR400070 ),                                      &LOW_( ADR400070 ), \
  161.                 &HIGH_( ADR400071 ),                                      &LOW_( ADR400071 ), \
  162.                 &HIGH_( ADR400072 ),                                      &LOW_( ADR400072 ), \
  163.                 &HIGH_( ADR400073 ),                                      &LOW_( ADR400073 ), \
  164.                 &HIGH_( ADR400074 ),                                      &LOW_( ADR400074 ), \
  165.                 &HIGH_( ADR400075 ),                                      &LOW_( ADR400075 ), \
  166.                 &HIGH_( ADR400076 ),                                      &LOW_( ADR400076 ), \
  167.                 &HIGH_( ADR400077 ),                                      &LOW_( ADR400077 ), \
  168.                 &HIGH_( ADR400078 ),                                      &LOW_( ADR400078 ), \
  169.                 &HIGH_( ADR400079 ),                                      &LOW_( ADR400079 ), \
  170.                 &HIGH_( ADR400080 ),                                      &LOW_( ADR400080 )

  171. #define MODBUS_ADDR_MAX 80         
  172.                                 

[53O52ZWSI5TQ(A3`Z_5T`K.png
 楼主| Harvard 发表于 2016-12-11 18:06 | 显示全部楼层
本帖最后由 Harvard 于 2016-12-11 18:15 编辑

而 实际变量的定义如下:



/*********************************************************************************************
*  2 -上位机通信电表  
**********************************************************************************************/
/* 2-1 : 定义需要提供给组态查询的通信变量 */

float    SYS_longitude          = 120.271723000;//经纬度
float    SYS_latitude           = 31.922272000 ;
u16      SYS_longitude_hi       = 0 ;//经纬度
u16      SYS_longitude_low      = 0  ;
u16      SYS_latitude_hi        = 0 ;//经纬度
u16      SYS_latitude_low       = 0  ;
u16      SYS_runstate           = 0;//经纬度
u16      SYS_current            = 0;
u16      SYS_voltage            = 0;
u16      SYS_kw                 = 0;
u16      SYS_oil_pressure       = 0;
int16    SYS_oil_temperatue     = 0;
int16    SYS_gas_level          = 0;//燃油液位

/* 预留:1-4, 400012-15 */
u16      SYS_current_A          = 0;
u16      SYS_current_B          = 0;
u16      SYS_current_C          = 0;


/*400016 */
u16      SYS_total_alarm        = 0;    //报警汇总: 1:报警, 0正常
u16      SYS_alarm1             = 0;    //报警汇总: 1:报警, 0正常
u16      SYS_alarm2             = 0;    //报警汇总: 1:报警, 0正常
u16      SYS_alarm3             = 0;    //报警汇总: 1:报警, 0正常
u16      SYS_alarm4             = 0;    //报警汇总: 1:报警, 0正常
u16      SYS_alarm5             = 0;    //报警汇总: 1:报警, 0正常
u16      SYS_alarm6             = 0;    //报警汇总: 1:报警, 0正常
u16      SYS_alarm7             = 0;    //报警汇总: 1:报警, 0正常
u16      SYS_alarm8             = 0;    //报警汇总: 1:报警, 0正常
u16      SYS_alarm9             = 0;    //报警汇总: 1:报警, 0正常
u16      SYS_alarm10            = 0;    //报警汇总: 1:报警, 0正常
u16      SYS_alarm11            = 0;    //报警汇总: 1:报警, 0正常
u16      SYS_alarm12            = 0;    //报警汇总: 1:报警, 0正常
u16      SYS_alarm13            = 0;    //报警汇总: 1:报警, 0正常
u16      SYS_alarm14            = 0;    //报警汇总: 1:报警, 0正常
u16      SYS_alarm15            = 0;    //报警汇总: 1:报警, 0正常

/*3- 历史数据相关:用于历史数据的发送.(把所有的记录挨个移入SYS_hix_xx系列;) */
u16      SYS_his_ID             =   1;      //当前读取的历史记录号
u16      SYS_his_begin_year     =   2016;   //当前这条记录其实年份
u16      SYS_his_begin_month    =   5;     //当前这条记录其实年份
u16      SYS_his_begin_day      =   8;     //当前这条记录其实年份
u16      SYS_his_begin_hour     =   8;     //当前这条记录其实年份
u16      SYS_his_begin_minute   =   8;      //当前这条记录其实年份
u16      SYS_his_begin_second   =   8;      //当前这条记录其实年份
u16      SYS_his_end_year       =   2016;    //当前这条记录其实年份
u16      SYS_his_end_month      =    4;      //当前这条记录其实年份
u16      SYS_his_end_day        =    23;      //当前这条记录其实年份
u16      SYS_his_end_hour       =   12;      //当前这条记录其实年份
u16      SYS_his_end_minute     =    8;      //当前这条记录其实年份
u16      SYS_his_end_second     =    8;      //当前这条记录其实年份
u16      SYS_his_length_hour    =   4;      //本次发电时长:小时
u16      SYS_his_length_minute  =   0;      //本次发电时长,分钟
u16      SYS_his_kwh_hi         =   0;      //单词发电量高低16位
u16      SYS_his_kwh_low        =   10;     //单词发电量高低16位
u16      SYS_his_gas_cost       =   0;      //油耗

u32     SYS_his_kwh = 0;    //单词发电量

u16      SYS_his_longitude_hi   =  0;     //经度高低16位
u16      SYS_his_longitude_low  =  0;     //经度高低16位
u16      SYS_his_latitude_hi    =  0;     //纬度高低16位
u16      SYS_his_latitude_low   =  0;     //纬度高低16位

float   SYS_his_longitude       = 0;    //历史记录:经度;
float   SYS_his_latitude        = 0;    //历史记录:纬度;

u16      SYS_his_alarm1         = 0;    //报警汇总: 1:报警, 0正常
u16      SYS_his_alarm2         = 0;    //报警汇总: 1:报警, 0正常
u16      SYS_his_alarm3         = 0;    //报警汇总: 1:报警, 0正常
u16      SYS_his_alarm4         = 0;    //报警汇总: 1:报警, 0正常
u16      SYS_his_alarm5         = 0;    //报警汇总: 1:报警, 0正常
u16      SYS_his_alarm6         = 0;    //报警汇总: 1:报警, 0正常
u16      SYS_his_alarm7         = 0;    //报警汇总: 1:报警, 0正常
u16      SYS_his_alarm8         = 0;    //报警汇总: 1:报警, 0正常
u16      SYS_his_alarm9         = 0;    //报警汇总: 1:报警, 0正常
u16      SYS_his_alarm10        = 0;    //报警汇总: 1:报警, 0正常
u16      SYS_his_alarm11        = 0;    //报警汇总: 1:报警, 0正常
u16      SYS_his_alarm12        = 0;    //报警汇总: 1:报警, 0正常
u16      SYS_his_alarm13        = 0;    //报警汇总: 1:报警, 0正常
u16      SYS_his_alarm14        = 0;    //报警汇总: 1:报警, 0正常
u16      SYS_his_alarm15        = 0;    //报警汇总: 1:报警, 0正常

u16      SYS_his_handshake      = 0;    //通信握手信号
                                        //1:HMI读到1,存数据库后,写0,
                                        //2:单片机读到0,move最老一条数据,写1,并且定时加1
                                        //3:单片机发现当前未运行或者正在运行,没有历史数据,不写1"


/*3- 历史记录相关 */
u16      SYS_rec_ID             =   1;      //当前读取的历史记录号 :
u16      SYS_rec_begin_year     =   2016;   //当前这条记录其实年份
u16      SYS_rec_begin_month    =   5;     //当前这条记录其实年份
u16      SYS_rec_begin_day      =   8;     //当前这条记录其实年份
u16      SYS_rec_begin_hour     =   8;     //当前这条记录其实年份
u16      SYS_rec_begin_minute   =   8;      //当前这条记录其实年份
u16      SYS_rec_begin_second   =   8;      //当前这条记录其实年份
u16      SYS_rec_end_year       =   2016;    //当前这条记录其实年份
u16      SYS_rec_end_month      =    4;      //当前这条记录其实年份
u16      SYS_rec_end_day        =    23;      //当前这条记录其实年份
u16      SYS_rec_end_hour       =   12;      //当前这条记录其实年份
u16      SYS_rec_end_minute     =    8;      //当前这条记录其实年份
u16      SYS_rec_end_second     =    8;      //当前这条记录其实年份
u16      SYS_rec_length_hour    =   4;      //本次发电时长:小时
u16      SYS_rec_length_minute  =   0;      //本次发电时长,分钟
u16      SYS_rec_kwh_hi         =   0;      //单词发电量高低16位
u16      SYS_rec_kwh_low        =   10;      //单词发电量高低16位
u16      SYS_rec_gas_cost       =   0;      //油耗

u32     SYS_rec_kwh = 0;    //单词发电量

u16      SYS_rec_longitude_hi   =  0;     //经度高低16位
u16      SYS_rec_longitude_low  =  0;     //经度高低16位
u16      SYS_rec_latitude_hi    =  0;     //纬度高低16位
u16      SYS_rec_latitude_low   =  0;     //纬度高低16位
           
float   SYS_rec_longitude       = 0;    //历史记录:经度;
float   SYS_rec_latitude        = 0;    //历史记录:纬度;
           
u16      SYS_rec_alarm1         = 0;    //报警汇总: 1:报警, 0正常
u16      SYS_rec_alarm2         = 0;    //报警汇总: 1:报警, 0正常
u16      SYS_rec_alarm3         = 0;    //报警汇总: 1:报警, 0正常
u16      SYS_rec_alarm4         = 0;    //报警汇总: 1:报警, 0正常
u16      SYS_rec_alarm5         = 0;    //报警汇总: 1:报警, 0正常
u16      SYS_rec_alarm6         = 0;    //报警汇总: 1:报警, 0正常
u16      SYS_rec_alarm7         = 0;    //报警汇总: 1:报警, 0正常
u16      SYS_rec_alarm8         = 0;    //报警汇总: 1:报警, 0正常
u16      SYS_rec_alarm9         = 0;    //报警汇总: 1:报警, 0正常
u16      SYS_rec_alarm10        = 0;    //报警汇总: 1:报警, 0正常
u16      SYS_rec_alarm11        = 0;    //报警汇总: 1:报警, 0正常
u16      SYS_rec_alarm12        = 0;    //报警汇总: 1:报警, 0正常
u16      SYS_rec_alarm13        = 0;    //报警汇总: 1:报警, 0正常
u16      SYS_rec_alarm14        = 0;    //报警汇总: 1:报警, 0正常
u16      SYS_rec_alarm15        = 0;    //报警汇总: 1:报警, 0正常

u16      SYS_rec_handshake      = 1;    //通信握手信号
                                        //1:HMI读到1,存数据库后,写0,
                                        //2:单片机读到0,move最老一条数据,写1,并且定时加1
                                        //3:单片机发现当前未运行或者正在运行,没有历史数据,不写1"



   2.2   协议相关的三个函数

sendframe();  cmd_proc(); dtu_mod_proc();  分别用于发送一阵数据,主要是回应主机; 协议解析具体实现, modbus协议处理状态机函数;

只要在没有大的死等延时的后台循环中,插入dtu_mod_proc函数,就可以实现modbus rtu 从机模式的读写功能了. 当然前提是正确的初始化串口,设定正确的波特率和modbus 从机地址;


  1. void dtu_mod_process(void)
  2. {
  3.         if( fFrameDone == OK )                                //接收已经完成
  4.         {
  5.                 fFrameDone = NO;                        //清除标志
  6.                 usart0_dis_recv();                                //禁止接收中断
  7.                 if ( fFrameBad ==TRUE )             //数据帧被破换,不予处理
  8.                 {
  9.                          fFrameBad = FALSE;             //清除错误标志
  10.                 }
  11.                 else                                                         //数据帧正常则解析协议
  12.                 {
  13.                          cmd_proc();       
  14.                 }
  15.                         RxBufPtr   = RX_BUF_BGN_ADR;//准备下次接收
  16.                         usart0_en_recv();                //重新接收
  17.         }
  18.        
  19.         if ( fRxBufFull == YES )                              //数据帧错误
  20.         {
  21.                  fFrameBad  =  FALSE;                     //清除错误标志
  22.          RxBufPtr   =  RX_BUF_BGN_ADR;        //准备下次接收
  23.         }
  24.                  fRxBufFull =  NO          ;             //清空缓冲区
  25.                  usart0_en_recv();                          //重新接收

  26. }

  27. /*命令处理*/
  28. void cmd_proc(void)
  29. {
  30.         u16 reg_adr =0; //临时 存放要读写的寄存器的地址
  31. //        u16 reg_n =0;   //临时 存放要读写的寄存器的数目
  32.         u08 reg_errcode =0;//出错时的错误编码  
  33. //        u08 i;

  34.         ////////////////////////////////////////////=======================CRC 校验               
  35.         if ( CRC != 0 )                           
  36.         {
  37.         ;
  38.                 //usart0_send_byte(0);           //错误代码0: crc 校验错误
  39.                 return ;
  40.         }
  41.                 CRC = 0xffff;        //准备计算crc       
  42.                
  43.         ///////////////////////////////////////////========================判断地址是否正确
  44.         if( SYS_485_adr != RxBuf[0] && 0 != RxBuf[0] )
  45.         {
  46.                 ;
  47.         // usart0_send_byte(1);          //错误代码1 通信地址错误
  48.                 return ;
  49.         }

  50.                 TxBuf[0] = SYS_485_adr ;//respond frame's header
  51.                 TxBuf[1] = RxBuf[1];//respond frame's function code                       
  52.                 CRC      = _crc16_update0( CRC ,TxBuf[0] );
  53.                 CRC      = _crc16_update0( CRC ,TxBuf[1] );
  54.         ////////////////////////////////////////// ========================识别功能码
  55.         if( RxBuf[1]==0x07 )                      //----------------fast read version
  56.         {
  57.                 TxBuf[2]= (HVERSION<<4)+SVERSION;
  58.                 CRC = _crc16_update0( CRC ,TxBuf[2] );
  59.                 TxBuf[3] = CRC%256;
  60.                 TxBuf[4] = CRC/256;
  61.                 TxBufPtr = 4;
  62.                 send_frame ();
  63.         }
  64.         else
  65.         if( RxBuf[1]==0x03  )         //-------------------------读寄存器
  66.         {
  67.                 u08  cnt;
  68.                          TxBuf[2]   = RxBuf[5]*2;                         //字节数
  69.                          CRC                 = _crc16_update0( CRC ,TxBuf[2] );
  70.                          TxBufPtr   = 3;                   //调整发送指针,
  71.             
  72.                 for( cnt =0 ; cnt< TxBuf[2] ; )//RxBuf[5]--word counts
  73.                 {
  74.                     TxBuf[TxBufPtr]
  75.                         = *((u08 *) modbus_tab [cnt +RxBuf[3]*2]);//RxBuf[3]中为起始地址低8位
  76.                         CRC         = _crc16_update0( CRC ,TxBuf[TxBufPtr] );
  77.                         cnt         =  cnt + 1;
  78.                         TxBufPtr    =  TxBufPtr + 1  ;
  79.                 }
  80.                         TxBuf[TxBufPtr] = CRC%256;
  81.                         TxBufPtr        = TxBufPtr + 1;        //调整发送指针
  82.                         TxBuf[TxBufPtr] = CRC/256;
  83.                         send_frame();//发送         
  84.          }
  85.         else
  86.         if( RxBuf[1]==0x10 )                                         //--------------------写寄存器
  87.         {
  88.                 u08  cnt;
  89.         TxBuf[2]   = RxBuf[2];//写起始地址高8位,一般为0
  90.         TxBuf[3]   = RxBuf[3];//写起始地址低8位
  91.         TxBuf[4]   = RxBuf[4];//欲写寄存器数目高8位,一般为0
  92.         TxBuf[5]   = RxBuf[5];//欲写寄存器数目低8位

  93.         //不允许写长度为0
  94.         if(RxBuf[6] < 2)         goto err_ret;               
  95.         //通信协议的规范性检测,从数据帧长度判断       
  96.         if( (6 + 2 +  RxBuf[6] )  != RxBufPtr )  goto err_ret;//指令格式不正确
  97.          
  98.         for( cnt =0 ; cnt< RxBuf[6] ;cnt++ )     //RxBuf[6]--byte counts
  99.         {
  100.          
  101.           *((u08 *) modbus_tab[ cnt +RxBuf[3]*2] )
  102.             
  103.             =   RxBuf[ 7 + cnt ];            //RxBuf[3]中为起始地址低8位,乘以2,是将之转换为内部的字节地址
  104.         }

  105.         for ( cnt = 0 ; cnt < 4 ; cnt ++ ) //发送字节指针为0-7,其中:0 .1 为固定,其crc已经计算好
  106.         {
  107.             CRC = _crc16_update0( CRC ,TxBuf[2+cnt] );//2-5
  108.         }
  109.         TxBuf[6] = CRC%256;
  110.         TxBuf[7] = CRC/256;                                
  111.         TxBufPtr = 7;                    //调整发送指针
  112.         send_frame();                    //发送
  113.         
  114.         err_ret:         TxBufPtr = TX_BUF_BGN_ADR;    //after sending ,reset ptr
  115.         
  116.      }
  117.         else
  118.         if( RxBuf[1]==0x06 )                                        //----------写寄存器(单个寄存器)
  119.         {
  120.                 /* 先获取寄存器地址 */
  121.                 reg_adr =((u16) RxBuf[2]<<8) + (u16) RxBuf[3]        ;        //        reg_adr = (u16) RxBuf[3]        ;

  122.         /* 最大写入地址限定 */
  123.         if(reg_adr > MODBUS_ADDR_MAX )       
  124.                 {
  125.                         reg_errcode = 2;//非法的数据地址
  126.                        
  127.                         goto err_ret_06;
  128.                 }
  129.                 /* 预写一次寄存器值
  130.             */
  131.                  *((u08 *)(modbus_tab [reg_adr*2]))=  RxBuf[4] ;
  132.                  *((u08 *)(modbus_tab [reg_adr*2+1]))=RxBuf[5] ;
  133.          
  134.         ///////发送会送数据帧
  135.         TxBuf[0] = RxBuf[0];
  136.         TxBuf[1] = RxBuf[1];
  137.         TxBuf[2] = RxBuf[2];
  138.         TxBuf[3] = RxBuf[3];
  139.         TxBuf[4] = RxBuf[4];
  140.         TxBuf[5] = RxBuf[5];
  141.         TxBuf[6] = RxBuf[6];
  142.         TxBuf[7] = RxBuf[7];
  143.         TxBufPtr = 7;                //调整发送指针
  144.         send_frame();                //发送                               
  145.                        
  146.         ///////保存参数
  147.         //save_user_data_to_flash();   //保存更改
  148.         
  149.         /////////////错误处理:
  150.         err_ret_06:
  151.         /* 如果是非法的数据地址 */
  152.         if( reg_errcode == 2 )
  153.         {
  154.             TxBuf[0] = RxBuf[0];
  155.             TxBuf[1] = 0x86;
  156.             CRC =0xffff;
  157.             CRC      = _crc16_update0( CRC ,TxBuf[0] );
  158.             CRC      = _crc16_update0( CRC ,TxBuf[1] );
  159.             TxBuf[2] = reg_errcode;
  160.             CRC      = _crc16_update0( CRC ,TxBuf[2] );
  161.             TxBuf[3] = CRC%256;
  162.             TxBuf[4] = CRC/256;
  163.             TxBufPtr = 4;
  164.             send_frame();
  165.         }
  166.         else
  167.         if( reg_errcode == 3 )
  168.         {
  169.             TxBuf[0] = RxBuf[0];
  170.             TxBuf[1] = 0x86;
  171.             CRC =0xffff;
  172.             CRC      = _crc16_update0( CRC ,TxBuf[0] );
  173.             CRC      = _crc16_update0( CRC ,TxBuf[1] );
  174.             TxBuf[2] = reg_errcode;
  175.             CRC      = _crc16_update0( CRC ,TxBuf[2] );
  176.             TxBuf[3] = CRC%256;
  177.             TxBuf[4] = CRC/256;
  178.             TxBufPtr = 4;
  179.             send_frame();
  180.         }
  181.             
  182.                         TxBufPtr = TX_BUF_BGN_ADR;    //after sending ,reset ptr         
  183.     }
  184.         
  185.         

  186. }

  187. /*发送一一帧数据*/
  188. void send_frame(void)
  189. {
  190.         u08  i;
  191.     /* 输出指示灯 */
  192.     LED_OUT = 0;
  193.    
  194.         for( i = 0 ; i <= TxBufPtr; i++ ) //TxBufPtr为发送缓冲区指示器
  195.         {
  196.                 usart0_send_byte(TxBuf[i]);
  197.         }
  198.         LED_OUT =1;
  199.    
  200.                 TxBufPtr = TX_BUF_BGN_ADR;    //after sending ,reset ptr
  201. }




欢迎用到的朋友交流,比较普通,但是可以做简单的modbus rtu通信应用的代码, 后面有时候就会继续优化改进.目前是一个简易可用的实现版本.















            
gejigeji521 发表于 2016-12-11 18:57 | 显示全部楼层
对这方面真心不熟悉,好好研究一下楼主分享的资料学习一下。
henryyam 发表于 2016-12-11 19:52 | 显示全部楼层
谢谢分享!先收藏了
 楼主| Harvard 发表于 2016-12-16 09:01 | 显示全部楼层
希望 可以对需要的朋友有用
捉虫天师 发表于 2016-12-16 18:12 | 显示全部楼层
收下了,这一块儿太深奥了。
a20084666 发表于 2017-1-10 00:07 | 显示全部楼层
写的不错,
a20084666 发表于 2017-1-10 00:08 | 显示全部楼层
楼主你用的串口助手不好,有bug,建议用sscom
 楼主| Harvard 发表于 2017-1-10 10:33 | 显示全部楼层
谢谢 提醒  哈哈  已经用了
aksu 发表于 2020-8-10 09:11 | 显示全部楼层
你好,我在使用新唐 m051 用的是3.0的库,做串口功能时遇到些问题向你请教一下。
1.硬件已经外接485芯片,软件要配置485功能吗、还是配置普通串功能就好了?看芯片资料上有几种模式可配置,不是很明白、期待你的回复。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

个人签名:单片机工作者

74

主题

1732

帖子

21

粉丝
快速回复 在线客服 返回列表 返回顶部