单片机课程设计
自适应屏幕亮度调节
班级:自动化
摘要:
本设计是基于单片机的的液晶屏幕亮度自动调节系统,如今市场上大多数电子设备的显示屏都是液晶的,虽然相比于传统电子设备的屏幕,它具有功耗小,辐射小(几乎没有辐射)等优点,但是由于它的亮度过高,反而更容易是我们的眼睛变得疲劳,造成视力下降,甚至产
生头痛等症状。基于此原因,我设计出了一个比较简易的液晶屏幕亮度自动调节装置,它通过根据所处环境中的光的亮度,来自动调节屏幕的背光的亮度,而不是使屏幕一直保持在同一个亮度,通过自动调节,在一定程度上可以使得它的功耗进一步减少,此外最重要的是通过自适应亮度调节,可以使我们(观察者)在浏览屏幕内容的时候,眼睛更加舒服,减少了亮度不适带来的眼睛疲劳,保护了我们的视力。
关键词:单片机技术、自动调节
一、 实验设计目的
学习并掌握单片机I/O口的基本输入输出功能;
学习信号处理中比较常用的A/D和D/A的过程处理; 学习单片机内部定时器的操作;
学习并掌握液晶屏1602的显示操作; 学习并掌握光敏电阻的使用方法;
二、 设计任务及要求
以单片机为核心处理部件,根据液晶屏设备所处的环境来自动调节屏幕背光亮度;
除了自动调节功能外,出于特殊情况,自动调节后的屏幕亮度可能不能满足我们的要求,这时就需要手动调节屏幕亮度,所以设计中还需要一个开关来负责切换自动调节功能和手动调节功能,在自动调节功能中,有两个按钮负责控制亮度增大或亮度减小。
三、 硬件设计
1. 光敏部分和A/D处理
此部分是根据当前所处环境的光的强度,转化成一个8位的数字量,其中这个数字量是自己定义的,然后送到C52芯片中进行处理。 原理框图如下:
光敏部分提取电压
电路原理图(用proteus仿真):
ADC0809转化电压为数字量74LS373锁存器C52处理
1)光强的处理部分,出于经济方面的原因,我采用的最原始的光敏电阻,根据光敏电阻的物理特性:随着外界光照强度的变大,光敏电阻的阻值减小。如上图所示,让它和一个电阻串联,根据分压关系,通过采集定值电阻两端的电压,来大致判断光线强度的变化。我采用的是光敏电阻5516,它的特性参数如下:
2)A/D模块这里采用的是ADC0809,下面是它的特性参数:
ADC0809的转化时间极快,需要极高频率的时钟脉冲,在这里我是
通过C52的定时器中断来产生一个500KHz的时钟脉冲,然后通过P1.0口输出给ADC0809的cp接口,此外ADC0809的START接口和EOC接口直接相连,这样就可以满足当一个信号转化完后,就会自动进行下一个模拟信号的转化。
3)输出部分通过74LS373锁存器实现,功能表如下:
电路中让OE接地,让LE接ADC0809的EOC接口,根据ADC0809资料可知,每次转化一个模拟信号,EOC会输出一个高电平,而在转化过程中保持低电平,根据此特性,正好可以触发74LS373,使它的工作方式发生变化,由保持工作方式转化为直通工作方式,正好达到了数据输出的目的。
2. C52单片机进行内部信息处理
此部分的作用是根据前面部分输出的数字信号,进行分析,来确定输出8位数字输出量(自定义的),用于后面电压大小的控制。 电路原理图如下(proteus仿真):
如图所示,P2口作为数据输入的端口,P3口作为数据输出的端口,
此部分关键是对数字量的处理,如何根据光强来确定屏幕光的强度,这里我的处理比较粗糙,我是将屏幕的亮度分成了1~5共5个等级,亮度逐级递增,据此将外界的光强划分成了5个阶段,然后根据光强所处的阶段,来确定屏幕的亮度,大体思想是外界的光线强度越大,屏幕的亮度越暗。因为没有科学的数据,只能通过自己的大致测量来确定,这里是比较粗糙的,如果有足够的时间来统计相关信息,并总结出较为科学的数据,这里可以将程序改的更加精确一些。
3. D/A部分数据处理
此部分用于将C52芯片输出的数字量转化为电压,来用于控制LCD1602的背光亮度。 结构框图如下:
C52输出数字量DAC0832进行DA转化电流LM324将电流转化为电压LCD1602电路原理图如下:(proteus仿真)
给DAC0832的参考电压输入5v,然后将8位2进制数据输入进行D/A转化,通过Iout1和Iout2输出,这里是电流量,我们需要把它转化为电压来处理,这里结合使用了运算放大器,但使用一个运算放大器输出电压的范围是-5v~0v,所以这里在电路的后面又接了一个反向运算放大器,通过控制电阻的阻值,调整放大系数为-1,这样就能保证最后的输出电压量为正值,然后输出到LCD1602的控制端口,控制屏幕亮度。
4. 显示电路部分
这里让C52的P0口作为显示部分的数据输出端口 电路原理图如下:(proteus仿真)
C52的P0口不同于P1~P3,它内部本身没有加上拉电阻,常态是处于高阻态,若想让它作为输入输出使用,必须自己给它外接上拉电阻,否则它的状态是不稳定的,这里接的是10K的上拉电阻,然后将LCD1602的其他管教根据要求连接好,其中15和16管脚表示背光控制的正极和负极,要想控制它的背光亮度,这两个管脚需要我们输入电压来控制,这里将16脚接地,然后把电压输入到15脚,达到控制的目的。
5. 整体硬件电路图
四、 软件设计
1. 产生500khz的时钟脉冲,提供给ADC0809
利用单片机定时器0,设置它的工作方式为1:16位定时器/计数器。ADC0809正
常工作需要的频率较高,需要大概100us左右就给一个脉冲,频率为500KHz左右。 当定时器的工作方式为1时,设机器周期为T,定时器产生一次中断的时间为t,那么需要计数的个数N=t/T,装入TH0和TL0中的数分别为:
TH0=(65536-N)/256,TL0=(65536-N)%256;据此求得N≈2,所以程序如下: TMOD=0x01;
TH0=(65536-2)/256; TL0=(65536-2)%256;
2. 数据分析
我将前端电路获取的8位数字量大致分成了5个阶段,分别为0~50,51~100,101~150,151~200,201~255;然后让它们分别对应的屏幕亮度为5,4,3,2,1;大体思想就是外界环境中光线强度越大,液晶屏的亮度越小;这种思路是比较粗糙的,
如果有足够的时间来进行相关信息的统计,并据此来获得比较科学的数据,程序可以进一步来改进。 代码如下:
if(P2>200&&P2<=255) { level=1; }
else if(P2>150&&P2<=200) { level=2; }
else if(P2>100&&P2<=150) { level=3; }
else if(P2>50&&P2<=100) { level=4; }
else if(P2>=0&&P2<=50) { level=5; }
3. 1602液晶显示以及显示汉字
LCD1602是字符型液晶显示屏,共有16个管脚,其中8个用于数据的传输,其余的管脚1、2、3分别表示VCC、GND和对比度调节端口,4、5、6端口用于设置显示器的工作方式,15、16分别表示屏幕背光强度的正极和负极。 控制液晶显示的主要步骤:
设置显示模式:0x38:16*2显示,5*7点阵,8位数据接口 设置数据指针:80H+地址码
控制显示开/关,并设置光标:0x06:打开显示,当读写一个字符后,地址指针加1 清屏设置:0x01
前面的设置工作做好了,然后就可以显示数据了。
要显示的数据存在了DDRAM,而我们还可以自定义一些字符来显示,LCD1602中的CGRAM有字节的空间存储自定义的字符,LCD1602中每个字符的像素为5*8,在自定义的字符中,每个字符占用8个字节(8个8位2进制数表示),所以最多可以存储8个自定义字符。
因为在程序中,我想用中文显示年月日,所以我在程序中自定义了几个字符,如下 uchar code table[]={
0x04,0x0E,0x1B,0x15,0x1B,0x0E,0x04,0x00,//背光 0x04,0x0F,0x12,0x0F,0x0A,0x1F,0x02,0x02,//年 0x0F,0x09,0x0F,0x09,0x0F,0x09,0x09,0x13,//月 0x1F,0x11,0x11,0x1F,0x11,0x11,0x1F,0x00,//日
0x00,0x00,0x00,0x0A,0x15,0x0A,0x04,0x00,//心型 0x00,0x04,0x15,0x0E,0x1F,0x0E,0x11,0x00//坦克 };
4. 按键消抖
在按键按下的时候,会存在键盘抖动现象,抖动时间的长短和机械特性有关,一般为5~10ms,通常我们手动按下键然后释放,这个动作中稳定闭合的时间超过10ms,因此单片机在检测键盘是,必须加上消抖电路, 这里采用的方式为检测键盘按下,延时10ms,然后再检测一次,若仍是按下,则可判断键盘按下了一次。 程序如下:
void check() {
if(s1==1) {
delayxms(10); if(s1==1) { led1=~led1; if(led1==0) { led2=0; } } s1=0;
}
五、 完整程序
#include #define uint unsigned int #define uchar unsigned char uchar code table[]={ 0x04,0x0E,0x1B,0x15,0x1B,0x0E,0x04,0x00,//背光 0x04,0x0F,0x12,0x0F,0x0A,0x1F,0x02,0x02,//年 0x0F,0x09,0x0F,0x09,0x0F,0x09,0x09,0x13,//月 0x1F,0x11,0x11,0x1F,0x11,0x11,0x1F,0x00,//日 0x00,0x00,0x00,0x0A,0x15,0x0A,0x04,0x00,//心型 0x00,0x04,0x15,0x0E,0x1F,0x0E,0x11,0x00//坦克 }; uchar code backlight[]={0,120,150,170,190,220}; uchar code table1[]={0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07}; uchar code year[]=\"2013\"; uchar code month[]=\"12\"; uchar code day[]=\"15\"; uchar code light[]=\"Light:\"; uchar code ex[]=\":\"; sbit lcdrs=P1^1; sbit lcden=P1^3; sbit cp=P1^0; sbit s1=P1^4; sbit s2=P1^5; sbit s3=P1^6; sbit led1=P1^7; sbit led2=P1^2; uchar num,level,nowlevel,pr; void delayxms(uint xms) { uint i,j; for(i=xms;i>0;i--) for(j=110;j>0;j--) ; } void write_com(uchar com) { lcdrs=0; P0=com; //delayxms(5); lcden=1; //delayxms(5); lcden=0; } void write_data(uchar date) { lcdrs=1; P0=date; //delayxms(5); lcden=1; //delayxms(5); lcden=0; } void init() { //dula=0; //wela=0; lcden=0; write_com(0x38); write_com(0x0c); write_com(0x06); //write_com(0x01); //write(0x20); write_com(0x40); //写入自定义字符 for(num=0;num<40;num++) { write_data(table[num]); //delayxms(5); } } void show() { init(); write_com(0x80); for(num=0;num<4;num++) { write_data(year[num]); //delayxms(1); } write_data(table1[1]); //delayxms(5); for(num=0;num<2;num++) { write_data(month[num]); //delayxms(1); } write_data(table1[2]); //delayxms(5); for(num=0;num<2;num++) { write_data(day[num]); //delayxms(5); } write_data(table1[3]); //delayxms(5); write_com(0x80+0x40); write_data(table1[0]); write_data(ex[0]); //共5等级 if(P2>200&&P2<=255) { level=1; } else if(P2>150&&P2<=200) { level=2; } else if(P2>100&&P2<=150) { level=3; } else if(P2>50&&P2<=100) { level=4; } else if(P2>=0&&P2<=50) { level=5; } if(led1==0) { if((backlight[level]-P3>=20)||(P3-backlight[level]>=20)) nowlevel=level; } else { if(s2==1) { delayxms(10); if(s2==1) { led2=~led2; if(nowlevel<5) nowlevel++; } s2=0; } if(s3==1) { delayxms(10); if(s3==1) { led2=~led2; if(nowlevel>1) nowlevel--; } s3=0; } } P3=backlight[nowlevel]; write_data(nowlevel+48); } void check() { if(s1==1) { delayxms(10); if(s1==1) { led1=~led1; if(led1==0) { led2=0; } } s1=0; } } void check2() { if(s1==1) { delayxms(10); if(s1==1) led1=1; } } void main() { TMOD=0x01; TH0=(65536-2)/256; TL0=(65536-2)%256; EA=1; ET0=1; TR0=1; cp=1; s1=0; //s1=0,自动调节;s1=1,手动调节。led1示意当前的工作状态,0:自动调节;1:手动调节 s2=0; //s2=0,亮度保持;s2=1,亮度增加。 s3=0; //s3=0,亮度保持;s3=1,亮度减少。 led1=0; led2=0; while(1) { check(); //check2(); show(); //delayxms(30); } } void T0_time() interrupt 1 { TH0=(65536-2)/256; TL0=(65536-2)%256; cp=~cp; } void T1_time() interrupt 3 //在一个中断中,中断的服务程序中的代码还未执行完毕,下一次中断又会来临,这样这次中断就会丢失。 { EA=0; TH1=(65536-45872)/256; TL1=(65536-45872)%256; led1=~led1; EA=1; } 六、 实验遇到的问题及解决方法 1. 问题:C52中有两个定时器/计数器,我让两个定时器同时工作,分别控制不同 的中断程序,其中T0定时器产生500KHz的时钟脉冲,T1定时器产生1Hz的时钟脉冲,在实验中,T1的中断一直不起作用。 分析:这问题困扰了我较长的时间,一开始还认为两个定时器不能一起工作,但这种想法显然是错误的,后来发现当T0的中断频率较小时,T1的中断产生了作用,然后又通过跟同学讨论并查找书籍资料,发现了问题的原因。在一个中断中,若中断的程序中的代码还未执行完毕,下一次中断又来临了,这样这次中断就会丢失。如上设置两个定时器工作时,没有考虑中断的优先级,定时器T0的优先级是高于T1的,而T0的中断频率又远远高于T1的中断频率,那么在T1的中断程序执行过程中,每次都会被T0打断,使得每次T1中断都会丢失,所以T1的中断就没起作用。 解决方法: ① 交换一下T0和T1的中断,让T0去执行频率较低的中断,T1去执行频率较高的中断,这样T0不会受到T1的打扰,所以两个中断都可以正常的工作。 ② 掉一个中断,因为1Hz的这个中断是我认为确定的数值,它对频率的要求不 是特别严格,我可以将它省略掉,然后放在T0中断后面的while循环里,并调用delayxms()来控制大致的频率。 2. 问题:当光线渐变时,有时屏幕亮度会特别不稳定,不停地闪动 分析:因为我是通过人为将获得数字量分为了5各阶段,分别为0~50,51~100,101~150,151~200,201~255;当光线产生的数字量恰好位于两个阶段的相邻阶段,由于外界光线的不稳定,会使数字量不断的在两个等级之间晃动,这也就造成了屏幕背光亮度的频繁闪动。 解决方法:我把上次获得数字量记录下来,然后每获得一个数字量,我会拿当前的数字量去和上一个比较,在此我自己设定了一个阈值,当两者之差超越这个阈值,就改变屏幕亮度,否则屏幕亮度保持不变。这样就可以在一定程度上,减少上述情况的发生。 3.问题:LM324需要正负电压,而板子上只有+5V的电源 分析:通过查找资料可知,单片机与计算机在通信时需要加电平转化芯片,计算机RS-232电平为±12V,在实验板上用MAX232芯片完成电平转化功能,因此在该芯片上可以找到正负电压,实际上MAX232的2、6脚输出大约﹢10v和﹣10v左右的电压,所以此电压可以作为运放的电源电压。 解决方法:在连接一个MAX232芯片,让它的2、6管脚输出作为运放的电源电压。 七、 实验感想 八、 参考文献 [1]戴胜华,蒋大明,杨世武等. 单片机原理与应用[M]. 北京:北京交通大学出版社,2008. [2]郭天祥. 51单片机C语言教程[M].电子工业出版社 [3]张毅刚. 单片机原理与应用[M]. 北京: 高等教育出版社, 2004. [4]周航慈. 单片机程序设计基础[M]. 北京: 北京航空航天大学出版社,1997. [5]张凡,盛珣华,戴胜华. 微机原理与接口技术[M].北京:北京交通大学出版社 [6]侯建军. 数字电子技术基础[M].北京:高等教育出版社
因篇幅问题不能全部显示,请点此查看更多更全内容
Copyright © 2019- 91gzw.com 版权所有 湘ICP备2023023988号-2
违法及侵权请联系:TEL:199 18 7713 E-MAIL:2724546146@qq.com
本站由北京市万商天勤律师事务所王兴未律师提供法律服务