一、移植库源代码
从github下载:https://github.com/armink/FreeModbus_Slave-Master-RTT-STM32
无法下载或者下载太慢可以用资源下载,无需积分。freeModbus主机源码下载
一、工程创建
三、源代码移植
将FreeModbus主机源代码拷贝到工程中间件-第三方库-freemodbus源码库:即Middlewares\Third_Party\FreeModbusMaster文件夹中。
将源码添加到工程中
头文件包含
3.1 源码接口完善
FreeModbus的移植主要包含:物理层接口的修改、应用层回调的修改。具体函数如下:
3.1.1 物理层接口:
(a) portserial_m.c
1)xMBMasterPortSerialInit()
2)vMBMasterPortSerialEnable()
3)xMBMasterPortSerialPutByte()
4)xMBMasterPortSerialGetByte()
(b) porttimer_m.c
1)xMBMasterPortTimersInit()
2)vMBMasterPortTimersT35Enable()
3)vMBMasterPortTimersDisable()
(c ) port.c
实现一个环形缓冲队列和临界区进入退出函数
void EnterCriticalSection(void);
void ExitCriticalSection(void);
void Put_in_fifo(Serial_fifo *buff, uint8_t *putdata, int length);
int Get_from_fifo(Serial_fifo *buff, uint8_t *getdata, int length);
(d) user_mb_app.c
实现接收回调
1)eMBMasterRegInputCB()
2)eMBMasterRegHoldingCB()
3)eMBMasterRegCoilsCB()
4)eMBMasterRegDiscreteCB()
3.1.2 具体修改代码
portserial_m.c接口
#include "port.h"
/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mbport.h"
#define USING_UART2
#if MB_MASTER_RTU_ENABLED > 0 || MB_MASTER_ASCII_ENABLED > 0
/* ----------------------- Static variables ---------------------------------*/
static volatile uint8_t rx_buff[FIFO_SIZE_MAX];
static Serial_fifo Master_serial_rx_fifo;
/* software simulation serial transmit IRQ handler thread stack */
/* software simulation serial transmit IRQ handler thread */
//static TaskHandle_t thread_serial_soft_trans_irq = NULL;
/* serial event */
static EventGroupHandle_t event_serial = NULL;
/* modbus master serial device */
static UART_HandleTypeDef *serial;
/* ----------------------- Defines ------------------------------------------*/
/* serial transmit event */
#define EVENT_SERIAL_TRANS_START (1<<0)
/* ----------------------- static functions ---------------------------------*/
static void prvvUARTTxReadyISR(void);
static void prvvUARTRxISR(void);
static void serial_soft_trans_irq(void *parameter);
//static void Master_TxCpltCallback(struct __UART_HandleTypeDef *huart);
void Master_RxCpltCallback(struct __UART_HandleTypeDef *huart);
static int stm32_getc(void);
static int stm32_putc(CHAR c);
/* ----------------------- Start implementation -----------------------------*/
BOOL xMBMasterPortSerialInit(UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits,
eMBParity eParity)
{
/**
* set 485 mode receive and transmit control IO
* @note MODBUS_MASTER_RT_CONTROL_PIN_INDEX need be defined by user
*/
/* set serial name */
if (ucPORT == 1)
{
#if defined(USING_UART1)
extern UART_HandleTypeDef huart1;
serial = &huart1;
// //printf("Master using uart1!\r\n");
#endif
}
else if (ucPORT == 2)
{
#if defined(USING_UART2)
extern UART_HandleTypeDef huart2;
serial = &huart2;
// //printf("Master using uart2!\r\n");
#endif
}
else if (ucPORT == 3)
{
#if defined(USING_UART3)
extern UART_HandleTypeDef huart3;
serial = &huart3;
// //printf("Master using uart3!\r\n");
#endif
}
/* set serial configure parameter */
serial->Init.BaudRate = ulBaudRate;
serial->Init.StopBits = UART_STOPBITS_1;
switch(eParity){
case MB_PAR_NONE: {
serial->Init.WordLength = UART_WORDLENGTH_8B;
serial->Init.Parity = UART_PARITY_NONE;
break;
}
case MB_PAR_ODD: {
serial->Init.WordLength = UART_WORDLENGTH_9B;
serial->Init.Parity = UART_PARITY_ODD;
break;
}
case MB_PAR_EVEN: {
serial->Init.WordLength = UART_WORDLENGTH_9B;
serial->Init.Parity = UART_PARITY_EVEN;
break;
}
}
if (HAL_UART_Init(serial) != HAL_OK)
{
Error_Handler();
}
__HAL_UART_DISABLE_IT(serial, UART_IT_RXNE);
__HAL_UART_DISABLE_IT(serial, UART_IT_TC);
/*registe recieve callback*/
// HAL_UART_RegisterCallback(serial, HAL_UART_RX_COMPLETE_CB_ID, Master_RxCpltCallback);
/* software initialize */
Master_serial_rx_fifo.buffer = rx_buff;
Master_serial_rx_fifo.get_index = 0;
Master_serial_rx_fifo.put_index = 0;
/* software initialize */
/* ´´½¨Ö÷Õ¾·¢ËÍÈÎÎñ */
event_serial = xEventGroupCreate();
if (NULL != event_serial)
{
//printf("Master create event_serial success! event_serial=%d\r\n",(int)event_serial);
}
else
{
//printf("Master create event_serial Failed!\r\n");
}
BaseType_t xReturn = pdPASS;
xReturn = xTaskCreate((TaskFunction_t)serial_soft_trans_irq, /* ÈÎÎñÈë¿Úº¯Êý */
(const char *)"master trans", /* ÈÎÎñÃû×Ö */
(uint16_t)128, /* ÈÎÎñÕ»´óС*/
(void *)NULL, /* ÈÎÎñÈë¿Úº¯Êý²ÎÊý */
(UBaseType_t)12, /* ÈÎÎñµÄÓÅÏȼ¶ */
NULL); /*ÈÎÎñ¿ØÖÆ¿é*/
if (xReturn == pdPASS)
{
//printf("xTaskCreate Master trans success\r\n");
}
else
{
// //printf("xTaskCreate Master trans faild!\r\n");
return FALSE;
}
return TRUE;
}
void vMBMasterPortSerialEnable(BOOL xRxEnable, BOOL xTxEnable)
{
/*ÕâÒ»²½²»ÄÜÊ¡ÂÔ£¬ÐèÒªÌáÇ°ÇåÀíµô±ê־룬·ñÔò½ÓÊÕ»áÓÐÎÊÌâ*/
__HAL_UART_CLEAR_FLAG(serial,UART_FLAG_RXNE);
__HAL_UART_CLEAR_FLAG(serial,UART_FLAG_TC);
if (xRxEnable == pdTRUE)
{
/* enable RX interrupt */
__HAL_UART_ENABLE_IT(serial, UART_IT_RXNE);
}
else
{
/* disable RX interrupt */
__HAL_UART_DISABLE_IT(serial, UART_IT_RXNE);
}
if (xTxEnable == pdTRUE)
{
/* start serial transmit */
xEventGroupSetBits(event_serial, EVENT_SERIAL_TRANS_START);
}
else
{
xEventGroupClearBits(event_serial, EVENT_SERIAL_TRANS_START);
}
}
void vMBMasterPortClose(void)
{
__HAL_UART_DISABLE(serial);
}
BOOL xMBMasterPortSerialPutByte(CHAR ucByte)
{
stm32_putc(ucByte);
return TRUE;
}
BOOL xMBMasterPortSerialGetByte(CHAR * pucByte)
{
Get_from_fifo(&Master_serial_rx_fifo, (uint8_t *)pucByte, 1);
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.
*/
void prvvUARTTxReadyISR(void)
{
pxMBMasterFrameCBTransmitterEmpty();
}
/*
* 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.
*/
void prvvUARTRxISR(void)
{
pxMBMasterFrameCBByteReceived();
}
/**
* Software simulation serial transmit IRQ handler.
*
* @param parameter parameter
*/
static void serial_soft_trans_irq(void* parameter) {
while (1)
{
/* waiting for serial transmit start */
xEventGroupWaitBits(event_serial, /* ʼþ¶ÔÏó¾ä±ú */
EVENT_SERIAL_TRANS_START, /* ½ÓÊÕÈÎÎñ¸ÐÐËȤµÄʼþ */
pdFALSE, /* Í˳öʱÇå³ýʼþ±êÖ¾*/
pdFALSE, /* Âú×ã¸ÐÐËȤµÄÈÎһʼþ */
portMAX_DELAY); /* Ö¸¶¨³¬Ê±Ê¼þ,ÎÞÏ޵ȴý */
/* execute modbus callback */
prvvUARTTxReadyISR();
}
}
/**
* @brief Rx Transfer completed callbacks.
* @param huart Pointer to a UART_HandleTypeDef structure that contains
* the configuration information for the specified UART module.
* @retval None
*/
void Master_RxCpltCallback(struct __UART_HandleTypeDef *huart)
{
int ch = -1;
/*UART RX·Ç¿ÕÖжϵ÷Ó㬲¢»ñÈ¡Ò»Ö¡Êý¾Ý*/
while (1)
{
ch = stm32_getc();
if (ch == -1)
{
break;
}
Put_in_fifo(&Master_serial_rx_fifo, (uint8_t *)&ch, 1);
}
prvvUARTRxISR();
}
/*UART·¢ËÍÒ»¸öÊý¾Ý*/
static int stm32_putc(CHAR c)
{
serial->Instance->DR = c;
while (!(serial->Instance->SR & UART_FLAG_TC));
return TRUE;
}
/*UART½ÓÊÕÒ»¸öÊý¾Ý*/
static int stm32_getc(void)
{
int ch;
ch = -1;
if (serial->Instance->SR & UART_FLAG_RXNE)
{
ch = serial->Instance->DR & 0xff;
}
return ch;
}
#endif
porttimer_m.c接口
/* ----------------------- Platform includes --------------------------------*/
#include "port.h"
/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mb_m.h"
#include "mbport.h"
#if MB_MASTER_RTU_ENABLED > 0 || MB_MASTER_ASCII_ENABLED > 0
/* ----------------------- Variables ----------------------------------------*/
static USHORT usT35TimeOut50us;
static TimerHandle_t timer = NULL;
static void prvvTIMERExpiredISR(void);
static void timer_timeout_ind(TimerHandle_t xTimer);
static BaseType_t pxHigherPriorityTaskWoken;
/* ----------------------- static functions ---------------------------------*/
static void prvvTIMERExpiredISR(void);
/* ----------------------- Start implementation -----------------------------*/
BOOL xMBMasterPortTimersInit(USHORT usTimeOut50us)
{
/* backup T35 ticks */
usT35TimeOut50us = usTimeOut50us;
timer = xTimerCreate("Master timer",
(50 * usT35TimeOut50us) / (1000 * 1000 / configTICK_RATE_HZ) + 1, pdFALSE,
(void *)1, timer_timeout_ind);
if (timer != NULL)
{
//printf("Create Master Timer Success!\r\n");
return TRUE;
}
else
{
//printf("Create Master Timer Faild!\r\n");
return FALSE;
}
}
void vMBMasterPortTimersT35Enable()
{
uint32_t timer_tick =(50 * usT35TimeOut50us) / (1000 * 1000 / configTICK_RATE_HZ) + 1;
/* Set current timer mode, don't change it.*/
vMBMasterSetCurTimerMode(MB_TMODE_T35);
if (IS_IRQ())
{
// xTimerStopFromISR(timer, 0);
xTimerChangePeriodFromISR((TimerHandle_t)timer, timer_tick, &pxHigherPriorityTaskWoken);
// xTimerStartFromISR(timer, 0);
}
else
{
// xTimerStop(timer, 0);
xTimerChangePeriod((TimerHandle_t)timer, timer_tick, 0);
// xTimerStart(timer, 0);
}
}
void vMBMasterPortTimersConvertDelayEnable()
{
uint32_t timer_tick = MB_MASTER_DELAY_MS_CONVERT * configTICK_RATE_HZ / 1000;
/* Set current timer mode, don't change it.*/
vMBMasterSetCurTimerMode(MB_TMODE_CONVERT_DELAY);
// xTimerStop(timer, 0);
if (IS_IRQ())
{
xTimerChangePeriodFromISR((TimerHandle_t)timer, timer_tick, &pxHigherPriorityTaskWoken);
}
else
{
xTimerChangePeriod((TimerHandle_t)timer, timer_tick, 0);
}
}
void vMBMasterPortTimersRespondTimeoutEnable()
{
uint32_t timer_tick = MB_MASTER_TIMEOUT_MS_RESPOND * configTICK_RATE_HZ / 1000;
/* Set current timer mode, don't change it.*/
vMBMasterSetCurTimerMode(MB_TMODE_RESPOND_TIMEOUT);
if (IS_IRQ())
{
// xTimerStopFromISR(timer, 0);
xTimerChangePeriodFromISR((TimerHandle_t)timer, timer_tick, &pxHigherPriorityTaskWoken);
// xTimerStartFromISR(timer, 0);
}
else
{
// xTimerStop(timer, 0);
xTimerChangePeriod((TimerHandle_t)timer, timer_tick, 0);
// xTimerStart(timer, 0);
}
}
void vMBMasterPortTimersDisable()
{
if (IS_IRQ())
{
xTimerStopFromISR((TimerHandle_t)timer, 0);
}
else
{
xTimerStop((TimerHandle_t)timer, 0);
}
}
void prvvTIMERExpiredISR(void)
{
(void) pxMBMasterPortCBTimerExpired();
}
static void timer_timeout_ind(void* parameter)
{
prvvTIMERExpiredISR();
}
#endif
port.h
#ifndef _PORT_H
#define _PORT_H
#include "FreeRTOS.h"
#include "cmsis_os.h"
#include "cmsis_os2.h"
#include "event_groups.h"
#include "main.h"
#include "usart.h"
#include "mbconfig.h"
#include "mbproto.h"
#include "semphr.h"
#include "task.h"
#include "timers.h"
#include <assert.h>
#include <inttypes.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
#include "stm32f1xx_hal.h"
#define INLINE inline
#define PR_BEGIN_EXTERN_C extern "C" {
#define PR_END_EXTERN_C }
#define ENTER_CRITICAL_SECTION() EnterCriticalSection()
#define EXIT_CRITICAL_SECTION() ExitCriticalSection()
typedef struct _serial_fifo
{
/* software fifo */
volatile uint8_t *buffer;
volatile uint16_t put_index, get_index;
} Serial_fifo;
#define FIFO_SIZE_MAX 265
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;
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
void EnterCriticalSection(void);
void ExitCriticalSection(void);
void Put_in_fifo(Serial_fifo *buff, uint8_t *putdata, int length);
int Get_from_fifo(Serial_fifo *buff, uint8_t *getdata, int length);
extern __inline bool IS_IRQ(void);
port.c
#include "port.h"
/* ----------------------- Variables ----------------------------------------*/
/* ----------------------- Start implementation -----------------------------*/
void EnterCriticalSection(void)
{
taskENTER_CRITICAL();
}
void ExitCriticalSection(void)
{
taskEXIT_CRITICAL();
}
/*put bytes in buff*/
void Put_in_fifo(Serial_fifo *buff, uint8_t *putdata, int length)
{
portDISABLE_INTERRUPTS();
while (length--)
{
buff->buffer[buff->put_index] = *putdata;
buff->put_index += 1;
if (buff->put_index >= FIFO_SIZE_MAX)
{
buff->put_index = 0;
}
/* if the next position is read index, discard this 'read char' */
if (buff->put_index == buff->get_index)
{
buff->get_index += 1;
if (buff->get_index >= FIFO_SIZE_MAX)
{
buff->get_index = 0;
}
}
}
portENABLE_INTERRUPTS();
}
/*get bytes from buff*/
int Get_from_fifo(Serial_fifo *buff, uint8_t *getdata, int length)
{
int size = length;
/* read from software FIFO */
while (length)
{
int ch;
/* disable interrupt */
portDISABLE_INTERRUPTS();
if (buff->get_index != buff->put_index)
{
ch = buff->buffer[buff->get_index];
buff->get_index += 1;
if (buff->get_index >= FIFO_SIZE_MAX)
{
buff->get_index = 0;
}
}
else
{
/* no data, enable interrupt and break out */
portENABLE_INTERRUPTS();
break;
}
*getdata = ch & 0xff;
getdata++;
length--;
/* enable interrupt */
portENABLE_INTERRUPTS();
}
return size - length;
}
/*ÅжÏÊÇ·ñ½øÈëÔÚÖжÏÖÐ*/
#ifndef IS_IRQ()
extern __asm uint32_t vPortGetIPSR(void); //µ÷ÓÃFreeRTOS API
__inline bool IS_IRQ(void) //ʹÓÃÄÚÁªº¯ÊýÌá¸ßËÙ¶È
{
if (vPortGetIPSR())
{
return TRUE;
}
return FALSE;
}
#endif // MACRO
回调接口user_mb_app.c
#include "user_mb_app.h"
/*-----------------------Master mode use these variables----------------------*/
#if MB_MASTER_RTU_ENABLED > 0 || MB_MASTER_ASCII_ENABLED > 0
//Master mode:DiscreteInputs variables
USHORT usMDiscInStart = M_DISCRETE_INPUT_START;
#if M_DISCRETE_INPUT_NDISCRETES%8
UCHAR ucMDiscInBuf[MB_MASTER_TOTAL_SLAVE_NUM][M_DISCRETE_INPUT_NDISCRETES/8+1];
#else
UCHAR ucMDiscInBuf[MB_MASTER_TOTAL_SLAVE_NUM][M_DISCRETE_INPUT_NDISCRETES/8];
#endif
//Master mode:Coils variables
USHORT usMCoilStart = M_COIL_START;
#if M_COIL_NCOILS%8
UCHAR ucMCoilBuf[MB_MASTER_TOTAL_SLAVE_NUM][M_COIL_NCOILS/8+1];
#else
UCHAR ucMCoilBuf[MB_MASTER_TOTAL_SLAVE_NUM][M_COIL_NCOILS/8];
#endif
//Master mode:InputRegister variables
USHORT usMRegInStart = M_REG_INPUT_START;
USHORT usMRegInBuf[MB_MASTER_TOTAL_SLAVE_NUM][M_REG_INPUT_NREGS];
//Master mode:HoldingRegister variables
USHORT usMRegHoldStart = M_REG_HOLDING_START;
USHORT usMRegHoldBuf[MB_MASTER_TOTAL_SLAVE_NUM][M_REG_HOLDING_NREGS];
/**
* Modbus master input register callback function.
*
* @param pucRegBuffer input register buffer
* @param usAddress input register address
* @param usNRegs input register number
*
* @return result
*/
eMBErrorCode eMBMasterRegInputCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs )
{
eMBErrorCode eStatus = MB_ENOERR;
USHORT iRegIndex;
USHORT * pusRegInputBuf;
USHORT REG_INPUT_START;
USHORT REG_INPUT_NREGS;
USHORT usRegInStart;
pusRegInputBuf = usMRegInBuf[ucMBMasterGetDestAddress() - 1];
REG_INPUT_START = M_REG_INPUT_START;
REG_INPUT_NREGS = M_REG_INPUT_NREGS;
usRegInStart = usMRegInStart;
/* it already plus one in modbus function method. */
usAddress--;
if ((usAddress >= REG_INPUT_START)
&& (usAddress + usNRegs <= REG_INPUT_START + REG_INPUT_NREGS))
{
iRegIndex = usAddress - usRegInStart;
while (usNRegs > 0)
{
pusRegInputBuf[iRegIndex] = *pucRegBuffer++ << 8;
pusRegInputBuf[iRegIndex] |= *pucRegBuffer++;
iRegIndex++;
usNRegs--;
}
}
else
{
eStatus = MB_ENOREG;
}
return eStatus;
}
/**
* Modbus master holding register callback function.
*
* @param pucRegBuffer holding register buffer
* @param usAddress holding register address
* @param usNRegs holding register number
* @param eMode read or write
*
* @return result
*/
eMBErrorCode eMBMasterRegHoldingCB(UCHAR * pucRegBuffer, USHORT usAddress,
USHORT usNRegs, eMBRegisterMode eMode)
{
eMBErrorCode eStatus = MB_ENOERR;
USHORT iRegIndex;
USHORT * pusRegHoldingBuf;
USHORT REG_HOLDING_START;
USHORT REG_HOLDING_NREGS;
USHORT usRegHoldStart;
pusRegHoldingBuf = usMRegHoldBuf[ucMBMasterGetDestAddress() - 1];
REG_HOLDING_START = M_REG_HOLDING_START;
REG_HOLDING_NREGS = M_REG_HOLDING_NREGS;
usRegHoldStart = usMRegHoldStart;
/* if mode is read, the master will write the received date to buffer. */
eMode = MB_REG_WRITE;
/* it already plus one in modbus function method. */
usAddress--;
if ((usAddress >= REG_HOLDING_START)
&& (usAddress + usNRegs <= REG_HOLDING_START + REG_HOLDING_NREGS))
{
iRegIndex = usAddress - usRegHoldStart;
switch (eMode)
{
/* read current register values from the protocol stack. */
case MB_REG_READ:
while (usNRegs > 0)
{
*pucRegBuffer++ = (UCHAR) (pusRegHoldingBuf[iRegIndex] >> 8);
*pucRegBuffer++ = (UCHAR) (pusRegHoldingBuf[iRegIndex] & 0xFF);
iRegIndex++;
usNRegs--;
}
break;
/* write current register values with new values from the protocol stack. */
case MB_REG_WRITE:
while (usNRegs > 0)
{
pusRegHoldingBuf[iRegIndex] = *pucRegBuffer++ << 8;
pusRegHoldingBuf[iRegIndex] |= *pucRegBuffer++;
iRegIndex++;
usNRegs--;
}
break;
}
}
else
{
eStatus = MB_ENOREG;
}
return eStatus;
}
/**
* Modbus master coils callback function.
*
* @param pucRegBuffer coils buffer
* @param usAddress coils address
* @param usNCoils coils number
* @param eMode read or write
*
* @return result
*/
eMBErrorCode eMBMasterRegCoilsCB(UCHAR * pucRegBuffer, USHORT usAddress,
USHORT usNCoils, eMBRegisterMode eMode)
{
eMBErrorCode eStatus = MB_ENOERR;
USHORT iRegIndex , iRegBitIndex , iNReg;
UCHAR * pucCoilBuf;
USHORT COIL_START;
USHORT COIL_NCOILS;
USHORT usCoilStart;
iNReg = usNCoils / 8 + 1;
pucCoilBuf = ucMCoilBuf[ucMBMasterGetDestAddress() - 1];
COIL_START = M_COIL_START;
COIL_NCOILS = M_COIL_NCOILS;
usCoilStart = usMCoilStart;
/* if mode is read,the master will write the received date to buffer. */
eMode = MB_REG_WRITE;
/* it already plus one in modbus function method. */
usAddress--;
if ((usAddress >= COIL_START)
&& (usAddress + usNCoils <= COIL_START + COIL_NCOILS))
{
iRegIndex = (USHORT) (usAddress - usCoilStart) / 8;
iRegBitIndex = (USHORT) (usAddress - usCoilStart) % 8;
switch (eMode)
{
/* read current coil values from the protocol stack. */
case MB_REG_READ:
while (iNReg > 0)
{
*pucRegBuffer++ = xMBUtilGetBits(&pucCoilBuf[iRegIndex++],
iRegBitIndex, 8);
iNReg--;
}
pucRegBuffer--;
/* last coils */
usNCoils = usNCoils % 8;
/* filling zero to high bit */
*pucRegBuffer = *pucRegBuffer << (8 - usNCoils);
*pucRegBuffer = *pucRegBuffer >> (8 - usNCoils);
break;
/* write current coil values with new values from the protocol stack. */
case MB_REG_WRITE:
while (iNReg > 1)
{
xMBUtilSetBits(&pucCoilBuf[iRegIndex++], iRegBitIndex, 8,
*pucRegBuffer++);
iNReg--;
}
/* last coils */
usNCoils = usNCoils % 8;
/* xMBUtilSetBits has bug when ucNBits is zero */
if (usNCoils != 0)
{
xMBUtilSetBits(&pucCoilBuf[iRegIndex++], iRegBitIndex, usNCoils,
*pucRegBuffer++);
}
break;
}
}
else
{
eStatus = MB_ENOREG;
}
return eStatus;
}
/**
* Modbus master discrete callback function.
*
* @param pucRegBuffer discrete buffer
* @param usAddress discrete address
* @param usNDiscrete discrete number
*
* @return result
*/
eMBErrorCode eMBMasterRegDiscreteCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNDiscrete )
{
eMBErrorCode eStatus = MB_ENOERR;
USHORT iRegIndex , iRegBitIndex , iNReg;
UCHAR * pucDiscreteInputBuf;
USHORT DISCRETE_INPUT_START;
USHORT DISCRETE_INPUT_NDISCRETES;
USHORT usDiscreteInputStart;
iNReg = usNDiscrete / 8 + 1;
pucDiscreteInputBuf = ucMDiscInBuf[ucMBMasterGetDestAddress() - 1];
DISCRETE_INPUT_START = M_DISCRETE_INPUT_START;
DISCRETE_INPUT_NDISCRETES = M_DISCRETE_INPUT_NDISCRETES;
usDiscreteInputStart = usMDiscInStart;
/* it already plus one in modbus function method. */
usAddress--;
if ((usAddress >= DISCRETE_INPUT_START)
&& (usAddress + usNDiscrete <= DISCRETE_INPUT_START + DISCRETE_INPUT_NDISCRETES))
{
iRegIndex = (USHORT) (usAddress - usDiscreteInputStart) / 8;
iRegBitIndex = (USHORT) (usAddress - usDiscreteInputStart) % 8;
/* write current discrete values with new values from the protocol stack. */
while (iNReg > 1)
{
xMBUtilSetBits(&pucDiscreteInputBuf[iRegIndex++], iRegBitIndex, 8,
*pucRegBuffer++);
iNReg--;
}
/* last discrete */
usNDiscrete = usNDiscrete % 8;
/* xMBUtilSetBits has bug when ucNBits is zero */
if (usNDiscrete != 0)
{
xMBUtilSetBits(&pucDiscreteInputBuf[iRegIndex++], iRegBitIndex,
usNDiscrete, *pucRegBuffer++);
}
}
else
{
eStatus = MB_ENOREG;
}
return eStatus;
}
#endif
user_mb_app.h
#include "mb.h"
#include "mb_m.h"
#include "mbconfig.h"
#include "mbframe.h"
#include "mbutils.h"
/* -----------------------Slave Defines -------------------------------------*/
#define S_DISCRETE_INPUT_START 0
#define S_DISCRETE_INPUT_NDISCRETES 16
#define S_COIL_START 0
#define S_COIL_NCOILS 64
#define S_REG_INPUT_START 0
#define S_REG_INPUT_NREGS 100
#define S_REG_HOLDING_START 0
#define S_REG_HOLDING_NREGS 100
/* salve mode: holding register's all address */
#define S_HD_RESERVE 0
#define S_HD_CPU_USAGE_MAJOR 1
#define S_HD_CPU_USAGE_MINOR 2
/* salve mode: input register's all address */
#define S_IN_RESERVE 0
/* salve mode: coil's all address */
#define S_CO_RESERVE 0
/* salve mode: discrete's all address */
#define S_DI_RESERVE 0
/* -----------------------Master Defines -------------------------------------*/
#define M_DISCRETE_INPUT_START 0
#define M_DISCRETE_INPUT_NDISCRETES 10
#define M_COIL_START 5900
#define M_COIL_NCOILS 16
#define M_REG_INPUT_START 5000
#define M_REG_INPUT_NREGS 10
#define M_REG_HOLDING_START 5400
#define M_REG_HOLDING_NREGS 10
/* master mode: holding register's all address */
#define M_HD_RESERVE 0
/* master mode: input register's all address */
#define M_IN_RESERVE 0
/* master mode: coil's all address */
#define M_CO_RESERVE 0
/* master mode: discrete's all address */
#define M_DI_RESERVE 0
其中定义了寄存器的起始地址和寄存器数量。用于判断寄存器地址的有效性。实际应用中可以根据需要修改。
#define M_DISCRETE_INPUT_START 0
#define M_DISCRETE_INPUT_NDISCRETES 10
#define M_COIL_START 5900
#define M_COIL_NCOILS 16
#define M_REG_INPUT_START 5000
#define M_REG_INPUT_NREGS 10
#define M_REG_HOLDING_START 5400
#define M_REG_HOLDING_NREGS 10
中断处理stm32f1xx_it.c
/**
* @brief This function handles USART2 global interrupt.
*/
void USART2_IRQHandler(void)
{
/* USER CODE BEGIN USART2_IRQn 0 */
if (__HAL_UART_GET_FLAG(&huart2, UART_FLAG_RXNE))
{
Master_RxCpltCallback(&huart2);
// HAL_UART_IRQHandler(&huart2);
__HAL_UART_CLEAR_FLAG(&huart2, UART_FLAG_RXNE);
}
if (__HAL_UART_GET_FLAG(&huart2, UART_FLAG_ORE))
{
uint16_t pucByte = (uint16_t)((&huart2)->Instance->DR & (uint16_t)0x01FF);
__HAL_UART_CLEAR_OREFLAG(&huart2);
}
if (__HAL_UART_GET_FLAG(&huart2, UART_FLAG_TC))
{
__HAL_UART_CLEAR_FLAG(&huart2, UART_FLAG_TC);
}
/* USER CODE END USART2_IRQn 0 */
/* USER CODE BEGIN USART2_IRQn 1 */
/* USER CODE END USART2_IRQn 1 */
}
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance == htim6.Instance)
{
#if MB_MASTER_RTU_ENABLED > 0
pxMBMasterPortCBTimerExpired();
#else
pxMBPortCBTimerExpired();
#endif
}
}
四、使用示例
freertos.c
生成的项目文件将任务初始化都生成再freertos.c文件中,配置时STM32Cube工程时,配置了一个MasterTask的任务,一个StartReadTask、一个StartWriteTask。修改任务代码如下:
初始化在void MX_FREERTOS_Init(void)函数
void MX_FREERTOS_Init(void)
{
HAL_TIM_Base_Start(&htim6); //开启定时器
eMBMasterInit(MB_RTU, 2, 115200, MB_PAR_NONE); /*主机初始化*/
eMBMasterEnable(); /*主机使能*/
/* creation of Start_Task */
Start_TaskHandle = osThreadNew(Start_Thread, NULL, &Start_Task_attributes);
/*创建写任务*/
WriteTaskHandle = osThreadNew(StartWriteTask, NULL, &Write_TASK);
/*创建主机任务*/
MasterTaskHandle = osThreadNew(MasterTask, NULL, &MasterTask_attributes);
/*创建读任务*/
ReadTaskHandle = osThreadNew(StartReadTask, NULL, &Read_Task);
}
修改主机任务
void MasterTask(void *argument)
{
for (;;)
{
eMBMasterPoll(); /*主机轮询*/
osDelay(50);
}
}
修改读任务
void StartReadTask(void *argument)
{
extern USHORT usMRegHoldBuf[MB_MASTER_TOTAL_SLAVE_NUM][M_REG_HOLDING_NREGS];
eMBMasterReqErrCode ret;
uint16_t slaveid;
uint16_t regaddr;
uint16_t regnums;
uint8_t i;
for (;;)
{
slaveid = 1;
regaddr = 5400;
regnums = 10;
/*读多个寄存器*/
ret = eMBMasterReqReadHoldingRegister(slaveid, regaddr, regnums, 1000);
if(ret == MB_MRE_NO_ERR)
{
//printf("===eMBMasterReqReadHoldingRegister successful! values : ");
for(i = 0; i < regnums; i++)
{
//printf("%d ", usMRegHoldBuf[slaveid-1][i]);
}
//printf("\r\n");
}
else
{
//printf("===eMBMasterReqReadHoldingRegister failed! ret = %d \r\n", ret);
for(i = 0; i < regnums; i++)
{
//printf("%d ", usMRegHoldBuf[slaveid-1][i]);
}
}
osDelay(1000);
}
}
修改写任务
void StartWriteTask(void *argument)
{
eMBMasterReqErrCode ret;
uint16_t data[10] = {6,2,3,4,5,8,2,50,10,11};
unsigned long H_value;
for (;;)
{
H_value = uxTaskGetStackHighWaterMark(WriteTaskHandle);
//printf("H_value:%d\r\n", (int)H_value);
/*写单个寄存器*/
// ret = eMBMasterReqWriteHoldingRegister(1, 5400, 8, 100);
/*写多个寄存器*/
ret = eMBMasterReqWriteMultipleHoldingRegister(1, 5400, 10, data, 100);
//printf("ret values :%d ", ret);
for(uint8_t i=0;i<sizeof(data)/sizeof(uint16_t);i++)
{
data[i]++;
}
osDelay(1000);
}
}
**注意:**实际使用中可以将任务代码放在单独的文件中。此外如果不使用操作系统,可以在裸机程序中初始化调用。
五、调试记录
读写都返回:MB_MRE_MASTER_BUSY, /*!< master is busy now. */
然后在初始化的时候发送一帧然后释放信号量:
ret = eMBMasterReqWriteHoldingRegister(1, 1, data[0], 100);
vMBMasterRunResRelease();
后面就完全正常了。如果不写一下再释放信号量也没有用。
猜想:信号量被占用了,发送数据后没有释放,需要释放一次信号量。但是在哪个位置释放会比较安全,不会影响其他状态下的处理。
接收和发送都是通过状态机判断完成的,根据不同状态进行相应的处理。
启动时处于BUSY状态,需要释放才能启动运行。启动运行一段时间后还会出现BUSY状态。MB_MRE_MASTER_BUSY。
解决办法:在eMBMasterPoll() 的EV_MASTER_READY状态下先释放信号量。
eMBErrorCode
eMBMasterPoll( void )
{
static UCHAR *ucMBFrame;
static UCHAR ucRcvAddress;
static UCHAR ucFunctionCode;
static USHORT usLength;
static eMBException eException;
int i , j;
eMBErrorCode eStatus = MB_ENOERR;
eMBMasterEventType eEvent;
eMBMasterErrorEventType errorType;
/* Check if the protocol stack is ready. */
if(( eMBState != STATE_ENABLED ) && ( eMBState != STATE_ESTABLISHED))
{
return MB_EILLSTATE;
}
/* Check if there is a event available. If not return control to caller.
* Otherwise we will handle the event. */
if( xMBMasterPortEventGet( &eEvent ) == TRUE )
{
switch ( eEvent )
{
case EV_MASTER_READY:
eMBState = STATE_ESTABLISHED;
vMBMasterRunResRelease(); /**< add by leo */
break;
case EV_MASTER_FRAME_RECEIVED:
eStatus = peMBMasterFrameReceiveCur( &ucRcvAddress, &ucMBFrame, &usLength );
/* Check if the frame is for us. If not ,send an error process event. */
if ( ( eStatus == MB_ENOERR ) && ( ucRcvAddress == ucMBMasterGetDestAddress() ) )
{
( void ) xMBMasterPortEventPost( EV_MASTER_EXECUTE );
}
else
{
vMBMasterSetErrorType(EV_ERROR_RECEIVE_DATA);
( void ) xMBMasterPortEventPost( EV_MASTER_ERROR_PROCESS );
}
break;
case EV_MASTER_EXECUTE:
ucFunctionCode = ucMBFrame[MB_PDU_FUNC_OFF];
eException = MB_EX_ILLEGAL_FUNCTION;
/* If receive frame has exception .The receive function code highest bit is 1.*/
if(ucFunctionCode >> 7) {
eException = (eMBException)ucMBFrame[MB_PDU_DATA_OFF];
}
else
{
for (i = 0; i < MB_FUNC_HANDLERS_MAX; i++)
{
/* No more function handlers registered. Abort. */
if (xMasterFuncHandlers[i].ucFunctionCode == 0) {
break;
}
else if (xMasterFuncHandlers[i].ucFunctionCode == ucFunctionCode) {
vMBMasterSetCBRunInMasterMode(TRUE);
/* If master request is broadcast,
* the master need execute function for all slave.
*/
if ( xMBMasterRequestIsBroadcast() ) {
usLength = usMBMasterGetPDUSndLength();
for(j = 1; j <= MB_MASTER_TOTAL_SLAVE_NUM; j++){
vMBMasterSetDestAddress(j);
eException = xMasterFuncHandlers[i].pxHandler(ucMBFrame, &usLength);
}
}
else {
eException = xMasterFuncHandlers[i].pxHandler(ucMBFrame, &usLength);
}
vMBMasterSetCBRunInMasterMode(FALSE);
break;
}
}
}
/* If master has exception ,Master will send error process.Otherwise the Master is idle.*/
if (eException != MB_EX_NONE) {
vMBMasterSetErrorType(EV_ERROR_EXECUTE_FUNCTION);
( void ) xMBMasterPortEventPost( EV_MASTER_ERROR_PROCESS );
}
else {
vMBMasterCBRequestScuuess( );
vMBMasterRunResRelease( );
}
break;
case EV_MASTER_FRAME_SENT:
/* Master is busy now. */
vMBMasterGetPDUSndBuf( &ucMBFrame );
eStatus = peMBMasterFrameSendCur( ucMBMasterGetDestAddress(), ucMBFrame, usMBMasterGetPDUSndLength() );
break;
case EV_MASTER_ERROR_PROCESS:
/* Execute specified error process callback function. */
errorType = eMBMasterGetErrorType();
vMBMasterGetPDUSndBuf( &ucMBFrame );
switch (errorType) {
case EV_ERROR_RESPOND_TIMEOUT:
vMBMasterErrorCBRespondTimeout(ucMBMasterGetDestAddress(),
ucMBFrame, usMBMasterGetPDUSndLength());
break;
case EV_ERROR_RECEIVE_DATA:
vMBMasterErrorCBReceiveData(ucMBMasterGetDestAddress(),
ucMBFrame, usMBMasterGetPDUSndLength());
break;
case EV_ERROR_EXECUTE_FUNCTION:
vMBMasterErrorCBExecuteFunction(ucMBMasterGetDestAddress(),
ucMBFrame, usMBMasterGetPDUSndLength());
break;
}
vMBMasterRunResRelease();
break;
default:
break;
}
}
return MB_ENOERR;
}
六、FreeModbus主机验证
使用从机Modbus Slave工具设定好寄存器地址、数量、以及数值。然后通过程序连接读取数据或者写修改数据。读的时候注意要设定好寄存器地址和数量。否则返回的是MB_MRE_EXE_FUN