单片机移植freemodbus主机(STM32、GD32、瑞萨、国民技术等)

文章介绍了如何将FreeModbus主机源码移植到基于STM32的RTOS系统中,包括库文件的拷贝、物理层接口、应用层回调的修改,以及中断服务例程的编写。还提到了FreeModbus在STM32上的工程创建、串口和定时器的初始化、回调函数实现,以及错误处理。示例代码展示了移植过程中关键函数的实现,如串口和定时器的配置、中断处理函数等。
摘要由CSDN通过智能技术生成

一、移植库源代码

从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
在这里插入图片描述

七、示例代码

STM32F103ZET6_TESTCODE

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Geek__1992

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值