GD32F103实现modbus协议

[复制链接]
2935|29
手机看帖
扫描二维码
随时随地手机跟帖
zeshoufx|  楼主 | 2020-12-18 17:16 | 显示全部楼层 |阅读模式
一、modbus
Modbus是一种串行通信协议,是Modicon公司(现在的施耐德电气Schneider Electric)于1979年为使用可编程逻辑控制器(PLC)通信而发表。
Modbus已经成为工业领域通信协议的业界标准(De facto),并且现在是工业电子设备之间常用的连接方式。在中国,Modbus 已经成为国家标准。
标准编号:GB/T19582-2008
标准名称:《基于 Modbus 协议的工业自动化网络规范》
分 3 个部分:
《GB/T 19582.1-2008 第 1 部分:Modbus 应用协议》
《GB/T 19582.2-2008 第 2 部分:Modbus 协议在串行链路上的实现指南》
《GB/T 19582.3-2008 第 3 部分: Modbus 协议在 TCP/IP 上的实现指南》


使用特权

评论回复
zeshoufx|  楼主 | 2020-12-18 17:18 | 显示全部楼层
二、移植freemodbus

下载:要获取 FreeModbus 源码,我们可以直接从他们的官网:
https://www.embedded-solutions.at/en/freemodbus/获取

freemodbus-v1.6.zip

4.78 MB

源码

使用特权

评论回复
zeshoufx|  楼主 | 2020-12-18 17:20 | 显示全部楼层
zeshoufx 发表于 2020-12-18 17:18
二、移植freemodbus

下载:要获取 FreeModbus 源码,我们可以直接从他们的官网:

1、函数注册
BOOL
xMBPortTimersInit( USHORT usTim1Timerout50us )
{
        timer_parameter_struct timer_struct;
        uint16_t  PrescalerValue=0;
       
        rcu_periph_clock_enable(RCU_TIMER3);
        PrescalerValue = (uint16_t) (SystemCoreClock / 20000) - 1;
       
        timer_struct.alignedmode=TIMER_COUNTER_EDGE;
        timer_struct.clockdivision=TIMER_CKDIV_DIV1;
        timer_struct.counterdirection=TIMER_COUNTER_UP;
        timer_struct.period=usTim1Timerout50us;
        timer_struct.prescaler=PrescalerValue;
        timer_struct.repetitioncounter=0;
        timer_init(TIMER3,&timer_struct);
       
        timer_autoreload_value_config(TIMER3,usTim1Timerout50us);
        nvic_irq_enable(TIMER3_IRQn,0,3);
       
        timer_interrupt_flag_clear(TIMER3,TIMER_INT_FLAG_UP);
        timer_interrupt_disable(TIMER3,TIMER_INT_UP);
        timer_disable(TIMER3);
    return TRUE;
}


使用特权

评论回复
zeshoufx|  楼主 | 2020-12-18 17:21 | 显示全部楼层

2、vMBPortTimersEnable和vMBPortTimersDisable函数注册
 void
vMBPortTimersEnable(  )
{
    /* Enable the timer with the timeout passed to xMBPortTimersInit( ) */
        timer_interrupt_flag_clear(TIMER3,TIMER_INT_FLAG_UP);
        timer_interrupt_enable(TIMER3,TIMER_INT_UP);
        timer_counter_value_config(TIMER3,0);
        timer_enable(TIMER3);
}
 void
vMBPortTimersDisable(  )
{
    /* Disable any pending timers. */
        timer_disable(TIMER3);
        timer_counter_value_config(TIMER3,0);
        timer_interrupt_disable(TIMER3,TIMER_INT_UP);
        timer_interrupt_flag_clear(TIMER3,TIMER_INT_FLAG_UP);
}


使用特权

评论回复
zeshoufx|  楼主 | 2020-12-18 17:22 | 显示全部楼层
zeshoufx 发表于 2020-12-18 17:21
2、vMBPortTimersEnable和vMBPortTimersDisable函数注册

3、定时器终端函数
void TIMER3_IRQHandler(void)
{
        if(timer_interrupt_flag_get(TIMER3,TIMER_INT_FLAG_UP))
        {
                prvvTIMERExpiredISR();
                timer_interrupt_flag_clear(TIMER3,TIMER_INT_FLAG_UP);
        }
}


使用特权

评论回复
zeshoufx|  楼主 | 2020-12-18 17:23 | 显示全部楼层
zeshoufx 发表于 2020-12-18 17:22
3、定时器终端函数

4、vMBPortSerialEnable函数注册
void
vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable )
{
    /* If xRXEnable enable serial receive interrupts. If xTxENable enable
     * transmitter empty interrupts.
     */
        if(TRUE==xRxEnable)
        {
                usart_interrupt_enable(USART1,USART_INT_RBNE);
                gpio_bit_reset(GPIOA,GPIO_PIN_1);
        }
        else
        {
                usart_interrupt_disable(USART1,USART_INT_RBNE);
                gpio_bit_set(GPIOA,GPIO_PIN_1);
        }
       
        if(TRUE==xTxEnable)
        {
                usart_interrupt_enable(USART1,USART_INT_TBE);
        }
        else
        {
                usart_interrupt_disable(USART1,USART_INT_TBE);
        }
       
}


使用特权

评论回复
zeshoufx|  楼主 | 2020-12-18 17:24 | 显示全部楼层
zeshoufx 发表于 2020-12-18 17:23
4、vMBPortSerialEnable函数注册

5、xMBPortSerialInit函数注册
BOOL
xMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity )
{
        (void)ucPORT;
        (void)ucDataBits;
       
        rcu_periph_clock_enable(RCU_GPIOA);
        rcu_periph_clock_enable(RCU_USART1);
       
        gpio_init(GPIOA,GPIO_MODE_AF_PP,GPIO_OSPEED_50MHZ,GPIO_PIN_2);
        gpio_init(GPIOA,GPIO_MODE_IN_FLOATING,GPIO_OSPEED_50MHZ,GPIO_PIN_3);
       
        gpio_init(GPIOA,GPIO_MODE_OUT_PP,GPIO_OSPEED_50MHZ,GPIO_PIN_1);
       
        usart_baudrate_set(USART1,ulBaudRate);
        usart_stop_bit_set(USART1,USART_STB_1BIT);
        usart_hardware_flow_cts_config(USART1,USART_CTS_DISABLE);
        usart_hardware_flow_rts_config(USART1,USART_RTS_DISABLE);
        usart_transmit_config(USART1,USART_TRANSMIT_ENABLE);
        usart_receive_config(USART1,USART_RECEIVE_ENABLE);
        switch(eParity)
        {
                case MB_PAR_NONE:
                {
                        usart_parity_config(USART1,USART_PM_NONE);
                        usart_word_length_set(USART1,USART_WL_8BIT);
                        usart_interrupt_disable(USART1,USART_INT_PERR);
                        break;
                }
               
                case MB_PAR_ODD:
                {
                        usart_parity_config(USART1,USART_PM_ODD);
                        usart_word_length_set(USART1,USART_WL_9BIT);
                        usart_interrupt_enable(USART1,USART_INT_PERR);
                        break;
                }
               
                case MB_PAR_EVEN:
                {
                        usart_parity_config(USART1,USART_PM_EVEN);
                        usart_word_length_set(USART1,USART_WL_9BIT);
                        usart_interrupt_enable(USART1,USART_INT_PERR);
                        break;
                }
               
                default :break;
        }
       
        usart_enable(USART1);
        nvic_irq_enable(USART1_IRQn,0,0);
    return TRUE;
}


使用特权

评论回复
zeshoufx|  楼主 | 2020-12-18 17:26 | 显示全部楼层
zeshoufx 发表于 2020-12-18 17:24
5、xMBPortSerialInit函数注册

6、发送字节、接收字节、及终端函数
BOOL
xMBPortSerialPutByte( CHAR ucByte )
{
    /* Put a byte in the UARTs transmit buffer. This function is called
     * by the protocol stack if pxMBFrameCBTransmitterEmpty( ) has been
     * called. */
        usart_data_transmit(USART1,ucByte);
        while(usart_flag_get(USART1,USART_FLAG_TC)==RESET){}
    return TRUE;
}
BOOL
xMBPortSerialGetByte( CHAR * pucByte )
{
    /* Return the byte in the UARTs receive buffer. This function is called
     * by the protocol stack after pxMBFrameCBByteReceived( ) has been called.
     */
        *pucByte=usart_data_receive(USART1);
    return TRUE;
}
void USART1_IRQHandler(void)
{
        if(usart_interrupt_flag_get(USART1,USART_INT_FLAG_PERR))
        {
                eMBEnable();
                usart_interrupt_flag_clear(USART1,USART_INT_FLAG_PERR);
        }
        else
        {
                if(usart_interrupt_flag_get(USART1,USART_INT_FLAG_RBNE))
                {
                        prvvUARTRxISR();
                        usart_interrupt_flag_clear(USART1,USART_INT_FLAG_RBNE);
                }
               
                if(usart_interrupt_flag_get(USART1,USART_INT_FLAG_TBE))
                {
                        prvvUARTTxReadyISR();
                        usart_interrupt_flag_clear(USART1,USART_INT_FLAG_TBE);
                }
        }
       
        if(usart_flag_get(USART1,USART_FLAG_ORERR))
        {
                usart_flag_clear(USART1,USART_FLAG_ORERR);
                usart_data_receive(USART1);
        }
}


使用特权

评论回复
zeshoufx|  楼主 | 2020-12-18 17:28 | 显示全部楼层
zeshoufx 发表于 2020-12-18 17:26
6、发送字节、接收字节、及终端函数

7、port.h文件添加内容
#define ENTER_CRITICAL_SECTION( )   INTX_DISABLE()
#define EXIT_CRITICAL_SECTION( )    INTX_ENABLE()



使用特权

评论回复
评论
编程小菜鸟123@ 2023-4-11 11:06 回复TA
Error: L6218E: Undefined symbol INTX_DISABLE (referred from mb.o). 报错内容,头文件是包含了的 
编程小菜鸟123@ 2023-4-11 11:03 回复TA
为什么我的这两句话会报错,好像是识别不了INTX_DISABLE(),楼主有遇到过这个问题吗 
你的灰 2021-2-1 09:01 回复TA
@zeshoufx : __asm void INTX_DISABLE(void) { CPSID I BX LR } __asm void INTX_ENABLE(void) { CPSIE I BX LR } 我指的是这个呢,编译时会报错 
zeshoufx 2021-1-29 20:43 回复TA
@你的灰 :我的bitband.c里没有汇编,, 
你的灰 2021-1-29 16:50 回复TA
楼主大大您好,我想问下这个bitband.c里的汇编部分无法在MDK里编译,您是如何解决这个问题的呢? 
zeshoufx|  楼主 | 2020-12-18 17:28 | 显示全部楼层
zeshoufx 发表于 2020-12-18 17:28
7、port.h文件添加内容

8、demo.c内容
#include "mb.h"
#include "mbport.h"

/* ----------------------- Defines ------------------------------------------*/
#define REG_COILS_START 0x0001
#define REG_COILS_SIZE 16
unsigned char ucRegCoilsBuf[REG_COILS_SIZE / 8]={0X00,0X00};

/* ----------------------- Static variables ---------------------------------*/
#define REG_DISC_START 0x0001
#define REG_DISC_SIZE 16
unsigned char ucRegDiscBuf[REG_DISC_SIZE / 8] = { 0x0F, 0XF0 };

/* ----------------------- Start implementation -----------------------------*/


#define REG_INPUT_START 0x0001
#define REG_INPUT_NREGS 8
static USHORT usRegInputStart = REG_INPUT_START;
static USHORT usRegInputBuf[REG_INPUT_NREGS]=
{0x1111,0x2222,0x3333,0x4444,0x5555,0x6666,0x7777,0x8888};


#define REG_HOLDING_START 0x0001 //保持寄存器起始地址
#define REG_HOLDING_NREGS 8 //保持寄存器数量
USHORT usRegHoldingStart = REG_HOLDING_START;
USHORT usRegHoldingBuf[REG_HOLDING_NREGS]=
{0X1100,0X2200,0X3300,0X4400,0X5500,0X6600,0X7700,0X8800};


eMBErrorCode
eMBRegInputCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs )
{
        eMBErrorCode eStatus = MB_ENOERR;
        int iRegIndex;

        if( ( usAddress >= REG_INPUT_START )
        && ( usAddress + usNRegs <= REG_INPUT_START + REG_INPUT_NREGS ) )
        {
                iRegIndex = ( int )( usAddress - usRegInputStart );
                while( usNRegs > 0 )
                {
                        *pucRegBuffer++ =
                        ( unsigned char )( usRegInputBuf[iRegIndex] >> 8 );
                        *pucRegBuffer++ =
                        ( unsigned char )( usRegInputBuf[iRegIndex] & 0xFF );
                        iRegIndex++;
                        usNRegs--;
                }
        }
                else
                {
                        eStatus = MB_ENOREG;
                }

                return eStatus;
}


eMBErrorCode
eMBRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs,
eMBRegisterMode eMode )
{
        eMBErrorCode eStatus = MB_ENOERR;
        int iRegIndex;

        if((usAddress>=REG_HOLDING_START) && (usAddress+usNRegs<=REG_HOLDING_START+REG_HOLDING_NREGS) )
        {
                iRegIndex = ( int )( usAddress - usRegHoldingStart );
                switch ( eMode )
                {
                case MB_REG_READ:
                                while( usNRegs > 0 )
                                {
                                        *pucRegBuffer++ = ( unsigned char )( usRegHoldingBuf[iRegIndex] >> 8 );
                                        *pucRegBuffer++ = ( unsigned char )( usRegHoldingBuf[iRegIndex] & 0xFF );
                                        iRegIndex++;

                                        usNRegs--;
                                }
                                break;

                case MB_REG_WRITE:
                                 while( usNRegs > 0 )
                                 {
                                        usRegHoldingBuf[iRegIndex] = *pucRegBuffer++ << 8;
                                        usRegHoldingBuf[iRegIndex] |= *pucRegBuffer++;
                                        iRegIndex++;
                                        usNRegs--;
                                }
                                break;
                }
        }
        else
        {
                eStatus = MB_ENOREG;
        }
        return eStatus;
}


eMBErrorCode
eMBRegCoilsCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNCoils,eMBRegisterMode eMode )
{
        eMBErrorCode eStatus = MB_ENOERR;

        int iNCoils = ( int )usNCoils;

        unsigned short usBitOffset;

        if( ( usAddress >= REG_COILS_START ) &&( usAddress + usNCoils <= REG_COILS_START + REG_COILS_SIZE ) )
        {
                usBitOffset = ( unsigned short )( usAddress - REG_COILS_START );
                switch ( eMode )
                {
                        /* Read current values and pass to protocol stack. */
                        case MB_REG_READ:
                        while( iNCoils > 0 )
                        {
                                *pucRegBuffer++ = xMBUtilGetBits( ucRegCoilsBuf, usBitOffset, ( unsigned char )( iNCoils > 8 ? 8 :iNCoils ) );
                        iNCoils -= 8;
                        usBitOffset += 8;
                        }
                        break;

        /* Update current register values. */
                        case MB_REG_WRITE:
                        while( iNCoils > 0 )
                        {
                                xMBUtilSetBits( ucRegCoilsBuf, usBitOffset,( unsigned char )( iNCoils >8 ? 8 : iNCoils ), *pucRegBuffer++ );
                                iNCoils -= 8;
                                usBitOffset += 8;
                        }
                        break;
                }
       
        }
        else
        {
                eStatus = MB_ENOREG;
        }
        return eStatus;
}

eMBErrorCode
        eMBRegDiscreteCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNDiscrete )
        {
                eMBErrorCode eStatus = MB_ENOERR;
                short iNDiscrete = ( short )usNDiscrete;
                unsigned short usBitOffset;
       
                /* Check if we have registers mapped at this block. */
                if( (usAddress >= REG_DISC_START) &&( usAddress + usNDiscrete<= REG_DISC_START + REG_DISC_SIZE ))
        {
                usBitOffset = (unsigned short)( usAddress - REG_DISC_START );
                while( iNDiscrete > 0 )
        {
                *pucRegBuffer++ =xMBUtilGetBits(ucRegDiscBuf, usBitOffset, (unsigned char)( iNDiscrete >8?8: iNDiscrete ) );
                iNDiscrete -= 8;
                usBitOffset+=8;
        }
        }
        else
        {
                eStatus = MB_ENOREG;//illegal register address
        }
        return eStatus;
}


使用特权

评论回复
zeshoufx|  楼主 | 2020-12-18 17:30 | 显示全部楼层

9、03功能码验证:可以发现返回8个寄存器值与demo.c的值一样
375fdc768a7edad.png

使用特权

评论回复
zeshoufx|  楼主 | 2020-12-18 17:33 | 显示全部楼层
zeshoufx 发表于 2020-12-18 17:30
9、03功能码验证:可以发现返回8个寄存器值与demo.c的值一样

10、需要注意的是,为保证串口不乱码,PLL倍频系数不得大于16,需要修改时钟配置,外部晶振不分频
系统时钟设置为96Mhz
RCU_CFG0 &= ~(RCU_CFG0_PLLSEL | RCU_CFG0_PREDV0);
    RCU_CFG0 |= (RCU_PLLSRC_HXTAL );

    /* CK_PLL = (CK_HXTAL/2) * 24 = 96 MHz */
    RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4);
    RCU_CFG0 |= RCU_PLL_MUL12;


使用特权

评论回复
zeshoufx|  楼主 | 2020-12-18 17:39 | 显示全部楼层
zeshoufx 发表于 2020-12-18 17:33
10、需要注意的是,为保证串口不乱码,PLL倍频系数不得大于16,需要修改时钟配置,外部晶振不分频
系统时 ...

11、工程demo
串口采用usart1,采用RS485方式,其他功能码也测试通过
354665fdc7873a31af.png

3-freemodbus.zip

7.75 MB

工程文件

使用特权

评论回复
一点点0321| | 2021-1-31 23:52 | 显示全部楼层
工程文件已下载。感谢

使用特权

评论回复
一点点0321| | 2021-1-31 23:52 | 显示全部楼层
测试看看可不可以

使用特权

评论回复
一点点0321| | 2021-1-31 23:53 | 显示全部楼层

使用特权

评论回复
天越萍踪| | 2021-2-20 15:46 | 显示全部楼层

工程文件已下载。感谢

使用特权

评论回复
fieldpeng| | 2021-6-7 15:18 | 显示全部楼层
主机和从机握手,一直发送数据,该如何实现呢

使用特权

评论回复
bestwell| | 2023-4-13 15:40 | 显示全部楼层
怎么利用modbus协议实现上位机与GD32F103的通讯

使用特权

评论回复
bestwell| | 2023-4-13 20:25 | 显示全部楼层
工控上用的比较多的是modbus协议。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

66

主题

1945

帖子

14

粉丝