freemodbus 主机源码 freemodbus stm32
chenpack 2025-06-20 16:30 20 浏览 0 评论
modbus是一个非常好的串口协议(当然也能用在网口上),它简洁、规范、强大。可以满足大部分的工业、嵌入式需求。
这里详细说下如何将freemodbus移植到stm32平台。我之前下载的版本是,当前官网最新的版本是。两者差别不大,这里以版本做演示。
1、下载
下载好之后,解压得到如下内容:
我们需要的是modbus这个文件夹,和demo->BARE下的port文件夹。
2、准备一个STM32的工程文件夹
在工程文件夹下新建一个文件夹:FreeModbus。将第一步获取的两个文件夹放到里面。
打开工程,添加两个group,名字分别为modbus和port。将这两个文件夹下的C文件都添加进来,tcp相关的除外。
文件包含路径,也添加这几个文件夹的位置:
3、完善portserial.c文件
该文件就是modbus通信中用到的串口的初始化配置文件。我这里选择usart1,波特率.
第一次打开这个文件,内容如下:
void
vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable )
{
}
BOOL
xMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity )
{
return FALSE;
}
BOOL
xMBPortSerialPutByte( CHAR ucByte )
{
return TRUE;
}
认真看一下函数名字,你会发现这些函数分别是:串口使能、串口初始化、发送一个字节、接收一个字节等等。
完善后代码如下:
void
vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable )
{
if(xRxEnable == TRUE)
{
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
}
else
{
USART_ITConfig(USART1, USART_IT_RXNE, DISABLE);
}
if(xTxEnable == TRUE)
{
USART_ITConfig(USART1, USART_IT_TC, ENABLE);
}
else
{
USART_ITConfig(USART1, USART_IT_TC, DISABLE);
}
}
BOOL
xMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity )
{
USART1_Config((uint16_t)ulBaudRate);
USART_NVIC();
return TRUE;
}
BOOL
xMBPortSerialPutByte( CHAR ucByte )
{
USART_SendData(USART1, ucByte);
return TRUE;
}
BOOL
xMBPortSerialGetByte( CHAR * pucByte )
{
*pucByte = USART_ReceiveData(USART1);
return TRUE;
}
static void prvvUARTTxReadyISR( void )
{
pxMBFrameCBTransmitterEmpty( );
}
static void prvvUARTRxISR( void )
{
pxMBFrameCBByteReceived( );
}
void USART1_IRQHandler(void)
{
if(USART_GetITStatus(USART1, USART_IT_RXNE) == SET)
{
prvvUARTRxISR();
USART_ClearITPendingBit(USART1, USART_IT_RXNE);
}
if(USART_GetITStatus(USART1, USART_IT_ORE) == SET)
{
USART_ClearITPendingBit(USART1, USART_IT_ORE);
prvvUARTRxISR();
}
if(USART_GetITStatus(USART1, USART_IT_TC) == SET)
{
prvvUARTTxReadyISR();
USART_ClearITPendingBit(USART1, USART_IT_TC);
}
}
其中USART1_Config((uint16_t)ulBaudRate);和 USART_NVIC();是串口初始化的代码,如下:
void USART1_Config(uint16_t buad)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
USART_InitStructure.USART_BaudRate = buad;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No ;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init(USART1, &USART_InitStructure);
USART_Cmd(USART1, ENABLE);
}
void USART_NVIC(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
4、完善porttimer.c文件
modbus工作时需要一个定时器,所以这里配置一个定时器。定时器时基是50us,周期做为参数输入。代码如下:
BOOL
xMBPortTimersInit( USHORT usTim1Timerout50us )
{
timer2_init(usTim1Timerout50us);
timer2_nvic();
return TRUE;
}
void vMBPortTimersEnable( )
{
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
TIM_SetCounter(TIM2,0x0000);
TIM_Cmd(TIM2, ENABLE);
}
void vMBPortTimersDisable( )
{
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
TIM_ITConfig(TIM2, TIM_IT_Update, DISABLE);
TIM_SetCounter(TIM2,0x0000);
TIM_Cmd(TIM2, DISABLE);
}
static void prvvTIMERExpiredISR( void )
{
( void )pxMBPortCBTimerExpired( );
}
void TIM2_IRQHandler(void)
{
if(TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET)
{
prvvTIMERExpiredISR();
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
}
}
其中 timer2_init(usTim1Timerout50us) 和 timer2_nvic() 是timer2初始化函数,内容如下:
void timer2_init(uint16_t period)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
TIM_DeInit(TIM2);
TIM_TimeBaseStructure.TIM_Period = period;
TIM_TimeBaseStructure.TIM_Prescaler = ( - 1);
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
TIM_Cmd(TIM2, ENABLE);
}
void timer2_nvic(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
5、在main.c文件中,定义各个模拟寄存器的地址和大小。
#define REG_INPUT_START 0x0000
#define REG_INPUT_NREGS 8
#define REG_HOLDING_START 0x0000
#define REG_HOLDING_NREGS 8
#define REG_COILS_START 0x0000
#define REG_COILS_SIZE
#define REG_DISCRETE_START 0x0000
#define REG_DISCRETE_SIZE
6
补全输入寄存器操作函数、保持寄存器操作函数
modbus功能进行初始化,设置地址和波特率。这部分内容可以参考官方资料里的例程,也可以直接复制别人写好的。我这里放别人写好的代码:
int main(void)
{
usRegInputBuf[0] = 'I';
usRegInputBuf[1] = ' ';
usRegInputBuf[2] = 'a';
usRegInputBuf[3] = 'm';
usRegInputBuf[4] = ' ';
usRegInputBuf[5] = 'I';
RCC_Config();
eMBInit(MB_RTU, 0x01, 0x01, , MB_PAR_NONE);
eMBEnable();
for(;;)
{
(void)eMBPoll();
}
}
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++ = ( UCHAR )( usRegInputBuf[iRegIndex] >> 8 );
*pucRegBuffer++ = ( UCHAR )( usRegInputBuf[iRegIndex] & 0xFF );
iRegIndex++;
usNRegs--;
}
}
else
{
eStatus = MB_ENOREG;
}
return eStatus;
}
eMBErrorCode
eMBRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs, eMBRegisterMode eMode )
{
eMBErrorCode eStatus = MB_ENOERR;
int iRegIndex;
if((usAddress >= REG_HOLDING_START)&&\
((usAddress+usNRegs) <= (REG_HOLDING_START + REG_HOLDING_NREGS)))
{
iRegIndex = (int)(usAddress - usRegHoldingStart);
switch(eMode)
{
case MB_REG_READ:
while(usNRegs > 0)
{
*pucRegBuffer++ = (u8)(usRegHoldingBuf[iRegIndex] >> 8);
*pucRegBuffer++ = (u8)(usRegHoldingBuf[iRegIndex] & 0xFF);
iRegIndex++;
usNRegs--;
}
break;
case MB_REG_WRITE:
while(usNRegs > 0)
{
usRegHoldingBuf[iRegIndex] = *pucRegBuffer++ << 8;
usRegHoldingBuf[iRegIndex] |= *pucRegBuffer++;
iRegIndex++;
usNRegs--;
}
}
}
else
{
eStatus = MB_ENOREG;
}
return eStatus;
}
eMBErrorCode
eMBRegCoilsCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNCoils, eMBRegisterMode eMode )
{
eMBErrorCode eStatus = MB_ENOERR;
int iRegIndex;
if((usAddress >= REG_HOLDING_START)&&\
((usAddress+usNCoils) <= (REG_HOLDING_START + REG_HOLDING_NREGS)))
{
iRegIndex = (int)(usAddress - usRegHoldingStart);
switch(eMode)
{
case MB_REG_READ:
while(usNCoils > 0)
{
iRegIndex++;
usNCoils--;
}
break;
case MB_REG_WRITE:
while(usNCoils > 0)
{
iRegIndex++;
usNCoils--;
}
}
}
else
{
eStatus = MB_ENOREG;
}
return eStatus;
}
eMBErrorCode
eMBRegDiscreteCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNDiscrete )
{
( void )pucRegBuffer;
( void )usAddress;
( void )usNDiscrete;
return MB_ENOREG;
}
7、修改mbrtu.c文件
否则modbus从机收到命令后,只会返回一次数据。在函数“eMBRTUSend”中。
eMBErrorCode
eMBRTUSend( UCHAR ucSlaveAddress, const UCHAR * pucFrame, USHORT usLength )
{
eMBErrorCode eStatus = MB_ENOERR;
USHORT usCRC16;
ENTER_CRITICAL_SECTION( );
if( eRcvState == STATE_RX_IDLE )
{
pucSndBufferCur = ( UCHAR * ) pucFrame - 1;
usSndBufferCount = 1;
pucSndBufferCur[MB_SER_PDU_ADDR_OFF] = ucSlaveAddress;
usSndBufferCount += usLength;
usCRC16 = usMBCRC16( ( UCHAR * ) pucSndBufferCur, usSndBufferCount );
ucRTUBuf[usSndBufferCount++] = ( UCHAR )( usCRC16 & 0xFF );
ucRTUBuf[usSndBufferCount++] = ( UCHAR )( usCRC16 >> 8 );
eSndState = STATE_TX_XMIT;
xMBPortSerialPutByte( ( CHAR )*pucSndBufferCur );
pucSndBufferCur++;
usSndBufferCount--;
vMBPortSerialEnable( FALSE, TRUE );
}
else
{
eStatus = MB_EIO;
}
EXIT_CRITICAL_SECTION( );
return eStatus;
}
8、修改mbconfig.h文件
取消对ASCII的支持。
#define MB_ASCII_ENABLED ( 0 )
9、保存,编译,下载。使用专用的modbus工具测试
工具配置如下:
modbus指令格式如下:
咱们这里设置如下: ,功能码,起始地址0,数据长度2.校验码没有写怎么办?
这就是这个工具的便利之处!我们不用管,它会自动计算!直接点击发送即可。得到结果如下:
可以看到下面的框里,绿色的是我们发送的内容,最后两位是工具自动补上的。蓝色内容是单片机(也就是modbus从机)返回给我们的。
相关推荐
- PC傻瓜式安装黑苹果并打造成全能逆向工作站
-
黑苹果详细安装教程-基于OpenCore官网指导-UPUPMO文章大纲.必备知识.作者当前硬件说明.主板BIOS版本升级.确定声卡、网卡信息.配置EFI驱动.配置ACP...
- SpaceX成功海上回收火箭(附视频):从星辰到大海!
-
SpaceX实现海上回收火箭!除了惊叹,你还应该知道这些!三体迷【地球往事专栏】今天一大早,就被SpaceX成功实现海上回收火箭的消息给刷屏了,确实是一个振奋人心的消息!北京时间年4月9日4时分,Sp...
- 讲真,网络工程师家里的wifi速度到底能有多快?
-
讲真,网络工程师家里的wifi速度到底能有多快?老杨身边挺多亲戚朋友总有这样的问题,wifi已经买的很高级了,可以信号还是不好。但是这件事情,对于咱们网络工程师来说,就是小case了。老杨今天就来解释...
- RouterOS 端口映射 (远程桌面)
-
一款功能强大的路由器系统-软路由-RouterOS推荐一款路由器系统,头条有很多喜欢使用软路由伙伴可能有很多都不知道RouterOS这个路由系统是,RouterOS是由拉脱维亚MikroTik...
- 神经网络中的编码器 神经网络视频编码
-
神经网络算法-一文搞懂Transformer(总体架构&三种注意力层)本文将从Transformer的本质、Transformer的原理、Transformer的应用三个方面,带您一...
- 必备资料103个WindowsXP运行命令
-
Windows中CMD最全命令行CMD命令:开始->运行(或者Windows+R)->键入cmd或command(在命令行里可以看到系统版本、文件系统版本)CMD命令锦集1.gpedit...
- 固态硬盘无法格式化怎么办
-
Windows中固态硬盘无法格式化怎么办?固态硬盘(简称SSD)是一种数据存储设备,与传统机械硬盘相比,它在许多方面表现得更好。因此,越来越多的用户希望使用固态硬盘,但是当人们购买固态硬盘后准备将其格...
- 手机信令数据分析_手机信令数据分析过程代码
-
清华大学公共管理学院刘志林教授:探索手机信令数据在城市治理中的应用中国发展网讯日前,由中国人民大学首都发展与战略研究院(以下简称“首发院”)主办的首都大讲堂(第7期)暨地方治理工作坊第二期在京举办。...
- python开发ping工具 ipad python开发工具
-
python之ping主机#coding=utf-8frompythonpingimportpingforiinrange,):ip=.+str(i)...
- 云容灾关键技术点简介_云容器技术
-
阿里云发布企业级云灾备解决方案:一键容灾、成本节省%5月日,阿里云对外发布了企业级云灾备解决方案。据介绍,此次发布的灾备解决方案来自阿里巴巴IT基础设施云化的灾备经验,完全省去灾备机房的建设规划,可大...
- 域名泛解析设置_域名解析包括泛域名解析
-
如何降低域名被恶意泛解析的风险买车用车不想被忽悠,就请关注缸微信号:kf12gang←长按可复制。我们每天将免费为您解答选车用车的相关问题。作者:QQ126058域名被恶意泛解析是域名安全最常见的问...
- 人人通云平台怎么注册 人人通云教学登录账号
-
世界那么大,她看到了:一个心理咨询师的十年心灵之旅来源:环球网“世界那么大,我想去看看。”十年前的那个春天,十个字的辞职信,戳中了无数国人的心,激起了无数个“诗和远方”的小梦想,被称为“史上最具情怀...
- 民用远程监控手机软件_民用远程监控手机软件下载
-
屏幕监控软件有哪些?3款好用的监控软件分享!管控摸鱼小case!作为企业管理者,我深知员工工作效率和信息安全的重要性。在日常管理中,我时常会遇到这样的难题:员工是否在认真工作?有没有利用公司资源做与工...
- 重量级!Maven史上最全教程,看了必懂
-
对8个MCP服务器框架的比较作者:FrankGoortani编译:小兰引言模型上下文协议(MCP)是一种新标准,用于以统一方式将AI助手(如LLM)与外部数据源和工具连接起来。自推出以来,各种框架已...
- 面试字节跳动,收到offer后我却拒绝了 ,给面试人的一些忠告!
-
面试字节跳动,我被面试官狂怼全过程!来源:https://www.zhihu.com/question//answer/人们都说,这个世界上有两种人注定单身,一种是太优秀的,另一种是太平凡的。我一听呀...
你 发表评论:
欢迎- 一周热门
-
-
维基百科Wikipedia镜像网站列表
-
超炫html+css+javascript幻化3D相册 (含背景音乐)程序员表白必备
-
6款图片查看器,丝滑干净无广告!(图片查看器软件)
-
不能读取文件“itunes.library.itl”因为它是由更高级别的itunes所创建的
-
用java编写一个QQ群发信息_用java语言写qq聊天程序
-
StreamReader StringReader 区别 reader和inputstream的区别
-
安卓系统手机文件夹及其文件详细解析
-
Windows Server 2003 详细安装与配置
-
作为一名独立开发者,我是如何建立我的科技创业公司的
-
计算机集成制造系统有哪些_计算机集成制造系统有哪些类型
-
- 最近发表
- 标签列表
-
- int.tryparse (62)
- list转list (108)
- repeat函数 (66)
- git force (69)
- springboot /error (71)
- mysql 更新 (74)
- save as pdf (63)
- lock tables (66)
- 同步 异步 阻塞 非阻塞 (62)
- rsyslog (66)
- querystring (63)
- c++ override (70)
- css 动画库 (61)
- vsphere web client (65)
- int32_t (63)
- c# task.run (68)
- find -size (64)
- golang flag包 (70)
- 二维数组作为参数传入函数 (62)
- sudo su root (60)
- crontab 安装 (61)
- c# 数组转成list (60)
- 下拉按钮 (64)
- 滚动条美化 (61)
- stringutils (61)