STM32F0使用LL库实现Modbus通讯
在本次项目中,限于空间要求我们选用了STM32F030F4作为控制芯片。这款MCU不但封装紧凑,而且自带的Flash空间也非常有限,所以我们选择了LL库实现。本篇将说明基于LL实现USART通讯。
1、概述
我们想要实现基于RS485的Modbus通讯实际就是基于USART的通讯。USART使用可编程波特率发生器提供非常广泛的波特率范围。根据不同配置可以实现我们不同的串行通讯应用需求。其结构图如下:

我们在USART基础上实现一个Modbus的从站应用,所以我们对于接收采用中断接收,而对于发送则根据接收到的信息做出反馈。所以我们要根据接收状态和发送状态来确定我们的操作。
2、USART配置
在实现之前,我们来了解一下满足前述的需求,我们应该做哪些配置。主要有2个寄存器需要注意:控制寄存器1(USART_CR1)和波特率寄存器(USART_BRR)。
首先我们还是来看一看控制寄存器1(USART_CR1)。其中RXNEIE(RXNE中断使能)、TE(发送使能)、RE(接收使能)、UE(USART使能)等位是需要我们注意的。该寄存器的结构如下:

在控制寄存器1(USART_CR1)中,RXNEIE(RXNE中断使能)被置位后,只要USART_ISRORE=1或者RXNE=1就会产生该中断。接下来我们看一看波特率寄存器(USART_BRR),其结构如下:

对于波特率寄存器(USART_BRR),顾名思义就是设置波特率。但波特率的值不是随意设置,有一套计算方法,可以查看STM的手册。
3、软件实现
接下来我们在软件上实现我们的应用。我们先看USART配置,我们将其配置为我们需要的参数,并将其配置为中断接收模式。具体代码如下:
/*配置上位通讯串口*/
static void Comm_UART_Configuration(void)
{
LL_USART_InitTypeDef USART_InitStruct = {0};
LL_GPIO_InitTypeDef GPIO_InitStruct = {0};
/* 使能相关外设时钟 */
LL_APB1_GRP2_EnableClock(LL_APB1_GRP2_PERIPH_USART1);
LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOA);
/* USART1 GPIO 配置:PA2 ------> USART1_TX
PA3 ------> USART1_RX */
GPIO_InitStruct.Pin = LL_GPIO_PIN_2;
GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
GPIO_InitStruct.Alternate = LL_GPIO_AF_1;
LL_GPIO_Init(GPIOA, &GPIO_InitStruct);
GPIO_InitStruct.Pin = LL_GPIO_PIN_3;
GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
GPIO_InitStruct.Alternate = LL_GPIO_AF_1;
LL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* USART1中断初始化 */
NVIC_SetPriority(USART1_IRQn, 1);
NVIC_EnableIRQ(USART1_IRQn);
/* USART1端口配置 */
USART_InitStruct.BaudRate = 115200;
USART_InitStruct.DataWidth = LL_USART_DATAWIDTH_8B;
USART_InitStruct.StopBits = LL_USART_STOPBITS_1;
USART_InitStruct.Parity = LL_USART_PARITY_NONE;
USART_InitStruct.TransferDirection = LL_USART_DIRECTION_TX_RX;
USART_InitStruct.HardwareFlowControl = LL_USART_HWCONTROL_NONE;
USART_InitStruct.OverSampling = LL_USART_OVERSAMPLING_16;
LL_USART_Init(USART1, &USART_InitStruct);
LL_USART_DisableIT_CTS(USART1);
LL_USART_ConfigAsyncMode(USART1);
LL_USART_Enable(USART1);
LL_USART_EnableIT_RXNE(USART1);
}
大部分配置都可通过初始化函数来实现,有一些需同过专门的LL库函数实现。配置完成后寄存器的状态如下图所示。

我们配置的波特率是115200,其在寄存器中的配置为1A1,关于波特率的配置问题可以查看相关资料。另外我们还需要编写一个接收中断的服务函数。
/*数据接收中断处理函数,添加到USART1中断响应函数中*/
void USART1_ReceiveDataHandle(void)
{
if(rxLength>=RECEIVEDATALENGTH)
{
rxLength=0;
}
/*接收寄存器为空,等待字节被对应的串口完全接收*/
if(LL_USART_IsActiveFlag_RXNE(USART1))
{
uint8_t rData;
/*获取接收到的字节*/
rData=LL_USART_ReceiveData8(USART1);
rxBuffer[rxLength++] = rData;
}
if(LL_USART_IsActiveFlag_ORE(USART1))
{
LL_USART_ClearFlag_ORE(USART1);
}
}
需要注意的是需要添加ORE溢出标志的检测,确保每次的接收中断都是有效的。
4、总结
最后我们来测试一下我们的代码。将相关项目下载到目标板并采用两种监测方式查看结果。首先接到PC机的串口,在上位中使用Modscan来查看,其结果如下:

然后通过J-Link在线调试监控数据通讯,其结果如下:

上图中,上部是我们的物理量数据,而下面是接收到的上位下发的读取保持寄存器的Modbus报文。结合前一张图,很明显收发都是正确的。
欢迎关注:

STM32F0使用LL库实现Modbus通讯的更多相关文章
- STM32F0使用LL库实现SHT70通讯
在本次项目中,限于空间要求我们选用了STM32F030F4作为控制芯片.这款MCU不但封装紧凑,而且自带的Flash空间也非常有限,所以我们选择了LL库实现.本篇我们将基于LL库采用模拟I2C接口的方 ...
- STM32F0使用LL库实现MS5536C通讯
在本次项目中,限于空间要求我们选用了STM32F030F4作为控制芯片.这款MCU不但封装紧凑,而且自带的Flash空间也非常有限,所以我们选择了LL库实现.在本文中我们说明一下,使用LL库实现MS5 ...
- C/C++ 用libcurl库进行http通讯网络编程
C/C++ 用libcurl库进行http通讯网络编程 目录索引: 一.LibCurl基本编程框架 二.一些基本的函数 三.curl_easy_setopt函数部分选项介绍 四.curl_easy_p ...
- 串口屏Modbus协议,串口屏的modbus协议资料,串口屏modbus通讯协议开发,串口屏之modbus协议使用技巧
串口屏Modbus协议,串口屏的modbus协议资料,串口屏modbus通讯协议开发,串口屏之modbus协议使用技巧 本例程中用51单片机作为Modbus从机,从机的设备地址为2,从机有4个寄存器, ...
- C++ 用libcurl库进行http通讯网络编程 【转】
http://www.cnblogs.com/moodlxs/archive/2012/10/15/2724318.html C++ 用libcurl库进行http通讯网络编程 目录索引: 一.Lib ...
- Modbus通讯协议
<ignore_js_op> O1CN01P1wxTI1dCdw5nAeMO_!!85243700.jpg (287.43 KB, 下载次数: 0) 下载附件 保存到相册 2019-6- ...
- (6)STM32使用HAL库实现modbus的简单通讯
1.判断地址.校验 2.读取本机数据并校验打包 3.发送数据包 4.本机数据长度比要读取的长度短怎么办 4.校验错误怎么办
- 6)STM32使用HAL库实现modbus的简单通讯
1.判断地址.校验 2.读取本机数据并校验打包 3.发送数据包 4.本机数据长度比要读取的长度短怎么办 4.校验错误怎么办
- C++ 用libcurl库进行http通讯网络编程
使用libcurl完成http通讯,很方便而且是线程安全,转载一篇比较好的入门文章 转载自 http://www.cnblogs.com/moodlxs/archive/2012/10/15/2724 ...
随机推荐
- ABP拦截器之AuthorizationInterceptor
在整体介绍这个部分之前,如果对ABP中的权限控制还没有一个很明确的认知,请先阅读这篇文章,然后在读下面的内容. AuthorizationInterceptor看这个名字我们就知道这个拦截器拦截用户一 ...
- Spring-webflow基础讲解
什么是webflow: Spring Web Flow构建于Spring MVC之上,允许实现Web应用程序的“流程”.流程封装了一系列步骤,指导用户执行某些业务任务.它跨越多个HTTP请求,具有状态 ...
- Frame Interpolation
对于视频网站.电视厂商以及进行视频压制的用户来说,改变视频的帧率算是一个比较常见的需求.视频网站改变帧率主要是为了向不同级别的网站用户提供差异化服务:电视厂商则是以提供更好的显示效果作为电视的卖点:对 ...
- 图像风格迁移(Pytorch)
图像风格迁移 最后要生成的图片是怎样的是难以想象的,所以朴素的监督学习方法可能不会生效, Content Loss 根据输入图片和输出图片的像素差别可以比较损失 \(l_{content} = \fr ...
- (转)Java并发包基石-AQS详解
背景:之前在研究多线程的时候,模模糊糊知道AQS这个东西,但是对于其内部是如何实现,以及具体应用不是很理解,还自认为多线程已经学习的很到位了,贻笑大方. Java并发包基石-AQS详解Java并发包( ...
- redis实现分页
redis实现分页功能,主要是将数据缓存起来,无需频繁查询数据库,减少数据库的压力. 适用场景:单用户操作列表界面分页,如博客列表. 缺点:不可模糊查询,缺少灵活性. 封装类: class XgRed ...
- Python 条件、循环、异常处理
一.条件语句 1.布尔值 条件语句中,判断条件的值一般是布尔值.即条件为真时,将执行什么,条件为假时,将执行什么. 下面的值在作为布尔表达式的时候,会被解释器看做假(false): False ...
- 快速掌握Nginx(四) —— Nginx日志切片和常用配置总结
1.Nginx日志管理 1.日志简单介绍 Nginx提供了日志记录的功能,日志文件在对我们管理网站十分有用,通过访问日志(access_log)我们可以获取请求来源.客户端信息.请求的资源等信息:通过 ...
- python之路(7)装饰器
前言 装饰器:为函数添加附属功能,本质为函数 原则:不修改被修饰函数的源代码 不修改被修饰函数的调用方式 装饰器=高阶函数+函数嵌套+闭包 使用场景演示 定义下面函数 def cal(l): res ...
- H5_0003:JS禁用调试,禁用右键,监听F12事件的方法
1,禁用调试 // 这个方法是防止恶意调试的 (function () { console["log"]("=============================== ...