stm32基于UART串口实现modbusRTU(软件方式)

这篇具有很好参考价值的文章主要介绍了stm32基于UART串口实现modbusRTU(软件方式)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1. 实现思路

 此程序中, 串口通信方式: 115200-n-8-1, modbus协议要求帧与帧之间的间隔必须大于3.5个字符时间间隙作为帧与帧之间的分割. 

字符时间计算公式: interval_time = character_interval * 8 / baud_speed * 10 ^ 6 (微秒)

1.1 设置定时器, 超时时间为interval_time.

1.2 设置stm32的uart串口接收数据中断, 每次读取数据都重置定时器计数为0

modbus stm32例程,stm32,单片机,嵌入式硬件
RDR中断启用标记

1.3 定时器超时后, 说明此时modbus帧已经传输结束, 在定时器超时函数中处理响应.

2.编程

2.1 设置定时器超时

使用的波特率为115200bit/s, 则字符间隔时间大约为2431微妙.

在cubemx中选择一个定时器设置超时时间为2431微妙.

这里我们使用的时钟是16MHz, 定时器prescaler设置为16, 每隔1us触发一次计时器计时, 共计时2430次. 

2.2 设置UART接收字符中断

modbus stm32例程,stm32,单片机,嵌入式硬件

  • cubemx配置好程序初始化后, 在CR1寄存器中启用RXNEIE标记位, 启用RDR可读中断
  • 在USART中断入口判断RDR寄存器是否可读, 可读则将数据加入全局缓冲区, 并修改缓冲区长度, 修改定时器计数寄存器 CNT = 0
//main.c文件中修改
HAL_TIM_Base_Start_IT(&htim14);//启动定时器
huart1.Instance->CR1 |= 1 << 5;//enable reception interruption


//stm32_it.c文件中修改UART1中断入口函数USART1_IRQHandler
if(huart1.Instance->ISR & 1 << 5)
{
//	uint8_t data = huart1.Instance->RDR;
//	while( (huart1.Instance->ISR & 1 << 7) == 0);
//	huart1.Instance->TDR = data;
	uart_buf[uart_buf_len++] = huart1.Instance->RDR;;
	TIM14->CNT = 0;
}
  • 当定时器中断触发后, 说明上一个帧已经接收完成, 在定时器中断超时函数处理帧.

2.3定时器中断函数实现modbusRTU协议解析

2.3.1 crc校验

modbus协议使用crc16-modbus标准校验.

此处通过直接计算方法生成crc.

#define POLYNOMIAL 0xa001

uint16_t generate_crc(uint8_t* buf, uint8_t len)
{
	uint16_t crc = 0xffff;
	uint8_t i, j;
	for(i = 0; i < len; i++)
	{
		crc ^= buf[i];
		for(j = 0; j < 8; j++)
		{
			uint8_t tmp = crc & 0x0001;
			crc >>= 1;
			if( tmp == 1)
				crc ^= POLYNOMIAL;
		}
	}
	return crc;

}
  • Tx:106-01 03 00 00 00 0A C5 CD

这是从modbuspoll中获取到的一个帧序列, 可以通过此序列进行crc校验, 后两个字节为校验码.

低地址在前, 高地址在后. CD C5 为其crc值 

2.3.2 判断是否解析帧

首先判断帧中的请求地址是否为此设备, 如果不是, 将第二个字节的高位置1, 重新生成crc, 发送给主机.

uint8_t check_receive_packet()
{
	
	if( uart_buf[0] != local_address) return 3;
	uint16_t crc = generate_crc(uart_buf, uart_buf_size - 2);
	if(crc != (uart_buf[uart_buf_size - 1] << 8 | uart_buf[uart_buf_size - 2]) )
			return 8;
	return 0;

}

2.3.3 帧解析文章来源地址https://www.toymoban.com/news/detail-610972.html

if(uart_buf_size > 0)
		{
			uint8_t i = 0;
			
			if( (ret = check_receive_packet()) )
			{
				send_buf[i++] = uart_buf[0];
				send_buf[i++] = uart_buf[1] | 0x80;
				send_buf[i++] = ret; //exception code
				crc = generate_crc(send_buf, i);
				send_buf[i++] = crc & 0xff;
				send_buf[i++] = crc >> 8;
				send_data(send_buf, i);
				uart_buf_size = 0;
				processed = 1;
				return;
			}
			uint8_t function_code = uart_buf[1];
			uint16_t reg_addr = uart_buf[2] << 8 | uart_buf[3];
			//uint16_t val;
			switch(function_code)
			{
				case 0x03:
					if( reg_addr == 0x01)
					{
						send_buf[i++] = uart_buf[0];
						send_buf[i++] = uart_buf[1];
						send_buf[i++] = 2;
						send_buf[i++] = (uint8_t)(g_device_id >> 8);
						send_buf[i++] = (uint8_t)g_device_id;
						crc = generate_crc(send_buf, i);
						send_buf[i++] = crc & 0xff;
						send_buf[i++] = crc >> 8;
						send_data(send_buf, i);
					}else if( reg_addr == 0x02)
					{
						send_buf[i++] = uart_buf[0];
						send_buf[i++] = uart_buf[1];
						send_buf[i++] = sizeof(uint8_t) * 2;
						send_buf[i++] = g_temperature_int;
						send_buf[i++] = g_temperature_dot;
						crc = generate_crc(send_buf, i);
						send_buf[i++] = crc & 0xff;
						send_buf[i++] = crc >> 8;
						send_data(send_buf, i);
					} else if( reg_addr == 0x03)
					{
						send_buf[i++] = uart_buf[0];
						send_buf[i++] = uart_buf[1];
						send_buf[i++] = sizeof(uint8_t) * 2;
						send_buf[i++] = g_humidity_int;
						send_buf[i++] = g_humidity_dot;
						crc = generate_crc(send_buf, i);
						send_buf[i++] = crc & 0xff;
						send_buf[i++] = crc >> 8;
						send_data(send_buf, i);
					} else if( reg_addr == 0x04)
					{
						send_buf[i++] = uart_buf[0];
						send_buf[i++] = uart_buf[1];
						send_buf[i++] = sizeof(uint8_t) * 2;
						
						uint8_t _int = (int)g_humidity_start;
						uint8_t x = g_humidity_start - _int;
						uint8_t _dot = (int)(x * 100); //keep 2 decimals
						
						send_buf[i++] = _int;
						send_buf[i++] = _dot;
						crc = generate_crc(send_buf, i);
						send_buf[i++] = crc & 0xff;
						send_buf[i++] = crc >> 8;
						send_data(send_buf, i);
					}
					else if(reg_addr == 0x05)
					{
						send_buf[i++] = uart_buf[0];
						send_buf[i++] = uart_buf[1];
						send_buf[i++] = sizeof(uint8_t) * 2;
						
						uint8_t _int = (int)g_temperature_start;
						uint8_t x = g_temperature_start - _int;
						uint8_t _dot = (int)(x * 100); //keep 2 decimals
						
						send_buf[i++] = _int;
						send_buf[i++] = _dot;
						crc = generate_crc(send_buf, i);
						send_buf[i++] = crc & 0xff;
						send_buf[i++] = crc >> 8;
						send_data(send_buf, i);
					}
					
					break;
				case 0x06:
					 i = 1;
					//val = uart_buf[4] << 8 | uart_buf[5];
					if( reg_addr == 0x04)
					{
						//register_val = val;
						//send_data(uart_buf, uart_buf_len);
						uint8_t _dot = uart_buf[4];
						uint8_t _int = uart_buf[5];
						g_humidity_start = uint2float(_int, _dot);
						
					}else if(reg_addr == 0x05)
					{
						uint8_t _dot = uart_buf[4];
						uint8_t _int = uart_buf[5];
						g_temperature_start = uint2float(_int, _dot);
					}
					send_data(uart_buf, uart_buf_size);
					break;
				}
			
				if(i == 0)
				{
					while(i < uart_buf_size)
					{
						fputc(uart_buf[i], (FILE*)100);
						i++;
					}
				}
				
				processed = 1;
				uart_buf_size = 0;
			}

到了这里,关于stm32基于UART串口实现modbusRTU(软件方式)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 基于STM32CubeMX和keil采用USART/UART实现非中断以及中断方式数据回环测试借助CH340以及XCOM

    这篇博客从串口通信的接口讲起,阐述原理,介绍通信方式,最后上机测试。 本篇博客主要以异步通信为例。 串口分为很多种,我们STM32学习过程中常见的就是UART/USART,前者是同步模式,后者是异步模式。还有RS485,RS232这种也是串口。我们平常使用的串口模块,大多都是类似

    2024年02月06日
    浏览(37)
  • 运动控制器设计——基于FreeModbus在STM32F4平台实现ModbusTCP和ModbusRTU

    本文笔者最近的项目是设计一款运动控制器,MCU使用的是STM32F429,要求是通过Modbus TCP协议实现与示教器通讯,并通过ModbusRTU实现与触摸屏通讯。 本文将介绍在STM32F4上实现 ModbusTCP和ModbusRTU通讯 的过程。笔者才疏学浅,如有错误还请指正。 Modbus协议是典型的主-从通讯结构,链

    2024年02月05日
    浏览(39)
  • 【基于STM32的串口通信----用软件模拟】

    注: 这是我上班第一次学习串口通信,此文章基于STM32F4系列单片机。 1、平常所用串口通信都是用的单片机外设,是单片机内部自带的模块,只需要配置好初始化就可以使用,在这里老师为了更好的让我们理解其原理所以让我们模拟串口通信; 2、软件模拟串口通信就是用普

    2024年02月05日
    浏览(28)
  • STM32+UART串口+DMA收发

    目录 1、cubemax端配置 1.1 初始化配置 1.2 GPIO配置  1.3 UART配置 1.3.1 串口基础配置 1.3.2 DMA配置 2、keil端代码设计 2.1 初始化配置 2.2 DMA接收初始化配置 2.3 DMA发送配置  2.4 接收回调函数设置 2.5 回调函数内容代码编写 2.5.1 接收回调函数 2.5.2 发送回调函数 2.6 回调函数内容代码优化

    2024年02月07日
    浏览(32)
  • STM32 HAL库 STM32CubeMx -- 串口的使用(USART/UART)

    在上一篇博客里面写了串口通信的理论知识,在这一篇中将讲述串口通信在STM32CubeMx里面的配置,以及在函数里面怎么使用。 对于串口发送信息,分为三种方法: 串口阻塞方式收发 、 串口中断方式收发 、 串口DMA方式收发 。(DMA方式在之后的DMA章节讲解) 关于STM32CubeMx的基

    2024年02月06日
    浏览(60)
  • # STM32中断方式实现串口通信(标准库)

    主要任务 : 1)当stm32接收到字符“s”时, 停止持续发送 “hello windows!”; 当接收到字符“t”时, 持续发送 “hello windows!”; 2)当stm32接收到字符“stop stm32!”时,停止持续发送“hello windows!”; 当接收到字符“go stm32!”时,持续发送“hello windows!” 实验工具: (1)软件 标

    2024年02月11日
    浏览(22)
  • STM32通过DMA方式实现串口通信

    目录 一、DMA工作原理  二、创建工程项目 三、编写代码 1.在main.c写入以下函数 2.main函数中的while循环中写入以下代码

    2024年02月15日
    浏览(32)
  • K210 UART串口通信介绍与 STM32通信

    目录 K210-UART串口通信相关函数: 使用K210串口的时候需要映射引脚: K210与STM32串口通信  发送单字节: K210端 STM32端 发送数据包 K210端 STM32端  K210的UART模块支持全双工通信,可以同时进行数据的发送和接收。在K210上使用UART串口通信,你可以连接外部设备,如传感器、显示器

    2024年03月23日
    浏览(32)
  • STM32 UART串口通信IDLE空闲中断的使用步骤

    参考了各路大神的资料,蒙蔽了半天,终于学会了,记录一下,以后忘了可以回来复习参考。 一、首先在stm32cube中配置打开对应uart串口的中断 二、工程main函数调用 __HAL_UART_ENABLE_IT(huart1,UART_IT_IDLE);//hal库宏定义,使能串口空闲中断     HAL_UART_Receive_DMA(huart1,data,sizeof(data));//使

    2024年02月12日
    浏览(32)
  • STM32CubeMx+MATLAB Simulink串口输出实验,UART/USART串口测试实验

    STM32CubeMx+MATLAB Simulink串口输出实验

    2024年02月21日
    浏览(35)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包