基于STM32F103C8T6的RS485 MODBUS通信及CRC校验

     在进行RS485 MODBUS收发通信,无固定帧头及帧尾的数据包传输时,两组数据包之间需要有3.5个字符的时间间隔,通过定时器中断延时判定数据包是否传输完成,同时进行CRC校验比对

一、串口通信配置

与常规USART配置基本一致,只是多一个控制发送与接收切换的引脚,下例为USART2的配置,使用GPIOA_PIN_1为控制引脚

Rs485.h

#ifndef __RS485_H
#define __RS485_H

#include <stdio.h>

extern uint8_t  Rs485_TxPacket[];
extern uint8_t  Rs485_RxPacket[];
extern uint8_t  Rs485_pRxPacket;	 
extern uint8_t  Rs485_RxStar;
extern uint8_t  Rs485_RxDataLen; 
extern uint32_t Rs485_RxDlay;
extern uint8_t  Rs485_RxFlag;

void Rs485_Init(void);
void Rs485_SendByte(uint8_t Byte);
void Rs485_SendArray(uint8_t *Array, uint16_t Length);
void Rs485_SendString(char *String);
void Rs485_SendNumber(uint32_t Number, uint8_t Length);
uint8_t Rs485_GetRxFlag(void);

#endif

Rs485.c

#include "stm32f10x.h"                  // Device header
#include <stdio.h>
#include <stdarg.h>

uint8_t  Rs485_TxPacket[32] = {0x01,0x02,0x03,0x04};  //定义发送数据包数组
uint8_t  Rs485_RxPacket[32];				          //定义接收数据包数组
uint8_t  Rs485_RxFlag;					              //定义接收数据包完成标志位
uint8_t  Rs485_pRxPacket = 0;	                      //定义接收数据包位置变量
uint8_t  Rs485_RxStar;                                //定义接收数据包开始
uint8_t  Rs485_RxDataLen = 0;                         //定义接收数据包长度
uint32_t Rs485_RxDlay = 0;                            //定义RS485数据包接收完成判定延时变量


/**
  * 函    数:485串口初始化
  * 参    数:无
  * 返 回 值:无
  */
void Rs485_Init(void)
{
	/*开启时钟*/
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);	//开启USART2的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);	//开启GPIOA的时钟
	
	/*GPIO初始化*/
	GPIO_InitTypeDef GPIO_InitStructure;
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;              //控制数据的接收、发送
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);

	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);		//将PA2引脚初始化为复用推挽输出(USART2  TX)
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);		//将PA3引脚初始化为上拉输入(USART2  RX)
	
	/*USART2初始化*/
	USART_InitTypeDef USART2_InitStructure;					                            //定义结构体变量
	USART2_InitStructure.USART_BaudRate = 9600;				                            //波特率9600
	USART2_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;	//硬件流控制,不需要
	USART2_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;	                //模式,发送模式和接收模式均选择
	USART2_InitStructure.USART_Parity = USART_Parity_No;		                        //奇偶校验,不需要
	USART2_InitStructure.USART_StopBits = USART_StopBits_1;                          	//停止位,选择1位
	USART2_InitStructure.USART_WordLength = USART_WordLength_8b;		                //字长,选择8位
	USART_Init(USART2, &USART2_InitStructure);				
	
	/*中断输出配置*/
	USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);		       //开启串口接收数据的中断
	
	/*NVIC中断分组*/
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);			   //配置NVIC为分组2
	
	/*NVIC配置*/
	NVIC_InitTypeDef NVIC_InitStructure;					   //定义结构体变量
	NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;		   //选择配置NVIC的USART1线
	NVIC_InitStructure.NVIC_IRQChannelCmd 
<think>好的,我需要帮助用户实现STM32F103C8T6通过Modbus协议读取温湿度传感器的代码示例。首先,我需要理清楚整个实现的步骤和所需的关键点。 用户提到了使用Modbus协议和串口通信,因此首先要确认温湿度传感器是否支持Modbus RTU模式。大多数工业传感器会支持Modbus RTU,但需要查看传感器的数据手册确认。比如,有些传感器可能使用特定的功能码,比如03功能码读取保持寄存器。 接下来,硬件连接部分。用户提供的引用[1]提到了TTL转RS485模块的连接方式,VCC接5V,TX接A2,RX接A3,GND相连,以及RS485的A和B线。需要确认STM32的USART引脚是否对应到A2和A3,可能需要使用USART2,因为PA2和PA3通常是USART2的TX和RX引脚。同时,RS485模块需要方向控制引脚来控制发送和接收模式,这个引脚可能连接到另一个GPIO,比如PA1,用于控制MAX485芯片的DE和RE引脚。 然后是软件部分,需要配置STM32的USART为异步模式,波特率通常设为9600或19200,数据位8位,停止位1位,无奇偶校验。需要开启USART中断,以便在接收到数据时进行处理。此外,需要实现Modbus RTU的帧结构,包括地址、功能码、寄存器地址、数据长度和CRC校验。 代码方面,需要初始化GPIO和USART,配置NVIC中断,实现发送和接收函数。Modbus的请求帧需要正确构造,比如读取保持寄存器的03功能码,指定起始地址和寄存器数量。接收数据时,需要验证从机地址、功能码和CRC校验,确保数据的正确性。 CRC校验的计算需要实现,Modbus使用CRC-16,计算多项式为0x8005,初始值为0xFFFF。需要编写CRC计算函数,对发送的数据进行校验,并在接收时验证校验码。 可能的难点包括RS485方向控制的时机,确保在发送时DE为高电平,接收时为低电平。另外,处理Modbus超时和错误重试机制也很重要,避免程序卡死。 最后,需要根据具体的传感器手册调整寄存器地址和数据解析方式。例如,温度可能存储在寄存器0x0001,湿度在0x0002,数据可能为整数或需要除以10得到实际值。 总结步骤: 1. 确认传感器Modbus参数(地址、波特率、寄存器地址)。 2. 硬件连接:USART2(PA2/PA3)接RS485模块,控制引脚PA1。 3. 配置USART和GPIO,设置中断。 4. 实现Modbus请求帧构造和发送。 5. 实现接收数据处理和CRC校验6. 解析温湿度数据并处理。 需要检查代码中的引脚配置是否正确,中断服务函数是否处理接收数据,以及超时处理是否到位。此外,测试时可以使用Modbus调试工具辅助验证。</think>要实现STM32F103C8T6通过Modbus协议读取温湿度传感器,需结合硬件连接、Modbus协议栈和传感器数据解析。以下是分步实现说明及代码示例: --- ### 一、硬件连接 1. **RS485模块连接**(参考引用[1]): - `STM32的PA9(TX)` ➔ RS485模块的`RX` - `STM32的PA10(RX)` ➔ RS485模块的`TX` - `RS485模块的A/B` ➔ 温湿度传感器的`A/B`端子 - `RS485方向控制引脚`(如PA8)控制发送/接收模式 2. **电源连接**: - 传感器和单片机共地(GND) - 传感器供电根据型号选择5V或3.3V --- ### 二、代码实现(基于HAL库) #### 1. USART及GPIO初始化 ```c // RS485方向控制引脚 #define RS485_DIR_GPIO_Port GPIOA #define RS485_DIR_Pin GPIO_PIN_8 // 初始化USART1(Modbus通信) void MX_USART1_UART_Init(void) { huart1.Instance = USART1; huart1.Init.BaudRate = 9600; huart1.Init.WordLength = UART_WORDLENGTH_8B; huart1.Init.StopBits = UART_STOPBITS_1; huart1.Init.Parity = UART_PARITY_NONE; huart1.Init.Mode = UART_MODE_TX_RX; huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE; HAL_UART_Init(&huart1); } // 初始化GPIO void GPIO_Init(void) { __HAL_RCC_GPIOA_CLK_ENABLE(); GPIO_InitTypeDef GPIO_InitStruct = {0}; // RS485方向控制引脚 GPIO_InitStruct.Pin = RS485_DIR_Pin; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(RS485_DIR_GPIO_Port, &GPIO_InitStruct); } ``` #### 2. Modbus请求帧构造(示例读取温湿度) ```c // 发送Modbus读取请求(功能码0x03) void Send_Modbus_Request(uint8_t slave_addr, uint16_t reg_addr, uint16_t reg_num) { uint8_t request[8]; request[0] = slave_addr; // 从机地址 request[1] = 0x03; // 功能码 request[2] = reg_addr >> 8; // 寄存器高地址 request[3] = reg_addr & 0xFF; // 寄存器低地址 request[4] = reg_num >> 8; // 寄存器数量高字节 request[5] = reg_num & 0xFF; // 寄存器数量低字节 uint16_t crc = Modbus_CRC16(request, 6); request[6] = crc & 0xFF; // CRC低字节 request[7] = crc >> 8; // CRC高字节 HAL_GPIO_WritePin(RS485_DIR_GPIO_Port, RS485_DIR_Pin, GPIO_PIN_SET); // 设置为发送模式 HAL_UART_Transmit(&huart1, request, 8, 100); HAL_GPIO_WritePin(RS485_DIR_GPIO_Port, RS485_DIR_Pin, GPIO_PIN_RESET); // 切回接收模式 } ``` #### 3. Modbus响应解析 ```c // 解析温湿度数据(假设温度在寄存器0x0000,湿度在0x0001) void Parse_Modbus_Response(uint8_t *data, uint16_t len) { if (data[1] == 0x03) { // 确认功能码 uint16_t temp = (data[3] << 8) | data[4]; uint16_t humi = (data[5] << 8) | data[6]; printf("温度: %.1f℃, 湿度: %.1f%%\r\n", temp/10.0, humi/10.0); } } // 接收中断回调 void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { if (huart == &huart1) { // 接收数据到缓冲区,需自行实现缓冲区管理 // 调用Parse_Modbus_Response解析有效数据 } } ``` #### 4. CRC16校验函数 ```c uint16_t Modbus_CRC16(uint8_t *data, uint16_t length) { uint16_t crc = 0xFFFF; for (uint16_t i = 0; i < length; i++) { crc ^= data[i]; for (uint8_t j = 0; j < 8; j++) { if (crc & 0x0001) { crc >>= 1; crc ^= 0xA001; } else { crc >>= 1; } } } return crc; } ``` --- ### 三、关键配置说明 1. **传感器参数匹配**: - 确认传感器Modbus地址(常见为0x01) - 寄存器地址需参考传感器手册(如温度可能为0x0001) 2. **超时处理**: ```c HAL_UART_Receive_IT(&huart1, rx_buf, 7); // 接收响应帧(长度根据实际调整) // 在定时器中检测响应超时 ``` --- ### 四、常见问题 1. **数据错误**: - 检查CRC校验是否正确 - 确认RS485方向控制时序(发送前拉高,完成后拉低) 2. **无响应**: - 测量RS485线路A/B电压差(应>200mV) - 用PC串口调试工具验证传感器是否正常[^2] ---
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值