不多说, 玩工控的对modbus rtu都很熟悉了,本人也是经常使用,一路从51,AVR, 用到新唐, 单片机在变,代码不变.下面要分享的一个简单实用的代码就是基于C的modbus rtu 从机读写的代码, 因为是从老的 8位机上的c代码移植而来,因此,在串口处理和超时处理, CRC校验上,还是力求可移植性 ,并没有过多的与硬件关联,利用上新唐M0的特性.
后面有时间的话,准备把自己的代码全部修改为适合新唐mcu特点,发灰新唐MCU特性的特有代码;代码如下:
一, 串口的基本通信部分, 说明: 代码是基于新唐2.0库编写,后期因为3.0库出来以后,部分功能使用到了3.0库,可能有些混乱.
-
- u16 _crc16_update0(u16 crc, u08 a)
- {
- int i;
- crc ^= a;
- for (i = 0; i < 8; ++i)
- {
- if (crc & 1)
- crc = (crc >> 1) ^ 0xA001;
- else
- crc = (crc >> 1);
- }
- return crc;
- }
- /*
- ** 串行口初始化
- ** baudrate取值为: 48 96 192
- */
- void usart0_init(u16 baudrate)
- {
- /* Unlock protected registers */
- SYS_UnlockReg();
-
- /* Reset UART0 module */
- SYS_ResetModule(UART0_RST);
-
- /* Enable UART clock */
- CLK_EnableModuleClock(UART0_MODULE);
- /*---------------------------------------------------------------------------------------------------------*/
- /* Init UART */
- /*---------------------------------------------------------------------------------------------------------*/
- /* 选择uart功能 */
- UART0->FUN_SEL = UART_FUNC_SEL_UART;
- /* Tx FIFO Reset & Rx FIFO Reset & FIFO Mode Enable */
- //_UART_FLUSH_FIFO(UART0,UART_FCR_TFR_Msk | UART_FCR_RFR_Msk);
- UART0->FCR |=UART_FCR_TFR_Msk | UART_FCR_RFR_Msk;
- /* 选择RX FIFO的阀值为1BYTE*/
- _UART_SET_RX_TRG_LEV(UART0,UART_FCR_RFITL_1BYTE);
-
- if( baudrate != 9600 && baudrate != 19200 && baudrate != 4800&&baudrate != 2400 ) baudrate = 2400;//默认值
- /* Set 115200 baudrate according to 50MHz system clock */
- //UART0->BAUD = UART_BAUD_MODE2 | UART_BAUD_MODE2_DIVIDER(__IRC22M, baudrate);
- UART0->BAUD = UART_BAUD_MODE2 | UART_BAUD_MODE2_DIVIDER(__HXT, baudrate);
-
- /* 设置串口的工作方式 */
- _UART_SET_DATA_FORMAT(UART0, UART_WORD_LEN_8 | UART_PARITY_NONE | UART_STOP_BIT_1);
-
-
- /* Lock protected registers */
- SYS_LockReg();
- /* Enable RDA\RLS\Time-out Interrupt */
- UART_ENABLE_INT(UART0, (UART_IER_RDA_IEN_Msk ));
-
- /* 开串口中断 */
- NVIC_EnableIRQ(UART02_IRQn);
- }
- /*
- * 通信辅助功能控制开关
- */
-
- void usart0_dis_recv(void)
- {
- _UART_DISABLE_INT(UART0,UART_IER_RDA_IEN_Msk);
- //_UART_RS485_SET_RXDISABLE(UART0);
- UART0->FCR |= UART_FCR_RX_DIS_Msk; /* Disable RS485 RX*/
-
- }
- void usart0_en_recv (void)
- {
- _UART_ENABLE_INT(UART0,UART_IER_RDA_IEN_Msk);
- //_UART_RS485_CLEAR_RXDISABLE(UART0);
- UART0->FCR &= ~ UART_FCR_RX_DIS_Msk; /* Enable RS485 RX */
- //UART0_SEND = 0;
- }
- /*超时定时器初始化及其开关控制*/
- #define T3_TICK_MIN 1000000/(F_CPU/1024)
- #define T3_TICK 1000 //1ms
- #define TIMER3_BGN_VAL (255-T3_TICK/T3_TICK_MIN)
- //static u08 CNT_t15=0; //1.5字符时间
- //static u08 CNT_t35=0; //3.5字符时间
- static u08 SYS_t3_cnt_frame=0; //时间间隔计数器,用于1.5 和3.5字符时间判断
- u08 SYS_t3_on = OFF ; //用于1.5 3.5字符判断的软定时器 开关.
- void timer3_on(void)
- {
- SYS_t3_cnt_frame = 0 ;
- SYS_t3_on = ON;
- }
- void timer3_off(void)
- {
- SYS_t3_on = OFF;
- /** 帧间隔控制 */
- SYS_t3_cnt_frame = 0;
- }
- /**
- * [url=home.php?mod=space&uid=247401]@brief[/url] Timer-3 IRQ
- *
- * @param None
- *
- * [url=home.php?mod=space&uid=266161]@return[/url] None
- *
- * [url=home.php?mod=space&uid=1543424]@Details[/url] The TIMER2 default IRQ, declared in startup_M051Series.s.
- */
- void TMR3_IRQHandler(void)
- {
- /* Clear TIMER2 Timeout Interrupt Flag */
- _TIMER_CLEAR_CMP_INT_FLAG(TIMER3);
- /* ***********************1- 串口0:超时处理 ***************************/
- if( SYS_t3_on == ON )
- {
- SYS_t3_cnt_frame++;
-
- if ( SYS_t3_cnt_frame > T35) //a frame was finished
- {
- usart0_dis_recv(); //禁止接收中断
- timer3_off () ; //tunr off timer;
- SYS_t3_cnt_frame = 0 ; //reset timer
- RxBufPtr--; //调整指针
- fFrameDone = OK; //frame is completed
-
- LED_DTU = 1;
- }
- }
- /* 定时器关,还未进行帧间隔判断 */
- else
- {
- SYS_t3_cnt_frame = 0;
- }
- /* ***********************2- 串口2:超时处理 ***************************/
- /* 直接调用定时器2的回调函数 */
- usart2_timeout();
- }
- u08 RxBuf[MAX_BUF_LEN]={0,1,2,3,4};
- u08 TxBuf[MAX_BUF_LEN];
- u08 RxBufPtr = RX_BUF_BGN_ADR;
- u08 TxBufPtr = TX_BUF_BGN_ADR1;
- BOOL fIsRXING = NO; //is receiving now
- BOOL fIsTXING = NO; //is transsending now
- BOOL fRxBufFull = NO;
- BOOL fTxBufFull = NO;
- BOOL fFrameDone = NO; //one frame finished
- BOOL fFrameBad = NO; //frame was broken
- u16 CRC = 0xFFFF; //CRC初始值
- /*接收中断*/
- void UART02_IRQHandler( void )
- {
- /* 1-定义临时变量获取中断标志 */
- uint32_t u32IntSts= UART0->ISR;
-
- /* 2- 判断是否是RDA中断 */
- // 2-1:串口0处理
- if( UART_GET_INT_FLAG(UART0,UART_ISR_RDA_INT_Msk) )
- {
- /* 通信指示灯处理 */
- LED_DTU = 0;
-
- /* 查询串口的RDA */
- while( UART_IS_RX_READY(UART0) )
- {
- /* Get the character from UART Buffer
- * BSP接收数据
- */
- RxBuf[RxBufPtr] = UART_READ(UART0);
- /* 复位定时器 */
- SYS_t3_cnt_frame = 0 ;
- /*new frame coming*/
- if( RxBufPtr == RX_BUF_BGN_ADR )//新的一帧开始
- {
- CRC = 0xffff ; //准备crc计算,
- timer3_on(); //开启定时器计时
- }
- /*with a frame and RxBuf is not full*/
- /*when we detect a frame was borken according to t15 ,
- * we keep on receiving in order to synchronize the frame
- */
- else if( RxBufPtr < MAX_BUF_LEN -1)
- {
- if( SYS_t3_cnt_frame > T15 && SYS_t3_cnt_frame < T35 )
- {
- SYS_t3_cnt_frame = 0 ; //reset timer
- fFrameBad = TRUE; //frame is broken
- }
- }
- /*RxBuf full */
- /*
- **
- */
- else if
- ( RxBufPtr >= MAX_BUF_LEN -1 )
- {
- usart0_dis_recv() ; //stop receving
- timer3_off () ; //tunr off timer;
- fRxBufFull = YES ; //缓冲区已满
- fFrameBad = TRUE; //frame is broken
- RxBufPtr --; //指针保持不变,防止破坏其他数据区
- LED_DTU = 1;
- }
- CRC = _crc16_update0( CRC,RxBuf[RxBufPtr] );
- RxBufPtr++; //adjust pointer
- }//end while
-
- }//end if
- // END 2-1:串口0处理
-
- // 2-1:串口2处理
- UART2_IRQHandler();
-
- }
- /*查询发送*/
- void usart0_send_byte( u08 txbyte )
- {
- //UART0_SEND = 1;
- UART_WRITE( UART0,txbyte );
- UART_WAIT_TX_EMPTY( UART0 );
- }
二, MODBUS RTU解析处理部分;
2.1 首先构造modbus协议的通信协议(相当于用户应用层的 ),就是规定多少号寄存器,是代表什么变量,取值范围等等;
/* Modbus地址与实际内部变量的映射表 */
const u08 * modbus_tab[] =
{
/* 2- 只读区域: 地址: 400001 - 400080 ,协议中规定的各种数据地址 */
MODBUS_ADDR_TAB,
};
MODBUS_ADDR_TAB,是一个特殊处理手段, 完成了modbus线性寄存器地址与散落在RAM里面的各个变量的对应工作,适合于应用协议数据量不是特别多的情况.
具体的构造如下,笨办法,谅解.
- /* 通信点表与实际的变量映射 ,空白处用Space16替代 */
- #define ADR400001 SYS_longitude_hi //经度
- #define ADR400002 SYS_longitude_low // 纬度
- #define ADR400003 SYS_latitude_hi
- #define ADR400004 SYS_latitude_low
- #define ADR400005 SYS_runstate
- #define ADR400006 SYS_current
- #define ADR400007 SYS_voltage
- #define ADR400008 SYS_kw
- #define ADR400009 SYS_oil_pressure
- #define ADR400010 SYS_oil_temperatue
-
- #define ADR400011 SYS_gas_level
- #define ADR400012 SYS_current_A
- #define ADR400013 SYS_current_B
- #define ADR400014 SYS_current_C
- #define ADR400015 Space16
- #define ADR400016 SYS_total_alarm
- #define ADR400017 SYS_alarm1
- #define ADR400018 SYS_alarm2
- #define ADR400019 SYS_alarm3
- #define ADR400020 SYS_alarm4
- #define ADR400021 SYS_alarm5
- #define ADR400022 SYS_alarm6
- #define ADR400023 SYS_alarm7
- #define ADR400024 SYS_alarm8
- #define ADR400025 SYS_alarm9
- #define ADR400026 SYS_alarm10
- #define ADR400027 SYS_alarm11
- #define ADR400028 SYS_alarm12
- #define ADR400029 SYS_alarm13
- #define ADR400030 SYS_alarm14
-
- #define ADR400031 SYS_alarm15
- #define ADR400032 SYS_his_ID
- #define ADR400033 SYS_his_begin_year
- #define ADR400034 SYS_his_begin_month
- #define ADR400035 SYS_his_begin_day
- #define ADR400036 SYS_his_begin_hour
- #define ADR400037 SYS_his_begin_minute
- #define ADR400038 SYS_his_begin_second
- #define ADR400039 SYS_his_end_year
- #define ADR400040 SYS_his_end_month
- #define ADR400041 SYS_his_end_day
- #define ADR400042 SYS_his_end_hour
- #define ADR400043 SYS_his_end_minute
- #define ADR400044 SYS_his_end_second
- #define ADR400045 SYS_his_length_hour
- #define ADR400046 SYS_his_length_minute
- #define ADR400047 SYS_his_kwh_low //注意顺序
- #define ADR400048 SYS_his_kwh_hi
-
- #define ADR400049 SYS_his_gas_cost
-
- #define ADR400050 SYS_his_longitude_hi
- #define ADR400051 SYS_his_longitude_low
- #define ADR400052 SYS_his_latitude_hi
- #define ADR400053 SYS_his_latitude_low
- #define ADR400054 Space16 //预留1
- #define ADR400055 Space16
- #define ADR400056 Space16
- #define ADR400057 Space16
- #define ADR400058 Space16
- #define ADR400059 SYS_his_alarm1
- #define ADR400060 SYS_his_alarm2
- #define ADR400061 SYS_his_alarm3
- #define ADR400062 SYS_his_alarm4
- #define ADR400063 SYS_his_alarm5
- #define ADR400064 SYS_his_alarm6
- #define ADR400065 SYS_his_alarm7
- #define ADR400066 SYS_his_alarm8
- #define ADR400067 SYS_his_alarm9
- #define ADR400068 SYS_his_alarm10
- #define ADR400069 SYS_his_alarm11
- #define ADR400070 SYS_his_alarm12
- #define ADR400071 SYS_his_alarm13
- #define ADR400072 SYS_his_alarm14
- #define ADR400073 SYS_his_alarm15
- #define ADR400074 SYS_his_handshake
- #define ADR400075 Space16
- #define ADR400076 Space16
- #define ADR400077 Space16
- #define ADR400078 Space16
- #define ADR400079 Space16
- #define ADR400080 Space16
- /* 2- 利用宏定义通信地址表格: 防止源码中太长,影响c代码可读性
- * 也可将本文件通过include 插入到modbus.c文件中
- */
- #define MODBUS_ADDR_TAB \
- \
- (u08 *) &SYS_longitude+1, ((u08 *) &SYS_longitude)+0,\
- ((u08 *) &SYS_longitude)+3, ((u08 *) &SYS_longitude)+2,\
- (u08 *) &SYS_latitude+1, ((u08 *) &SYS_latitude)+0,\
- ((u08 *) &SYS_latitude)+3, ((u08 *) &SYS_latitude)+2,\
- &HIGH_( ADR400005 ), &LOW_( ADR400005 ), \
- &HIGH_( ADR400006 ), &LOW_( ADR400006 ), \
- &HIGH_( ADR400007 ), &LOW_( ADR400007 ), \
- &HIGH_( ADR400008 ), &LOW_( ADR400008 ), \
- &HIGH_( ADR400009 ), &LOW_( ADR400009 ), \
- &HIGH_( ADR400010 ), &LOW_( ADR400010 ), \
- &HIGH_( ADR400011 ), &LOW_( ADR400011 ), \
- &HIGH_( ADR400012 ), &LOW_( ADR400012 ), \
- &HIGH_( ADR400013 ), &LOW_( ADR400013 ), \
- &HIGH_( ADR400014 ), &LOW_( ADR400014 ), \
- &HIGH_( ADR400015 ), &LOW_( ADR400015 ), \
- &HIGH_( ADR400016 ), &LOW_( ADR400016 ), \
- &HIGH_( ADR400017 ), &LOW_( ADR400017 ), \
- &HIGH_( ADR400018 ), &LOW_( ADR400018 ), \
- &HIGH_( ADR400019 ), &LOW_( ADR400019 ), \
- &HIGH_( ADR400020 ), &LOW_( ADR400020 ), \
- &HIGH_( ADR400021 ), &LOW_( ADR400021 ), \
- &HIGH_( ADR400022 ), &LOW_( ADR400022 ), \
- &HIGH_( ADR400023 ), &LOW_( ADR400023 ), \
- &HIGH_( ADR400024 ), &LOW_( ADR400024 ), \
- &HIGH_( ADR400025 ), &LOW_( ADR400025 ), \
- &HIGH_( ADR400026 ), &LOW_( ADR400026 ), \
- &HIGH_( ADR400027 ), &LOW_( ADR400027 ), \
- &HIGH_( ADR400028 ), &LOW_( ADR400028 ), \
- &HIGH_( ADR400029 ), &LOW_( ADR400029 ), \
- &HIGH_( ADR400030 ), &LOW_( ADR400030 ), \
- &HIGH_( ADR400031 ), &LOW_( ADR400031 ), \
- &HIGH_( ADR400032 ), &LOW_( ADR400032 ), \
- &HIGH_( ADR400033 ), &LOW_( ADR400033 ), \
- &HIGH_( ADR400034 ), &LOW_( ADR400034 ), \
- &HIGH_( ADR400035 ), &LOW_( ADR400035 ), \
- &HIGH_( ADR400036 ), &LOW_( ADR400036 ), \
- &HIGH_( ADR400037 ), &LOW_( ADR400037 ), \
- &HIGH_( ADR400038 ), &LOW_( ADR400038 ), \
- &HIGH_( ADR400039 ), &LOW_( ADR400039 ), \
- &HIGH_( ADR400040 ), &LOW_( ADR400040 ), \
- &HIGH_( ADR400041 ), &LOW_( ADR400041 ), \
- &HIGH_( ADR400042 ), &LOW_( ADR400042 ), \
- &HIGH_( ADR400043 ), &LOW_( ADR400043 ), \
- &HIGH_( ADR400044 ), &LOW_( ADR400044 ), \
- &HIGH_( ADR400045 ), &LOW_( ADR400045 ), \
- &HIGH_( ADR400046 ), &LOW_( ADR400046 ), \
- &HIGH_( ADR400047 ), &LOW_( ADR400047 ), \
- &HIGH_( ADR400048 ), &LOW_( ADR400048 ), \
- &HIGH_( ADR400049 ), &LOW_( ADR400049 ), \
- (u08 *) &SYS_his_longitude+1, ((u08 *) &SYS_his_longitude)+0,\
- ((u08 *) &SYS_his_longitude)+3, ((u08 *) &SYS_his_longitude)+2,\
- (u08 *) &SYS_his_latitude+1, ((u08 *) &SYS_his_latitude)+0,\
- ((u08 *) &SYS_his_latitude)+3, ((u08 *) &SYS_his_latitude)+2,\
- &HIGH_( ADR400054 ), &LOW_( ADR400054 ), \
- &HIGH_( ADR400055 ), &LOW_( ADR400055 ), \
- &HIGH_( ADR400056 ), &LOW_( ADR400056 ), \
- &HIGH_( ADR400057 ), &LOW_( ADR400057 ), \
- &HIGH_( ADR400058 ), &LOW_( ADR400058 ), \
- &HIGH_( ADR400059 ), &LOW_( ADR400059 ), \
- &HIGH_( ADR400060 ), &LOW_( ADR400060 ), \
- &HIGH_( ADR400061 ), &LOW_( ADR400061 ), \
- &HIGH_( ADR400062 ), &LOW_( ADR400062 ), \
- &HIGH_( ADR400063 ), &LOW_( ADR400063 ), \
- &HIGH_( ADR400064 ), &LOW_( ADR400064 ), \
- &HIGH_( ADR400065 ), &LOW_( ADR400065 ), \
- &HIGH_( ADR400066 ), &LOW_( ADR400066 ), \
- &HIGH_( ADR400067 ), &LOW_( ADR400067 ), \
- &HIGH_( ADR400068 ), &LOW_( ADR400068 ), \
- &HIGH_( ADR400069 ), &LOW_( ADR400069 ), \
- &HIGH_( ADR400070 ), &LOW_( ADR400070 ), \
- &HIGH_( ADR400071 ), &LOW_( ADR400071 ), \
- &HIGH_( ADR400072 ), &LOW_( ADR400072 ), \
- &HIGH_( ADR400073 ), &LOW_( ADR400073 ), \
- &HIGH_( ADR400074 ), &LOW_( ADR400074 ), \
- &HIGH_( ADR400075 ), &LOW_( ADR400075 ), \
- &HIGH_( ADR400076 ), &LOW_( ADR400076 ), \
- &HIGH_( ADR400077 ), &LOW_( ADR400077 ), \
- &HIGH_( ADR400078 ), &LOW_( ADR400078 ), \
- &HIGH_( ADR400079 ), &LOW_( ADR400079 ), \
- &HIGH_( ADR400080 ), &LOW_( ADR400080 )
- #define MODBUS_ADDR_MAX 80
-
|