在 8051 单片机项目中,温度采集是非常典型的一类应用。

本例基于 HRTOS 硬实时操作系统,使用 DS18B20 采集当前温度,并通过 4 位数码管实时显示温度数据。

整个应用并没有将所有功能堆在一个 while(1) 中,而是按照功能划分为两个独立任务:

  • DS18B20 温度读取任务

  • 4 位数码管显示任务

通过 HRTOS 的任务调度机制,让温度采集和数据显示分别运行,使程序结构更加清晰,也更接近实际嵌入式工程的组织方式。


一、系统功能

本示例主要完成以下功能:

  1. 初始化 DS18B20

  2. 初始化 4 位数码管

  3. 周期性启动 DS18B20 温度转换

  4. 等待温度转换完成

  5. 读取当前温度

  6. 将温度数据保存到全局变量

  7. 通过数码管显示当前温度

温度数据采用 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 应用。

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐