基于 51 单片机的 NTC 温度采样程序
一、硬件设计
1、典型 NTC 采样电路
VCC (5V)
│
│
[R1] 10kΩ(精密电阻)
│
├─── ADC_IN(P1.0)
│
[NTC] 10kΩ @25℃
│
│
GND
推荐参数
| 参数 | 值 |
|---|---|
| NTC 型号 | NTC 10kΩ B3950 |
| 分压电阻 | 10kΩ ±1% |
| 滤波电容 | 0.1µF |
| ADC 参考电压 | 5V |
| 采样通道 | P1.0(ADC0) |
二、NTC 温度计算原理
1、电阻计算
Vadc = Vcc × Rntc / (R1 + Rntc)
Rntc = R1 × Vadc / (Vcc - Vadc)
2、温度公式(Steinhart-Hart)
1/T = 1/T0 + (1/B) × ln(Rntc/R0)
T(℃) = T(K) - 273.15
简化计算(B3950)
R25 = 10kΩ
B = 3950
T25 = 298.15K
三、完整程序(Keil C51)
1、头文件与宏定义
#include <reg52.h>
#include <intrins.h>
#include <math.h>
#define uchar unsigned char
#define uint unsigned int
/* 硬件引脚 */
sbit LED = P2^0; // 状态指示
sbit KEY = P3^2; // 校准按键
/* NTC 参数 */
#define R1 10000.0f // 分压电阻 10k
#define R25 10000.0f // NTC 25℃阻值
#define B_VALUE 3950.0f // B值
#define T25_K 298.15f // 25℃开尔文
/* ADC 参数 */
#define ADC_MAX 255 // 8位ADC
#define VCC 5.0f // 供电电压
2、延时函数
void DelayMs(uint ms)
{
uint i, j;
for(i = ms; i > 0; i--)
for(j = 110; j > 0; j--);
}
3、ADC 读取(STC89C52 内部 ADC)
/* ADC 初始化 */
void ADC_Init(void)
{
P1ASF = 0x01; // P1.0 作为 ADC
ADC_RES = 0;
ADC_CONTR = 0x80; // 开启 ADC 电源
DelayMs(1);
}
/* 读取 ADC 值 */
uint ADC_Read(uchar ch)
{
uint val;
ADC_CONTR = 0x88 | ch; // 启动 ADC
_nop_(); _nop_(); _nop_(); _nop_();
while(!(ADC_CONTR & 0x10)); // 等待转换完成
ADC_CONTR &= ~0x10; // 清除标志
val = ADC_RES; // 8位结果
return val;
}
如果是 AT89C51(无 ADC)
→ 用 RC 充放电法 或 外部 ADC(PCF8591)
4、NTC 电阻计算
/* 计算 NTC 电阻值 */
float NTC_GetResistance(uint adc_val)
{
float voltage, resistance;
voltage = (float)adc_val * VCC / ADC_MAX;
resistance = R1 * voltage / (VCC - voltage);
return resistance;
}
5、温度计算
/* 电阻转温度(℃) */
float NTC_GetTemperature(float resistance)
{
float temp_k, temp_c;
temp_k = 1.0f / (1.0f / T25_K + (1.0f / B_VALUE) *
log(resistance / R25));
temp_c = temp_k - 273.15f;
return temp_c;
}
6、滑动平均滤波(抗干扰)
#define FILTER_LEN 10
float Temperature_Filter(float new_temp)
{
static float buffer[FILTER_LEN] = {0};
static uchar index = 0;
float sum = 0;
buffer[index] = new_temp;
index = (index + 1) % FILTER_LEN;
for(uchar i = 0; i < FILTER_LEN; i++)
sum += buffer[i];
return sum / FILTER_LEN;
}
7、数码管显示
uchar seg_tab[10] = {
0x3f, 0x06, 0x5b, 0x4f, 0x66,
0x6d, 0x7d, 0x07, 0x7f, 0x6f
};
void Seg_Display(float temp)
{
int t = (int)(temp * 10);
P0 = seg_tab[t / 100]; // 十位
P2 = 0xFE; DelayMs(2);
P0 = seg_tab[t / 10 % 10]; // 个位
P2 = 0xFD; DelayMs(2);
P0 = seg_tab[t % 10]; // 小数
P2 = 0xFB; DelayMs(2);
}
8、主函数
void main(void)
{
float resistance, temperature, temp_filtered;
uint adc_val;
ADC_Init();
EA = 1; // 开总中断
while(1) {
adc_val = ADC_Read(0); // 读取 ADC
resistance = NTC_GetResistance(adc_val);
temperature = NTC_GetTemperature(resistance);
temp_filtered = Temperature_Filter(temperature);
Seg_Display(temp_filtered); // 显示
if(temp_filtered > 50.0f) // 超温报警
LED = 0;
else
LED = 1;
DelayMs(500); // 500ms 采样
}
}
四、无 ADC 的 51 单片机方案(RC 充放电)
1、RC 测电阻原理
P1.0 ── Rntc ──┬── P1.1
│
C 100nF
│
GND
2、简易 RC 采样代码
uint RC_GetValue(void)
{
uint count = 0;
P1 = 0x00; // 放电
DelayMs(10);
P1_0 = 1; // 开始充电
while(!P1_1 && count < 50000) {
count++;
}
return count;
}
五、精度优化技巧
提高精度的方法
| 方法 | 效果 |
|---|---|
| 分压电阻用 1% | ±0.5℃ |
| 多次采样平均 | 抗干扰 |
| 分段查表法 | 避免浮点误差 |
| 软件校准 | 消除系统误差 |
| 温度补偿 | 抵消自热 |
分段查表法(不用浮点)
code uint16_t ntc_table[21] = {
32200, 24300, 18500, 14200, 11000, // -10~10℃
8600, 6800, 5400, 4300, 3500,
2900, 2400, 2000, 1700, 1450,
1250, 1080, 950, 840, 740
};
float Table_GetTemp(uint16_t r)
{
uchar i;
for(i = 0; i < 20; i++) {
if(r > ntc_table[i])
return (float)(i - 10);
}
return 20.0f;
}
参考代码 基于51单片机的NTC温度采样程序 www.youwenfan.com/contentcnv/72815.html
六、典型误差来源
| 误差来源 | 影响 | 解决 |
|---|---|---|
| 分压电阻误差 | ±1℃ | 用 1% 电阻 |
| ADC 非线性 | ±0.5℃ | 软件校准 |
| 自热效应 | ±0.3℃ | 降低采样电流 |
| 导线电阻 | ±0.2℃ | 三线制 |
| 环境温度 | ±0.5℃ | 温度补偿 |
七、完整工程结构
NTC_Temp_51/
├── Src/
│ ├── main.c
│ ├── ntc.c
│ ├── adc.c
│ └── display.c
├── Inc/
│ ├── ntc.h
│ ├── adc.h
│ └── display.h
├── Doc/
│ ├── NTC_10K_B3950.txt
│ └── Schematic.pdf
└── Keil/
└── NTC_Temp.uvproj

浙公网安备 33010602011771号