设计一个智能电压监视仪;电网电压的信号可用变压器从市电上取得,也可用+5V的电位器模拟。
要求:
1)显示内容:电压当前值;最大电压值;最小电压值;电压合格率;超上限率;超下限率;电压上、下限给定值、当前时间
2)利用小键盘实现电压上、下限给定值的输入和显示选择命令
3)电压采样周期为1秒,平均值的计算周期为2分钟,所求得的电压平均值作为统计和计算的依据,统计和计算以一天为单位,超过24小时则从头开始统计计算
4)超上限率=(其中超上限的次数)/(采样计算得到的总的电压平均值次数)*100%;超下限率的计算公式与此类似
5)合格率=1-超上限率-超下限率
6)显示北京时间,可调整
7)当前电压超上限或下限时,利用蜂鸣器报警
以上就是该电压监视仪的功能,然后我们根据要求分析需要使用板子上的哪些模块。
1)电源模块(不需要编写代码)
2)显示模块(采用LCD1602)
3)键盘模块(采用行列式扫描)
4)蜂鸣器
5)时钟模块(采用DS1307,能掉电后继续计时;若采用定时器,则掉电后重新计时)
6)AD模块(ADuC848有内置AD,没有专门的AD转换芯片)
下面就根据各模块逐步讲解代码:
显示模块:
#include <lcd1602.h>
//延时函数
void lcd_delay(uint t)
{
while(--t);
}
//检测忙信号
void Check_Busy(void)
{
P0 = 0xff; //P0口作为输入
lcd_delay(50);
rs = 0;
rw = 1;
e = 1;
lcd_delay(50);
while(P0&0X80);
e = 0;
rw = 0;
P0 = 0X00;
}
//lcd写指令
void lcd_wc(uchar cmd)
{
Check_Busy();
rs=0;
rw=0;
e=1;
P0=cmd;
lcd_delay(3);
e=0;
lcd_delay(65535);
lcd_delay(65535);
}
//lcd写数据
void lcd_wd(uchar dat)
{
Check_Busy();
rs=1;
rw=0;
e=1;
P0=dat;
lcd_delay(3);
e=0;
lcd_delay(250);
}
//显示位置
void lcd_pos(uchar pos )
{
pos+=0x80;
lcd_wc(pos);
}
//lcd初始化
void lcd_init()
{
uchar code table[]={"Voltage Monitor"};
uint i=0;
lcd_delay(60000);
lcd_delay(60000);
lcd_delay(60000);
lcd_delay(60000);
lcd_wc(0x38);
lcd_delay(60000);
lcd_delay(60000);
lcd_wc(0x38);
lcd_delay(60000);
lcd_delay(60000);
lcd_wc(0x38);
lcd_delay(60000);
lcd_delay(60000);
lcd_wc(0x38); //设置16x2显示,5x7点阵,8位数据接口
lcd_wc(0x0c); //开显示,不显示光标
lcd_wc(0x06); //地址指针加1,且光标加1,整屏显示不移动
lcd_wc(0x01); //清屏
// lcd_wc(0x0f); //显示开关
lcd_pos(0);
while(table[i]!='\0')
{
lcd_wd(table[i]);
i++;
}
}
这里需要注意三个函数:lcd_init()、lcd_wc()、lcd_wd()。
lcd_init()是lcd1602的初始化函数,就是设置几行显示、是否显示光标、文字是否滚动。在主函数刚开始就需要调用,后面就不再需要。
lcd_wc()是写命令函数,可以写清屏等命令,或者要将字符写在显示屏的位置。举例来说,液晶显示屏的第一行初始位置是0x80,第二行初始位置是0xc0,若你想在第一行起始位置输入一个字符,就要调用lcd_wc(0x80);之后的位置逐渐+1既是。
1602液晶模块的读写操作、屏幕和光标的操作都是通过指令编程来实现的。(说明:1为高电平、0为低电平)
指令1:清显示,指令码01H,光标复位到地址00H位置。
指令2:光标复位,光标返回到地址00H。
指令3:光标和显示模式设置 I/D:光标移动方向,高电平右移,低电平左移 S:屏幕上所有文字是否左移或者右移。高电平表示有效,低电平则无效。
指令4:显示开关控制。 D:控制整体显示的开与关,高电平表示开显示,低电平表示关显示 C:控制光标的开与关,高电平表示有光标,低电平表示无光标 B:控制光标是否闪烁,高电平闪烁,低电平不闪烁。
指令5:光标或显示移位 S/C:高电平时移动显示的文字,低电平时移动光标。
指令6:功能设置命令 DL:高电平时为4位总线,低电平时为8位总线 N:低电平时为单行显示,高电平时双行显示 F: 低电平时显示5x7的点阵字符,高电平时显示5x10的点阵字符。
指令7:字符发生器RAM地址设置。
指令8:DDRAM地址设置。
指令9:读忙信号和光标地址 BF:为忙标志位,高电平表示忙,此时模块不能接收命令或者数据,如果为低电平表示不忙。
指令10:写数据。
指令11:读数据。
lcd_wd()是写数据函数,就是你要显示的内容。它是和写命令函数共同使用的。一般一句写命令跟一句写数据,但也有一句写命令跟多句写数据(通过数组实现)。
例:代码红色部分,lcd_pos(0)表示显示屏最初始位置。
----------------------------------------------------------------------------------------------
DS1307模块(显示北京时间)
DS1307模块要用DS1307时钟芯片,想要具体了解可以查看该芯片使用说明书,这里就不详谈了。直接给出所有需要调用的函数。
#include<DS1307.h> //#include<LCD1602.h> //#define uchar unsigned char //#define uint unsigned int void iic_delay_4us() { uchar t=25+5; //适当增加延时,保证信号 while(--t); } void iic_delay_5us() { uchar t=32+5; while(--t); } void iic_init() { I2CM=1; //master模式 MDE=1; MCO=0; //允许SDA改变 MDO=1; //释放SDA } void iic_start() { MDE=1; //允许输出 MCO=0; //时钟拉低,允许数据线改变 MDO=0; //下面在数据线上产生一个脉冲 MCO=1; //制造起始条件 iic_delay_4us();//等待稳定 MDO=1; //上升 iic_delay_5us();//等待稳定 MDO=0; //拉低数据线,产生起始信号 iic_delay_4us();//等待稳定 MCO=0; //时钟拉低,开始 } void iic_stop() { MDE=1; //允许输出 MCO=0; MDO=0; MCO=1; iic_delay_4us(); MDO=1; iic_delay_5us(); // MDO=0; //中止后应该释放数据线 } //1307读入位数据 void iic_write_bit(bit dat) { MDE=1; MCO=0; MDO=dat; iic_delay_4us(); MCO=1; iic_delay_4us(); MCO=0; iic_delay_4us(); } //1307写出,输出1307发送出的位数据 bit iic_read_bit() { bit dat; MDE=1; MCO=0; MDO=1; //释放数据线 MDE=0; iic_delay_4us(); MCO=1; iic_delay_4us(); dat=MDI; MCO=0; iic_delay_4us(); return dat; } //判断1307写出是否有效,即从机是否接受,输出应答信号。该信号为低,则表明有效 bit iic_write_char(uchar dat) { uchar i; bit res; for(i=0;i<8;i++) { iic_write_bit((dat>>(7-i))&0x01); } res=iic_read_bit(); return res; } //读入1307,输出写入的字符数据 uchar iic_read_char(bit nack) { uchar dat=0,i; for(i=0;i<8;i++) { dat<<=1; if(iic_read_bit()==1)dat++; } iic_write_bit(nack); return dat; } //判断写地址是否有效 bit iic_write_address(uchar id,uchar address) { iic_start(); if(iic_write_char(id)!=0)return 1; if(iic_write_char(address)!=0)return 1; return 0; }
----------------------------------------------------------------------------------------------
AD模块
ADuC848中包含一个双通道16位ADC模块,该开发板采用内部参考电压,因此使用ADC模块时只需配置相关寄存器,在AD转换完成时即可读取转换值。
#include <lcd1602.h>
#include <AD.h>
#define v_count 9
#define a_count 3
unsigned char InterruptCounter=0;//定义中断计数变量并赋初值0
unsigned int count=0;//定义秒计数变量并赋初值0
unsigned int count1=0;
unsigned int max,min;//电压最大、最小值
unsigned int CurrentValue;//电压当前值
unsigned int qual_rate,overup_rate,overlow_rate;//电压上、下限值、合格率、超上限率、超下限率
unsigned int upper_limit=20000;
unsigned int lower_limit=0;
bit bz_flag;//设标志位,区分对象是电压值还是百分比,输出“V”还是“%”
unsigned int Fir_bit,Sec_bit,Thi_bit,Fou_bit; //电压位
unsigned int num=0,num1=0; //num为电压采样周期内总计数,num1为平均值计算周期内总计数
unsigned int num_up=0,num_low=0; //num_up为超上限次数,num_low为超下限次数
unsigned int sum=0; //sum为电压采样周期内电压总数和
unsigned int value[v_count]; //取样电压值数组,存放一个平均值计算周期内的每秒电压值
unsigned int average[a_count]; //平均值数组,存放平均值
void Init()
{
ADCMODE = 0X23; //一直转换 /CHOP使能
ADC0CON1 = 0x27; //主ADC选用放大1倍,单端输入,1.28V
ADC0CON2 = 0x0C; //选用内部参考电源,模拟信号从AIN5 AIN6输入
/*
TMOD=0x01;
TH0=(65536-50000)/256;
TL0=(65536-50000)%256;
EA=1;
ET0=1;
TR0=1; */
TMOD=0x01;//配置定时器0为16位模式,时钟是core clock,默认为1.572864MHz
IE=0x82;//使能定时器0中断和全局中断
TH0=0X00;
TL0=0X00;
TR0=1;//开启定时器0
}
void Data_Process(unsigned char ad)
{
unsigned int w;
if(RDY0)
{
CurrentValue=((ADC0H*256)+ADC0M)*0.1953125;
// CurrentValue=1000;
value[num] = ((ADC0H*256)+ADC0M)*0.1953125;
max=value[0];
min=value[0];
//求最大、小值
for(w=1;w<num;w++)
{
if(value[w-1]>value[w])
{
value[w]=value[w-1];
}
else
{
max=value[w];
}
if(value[w-1]<value[w])
{
value[w]=value[w-1];
}
else
{
min=value[w];
}
}
sum+=value[num];//求电压采样周期内电压总和
average[num1]=sum/v_count; //取计算周期内所有采样电压值总和的平均值
//计算上、下限率
overup_rate=(num_up/num1)*100;
overlow_rate=num_low/num1*100;
qual_rate=100*(1-overup_rate/100-overlow_rate/100);
//当超过上下限值时,蜂鸣器鸣响报警
if(CurrentValue>upper_limit||CurrentValue<lower_limit)
{
beep=0;delay_beep(10);
beep=1;delay_beep(10);
}
//选择显示对应AD标号电压值或比率
switch(ad)
{
case 1: AD_display(CurrentValue);
break;
case 2: AD_display(max);
break;
case 3: AD_display(min);
break;
case 4: AD_display(qual_rate);
break;
case 5: AD_display(overup_rate);
break;
case 6: AD_display(overlow_rate);
break;
case 7: AD_display(upper_limit);
break;
case 8: AD_display(lower_limit);
break;
}
RDY0 = 0;
}
}
void delay_beep(unsigned int z)
{
unsigned int x,y;
for(x=z;x>0;x--)
{
for(y=100;y>0;y--);
}
}
//定时器中断
void timer0() interrupt 1
{
unsigned int k;
InterruptCounter++;
if(InterruptCounter>=24*8)
{
InterruptCounter=0;
count++;
num++;
//1秒采样一个电压
if(count>1)
{
count=0;
}
//每一个采样周期获得一个平均值
if(num==v_count)
{
num1++;
num=0;
sum=0;
//一个采样周期后采样电压值清零,重新存储
for(k=0;k<v_count;k++)
{
value[k]=0;
}
}
//判断电压是否超上下限
for(k=0;k<num1;k++)
{
if(average[k]>upper_limit&&num_up<a_count)
{
num_up++;
}
if(average[k]<lower_limit&&num_low<a_count)
{
num_low++;
}
}
//一个计算周期后清零重置
if(num1==a_count)
{
num1=0;
num=0;
num_up=0;
num_low=0;
for(k=0;k<a_count;k++)
{
average[k]=0;
}
}
}
}
//将电压数据或者比率显示在lcd上
void AD_display(unsigned int d)
{
if(RDY0)
{
if(bz_flag==0)
{
Fir_bit = d/10000;
Sec_bit = d/1000%10;
Thi_bit = d/100%10;
Fou_bit = d/10%10;
lcd_pos(lcd_next); //向液晶写指令
lcd_wd(0x30+Fir_bit);
lcd_wd(0x2e); //向液晶写'.'
lcd_wd(0x30+Sec_bit);
lcd_wd(0x30+Thi_bit);
lcd_wd(0x30+Fou_bit);
lcd_wd(0x56); //向液晶写'V'
}
else
{
d*=100;
Fir_bit = d/1000;
Sec_bit = d/100%10;
Thi_bit = d/10%10;
Fou_bit = d%10;
if(Fir_bit>9)
{
Fir_bit=9;
}
lcd_pos(lcd_next); //向液晶写指令
lcd_wd(0x30+Fir_bit);
lcd_wd(0x30+Sec_bit);
lcd_wd(0x2e); //向液晶写'.'
lcd_wd(0x30+Thi_bit);
lcd_wd(0x30+Fou_bit);
lcd_wd(0x25);//向液晶写'%'
}
/*
if(bz_flag==1)
{
d*=100;
Fir_bit = d/1000;
Sec_bit = d/100%10;
Thi_bit = d/10%10;
Fou_bit = d%10;
lcd_pos(lcd_next); //向液晶写指令
lcd_wd(0x30+Fir_bit);
lcd_wd(0x30+Sec_bit);
lcd_wd(0x30+Thi_bit);
lcd_wd(0x2e); //向液晶写'.'
lcd_wd(0x30+Fou_bit);
lcd_wd(0x25);//向液晶写'%'
} */
RDY0=0;
}
}
//加压
void tiaoya_add(unsigned char temp)
{
Fir_bit++;
if(Fir_bit>9)
{
Fir_bit=0;
}
lcd_wc(0xc0);
lcd_wd(0x30+Fir_bit);
lcd_wc(0xc0);
if(temp==1)
{
upper_limit=Fir_bit*10000;
}
if(temp==2)
{
lower_limit=Fir_bit*10000;
}
}
//减压
void tiaoya_sub(unsigned char temp)
{
if(Fir_bit==0)
{
Fir_bit=10;
}
Fir_bit--;
lcd_wc(0xc0);
lcd_wd(0x30+Fir_bit);
lcd_wc(0xc0);
if(temp==1)
{
upper_limit=Fir_bit*10000;
}
if(temp==2)
{
lower_limit=Fir_bit*10000;
}
}
//设定标志位区分电压值或者比率
void setbz_flag(bit nack)
{
bz_flag=nack;
}
红色部分就是修改寄存器的操作。
-----------------------------------------------------------------------------------------------
键盘模块
首先先谈下键盘扫描的原理:
矩阵键盘两端都与单片机I/O口相连,因此在检测时需通过单片机一端的I/O口人为产生低电平。检测时,先使第一列为低电平,其余几列全为高电平,然后检测各行是否有低电平,若检测到第一行为低电平,则可确认第一行第一列的按键被按下。用同样的方法轮流送各列一次低电平,再轮流检测一次各行是否变为低电平,这样即可检测完所有的按键,当有键被按下时便可判断出按下的键是哪一个键,然后根据返回的键值取相应的数码管段码。
键盘流程图如下:
然后我们开始设计下各键盘按钮的作用,在此之前我们再明确下要实现的功能。
具体功能如下:
功能一:显示当前电压值。
功能二:显示最大电压值。
功能三:显示最小电压值。
功能四:显示电压合格率。
电压合格率=1-超上线率-超下限率
功能五:显示电压上限率。
电压上限率=超上限率=(其中超上限的次数)/(采样计算得到的总的电压平均值次数)*100%
功能六:显示电压下限率。
电压下限率=超下限率=(其中超下限的次数)/(采样计算得到的总的电压平均值次数)*100%
功能七:显示电压上限值。
功能八:显示电压下限值。
功能九:显示当前北京时间。
功能十:若当前电压超上限或下限时,蜂鸣器会报警。
然后再分析下操作:
首先给键盘各按钮标号如下:
0 1 2 3
4 5 6 7
8 9 A B
C D E F
各按钮功能:
0:显示当前电压值。
1:显示最大电压值。
2:显示最小电压值。
3:显示电压合格率。
4:显示电压上限率。
5:显示电压下限率。
6:显示电压上限值。
7:显示电压下限值。
8:显示当前北京时间。
9:调整时间,光标闪烁,按一下,光标右移一位;
调整电压值,光标在电压初始位闪烁。
A:光标所在的时间位的数值加1,到了最大限值后则又从最小限值开始。
B:光标所在的时间位的数值减1,到了最小限值后则又从最大限值开始。
C:确认调整时间完毕。
D:调整电压值,光标所在位的数值加1,到了最大限值后则又从最小限值开始。
E:调整电压值,光标所在位的数值减1,到了最小限值后则又从最大限值开始。
F:确认调整电压完毕。
分析完各按钮的功能后,我们就可以开始编写键盘模块的程序了,如下:
#include<AD.h> #include<table.h> #include<lcd1602.h> #include<jianpan.h> #include<DS1307.h> #include<main.h> idata uchar id; //跳出显示时间界面的标志量,id=0,跳出;否则,继续显示。 idata uchar kcount=0; //按键次数量,亦即光标显示位置的标志量 idata uchar kcount1; //当与kcount相等时,说明没按下光标移动键 uchar ad_flag=0; //设置上下限值的标志量,上限值对应1,下限值对应2,其他值对应3 bit tflag=0; //改动时间标志,tflag=1时能改动时间,tflag=0时锁存时间,禁止改动。 uchar ad=0; //AD显示物理量标志号,各AD物理量对应不同标志号,如当前电压值为1,最大值为2,最小值为3...... void delay() //延时函数 { uchar i; for(i = 0;i < 100;i++); } //光标移动 void move() { if(tflag) { if(ad_flag==0) //调节时间时光标移动情况 { //超过范围则光标复位 if(kcount>16) { kcount=1; } //遇到非数字符号调到下一位有效位 if(kcount==3||kcount==6||kcount==11||kcount==14) { kcount++; } lcd_wc(0x0f);//光标显示并闪烁 //光标闪烁位置 if(kcount>8) { lcd_wc(0xC0+kcount-9); //当kcount大于8时,光标从第二行开始闪烁 } else { lcd_wc(0x80+kcount-1); //当kcount小于8时,光标从第一行开始闪烁 } //当不再按键时,光标停留在原位上 kcount1=kcount; delay(); while(kcount==kcount1) { Keyscan(); } } if(ad_flag==1||ad_flag==2) //调节电压上、下限值时光标移动情况 { lcd_wc(0x0f);//光标显示并闪烁 lcd_wc(0xC0); } } tflag=0; } //lcd屏幕显示 void lcd_display(uchar key) { switch(key) { case 1: //当前电压值 Check_Busy(); lcd_wc(lcd_clear); gettable1(); id=0; ad_flag=3; setbz_flag(0); while(ad) { Data_Process(ad); Keyscan(); } break; case 2: //最大电压值 Check_Busy(); lcd_wc(lcd_clear); gettable2(); id=0; ad_flag=3; setbz_flag(0); while(ad) { Data_Process(ad); Keyscan(); } break; case 3: //最小电压值 Check_Busy(); lcd_wc(lcd_clear); gettable3(); id=0; ad_flag=3; setbz_flag(0); while(ad) { Data_Process(ad); Keyscan(); } break; case 4: //电压合格率 Check_Busy(); lcd_wc(lcd_clear); gettable4(); id=0; ad_flag=3; setbz_flag(1); while(ad) { Data_Process(ad); Keyscan(); } break; case 5: //超上限率 Check_Busy(); lcd_wc(lcd_clear); gettable5(); id=0; ad_flag=3; setbz_flag(2); while(ad) { Data_Process(ad); Keyscan(); } break; case 6: //超下限率 Check_Busy(); lcd_wc(lcd_clear); gettable6(); id=0; ad_flag=3; setbz_flag(3); while(ad) { Data_Process(ad); Keyscan(); } break; case 7: //电压上限值 Check_Busy(); lcd_wc(lcd_clear); gettable7(); id=0; ad_flag=1; tflag=1; setbz_flag(0); while(ad) { Data_Process(ad); Keyscan(); } break; case 8: //电压下限值 Check_Busy(); lcd_wc(lcd_clear); gettable8(); id=0; ad_flag=2; tflag=1; setbz_flag(0); while(ad) { Data_Process(ad); Keyscan(); } break; case 9: //显示时间 Check_Busy(); lcd_wc(lcd_clear); id=1; ad_flag=0; tflag=1; display(); break; case 10: //确认 Check_Busy(); id=1; makeSure(); break; } } //键盘扫描 void Keyscan() { unsigned char temp; unsigned int j=0; P1&=0xf0; //P1口低四位作为数字输入时应往相应引脚写0 P2&=0xf0; //置列扫描信号为0 temp=P1&0x0f; if(temp!=0x0f) //P1口低四位不全为1,则有键被按下 { delay(); //延时消抖 if(temp==(P1&0x0f)) //消抖后再判断键值 { P2|=0x07; //P2.3为0 switch(P1&0x0f) { case 0x0e://显示电压当前值 ad=1; lcd_display(1); break; case 0x0d: //显示超上限率 ad=5; lcd_display(5); break; case 0x0b: //当前时间 ad=0; lcd_display(9); break; case 0x07: //确认 ad=0; lcd_display(10); break; case 0x0f:break; default:break; } P2&=0xf0; P2|=0x0b; //P2.2为0 switch(P1&0x0f) { case 0x0e: //显示最大电压值 ad=2; lcd_display(2); break; case 0x0d://显示超下限率 ad=6; lcd_display(6); break; case 0x0b: //光标移动 ad=0; kcount++; move(); break; case 0x07://增加电压值 ad=0; tiaoya_add(ad_flag); break; case 0x0f:break; default:break; } P2&=0xf0; P2|=0x0d; //P2.1为0 switch(P1&0x0f) { case 0x0e://显示最小电压值 ad=3; lcd_display(3); break; case 0x0d: //电压上限给定值 ad=7; lcd_display(7); break; case 0x0b: //加光标所在位置的数值 ad=0; addValue(kcount); break; case 0x07: //减小电压值 ad=0; tiaoya_sub(ad_flag); break; case 0x0f:break; default:break; } P2&=0xf0; P2|=0x0e; //P2.0为0 switch(P1&0x0f) { case 0x0e://显示电压合格率 ad=4; lcd_display(4); break; case 0x0d: //电压下限给定值 ad=8; lcd_display(8); break; case 0x0b: //减光标所在位置的数值 ad=0; subValue(kcount); break; case 0x07: //电压调整完毕 ad=0; lcd_wc(0x0c); break; case 0x0f:break; default:break; } } } } /* uchar getk() { return kcount; } */ //设置按键次数量,主要负责清零重置 void setk(uchar count) { kcount=count; } //获得跳出显示时间界面的标志量 uchar getid() { return id; } //获得设置上下限值的标志量 uchar getad_flag() { return ad_flag; } //获得AD显示物理量标志号 uchar getad() { return ad; }
----------------------------------------------------------------------------------------------
输出各物理量名的模块
#include<AD.h> #include<lcd1602.h> #include<table.h> //当前电压值 void gettable1() { lcd_pos(0); lcd_wd(0x43); lcd_pos(0x01); lcd_wd(0x75); lcd_pos(0x02); lcd_wd(0x72); lcd_pos(0x03); lcd_wd(0x72); lcd_pos(0x04); lcd_wd(0x65); lcd_pos(0x05); lcd_wd(0x6E); lcd_pos(0x06); lcd_wd(0x74); lcd_pos(0x07); lcd_wd(0x20); lcd_pos(0x08); lcd_wd(0x56); lcd_pos(0x09); lcd_wd(0x6F); lcd_pos(0x0A); lcd_wd(0x6C); lcd_pos(0x0B); lcd_wd(0x74); lcd_pos(0x0C); lcd_wd(0x61); lcd_pos(0x0D); lcd_wd(0x67); lcd_pos(0x0E); lcd_wd(0x65); lcd_pos(0x0F); lcd_wd(0x3A); lcd_pos(lcd_next); } //最大电压值 void gettable2() { lcd_pos(0); lcd_wd(0x4D); lcd_pos(0x01); lcd_wd(0x61); lcd_pos(0x02); lcd_wd(0x78); lcd_pos(0x03); lcd_wd(0x20); lcd_pos(0x04); lcd_wd(0x56); lcd_pos(0x05); lcd_wd(0x6F); lcd_pos(0x06); lcd_wd(0x6C); lcd_pos(0x07); lcd_wd(0x74); lcd_pos(0x08); lcd_wd(0x61); lcd_pos(0x09); lcd_wd(0x67); lcd_pos(0x0A); lcd_wd(0x65); lcd_pos(0x0B); lcd_wd(0x3A); lcd_pos(lcd_next); } //最小电压值 void gettable3() { lcd_pos(0); lcd_wd(0x4D); lcd_pos(0x01); lcd_wd(0x69); lcd_pos(0x02); lcd_wd(0x6E); lcd_pos(0x03); lcd_wd(0x20); lcd_pos(0x04); lcd_wd(0x56); lcd_pos(0x05); lcd_wd(0x6F); lcd_pos(0x06); lcd_wd(0x6C); lcd_pos(0x07); lcd_wd(0x74); lcd_pos(0x08); lcd_wd(0x61); lcd_pos(0x09); lcd_wd(0x67); lcd_pos(0x0A); lcd_wd(0x65); lcd_pos(0x0B); lcd_wd(0x3A); lcd_pos(lcd_next); } //电压合格率"Quality Rate" void gettable4() { lcd_pos(0); lcd_wd(0x51); lcd_pos(0x01); lcd_wd(0x75); lcd_pos(0x02); lcd_wd(0x61); lcd_pos(0x03); lcd_wd(0x6C); lcd_pos(0x04); lcd_wd(0x69); lcd_pos(0x05); lcd_wd(0x74); lcd_pos(0x06); lcd_wd(0x79); lcd_pos(0x07); lcd_wd(0x20); lcd_pos(0x08); lcd_wd(0x52); lcd_pos(0x09); lcd_wd(0x61); lcd_pos(0x0A); lcd_wd(0x74); lcd_pos(0x0B); lcd_wd(0x65); lcd_pos(0x0C); lcd_wd(0x3A); lcd_pos(lcd_next); } //超上限率"Upper Limit Rate " void gettable5() { lcd_pos(0); lcd_wd(0x55); lcd_pos(0x01); lcd_wd(0x70); lcd_pos(0x02); lcd_wd(0x70); lcd_pos(0x03); lcd_wd(0x65); lcd_pos(0x04); lcd_wd(0x72); lcd_pos(0x05); lcd_wd(0x20); lcd_pos(0x06); lcd_wd(0x4C); lcd_pos(0x07); lcd_wd(0x69); lcd_pos(0x08); lcd_wd(0x6D); lcd_pos(0x09); lcd_wd(0x69); lcd_pos(0x0A); lcd_wd(0x74); lcd_pos(0x0B); lcd_wd(0x20); lcd_pos(0x0C); lcd_wd(0x52); lcd_pos(0x0D); lcd_wd(0x61); lcd_pos(0x0E); lcd_wd(0x74); lcd_pos(0x0F); lcd_wd(0x65); lcd_pos(lcd_next); } //超下限率"Lower Limit Rate" void gettable6() { lcd_pos(0); lcd_wd(0x4C); lcd_pos(0x01); lcd_wd(0x6F); lcd_pos(0x02); lcd_wd(0x77); lcd_pos(0x03); lcd_wd(0x65); lcd_pos(0x04); lcd_wd(0x72); lcd_pos(0x05); lcd_wd(0x20); lcd_pos(0x06); lcd_wd(0x4C); lcd_pos(0x07); lcd_wd(0x69); lcd_pos(0x08); lcd_wd(0x6D); lcd_pos(0x09); lcd_wd(0x69); lcd_pos(0x0A); lcd_wd(0x74); lcd_pos(0x0B); lcd_wd(0x20); lcd_pos(0x0C); lcd_wd(0x52); lcd_pos(0x0D); lcd_wd(0x61); lcd_pos(0x0E); lcd_wd(0x74); lcd_pos(0x0F); lcd_wd(0x65); lcd_pos(lcd_next); } //"Upper Limit" void gettable7() { lcd_pos(0); lcd_wd(0x55); lcd_pos(0x01); lcd_wd(0x70); lcd_pos(0x02); lcd_wd(0x70); lcd_pos(0x03); lcd_wd(0x65); lcd_pos(0x04); lcd_wd(0x72); lcd_pos(0x05); lcd_wd(0x20); lcd_pos(0x06); lcd_wd(0x4C); lcd_pos(0x07); lcd_wd(0x69); lcd_pos(0x08); lcd_wd(0x6D); lcd_pos(0x09); lcd_wd(0x69); lcd_pos(0x0A); lcd_wd(0x74); lcd_pos(0x0B); lcd_wd(0x3A); lcd_pos(lcd_next); } //超下限值"Lower Limit" void gettable8() { lcd_pos(0); lcd_wd(0x4C); lcd_pos(0x01); lcd_wd(0x6F); lcd_pos(0x02); lcd_wd(0x77); lcd_pos(0x03); lcd_wd(0x65); lcd_pos(0x04); lcd_wd(0x72); lcd_pos(0x05); lcd_wd(0x20); lcd_pos(0x06); lcd_wd(0x4C); lcd_pos(0x07); lcd_wd(0x69); lcd_pos(0x08); lcd_wd(0x6D); lcd_pos(0x09); lcd_wd(0x69); lcd_pos(0x0A); lcd_wd(0x74); lcd_pos(0x0B); lcd_wd(0x3A); lcd_pos(lcd_next); }
------------------------------------------------------------------------------------------------------------------------------------
主函数所在的模块:
//ADuC847寄存器地址 /*#ifndef _ADUC847_H_ #define _ADUC847_H_ #include <aduc848.h> #endif */ //LCD1602驱动 #include <lcd1602.h> #include <AD.h> #include<DS1307.h> #include<jianpan.h> #include<table.h> #define uchar unsigned char #define uint unsigned int bit a,b; uchar dat[7]; uchar dispnum[]="0123456789ABCDEF"; idata uchar day[7][4]={"SUN","MON","TUE","WED","THU","FRI","SAT"}; idata uchar kcount2; uchar dat2[6]; //存储时分秒等的改变值 bit zf_flag; //加减标志位 //改变光标所在位置的数值 void changeValue(uint i) { //设置时间值 if(zf_flag==0) { if(i==0) { dat[6]+=0x10; lcd_wc(0x80); lcd_wd(dispnum[dat[6]>>4]); lcd_wc(0x0f); lcd_wc(0x80); } if(i==1) { dat[6]+=0x01; lcd_wc(0x81); lcd_wd(dispnum[dat[6]&0x0f]); lcd_wc(0x0f); lcd_wc(0x81); } if(i==2) { dat[5]+=0x10; lcd_wc(0x83); lcd_wd(dispnum[dat[5]>>4]); lcd_wc(0x0f); lcd_wc(0x83); } if(i==3) { dat[5]+=0x01; lcd_wc(0x84); lcd_wd(dispnum[dat[5]&0x0f]); lcd_wc(0x0f); lcd_wc(0x84); } if(i==4) { dat[4]+=0x10; lcd_wc(0x86); lcd_wd(dispnum[dat[4]>>4]); lcd_wc(0x0f); lcd_wc(0x86); } if(i==5) { dat[4]+=0x01; lcd_wc(0x87); lcd_wd(dispnum[dat[4]&0x0f]); lcd_wc(0x0f); lcd_wc(0x87); } if(i==6) { dat[2]+=0x10; if(dat[2]>0x20) { dat[2]-=0x20; } lcd_wc(0xc0); lcd_wd(dispnum[dat[2]>>4]); lcd_wc(0x0f); lcd_wc(0xc0); } if(i==7) { dat[2]+=0x01; if(dispnum[dat[2]&0x0f]=='A'||dispnum[dat[2]&0x0f]=='B'||dispnum[dat[2]&0x0f]=='C'||dispnum[dat[2]&0x0f]=='D'||dispnum[dat[2]&0x0f]=='E'||dispnum[dat[2]&0x0f]=='F') { dat[2]-=10; } lcd_wc(0xc1); lcd_wd(dispnum[dat[2]&0x0f]); lcd_wc(0x0f); lcd_wc(0xc1); } if(i==8) { dat[1]+=0x10; if(dat[1]>0x60) { dat[1]-=0x60; } lcd_wc(0xc3); lcd_wd(dispnum[dat[1]>>4]); lcd_wc(0x0f); lcd_wc(0xc3); } if(i==9) { dat[1]+=0x01; if(dispnum[dat[1]&0x0f]=='A'||dispnum[dat[1]&0x0f]=='B'||dispnum[dat[1]&0x0f]=='C'||dispnum[dat[1]&0x0f]=='D'||dispnum[dat[1]&0x0f]=='E'||dispnum[dat[1]&0x0f]=='F') { dat[1]-=10; } lcd_wc(0xc4); lcd_wd(dispnum[dat[1]&0x0f]); lcd_wc(0x0f); lcd_wc(0xc4); } if(i==10) { dat[0]+=0x10; if(dat[0]>0x60) { dat[0]-=0x60; } lcd_wc(0xc6); lcd_wd(dispnum[dat[0]>>4]); lcd_wc(0x0f); lcd_wc(0xc6); } if(i==11) { dat[0]+=0x01; if(dispnum[dat[0]&0x0f]=='A'||dispnum[dat[0]&0x0f]=='B'||dispnum[dat[0]&0x0f]=='C'||dispnum[dat[0]&0x0f]=='D'||dispnum[dat[0]&0x0f]=='E'||dispnum[dat[0]&0x0f]=='F') { dat[0]-=10; } lcd_wc(0xc7); lcd_wd(dispnum[dat[0]&0x0f]); lcd_wc(0x0f); lcd_wc(0xc7); } } else { if(i==0) { dat[6]-=0x10; lcd_wc(0x80); lcd_wd(dispnum[dat[6]>>4]); lcd_wc(0x0f); lcd_wc(0x80); } if(i==1) { dat[6]-=0x01; lcd_wc(0x81); lcd_wd(dispnum[dat[6]&0x0f]); lcd_wc(0x0f); lcd_wc(0x81); } if(i==2) { dat[5]-=0x10; lcd_wc(0x83); lcd_wd(dispnum[dat[5]>>4]); lcd_wc(0x0f); lcd_wc(0x83); } if(i==3) { dat[5]-=0x01; lcd_wc(0x84); lcd_wd(dispnum[dat[5]&0x0f]); lcd_wc(0x0f); lcd_wc(0x84); } if(i==4) { dat[4]-=0x10; lcd_wc(0x86); lcd_wd(dispnum[dat[4]>>4]); lcd_wc(0x0f); lcd_wc(0x86); } if(i==5) { dat[4]-=0x01; lcd_wc(0x87); lcd_wd(dispnum[dat[4]&0x0f]); lcd_wc(0x0f); lcd_wc(0x87); } if(i==6) { if(dat[2]>>4==0) { dat[2]+=0x20; } dat[2]-=0x10; lcd_wc(0xc0); lcd_wd(dispnum[dat[2]>>4]); lcd_wc(0x0f); lcd_wc(0xc0); } if(i==7) { if(dispnum[dat[2]&0x0f]=='A'||dispnum[dat[2]&0x0f]=='B'||dispnum[dat[2]&0x0f]=='C'||dispnum[dat[2]&0x0f]=='D'||dispnum[dat[2]&0x0f]=='E'||dispnum[dat[2]&0x0f]=='F') { dat[2]-=0x05; } if(dispnum[dat[2]&0x0f]=='0') { dat[2]+=0x0a; } dat[2]-=0x01; if(dispnum[dat[2]&0x0f]=='F') { dat[2]-=0x06; } lcd_wc(0xc1); lcd_wd(dispnum[dat[2]&0x0f]); lcd_wc(0x0f); lcd_wc(0xc1); } if(i==8) { if(dat[1]>>4==0) { dat[1]+=0x60; } dat[1]-=0x10; lcd_wc(0xc3); lcd_wd(dispnum[dat[1]>>4]); lcd_wc(0x0f); lcd_wc(0xc3); } if(i==9) { if(dispnum[dat[1]&0x0f]=='A'||dispnum[dat[1]&0x0f]=='B'||dispnum[dat[1]&0x0f]=='C'||dispnum[dat[1]&0x0f]=='D'||dispnum[dat[1]&0x0f]=='E'||dispnum[dat[1]&0x0f]=='F') { dat[1]-=0x05; } if(dispnum[dat[1]&0x0f]=='0') { dat[1]+=0x0a; } dat[1]-=0x01; if(dispnum[dat[1]&0x0f]=='F') { dat[1]-=0x06; } lcd_wc(0xc4); lcd_wd(dispnum[dat[1]&0x0f]); lcd_wc(0x0f); lcd_wc(0xc4); } if(i==10) { if(dat[0]>>4==0) { dat[0]+=0x60; } dat[0]-=0x10; lcd_wc(0xc6); lcd_wd(dispnum[dat[0]>>4]); lcd_wc(0x0f); lcd_wc(0xc6); } if(i==11) { if(dispnum[dat[0]&0x0f]=='A'||dispnum[dat[0]&0x0f]=='B'||dispnum[dat[0]&0x0f]=='C'||dispnum[dat[0]&0x0f]=='D'||dispnum[dat[0]&0x0f]=='E'||dispnum[dat[0]&0x0f]=='F') { dat[0]-=0x05; } if(dispnum[dat[0]&0x0f]=='0') { dat[0]+=0x0a; } dat[0]-=0x01; if(dispnum[dat[0]&0x0f]=='F') { dat[0]-=0x06; } lcd_wc(0xc7); lcd_wd(dispnum[dat[0]&0x0f]); lcd_wc(0x0f); lcd_wc(0xc7); } } } //加值 void addValue(uchar k1) { zf_flag=0; switch(k1) { case 1: /* addv[0]++; lcd_wc(0x80); dat1[0]=(dispnum[dat[6]>>4])+addv[0]; lcd_wd(dat1[0]); lcd_wc(0x0f); lcd_wc(0x80); */ changeValue(0); break; case 2: changeValue(1); break; case 4: changeValue(2); break; case 5: changeValue(3); break; case 7: changeValue(4); break; case 8: changeValue(5); break; case 9: changeValue(6); break; case 10: changeValue(7); break; case 12: changeValue(8); break; case 13: changeValue(9); break; case 15: changeValue(10); break; case 16: changeValue(11); break; } } //减值 void subValue(uchar k1) { zf_flag=1; switch(k1) { case 1: changeValue(0); break; case 2: changeValue(1); break; case 4: changeValue(2); break; case 5: changeValue(3); break; case 7: changeValue(4); break; case 8: changeValue(5); break; case 9: changeValue(6); break; case 10: changeValue(7); break; case 12: changeValue(8); break; case 13: changeValue(9); break; case 15: changeValue(10); break; case 16: changeValue(11); break; } } uchar Decimal_to_BCD(uchar temp)//十进制转换成BCD码 { uchar a,b,c; a=temp; b=0; if(a>=10) { while(a>=10) { a=a-10; b=b+16; c=a+b; temp=c; } } return temp; } //DS1307传输准备,时间初始化 void display_ready() { iic_init(); a=iic_write_address(0xd0,0x00); b=iic_write_char(0x30); b=iic_write_char(0x26); b=iic_write_char(0x16); b=iic_write_char(0x01); //DS1307只能记录20个小时,每20小时重置。即时最大为19!! b=iic_write_char(0x01); b=iic_write_char(0x06); b=iic_write_char(0x15); iic_stop(); } //时间显示 void display() { while(1&getid()) { if(iic_write_address(0xd0,0x00)==0) { iic_start(); if(iic_write_char(0xd1)==0) { dat[0]=iic_read_char(0); dat[1]=iic_read_char(0); dat[2]=iic_read_char(0); dat[3]=iic_read_char(0); dat[4]=iic_read_char(0); dat[5]=iic_read_char(0); dat[6]=iic_read_char(1); } iic_stop(); lcd_pos(0); lcd_wd(dispnum[dat[6]>>4]); lcd_wd(dispnum[dat[6]&0x0f]); lcd_wd('-'); lcd_wd(dispnum[dat[5]>>4]); lcd_wd(dispnum[dat[5]&0x0f]); lcd_wd('-'); lcd_wd(dispnum[dat[4]>>4]); lcd_wd(dispnum[dat[4]&0x0f]); lcd_wd(' '); lcd_wd(day[dat[3]][0]); lcd_wd(day[dat[3]][1]); lcd_wd(day[dat[3]][2]); lcd_pos(lcd_next); lcd_wd(dispnum[(dat[2]>>4)&0x01]); lcd_wd(dispnum[dat[2]&0x0f]); lcd_wd(':'); lcd_wd(dispnum[dat[1]>>4]); lcd_wd(dispnum[dat[1]&0x0f]); lcd_wd(':'); lcd_wd(dispnum[(dat[0]>>4)&0x07]); lcd_wd(dispnum[dat[0]&0x0f]); /* //记录按键按下次数并显示 lcd_pos(lcd_next+0x0C); if(getk()>9&&getk()<13) { lcd_wd(0x30+1); lcd_pos(lcd_next+0x0D); lcd_wd(0x30+getk()-10); } else if(getk()>12) { lcd_wd(0x30+0); lcd_pos(lcd_next+0x0D); lcd_wd(0x30+0); } else { lcd_wd(0x30+0); lcd_pos(lcd_next+0x0D); lcd_wd(0x30+getk()); } */ } iic_delay_5us(); Keyscan(); //继续扫描,使能跳出时间显示 } } void makeSure() { //按第一位复位;按第二位除它其他复位; uchar i; setk(0); //让kcount复零,光标复位 lcd_wc(0x0c); lcd_wc(0x02); dat2[0]=Decimal_to_BCD((dispnum[dat[6]>>4]-0x30)*10+(dispnum[dat[6]&0x0f]-0x30)); dat2[1]=Decimal_to_BCD((dispnum[dat[5]>>4]-0x30)*10+(dispnum[dat[5]&0x0f]-0x30)); dat2[2]=Decimal_to_BCD((dispnum[dat[4]>>4]-0x30)*10+(dispnum[dat[4]&0x0f]-0x30)); dat2[3]=Decimal_to_BCD((dispnum[dat[2]>>4]-0x30)*10+(dispnum[dat[2]&0x0f]-0x30)); dat2[4]=Decimal_to_BCD((dispnum[dat[1]>>4]-0x30)*10+(dispnum[dat[1]&0x0f]-0x30)); dat2[5]=Decimal_to_BCD((dispnum[dat[0]>>4]-0x30)*10+(dispnum[dat[0]&0x0f]-0x30)); iic_init(); a=iic_write_address(0xd0,0x00); //0xd0为器件写地址 b=iic_write_char(dat2[5]); b=iic_write_char(dat2[4]); b=iic_write_char(dat2[3]); b=iic_write_char(0x00); b=iic_write_char(dat2[2]); b=iic_write_char(dat2[1]); b=iic_write_char(dat2[0]); display(); } void main() { PLLCON&=0xf8; //设置频率为12.582912MHz lcd_init(); // iic_init(); display_ready(); Init(); // display(); while(1) { Keyscan(); } }
这里说下如何调整显示时间。
//DS1307传输准备,时间初始化 void display_ready() { iic_init(); a=iic_write_address(0xd0,0x00); b=iic_write_char(0x30); b=iic_write_char(0x26); b=iic_write_char(0x16); b=iic_write_char(0x01); b=iic_write_char(0x01); b=iic_write_char(0x06); b=iic_write_char(0x15); iic_stop(); }
30对应的是秒,26对应的是分,16对应的是时,01对应的是星期即星期一,之后分别代表日月年。
如果要显示15年6月5日星期四0时42分00秒,可修改为:
//DS1307传输准备,时间初始化 void display_ready() { iic_init(); a=iic_write_address(0xd0,0x00); b=iic_write_char(0x00); b=iic_write_char(0x42); b=iic_write_char(0x00); b=iic_write_char(0x04); b=iic_write_char(0x05); b=iic_write_char(0x06); b=iic_write_char(0x15); iic_stop(); }
若再想实现掉电后能继续计时,则下次将程序写入板子前记得把这个函数注释掉,不需要再定义了。
这样效果就会如:掉电前是0时45分00秒,关开关,过10秒后再开开关,时间显示为0时45分10秒!!
综合以上,就能实现出一个智能电压测试仪了。下面再给出调整时间或电压上下限值的操作。
操作指南
操作一:调整北京时间
先按下按钮8,然后按下按钮9,通过按动9使光标移动到想要调整的位置,再通过按钮A或B改动所在位的时间值,最后再按下按钮C则调整完毕。
操作二:调整电压上、下限值
以调整电压上限值为例,按下按钮6,然后按下按钮9,通过按钮D或E改动电压上限值,最后按下按钮F确定。