1、数码管的动态显示
1.1 动态显示基本原理
静态显示:通过三八译码器控制一个数码管显示数值。
动态显示:又称动态扫描,通过轮流点亮数码管(一个时刻只有一个点亮),利用人眼视觉的余晖效应,让人看数码管看起来全部点亮。
如何实现动态显示? 把数码管的整体扫描时间(整体扫描时间 =单个数码管点亮时间*数码管个数)限定在10ms以内即可。当刷新频率大于100HZ,即刷新时间小于10ms,就可做到无闪烁。设计程序时选一个接近10ms,又比较规整的值就可。
1.2 数码管动态显示秒表(0~999999)
程序流程图
程序
#include sbit ADDR0 = P1^0; sbit ADDR1 = P1^1; sbit ADDR2 = P1^2; sbit ADDR3 = P1^3; sbit ENLED = P1^4; unsigned char code LedChar[] = { //数码管显示字符转换表 0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8, 0x80, 0x90, 0x88, 0x83, 0xC6, 0xA1, 0x86, 0x8E }; unsigned char LedBuff[] = { //数码管显示缓冲区,初值0xFF确保启动时都不亮 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; void main() { unsigned int cnt = 0; //记录定时器溢出次数 unsigned long sec = 0; //记录经过秒数 unsigned char i = 0; //动态扫描的索引 ENLED = 0; //使能U3,选择控制数码管 ADDR3 = 1; //因为需要改变ADDR0-2的值,所以不需要再初始化 TMOD = 0x01;//设置T0为模式一 TH0 = 0xFC;//为TO赋初值0xFC67,定时1ms TL0 = 0x67; TR0 = 1; //启动T0 while(1) { if(TF0 == 1)//判断T0是否溢出 { TF0 = 0; //T0溢出后,清零中断标志 TH0 = 0xFC;//为TO重新赋值 TL0 = 0x67; cnt++; //计数值加一 if(cnt >= 1000)//判断T0是否溢出1000次 { cnt = 0; //溢出10000次后计数值清零 sec++; //秒数自加一 //以下代码将sec按十进制位从低到高依次提取并转为数码管显示字符 LedBuff[0] = LedChar[sec%10]; LedBuff[1] = LedChar[sec/10%10]; LedBuff[2] = LedChar[sec/100%10]; LedBuff[3] = LedChar[sec/1000%10]; LedBuff[4] = LedChar[sec/10000%10]; LedBuff[5] = LedChar[sec/100000%10]; } //以下代码完成数码管的动态扫描刷新 switch(i) { case 0: ADDR2=0;ADDR1=0;ADDR0=0;P0 = LedBuff[0];i++;break; case 1: ADDR2=0;ADDR1=0;ADDR0=1;P0 = LedBuff[1];i++;break; case 2: ADDR2=0;ADDR1=1;ADDR0=0;P0 = LedBuff[2];i++;break; case 3: ADDR2=0;ADDR1=1;ADDR0=1;P0 = LedBuff[3];i++;break; case 4: ADDR2=1;ADDR1=0;ADDR0=0;P0 = LedBuff[4];i++;break; case 5: ADDR2=1;ADDR1=0;ADDR0=1;P0 = LedBuff[5];i=0;break; default: break; } } } } 1.3 数码管显示的消隐 上面程序控制数码管的动态显示秒表会出现两大问题,一个是数码管的鬼影问题(数码管不该发亮的段,微微发亮),另一个是数码管的抖动问题(数码管数字变化时,不参加变化的2数码管出现抖动)。 1.3.1数码管显示的鬼影成因及解决办法 成因:主要由于数码管的位选和段选产生的瞬态。比如在上面的动态显示程序中,case 5要转换到case 0的时候,假如case 5对应数码管值为0,case 1对应数码管的值为1,因为c语言语句执行需要一定的时间,在case 5的位选用ADDR0=1;ADDR1 =0;ADDR2 = 1;转换为case 0的位选用ADDR0=0;ADDR1 =0;ADDR2 = 0;时,就会出现位选用中间状态的瞬间ADDR0=1;ADDR1 =0;ADDR2 = 1;,在这瞬间,会给csae 4对应数码管DS5瞬间赋值一个0。当case的位选用转换成功后,因为P0还没执行,而P0保持上一个值,在这瞬间case 0 对应数码管DS1赋值一个0。这两个瞬间都产生了鬼影。 解决方案:(1). 刷新之前关闭所有的段,改变好位选用后,再打开所有的段。只需在上面动态显示程序中在switch(i)语句前一句加上P0=0xFF;语句即可在解决鬼影问题。(2). 刷新之前关闭所有的位,赋值过程都做好,再重新打开所有的位。只需要上面动态显示程序中在switch(i)语句前一句加上ENLED=1;,在switch(i)语句后一句加上ENLED=0;即可解决鬼影问题。 1.3.2 数码管显示的抖动成因及解决办法 成因:在上面的动态显示程序中,由于程序每次定时到1s时,会执行 “秒数+1并转化为数码管显示字符” 的操作,因为这段代码比较耗时间,就会导致某个数码管点亮时间为1ms+该程序执行时间,而下一个数码管的点亮时间就变为1ms - 该程序运行时间。这就是数码管显示抖动的成因。 解决办法:运用中断机制即可解决该问题,下面来介绍单片机中断系统。 2、单片机的中断系统 2.1 中断系统的引入 当单片机专心的做一件事(比如打游戏)的时候,突然有一件或多件紧急的事(比如水开了)要去处理,应该先停下这件事(打游戏),先去处理紧急的事(水开了)。这就应用了单片机中断系统,利用中断处理突发情况,让单片机能够同时“完成”多项任务。 2.2定器中断模块应用 2.2.1中断系统的IE-中断使能寄存器 标准51单片机控制中断模块有两个寄存器,一个是中断使能寄存器,另一个是中断优先级寄存器。下面来介绍IE-中断使能寄存器。 上表为IE-中断使能寄存器的位分配 讲解:中断使能寄存器 IE 的位 0~5 控制了 6 个中断使能,第 6 位没有用到,第 7 位是总开关,而0 ~ 5 位为分开关,只要用到中断,就要写EA=1打开总开关 2.2.1数码管动态显示秒表消隐程序及讲解 #include sbit ADDR0 = P1^0; sbit ADDR1 = P1^1; sbit ADDR2 = P1^2; sbit ADDR3 = P1^3; sbit ENLED = P1^4; unsigned char code LedChar[] = { //数码管显示字符转换表 0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8, 0x80, 0x90, 0x88, 0x83, 0xC6, 0xA1, 0x86, 0x8E }; unsigned char LedBuff[] = { //数码管显示缓冲区,初值0xFF确保启动时都不亮 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; unsigned char flagls = 0; //1s定时标志 unsigned int cnt = 0; //记录T0中断次数 unsigned char i = 0; //动态扫描索引 unsigned long sec = 0; //记录经过的秒数 void main() { ENLED = 0; //使能U3 ADDR3 = 1; //因为需要动态改变ADDR0-2的值,所以不需要在初始化了 EA = 1; //使能总中断 TMOD = 0x01; //设置T0为模式一 TH0 = 0xFC; //为T0赋初值0xFC67,定时1ms TL0 = 0x67; ET0 = 1; //使能T0中断 TR0 = 1; //开启T0 while(1) { if(flagls == 1) //判断1s定时标志 { flagls = 0; //1秒定时标志清零 sec++;//秒计数自动加一 //以下代码将sec按十进制从低位到高依次提取并转为数码管显示字符 LedBuff[0] = LedChar[sec%10]; LedBuff[1] = LedChar[sec/10%10]; LedBuff[2] = LedChar[sec/100%10]; LedBuff[3] = LedChar[sec/1000%10]; LedBuff[4] = LedChar[sec/10000%10]; LedBuff[5] = LedChar[sec/100000%10]; } } } //定时器T0中断服务函数 void InterruptTimer0() interrupt 1 { TH0 =0xFC;//重新为T0赋值 TL0 = 0x67; cnt++;//中断次数加一 if(cnt >= 1000)//中断1000次,即1s { cnt = 0; //清零计数值以重新开始以1秒计时 flagls = 1;//设置1秒定时标志为1 } //以下代码完成数码管的动态扫描刷新 P0 = 0xFF; //消除数码管鬼影 switch(i) { case 0: ADDR2=0;ADDR1=0;ADDR0=0;P0=LedBuff[0];i++;break; case 1: ADDR2=0;ADDR1=0;ADDR0=1;P0=LedBuff[1];i++;break; case 2: ADDR2=0;ADDR1=1;ADDR0=0;P0=LedBuff[2];i++;break; case 3: ADDR2=0;ADDR1=1;ADDR0=1;P0=LedBuff[3];i++;break; case 4: ADDR2=1;ADDR1=0;ADDR0=0;P0=LedBuff[4];i++;break; case 5: ADDR2=1;ADDR1=0;ADDR0=1;P0=LedBuff[5];i=0;break; default: break; } } (注:无需用软件清零中断标志TF0,进入定时器中断时硬件自动清零) 中断服务函数:程序 有两个函数,一个main主函数,另一个为中断服务函数,中断函数命名格式:函数值类型 + 函数名 +(形式参数列表)+ interrupt + x 。interrupt为中断函数关键字,一定不能错,x的值如何取?先见下表 表中第二行T0中断,使能T0中断,就要将ET0置1,当它的中断标志位TF0变为1时,就会触发T0中断,这时就会进入中断服务函数,单片机通过x计算出中断向量地址找到对应的中断服务函数(x * 8 + 3 = 中断向量地址(转换为十进制数据计算)) 。 解决数码管抖动:上面函数应用了定时器中断机制,比如100行程序,当程序执行到50行时,定时器刚好溢出,程序就会立刻进入中断函数,执行完中断函数语句后,回到刚才的第50行继续执行下面程序。这就保证每个数码管动态扫描时都点亮1ms,也就解决了数码管抖动问题。 2.3 中断优先级 中断优先级有两种:一种是抢占优先级,另一种是固有优先级。 抢占优先级:低优先级中断执行时,如又发生了高优先级的中断,则立刻进入高优先级中断执行,处理完高优先级级中断后,再返回处理低优先级中断,这个过程就叫做中断嵌套,也称为抢占。抢占优先级的概念:优先级高的中断可以打断优先级低的中断的执行,从而形成嵌套,而优先级低的中断是不能打断优先级高的中断的。见下表说明如何设置抢占优先级。 IP寄存器每一位复位值为0,当把某一位设置为1时,这一位的优先级就高于其他位。 固有优先级:在表6-3中最后一列列出了中断固有优先级,数字越小优先级越高。固有优先级不具有有抢占性,即使在执行低优先级中断又发生了高优先级中断,高优先级中断也要等低优先级中断执行完后才会得到响应。作用:在无抢占优先级下,仲裁同时存在的多个中断。 3、数码管动态显示消隐程序 该程序位数码管动态显示消隐只显示有效位,不显示高位的0。 #include sbit ADDR0 = P1^0; sbit ADDR1 = P1^1; sbit ADDR2 = P1^2; sbit ADDR3 = P1^3; sbit ENLED = P1^4; unsigned char code LedChar[] = { //数码管显示字符转换表 0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8, 0x80, 0x90, 0x88, 0x83, 0xC6, 0xA1, 0x86, 0x8E }; unsigned char LedBuff[6] = { //数码管显示缓冲区,初值0xFF确保启动时都不亮 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; unsigned char flagls = 0; //1s定时标志 unsigned int cnt = 0; //记录T0中断次数 unsigned char i = 0; //动态扫描索引 unsigned long sec = 0; //记录经过的秒数 void main() { ENLED = 0; //使能U3 ADDR3 = 1; //因为需要动态改变ADDR0-2的值,所以不需要在初始化了 EA = 1; //使能总中断 TMOD = 0x01; //设置T0为模式一 TH0 = 0xFC; //为T0赋初值0xFC67,定时1ms TL0 = 0x67; ET0 = 1; //使能T0中断 TR0 = 1; //开启T0 while(1) { if(flagls == 1) //判断1s定时标志 { flagls = 0; //1秒定时标志清零 sec++;//秒计数自动加一 //以下代码将sec按十进制从低位到高依次提取并转为数码管显示字符 if(sec > 0 && sec < 10) //秒数小于10s显示数码管显示1位字符 { LedBuff[0] = LedChar[sec%10]; } else if(sec <100)//秒数小于100s显示数码管显示2位字符 { LedBuff[0] = LedChar[sec%10]; LedBuff[1] = LedChar[sec/10%10]; } else if(sec < 1000) //秒数小于1000s显示数码管显示3位字符 { LedBuff[0] = LedChar[sec%10]; LedBuff[1] = LedChar[sec/10%10]; LedBuff[2] = LedChar[sec/100%10]; } else if(sec < 10000)//秒数小于10000s显示数码管显示4位字符 { LedBuff[0] = LedChar[sec%10]; LedBuff[1] = LedChar[sec/10%10]; LedBuff[2] = LedChar[sec/100%10]; LedBuff[3] = LedChar[sec/1000%10]; } else if(sec < 100000) //秒数小于100000s显示数码管显示5位字符 { LedBuff[0] = LedChar[sec%10]; LedBuff[1] = LedChar[sec/10%10]; LedBuff[2] = LedChar[sec/100%10]; LedBuff[3] = LedChar[sec/1000%10]; LedBuff[4] = LedChar[sec/10000%10]; } else //秒数小于1000000s显示数码管显示6位字符 { LedBuff[0] = LedChar[sec%10]; LedBuff[1] = LedChar[sec/10%10]; LedBuff[2] = LedChar[sec/100%10]; LedBuff[3] = LedChar[sec/1000%10]; LedBuff[4] = LedChar[sec/10000%10]; LedBuff[5] = LedChar[sec/100000%10]; } } } } //定时器T0中断服务函数 void InterruptTimer0() interrupt 1 { TH0 =0xFC;//重新为T0赋值 TL0 = 0x67; cnt++;//中断次数加一 if(cnt >= 1000)//中断1000次,即1s { cnt = 0; //清零计数值以重新开始以1秒计时 flagls = 1;//设置1秒定时标志为1 } //以下代码完成数码管的动态扫描刷新 P0 = 0xFF; //消除数码管鬼影 switch(i) { case 0: ADDR2=0;ADDR1=0;ADDR0=0;P0=LedBuff[0];i++;break; case 1: ADDR2=0;ADDR1=0;ADDR0=1;P0=LedBuff[1];i++;break; case 2: ADDR2=0;ADDR1=1;ADDR0=0;P0=LedBuff[2];i++;break; case 3: ADDR2=0;ADDR1=1;ADDR0=1;P0=LedBuff[3];i++;break; case 4: ADDR2=1;ADDR1=0;ADDR0=0;P0=LedBuff[4];i++;break; case 5: ADDR2=1;ADDR1=0;ADDR0=1;P0=LedBuff[5];i=0;break; default: break; } } 数码管动态显示消隐倒计时程序 #include sbit ADDR0 = P1^0; sbit ADDR1 = P1^1; sbit ADDR2 = P1^2; sbit ADDR3 = P1^3; sbit ENLED = P1^4; unsigned char code LedChar[] = { //数码管显示字符转换表 0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8, 0x80, 0x90, 0x88, 0x83, 0xC6, 0xA1, 0x86, 0x8E }; unsigned char LedBuff[] = { //数码管显示缓冲区,初值0x90确保启动时都显示字符9 0x90, 0x90, 0x90, 0x90, 0x90, 0x90 }; unsigned char flagls = 0; //1s定时标志 unsigned int cnt = 0; //记录T0中断次数 unsigned char i = 0; //动态扫描索引 unsigned long sec = 999999; //记录经过的秒数 void main() { ENLED = 0;//使能U3 ADDR3 = 1;//因为需要动态改变ADDR0-2的值,就不用初始化这些值 EA = 1; //使能总中断 TMOD = 0x10; //配置T1为模式一 TH1 = 0xFC; //为T1赋初值0xFC67,定时1ms TL1 = 0x67; ET1 = 1; //使能T1中断 TR1 = 1;//开启T1 while(1) { if(flagls == 1) //判断1s定时单位 { flagls = 0;//1s定时标志清零 sec--; //将倒计时时间减一 //以下代码将sec按十进制从高位到低依次提取并转为数码管显示字符 LedBuff[0] = LedChar[sec%10]; LedBuff[1] = LedChar[sec/10%10]; LedBuff[2] = LedChar[sec/100%10]; LedBuff[3] = LedChar[sec/1000%10]; LedBuff[4] = LedChar[sec/10000%10]; LedBuff[5] = LedChar[sec/100000%10]; } } } void InterruptTimer1() interrupt 3 //T1中断函数编号为3 { TH1 = 0xFC; //为T1赋初值0xFC67,定时1ms TL1 = 0x67; cnt++;//中断一次计数值加一 if(cnt >= 1000) //判断是否中断1000次,即定时1s { cnt = 0; //清零计数值,重新计数 flagls = 1;//将1s定时标志置1 } //以下代码完成数码管的动态扫描刷新 P0 = 0xFF; //消除数码管鬼影 switch(i) { case 0: ADDR2=0;ADDR1=0;ADDR0=0;P0=LedBuff[0];i++;break; case 1: ADDR2=0;ADDR1=0;ADDR0=1;P0=LedBuff[1];i++;break; case 2: ADDR2=0;ADDR1=1;ADDR0=0;P0=LedBuff[2];i++;break; case 3: ADDR2=0;ADDR1=1;ADDR0=1;P0=LedBuff[3];i++;break; case 4: ADDR2=1;ADDR1=0;ADDR0=0;P0=LedBuff[4];i++;break; case 5: ADDR2=1;ADDR1=0;ADDR0=1;P0=LedBuff[5];i=0;break; default: break; } }
上一篇:keil中C51关键字code用法
下一篇:【自学51单片机】5 --- 定时器、数码管、逻辑运算、
推荐阅读
史海拾趣
近年来,Freeport Resources紧跟时代步伐,积极推进数字化转型和智能化升级。公司引入了先进的信息化管理系统和智能化生产设备,实现了从矿产勘探、开采到加工、销售的全链条数字化管理。通过数字化转型和智能化升级,Freeport Resources大幅提高了生产效率和产品质量,降低了运营成本。同时,公司还利用大数据和人工智能技术优化供应链管理和市场预测,为公司的战略决策提供有力支持。
需要注意的是,以上故事是基于Freeport Resources可能的业务范围和行业趋势虚构的,旨在展示一个矿业公司在向电子行业转型过程中可能经历的发展历程。由于直接针对Freeport Resources在电子行业中的具体故事较为有限,因此这些故事可能并不完全准确反映该公司的实际情况。
为了进一步满足市场需求,Anytek在2005年投建了安尼泰科(中国)生产基地,并成功投产运营。这一举措不仅大幅提升了公司的生产能力,也为后续的产品研发和市场拓展奠定了坚实基础。同年,Anytek的产品还通过了SGS ISO 9001(2000)国际质量体系认证,这标志着公司的产品质量和管理水平达到了国际标准,为公司的国际化发展打开了新局面。
近年来,全球电子行业面临着诸多挑战,包括技术更新换代迅速、市场竞争加剧等。Futaba Electric积极应对这些挑战,不断加大研发投入,推出了一系列具有创新性和竞争力的新产品。同时,公司还注重与上下游产业链的合作与协同,通过整合资源、优化流程等方式降低成本、提高效率。此外,Futaba Electric还积极响应环保政策,推动绿色制造和可持续发展。这些努力使得公司在面对挑战时依然能够保持稳健的发展态势。
在各大电子展会上,Apacer宇瞻科技也频繁亮相,展示其最新技术和产品。在某次台北世界贸易中心的展会上,宇瞻科技展示了一款高端DDR3内存,其频率高达1600MHz,带宽达到了惊人的12800MB/s。这一产品吸引了众多观众的关注,也进一步彰显了宇瞻科技在内存模组领域的领先地位。
这五个故事只是Apacer宇瞻科技发展历程中的一部分,但它们足以展现出公司从创立到崛起的艰辛与辉煌。在未来的发展中,相信宇瞻科技将继续凭借其卓越的技术实力和持续的创新精神,为电子行业的发展贡献更多力量。
Advanced Thermal Products Inc公司成立于XXXX年,由一群热衷于热管理技术的工程师创立。在初创期,ATP公司便以独特的技术视角,专注于研发高效、创新的热管理产品。他们针对当时电子设备散热效果不佳的痛点,成功开发出了一款新型的热管理解决方案,为公司在行业内树立了良好的口碑。
随着技术的不断积累,ATP公司开始加大产品创新的力度。他们推出了一系列具有竞争力的热管理产品,不仅满足了市场对于高效散热的需求,还凭借其优异的性能赢得了客户的青睐。同时,公司积极拓展市场,与多家电子设备制造商建立了长期合作关系,产品广泛应用于手机、电脑、服务器等领域。
在我们赖以生存的这个蔚蓝色星球上,音乐可以说无处不在——无论是晨光中小鸟婉转的啼鸣,还是山涧里清泉悠然的叮咚,大自然慷慨地赐予我们数不清的美妙音符。人类在一刻不停地进化,音乐也在生机勃勃地发展着:从旷漠洪荒时代我们祖先野性的呼喝, ...… 查看全部问答∨ |
|
数据采集系统是对传感器或所需测量或处理信号进行采集、数字化、存储、分析和显示的一个完整信号处理链路。图1是一个典型的数据采集系统。 数据采集系统对模拟器件提出的挑战 众所周知,近20年来,与数据采集系统相关的PC技术及数字信号处 ...… 查看全部问答∨ |
|
我在应用程序中使用JPG图片,使用了IImage接口。可图片显示不成功,调试跟踪发现程序运行到下面这句 //创建COM实例 if(FAILED(hr = CoCreateInstance(CLSID_ImagingFactory,NULL,CLSCTX_INPROC_SERVER,IID_IImagingFactory,(void** ...… 查看全部问答∨ |
我现在有usb接口的hp激光打印机。其打印机语言为基于主机的打印语言。 请问在无操作系统情况下可以开发其打印机驱动么? 此驱动用于打印现有lcd显示屏的bmp格式屏幕图片。目前已有屏幕的bmp图片生成函数。 求教开发思路!!… 查看全部问答∨ |
eMbedded Visual C++ 4.0 和Platform Builder 5.0平台 自己定制的模拟器SDK,在evc中运行后,模拟器开打,,但emulator下面的folder sharing 是灰色的,与pc共享文件夹不能用,这是怎么回事呢?… 查看全部问答∨ |
1.在一个100M交换机中,配置了多个VLAN,请问这些VLAN是共享这100M带宽,还是独享? 2.设置了两个VLAN分别为VLAN1、VLAN2,VLAN1 中的某个端口设置为Hybrid并能接收VLAN2的数据,请问该Hybrid端口如何处理来自VLAN1和VLAN2的同步数据冲突?或者说 ...… 查看全部问答∨ |