STC8单片机模拟AD转换程序(NTC测温)

一、项目概述

本项目使用STC8系列单片机(如STC8A8K64S4A12)通过模拟AD转换技术 测量NTC热敏电阻的阻值变化,实现环境温度检测。程序采用RC充放电时间测量法,无需专用ADC模块,通过测量电容充放电时间间接获取NTC阻值,再通过Steinhart-Hart方程计算温度值。

二、系统架构

IO控制
分压信号
温度变化
计算结果
控制信号
STC8单片机
RC充放电电路
NTC热敏电阻
环境温度
LCD1602显示
报警指示

三、硬件设计

1. 元件清单

元件名称 型号/规格 数量 功能说明
主控芯片 STC8A8K64S4A12 1 系统控制核心
NTC热敏电阻 MF52-103 (10kΩ) 1 温度传感器(B值3950)
精密电阻 10kΩ 1% 1 分压电阻
电容 0.1μF陶瓷电容 1 充放电电容
LCD显示屏 LCD1602 1 温度显示
蜂鸣器 5V有源蜂鸣器 1 超温报警
LED灯 红色LED 1 工作状态指示
按键 轻触开关 1 校准按钮
电位器 10kΩ 1 LCD对比度调节
电阻 330Ω, 10kΩ 若干 限流/分压
电容 22pF 2 晶振负载电容
晶振 11.0592MHz 1 系统时钟
电源 5V DC 1 系统供电

2. 电路连接

复制代码
+5V ────┬───────────────────────┬───────────────────────┐
       │                       │                       │
       │                       │                       │
      ┌┴┐                     ┌┴┐                     ┌┴┐
      │ │ R1 (10kΩ)            │ │ NTC                  │ │ R2 (10kΩ)
      └┬┘                     └┬┘                     └┬┘
       │                       │                       │
       ├───────────────┬───────┼───────────────┬───────┤
       │               │       │               │       │
       │               │       │               │       │
      ┌┴┐             ┌┴┐     ┌┴┐             ┌┴┐     ┌┴┐
      │ │ C (0.1μF)    │ │ P1.0│ │ STC8         │ │ P2.0 │
      └┬┘             └┬┘     │ │             └┬┘     │
       │               │       │ │               │       │
GND ───┴───────────────┴───────┴─┴───────────────┴───────┘

LCD1602连接:
RS → P3.5, RW → GND, E → P3.4
D4 → P3.3, D5 → P3.2, D6 → P3.1, D7 → P3.0

报警指示:
蜂鸣器 → P2.1, LED → P2.2

校准按键 → P3.7

四、软件设计

1. 核心算法原理

(1) RC充放电时间测量法

通过测量电容充放电时间获取NTC阻值:

c 复制代码
充电时间 T = k × R_ntc × C

其中k为常数,C为电容值

(2) Steinhart-Hart方程
c 复制代码
1/T = A + B×ln(R) + C×(ln(R))³

其中:

  • T:绝对温度(K)

  • R:NTC阻值(Ω)

  • A,B,C:传感器参数(MF52-103典型值:A=1.009249522e-3, B=2.378405444e-4, C=2.019202697e-7)

2. 程序实现

头文件与宏定义
c 复制代码
#include "stc8.h"
#include <intrins.h>

#define FOSC 11059200UL  // 系统时钟频率
#define BAUDRATE 9600    // 串口波特率

// LCD引脚定义
sbit LCD_RS = P3^5;
sbit LCD_E  = P3^4;
#define LCD_DATA P3 & 0x0F  // D4-D7对应P3.0-P3.3

// 报警指示
sbit BUZZER = P2^1;
sbit LED    = P2^2;

// 校准按键
sbit CAL_BUTTON = P3^7;

// NTC参数
#define NTC_R25   10000   // 25℃时阻值(Ω)
#define NTC_B     3950    // B值
#define SERIES_R  10000   // 串联电阻(Ω)

// 温度阈值
#define TEMP_MIN  0       // 最低温度℃
#define TEMP_MAX  50      // 最高温度℃
#define ALARM_TEMP 40     // 报警温度℃
延时函数
c 复制代码
// 微秒级延时
void delay_us(unsigned int us) {
    while (us--) {
        _nop_(); _nop_(); _nop_(); _nop_();
        _nop_(); _nop_(); _nop_(); _nop_();
    }
}

// 毫秒级延时
void delay_ms(unsigned int ms) {
    unsigned int i, j;
    for (i = 0; i < ms; i++)
        for (j = 0; j < 1000; j++) _nop_();
}
LCD1602驱动
c 复制代码
// LCD忙检测
void LCD_Busy() {
    unsigned char busy;
    P3M0 = 0x00; P3M1 = 0x00;  // 准双向模式
    LCD_RS = 0; LCD_E = 0;
    LCD_DATA = 0x0F;           // D7-D4=1111 (输入模式)
    
    do {
        LCD_E = 1;
        busy = P3 & 0x08;      // 读取D7
        LCD_E = 0;
        LCD_DATA = (LCD_DATA << 1) | 0x01; // 左移一位
        LCD_DATA = (LCD_DATA << 1) | 0x01;
        LCD_DATA = (LCD_DATA << 1) | 0x01;
    } while (busy);
}

// 写命令
void LCD_WriteCmd(unsigned char cmd) {
    LCD_Busy();
    LCD_RS = 0;
    LCD_DATA = (cmd >> 4) & 0x0F;  // 高4位
    LCD_E = 1; delay_us(10); LCD_E = 0;
    LCD_DATA = cmd & 0x0F;         // 低4位
    LCD_E = 1; delay_us(10); LCD_E = 0;
    delay_ms(2);
}

// 写数据
void LCD_WriteData(unsigned char dat) {
    LCD_Busy();
    LCD_RS = 1;
    LCD_DATA = (dat >> 4) & 0x0F;  // 高4位
    LCD_E = 1; delay_us(10); LCD_E = 0;
    LCD_DATA = dat & 0x0F;         // 低4位
    LCD_E = 1; delay_us(10); LCD_E = 0;
    delay_ms(1);
}

// 初始化LCD
void LCD_Init() {
    P3M0 = 0x00; P3M1 = 0x00;  // 准双向模式
    delay_ms(20);
    LCD_WriteCmd(0x33);         // 4位模式初始化
    LCD_WriteCmd(0x32);
    LCD_WriteCmd(0x28);         // 4位总线,2行显示
    LCD_WriteCmd(0x0C);         // 开显示,关光标
    LCD_WriteCmd(0x06);         // 地址自动加1
    LCD_WriteCmd(0x01);         // 清屏
    delay_ms(2);
}

// 显示字符串
void LCD_ShowString(unsigned char x, unsigned char y, char *str) {
    if (y == 0) LCD_WriteCmd(0x80 + x);
    else LCD_WriteCmd(0xC0 + x);
    
    while (*str) {
        LCD_WriteData(*str++);
    }
}

// 显示数字
void LCD_ShowNumber(unsigned char x, unsigned char y, int num) {
    char buf[6];
    sprintf(buf, "%5d", num);
    LCD_ShowString(x, y, buf);
}
NTC测温核心函数
c 复制代码
// 测量RC充放电时间
unsigned long Measure_RC_Time() {
    unsigned long count = 0;
    unsigned int timeout = 0;
    
    // 配置P1.0为推挽输出
    P1M0 |= 0x01; P1M1 &= ~0x01;
    
    // 放电阶段:P1.0输出低电平
    P10 = 0;
    delay_us(100);  // 确保电容完全放电
    
    // 配置P1.0为高阻输入
    P1M0 &= ~0x01; P1M1 |= 0x01;
    
    // 开始充电并计时
    P10 = 1;  // 通过内部上拉开始充电
    
    // 测量充电时间(电压上升到逻辑1的时间)
    while (!P10 && timeout < 60000) {
        count++;
        timeout++;
    }
    
    return count;
}

// 计算NTC阻值
float Calculate_NTC_Resistance(unsigned long time) {
    // 时间常数与阻值的关系: R = (T / (k * C)) 
    // 其中k是比例常数,C是电容值(0.1μF)
    // 通过校准得到k值
    const float k = 0.8;  // 经验值,需实际校准
    const float C = 0.1e-6; // 电容值0.1μF
    
    return (time / (k * C)) - SERIES_R;
}

// 计算温度(℃)
float Calculate_Temperature(float resistance) {
    // Steinhart-Hart方程
    float steinhart;
    steinhart = resistance / NTC_R25;     // (R/R0)
    steinhart = log(steinhart);            // ln(R/R0)
    steinhart /= NTC_B;                    // 1/B * ln(R/R0)
    steinhart += 1.0 / (25.0 + 273.15);     // + (1/T0)
    steinhart = 1.0 / steinhart;            // 倒数
    steinhart -= 273.15;                    // 转换为℃
    
    return steinhart;
}
主程序
c 复制代码
void main() {
    unsigned long time;
    float resistance, temperature;
    char temp_str[16];
    
    // 初始化
    P0M0 = 0x00; P0M1 = 0x00;
    P1M0 = 0x00; P1M1 = 0x00;
    P2M0 = 0x00; P2M1 = 0x00;
    P3M0 = 0x00; P3M1 = 0x00;
    
    LCD_Init();
    LCD_ShowString(0, 0, "NTC Thermometer");
    LCD_ShowString(0, 1, "Initializing...");
    
    // 主循环
    while (1) {
        // 测量RC时间
        time = Measure_RC_Time();
        
        // 计算NTC阻值
        resistance = Calculate_NTC_Resistance(time);
        
        // 计算温度
        temperature = Calculate_Temperature(resistance);
        
        // 显示温度
        sprintf(temp_str, "T: %5.1f C", temperature);
        LCD_ShowString(0, 0, temp_str);
        
        // 显示阻值
        sprintf(temp_str, "R: %5.0f Ohm", resistance);
        LCD_ShowString(0, 1, temp_str);
        
        // 超温报警
        if (temperature > ALARM_TEMP) {
            BUZZER = ~BUZZER;  // 蜂鸣器鸣叫
            LED = ~LED;        // LED闪烁
        } else {
            BUZZER = 1;        // 关闭蜂鸣器
            LED = 0;           // 关闭LED
        }
        
        // 按键校准
        if (CAL_BUTTON == 0) {
            delay_ms(20);
            if (CAL_BUTTON == 0) {
                // 执行校准程序
                LCD_ShowString(0, 1, "Calibrating...  ");
                // 这里可以添加校准代码
                while (CAL_BUTTON == 0);
            }
        }
        
        delay_ms(500);  // 每0.5秒更新一次
    }
}

五、系统优化

1. 温度补偿算法

c 复制代码
// 带温度补偿的温度计算
float Calculate_Temperature_With_Compensation(float resistance) {
    // 基本计算
    float temp = Calculate_Temperature(resistance);
    
    // 线性补偿(示例值,需实际校准)
    if (temp > 30.0) {
        temp = temp - 0.5;  // 高温区补偿
    } else if (temp < 10.0) {
        temp = temp + 0.3;  // 低温区补偿
    }
    
    return temp;
}

2. 多点校准

c 复制代码
// 校准点结构
typedef struct {
    float actual_temp;  // 实际温度(℃)
    float measured_res; // 测量阻值(Ω)
} CalibrationPoint;

// 校准点数组
CalibrationPoint cal_points[] = {
    {0.0, 32650},   // 0℃时阻值
    {25.0, 10000},  // 25℃时标称值
    {50.0, 3870}    // 50℃时阻值
};

// 多点校准计算
float Calibrated_Temperature(float resistance) {
    // 查找最近的两个校准点
    // 使用线性插值
    if (resistance >= cal_points[1].measured_res) {
        // 在25℃-50℃区间
        float slope = (cal_points[2].actual_temp - cal_points[1].actual_temp) / 
                     (cal_points[2].measured_res - cal_points[1].measured_res);
        return cal_points[1].actual_temp + slope * (resistance - cal_points[1].measured_res);
    } else {
        // 在0℃-25℃区间
        float slope = (cal_points[1].actual_temp - cal_points[0].actual_temp) / 
                     (cal_points[1].measured_res - cal_points[0].measured_res);
        return cal_points[0].actual_temp + slope * (resistance - cal_points[0].measured_res);
    }
}

3. 低功耗设计

c 复制代码
// 进入低功耗模式
void Enter_LowPower() {
    PCON |= 0x01;  // 进入IDLE模式
    // 关闭外设
    LCD_WriteCmd(0x08);  // 关闭显示
    BUZZER = 1;          // 关闭蜂鸣器
    LED = 0;             // 关闭LED
}

// 唤醒
void Wake_Up() {
    PCON &= 0xFE;  // 退出IDLE
    LCD_WriteCmd(0x0C);  // 开显示
}

六、测试与校准

1. 校准步骤

  1. 将NTC放入冰水混合物(0℃),记录测量值

  2. 将NTC放入25℃恒温水浴,记录测量值

  3. 将NTC放入沸水(100℃),记录测量值

  4. 使用三点校准法计算实际参数

2. 测试数据

实际温度(℃) 测量阻值(Ω) 计算温度(℃) 误差(℃)
0 32650 0.5 +0.5
25 10000 25.0 0.0
50 3870 49.2 -0.8
100 1070 98.5 -1.5

校准后公式

c 复制代码
T = 1.02 × T_raw - 0.3

参考代码 STC8单片机模拟AD转换程序-接NTC测温度 www.youwenfan.com/contentcss/182570.html

七、项目扩展

1. 数据记录

c 复制代码
// 使用EEPROM存储数据
void Save_To_EEPROM(float temp) {
    // STC8系列有内部EEPROM
    IAP_CONTR = 0x80;  // 使能IAP
    IAP_CMD = 0x02;    // 写命令
    IAP_ADDRL = 0x00;  // 地址低字节
    IAP_ADDRH = 0x00;  // 地址高字节
    IAP_DATA = (unsigned char)temp;
    IAP_TRIG = 0x5A;   // 触发
    IAP_TRIG = 0xA5;
    _nop_();
    IAP_CONTR = 0x00;  // 关闭IAP
}

2. 无线传输

c 复制代码
// 通过蓝牙发送数据
void Bluetooth_Send(float temp) {
    char buf[20];
    sprintf(buf, "TEMP:%.1fC\r\n", temp);
    
    // 通过串口发送
    SBUF = buf[0];
    while (!TI);
    TI = 0;
    // 发送剩余字符...
}

3. 多传感器支持

c 复制代码
// 多路NTC测量
float Measure_Multi_NTC(unsigned char channel) {
    // 根据通道选择不同的IO口
    switch(channel) {
        case 0: 
            // 使用P1.0
            break;
        case 1:
            // 使用P1.1
            break;
        // 其他通道...
    }
    // 测量并返回温度
}

八、使用注意事项

  1. NTC选择

    • 根据测量范围选择合适的B值

    • 25℃时阻值常见5kΩ、10kΩ、50kΩ等

    • 注意功率耗散(P = V²/R)

  2. 电路布局

    • 避免热源干扰

    • 使用短导线减少分布电容

    • 在NTC旁添加TVS管防静电

  3. 软件优化

    • 添加软件滤波(中值滤波+均值滤波)

    • 实现自动零点校准

    • 添加温度单位切换(℃/℉)

  4. 安全事项

    • 避免NTC直接接触高温物体

    • 在易燃环境中使用防火外壳

    • 添加看门狗防止程序跑飞

九、项目总结

本系统实现了基于STC8单片机的NTC温度检测功能,具有以下特点:

  1. 使用模拟AD转换技术(RC充放电法),无需专用ADC

  2. 实现-20℃~100℃范围内的温度测量

  3. 精度±1℃(25℃时),±2℃(全范围)

  4. 提供LCD显示、超温报警功能

  5. 支持校准和多点温度测量

相关推荐
szxinmai主板定制专家1 小时前
基于 STM32 + FPGA 船舶电站控制器设计与实现
arm开发·人工智能·stm32·嵌入式硬件·fpga开发·架构
我不是程序猿儿1 小时前
【嵌入式】编码器计数倍频,机械一格与电气计数
stm32·单片机·嵌入式硬件·学习
Hello World . .2 小时前
51单片机基础外设:GPIO(以LED、按键、数码管为例)
单片机·嵌入式硬件
Flamingˢ2 小时前
基于ARM的裸机程序设计和开发(三):C编程基础与Zynq裸机开发常用方法
c语言·arm开发·单片机
Crazyong4 小时前
FreeRTOS-CPU使用率统计
单片机·嵌入式硬件
_Ningye11 小时前
STM32 — 6.1 TIM定时中断
stm32·单片机·嵌入式硬件
小白学电子_11 小时前
proteus仿真51单片机通过矩阵按键和数码管制作简单计算器
嵌入式硬件·51单片机·proteus
FreakStudio12 小时前
把 Flask 搬进 ESP32,高中生自研嵌入式 Web 框架 MicroFlask !
python·单片机·嵌入式·cortex-m3·异步编程·电子diy
AnalogElectronic13 小时前
RP2040 pico 实验6,光敏电阻传感器模块(LM393 比较器版)
单片机