概述
在STM32F030平台上实现SHT15温湿度传感器数据采集,并通过Modbus RTU协议对外提供数据。
硬件连接
1. STM32F030 引脚分配
SHT15连接:DATA -> PA0 (GPIO_Input) 需接10K上拉电阻SCK -> PA1 (GPIO_Output)VDD -> 3.3VGND -> GNDUART连接(Modbus RTU):USART1_TX -> PA9USART1_RX -> PA10(RS485接口需通过MAX485芯片转换)
工程代码
1. 主程序 (main.c)
#include "stm32f0xx.h"
#include "sht15.h"
#include "modbus.h"
#include "delay.h"
#include <string.h>// Modbus保持寄存器定义
volatile uint16_t modbus_registers[MODBUS_REG_COUNT] = {0x0000, // 寄存器0: 设备状态0x0000, // 寄存器1: 温度值*10 (整数)0x0000, // 寄存器2: 湿度值*10 (整数)0x0000, // 寄存器3: 温度小数部分0x0000, // 寄存器4: 湿度小数部分0x0000, // 寄存器5: CRC校验0x0000, // 寄存器6: 设备地址0x0000, // 寄存器7: 波特率设置
};// 全局变量
static uint8_t device_address = 0x01; // Modbus设备地址
static float temperature = 0.0;
static float humidity = 0.0;int main(void)
{// 系统时钟初始化SystemInit();RCC->AHBENR |= RCC_AHBENR_GPIOAEN; // 使能GPIOA时钟// 初始化外设SHT15_Init(); // 初始化SHT15MODBUS_Init(9600); // 初始化Modbus, 波特率9600SysTick_Init(); // 初始化系统滴答定时器// 设置设备地址modbus_registers[6] = device_address;while(1){// 每秒读取一次温湿度if(SHT15_Read(&temperature, &humidity) == 0){// 将浮点数转换为Modbus寄存器格式// 温度: 整数部分放大10倍存储int16_t temp_int = (int16_t)temperature;int16_t temp_frac = (int16_t)((temperature - temp_int) * 100);modbus_registers[1] = (uint16_t)temp_int;modbus_registers[3] = (uint16_t)temp_frac;// 湿度: 整数部分放大10倍存储int16_t hum_int = (int16_t)humidity;int16_t hum_frac = (int16_t)((humidity - hum_int) * 100);modbus_registers[2] = (uint16_t)hum_int;modbus_registers[4] = (uint16_t)hum_frac;}// 处理Modbus通信MODBUS_Process();Delay_ms(1000); // 1秒采样间隔}
}
2. SHT15驱动 (sht15.h)
#ifndef __SHT15_H
#define __SHT15_H#include "stm32f0xx.h"// 引脚定义
#define SHT15_DATA_PIN GPIO_Pin_0
#define SHT15_SCK_PIN GPIO_Pin_1
#define SHT15_GPIO GPIOA// 命令定义
#define SHT15_MEASURE_TEMP 0x03
#define SHT15_MEASURE_HUMI 0x05
#define SHT15_WRITE_STATUS 0x06
#define SHT15_READ_STATUS 0x07
#define SHT15_RESET 0x1E// 状态寄存器位定义
#define STATUS_LOW_RES 0x01
#define STATUS_NO_OTP_RELOAD 0x02
#define STATUS_HEATER 0x04
#define STATUS_BATTERY_LOW 0x40// 错误码
#define SHT15_OK 0
#define SHT15_ERR_COMM 1
#define SHT15_ERR_CRC 2
#define SHT15_ERR_TIMEOUT 3// 函数声明
void SHT15_Init(void);
uint8_t SHT15_Read(float *temperature, float *humidity);
static uint8_t SHT15_Measure(uint16_t *value, uint8_t mode);
static uint8_t SHT15_CRC_Check(uint8_t *data, uint8_t len);
static void SHT15_TransStart(void);
static uint8_t SHT15_WriteByte(uint8_t data);
static uint8_t SHT15_ReadByte(uint8_t ack);
static void SHT15_ConnectionReset(void);
static float SHT15_CalcTemperature(uint16_t raw);
static float SHT15_CalcHumidity(uint16_t raw, float temp);#endif
3. SHT15驱动 (sht15.c)
#include "sht15.h"
#include "delay.h"
#include <math.h>// 初始化SHT15
void SHT15_Init(void)
{GPIO_InitTypeDef GPIO_InitStruct;// 使能GPIOA时钟RCC->AHBENR |= RCC_AHBENR_GPIOAEN;// 配置DATA引脚为开漏输出(初始状态)GPIO_InitStruct.GPIO_Pin = SHT15_DATA_PIN;GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;GPIO_InitStruct.GPIO_OType = GPIO_OType_OD; // 开漏输出GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP; // 上拉GPIO_InitStruct.GPIO_Speed = GPIO_Speed_Level_3;GPIO_Init(SHT15_GPIO, &GPIO_InitStruct);// 配置SCK引脚为推挽输出GPIO_InitStruct.GPIO_Pin = SHT15_SCK_PIN;GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;GPIO_InitStruct.GPIO_OType = GPIO_OType_PP; // 推挽输出GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;GPIO_InitStruct.GPIO_Speed = GPIO_Speed_Level_3;GPIO_Init(SHT15_GPIO, &GPIO_InitStruct);// 初始状态GPIO_ResetBits(SHT15_GPIO, SHT15_SCK_PIN);GPIO_SetBits(SHT15_GPIO, SHT15_DATA_PIN);Delay_ms(20); // 等待传感器稳定// 发送复位命令SHT15_ConnectionReset();
}// 启动传输序列
static void SHT15_TransStart(void)
{GPIO_SetBits(SHT15_GPIO, SHT15_DATA_PIN);GPIO_SetBits(SHT15_GPIO, SHT15_SCK_PIN);Delay_us(5);GPIO_ResetBits(SHT15_GPIO, SHT15_DATA_PIN);Delay_us(5);GPIO_ResetBits(SHT15_GPIO, SHT15_SCK_PIN);Delay_us(5);GPIO_SetBits(SHT15_GPIO, SHT15_SCK_PIN);Delay_us(5);GPIO_SetBits(SHT15_GPIO, SHT15_DATA_PIN);Delay_us(5);GPIO_ResetBits(SHT15_GPIO, SHT15_SCK_PIN);Delay_us(5);
}// 写一个字节到SHT15
static uint8_t SHT15_WriteByte(uint8_t data)
{uint8_t i, ack = 0;// 设置DATA为输出模式GPIO_InitTypeDef GPIO_InitStruct;GPIO_InitStruct.GPIO_Pin = SHT15_DATA_PIN;GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;GPIO_InitStruct.GPIO_OType = GPIO_OType_OD;GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;GPIO_InitStruct.GPIO_Speed = GPIO_Speed_Level_3;GPIO_Init(SHT15_GPIO, &GPIO_InitStruct);// 发送8位数据for(i = 0; i < 8; i++){if(data & 0x80)GPIO_SetBits(SHT15_GPIO, SHT15_DATA_PIN);elseGPIO_ResetBits(SHT15_GPIO, SHT15_DATA_PIN);data <<= 1;GPIO_SetBits(SHT15_GPIO, SHT15_SCK_PIN);Delay_us(5);GPIO_ResetBits(SHT15_GPIO, SHT15_SCK_PIN);Delay_us(5);}// 在第9个时钟周期读取ACKGPIO_SetBits(SHT15_GPIO, SHT15_SCK_PIN);// 设置DATA为输入模式GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN;GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;GPIO_Init(SHT15_GPIO, &GPIO_InitStruct);Delay_us(5);ack = GPIO_ReadInputDataBit(SHT15_GPIO, SHT15_DATA_PIN);GPIO_ResetBits(SHT15_GPIO, SHT15_SCK_PIN);Delay_us(5);return (ack == 0); // 返回ACK是否有效
}// 从SHT15读取一个字节
static uint8_t SHT15_ReadByte(uint8_t ack)
{uint8_t i, data = 0;// 设置DATA为输入模式GPIO_InitTypeDef GPIO_InitStruct;GPIO_InitStruct.GPIO_Pin = SHT15_DATA_PIN;GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN;GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;GPIO_Init(SHT15_GPIO, &GPIO_InitStruct);// 读取8位数据for(i = 0; i < 8; i++){data <<= 1;GPIO_SetBits(SHT15_GPIO, SHT15_SCK_PIN);Delay_us(5);if(GPIO_ReadInputDataBit(SHT15_GPIO, SHT15_DATA_PIN))data |= 0x01;GPIO_ResetBits(SHT15_GPIO, SHT15_SCK_PIN);Delay_us(5);}// 发送ACK/NACKGPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;GPIO_InitStruct.GPIO_OType = GPIO_OType_OD;GPIO_Init(SHT15_GPIO, &GPIO_InitStruct);if(ack)GPIO_ResetBits(SHT15_GPIO, SHT15_DATA_PIN);elseGPIO_SetBits(SHT15_GPIO, SHT15_DATA_PIN);GPIO_SetBits(SHT15_GPIO, SHT15_SCK_PIN);Delay_us(5);GPIO_ResetBits(SHT15_GPIO, SHT15_SCK_PIN);Delay_us(5);// 释放DATA线GPIO_SetBits(SHT15_GPIO, SHT15_DATA_PIN);return data;
}// 连接复位
static void SHT15_ConnectionReset(void)
{uint8_t i;// 发送9个时钟脉冲,DATA保持高电平GPIO_SetBits(SHT15_GPIO, SHT15_DATA_PIN);for(i = 0; i < 9; i++){GPIO_SetBits(SHT15_GPIO, SHT15_SCK_PIN);Delay_us(5);GPIO_ResetBits(SHT15_GPIO, SHT15_SCK_PIN);Delay_us(5);}// 发送传输启动序列SHT15_TransStart();
}// CRC校验
static uint8_t SHT15_CRC_Check(uint8_t *data, uint8_t len)
{uint8_t crc = 0;uint8_t i, j;for(i = 0; i < len; i++){crc ^= data[i];for(j = 0; j < 8; j++){if(crc & 0x80)crc = (crc << 1) ^ 0x31;elsecrc <<= 1;}}return crc;
}// 测量温湿度
static uint8_t SHT15_Measure(uint16_t *value, uint8_t mode)
{uint8_t data[3];uint8_t crc, i;// 发送启动序列SHT15_TransStart();// 发送测量命令if(!SHT15_WriteByte(mode))return SHT15_ERR_COMM;// 等待测量完成GPIO_InitTypeDef GPIO_InitStruct;GPIO_InitStruct.GPIO_Pin = SHT15_DATA_PIN;GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN;GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;GPIO_Init(SHT15_GPIO, &GPIO_InitStruct);for(i = 0; i < 100; i++) // 等待最多100ms{if(GPIO_ReadInputDataBit(SHT15_GPIO, SHT15_DATA_PIN) == 0)break;Delay_ms(1);}if(i >= 100)return SHT15_ERR_TIMEOUT;// 读取测量结果data[0] = SHT15_ReadByte(1); // 高字节,ACKdata[1] = SHT15_ReadByte(1); // 低字节,ACKdata[2] = SHT15_ReadByte(0); // CRC,NACK// CRC校验crc = SHT15_CRC_Check(data, 2);if(crc != data[2])return SHT15_ERR_CRC;*value = (data[0] << 8) | data[1];return SHT15_OK;
}// 计算温度
static float SHT15_CalcTemperature(uint16_t raw)
{float temperature;// 12位精度temperature = (float)raw * 0.01 - 40.0;return temperature;
}// 计算相对湿度
static float SHT15_CalcHumidity(uint16_t raw, float temp)
{float humidity, linear_humidity;// 线性转换linear_humidity = -2.0468 + 0.0367 * raw - 1.5955E-6 * raw * raw;// 温度补偿humidity = (temp - 25.0) * (0.01 + 0.00008 * raw) + linear_humidity;if(humidity > 100.0) humidity = 100.0;if(humidity < 0.1) humidity = 0.1;return humidity;
}// 读取温湿度
uint8_t SHT15_Read(float *temperature, float *humidity)
{uint16_t raw_temp, raw_humi;uint8_t ret;// 测量温度ret = SHT15_Measure(&raw_temp, SHT15_MEASURE_TEMP);if(ret != SHT15_OK)return ret;*temperature = SHT15_CalcTemperature(raw_temp);// 测量湿度ret = SHT15_Measure(&raw_humi, SHT15_MEASURE_HUMI);if(ret != SHT15_OK)return ret;*humidity = SHT15_CalcHumidity(raw_humi, *temperature);return SHT15_OK;
}
4. Modbus协议栈 (modbus.h)
#ifndef __MODBUS_H
#define __MODBUS_H#include "stm32f0xx.h"
#include <stdint.h>// Modbus寄存器定义
#define MODBUS_REG_COUNT 16
#define MODBUS_HOLDING_START 0// Modbus功能码
#define MODBUS_FC_READ_COILS 0x01
#define MODBUS_FC_READ_DISCRETE_INPUTS 0x02
#define MODBUS_FC_READ_HOLDING_REGS 0x03
#define MODBUS_FC_READ_INPUT_REGS 0x04
#define MODBUS_FC_WRITE_SINGLE_COIL 0x05
#define MODBUS_FC_WRITE_SINGLE_REG 0x06
#define MODBUS_FC_WRITE_MULTIPLE_COILS 0x0F
#define MODBUS_FC_WRITE_MULTIPLE_REGS 0x10// Modbus异常码
#define MODBUS_EX_NONE 0x00
#define MODBUS_EX_ILLEGAL_FUNCTION 0x01
#define MODBUS_EX_ILLEGAL_DATA_ADDRESS 0x02
#define MODBUS_EX_ILLEGAL_DATA_VALUE 0x03
#define MODBUS_EX_SLAVE_DEVICE_FAILURE 0x04// 缓冲区大小
#define MODBUS_BUF_SIZE 256
#define MODBUS_FRAME_SIZE 128// Modbus帧结构
typedef struct {uint8_t address;uint8_t function;uint16_t starting_address;uint16_t quantity;uint16_t byte_count;uint8_t data[MODBUS_FRAME_SIZE];uint16_t crc;
} MODBUS_Frame;// 函数声明
void MODBUS_Init(uint32_t baudrate);
void MODBUS_Process(void);
void USART1_IRQHandler(void);
uint16_t MODBUS_CRC16(uint8_t *buf, uint16_t len);
void MODBUS_Send_Exception(uint8_t slave_addr, uint8_t function, uint8_t exception);
void MODBUS_Send_Response(uint8_t *data, uint16_t len);// 外部变量
extern volatile uint16_t modbus_registers[MODBUS_REG_COUNT];#endif
5. Modbus协议栈 (modbus.c)
#include "modbus.h"
#include "delay.h"
#include <string.h>// Modbus接收缓冲区
static uint8_t modbus_rx_buf[MODBUS_BUF_SIZE];
static uint16_t modbus_rx_index = 0;
static uint8_t modbus_rx_complete = 0;// Modbus发送缓冲区
static uint8_t modbus_tx_buf[MODBUS_BUF_SIZE];
static uint16_t modbus_tx_index = 0;
static uint16_t modbus_tx_len = 0;// 超时计时
static uint32_t modbus_timeout = 0;
#define MODBUS_TIMEOUT_MS 50 // 帧间隔超时时间// 初始化Modbus
void MODBUS_Init(uint32_t baudrate)
{GPIO_InitTypeDef GPIO_InitStruct;USART_InitTypeDef USART_InitStruct;NVIC_InitTypeDef NVIC_InitStruct;// 使能时钟RCC->AHBENR |= RCC_AHBENR_GPIOAEN; // 使能GPIOA时钟RCC->APB2ENR |= RCC_APB2ENR_USART1EN; // 使能USART1时钟// 配置USART1引脚// PA9 - USART1_TX// PA10 - USART1_RXGPIO_InitStruct.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10;GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF; // 复用功能GPIO_InitStruct.GPIO_Speed = GPIO_Speed_Level_2;GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;GPIO_Init(GPIOA, &GPIO_InitStruct);// 配置AF功能GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_1);GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_1);// 配置USART1USART_InitStruct.USART_BaudRate = baudrate;USART_InitStruct.USART_WordLength = USART_WordLength_8b;USART_InitStruct.USART_StopBits = USART_StopBits_1;USART_InitStruct.USART_Parity = USART_Parity_No;USART_InitStruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;USART_Init(USART1, &USART_InitStruct);// 使能USART1USART_Cmd(USART1, ENABLE);// 使能接收中断USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);USART_ITConfig(USART1, USART_IT_IDLE, ENABLE); // 空闲中断用于检测帧结束// 配置NVICNVIC_InitStruct.NVIC_IRQChannel = USART1_IRQn;NVIC_InitStruct.NVIC_IRQChannelPriority = 0;NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;NVIC_Init(&NVIC_InitStruct);
}// USART1中断处理
void USART1_IRQHandler(void)
{uint8_t data;if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET){data = USART_ReceiveData(USART1);if(modbus_rx_index < MODBUS_BUF_SIZE){modbus_rx_buf[modbus_rx_index++] = data;}modbus_timeout = 0; // 重置超时计时}else if(USART_GetITStatus(USART1, USART_IT_IDLE) != RESET){// 空闲中断,表示一帧数据接收完成data = USART_ReceiveData(USART1); // 读DR清空中断(void)data;if(modbus_rx_index > 0){modbus_rx_complete = 1;}}if(USART_GetITStatus(USART1, USART_IT_TC) != RESET){// 发送完成中断USART_ClearITPendingBit(USART1, USART_IT_TC);}
}// CRC16计算
uint16_t MODBUS_CRC16(uint8_t *buf, uint16_t len)
{uint16_t crc = 0xFFFF;uint16_t i, j;for(i = 0; i < len; i++){crc ^= buf[i];for(j = 0; j < 8; j++){if(crc & 0x0001){crc >>= 1;crc ^= 0xA001;}else{crc >>= 1;}}}return crc;
}// 发送异常响应
void MODBUS_Send_Exception(uint8_t slave_addr, uint8_t function, uint8_t exception)
{uint8_t tx_buf[5];uint16_t crc;tx_buf[0] = slave_addr;tx_buf[1] = function | 0x80; // 异常功能码tx_buf[2] = exception;crc = MODBUS_CRC16(tx_buf, 3);tx_buf[3] = crc & 0xFF;tx_buf[4] = (crc >> 8) & 0xFF;// 发送数据for(uint8_t i = 0; i < 5; i++){while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);USART_SendData(USART1, tx_buf[i]);}
}// 发送响应
void MODBUS_Send_Response(uint8_t *data, uint16_t len)
{for(uint16_t i = 0; i < len; i++){while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);USART_SendData(USART1, data[i]);}
}// 处理读保持寄存器(功能码03)
static uint8_t MODBUS_Handle_Read_Holding_Registers(uint8_t *frame, uint16_t len)
{uint8_t response[256];uint16_t start_addr, reg_count, byte_count;uint16_t crc, i;if(len < 8) // 至少需要地址(1)+功能码(1)+起始地址(2)+数量(2)+CRC(2)return MODBUS_EX_ILLEGAL_FUNCTION;// 解析起始地址和寄存器数量start_addr = (frame[2] << 8) | frame[3];reg_count = (frame[4] << 8) | frame[5];// 校验地址范围if(start_addr >= MODBUS_REG_COUNT || (start_addr + reg_count) > MODBUS_REG_COUNT ||reg_count == 0 || reg_count > 125){return MODBUS_EX_ILLEGAL_DATA_ADDRESS;}// 构建响应byte_count = reg_count * 2;response[0] = frame[0]; // 地址response[1] = 0x03; // 功能码response[2] = byte_count; // 字节数// 复制寄存器数据for(i = 0; i < reg_count; i++){response[3 + i * 2] = (modbus_registers[start_addr + i] >> 8) & 0xFF;response[4 + i * 2] = modbus_registers[start_addr + i] & 0xFF;}// 计算CRCcrc = MODBUS_CRC16(response, 3 + byte_count);response[3 + byte_count] = crc & 0xFF;response[4 + byte_count] = (crc >> 8) & 0xFF;// 发送响应MODBUS_Send_Response(response, 5 + byte_count);return MODBUS_EX_NONE;
}// 处理写单个寄存器(功能码06)
static uint8_t MODBUS_Handle_Write_Single_Register(uint8_t *frame, uint16_t len)
{uint8_t response[8];uint16_t reg_addr, reg_value, crc;if(len < 8)return MODBUS_EX_ILLEGAL_FUNCTION;// 解析寄存器地址和值reg_addr = (frame[2] << 8) | frame[3];reg_value = (frame[4] << 8) | frame[5];// 校验地址范围if(reg_addr >= MODBUS_REG_COUNT){return MODBUS_EX_ILLEGAL_DATA_ADDRESS;}// 写入寄存器modbus_registers[reg_addr] = reg_value;// 特殊寄存器处理if(reg_addr == 6) // 设备地址{// 更新设备地址}else if(reg_addr == 7) // 波特率设置{// 波特率设置处理}// 构建响应(回显写入的数据)response[0] = frame[0];response[1] = 0x06;response[2] = frame[2];response[3] = frame[3];response[4] = frame[4];response[5] = frame[5];crc = MODBUS_CRC16(response, 6);response[6] = crc & 0xFF;response[7] = (crc >> 8) & 0xFF;MODBUS_Send_Response(response, 8);return MODBUS_EX_NONE;
}// 处理写多个寄存器(功能码16)
static uint8_t MODBUS_Handle_Write_Multiple_Registers(uint8_t *frame, uint16_t len)
{uint8_t response[8];uint16_t start_addr, reg_count, byte_count;uint16_t crc, i;if(len < 9)return MODBUS_EX_ILLEGAL_FUNCTION;start_addr = (frame[2] << 8) | frame[3];reg_count = (frame[4] << 8) | frame[5];byte_count = frame[6];// 校验if(start_addr >= MODBUS_REG_COUNT || (start_addr + reg_count) > MODBUS_REG_COUNT ||reg_count == 0 || reg_count > 123 ||byte_count != reg_count * 2){return MODBUS_EX_ILLEGAL_DATA_ADDRESS;}// 写入寄存器for(i = 0; i < reg_count; i++){modbus_registers[start_addr + i] = (frame[7 + i * 2] << 8) | frame[8 + i * 2];}// 构建响应response[0] = frame[0];response[1] = 0x10;response[2] = frame[2];response[3] = frame[3];response[4] = frame[4];response[5] = frame[5];crc = MODBUS_CRC16(response, 6);response[6] = crc & 0xFF;response[7] = (crc >> 8) & 0xFF;MODBUS_Send_Response(response, 8);return MODBUS_EX_NONE;
}// 处理Modbus帧
static void MODBUS_Process_Frame(uint8_t *frame, uint16_t len)
{uint8_t slave_addr = frame[0];uint8_t function_code = frame[1];uint16_t crc_received, crc_calculated;uint8_t exception = MODBUS_EX_NONE;// 检查最小长度if(len < 4) // 至少需要地址+功能码+CRC(2)return;// CRC校验crc_received = (frame[len-1] << 8) | frame[len-2];crc_calculated = MODBUS_CRC16(frame, len-2);if(crc_received != crc_calculated)return; // CRC错误,丢弃帧// 检查地址是否匹配if(slave_addr != 0x01 && slave_addr != 0xFF) // 0xFF是广播地址return;// 根据功能码处理switch(function_code){case MODBUS_FC_READ_HOLDING_REGS:exception = MODBUS_Handle_Read_Holding_Registers(frame, len);break;case MODBUS_FC_WRITE_SINGLE_REG:exception = MODBUS_Handle_Write_Single_Register(frame, len);break;case MODBUS_FC_WRITE_MULTIPLE_REGS:exception = MODBUS_Handle_Write_Multiple_Registers(frame, len);break;default:exception = MODBUS_EX_ILLEGAL_FUNCTION;break;}// 发送异常响应if(exception != MODBUS_EX_NONE && slave_addr != 0xFF){MODBUS_Send_Exception(slave_addr, function_code, exception);}
}// 主处理函数
void MODBUS_Process(void)
{static uint32_t last_tick = 0;uint32_t current_tick = SysTick_GetTick();// 超时检测if(modbus_rx_index > 0 && (current_tick - modbus_timeout) > MODBUS_TIMEOUT_MS){modbus_rx_complete = 1;}// 处理接收完成的帧if(modbus_rx_complete){MODBUS_Process_Frame(modbus_rx_buf, modbus_rx_index);// 清除接收状态modbus_rx_index = 0;modbus_rx_complete = 0;}
}
6. 延时函数 (delay.h/c)
// delay.h
#ifndef __DELAY_H
#define __DELAY_H#include "stm32f0xx.h"void SysTick_Init(void);
uint32_t SysTick_GetTick(void);
void Delay_ms(uint32_t ms);
void Delay_us(uint32_t us);#endif// delay.c
#include "delay.h"static volatile uint32_t sys_tick = 0;void SysTick_Init(void)
{// 配置SysTick为1ms中断if(SysTick_Config(SystemCoreClock / 1000)){while(1);}
}uint32_t SysTick_GetTick(void)
{return sys_tick;
}void Delay_ms(uint32_t ms)
{uint32_t tickstart = SysTick_GetTick();while((SysTick_GetTick() - tickstart) < ms);
}void Delay_us(uint32_t us)
{uint32_t i;for(i = 0; i < us * 8; i++) // 根据时钟频率调整{__NOP();}
}// SysTick中断处理函数
void SysTick_Handler(void)
{sys_tick++;
}
参考代码 实现温湿度传感器 www.youwenfan.com/contentcnu/56262.html
7. KEIL工程配置
项目设置:
- 目标芯片:STM32F030C8Tx
- 时钟配置:8MHz HSI,系统时钟48MHz
- 优化级别:-O1
- 包含路径:
- .\
- ..\CMSIS
- ..\StdPeriph_Driver\inc
链接器设置:
--cpu Cortex-M0
--library_type=microlib
--strict
--scatter ".\STM32F030C8Tx.sct"
8. Modbus测试工具使用
使用Modbus Poll或ModScan等工具测试:
读取温度:
设备地址: 0x01
功能码: 0x03
起始地址: 0x0001
寄存器数量: 2
返回:温度值(寄存器1-2)、湿度值(寄存器3-4)
设置设备地址:
功能码: 0x06
寄存器地址: 0x0006
寄存器值: 新地址
9. 调试建议
-
硬件连接检查:
- 确保SHT15的DATA线有上拉电阻
- 确保RS485方向控制正确
- 检查电源电压3.3V稳定
-
软件调试:
- 先单独测试SHT15读取
- 再测试串口通信
- 最后测试完整的Modbus协议
-
优化方向:
- 添加看门狗
- 实现EEPROM保存设备地址
- 添加Modbus异常处理
- 实现软件CRC校验
