不多说, 玩工控的对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
|
|