一、要求与技术指标
1.功能 1:数字电压表(0808或stc A/D)。
2.功能 2:数字时钟,显示时、分、秒,可通过按键设定时间。
3.将功能 1、功能 2 合并。
4.扩展功能。(发送“cx”字符串到单片机,单片机将会返回电压值和时间)
解:
代码和使用方法等相关文件:https://download.csdn.net/download/chenger_32123/10577316
实现了LCD1602里光标的移动、闪烁的同时电压值可以同步显示。
1.原理图
2.keil工程
3.部分代码
/*********************main.c**********************/
/*******************************************
//版本:2017/12/4 Chenger
//芯片:STC89C52
//晶振:12MHz 12T模式:12个时钟周期做一个机器周期
//功能:lcd1602显示adc0808采集到的电压值,同时显示时钟,时钟可通过按键设置。
//串口波特率:[email protected]
*******************************************/
#include<reg52.h>
#include"main.h"
#include"lcd1602.h"
#include"adc0808.h"
#include"read_key.h"
/**************************************
//主函数
***************************************/
void main()
{
uchar value[COUNT];
uchar count=0;
uint voltage;
uchar display_flag=0; //lcd1602显示标志
initialize(); //初始化
LCD_StringOut(0x80,"Time:");//lcd1602第一行显示
LCD_StringOut(0x80+0x40,"Voltage:"); //lcd1602第二行显示
while(1)
{
value[count]=digital_out(); //获取adc808数字输出量并保存在数组中
count++;
if(COUNT==count) //若获取够了COUNT个数则求均值
{
voltage=filter(value,count)*1.0/255*VOLTAGE_REFER;
count=0;
}
if(key_flag) //约每20mS扫描一次键盘
{
key_process(read_key()); //读键盘值并处理
key_flag=0;
display_flag++;
}
if(display_flag>=10) //约每200mS更新一次LCD1602显示
{
lcd_display(voltage); //LCD1602显示电压值
display_flag=0; //显示标志变量清零
}
if(inquire_flag) //发送时间到PC
{
serial_send();
inquire_flag=0; //查询标志清零
}
}
}
/**************************************
//初始化函数
***************************************/
void initialize()
{
key_set=1; //定义连接按键的IO口为读取模式
key_cursor=1;
key_add=1;
LCD_Initialize(); //lcd1602初始化
adc0808_initialize();//ADC0808初始化
serial_initialize(); //串口初始化
ET0=1; //开启定时器0中断
ES=1; //开串口中断
EA=1; //开总中断
TR0=1; //启动定时器0
}
/**************************************
//串口初始化函数
***************************************/
void serial_initialize() //[email protected]
{
PCON &= 0x7F; //波特率不倍速
SCON = 0x50; //8位数据,可变波特率
// AUXR &= 0xBF; //定时器1时钟为Fosc/12,即12T
// AUXR &= 0xFE; //串口1选择定时器1为波特率发生器
TMOD &= 0x0F; //清除定时器1模式位
TMOD |= 0x20; //设定定时器1为8位自动重装方式
TL1 = 0xF3; //设定定时初值
TH1 = 0xF3; //设定定时器重装值
ET1 = 0; //禁止定时器1中断
TR1 = 1; //启动定时器1
}
/**************************************
//定时器0中断函数
***************************************/
void timer0_interrtupt() interrupt 1
{
static uchar i=0;
i++;
if(200==i) //约每20mS进入一次
{
i=0;
key_flag=1; //扫描键盘标志变量
}
if(count_flag)
{
counter++;
if(10000==counter)
{
counter=0;
second++;
if(60==second)
{
second=0;
minute++;
if(60==minute)
{
minute=0;
hour++;
if(24==hour)
{
hour=0;
}
}
}
}
}
clock=!clock; //ADC0808时钟
}
/**************************************
//串口中断函数
***************************************/
void serial_interrupt() interrupt 4
{
static bit count=0; //定义静态局部变量
static uchar temp_str[3];
if(RI) //接收
{
RI=0; //接收中断标志位清零
temp_str[count]=SBUF;
if('c'==temp_str[count] && 0==count)
{ /*含有"cx"字符即为查询*/
count=1;
}
else if(1==count && 'x'==temp_str[count])
{
count=0;
inquire_flag=1; //表示收到"cx"字符,标志位置1
}
else count=0;
}
if(TI) //发送
{
TI=0; //发送中断标志位清零
send++; //发送下一个字符
if(*send != '\0')
{
SBUF=*send; //未遇到"\0"结束字符,则继续发送
}
}
}
/**************************************
//lcd1602显示函数
***************************************/
void lcd_display(uint _voltage)
{
display2[0]=_voltage/1000 + '0';
display2[2]=_voltage%1000/100 + '0';
display2[3]=_voltage%1000%100/10 + '0';
display2[4]=_voltage%1000%100%10 + '0';
LCD_StringOut(0x80+0x49,display2); //显示电压值
if(cursor_blink)
{
display1[0]=hour/10 +'0'; //时十位
display1[1]=hour%10 +'0'; //时个位
display1[3]=minute/10+'0'; //分十位
display1[4]=minute%10+'0'; //分个位
display1[6]=second/10+'0'; //秒十位
display1[7]=second%10+'0'; //秒个位
LCD_StringOut(0x80+0x06,display1); //lcd1602显示时间
}
else
{
switch(cursor) //判断光标位置
{
case 0:
display1[0]='_';
display1[1]='_';
break;
case 1:
display1[3]='_';
break;
case 2:
display1[4]='_';
break;
case 3:
display1[6]='_';
break;
case 4:
display1[7]='_';
break;
}
LCD_StringOut(0x80+0x06,display1);//光标闪烁
}
if(set_flag)
{
if(0 == cursor_blink)
{
cursor_blink=8; //约每1.6秒闪烁一次光标
}
else
{
cursor_blink--;
}
}
else
{
cursor_blink=1;
}
}
/**************************************
//滤波函数
//功能:接收一维数组,length是数组长度。中位值平均滤波法(防脉冲干扰平均滤波法),
//采样length个数据,去掉一个最大值和最小值,然后计算length-2个数据的算术平均值
//input:数组首地址,数组长度。
//output:去除最小值和最大值后的算术平均值
//注意:由于定义为无符号,所以只能计算非负数。且length>=3。
***************************************/
uchar filter(uchar *_value,uchar length)
{
uchar temp;
uchar i,j;
uint sum=0;
for(j=1;j<length;j++)
{ //冒泡法。从小到大排列
for(i=0;i<length-j;i++)
{
if(*(_value+i) > *(_value+i+1))
{
temp=*(_value+i);
*(_value+i)=*(_value+i+1);
*(_value+i+1)=temp;
}
}
}
for(i=1;i<length-1;i++)
{ //去掉最大和最小值求和
sum=*(_value+i)+sum;//求和
}
return (uchar)((sum+(length-2)/2)/(length-2));
}
/********************************************************/
/*键值处理函数*/
/********************************************************/
void key_process(uchar key_temp)
{
switch(key_temp)
{
case 0: //count+键,数值加
if(set_flag)
{
time_setting(); //设置时间
counter=0;
}
else //功能2,清零
{
hour=0;
minute=0;
second=0;
}
break;
case 1: //cursor键,光标移动
if(set_flag) //功能1
{
/* if(4==cursor) //光标循环
{
cursor=0;
}
else
{
cursor++; //光标移动
} */ cursor++;cursor%=5;
cursor_blink=0; //lcd1602显示光标闪烁
}
else //功能2,启动或暂停
{
/* if(count_flag)
{
count_flag=0; //暂停
}
else
{
count_flag=1;//启动计时
} */ count_flag=~count_flag;
}
break;
case 2: //set键,时间设置
if(set_flag)
{
set_flag=0; //取消时间设置状态
}
else //set_flag==0,即不在时间设置状态
{
count_flag=0;//停止计时
set_flag=1; //进入时间设置状态
cursor=0; //默认光标位置在时的十位
cursor_blink=0; //lcd1602显示光标闪烁
}
break;
}
}
/********************************************************/
/*时间设置函数*/
/********************************************************/
void time_setting()
{
switch(cursor)
{
case 0: //光标在时位
/* if(23 == hour)
{ //hour++;hour%=24;
hour=0;
}
else
{
hour++;
} */
hour++;hour%=24;
break;
case 1: //光标在分的十位
/* if(5 == minute/10) //minute+=10;minute%=60;
{
minute-=50;
}
else
{
minute+=10;
} */ minute+=10;minute%=60;
break;
case 2: //光标在分的个位
if(9 == minute%10)
{
minute-=9;
}
else
{
minute++;
}
break;
case 3: //光标在秒的十位
/* if(5 == second/10) //second+=10;second%=60;
{
second-=50;
}
else
{
second+=10;
} */ second+=10;second%=60;
break;
case 4: //光标在秒的个位
if(9 == second%10)
{
second-=9;
}
else
{
second++;
}
break;
}
}
/**************************************
//串口发送
***************************************/
void serial_send()
{
static uchar send_str[26];
uchar i=0,j=0;
if(set_flag) //如果正在设置时间
{
while(busy_str[i] != '\0')
{
send_str[i]=busy_str[i];
i++;
}
send_str[i]='\r';
i++;
send_str[i]='\n';
i++; //"\r\n"是回车符
while(display2[j] != '\0')
{
send_str[i+j]=display2[j];
j++;
}
send_str[i+j]='\r';
send_str[i+j+1]='\n'; //"\r\n"是回车符
send_str[i+j+2]='\0';
send = send_str;
SBUF=*send;
}
else
{
while(display1[i] != '\0')
{
send_str[i]=display1[i];
i++;
}
send_str[i]='\t';
i++;
while(display2[j] != '\0')
{
send_str[i+j]=display2[j];
j++;
}
send_str[i+j]='\r';
send_str[i+j+1]='\n'; //"\r\n"是回车符
send_str[i+j+2]='\0'; //结束符
send=send_str;
SBUF=*send;
}
}