STM32移植FreeModbus
Posted 旧年不在666
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了STM32移植FreeModbus相关的知识,希望对你有一定的参考价值。
STM32移植FreeModbus
一、平台
-
芯片:STM32F103RDT6
-
FreeModbus版本:freemodbus-v1.5.0
-
开发工具:stm32cubemx V6.1.2 + keil MDK V5.27.1.0
-
开发所用的库:HAL库
二、源码
三、串口、定时器配置
四、文件移植
五、移植串口相关接口
/*
* 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: portserial.c,v 1.1 2006/08/22 21:35:13 wolti Exp $
*/
#include "port.h"
#include "usart.h"
#include "main.h"
#include <stdint.h>
//#include "stm32f1xx_ll_usart.h"
/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mbport.h"
/* ----------------------- static functions ---------------------------------*/
void prvvUARTTxReadyISR( void );
void prvvUARTRxISR( void );
// uint8_t modbus_usart_rx_data;
// uint8_t modbus_usart_tx_data;
/* ----------------------- Start implementation -----------------------------*/
void
vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable )
{
/* If xRXEnable enable serial receive interrupts. If xTxENable enable
* transmitter empty interrupts.
*/
//STM32串口接收中断使能
if(xRxEnable == TRUE)
{
//使能接收中断
//HAL_UART_Receive_IT(&huart2, &modbus_usart_rx_data, 1);
__HAL_UART_ENABLE_IT(&huart2, UART_IT_RXNE);
}
else
{
//禁止接收中断
//HAL_UART_AbortReceive_IT(&huart2);
__HAL_UART_DISABLE_IT(&huart2, UART_IT_RXNE);
}
//STM32串口发送中断使能
if(xTxEnable == TRUE)
{
//使能发送中断
//LL_USART_EnableIT_TXE(huart2.Instance);
//通知MODBUS库发送缓冲区为空
//prvvUARTTxReadyISR();
__HAL_UART_ENABLE_IT(&huart2, UART_IT_TXE);
}
else
{
//LL_USART_DisableIT_TXE(huart2.Instance);
__HAL_UART_DISABLE_IT(&huart2, UART_IT_TXE);
}
}
BOOL
xMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity )
{
//因为串口初始化已经由stm32cubemx生成,这里直接返回true
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. */
// modbus_usart_tx_data = ucByte;
// HAL_UART_Transmit_IT(&huart2, &modbus_usart_tx_data, 1);
// return TRUE;
if (HAL_UART_Transmit(&huart2, (uint8_t *)&ucByte, 1, 100) != HAL_OK)
{
return FALSE;
}
else
{
return TRUE;
}
}
BOOL
xMBPortSerialGetByte( CHAR * pucByte )
{
/* Return the byte in the UARTs receive buffer. This function is called
* by the protocol stack after pxMBFrameCBByteReceived( ) has been called.
*/
// *pucByte = modbus_usart_rx_data;
// HAL_UART_Receive_IT(&huart2, &modbus_usart_rx_data, 1);
// return TRUE;
if (HAL_UART_Receive(&huart2, (uint8_t *)pucByte, 1, 100) != HAL_OK)
{
return FALSE;
}
else
{
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 )
{
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.
*/
void prvvUARTRxISR( void )
{
pxMBFrameCBByteReceived( );
}
六、移植定时器相关接口
/*
* 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: porttimer.c,v 1.1 2006/08/22 21:35:13 wolti Exp $
*/
/* ----------------------- Platform includes --------------------------------*/
#include "port.h"
#include "tim.h"
/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mbport.h"
/* ----------------------- static functions ---------------------------------*/
void prvvTIMERExpiredISR( void );
/* ----------------------- Start implementation -----------------------------*/
BOOL
xMBPortTimersInit( USHORT usTim1Timerout50us )
{
//因为定时器初始化已经由stm32cubemx生成,这里直接返回true
return TRUE;
}
inline void
vMBPortTimersEnable( )
{
/* Enable the timer with the timeout passed to xMBPortTimersInit( ) */
__HAL_TIM_CLEAR_IT(&htim3, TIM_IT_UPDATE);
__HAL_TIM_ENABLE_IT(&htim3, TIM_IT_UPDATE);
__HAL_TIM_SET_COUNTER(&htim3, 0);
__HAL_TIM_ENABLE(&htim3);
}
inline void
vMBPortTimersDisable( )
{
/* Disable any pending timers. */
__HAL_TIM_DISABLE(&htim3);
__HAL_TIM_SET_COUNTER(&htim3, 0);
__HAL_TIM_DISABLE_IT(&htim3, TIM_IT_UPDATE);
__HAL_TIM_CLEAR_IT(&htim3, TIM_IT_UPDATE);
}
/* 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.
*/
void prvvTIMERExpiredISR( void )
{
( void )pxMBPortCBTimerExpired( );
}
七、port.h相关接口移植
#define ENTER_CRITICAL_SECTION( ) __set_PRIMASK(1)
#define EXIT_CRITICAL_SECTION( ) __set_PRIMASK(0)
八、编写定时器和串口中断回调函数
/**
* @brief This function handles USART2 global interrupt.
*/
void USART2_IRQHandler(void)
{
/* USER CODE BEGIN USART2_IRQn 0 */
drv_usart_irq_handler_callbake(&huart2);
/* USER CODE END USART2_IRQn 0 */
HAL_UART_IRQHandler(&huart2);
/* USER CODE BEGIN USART2_IRQn 1 */
/* USER CODE END USART2_IRQn 1 */
}
extern void prvvUARTTxReadyISR(void);
extern void prvvUARTRxISR(void);
/******************************************************************************
* @Function: drv_usart2_rxne_irq_callbake
* @Description: USART2接收中断服务函数
* @Input: void
* @Output: None
* @Return: void
* @Others: None
* @param {UART_HandleTypeDef} *huart
*******************************************************************************/
void drv_usart2_rxne_irq_callbake(UART_HandleTypeDef *huart)
{
prvvUARTRxISR();
}
/******************************************************************************
* @Function: drv_usart2_txe_irq_callbake
* @Description: USART2发送为空中断服务函数
* @Input: void
* @Output: None
* @Return: void
* @Others: None
* @param {UART_HandleTypeDef} *huart
*******************************************************************************/
void drv_usart2_txe_irq_callbake(UART_HandleTypeDef *huart)
{
prvvUARTTxReadyISR();
}
/******************************************************************************
* @Function: drv_usart_irq_handler_callbake
* @Description: USART中断回调函数
* @Input: void
* @Output: None
* @Return: void
* @Others: None
* @param {UART_HandleTypeDef} *huart
*******************************************************************************/
void drv_usart_irq_handler_callbake(UART_HandleTypeDef *huart)
{
if (huart->Instance == USART2)
{
if(__HAL_UART_GET_IT_SOURCE(huart, UART_IT_RXNE)!= RESET)
{
drv_usart2_rxne_irq_callbake(huart);
}
if(__HAL_UART_GET_IT_SOURCE(huart, UART_IT_TXE)!= RESET)
{
drv_usart2_txe_irq_callbake(huart);
}
HAL_NVIC_ClearPendingIRQ(USART2_IRQn);
}
}
extern void prvvTIMERExpiredISR( void );
/******************************************************************************
* @Function: drv_tim3_update_irq_callbake
* @Description: TIM3更新中断服务函数
* @Input: void
* @Output: None
* @Return: void
* @Others: None
*******************************************************************************/
void drv_tim3_update_irq_callbake(void)
{
prvvTIMERExpiredISR();
}
/******************************************************************************
* @Function: HAL_TIM_PeriodElapsedCallback
* @Description: TIM更新中断回调函数
* @Input: void
* @Output: None
* @Return: void
* @Others: None
* @param {TIM_HandleTypeDef} *htim
*******************************************************************************/
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if (htim->Instance == TIM3)
{
drv_tim3_update_irq_callbake();
}
}
九、编写处理函数
-
因为我只用到输入寄存器和保持寄存器,这里只给出这两个处理函数示例:
- pal_modbus.h
#ifndef __PAL_MODBUS_H__ #define __PAL_MODBUS_H__ #include <stdint.h> #ifndef MODBUS_SLAVE_ADDR #define MODBUS_SLAVE_ADDR 0x5A #endif #define MODBUS_INPUT_REGISTER_DEFINE(reg) (reg+1) #define MODBUS_HOLD_REGISTER_DEFINE(reg) (reg+1) typedef struct { uint16_t reg; uint8_t reg_max_len; void (*write)(uint8_t *register_val, uint16_t len, uint16_t index); void (*read)(uint8_t *register_val, uint16_t len, uint16_t index); }modbus_cmd_ops_stu_t; typedef struct { uint16_t reg_addr; uint8_t read_write_reg_cnt; uint8_t read_write_reg_index; }modbus_debug_info_stu_t; #endif
- pal_modbus.c
#include "pal_modbus.h"
#include "mb.h"
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
modbus_cmd_ops_stu_t input_reg[] =
{
{MODBUS_INPUT_REGISTER_DEFINE(0x30A0), 2, NULL, read_power_off_request},
{MODBUS_INPUT_REGISTER_DEFINE(0x30D0), 4, NULL, read_temperature_data},
{MODBUS_INPUT_REGISTER_DEFINE(0x30FC), 10, NULL, read_uitrasonic_data},
{MODBUS_INPUT_REGISTER_DEFINE(0x3308), 40, NULL, read_mcu_version_info},
{MODBUS_INPUT_REGISTER_DEFINE(0x3400), 12, NULL, read_rtc_data},
{MODBUS_INPUT_REGISTER_DEFINE(0x3500), 4, NULL, read_periphral_status},
};
modbus_cmd_ops_stu_t hold_reg[] =
{
{MODBUS_HOLD_REGISTER_DEFINE(0x4132), 2, write_power_on_or_off_respond, NULL},
};
/******************************************************************************
* @Function: eMBRegInputCB
* @Description: 读输入寄存器处理函数
* @Input: pucRegBuffer:存放要读取得数据的地址
* usAddress:读取的寄存器地址
* usNRegs:要读取的寄存器的个数
* @Output: None
* @Return: void
* @Others: 对应功能码04
* @param {UCHAR} *pucRegBuffer
* @param {USHORT} usAddress
* @param {USHORT} usNRegs
*******************************************************************************/
eMBErrorCode eMBRegInputCB(UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs)
{
eMBErrorCode eStatus = MB_ENOERR;
uint8_t i = 0;
uint8_t index = 0;
for (i = 0; i < ARRAY_SIZE(input_reg); i++)
{
if ((usAddress >= input_reg[i].reg) && ((usAddress + usNRegs) <= (input_reg[i].reg+input_reg[i].reg_max_len)))
{
index = usAddress-input_reg[i].reg;
input_reg[i].read(pucRegBuffer, 2*usNRegs, 2*index);
eStatus = MB_ENOERR;
break;
}
else
{
eStatus = MB_ENOREG;
}
}
return eStatus;
}
/******************************************************************************
* @Function: eMBRegHoldingCB
* @Description: 保持寄存器处理函数
* @Input: pucRegBuffer:读写数据地址
* usAddress:读写寄存器地址
* usNRegs:读写寄存器个数
* eMode:读写模式
* @Output: None
* @Return: void
* @Others: 对应功能码03、06、16
* @param {UCHAR} *pucRegBuffer
* @param {USHORT} usAddress
* @param {USHORT} usNRegs
* @param {eMBRegisterMode} eMode
*******************************************************************************/
eMBErrorCode eMBRegHoldingCB(UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs, eMBRegisterMode eMode)
{
eMBErrorCode eStatus = MB_ENOERR;
uint8_t i = 0;
uint8_t index = 0;
for (i = 0; i < ARRAY_SIZE(hold_reg); i++)
{
if ((usAddress >= hold_reg[i].reg) && (usAddress <= hold_reg[i].reg+hold_reg[i].reg_max_len))
{
switch (eMode)
{
case MB_REG_READ:
index = usAddress-hold_reg[i].reg;
hold_reg[i].read(pucRegBuffer, 2*usNRegs, 2*index);
eStatus = MB_ENOERR;
break;
case MB_REG_WRITE:
index = usAddress-hold_reg[i].reg;
hold_reg[i].write(pucRegBuffer, 2*usNRegs, 2*index);
eStatus = MB_ENOERR;
break;
}
}
else
{
eStatus = MB_ENOREG;
}
}
return eStatus;
}
/******************************************************************************
* @Function: eMBRegCoilsCB
* @Description: 线圈寄存器处理函数
* @Input: pucRegBuffer:读写数据地址
* usAddress:读写寄存器地址
* usNRegs:读写寄存器个数
* eMode:读写模式
* @Output: None
* @Return: void
* @Others: 对应功能码01、05、15
* @param {UCHAR} *pucRegBuffer
* @param {USHORT} usAddress
* @param {USHORT} usNCoils
* @param {eMBRegisterMode} eMode
*******************************************************************************/
eMBErrorCode eMBRegCoilsCB(UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNCoils, eMBRegisterMode eMode)
{
eMBErrorCode eStatus = MB_ENOERR;
return eStatus;
}
/******************************************************************************
* @Function: eMBRegDiscreteCB
* @Description:
* @Input: pucRegBuffer:存放要读取得数据的地址
* usAddress:读取的寄存器地址
* usNRegs:要读取的寄存器的个数
* @Output: None
* @Return: void
* @Others: 对应功能码02
* @param {UCHAR} *pucRegBuffer
* @param {USHORT} usAddress
* @param {USHORT} usNDiscrete
*******************************************************************************/
eMBErrorCode eMBRegDiscreteCB(UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNDiscrete)
{
eMBErrorCode eStatus = MB_ENOERR;
return eStatus;
}
十、开启Modbus功能
/******************************************************************************
* @Function: pal_modbus_init
* @Description: MODBUS初始化
* @Input: void
* @Output: None
* @Return: void
* @Others: None
*******************************************************************************/
void pal_modbus_init(void)
{
eMBInit(MB_RTU, MODBUS_SLAVE_ADDR, 0x02, 115200, MB_PAR_NONE);
eMBSetSlaveID(MODBUS_SLAVE_ADDR, TRUE, NULL, 0);
eMBEnable();
while (1)
{
eMBPoll();
}
}
以上是关于STM32移植FreeModbus的主要内容,如果未能解决你的问题,请参考以下文章