STC15单片机+DS18B20:非阻塞式温度采集系统设计与实现

在嵌入式系统开发中,阻塞式延时往往是影响系统实时性和资源利用率的瓶颈。当我们需要同时处理多个任务时,传统的 Delay800ms() 方式会让CPU陷入无意义的等待状态。本文将介绍如何利用定时器中断构建一个高效、稳定的非阻塞式温度采集系统,实现DS18B20温度传感器数据的可靠获取。

1. 系统架构设计思路

传统的DS18B20驱动代码通常采用阻塞式延时等待温度转换完成,这种方式在简单的单任务系统中尚可接受,但在需要同时处理显示、按键、通信等多任务的系统中就显得力不从心。我们需要的是一种能够:

  • 不占用CPU资源的等待机制
  • 精确控制采样周期
  • 安全可靠的数据传递方式
  • 易于集成到现有系统中

核心设计思想 是将温度采集过程分解为多个状态,通过定时器中断推进状态机运行,主循环只需检查状态标志即可获取最新温度值。这种方式完全避免了阻塞式延时,让CPU可以高效处理其他任务。

2. 硬件与基础配置

2.1 硬件连接

STC15单片机与DS18B20的典型连接方式如下:

STC15F2K60S2          DS18B20
P1.4 (DQ)  --------   DQ (2)
GND        --------   GND (1)
VCC (5V)   --------   VCC (3)

注意:实际应用中建议在DQ线上加4.7K上拉电阻

2.2 定时器初始化

我们使用定时器0作为系统时基,配置为16位自动重装模式,产生10ms中断:

void Timer0_Init(void)
{
    AUXR |= 0x80;       // 定时器0为1T模式
    TMOD &= 0xF0;       // 设置定时器模式
    TL0 = 0x00;         // 初始化定时值
    TH0 = 0xDC;         // 10ms@12MHz
    TF0 = 0;            // 清除TF0标志
    TR0 = 1;            // 定时器0开始计时
    ET0 = 1;            // 使能定时器0中断
    EA = 1;             // 开总中断
}

3. 状态机设计与实现

3.1 温度采集状态机

我们将温度采集过程划分为以下几个状态:

typedef enum {
    TEMP_IDLE,          // 空闲状态
    TEMP_START_CONV,    // 启动转换
    TEMP_WAIT_CONV,     // 等待转换完成
    TEMP_READ_DATA,     // 读取温度数据
    TEMP_DATA_READY     // 数据就绪
} TempState;

3.2 定时器中断服务程序

在定时器中断中推进状态机:

void Timer0_Isr() interrupt 1
{
    static uint8_t count = 0;
    
    if(++count >= 50) { // 500ms采样周期
        count = 0;
        if(tempState == TEMP_IDLE) {
            tempState = TEMP_START_CONV;
        }
    }
    
    // 状态机处理
    switch(tempState) {
        case TEMP_START_CONV:
            if(init_ds18b20()) {
                Write_DS18B20(0xCC); // 跳过ROM
                Write_DS18B20(0x44); // 启动转换
                tempState = TEMP_WAIT_CONV;
                waitCount = 0;
            }
            break;
            
        case TEMP_WAIT_CONV:
            if(++waitCount >= 80) { // 800ms等待
                tempState = TEMP_READ_DATA;
            }
            break;
            
        case TEMP_READ_DATA:
            if(init_ds18b20()) {
                Write_DS18B20(0xCC); // 跳过ROM
                Write_DS18B20(0xBE); // 读取暂存器
                tempLSB = Read_DS18B20();
                tempMSB = Read_DS18B20();
                tempState = TEMP_DATA_READY;
            }
            break;
            
        default:
            break;
    }
}

4. 主循环与数据安全

4.1 主循环设计

主循环只需检查 tempState 状态并处理就绪的温度数据:

void main()
{
    float currentTemp;
    Timer0_Init();
    
    while(1) {
        if(tempState == TEMP_DATA_READY) {
            // 数据处理
            currentTemp = ((tempMSB << 8) | tempLSB) * 0.0625;
            
            // 更新显示或其他处理
            updateDisplay(currentTemp);
            
            // 重置状态
            tempState = TEMP_IDLE;
        }
        
        // 其他任务处理
        scanKeyboard();
        processCommunication();
    }
}

4.2 数据安全措施

为确保温度数据的可靠性,我们采取以下措施:

  1. 数据校验 :检查读取的温度值是否在合理范围内(-55℃~+125℃)
  2. 中值滤波 :保存最近3次采样值,取中间值作为最终结果
  3. 异常处理 :当连续多次读取失败时,自动重置DS18B20
#define MAX_TEMP 125.0
#define MIN_TEMP -55.0

float validateTemperature(float temp)
{
    static float history[3];
    static uint8_t index = 0;
    float sorted[3];
    
    // 范围检查
    if(temp < MIN_TEMP || temp > MAX_TEMP) {
        return INVALID_TEMP; // 定义无效温度常量
    }
    
    // 中值滤波
    history[index++] = temp;
    if(index >= 3) index = 0;
    
    memcpy(sorted, history, sizeof(history));
    bubbleSort(sorted); // 实现简单的排序算法
    
    return sorted[1];
}

5. 性能优化技巧

5.1 降低中断影响

DS18B20对时序要求严格,中断可能干扰单总线通信。我们采取以下优化:

  • 在关键时序操作期间临时关闭中断
  • 将单总线操作放在主循环中执行
  • 使用状态机避免长时间占用总线
void Write_DS18B20_Optimized(unsigned char dat)
{
    EA = 0; // 关闭中断
    // 原有写操作代码
    EA = 1; // 恢复中断
}

5.2 动态调整采样率

根据系统负载动态调整温度采样频率:

void adjustSampleRate(uint8_t systemLoad)
{
    // 系统负载高时降低采样率
    if(systemLoad > 70) {
        sampleInterval = 100; // 1秒
    } else {
        sampleInterval = 50;  // 0.5秒
    }
}

5.3 低功耗优化

对于电池供电设备,可以进一步优化:

  • 温度转换期间进入空闲模式
  • 降低MCU工作频率
  • 关闭不必要的外设
void enterLowPowerMode(void)
{
    PCON |= 0x01; // 进入空闲模式
    // 由外部中断或定时器唤醒
}

6. 实际应用案例

6.1 智能温控系统

将非阻塞式温度采集应用于恒温箱控制:

  1. 500ms采集一次温度
  2. PID算法计算控制量
  3. 驱动加热/制冷模块
  4. LCD实时显示温度曲线

6.2 多节点温度监测

通过单总线连接多个DS18B20,按地址轮询采集:

void readMultipleSensors(void)
{
    if(init_ds18b20()) {
        Write_DS18B20(0x55); // 匹配ROM命令
        Write_DS18B20_Sequence(romCode); // 发送传感器地址
        Write_DS18B20(0x44); // 启动转换
        // 其他处理...
    }
}

注意:多节点系统需要先执行搜索ROM操作获取所有设备地址

7. 常见问题解决方案

7.1 温度值跳动大

可能原因及解决方法:

  1. 电源噪声 :在VCC和GND之间添加0.1μF去耦电容
  2. 总线干扰 :缩短总线长度,增加上拉电阻值
  3. 时序不精确 :检查MCU时钟配置,确保延时准确

7.2 读取失败处理

建立重试机制:

#define MAX_RETRY 3

float safeReadTemperature(void)
{
    uint8_t retry = 0;
    float temp;
    
    while(retry < MAX_RETRY) {
        temp = readTemperature();
        if(temp != INVALID_TEMP) {
            return temp;
        }
        retry++;
        delayMs(100);
    }
    return INVALID_TEMP; // 仍失败返回无效值
}

7.3 负温度处理

正确处理负温度值:

float processNegativeTemp(int16_t raw)
{
    if(raw & 0xF800) { // 检查符号位
        raw = ~raw + 1; // 取补码
        return -(raw * 0.0625);
    }
    return raw * 0.0625;
}

���过本文介绍的非阻塞式设计方法,开发者可以构建出响应更快、资源利用率更高的温度监测系统。实际项目中,建议根据具体需求调整采样频率和滤波算法,在实时性和稳定性之间取得平衡。

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐