#本文档是基于AT89S52单片机.docx
《#本文档是基于AT89S52单片机.docx》由会员分享,可在线阅读,更多相关《#本文档是基于AT89S52单片机.docx(37页珍藏版)》请在冰豆网上搜索。
#本文档是基于AT89S52单片机
本文档是基于AT89S52单片机,通过C语言编程实现了对测温电路的控制,其中包括对加热继电器的控制,对A/D转换的控制以及数码管显示。
一.程序结构说明:
程序流程图如图5.2.1示:
图5.2.1程序流程图
1.1.1软件主要模块划分
表5.2.1表的名称
按键处理模块
消抖模块一个
按键响应分类模块一个
按键响应模块两个<对应系统两个工作状态)
曲线拟合模块包括
拟合准备模块一个
拟合参数计算模块一个
显示和定时模块
中断模块一个<刷新数码管和LED,提供若干定时溢出信号)
显示匹配模块一个<将所需显示的内容翻译为中断快可以使用的信息)
数码管匹配模块一个<将所需显示的字符翻译为数码管控制字)
系统初始化模块
系统初始化模块一个
ad采样模块
利用adc0804对水温进行读数
控制模块
对水温进行实时控制,主要通过控制继电器的关断频率实现
1.1.2重要的全局变量
表5.2.2重要全局变量表
所属模块
变量名
变量类型
功能
定时器
count0_high_H
unsignedchar
定时器0初值的高4位
count0_high_L
unsignedchar
定时器0初值的低4位
count0_low_H
unsignedchar
定时器1初值的高4位
count0_low_L
unsignedchar
定时器1初值的低4位
按键
count_key
unsignedintxdata
消抖基础频率计数器
overload_key
bit
消抖溢出标志
key_down
unsignedcharxdata
按键按下存储器
last_key_down
unsignedcharxdata
按键按下确认存储器
count_key_flash
unsignedintxdata
按键重复计数器
显示
digi_scaner
unsignedcharxdata
数码管扫描驱动指针
output_sel
unsignedcharbdata
数码管位驱动和指示灯驱动信号输出缓存
led_1
sbit
output_sel^5
led_2
sbit
output_sel^6
led_3
sbit
output_sel^7
led_4
sbit
output_sel^4
显示
Vo
unsignedintxdata
输出电压
digi[4]
unsignedcharxdata
数码管输出寄存器
digi_flash_temp
unsignedcharxdata
数码管闪烁用寄存器
doublexdata
pulse_width
当前的占空比
input_for_display
unsignedintxdata
输入显示用寄存器
count_digi_flash
unsignedintxdata
闪烁计数器
input_position
unsignedcharxdata
输入位(即数码管闪烁位>
拟合
coefficient_A
doublexdata
拟合曲线的系数
coefficient_B
doublexdata
coefficient_C
doublexdata
Vo_adjust[4]
longintxdata
输出电压数组
display_for_adjust[4]
intxdata
显示数组
Ave
unsignedcharxdata
ad读数
继电器
ctr_out_foot
unsignedcharxdata
继电器电平信号
sub_set_val
unsignedcharxdata
系统设定温度
sub_cur_val
unsignedcharxdata
当前温度
1.2控制软件各功能模块介绍
1.2.1AD转换模块介绍
当ad_mode==1时,此模块有效,以方便开闭环的转换。
程序利用变量ad_flag作为标记,判断读adc0804的时机,在读D时采用了取平均值的方法。
在进行了一定次数后将采样值与标准值比较。
其中定义采样次数AD_SMPL_NUM=30。
对实际水温0—100分2段拟合:
(1>.0—80曲线拟和:
temp=-0.004*(double>ave*(double>ave+3.856*(double>ave+307.64。
(2>.80—100曲线拟和:
temp=-0.0061*(double>ave*(double>ave+4.011*(double>ave+268.27。
1.2.2模块程序第一部分
图5.3.1AD转换流程图
源程序见附录
1.2.3继电器控制模块介绍
此模块根据加热时水温的上升和过冲程度决定继电器的通断,可以改变继电器的通断频率来解决温度过冲问题,并在达到设定温度后保持恒温。
1.2.4模块程序第二部分
分两段对继电器控制:
0—75度:
继电器1.05s通断一次
75—100度:
继电器0.25s通断一次
每段作如下处理
图5.3.2控制模块原理图
其效果是温度达到指定范围后继电器即处于关断交替状态
算法如下:
if(ctr_out_foot==1>//当继电器接通时
{
if(ave>150>//第一段大于75度
{
if(sub_cur_val+40>=sub_set_val>
{
ctr_out_foot=0。
count_self_ctrl=2000。
//设延迟为0.2s
}
}
else//第二段小于75度
if(sub_cur_val+60>=sub_set_val>
{
ctr_out_foot=0。
count_self_ctrl=10000。
//设延迟为1s
}
}
else//继电器断开
{
if(sub_cur_val<=sub_set_val>
{
ctr_out_foot=1。
count_self_ctrl=500。
//循环延迟50ms
}
}
1.2.5按键解释功能说明
按键是人机互最重要的部分,基本所以的操作都要通过按键来完成,在系统运行中,不断扫描按键,以判断用户是否有任何输入指示,一旦用户有按键操作,系统将做出判断,给予按键解释,执行相应的命令。
但是值得注意的是任何按键都有可能有抖动的现象,因此软件中必须含有消抖程序。
在水温控制中,key3实现自动与手动的切换,在自动情况下,系统接收本地主系统的控制,自动调整水温;当在手动情况下,可以通过key1切换继电器的开和关,这样在调试时比较方便。
对于温度的设置,采用了对位的调整方法,使得温度设定更为方便。
二.程序清单:
#include
#include
#include
/*0~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~全局变量开始~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
/*****************定时器******************/
#definecount0_H0xfc//计数器0计数初值(1.0004ms时基>,(crystal11.059200MHz>
#definecount0_L0x65
#definecount1_H0xff//9600波特率(307200/32>
#definecount1_L0xa3
/*************按键key*************/
#defineKEY_SCAN_TIME500//按键扫描周期
#defineKEY_REPEAT_TIME10//按键重复周期
#defineKEY_TYPE_SEC1
#defineKEY_TYPE_DIS2
#defineKEY_TYPE_CHG3
#defineKEY_TYPE_POS4
#defineKEY_TYPE_ERROR5
sbitKEY1=P1^0。
sbitKEY2=P1^1。
sbitKEY3=P1^2。
sbitKEY4=P1^3。
intcount_key_scan。
//按键扫描计数器
charxdatalast_key_down。
//上次按键
charxdatacount_key_repeat。
//按键重复计数器
charxdatakey_type。
/*************数码管显示************/
#defineADDR_8SEGXBYTE[0x2000]//数码管段驱动寄存器地址
#defineADDR_SELXBYTE[0x4000]//数码管位驱动和指示灯驱动寄存器地址
#defineLED_SCAN_TIME40//数码管扫描周期(1000/4/4=62.5Hz>
#defineLED_FLASH_MAX_TIME100
#defineLED_FLASH_GATE_TIME50
#defineSET_TIME20000。
intcount_led_scan。
//数码管扫描计数器
unsignedcharxdataled_scaner。
//数码管扫描驱动指针
unsignedcharbdataoutput_sel。
//数码管位驱动和指示灯驱动信号输出缓存,定义了一个可位寻址的变量
sbitled_1=output_sel^5。
sbitled_2=output_sel^6。
sbitled_3=output_sel^7。
sbitled_4=output_sel^4。
charxdataled[4]。
//数码管状态寄存器
charled_flash_pos。
//数码管闪烁位
intcount_led_flash。
charled_flash_temp。
intcount_set_time。
chardis_rewrite_flag。
chardis_set_flag。
/***************通信****************/
#defineCOM_FRAME_LENGTH8//6+2
#defineCOM_BUFFER_LENGTH16//8*2
#defineCOM_TYPE_STOP0//通讯停止
#defineCOM_TYPE_IN1//接收
#defineCOM_TYPE_OUT2//发送
#defineCOM_TYPE_MANAGE4//接收后处理
#defineSUB_ADDR2//作为从机的地址
#defineSUB_ADDR_0'0'
#defineSUB_ADDR_1'2'
#defineMASTER_ADDR0//主机地址
#defineLOCAL_VER1//本地协议版本号
#defineBYTE_MISS_TIME400//字节最大延时
#defineBYTE_DELAY_TIME170//字节发送延时
#defineSTD_FRAME_LEN12//标准最短帧长
intcount_byte_miss。
//接收字节计时
intcount_byte_delay。
//发送字节计时
charcom_tra_pin。
//发送用指针
charcom_tra_pos。
//发送数据区标志
charcom_tra_buf[COM_BUFFER_LENGTH+2]。
//发送帧缓冲区
charcom_rec_pos。
//接收数据区标志
charcom_rec_buf[COM_BUFFER_LENGTH]。
//接受帧缓冲区
charcom_type。
//通信机状态
/********************子系统相关*****************/
#defineADDR_0804XBYTE[0x6000]
#defineAD_TIME500//0.05秒采样
#defineDATA_BUF_LEN10//数据缓冲区长度
sbitctr_out_foot=P3^3。
unsignedchardata_buf[DATA_BUF_LEN]。
//数据缓冲区
intcount_self_ctrl=0。
//抽样计数器
intcount_data_manage=0。
//数据处理计数器
intxdatasub_cur_val。
//初值
intxdatasub_set_val。
//设定值
intxdatasub_max_val。
//最大值
intxdatasub_min_val。
//最小值
intxdatasub_max_val_base。
//最大数量级
intxdatasub_dis_temp。
//显示缓冲
intxdatasub_cur_val_base。
//当前输入位数量级
intxdatasub_val_base。
charxdatasub_set_posib。
//可设置标志位
charxdatamaster_lock。
//锁定标志位
/*0^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^全局变量结束^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
/*1~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~硬件接口输出层开始~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
/*1.1*/
/*****7段数码显示译码(D7~0=PGFEDCBA>*****/
unsignedcharNUMTOSEG7(charDATA>
{
unsignedcharAA。
if(DATA<0>
{
DATA=-DATA。
AA=0x00。
}else
{
AA=0x80。
}
switch(DATA>
{
case'0':
AA|=0x40。
break。
//0
case'1':
AA|=0x79。
break。
//1
case'2':
AA|=0x24。
break。
//2
case'3':
AA|=0x30。
break。
//3
case'4':
AA|=0x19。
break。
//4
case'5':
AA|=0x12。
break。
//5
case'6':
AA|=0x02。
break。
//6
case'7':
AA|=0x78。
break。
//7
case'8':
AA|=0x00。
break。
//8
case'9':
AA|=0x10。
break。
//9
case'A':
AA|=0x08。
break。
//A
case'B':
AA|=0x03。
break。
//B
case'C':
AA|=0x46。
break。
//C
case'D':
AA|=0x21。
break。
//D
case'E':
AA|=0x06。
break。
//E
case'F':
AA|=0x0e。
break。
//F
case'G':
AA|=0x42。
break。
//G
case'H':
AA|=0x09。
break。
//H
case'I':
AA|=0x4f。
break。
//I
case'J':
AA|=0x71。
break。
//J
case'K':
AA|=0x0f。
break。
//K
case'L':
AA|=0x47。
break。
//L
case'P':
AA|=0x0c。
break。
//P
case'-':
AA|=0x3f。
break。
//减号
case'_':
AA|=0x77。
break。
//下划线
case'':
AA|=0x7f。
break。
//消隐
case'~':
AA|=0x7e。
break。
//上划线
default:
AA=0xff。
}
return(AA>。
}
/*1.2*/
/*1.2LED刷新*/
voidLEDDisplayHard(>
{
led_scaner=(led_scaner+1>%5。
//移动扫描指针
output_sel=(output_sel&0xf0>|0x01<//选通下一个数码管
//output_sel=0xf0。
/*初值,令数码管驱动位无效,指示灯全灭*/
/*if(KEY1==0>led_1=0。
if(KEY2==0>led_2=0。
if(KEY3==0>led_3=0。
if(KEY4==0>led_4=0。
*/
if(led_flash_pos<=3&&led_flash_pos>=0>//数码管闪烁
{
count_led_flash--。
if(count_led_flash==LED_FLASH_GATE_TIME>//点亮
{
led[led_flash_pos]=led_flash_temp。
}elseif(count_led_flash==0>//熄灭
{
count_led_flash=LED_FLASH_MAX_TIME。
//循环计时
if(led[led_flash_pos]<0>//原来带小数点
led[led_flash_pos]=-''。
//显示小数点
else//原来不带小数点
led[led_flash_pos]=''。
//消隐
}
}
if(com_type==COM_TYPE_IN||com_type==COM_TYPE_OUT>//通信指示灯
led_4=0。
else
led_4=1。
ADDR_8SEG=NUMTOSEG7(led[led_scaner]>。
//输出数码管形状
ADDR_SEL=output_sel。
//输出选通
}
/*1^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^硬件接口输出层结束^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
/*2~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~硬件接口功能层开始~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
/*2.1*/
/*2.2*/
/*按键消抖*/
charkeyDown(void>
{
charkey_down=0。
/*扫描按键*/
if(KEY1==0>key_down=1。
elseif(KEY2==0>key_down=2。
elseif(KEY3==0>key_down=3。
elseif(KEY4==0>key_down=4。
if(last_key_down!
=key_down>//抖动
{
last_key_down=key_down。
count_key_repeat=0。
}else//有效信号
{
count_key_repeat--。
}
if(count_key_repeat<=0>//达到返回点
{
count_key_repeat=KEY_REPEAT_TIME。
returnlast_key_down。
}else
return0。
}
timer0(>interrupt1using0
{
}
/*2.3*/
/*T0时钟中断服务程序*/
/*提供系统时基*/
timer1(>interrupt3using1
{
ET0=0。
//关中断
TH1=count1_H。
//1ms定时
TL1=count1_L。
TR1=1。
if(count_led_scan!
=0>//扫描数码管
count_led_scan--。
if(count_key_scan!
=0>//扫描按键
count_key_scan-=1。
if(count_byte_miss!
=0>//字节丢失
count_byte_miss--。
elseif(com_type==COM_TYPE_IN>
{
com_type=COM_TYPE_STOP。
//通讯停止
}
if(count_byte_delay!
=0>//字节延时
count_byte_delay--。
elseif(com_type==COM_TYPE_OUT>
{
SBUF=com_tra_buf[com_tra_pin]。
count_byte_delay=BYTE_DELAY_TIME。
//延迟5ms发送下一字节
}
if(count_set_time!
=0>//显示设定值
count_set_time--。
if(count_self_ctrl!
=0>//自控
count_self_ctrl--。
ET0=1。
//开中断
}
/*2.4*/
/****串口中断服务程序*****/
/****通信用中断*****/
comHard(>interrupt4using3
{
chartemp。
ES=0。
//关中断
if(RI==1>//接收中断
{
RI=0。
//接收中断复位
temp=SBUF。
if(temp=='~'&&com_type//出现SOI
{
com_rec_pos=0。
//(重新>开始接收
com_type=COM_TYPE_IN。
//转接收
count_byte_miss=BY