keilc51的程序
时间: 2025-06-30 18:22:19 浏览: 19
<think>我们根据用户需求设计一个基于Keil C51的温度测控系统程序。系统将使用热敏电阻和热电偶作为温度传感器,通过查表插值法计算温度,支持定时或实时采集,具有通道显示、阈值报警、执行机构控制及串口通信功能。设计思路:1.温度采集:使用ADC(如ADC0809)读取热敏电阻的电压值,热电偶信号需经过放大和冷端补偿后由ADC读取。2.温度计算:采用查表法结合线性插值计算实际温度。3.采集模式:支持定时采集(使用定时器中断)和实时采集(通过按键触发)。4.通道显示:通过数码管或LCD显示不同通道的温度值。5.阈值报警:当温度超过设定阈值时,触发报警(如蜂鸣器、LED)。6.执行机构控制:通过继电器或可控硅控制加热/制冷设备。7.串口通信:将温度数据发送到上位机,并接收上位机的控制命令。硬件连接假设:-热敏电阻通道:P1.0(ADC通道0)-热电偶通道:P1.1(ADC通道1)
-数码管显示:P0口(段选),P2口(位选)
-报警输出:P3.2(蜂鸣器),P3.3(LED)-执行机构控制:P3.4(继电器控制加热)-串口通信:P3.0(RXD),P3.1(TXD)软件设计:1.主程序:初始化各模块,循环处理显示、按键、串口等任务。2.定时器0:用于定时采集(例如每1秒采集一次)。3.定时器1:用于串口波特率发生器。4.串口中断:接收上位机命令,发送温度数据。5. ADC读取函数:读取指定通道的ADC值。6.温度计算函数:根据ADC值查表并插值得到温度。7.显示函数:在数码管上轮流显示两个通道的温度。8.报警判断函数:判断当前温度是否超过阈值,控制报警输出。9.控制函数:根据温度和目标值,控制执行机构。由于热敏电阻和热电偶的特性不同,我们将分别建立两个表格(数组)用于查表。步骤:1.初始化系统时钟、定时器、串口、ADC、IO口等。2.主循环中检测按键和串口命令,切换采集模式(定时/实时)和设置参数。3.在定时器中断中触发采集(定时模式),或者按键触发采集(实时模式)。4.采集后计算温度,更新显示,判断报警,执行控制。5.串口接收命令可设置阈值、控制执行机构开关、请求发送数据等。代码结构如下:注意:由于代码较长,这里给出主要框架和关键函数,详细实现需要根据具体硬件调整。首先,包含必要的头文件,定义全局变量和常量。
*/#include<reg52.h>#include <stdio.h> //用于串口打印//定义ADC控制引脚(假设使用ADC0809)sbitADC_START =P2^4;sbitADC_EOC =P2^5;
sbit ADC_OUTPUT_ENABLE= P2^6;sbitADC_ADDR_A =P2^0; //地址线低三位选择通道,这里只用两位sbit ADC_ADDR_B =P2^1;
sbit ADC_ADDR_C =P2^2;//定义显示控制引脚(假设使用4位数码管)
#defineSEG_PORTP0//段选#defineDIG_PORTP2//位选,使用高4位//定义报警和控制引脚sbitBUZZER =P3^2;
sbit ALARM_LED =P3^3;
sbit RELAY_HEAT =P3^4;
//定义按键sbitMODE_KEY= P3^5;//切换采集模式sbitCHANNEL_KEY= P3^6;//切换显示通道sbitREAL_TIME_KEY =P3^7; //实时采集按键//定义全局变量unsignedchar adc_value; //存放ADC读取的值unsignedint temperature1; //通道1(热敏电阻)温度,单位0.1℃unsigned inttemperature2; //通道2(热电偶)温度,单位0.1℃unsignedchar display_channel=0;//当前显示通道,0:通道1,1:通道2unsigned charcollect_mode=0;//采集模式,0:定时采集,1:实时采集unsigned inthigh_threshold =800; //高温阈值(80.0℃)unsigned intlow_threshold =50; //低温阈值(5.0℃)
//热敏电阻查表表(示例,需根据实际热敏电阻特性制作)
//表格结构:ADC值(0-255),对应的温度(单位0.1℃)
codeunsigned intthermistor_table[]= {//这里填入实际表格数据,例如:0,4000,// ADC=0时温度400.0℃(实际不可能,仅示例)100,2500,200,1000,255,0// ADC=255时温度0℃};//热电偶查表表(同样需要根据实际热电偶类型制作)
codeunsigned intthermocouple_table[] ={//示例数据0,0,100,500,200,1000,255,1200};//数码管显示数字的段码(共阴)
codeunsigned charseg_code[] ={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f, //0-90x00 //熄灭};//函数声明voidinit_sys();void init_timer();void init_serial();
voidadc_read(unsigned charchannel);unsigned intcalculate_temp(unsignedchar adc_val, unsignedchar sensor_type);void display_temperature(unsignedint temp);void check_alarm();
voidcontrol_heater();
voidserial_send_str(char *s);void serial_send_temp(unsignedinttemp);//主函数voidmain(){init_sys();init_timer();init_serial();EA=1;//开总中断while(1) {//按键处理if(MODE_KEY==0){delay_ms(10);//消抖if (MODE_KEY ==0){collect_mode= !collect_mode;while(!MODE_KEY);//等待按键释放}}if(CHANNEL_KEY ==0){delay_ms(10);if (CHANNEL_KEY==0){display_channel =!display_channel;while(!CHANNEL_KEY);}}if (REAL_TIME_KEY==0){delay_ms(10);if (REAL_TIME_KEY==0){//实时采集按键按下,立即采集两个通道adc_read(0);temperature1 =calculate_temp(adc_value,0);//0表示热敏电阻adc_read(1);temperature2= calculate_temp(adc_value,1); //1表示热电偶while(!REAL_TIME_KEY);}}//显示当前通道温度if(display_channel==0){display_temperature(temperature1);} else{display_temperature(temperature2);}//报警判断check_alarm();//控制执行机构control_heater();//串口处理(在中断中接收,这里可以发送数据等)//例如:定时向上位机发送温度数据(这里省略定时发送逻辑,可在定时器中断中标记发送标志)}}
//初始化系统voidinit_sys(){//初始化IO口ADC_START=0;ADC_OUTPUT_ENABLE=0;BUZZER=0;ALARM_LED =0;RELAY_HEAT=0;}
//初始化定时器voidinit_timer(){//定时器0用于定时采集(模式1,16位定时)TMOD=0x11;//定时器0和1都设置为模式1TH0 =(65536-50000)/256; //50ms定时TL0= (65536-50000)%256;ET0 =1; //允许定时器0中断TR0=1;//启动定时器0//定时器1用于串口波特率(模式2,8位自动重装)TH1=0xFD;//9600波特率(11.0592MHz)TL1=0xFD;TR1 =1;
}//初始化串口void init_serial() {SCON =0x50;//模式1,允许接收ES=1;//允许串口中断}//定时器0中断服务函数(用于定时采集)
voidtimer0_isr() interrupt1{static unsignedchar count=0;TH0 =(65536-50000)/256;TL0 =(65536-50000)%256;count++;if(count==20){ //1秒(20*50ms)count =0;if (collect_mode==0){ //定时采集模式adc_read(0);//读取热敏电阻通道temperature1= calculate_temp(adc_value,0);adc_read(1);//读取热电偶通道temperature2= calculate_temp(adc_value,1);}}
}//串口中断服务函数void serial_isr() interrupt4{if (RI){RI =0;unsigned charcmd =SBUF;//处理命令,例如:设置阈值、请求数据等//这里简单示例:如果收到字符'S',则发送当前温度数据if (cmd =='S') {//发送通道1温度serial_send_temp(temperature1);//发送通道2温度serial_send_temp(temperature2);}}if (TI){TI =0;//发送完成,可清除发送标志}}
//读取指定通道的ADC值voidadc_read(unsignedchar channel) {//设置通道ADC_ADDR_A =channel &0x01;ADC_ADDR_B= (channel>>1)&0x01;ADC_ADDR_C =(channel>>2) &0x01;//启动转换ADC_START =0;delay_us(2);ADC_START =1;delay_us(2);ADC_START =0;//等待转换结束while(ADC_EOC ==0);//读取转换结果ADC_OUTPUT_ENABLE=1;adc_value= P1;//假设ADC数据输出接P1口ADC_OUTPUT_ENABLE=0;}
//计算温度(查表插值法)unsignedint calculate_temp(unsignedchar adc_val, unsignedchar sensor_type){unsigned int*table;unsignedchar table_size;if(sensor_type==0){ //热敏电阻table =thermistor_table;table_size =sizeof(thermistor_table)/ sizeof(thermistor_table[0])/2;}else {//热电偶table =thermocouple_table;table_size= sizeof(thermocouple_table) /sizeof(thermocouple_table[0])/2;}//查找adc_val在表中的位置unsignedchar i;for (i=0;i<table_size-1;i++){if (adc_val >=table[i*2] &&adc_val <=table[(i+1)*2]){break;}}//线性插值unsigned intx0= table[i*2];unsignedint y0 =table[i*2+1];unsigned intx1= table[(i+1)*2];unsigned inty1= table[(i+1)*2+1];//计算温度unsignedint temp= y0 +(y1 -y0) *(adc_val- x0)/ (x1- x0);returntemp;}
//显示温度(单位0.1℃,在数码管上显示为xx.x)void display_temperature(unsigned inttemp){//将温度转换为整数部分和小数部分(整数部分:temp/10,小数部分:temp%10)unsigned intinteger =temp /10;unsigned chardecimal =temp %10;//显示在4位数码管上(假设显示格式:整数部分3位,小数部分1位)//例如:25.5℃显示为“0255”(实际显示时小数点点亮)//这里需要根据硬件连接和显示方式调整//简化:只显示整数部分(实际需要更复杂的处理)unsigned chardigits[4];digits[0]= integer/100;digits[1] =(integer%100)/10;digits[2] =integer %10;digits[3]= decimal;//轮流显示4位数码管(动态扫描)for (unsigned chari=0;i<4;i++){DIG_PORT= ~(1<<(i+4)); //位选(假设数码管位选接P2.4-P2.7)SEG_PORT= seg_code[digits[i]];if(i==2) {//在第三位后显示小数点SEG_PORT |=0x80; //点亮小数点}delay_ms(2); //延时2msSEG_PORT=0x00;//消隐}}
//报警判断void check_alarm() {if(temperature1 >high_threshold ||temperature2> high_threshold) {BUZZER=1;//报警ALARM_LED =1;}else if(temperature1 <low_threshold ||temperature2< low_threshold) {BUZZER=1;ALARM_LED =1;} else{BUZZER =0;ALARM_LED=0;}}
//控制加热器(简单逻辑:低于低温阈值加热,高于高温阈值停止)void control_heater(){if (temperature1< low_threshold|| temperature2 <low_threshold){RELAY_HEAT=1;//开启加热} elseif (temperature1> high_threshold|| temperature2 >high_threshold){RELAY_HEAT=0;//关闭加热}
}//串口发送字符串voidserial_send_str(char *s){while(*s){SBUF= *s++;while(!TI);TI=0;}}
//串口发送温度值(格式:温度值,单位0.1℃)void serial_send_temp(unsignedint temp) {charbuf[10];sprintf(buf,"%u", temp);serial_send_str(buf);}
/*注意:以上代码是一个框架,实际应用中需要根据硬件调整引脚定义、ADC读取方式、显示方式等。同时,查表表需要根据实际传感器特性制作,线性插值部分需要确保表内数据单调。由于热敏电阻通常是非线性的,查表法是一种常用的方法。热电偶也需要进行冷端补偿,本例中未涉及冷端补偿,实际应用中需要添加。此外,数码管显示函数需要根据实际连接的数码管类型(共阴/共阳)修改段码表,以及动态扫描的实现。串口通信部分,我们使用中断方式接收命令,在中断服务函数中处理简单的命令。以上代码仅供参考,可能需要进一步调试和完善。*/
/*§§相关问题§§1.如何根据热敏电阻的阻值-温度特性创建查表表?2.热电偶的冷端补偿在程序中如何实现?
3.如何优化数码管显示以减少闪烁?4.串口通信协议如何设计以实现更复杂的控制命令?
5.如何提高温度采集的精度?
*/</think>### Keil C51 温度测控系统程序设计
#### 系统设计概述
本系统基于STC89C52单片机实现温度测控功能,使用热敏电阻和热电偶作为温度传感器,支持查表插值法计算温度、定时/实时采集、通道显示、阈值报警、执行机构控制及串口通信。系统结构如下:
```mermaid
graph TD
A[温度传感器] --> B[信号调理电路]
B --> C[ADC转换]
C --> D[单片机]
D --> E[显示模块]
D --> F[报警输出]
D --> G[执行机构]
D --> H[串口通信]
```
#### 核心代码实现
```c
#include <reg52.h>
#include <intrins.h>
#include <math.h>
// 硬件定义
#define ADC_PORT P1 // ADC输入端口
#define DISPLAY_PORT P0 // 数码管显示
#define RELAY P3_4 // 继电器控制
#define BUZZER P3_5 // 蜂鸣器
#define LED_ALARM P3_6 // 报警LED
// 全局变量
unsigned int temp_ntc; // 热敏电阻温度值
unsigned int temp_tc; // 热电偶温度值
unsigned char channel = 0; // 当前通道(0:NTC,1:TC)
// 热敏电阻查表数据(示例)
code float ntc_table[] = {
// ADC值, 温度值
0, -40.0,
102, 0.0,
450, 25.0,
790, 50.0,
1023, 100.0
};
// 热电偶查表数据(示例)
code float tc_table[] = {
// mV值, 温度值
0.0, 0.0,
1.5, 25.0,
4.1, 100.0,
8.1, 200.0,
16.3, 400.0
};
// ADC初始化
void ADC_Init() {
P1ASF = 0x03; // 设置P1.0/P1.1为ADC输入
ADC_RES = 0;
ADC_CONTR = 0x80; // 使能ADC, 90T时钟
}
// 读取ADC值(通道0-7)
unsigned int ADC_Read(unsigned char ch) {
ADC_CONTR = 0x80 | (ch << 3); // 选择通道
_nop_(); _nop_(); _nop_(); // 等待稳定
ADC_CONTR |= 0x08; // 启动转换
while (!(ADC_CONTR & 0x10)); // 等待完成
ADC_CONTR &= 0xE7; // 清除标志
return (ADC_RES << 2) | (ADC_RESL & 0x03);
}
// 线性插值计算温度
float interpolate(float x, code float *table, int size) {
for (int i = 0; i < size - 2; i += 2) {
if (x >= table[i] && x <= table[i + 2]) {
float x0 = table[i];
float y0 = table[i + 1];
float x1 = table[i + 2];
float y1 = table[i + 3];
return y0 + (y1 - y0) * (x - x0) / (x1 - x0);
}
}
return 0.0; // 超范围
}
// 热敏电阻温度计算
float calc_ntc_temp(unsigned int adc_val) {
return interpolate(adc_val, ntc_table, sizeof(ntc_table)/sizeof(float));
}
// 热电偶温度计算(含冷端补偿)
float calc_tc_temp(unsigned int adc_val, float cold_junction) {
float mv = (adc_val * 5.0 / 1023.0) * 1000; // 转换为mV
float temp = interpolate(mv, tc_table, sizeof(tc_table)/sizeof(float));
return temp + cold_junction; // 冷端补偿
}
// 串口初始化(9600bps)
void UART_Init() {
SCON = 0x50; // 模式1, 允许接收
TMOD |= 0x20; // T1模式2
TH1 = 0xFD; // 9600bps @11.0592MHz
TR1 = 1;
EA = 1; // 开总中断
ES = 1; // 开串口中断
}
// 串口发送字符串
void UART_SendString(char *s) {
while (*s) {
SBUF = *s++;
while (!TI);
TI = 0;
}
}
// 主函数
void main() {
ADC_Init();
UART_Init();
TMOD = 0x01; // T0模式1
TH0 = 0xFC; // 1ms定时
TL0 = 0x66;
TR0 = 1;
ET0 = 1;
while (1) {
// 温度采集任务
if (channel == 0) {
temp_ntc = calc_ntc_temp(ADC_Read(0));
} else {
float cold_temp = calc_ntc_temp(ADC_Read(1)); // 冷端温度
temp_tc = calc_tc_temp(ADC_Read(0), cold_temp);
}
// 报警检测
if ((channel == 0 && temp_ntc > 80.0) ||
(channel == 1 && temp_tc > 200.0)) {
BUZZER = 1;
LED_ALARM = 0;
} else {
BUZZER = 0;
LED_ALARM = 1;
}
// 执行机构控制
RELAY = (channel == 0) ? (temp_ntc < 25.0) : (temp_tc < 50.0);
}
}
// 定时器中断(1ms)
void Timer0_ISR() interrupt 1 {
static unsigned int count = 0;
TH0 = 0xFC;
TL0 = 0x66;
// 500ms切换通道
if (++count >= 500) {
count = 0;
channel = !channel;
// 串口发送温度数据
char buf[32];
if (channel) {
sprintf(buf, "TC Temp: %.1fC\r\n", temp_tc);
} else {
sprintf(buf, "NTC Temp: %.1fC\r\n", temp_ntc);
}
UART_SendString(buf);
}
}
// 串口中断处理
void UART_ISR() interrupt 4 {
if (RI) {
RI = 0;
char cmd = SBUF;
// 命令处理示例
if (cmd == 'S') { // 切换通道
channel = !channel;
} else if (cmd == 'R') { // 实时采集
// 触发立即采集
}
}
}
```
#### 关键设计说明
1. **双传感器处理**:
- 热敏电阻(NTC):直接ADC采集,查表插值计算温度
- 热电偶(TC):ADC采集后转换为mV值,结合冷端补偿查表计算温度
$$V_{out} = \alpha \cdot (T_{hot} - T_{cold})$$
其中$\alpha$为塞贝克系数
2. **查表插值算法**:
- 使用线性插值提高精度:
$$T = T_0 + \frac{(ADC - ADC_0) \cdot (T_1 - T_0)}{ADC_1 - ADC_0}$$
3. **冷端补偿**:
- 使用独立热敏电阻测量冷端温度
- 热电偶实际温度 = 测量温度 + 冷端温度
4. **多通道管理**:
- 定时切换采集通道(500ms轮换)
- 支持串口命令强制切换
5. **报警与控制**:
- 双阈值报警(蜂鸣器+LED)
- 继电器控制执行机构
- 可扩展PID控制算法:
$$u(t) = K_p e(t) + K_i \int_0^t e(\tau)d\tau + K_d \frac{de(t)}{dt}$$
#### 系统优化建议
1. **软件滤波**:
```c
#define FILTER_LEN 5
float moving_avg(float new_val) {
static float buffer[FILTER_LEN];
static int index = 0;
buffer[index] = new_val;
index = (index + 1) % FILTER_LEN;
float sum = 0;
for(int i=0; i<FILTER_LEN; i++) {
sum += buffer[i];
}
return sum / FILTER_LEN;
}
```
2. **非线性补偿**:
- 热敏电阻使用Steinhart-Hart方程:
$$\frac{1}{T} = A + B \cdot \ln(R) + C \cdot (\ln(R))^3$$
3. **通信协议**:
```plaintext
| 帧头(0xAA) | 命令字 | 数据长度 | 数据域 | 校验和 |
```
#### 设计注意事项
1. 热电偶信号需前置放大电路(如AD620)
2. 热敏电阻需配置恒流源电路
3. 执行机构需光耦隔离驱动
4. PCB布局时模拟/数字地分离
5. 使用看门狗定时器防死机
本设计充分利用了51单片机的资源,通过查表插值法解决了非线性传感器的温度计算问题,结合串口通信实现了远程监控功能,可广泛应用于工业温控场景[^1][^2][^3]。
阅读全文
相关推荐















