2、GD32 基于RT_Thread移植FreeModbus Slave
·

文章目录
一、准备工作
1.1获取freemodbus v1.6源码
1.2准备一个GD32F30x已经移植好RT_Thread的基础工程
移植参考链接
直接下载基础工程
二、FreeModbus文件移植
FreeModbus文件移植
解压freemodbus-v1.6,将源码目录的以下两个文件夹复制到工程的文件夹中
- freemodbus-v1.6/modbus
- freemodbus-v1.6/demo/BARE

将源码添加到工程中
这里仅展示RTU的实现

添加.h文件路径
进行编译,这里第一次编译会有错误,没有关系
三、修改FreeModbus相关文件
3.1 port.h
- 主机和从机通用该文件;
- 内联inline可能编译器不支持,我的会报错,如果有函数报未定义,可以检查是否内联,删去即可;或开启C99 mode
/*
* 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$
*/
#ifndef _PORT_H
#define _PORT_H
//#include <assert.h>
#include <inttypes.h>
#include <gd32f30x.h>
#define INLINE inline
#define PR_BEGIN_EXTERN_C extern "C" {
#define PR_END_EXTERN_C }
#include <rtthread.h>
#include <rthw.h> // 包含硬件中断相关函数声明
/* 保存中断状态的变量 */
static rt_base_t critical_level;
/* 进入临界区:关闭中断和线程调度 */
#define ENTER_CRITICAL_SECTION() do { \
critical_level = rt_hw_interrupt_disable(); \
rt_enter_critical(); \
} while (0)
/* 退出临界区:恢复中断和线程调度 */
#define EXIT_CRITICAL_SECTION() do { \
rt_exit_critical(); \
rt_hw_interrupt_enable(critical_level); \
} while (0)
typedef uint8_t BOOL;
typedef unsigned char UCHAR;
typedef char CHAR;
typedef uint16_t USHORT;
typedef int16_t SHORT;
typedef uint32_t ULONG;
typedef int32_t LONG;
/* 如果未定义 assert_param 宏,则进行定义 */
#define assert(condition) do{ }while(0);
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
#endif
3.2 portserial.c
/*
* FreeModbus Libary: GD32 Port
* Copyright (C) 2013 Armink <armink.ztl@gmail.com>
*
* 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: portserial.c,v 1.60 2013/08/13 15:07:05 Armink $
*/
#include "port.h"
/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mbport.h"
/* ----------------------- static functions ---------------------------------*/
static void prvvUARTTxReadyISR(void);
static void prvvUARTRxISR(void);
/* ----------------------- Start implementation -----------------------------*/
uint8_t isr_flag;
#define SLAVE_PORT USART0
#define SLVAE_RX_PORT GPIOB
#define SLAVE_RX_PIN GPIO_PIN_7
#define SLVAE_TX_PORT GPIOB
#define SLAVE_TX_PIN GPIO_PIN_6
#define SLAVE_DIR_PORT GPIOB
#define SLAVE_DIR_PIN GPIO_PIN_8
#define SLAVE_RS485_RECEIVE_MODE gpio_bit_reset(SLAVE_DIR_PORT, SLAVE_DIR_PIN)
#define SLAVE_RS485_SEND_MODE gpio_bit_set(SLAVE_DIR_PORT, SLAVE_DIR_PIN)
void vMBPortSerialEnable(BOOL xRxEnable, BOOL xTxEnable)
{
if (xRxEnable)
{
/* 485通信时,等待串口移位寄存器中的数据发送完成后,再去使能485的接收、使能485的发送*/
while (!usart_flag_get(SLAVE_PORT, USART_FLAG_TC))
;
SLAVE_RS485_RECEIVE_MODE;
usart_interrupt_enable(SLAVE_PORT, USART_INT_RBNE);
}
else
{
SLAVE_RS485_SEND_MODE;
usart_interrupt_disable(SLAVE_PORT, USART_INT_RBNE);
}
if (xTxEnable)
{
usart_interrupt_enable(SLAVE_PORT, USART_INT_TBE);
}
else
{
usart_interrupt_disable(SLAVE_PORT, USART_INT_TBE);
}
}
void vMBPortClose(void)
{
usart_interrupt_disable(SLAVE_PORT, USART_INT_RBNE);
usart_interrupt_disable(SLAVE_PORT, USART_INT_TBE);
usart_disable(SLAVE_PORT);
}
// 默认一个从机 串口0 波特率可设置 奇偶检验可设置
BOOL xMBPortSerialInit(UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits,
eMBParity eParity)
{
// 直接使用函数配置 NVIC,不使用结构体
// 设置NVIC优先级分组为Group2:0-3抢占式优先级,0-3的响应式优先级
nvic_priority_group_set(NVIC_PRIGROUP_PRE2_SUB2);
//======================时钟初始化=======================================
rcu_periph_clock_enable(RCU_GPIOB);
rcu_periph_clock_enable(RCU_USART0);
// 重映射
rcu_periph_clock_enable(RCU_AF);
gpio_pin_remap_config(GPIO_USART0_REMAP, ENABLE);
//======================IO初始化=======================================
// SLAVE_PORT_TX
gpio_init(SLVAE_RX_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, SLAVE_TX_PIN);
// SLAVE_PORT_RX
gpio_init(SLVAE_RX_PORT, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, SLAVE_RX_PIN);
// 配置485发送和接收模式
gpio_init(SLAVE_DIR_PORT, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, SLAVE_DIR_PIN);
//======================串口初始化=======================================
usart_deinit(SLAVE_PORT);
usart_baudrate_set(SLAVE_PORT, ulBaudRate);
// 设置校验模式
switch (eParity)
{
case MB_PAR_NONE: // 无校验
usart_parity_config(SLAVE_PORT, USART_PM_NONE);
usart_word_length_set(SLAVE_PORT, USART_WL_8BIT);
break;
case MB_PAR_ODD: // 奇校验
usart_parity_config(SLAVE_PORT, USART_PM_ODD);
usart_word_length_set(SLAVE_PORT, USART_WL_9BIT);
break;
case MB_PAR_EVEN: // 偶校验
usart_parity_config(SLAVE_PORT, USART_PM_EVEN);
usart_word_length_set(SLAVE_PORT, USART_WL_9BIT);
break;
default:
return FALSE;
}
usart_stop_bit_set(SLAVE_PORT, USART_STB_1BIT);
usart_hardware_flow_rts_config(SLAVE_PORT, USART_RTS_DISABLE);
usart_hardware_flow_cts_config(SLAVE_PORT, USART_CTS_DISABLE);
usart_receive_config(SLAVE_PORT, USART_RECEIVE_ENABLE);
usart_transmit_config(SLAVE_PORT, USART_TRANSMIT_ENABLE);
/* SLAVE_PORT */
usart_enable(SLAVE_PORT);
//=====================中断初始化======================================
nvic_irq_enable(USART0_IRQn, 0, 0);
SLAVE_RS485_RECEIVE_MODE;
EXIT_CRITICAL_SECTION(); // 开全局中断
return TRUE;
}
BOOL xMBPortSerialPutByte(CHAR ucByte)
{
usart_data_transmit(SLAVE_PORT, (uint8_t)ucByte);
return TRUE;
}
BOOL xMBPortSerialGetByte(CHAR *pucByte)
{
*pucByte = (CHAR)usart_data_receive(SLAVE_PORT);
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();
}
/*******************************************************************************
* Function Name : USART0_IRQHandler
* Description : This function handles USART0 global interrupt request.
* Input : None
* Output : None
* Return : None
*******************************************************************************/
void USART0_IRQHandler(void)
{
rt_interrupt_enter();
// 溢出错误
if (usart_flag_get(SLAVE_PORT, USART_FLAG_ORERR) == SET)
{
usart_flag_clear(SLAVE_PORT, USART_FLAG_ORERR);
prvvUARTRxISR();
}
// 接收中断
if (usart_interrupt_flag_get(SLAVE_PORT, USART_INT_FLAG_RBNE) == SET)
{
usart_interrupt_flag_clear(SLAVE_PORT, USART_INT_FLAG_RBNE);
prvvUARTRxISR();
}
// 发送中断
if (usart_interrupt_flag_get(SLAVE_PORT, USART_INT_FLAG_TBE) == SET)
{
prvvUARTTxReadyISR();
}
rt_interrupt_leave();
}
3.3 pottimer.c
/*
* FreeModbus Libary: RT-Thread Port
* Copyright (C) 2013 Armink <armink.ztl@gmail.com>
*
* 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: porttimer.c,v 1.60 2013/08/13 15:07:05 Armink $
*/
/* ----------------------- Platform includes --------------------------------*/
#include "port.h"
/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mbport.h"
#define MBS_SOFT_TIMER (FALSE)
#define USE_UART_DMA (FALSE)
/* ----------------------- static functions ---------------------------------*/
static struct rt_timer timer;
static void prvvTIMERExpiredISR(void);
static void timer_timeout_ind(void* parameter);
/* ----------------------- Start implementation -----------------------------*/
BOOL xMBPortTimersInit(USHORT usTim1Timerout50us)
{
rt_timer_init(&timer, "slave timer",
timer_timeout_ind, /* bind timeout callback function */
RT_NULL,
(50 * usTim1Timerout50us) / (1000 * 1000 / RT_TICK_PER_SECOND) + 1,
RT_TIMER_FLAG_ONE_SHOT); /* one shot */
return TRUE;
}
void vMBPortTimersEnable()
{
rt_timer_start(&timer);
}
void vMBPortTimersDisable()
{
rt_timer_stop(&timer);
}
void prvvTIMERExpiredISR(void)
{
(void) pxMBPortCBTimerExpired();
}
static void timer_timeout_ind(void* parameter)
{
prvvTIMERExpiredISR();
}
/**
* 销毁Modbus从机定时器
*
* @return 成功返回TRUE,失败返回FALSE
*/
BOOL xMBPortTimersDestroy(void)
{
rt_err_t ret;
// 1. 先停止定时器(确保定时器处于非运行状态)
ret = rt_timer_stop(&timer);
if (ret != RT_EOK && ret != -RT_ERROR) // 允许返回-RT_ERROR(定时器已停止)
{
rt_kprintf("Modbus timer stop failed: %d\n", ret);
return FALSE;
}
// 2. 销毁定时器(释放相关资源)
ret = rt_timer_detach(&timer);
if (ret != RT_EOK)
{
rt_kprintf("Modbus timer destroy failed: %d\n", ret);
return FALSE;
}
// 3. 可选:清除定时器结构体(对于静态定时器非必需,但增强安全性)
rt_memset(&timer, 0, sizeof(struct rt_timer));
return TRUE;
}
或者使用硬件定时器
/*
* FreeModbus Libary: RT-Thread Port
* Copyright (C) 2013 Armink <armink.ztl@gmail.com>
*
* 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: porttimer.c,v 1.60 2013/08/13 15:07:05 Armink $
*/
/* ----------------------- Platform includes --------------------------------*/
#include "port.h"
/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mbport.h"
#define MBS_SOFT_TIMER (FALSE)
#define USE_UART_DMA (FALSE)
/* ----------------------- Start implementation -----------------------------*/
BOOL xMBPortTimersInit(USHORT usTim1Timerout50us)
{
uint16_t prescaler_value = 0;
// GD32 V2.x中定时器初始化结构体
timer_parameter_struct timer_initpara;
// 1. 使能定时器时钟(与串口代码保持一致的RCU使能方式)
rcu_periph_clock_enable(RCU_TIMER3);
// 2. 计算预分频值(目标:50us周期,即20kHz计数频率)
// 假设APB1时钟为36MHz,定时器时钟 = APB1时钟 × 2(当APB1预分频>1时)
prescaler_value = (uint16_t)(SystemCoreClock / 20000) - 1;
// 3. 定时器复位与初始化(V2.x风格)
timer_deinit(TIMER3);
timer_initpara.prescaler = prescaler_value;
timer_initpara.alignedmode = TIMER_COUNTER_EDGE;
timer_initpara.counterdirection = TIMER_COUNTER_UP;
timer_initpara.period = (uint16_t)usTim1Timerout50us; // 超时值(单位:50us)
timer_initpara.clockdivision = TIMER_CKDIV_DIV1;
timer_initpara.repetitioncounter = 0;
timer_init(TIMER3, &timer_initpara);
// 使能自动重装载影子寄存器
timer_auto_reload_shadow_enable(TIMER3);
// 4. 配置NVIC中断(与串口代码一致的优先级配置方式)
nvic_priority_group_set(NVIC_PRIGROUP_PRE2_SUB2); // 与串口共用同一优先级分组
nvic_irq_enable(TIMER3_IRQn, 1, 1); // 抢占优先级1,子优先级1(高于串口中断)
// 5. 初始化时禁用定时器和中断
timer_flag_clear(TIMER3, TIMER_FLAG_UP);
timer_interrupt_disable(TIMER3, TIMER_INT_UP);
timer_disable(TIMER3);
return TRUE;
}
void vMBPortTimersEnable()
{
// 清除标志→使能中断→重置计数器→启动定时器(V2.x函数风格)
timer_flag_clear(TIMER3, TIMER_FLAG_UP);
timer_interrupt_enable(TIMER3, TIMER_INT_UP);
timer_counter_value_config(TIMER3, 0); // V2.x中计数器设置函数
timer_enable(TIMER3);
}
void vMBPortTimersDisable()
{
// 停止定时器→禁用中断→清除标志→重置计数器
timer_disable(TIMER3);
timer_interrupt_disable(TIMER3, TIMER_FLAG_UP);
timer_flag_clear(TIMER3, TIMER_INT_UP);
timer_counter_value_config(TIMER3, 0);
}
void prvvTIMERExpiredISR(void)
{
(void)pxMBPortCBTimerExpired(); // 调用Modbus超时回调
}
/* 定时器3中断服务程序(与串口中断处理风格一致) */
void TIMER3_IRQHandler(void)
{
rt_interrupt_enter(); // 与串口中断保持一致的RT-Thread中断上下文处理
// 检查更新中断标志(V2.x中断标志判断方式)
if (timer_interrupt_flag_get(TIMER3, TIMER_FLAG_UP) == SET)
{
timer_interrupt_flag_clear(TIMER3, TIMER_INT_UP); // 清除中断标志
prvvTIMERExpiredISR(); // 处理超时事件
}
rt_interrupt_leave();
}
四、运行modbus
demo.c
/*
* FreeModbus Libary: BARE Demo Application
* Copyright (C) 2006 Christian Walter <wolti@sil.at>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* File: $Id$
*/
/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mbport.h"
/* ----------------------- Defines ------------------------------------------*/
#define REG_INPUT_START 1000
#define REG_INPUT_NREGS 4
/* ----------------------- Static variables ---------------------------------*/
static USHORT usRegInputStart = REG_INPUT_START;
static USHORT usRegInputBuf[REG_INPUT_NREGS];
// 静态定义线程控制块和线程栈
#define MBSLAVE_THREAD_STACK_SIZE 1024
#define MBSLAVE_THREAD_PRIORITY 8
#define MBSLAVE_THREAD_TIMESLICE 5
static struct rt_thread mbslave_thread;
static rt_uint8_t mbslave_thread_stack[MBSLAVE_THREAD_STACK_SIZE];
/* ----------------------- Start implementation -----------------------------*/
static void mbslave_thread_entry(void *parameter)
{
eMBErrorCode eStatus;
eStatus = eMBInit( MB_RTU, 0x0A, 0, 38400, MB_PAR_EVEN );
/* Enable the Modbus Protocol Stack. */
eStatus = eMBEnable( );
for( ;; )
{
( void )eMBPoll( );
/* Here we simply count the number of poll cycles. */
usRegInputBuf[0]++;
rt_thread_dela(10);
}
}
/* 初始化函数 */
int thread_mbslave_init(void)
{
// 静态初始化线程
rt_err_t result = rt_thread_init(&mbslave_thread,
"mbslave",
mbslave_thread_entry,
RT_NULL,
&mbslave_thread_stack[0],
sizeof(mbslave_thread_stack),
MBSLAVE_THREAD_PRIORITY,
MBSLAVE_THREAD_TIMESLICE);
if (result == RT_EOK)
{
rt_thread_startup(&mbslave_thread);
rt_kprintf("mbslave thread started\n");
return 0;
}
else
{
rt_kprintf("Failed to initialize mbslave thread!\n");
return -1;
}
}
INIT_APP_EXPORT(thread_mbslave_init); /* 系统启动时自动初始化 */
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 )
{
return MB_ENOREG;
}
eMBErrorCode
eMBRegCoilsCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNCoils,
eMBRegisterMode eMode )
{
return MB_ENOREG;
}
eMBErrorCode
eMBRegDiscreteCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNDiscrete )
{
return MB_ENOREG;
}
至此,freemodbus v1.6移植成功
更多推荐


所有评论(0)