数字时钟数字电压表

一、要求与技术指标 
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;
    }
}

猜你喜欢

转载自blog.csdn.net/Chenger_32123/article/details/81331935
今日推荐