stm32F407移植MODBUS协议栈
发帖作为一个记录留存
参考文章
FreeModbus RTU 从机Hal库裸机移植避坑指南 – Atul-8 – 博客园
大致流程:
1.首先下载官方的文件
为了方便加入工程,将以下所有路径下的文件全部转移到新建文件夹中
demo里的BARE下port文件中的.c和.h
modbus文件夹中的所有文件 (每个文件夹都打开,将里面的.c.h复制出来,还有mb.c)
modbus是我自己建的文件夹 用来存放以上的所有文件
如图 这些就是我们所需要用的文件
2.工程的配置
先在移植之前保证自己的串口和定时器是可以正常使用的
这里需要注意的就是定时器的配置 我的芯片是168Mhz ,为了实现50us的定时,配置如下
不同频率可以自己用公式计算,或者直接问ai也是一个好主意?
打开串口和定时器的中断,并关闭HAL库自己生成的中断函数
3.移植文件的修改
3.1 portserial.c
框出来的两个是我所使用的485的控制引脚,如果你也使用485可以将控制引脚放到这里,不适用485的话就将这两句删除即可
xMBPortSerialInit函数
直接调用hal库的串口初始化即可
xMBPortSerialPutByte( CHAR ucByte )和xMBPortSerialGetByte 这两个是发送接收字符函数,此处不可以使用hal库的串口发送接收函数
最后则是手动编写中断函数
portserial.c
#include "port.h"
#include "usart.h"
#include "gpio.h"
/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mbport.h"
#define GPIO_485_CONT_PORT GPIOC
#define GPIO_485_CONT_PIN GPIO_PIN_5
/* ----------------------- static functions ---------------------------------*/
static void prvvUARTTxReadyISR( void );
static void prvvUARTRxISR( void );
/* ----------------------- Start implementation -----------------------------*/
void
vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable )
{
/* If xRXEnable enable serial receive interrupts. If xTxENable enable
* transmitter empty interrupts.
*/
if(xRxEnable)
{
HAL_GPIO_WritePin(GPIO_485_CONT_PORT,GPIO_485_CONT_PIN,GPIO_PIN_RESET);
__HAL_UART_ENABLE_IT(&huart1, UART_IT_RXNE); // 使能接收非空中断
}
else
{
__HAL_UART_DISABLE_IT(&huart1, UART_IT_RXNE); // 禁能接收非空中断
}
if(xTxEnable)
{
//
HAL_GPIO_WritePin(GPIO_485_CONT_PORT,GPIO_485_CONT_PIN,GPIO_PIN_SET);
//
// MAX485_SetMode(MODE_SENT);
__HAL_UART_ENABLE_IT(&huart1, UART_IT_TXE); // 使能发送为空中断
}
else
{
__HAL_UART_DISABLE_IT(&huart1, UART_IT_TXE); // 禁能发送为空中断
}
}
BOOL
xMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity )
{
MX_USART1_UART_Init();
return TRUE;
}
BOOL
xMBPortSerialPutByte( CHAR ucByte )
{
USART1->DR = ucByte;
return TRUE;
}
BOOL
xMBPortSerialGetByte( CHAR * pucByte )
{
*pucByte = (USART1->DR & (uint16_t)0x00FF);
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( );
}
void USART1_IRQHandler(void)
{
if(__HAL_UART_GET_FLAG(&huart1, UART_FLAG_RXNE))
{
__HAL_UART_CLEAR_FLAG(&huart1, UART_FLAG_RXNE);
prvvUARTRxISR();
}
if(__HAL_UART_GET_FLAG(&huart1, UART_FLAG_TXE))
{
__HAL_UART_CLEAR_FLAG(&huart1, UART_FLAG_TXE);
prvvUARTTxReadyISR();
}
}
3.2 porttimer.c
xMBPortTimersInit函数
初始化定时器,调用HAL库的初始化即可
vMBPortTimersEnable函数,使能定时器;vMBPortTimersDisable函数;关闭定时器
然后手动添加定时器中断函数
/*
* 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 <stdio.h>
/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mbport.h"
#include "tim.h"
/* ----------------------- static functions ---------------------------------*/
static void prvvTIMERExpiredISR( void );
/* ----------------------- Start implementation -----------------------------*/
BOOL
xMBPortTimersInit( USHORT usTim1Timerout50us )
{
MX_TIM4_Init();
__HAL_TIM_CLEAR_FLAG(&htim4, TIM_FLAG_UPDATE); // 先清除一下定时器的中断标记,防止使能中断后直接触发中断
__HAL_TIM_ENABLE_IT(&htim4, TIM_IT_UPDATE); // 使能定时器更新中断
return TRUE;
}
inline void
vMBPortTimersEnable( )
{
__HAL_TIM_SET_COUNTER(&htim4, 0); // 清空计数器
__HAL_TIM_ENABLE(&htim4); // 使能定时器
/* Enable the timer with the timeout passed to xMBPortTimersInit( ) */
}
inline void
vMBPortTimersDisable( )
{
__HAL_TIM_DISABLE(&htim4); // 禁能定时器
/* Disable any pending timers. */
}
/* Create an ISR which is called whenever the timer has expired. This function
* must then call pxMBPortCBTimerExpired( ) to notify the protocol stack that
* the timer has expired.
*/
static void prvvTIMERExpiredISR( void )
{
( void )pxMBPortCBTimerExpired( );
}
/// 定时器4中断服务程序
void TIM4_IRQHandler(void)
{
if(__HAL_TIM_GET_FLAG(&htim4, TIM_FLAG_UPDATE))
{
__HAL_TIM_CLEAR_FLAG(&htim4, TIM_FLAG_UPDATE);
prvvTIMERExpiredISR();
}
}
3.3 port.c
这个是自己新建一个.C文件添加进工程,将以下代码复制进去即可
这个文件就是FreeModbus移植的关键位置了, 其通讯都是在调用这些信息;
以下直接贴验证的好代码, 想要自定义就修改前面那四个寄存器就可以;
#include "mb.h"
#include "mbport.h"
// 十路输入寄存器
#define REG_INPUT_SIZE 10
uint16_t REG_INPUT_BUF[REG_INPUT_SIZE];
// 十路保持寄存器
#define REG_HOLD_SIZE 10
uint16_t REG_HOLD_BUF[REG_HOLD_SIZE];
// 十路线圈
#define REG_COILS_SIZE 10
uint8_t REG_COILS_BUF[REG_COILS_SIZE] = {1, 1, 1, 1, 0, 0, 0, 0, 1, 1};
// 十路离散量
#define REG_DISC_SIZE 10
uint8_t REG_DISC_BUF[REG_DISC_SIZE] = {1,1,1,1,0,0,0,0,1,1};
/// CMD4命令处理回调函数
eMBErrorCode eMBRegInputCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs )
{
USHORT usRegIndex = usAddress - 1;
// 非法检测
if((usRegIndex + usNRegs) > REG_INPUT_SIZE)
{
return MB_ENOREG;
}
// 循环读取
while( usNRegs > 0 )
{
*pucRegBuffer++ = ( unsigned char )( REG_INPUT_BUF[usRegIndex] >> 8 );
*pucRegBuffer++ = ( unsigned char )( REG_INPUT_BUF[usRegIndex] & 0xFF );
usRegIndex++;
usNRegs--;
}
// 模拟输入寄存器被改变
for(usRegIndex = 0; usRegIndex < REG_INPUT_SIZE; usRegIndex++)
{
REG_INPUT_BUF[usRegIndex]++;
}
return MB_ENOERR;
}
/// CMD6、3、16命令处理回调函数
eMBErrorCode eMBRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs, eMBRegisterMode eMode )
{
USHORT usRegIndex = usAddress - 1;
// 非法检测
if((usRegIndex + usNRegs) > REG_HOLD_SIZE)
{
return MB_ENOREG;
}
// 写寄存器
if(eMode == MB_REG_WRITE)
{
while( usNRegs > 0 )
{
REG_HOLD_BUF[usRegIndex] = (pucRegBuffer[0] << 8) | pucRegBuffer[1];
pucRegBuffer += 2;
usRegIndex++;
usNRegs--;
}
}
// 读寄存器
else
{
while( usNRegs > 0 )
{
*pucRegBuffer++ = ( unsigned char )( REG_HOLD_BUF[usRegIndex] >> 8 );
*pucRegBuffer++ = ( unsigned char )( REG_HOLD_BUF[usRegIndex] & 0xFF );
usRegIndex++;
usNRegs--;
}
}
return MB_ENOERR;
}
/// CMD1、5、15命令处理回调函数
eMBErrorCode eMBRegCoilsCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNCoils, eMBRegisterMode eMode )
{
USHORT usRegIndex = usAddress - 1;
UCHAR ucBits = 0;
UCHAR ucState = 0;
UCHAR ucLoops = 0;
// 非法检测
if((usRegIndex + usNCoils) > REG_COILS_SIZE)
{
return MB_ENOREG;
}
if(eMode == MB_REG_WRITE)
{
ucLoops = (usNCoils - 1) / 8 + 1;
while(ucLoops != 0)
{
ucState = *pucRegBuffer++;
ucBits = 0;
while(usNCoils != 0 && ucBits < 8)
{
REG_COILS_BUF[usRegIndex++] = (ucState >> ucBits) & 0X01;
usNCoils--;
ucBits++;
}
ucLoops--;
}
}
else
{
ucLoops = (usNCoils - 1) / 8 + 1;
while(ucLoops != 0)
{
ucState = 0;
ucBits = 0;
while(usNCoils != 0 && ucBits < 8)
{
if(REG_COILS_BUF[usRegIndex])
{
ucState |= (1 << ucBits);
}
usNCoils--;
usRegIndex++;
ucBits++;
}
*pucRegBuffer++ = ucState;
ucLoops--;
}
}
return MB_ENOERR;
}
/// CMD2命令处理回调函数
eMBErrorCode eMBRegDiscreteCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNDiscrete )
{
USHORT usRegIndex = usAddress - 1;
UCHAR ucBits = 0;
UCHAR ucState = 0;
UCHAR ucLoops = 0;
// 非法检测
if((usRegIndex + usNDiscrete) > REG_DISC_SIZE)
{
return MB_ENOREG;
}
ucLoops = (usNDiscrete - 1) / 8 + 1;
while(ucLoops != 0)
{
ucState = 0;
ucBits = 0;
while(usNDiscrete != 0 && ucBits < 8)
{
if(REG_DISC_BUF[usRegIndex])
{
ucState |= (1 << ucBits);
}
usNDiscrete--;
usRegIndex++;
ucBits++;
}
*pucRegBuffer++ = ucState;
ucLoops--;
}
// 模拟离散量输入被改变
for(usRegIndex = 0; usRegIndex < REG_DISC_SIZE; usRegIndex++)
{
REG_DISC_BUF[usRegIndex] = !REG_DISC_BUF[usRegIndex];
}
return MB_ENOERR;
}
main.c的编写
截两张参考文章的内容有需要可自行查看 ,链接在顶部
自此移植结束 可以使用工具发送进行测试
使用串口调试助手的话 一定要勾选 16进制发送 和16进制接收 over
作者:a234abr