第9集、第10集 数码管的静态显示和动态显示
第9集和第10集主要讲了用单片机来驱动数码管显示,怎么来看待数码管呢,其实他就是一堆LED的组合,只是把他摆成了个数字8的形状,而通过点亮这个数字8的不同笔划,我们可以得到一系列的字符表,如数字“0~9”,甚至还有字母"a~z",这个就牵涉到7段字符表,这里就不再多述了。本质就是7个LED来组成字符,这7个LED要么是共阳极(正极),要么是共阴极(负极),大家在接线的时候一定要特别注意。并且这个LED的耐压值和电流值较小,所以大家也一定要串联1个限流电阻,一般是2K~5K,要看下数码管的显示亮度来选择合适的电阻。
数码管分为段码和位码,段码就是每个LED单独的那头,而位码就是共阴或共阳的那头,数码管有多少位就代表它有多少个位码,如常用的四位共阴数码管,就是有4个位码,段码一般是8个,包含右下角那个小数点。注意了段码的ABCDEFG段是分别并联起来的。可以看上面贴子的3位数码管图
怎么点亮相应的数码管和想要显示的字符呢?需要将上面的位码与段码组合,先讲下静态显示,以四位共阴极数码管为例,有以下步骤,
第1,你想要显示在第几个位码,如第2位,那么你要把这第2位要输出低电平(共阻极数码管么),
第2,你想要在第2位上显示什么字符,如数字”1“,数字1要显示,B段和C段LED要点亮,对应的B和C段要输出高电平,这样就点亮你想要显示的数码管字符了。
上面是只在某一位上显示1个字符,如果想要在不同位上显示不同的数字,那应该怎么办呢,这就牵涉到动态显示了。
我们人眼的结构很特别,有一种物理现象叫视觉残留,举个例子,我们人眼盯着明亮的白炽灯泡观看一段时间(10s),当灯突然熄灭后,人眼还有一瞬间还能感到明亮,这就叫视觉残留,所以关灯的时候先提前闭上眼睛就不会有抓瞎的感觉,能提前适应黑暗吧。
而数码管的动态显示呢,就是利用视觉差,从1个字符移到另1个字符用时极短,从第1位到第4位总时间不超过20s,人眼看到的就是同时4个字符。
视频里说了,ABCDEFG段是并联的,比如说时同给4个位码低电平,第1位AB高电平,第2位CD高电平,第3位EF高电平,第4位G高电平,那得到的显示就是”8888“。
记住1个原则,动态显示时,不能是2个或多个位码同时高电平,这样会显示乱码,只能是从第1位输出,并配合相应的段码输出后,延时2ms,再转移到第2位,依次循环,从第1位到最后1位的总累计时间不能超过20ms,这就是动态效果。
本课程我运用自己学到的知识有while和for循环,以及数组的应用,大大减少了程序的篇幅,提高程序的可阅读性。
这里也讲下自己对两个条件判断语句的心得体会:
条件判断初始化
while(条件判断式) // 条件判断式为0时跳出循环,为1时执行循环语句内容,这里注意一定要在语句内要有能跳出循环的语句,否则会形成死循环
{
循环语句内容;
能跳出循环的语句;
}
如:
a = 0 ;
while(a<3)
{
循环语句内容;
a++; // 这个a++就是此处代表能跳出循环的语句,如果没有这句就会一直死循环。
}
for(变量初始化;条件判断式;变量变化)
{
循环语句内容;
}
如
int a;
for( a = 0; a<3; a++)
{
循环语句内容; // a++也可以放到这里
}
最后讲下循环的嵌套,即循环套循环的执行过程:从A判断到B,再判断到C,先执行C再返回判断B,再返回判断A,即先执行最里面的循环再到外在的循环,这里可以看下我写的计时器程序,有用3个嵌套循环
while(A)
{
while(B)
{
while(C)
{
语句内容C;
}
}
}
- /*--------------------------------------------------------------------------------------
- 用数码管来制作定时器
-
- 编写日期:2024年2月26日 版本:V01 开发者:dumon
-
- --------------------------------------------------------------------------------------*/
-
- #include <STC32G.H>
- #include "comm/stc32_stc8_usb.h"
-
- #define MAIN_Fosc 24000000UL // 定义1个主时钟为24MHz
-
- // 流水灯相关说明,P2.0~P2.7为共阳极连接,P4.5接三极管基极,为低电平时发射极与基极接通,
- // 电流饱合后,发射极到集电极接通,LED得到高电平,P2.0~P2.7输出低电平即可点灯。
- sbit ON_LED = P4^5; // 点灯总开关,三极管是小电流控制大电流的开关
- sbit LED_1 = P2^0; // 流水灯LED1
- sbit LED_2 = P2^1; // 流水灯LED2
- sbit LED_3 = P2^2; // 流水灯LED3
- sbit LED_4 = P2^3; // 流水灯LED4
- sbit LED_5 = P2^4; // 流水灯LED5
- sbit LED_6 = P2^5; // 流水灯LED6
- sbit LED_7 = P2^6; // 流水灯LED7
- sbit LED_8 = P2^7; // 流水灯LED8
-
- // 数码管相关说明,P0.0~P0.7为数码管的8个段码,P1.0,P1.1,P1.3为3个位码,共阴极数码管
- // 位码要接低电平、段码接高电平,共阳极数码管位码要接高电平、段码接低电平
- sbit DIG_A = P0^0; // 数码管段码‘A’
- sbit DIG_B = P0^0; // 数码管段码‘B’
- sbit DIG_C = P0^0; // 数码管段码‘C’
- sbit DIG_D = P0^0; // 数码管段码‘D’
- sbit DIG_E = P0^0; // 数码管段码‘E’
- sbit DIG_F = P0^0; // 数码管段码‘F’
- sbit DIG_G = P0^0; // 数码管段码‘G’
- sbit DIG_P = P0^0; // 数码管段码‘dP’
-
- sbit SEG_1 = P1^0; // 第1位数码管位码
- sbit SEG_2 = P1^1; // 第2位数码管位码
- sbit SEG_3 = P1^3; // 第3位数码管位码
-
- // 蜂鸣器参照STC32G实验箱V9.4增加,P5.4输出低电平接三极管基极,同流水灯控制原理,
- // 在蜂鸣器负责两端并联反接1个二极管,防止短路
- sbit BEEP = P5^4; // 蜂鸣器端口
-
- // 数组变量
- u8 LED_Blinker[8] = {0xFE,0xFD,0xFB,0xF7,0xEF,0xDF,0xBF,0x7F};
- u8 DIG_Number[21] = {0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F, /*不带小数点*/
- 0xBF,0x86,0xDB,0xCF,0xE6,0xED,0xFD,0x87,0xFF,0xEF, /*带小数点*/
- 0x00 /*全部熄灭*/};
- /*
- P0.7 P0.6 P0.5 P0.4 P0.3 P0.2 P0.1 P0.0 BIN HEX
- 不带小数 DP G F E D C B A
- '0' 0 0 1 1 1 1 1 1 0011 1111 0x3F
- '1' 0 0 0 0 0 1 1 0 0000 0110 0x06
- '2' 0 1 0 1 1 0 1 1 0101 1011 0x5B
- '3' 0 1 0 0 1 1 1 1 0100 1111 0x4F
- '4' 0 1 1 0 0 1 1 0 0110 0110 0x66
- '5' 0 1 1 0 1 1 0 1 0110 1101 0x6D
- '6' 0 1 1 1 1 1 0 1 0111 1101 0x7D
- '7' 0 0 0 0 0 1 1 1 0000 0111 0x07
- '8' 0 1 1 1 1 1 1 1 0111 1111 0x7F
- '9' 0 1 1 0 1 1 1 1 0110 1111 0x6F
-
- 带小数就把P0.7全部置1,再重新计算8位二进制数的HEX值
- */
-
- u8 SEG_Number[3] = {0xFE,0xFD,0xF7}; // 位码,1位,2位,3位单独显示
-
- u16 a,b,c,d; // 循环计数变量
-
- u32 time; // 定义1个计时变量,用来累加计时
- u32 count_time = 3000; // 定义1个目标计时常量,3000表示30s
-
- //u8 S1,S2,S3; // 3个段位数值存储变量
- u8 S[3] = {0,0,0 }; // 用1个数组来存储位码
-
- void delay_ms(u16 ms) // 自定义延时函数
- {
- u16 i;
- do{
- i = MAIN_Fosc/6000;
- while(--i);
- } while(--ms);
- }
- void main()
- {
- WTST = 1;
- // IO口模式配置
- P0M1 = 0x00; P0M0 = 0x00;
- P1M1 = 0x00; P1M0 = 0x00;
- P2M1 = 0x00; P2M0 = 0x00;
- P3M1 = 0x00; P3M0 = 0x00;
- P4M1 = 0x00; P4M0 = 0x00;
- P5M1 = 0x00; P5M0 = 0x00;
-
- while(1)
- {
- // while(a<3)
- // {
- // P1 = SEG_Number[a];
- // while(b<10)
- // {
- // P0 = DIG_Number[b];
- // delay_ms(1000);
- // b++;
- // }
- // b = 0;
- // a++;
- // }
- // a = 0;
-
- // 正计时,从0一直加到设置的count_time值
- // 循环嵌套,先从最内侧开始循环,逐层往外,共循环a*b*c= 99900次,因为是3位数码管,省略十位和个位,只显示万、千、百位;
- // 这里用delay_ms(6),搭配3次循环刚好是time每加100,总用时1S。用手机计时器计算基本没差
- for( a= 0; a<100; a++)
- {
- for( b= 0; b<333; b++)
- {
- for( c= 0; c<3; c++)
- {
- // 数码管的动态刷新,先选择位码,再选段码,延时2ms,位码逐步往前移,利用人眼视觉暂留达到同时显示3个数字目的,
- // 位码的切换必须总延时必须在20ms内,否则会看到明显的数字移动和停留。
- // 如这里循环3次,有3个位码,每个位码延时6ms,总延时=3*6=18ms,可以将延时改成10ms看看显示效果
- S[2] = time%100000/10000; // 显示万位 76549 =INT(MOD(C2,100000)/10000)算万位
- S[1] = time%10000/1000; // 显示千位
- S[0] = time%1000/100; // 显示百位
- P1 = SEG_Number[c]; // 选择位码
- P0 = DIG_Number[S[c]]; // 根据数字显示段码
- delay_ms(6);
- time++;
- // 用while保持状态让数码管一直显示最后的数值不变,并且蜂鸣器工作,代表计时结束
- while( time == count_time) // 当time = count_time时,计时结束
- {
- for( c= 0; c<3; c++)
- {
- S[2] = time%100000/10000; // 计算百位 76549 =INT(MOD(C2,100000)/10000)算万位
- S[1] = time%10000/1000; // 计算十位
- S[0] = time%1000/100; // 计算个位
- P1 = SEG_Number[c];
- P0 = DIG_Number[S[c]];
- delay_ms(1);
- BEEP = 0;
- }
- } // 内部while循环,用于保持计时结束后的循环执行语句
- } // c的循环
- } // b的循环
- } // a的循环
-
- // // 倒计时计数,从设置值一直倒数到0
- // for( a= 0; a<100; a++)
- // {
- // for( b= 0; b<333; b++)
- // {
- // for( c= 0; c<3; c++)
- // {
- // // 数码管的动态刷新,先选择位码,再选段码,延时2ms,位码逐步往前移,利用人眼视觉暂留达到同时显示3个数字目的,
- // // 位码的切换必须总延时必须在20ms内,否则会看到明显的数字移动和停留。
- // // 如这里循环3次,有3个位码,每个位码延时6ms,总延时=3*6=18ms,可以将延时改成10ms看看显示效果
- // S[2] = count_time%100000/10000; // 显示万位 76549 =INT(MOD(C2,100000)/10000)算万位
- // S[1] = count_time%10000/1000; // 显示千位
- // S[0] = count_time%1000/100; // 显示百位
- // P1 = SEG_Number[c]; // 选择位码
- // P0 = DIG_Number[S[c]]; // 根据数字显示段码
- // delay_ms(6);
- // count_time--;
- // // 用while保持状态让数码管一直显示最后的数值不变,并且蜂鸣器工作,代表计时结束
- // while( count_time == 0) // 当time = count_time时,计时结束
- // {
- // for( c= 0; c<3; c++)
- // {
- // S[2] = count_time%100000/10000; // 计算百位 76549 =INT(MOD(C2,100000)/10000)算万位
- // S[1] = count_time%10000/1000; // 计算十位
- // S[0] = count_time%1000/100; // 计算个位
- // P1 = SEG_Number[c];
- // P0 = DIG_Number[S[c]];
- // delay_ms(1);
- // BEEP = 0;
- // }
- // } // 内部while循环,用于保持计时结束后的循环执行语句
- // } // c的循环
- // } // b的循环
- // } // a的循环
-
- }
- }
复制代码
|