初中生
最后登录1970-1-1
在线时间 小时
注册时间2022-11-2
|
一、简介
Modbus是一种串行通信协议,是Modicon公司(现在的施耐德电气 Schneider Electric)于1979年为使用可编程逻辑控制器(PLC)通信而发表。
Modbus已经成为工业领域通信协议的业界标准(De facto),并且现在是工业电子设备之间常用的连接方式。
Modbus通信协议是一种主从的协议,在一个Modbus网络里面只能有一个主机,但是可以有多个从机,从机与从机之间通过地址进行区分,每个从机都具有唯一的设备地址。
每次通讯必须由主机进行发起,从机进行响应。
本次基于开发板的485硬件接口移植Modbus开源协议栈FreeModbus实现Modbus从机。
(FreeModbus目前只能支持从机)
二、FSP驱动配置
FreeModbus库需要用到硬件资源包括:
①485接口(开发板已经板载有TTL转485芯片,对于MCU来说就是配置UART)
②timer定时器
(1)、UART配置
首先打开原理图,确认板载的485接口接在开发板的哪个串口上,引脚号是多少
从原理图可以看到,第一路485接在uart5上,端口号为:P501,P502,P503
因此FSP配置UART5如下:
(2)、配置timer
为了能够与FreeModbus对接,驱动需要配置时间基准为50us的定时器,并开启溢出中断。
根据手册,RA6M5一共有10个通用定时器,其中0~3为32位定时器,其余为16位定时器,如下:
我这里就选择timer3来进行配置:
另外,可以根据需要再配置一路uart作为调试串口使用,我配置了uart4作为调试串口,配置方法与uart5基本类似。
现在,需要配置的硬件都已经配置好了(别忘了点击按钮生成代码哦),可以正式开始移植了。
三、移植FreeModbus
(1)、下载FreeModbus源码
源码下载地址:https://www.embedded-experts.at/en/freemodbus-downloads/
(2)、将源码加入工程
源码解压后,得到如下文件和文件夹:
我们需要的是modbus这个文件夹,和demo->BARE下的port文件夹。
将这个两个文件夹添加到工程中:
添加头文件路径“
(3)、修改portserial.c文件
portserial.c文件主要是对UART驱动的对接,需要对接的函数主要有4个。
① void vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable )
用于对串口发送和接收进行使能或者失能
② BOOL xMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity )
用于对485进行初始化
③BOOLxMBPortSerialPutByte( CHAR ucByte )
用于485发送一个字节
④BOOLxMBPortSerialGetByte( CHAR * pucByte )
用于485读取一个字节
另外,我将uart的回调函数也是放在这个文件中。
对接后完整portserial.c文件如下:
- /*
- * FreeModbus Libary: BARE Port
- * Copyright (C) 2006 Christian Walter <wolti@sil.at>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * File: $Id$
- */
- #include "port.h"
- #include "uart.h"
- #include "hal_data.h"
- /* ----------------------- Modbus includes ----------------------------------*/
- #include "mb.h"
- #include "mbport.h"
- /* ----------------------- static functions ---------------------------------*/
- static void prvvUARTTxReadyISR( void );
- static void prvvUARTRxISR( void );
- /* ----------------------- Start implementation -----------------------------*/
- void
- vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable )
- {
- /* If xRXEnable enable serial receive interrupts. If xTxENable enable
- * transmitter empty interrupts.
- */
- if (xRxEnable == TRUE)
- {
- uart5_rx_state = UART5_RX_ENABLE;
- }
- else
- {
- uart5_rx_state = UART5_RX_DISABLE;
- }
- if (xTxEnable == TRUE)
- {
- uart5_tx_state = UART5_TX_ENABLE;
- }
- else
- {
- uart5_tx_state = UART5_TX_DISABLE;
- }
- }
- BOOL
- xMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity )
- {
- uart5_init();
- return TRUE;
- }
- 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. */
- R_SCI_UART_Write(&g_uart5_ctrl, &ucByte, 1);
- 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.
- */
- //R_SCI_UART_Read (&g_uart5_ctrl, pucByte, 1);
- *pucByte = uart5_recv_data;
- return TRUE;
- }
- /* Create an interrupt handler for the transmit buffer empty interrupt
- * (or an equivalent) for your target processor. This function should then
- * call pxMBFrameCBTransmitterEmpty( ) which tells the protocol stack that
- * a new character can be sent. The protocol stack will then call
- * xMBPortSerialPutByte( ) to send the character.
- */
- static void prvvUARTTxReadyISR( void )
- {
- pxMBFrameCBTransmitterEmpty( );
- }
- /* Create an interrupt handler for the receive interrupt for your target
- * processor. This function should then call pxMBFrameCBByteReceived( ). The
- * protocol stack will then call xMBPortSerialGetByte( ) to retrieve the
- * character.
- */
- static void prvvUARTRxISR( void )
- {
- pxMBFrameCBByteReceived( );
- }
- /* uart4中断回调函数 */
- void uart5_callback (uart_callback_args_t * p_args)
- {
- /* Handle the UART event */
- switch (p_args->event)
- {
- /* Received a character */
- case UART_EVENT_RX_CHAR:
- {
- if (uart5_rx_state == UART5_RX_ENABLE)
- {
- uart5_recv_data = p_args->data;
- //R_SCI_UART_Write(&g_uart4_ctrl, &uart5_recv_data, 1);
- prvvUARTRxISR();
- }
- break;
- }
- /* Receive complete */
- case UART_EVENT_RX_COMPLETE:
- {
- break;
- }
- /* Transmit complete */
- case UART_EVENT_TX_COMPLETE:
- {
- //uart4_tx_complete = true;
- if (uart5_tx_state == UART5_TX_ENABLE)
- {
- prvvUARTTxReadyISR();
- }
- break;
- }
- default:
- {
- }
- }
- }
复制代码 (4)、修改porttimer.c文件
该文件需要对接3个函数。
① BOOL xMBPortTimersInit( USHORT usTim1Timerout50us )
用于对timer进行初始化
② void vMBPortTimersEnable( )
用于使能timer,让timer开始计数
③ void vMBPortTimersDisable( )
用于失能timer,让timer停止计数
另外,timer中断的回调函数也是放在这个文件中。
对接后完整porttimer.c文件如下:
- /*
- * FreeModbus Libary: BARE Port
- * Copyright (C) 2006 Christian Walter <wolti@sil.at>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * File: $Id$
- */
- /* ----------------------- Platform includes --------------------------------*/
- #include "port.h"
- /* ----------------------- Modbus includes ----------------------------------*/
- #include "mb.h"
- #include "mbport.h"
- #include "hal_data.h"
- #include <stdio.h>
- /* ----------------------- static functions ---------------------------------*/
- static void prvvTIMERExpiredISR( void );
- /* ----------------------- Start implementation -----------------------------*/
- BOOL
- xMBPortTimersInit( USHORT usTim1Timerout50us )
- {
- /* Initializes the module. */
- fsp_err_t err = R_GPT_Open(&g_timer3_ctrl, &g_timer3_cfg);
- if(err != FSP_SUCCESS)
- {
- printf("gpt3 open error\r\n");
- }
- /* 设置timer的中断周期
- ** 100表示timer的计数时钟是100MHz
- */
- err = R_GPT_PeriodSet(&g_timer3_ctrl, (uint32_t)usTim1Timerout50us * 50U * 100U);
- if(err != FSP_SUCCESS)
- {
- printf("gpt3 periodset error\r\n");
- }
- return TRUE;
- }
- void
- vMBPortTimersEnable( )
- {
- /* Enable the timer with the timeout passed to xMBPortTimersInit( ) */
- fsp_err_t err = R_GPT_Reset (&g_timer3_ctrl);
- if(err != FSP_SUCCESS)
- {
- printf("gpt3 reset error\r\n");
- }
- /* Start the timer. */
- err = R_GPT_Start(&g_timer3_ctrl);
- if(err != FSP_SUCCESS)
- {
- printf("gpt3 start error\r\n");
- }
- }
- void
- vMBPortTimersDisable( )
- {
- /* Disable any pending timers. */
- fsp_err_t err = R_GPT_Stop (&g_timer3_ctrl);
- if(err != FSP_SUCCESS)
- {
- printf("gpt3 stop error\r\n");
- }
- }
- /* Create an ISR which is called whenever the timer has expired. This function
- * must then call pxMBPortCBTimerExpired( ) to notify the protocol stack that
- * the timer has expired.
- */
- static void prvvTIMERExpiredISR( void )
- {
- ( void )pxMBPortCBTimerExpired( );
- }
- /* 定时器中断回调函数. */
- void g_timer3_callback (timer_callback_args_t * p_args)
- {
- if (TIMER_EVENT_CYCLE_END == p_args->event)
- {
- uint8_t led_status;
- R_IOPORT_PinRead (&g_ioport_ctrl, BSP_IO_PORT_04_PIN_00, (bsp_io_level_t *)&led_status);
- if(led_status)
- {
- R_IOPORT_PinWrite(&g_ioport_ctrl, BSP_IO_PORT_04_PIN_00, BSP_IO_LEVEL_LOW);
- }
- else
- {
- R_IOPORT_PinWrite(&g_ioport_ctrl, BSP_IO_PORT_04_PIN_00, BSP_IO_LEVEL_HIGH);
- }
- /* Add application code to be called periodically here. */
- prvvTIMERExpiredISR();
- }
- }
复制代码 (5)、定义Modbus寄存器并实现读写函数
我将寄存器定义和读写函数的实现放在了自己定义的modbus_read_write.c文件中,完整代码如下:
注意:这个几个函数必须实现,否则会报错
- /* ----------------------- Platform includes --------------------------------*/
- #include "port.h"
- /* ----------------------- Modbus includes ----------------------------------*/
- #include "mb.h"
- #include "mbport.h"
- /* ----------------------- Defines ------------------------------------------*/
- #define REG_INPUT_START 1000
- #define REG_INPUT_NREGS 4
- #define REG_HOLDING_START 1000
- #define REG_HOLDING_NREGS 130
- /* ----------------------- Static variables ---------------------------------*/
- static USHORT usRegInputStart = REG_INPUT_START;
- static USHORT usRegInputBuf[REG_INPUT_NREGS] = {1,2,3,4};
- static USHORT usRegHoldingStart = REG_HOLDING_START;
- static USHORT usRegHoldingBuf[REG_HOLDING_NREGS] = {10,11,12,13};
- /* ----------------------- Start implementation -----------------------------*/
- void free_modbus_init(eMBMode eMode, UCHAR ucSlaveAddress, UCHAR ucPort, ULONG ulBaudRate, eMBParity eParity)
- {
- eMBInit( eMode, ucSlaveAddress, ucPort, ulBaudRate, eParity );
- eMBEnable();
- }
- 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 )
- {
- /* Pass current register values to the protocol stack. */
- case MB_REG_READ:
- while( usNRegs > 0 )
- {
- *pucRegBuffer++ = ( unsigned char )( usRegHoldingBuf[iRegIndex] >> 8 );
- *pucRegBuffer++ = ( unsigned char )( usRegHoldingBuf[iRegIndex] & 0xFF );
- iRegIndex++;
- usNRegs--;
- }
- break;
- /* Update current register values with new values from the
- * protocol stack. */
- case MB_REG_WRITE:
- while( usNRegs > 0 )
- {
- usRegHoldingBuf[iRegIndex] = *pucRegBuffer++ << 8;
- usRegHoldingBuf[iRegIndex] |= *pucRegBuffer++;
- iRegIndex++;
- usNRegs--;
- }
- }
- }
- else
- {
- eStatus = MB_ENOREG;
- }
- return eStatus;
- }
- eMBErrorCode
- eMBRegCoilsCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNCoils, eMBRegisterMode eMode )
- {
- return MB_ENOREG;
- }
- eMBErrorCode
- eMBRegDiscreteCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNDiscrete )
- {
- return MB_ENOREG;
- }
复制代码
(6)、修改hal_entry.c文件
在hal_entry()函数中加入Modbus初始化相关内容和状态轮询函数,完整内容如下:
- void hal_entry(void)
- {
- /* TODO: add your own code here */
- /* 初始化GPIO端口 */
- R_IOPORT_Open(&g_ioport_ctrl, &g_bsp_pin_cfg);
- /* 初始化uart4,作为调试串口使S用 */
- uart4_init();
- //uart5_init();//初始化uart5
- printf("Renesas RA6M5 Start...\r\n");
- /* 初始化Modbus端口和状态 */
- free_modbus_init(MB_RTU, 1, 5, 9600, MB_PAR_NONE);
- while(1)
- {
- /* 状态机轮询 */
- ( void )eMBPoll();
- }
- #if BSP_TZ_SECURE_BUILD
- /* Enter non-secure code */
- R_BSP_NonSecureEnter();
- #endif
- }
复制代码
(7)、修改mbrtu.c文件
修改mbrtu.c中eMBRTUSend函数,如果不修改无法触发485发送。
修改后MBRTUSend函数完整内容如下:
- eMBErrorCode
- eMBRTUSend( UCHAR ucSlaveAddress, const UCHAR * pucFrame, USHORT usLength )
- {
- eMBErrorCode eStatus = MB_ENOERR;
- USHORT usCRC16;
- ENTER_CRITICAL_SECTION( );
- /* Check if the receiver is still in idle state. If not we where to
- * slow with processing the received frame and the master sent another
- * frame on the network. We have to abort sending the frame.
- */
- if( eRcvState == STATE_RX_IDLE )
- {
- /* First byte before the Modbus-PDU is the slave address. */
- pucSndBufferCur = ( UCHAR * ) pucFrame - 1;
- usSndBufferCount = 1;
- /* Now copy the Modbus-PDU into the Modbus-Serial-Line-PDU. */
- pucSndBufferCur[MB_SER_PDU_ADDR_OFF] = ucSlaveAddress;
- usSndBufferCount += usLength;
- /* Calculate CRC16 checksum for Modbus-Serial-Line-PDU. */
- usCRC16 = usMBCRC16( ( UCHAR * ) pucSndBufferCur, usSndBufferCount );
- ucRTUBuf[usSndBufferCount++] = ( UCHAR )( usCRC16 & 0xFF );
- ucRTUBuf[usSndBufferCount++] = ( UCHAR )( usCRC16 >> 8 );
- /* Activate the transmitter. */
- eSndState = STATE_TX_XMIT;
- //启动第一次发送,后面才能进完成中断
- xMBPortSerialPutByte((CHAR)*pucSndBufferCur);
- pucSndBufferCur++;
- usSndBufferCount--;
- vMBPortSerialEnable( FALSE, TRUE );
- }
- else
- {
- eStatus = MB_EIO;
- }
- EXIT_CRITICAL_SECTION( );
- return eStatus;
- }
复制代码
(8)、修改mbconfig.h文件
取消对ASCII的支持
(9)、去掉寄存器地址自动加一
需要修改四个文件,分别为mbfunccoils.c、mbfuncdisc.c、mbfuncholding.c、mbfuncinput.c
直接去对应的文件搜索:usRegAddress++;
然后屏蔽掉。
mbfunccoils.c里面有三处
mbfuncdisc.c里面有一处
mbfuncholding.c里面有三处
mbfuncinput.c里面有一处
如果不修改,绝对不能正常工作。
四、测试
编译下载运行,并使用PC端工具Modbus Poll模拟Modbus主机来进行测试。
如下:
经过测试,实测开发板能正确与Modbus Poll相互通讯。
至此,FreeModbus开源库移植成功/
|
|