一、项目概述
本项目使用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. 校准步骤
-
将NTC放入冰水混合物(0℃),记录测量值
-
将NTC放入25℃恒温水浴,记录测量值
-
将NTC放入沸水(100℃),记录测量值
-
使用三点校准法计算实际参数
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;
// 其他通道...
}
// 测量并返回温度
}
八、使用注意事项
-
NTC选择:
-
根据测量范围选择合适的B值
-
25℃时阻值常见5kΩ、10kΩ、50kΩ等
-
注意功率耗散(P = V²/R)
-
-
电路布局:
-
避免热源干扰
-
使用短导线减少分布电容
-
在NTC旁添加TVS管防静电
-
-
软件优化:
-
添加软件滤波(中值滤波+均值滤波)
-
实现自动零点校准
-
添加温度单位切换(℃/℉)
-
-
安全事项:
-
避免NTC直接接触高温物体
-
在易燃环境中使用防火外壳
-
添加看门狗防止程序跑飞
-
九、项目总结
本系统实现了基于STC8单片机的NTC温度检测功能,具有以下特点:
-
使用模拟AD转换技术(RC充放电法),无需专用ADC
-
实现-20℃~100℃范围内的温度测量
-
精度±1℃(25℃时),±2℃(全范围)
-
提供LCD显示、超温报警功能
-
支持校准和多点温度测量