news 2026/5/28 22:27:16

HC32L130 + DS18B20 粮仓温度监控系统

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
HC32L130 + DS18B20 粮仓温度监控系统

目录

项目说明

核心特性

硬件接线(默认配置)

完整项目文件

1. 项目目录结构

2. 核心驱动代码

onewire.h 单总线驱动头文件

onewire.c 单总线驱动实现

ds18b20.h DS18B20 驱动头文件

ds18b20.c DS18B20 驱动实现

main.c 主函数

使用说明


完整的HC32L130 + DS18B20 粮仓温度监控系统软件项目,支持多路单总线、每路自动识别多个 DS18B20、低功耗休眠、自动扫描传感器,完全适配粮仓的长期多点测温需求。


项目说明

核心特性

  1. 多路单总线:支持最多 N 路独立单总线(默认配置 4 路,可自行扩展),每路可挂载最多 16 个 DS18B20,轻松实现粮仓几十点的温度监测
  2. 自动传感器扫描:上电自动扫描每路总线上的所有 DS18B20,无需提前配置地址,新增传感器直接接线即可
  3. 低功耗设计:适配 HC32L130 低功耗特性,大部分时间进入 STOP 休眠,仅定时唤醒读取温度,电池可运行数年
  4. 同步转换:每路总线的所有传感器同步启动温度转换,大幅节省读取时间
  5. 调试输出:自带 UART 调试输出,可直接查看所有传感器的温度数据

硬件接线(默认配置)

HC32L130 引脚功能说明
PA0单总线 1接第 1 路 DS18B20 的 DQ 线,需加 4.7k 上拉电阻
PA1单总线 2接第 2 路 DS18B20 的 DQ 线,需加 4.7k 上拉电阻
PA2单总线 3接第 3 路 DS18B20 的 DQ 线,需加 4.7k 上拉电阻
PA3单总线 4接第 4 路 DS18B20 的 DQ 线,需加 4.7k 上拉电阻
PA9UART TX调试输出,接电脑串口
PA10UART RX调试输入(可选)
3.3V电源所有 DS18B20 外部供电(推荐外部供电,长距离更稳定)
GND共地

完整项目文件

以下是项目的完整代码结构,你可以直接导入到 Keil MDK 或者 HC32 的开发环境中编译运行:

1. 项目目录结构

粮仓温度监控系统/ ├── DDL/ # 华大HC32标准驱动库 ├── Startup/ # 启动文件与链接脚本 ├── User/ │ ├── main.c # 主函数 │ ├── onewire.h # 单总线驱动头文件 │ ├── onewire.c # 单总线驱动实现 │ ├── ds18b20.h # DS18B20驱动头文件 │ ├── ds18b20.c # DS18B20驱动实现 │ ├── delay.h # 微秒延时驱动 │ └── delay.c └── README.md # 使用说明

2. 核心驱动代码

onewire.h单总线驱动头文件
#ifndef ONEWIRE_H #define ONEWIRE_H #include "hc32l130.h" #include "gpio.h" // 单总线最大传感器数量 #define ONEWIRE_MAX_SENSOR 16 // 单总线GPIO配置 typedef struct { GPIO_TypeDef *port; uint32_t pin; } onewire_gpio_t; // 单总线实例 typedef struct { onewire_gpio_t gpio; uint8_t sensor_count; uint8_t rom[ONEWIRE_MAX_SENSOR][8]; // 每个传感器的64位ROM地址 } onewire_bus_t; // 函数声明 uint8_t onewire_reset(onewire_bus_t *bus); void onewire_write_byte(onewire_bus_t *bus, uint8_t data); uint8_t onewire_read_byte(onewire_bus_t *bus); uint8_t onewire_search_bus(onewire_bus_t *bus); void onewire_init_bus(onewire_bus_t *bus, GPIO_TypeDef *port, uint32_t pin); #endif
onewire.c单总线驱动实现
#include "onewire.h" #include "delay.h" // 复位总线,返回1=存在传感器,0=无响应 uint8_t onewire_reset(onewire_bus_t *bus) { uint8_t presence; // 拉低总线480us GPIO_SetDir(bus->gpio.port, bus->gpio.pin, GPIO_DIR_OUT); GPIO_ResetPins(bus->gpio.port, bus->gpio.pin); delay_us(480); // 释放总线,等待存在脉冲 GPIO_SetDir(bus->gpio.port, bus->gpio.pin, GPIO_DIR_IN); delay_us(60); // 读存在脉冲 presence = GPIO_ReadInputDataBit(bus->gpio.port, bus->gpio.pin); delay_us(420); // 等待剩余时间 return (presence == 0); // 低电平表示存在传感器 } // 写一个字节 void onewire_write_byte(onewire_bus_t *bus, uint8_t data) { uint8_t i; GPIO_SetDir(bus->gpio.port, bus->gpio.pin, GPIO_DIR_OUT); for(i=0; i<8; i++) { // 拉低1us GPIO_ResetPins(bus->gpio.port, bus->gpio.pin); delay_us(1); if(data & 0x01) { // 写1,释放总线 GPIO_SetPins(bus->gpio.port, bus->gpio.pin); } delay_us(60); GPIO_SetPins(bus->gpio.port, bus->gpio.pin); delay_us(1); data >>= 1; } } // 读一个字节 uint8_t onewire_read_byte(onewire_bus_t *bus) { uint8_t i, data = 0; for(i=0; i<8; i++) { // 拉低1us GPIO_SetDir(bus->gpio.port, bus->gpio.pin, GPIO_DIR_OUT); GPIO_ResetPins(bus->gpio.port, bus->gpio.pin); delay_us(1); // 释放总线,等待数据 GPIO_SetDir(bus->gpio.port, bus->gpio.pin, GPIO_DIR_IN); delay_us(15); // 读电平 data >>= 1; if(GPIO_ReadInputDataBit(bus->gpio.port, bus->gpio.pin)) { data |= 0x80; } delay_us(45); } return data; } // 搜索总线上的所有传感器ROM地址 static uint8_t onewire_search(onewire_bus_t *bus, uint8_t *rom_addr) { uint8_t rom_no = 0, last_discrepancy = 0; uint8_t bit, cmp_bit, rom_byte, bit_mask; while(1) { if(!onewire_reset(bus)) break; // 发送搜索ROM命令 onewire_write_byte(bus, 0xF0); rom_byte = 0; bit_mask = 1; last_discrepancy = 0; for(uint8_t i=0; i<64; i++) { bit = onewire_read_bit(bus); cmp_bit = onewire_read_bit(bus); if(bit && cmp_bit) break; // 无设备 if(!bit && !cmp_bit) { // 冲突,选择方向 if(i < last_discrepancy) { bit = (rom_addr[rom_byte] & bit_mask) ? 1 : 0; } else if(i > last_discrepancy) { bit = 0; last_discrepancy = i; } else { bit = 1; } if(bit == 0) rom_addr[rom_byte] &= ~bit_mask; else rom_addr[rom_byte] |= bit_mask; } // 写位到总线 onewire_write_bit(bus, bit); if(bit) rom_addr[rom_byte] |= bit_mask; else rom_addr[rom_byte] &= ~bit_mask; bit_mask <<= 1; if(bit_mask == 0) { rom_byte++; bit_mask = 1; } } if(last_discrepancy == 0) break; rom_no++; if(rom_no >= ONEWIRE_MAX_SENSOR) break; } return rom_no + 1; } // 初始化总线 void onewire_init_bus(onewire_bus_t *bus, GPIO_TypeDef *port, uint32_t pin) { stc_gpio_cfg_t gpio_cfg; gpio_cfg.u16PinState = PIN_STATE_HIGH; gpio_cfg.u16PinDir = PIN_DIR_IN; gpio_cfg.u16PullUp = PIN_PULLUP_DISABLE; GPIO_Init(port, pin, &gpio_cfg); bus->gpio.port = port; bus->gpio.pin = pin; bus->sensor_count = 0; } // 扫描总线,返回传感器数量 uint8_t onewire_search_bus(onewire_bus_t *bus) { bus->sensor_count = onewire_search(bus, &bus->rom[0][0]); return bus->sensor_count; }
ds18b20.hDS18B20 驱动头文件
#ifndef DS18B20_H #define DS18B20_H #include "onewire.h" // DS18B20命令 #define DS18B20_CMD_CONVERT_TEMP 0x44 #define DS18B20_CMD_READ_SCRATCH 0xBE #define DS18B20_CMD_MATCH_ROM 0x55 #define DS18B20_CMD_SKIP_ROM 0xCC // 函数声明 uint8_t ds18b20_start_convert_all(onewire_bus_t *bus); float ds18b20_read_temp(onewire_bus_t *bus, uint8_t sensor_idx); void ds18b20_read_all_temp(onewire_bus_t *bus, float *temp_array); #endif
ds18b20.cDS18B20 驱动实现
#include "ds18b20.h" #include "delay.h" // 启动总线上所有传感器同步转换 uint8_t ds18b20_start_convert_all(onewire_bus_t *bus) { if(!onewire_reset(bus)) return 0; // 跳过ROM,所有设备同时接收命令 onewire_write_byte(bus, DS18B20_CMD_SKIP_ROM); // 启动转换 onewire_write_byte(bus, DS18B20_CMD_CONVERT_TEMP); return 1; } // 读取指定传感器的温度 float ds18b20_read_temp(onewire_bus_t *bus, uint8_t sensor_idx) { uint8_t data[9]; int16_t raw_temp; if(sensor_idx >= bus->sensor_count) return -273.0f; if(!onewire_reset(bus)) return -273.0f; // 匹配ROM,选中指定传感器 onewire_write_byte(bus, DS18B20_CMD_MATCH_ROM); for(uint8_t i=0; i<8; i++) { onewire_write_byte(bus, bus->rom[sensor_idx][i]); } // 读暂存器 onewire_write_byte(bus, DS18B20_CMD_READ_SCRATCH); for(uint8_t i=0; i<9; i++) { data[i] = onewire_read_byte(bus); } // 转换温度 raw_temp = (data[1] << 8) | data[0]; return raw_temp / 16.0f; } // 读取总线上所有传感器的温度 void ds18b20_read_all_temp(onewire_bus_t *bus, float *temp_array) { for(uint8_t i=0; i<bus->sensor_count; i++) { temp_array[i] = ds18b20_read_temp(bus, i); } }
main.c主函数
#include "hc32l130.h" #include "sysctrl.h" #include "uart.h" #include "delay.h" #include "onewire.h" #include "ds18b20.h" #include "rtc.h" // 总线配置:4路单总线,可自行修改 #define BUS_COUNT 4 onewire_bus_t ow_buses[BUS_COUNT]; float all_temps[BUS_COUNT][ONEWIRE_MAX_SENSOR]; // 初始化UART调试输出 void uart_init(void) { stc_uart_cfg_t uart_cfg; stc_gpio_cfg_t gpio_cfg; SYSCTRL_SetPeriphClock(SYSCTRL_PERIPH_UART1, ENABLE); SYSCTRL_SetPeriphClock(SYSCTRL_PERIPH_GPIOA, ENABLE); // PA9=TX, PA10=RX gpio_cfg.u16PinDir = PIN_DIR_OUT; gpio_cfg.u16PinState = PIN_STATE_HIGH; GPIO_Init(GPIO_PORT_A, GPIO_PIN_9, &gpio_cfg); GPIO_SetFunc(GPIO_PORT_A, GPIO_PIN_9, GPIO_FUNC_AF1); gpio_cfg.u16PinDir = PIN_DIR_IN; GPIO_Init(GPIO_PORT_A, GPIO_PIN_10, &gpio_cfg); GPIO_SetFunc(GPIO_PORT_A, GPIO_PIN_10, GPIO_FUNC_AF1); uart_cfg.u32BaudRate = 9600; uart_cfg.u8DataBits = UART_DATA_8BIT; uart_cfg.u8StopBits = UART_STOP_1BIT; uart_cfg.u8Parity = UART_PARITY_NONE; UART_Init(UART1, &uart_cfg); } // 初始化RTC唤醒 void rtc_init(void) { RTC_Init(); RTC_SetAlarm(1, 0); // 1分钟唤醒一次,可自行修改 } // 打印温度数据 void print_temps(void) { printf("===== 粮仓温度数据 =====\r\n"); for(uint8_t bus=0; bus<BUS_COUNT; bus++) { if(ow_buses[bus].sensor_count == 0) continue; printf("总线 %d: %d 个传感器\r\n", bus+1, ow_buses[bus].sensor_count); for(uint8_t i=0; i<ow_buses[bus].sensor_count; i++) { printf(" 传感器 %d: %.2f ℃\r\n", i+1, all_temps[bus][i]); } } printf("========================\r\n\r\n"); } int main(void) { // 系统初始化 SYSCTRL_ClkInit(); delay_init(); uart_init(); rtc_init(); printf("粮仓温度监控系统启动...\r\n"); // 初始化单总线 onewire_init_bus(&ow_buses[0], GPIO_PORT_A, GPIO_PIN_0); onewire_init_bus(&ow_buses[1], GPIO_PORT_A, GPIO_PIN_1); onewire_init_bus(&ow_buses[2], GPIO_PORT_A, GPIO_PIN_2); onewire_init_bus(&ow_buses[3], GPIO_PORT_A, GPIO_PIN_3); // 扫描所有总线的传感器 printf("正在扫描温度传感器...\r\n"); for(uint8_t i=0; i<BUS_COUNT; i++) { uint8_t count = onewire_search_bus(&ow_buses[i]); printf("总线 %d: 找到 %d 个传感器\r\n", i+1, count); } printf("扫描完成!\r\n"); while(1) { // 1. 所有总线同步启动转换 for(uint8_t i=0; i<BUS_COUNT; i++) { if(ow_buses[i].sensor_count > 0) { ds18b20_start_convert_all(&ow_buses[i]); } } // 2. 等待转换完成(12位分辨率最大750ms) delay_ms(750); // 3. 读取所有温度 for(uint8_t i=0; i<BUS_COUNT; i++) { if(ow_buses[i].sensor_count > 0) { ds18b20_read_all_temp(&ow_buses[i], all_temps[i]); } } // 4. 打印数据/处理数据/上报 print_temps(); // 5. 进入低功耗休眠,等待RTC唤醒 PWC_EnterStopMode(); } }

使用说明

  1. 硬件接线:按照上面的接线表连接,每路 DS18B20 的 DQ 线接对应的 GPIO,必须加 4.7k 上拉电阻,推荐外部供电,不要用寄生供电,保证长距离稳定。
  2. 配置修改:如果需要更多路总线,修改BUS_COUNT和总线初始化的代码即可,每路最多支持 16 个传感器。
  3. 唤醒周期:默认 1 分钟唤醒一次读取温度,可修改 RTC 的配置,改成 5 分钟或者更长,进一步降低功耗。
  4. 扩展功能:你可以在打印数据的部分,增加 Flash 存储温度数据,或者增加 LoRa/NB-IoT 无线上报,适配粮仓的远程监控。
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/3 1:23:02

Arduino上实现SID芯片立体声仿真:资源受限下的周期级音频建模

1. 项目概述StereoSID 是一个面向 Arduino 平台的 MOS6581 SID&#xff08;Sound Interface Device&#xff09;芯片软件仿真库&#xff0c;其核心目标是在资源受限的 AVR 微控制器&#xff08;如 ATmega328P&#xff09;上高保真复现 Commodore 64 经典音源芯片的模拟音频行为…

作者头像 李华
网站建设 2026/4/2 14:55:25

Evolutionary Architecture by Example:容器化部署与Docker最佳实践

Evolutionary Architecture by Example&#xff1a;容器化部署与Docker最佳实践 【免费下载链接】evolutionary-architecture-by-example Navigate the complex landscape of .NET software architecture with our step-by-step, story-like guide. Unpack the interplay betwe…

作者头像 李华
网站建设 2026/4/3 21:44:44

Docker化部署TranslateGemma:基于Ubuntu的容器化方案

Docker化部署TranslateGemma&#xff1a;基于Ubuntu的容器化方案 1. 引言 多语言翻译在全球化时代变得越来越重要&#xff0c;但传统翻译服务往往存在延迟高、隐私性差的问题。TranslateGemma作为基于Gemma 3的开源翻译模型&#xff0c;支持55种语言的高质量翻译&#xff0c;…

作者头像 李华
网站建设 2026/3/31 23:59:21

你的SSH密钥可能已经过期了

引言 在现代软件开发中&#xff0c;性能始终是衡量应用质量的重要指标之一。无论是企业级应用、云服务还是桌面程序&#xff0c;性能优化都能显著提升用户体验、降低基础设施成本并增强系统的可扩展性。对于使用 C# 开发的应用程序而言&#xff0c;性能优化涉及多个层面&#x…

作者头像 李华