HRTOS 实战:DS18B20 温度采集与4位数码管显示
在 8051 单片机项目中,温度采集是非常典型的一类应用。
本例基于 HRTOS 硬实时操作系统,使用 DS18B20 采集当前温度,并通过 4 位数码管实时显示温度数据。
整个应用并没有将所有功能堆在一个 while(1) 中,而是按照功能划分为两个独立任务:
-
DS18B20 温度读取任务
-
4 位数码管显示任务
通过 HRTOS 的任务调度机制,让温度采集和数据显示分别运行,使程序结构更加清晰,也更接近实际嵌入式工程的组织方式。
一、系统功能
本示例主要完成以下功能:
-
初始化 DS18B20
-
初始化 4 位数码管
-
周期性启动 DS18B20 温度转换
-
等待温度转换完成
-
读取当前温度
-
将温度数据保存到全局变量
-
通过数码管显示当前温度
温度数据采用 0.01℃ 作为内部单位。
例如:
2525 → 25.25℃
1850 → 18.50℃
3000 → 30.00℃
这样可以避免在单片机中频繁使用浮点数,同时也方便进行整数运算和数据显示。
二、程序结构
本例使用两个 HRTOS 任务:
HRTOS
│
┌─────────┴─────────┐
│ │
▼ ▼
DS18B20温度读取任务 数码管显示任务
│ │
▼ ▼
读取温度数据 获取当前温度
│ │
└───────┬───────────┘
▼
4位数码管显示
两个任务各自负责一项功能。
任务一:温度采集
void task_ds18b20_read(void)
{
while(1)
{
drv_ds18b20_start();
os_delay(75);
ds18b20_temperature = drv_ds18b20_read();
os_delay(25);
}
}
这个任务主要负责 DS18B20 的完整温度采集流程。
首先启动温度转换:
drv_ds18b20_start();
然后等待转换完成:
os_delay(75);
最后读取温度:
ds18b20_temperature = drv_ds18b20_read();
三、为什么这里使用 os_delay()?
DS18B20 在 12 位分辨率下,一次温度转换最长需要约 750ms。
本例中 HRTOS 的 os_delay() 时间单位为 10ms,因此:
75 × 10ms = 750ms
所以代码使用:
os_delay(75);
等待温度转换完成。
这里使用操作系统延时的一个重要意义是:
温度转换期间不需要让 CPU 忙等。
传统裸机程序可能会写成类似:
while(等待转换完成)
{
}
而在 RTOS 中,可以直接将当前任务延时:
os_delay(75);
当前任务进入等待状态后,系统可以继续调度其他任务。
这也是 RTOS 在实际应用中非常典型的使用方式:
一个任务等待自己的外设操作时,不需要阻塞整个系统。
四、温度数据为什么采用 0.01℃?
程序中定义:
signed int ds18b20_temperature = 0;
温度单位为 0.01℃。
例如:
25.25℃ → 2525
18.50℃ → 1850
这样设计主要有两个好处。
1. 不需要浮点数
8051 属于资源比较有限的 MCU,很多应用没有必要为了显示一个温度值而引入浮点运算。
使用整数即可完成温度处理。
2. 方便数码管拆分
例如:
value = temperature;
然后直接进行十进制位分解:
drv_seg_display(0, value / 1000);
drv_seg_display(1, value / 100 % 10);
drv_seg_display(2, value / 10 % 10);
drv_seg_display(3, value % 10);
这样就可以将温度数据拆分成 4 个数字。
例如:
2525
千位:2
百位:5
十位:2
个位:5
最终数码管显示:
2525
实际硬件显示时,可以根据数码管硬件设计决定小数点的位置,从而表达为:
25.25℃
五、数码管显示任务
第二个任务负责显示:
void task_seg_display(void)
{
signed int temperature;
unsigned int value;
while(1)
{
temperature = ds18b20_temperature;
if(temperature >= 0)
{
value = temperature;
drv_seg_display(0, value / 1000);
drv_seg_display(1, value / 100 % 10);
drv_seg_display(2, value / 10 % 10);
drv_seg_display(3, value % 10);
}
}
}
这里可以看到,显示任务并不负责 DS18B20 的通信。
它只关心一件事情:
当前温度是多少?
因此两个任务之间形成了比较清晰的职责划分。
task_ds18b20_read
│
│ 更新
▼
ds18b20_temperature
│
│ 读取
▼
task_seg_display
│
▼
数码管
这种设计比把“传感器读取 + 数据处理 + 数码管显示”全部写在一个超级循环里面更加容易扩展。
六、HRTOS 任务创建
在 hrtos_main() 中完成硬件初始化以及任务创建:
void hrtos_main(void)
{
drv_ds18b20_init();
drv_seg_init();
os_task_create(
task_ds18b20_read,
2,
2,
6
);
os_task_create(
task_seg_display,
3,
2,
4
);
}
首先初始化 DS18B20:
drv_ds18b20_init();
然后初始化数码管:
drv_seg_init();
最后创建两个任务。
七、这个例子体现了什么?
这个示例虽然功能非常简单,但实际上已经具备了一个典型嵌入式 RTOS 应用的基本结构。
可以将整个程序理解成:
HRTOS
│
┌───────────┴───────────┐
│ │
▼ ▼
温度采集任务 显示任务
│ │
▼ ▼
DS18B20 数码管
│
▼
温度共享数据
应用层只需要关心任务本身的功能。
例如温度任务只负责:
启动转换
↓
等待
↓
读取温度
↓
保存数据
↓
下一次采集
显示任务只负责:
获取温度
↓
数据拆分
↓
数码管显示
↓
持续刷新
这种模块化结构对于后续增加功能非常方便。
例如以后增加:
-
按键设置温度阈值
-
蜂鸣器报警
-
LCD/OLED 显示
-
串口输出温度
-
Modbus 温度上传
-
温度数据存储
-
多个 DS18B20
-
温度控制
都可以继续增加独立任务或者独立驱动,而不需要重新组织整个主循环。
八、HRTOS 与传统裸机程序的区别
如果采用传统裸机方式,程序可能会集中在一个循环中:
while(1)
{
ds18b20_start();
/* 等待温度转换 */
temperature = ds18b20_read();
/* 数码管显示 */
/* 其他功能 */
}
随着功能增加,主循环会逐渐变得复杂。
而使用 HRTOS 后,可以将功能拆分:
任务1:温度采集
任务2:数码管显示
任务3:按键扫描
任务4:串口通信
任务5:温度报警
任务6:数据存储
每个任务负责自己的工作。
这也是 HRTOS 应用层示例希望体现的一点:
RTOS 并不是为了让一个简单程序变复杂,而是为了让多个功能同时存在时,程序仍然保持清晰的结构。
九、完整代码
#include "hrtos.h"
#include "hrtos_hal.h"
signed int ds18b20_temperature = 0;
/*------------------------------------------------
* DS18B20 温度读取任务
*------------------------------------------------*/
void task_ds18b20_read(void)
{
while(1)
{
/* 启动 DS18B20 温度转换 */
drv_ds18b20_start();
/*
* 12 位分辨率下,
* 温度转换最长约 750ms。
*
* os_delay() 单位为 10ms。
*/
os_delay(75);
/* 读取温度,单位:0.01℃ */
ds18b20_temperature = drv_ds18b20_read();
/* 等待下一次采集 */
os_delay(25);
}
}
/*------------------------------------------------
* 数码管温度显示任务
*------------------------------------------------*/
void task_seg_display(void)
{
signed int temperature;
unsigned int value;
while(1)
{
/* 获取当前温度 */
temperature = ds18b20_temperature;
/* 当前示例处理非负温度 */
if(temperature >= 0)
{
value = temperature;
/* 千位 */
drv_seg_display(0, value / 1000);
/* 百位 */
drv_seg_display(1, value / 100 % 10);
/* 十位 */
drv_seg_display(2, value / 10 % 10);
/* 个位 */
drv_seg_display(3, value % 10);
}
}
}
/**
* @brief HRTOS 应用程序入口
*/
void hrtos_main(void)
{
/* 初始化 DS18B20 */
drv_ds18b20_init();
/* 初始化 4 位数码管 */
drv_seg_init();
/* 创建 DS18B20 温度读取任务 */
os_task_create(
task_ds18b20_read,
2,
2,
6
);
/* 创建数码管显示任务 */
os_task_create(
task_seg_display,
3,
2,
4
);
}
十、总结
通过这个 DS18B20 + 4 位数码管示例,可以看到一个比较完整的 8051 RTOS 应用基本结构:
底层驱动负责硬件,HRTOS 负责任务调度,应用任务负责具体功能。
最终形成:
应用层
│
├── 温度采集任务
│
└── 数码管显示任务
│
▼
HRTOS
│
▼
HAL / Driver
│
┌────┴────┐
▼ ▼
DS18B20 数码管
对于 8051 这类资源有限的 MCU,这种结构尤其适合功能逐渐增加的工程项目。
一个温度显示程序只是起点。
当继续加入按键、通信、报警、控制、存储等功能后,就可以逐渐形成一个真正意义上的多任务嵌入式应用。
这也是 HRTOS 应用层示例的意义:不仅展示某个驱动怎么使用,更展示如何把多个硬件功能组织成一个完整的 RTOS 应用。
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐

所有评论(0)