折腾手记 篇三:Arduino 控制7段数码管显示
趁着这2天台风,窝在家里,把第三篇写掉。
首先讲讲为啥要学7段数码管的控制。
7段数码管是什么?就是下图这种类型的数字显示器,当然有多种形式,一位的,2位,三位,4位,可以说这个数码显示管在各种工业消费产品上非常常见,用于显示数字。
数码管的显示原理:用7段长条形的LED放光条,通过驱动电路的控制,点亮不同的灯来显示出0-9数字和部分英文字符,说是7段,其实算上右下角小数点的话,其实有8个LED发光管。
现在多数场合这种数码管都会直接用一个驱动IC驱动,可以节省控制针脚的使用。
我们学习时候仍然通过用Arduino直接驱动数码管的方式,因为这样可以让我们掌握几个编程的知识点。
学习这个数码管驱动编程有哪几个新知识点呢:
1,学会使用for循环语句快速完成重复动作
2,判断语句-switch case的使用
3,学会利用数组或者16进制数字作为pattern来方便快速实现动作
4,利用视觉暂留,让高速闪动的数字变成眼见的持续显示数字串
本篇使用零件清单:
1,arduino板一只(UNO,Nano,Mini都可以)
2,1位数码管,4位数码管各一
3,220欧姆电阻(4-10个)
4,半长面包板一块
5,跳线若干
下面首先开始介绍硬件连接:
首先是一位数码管,引脚图见下
一共10个引脚,中间2个为公共端引脚(有共阴极和共阳极的区别),本文用共阴极数码管讲解,因此公共端接Arduino的GND针脚,a-g引脚依次接arduino的2-8号针脚,dp(也有称h)引脚接9号针脚。接线图如下:
本图没有使用限流电阻,如果不清楚你的数码管是否有内置限流电阻,为防止烧坏LED发光管,最好在公共端或者其他8个引脚端串联220欧姆电阻。
编程前的思考:
要通过点亮不同的灯管来显示数字,我们要准备每个数字对应点亮灯管的编号,要用数字来对应点亮显示该数字的灯管,显示下一个数字前要清除上一个显示。
那么首先我们要准备一个Pattern,就是存储显示不同数字是需要点亮的LED编号。
比如要显示数字1,看引脚图,我们知道要点亮b和c灯管,如果这样我们可能要写多个子函数,每一个子函数里用针脚输出命令把想要的针脚输出高电平点亮对应LED灯,这样的程序示例如下:
int a=2;//定义数字接口7 连接a 段数码管int b=3;// 定义数字接口6 连接b 段数码管int c=4;// 定义数字接口5 连接c 段数码管int d=5;// 定义数字接口11 连接d 段数码管int e=6;// 定义数字接口10 连接e 段数码管int f=7;// 定义数字接口8 连接f 段数码管int g=8;// 定义数字接口9 连接g 段数码管int dp=9;// 定义数字接口4 连接dp 段数码管void digital_1(void) //显示数字1{unsigned char j;digitalWrite(c,HIGH);//给数字接口5 引脚高电平,点亮c 段digitalWrite(b,HIGH);//点亮b 段for(j=7;j<=11;j++)//熄灭其余段digitalWrite(j,LOW);digitalWrite(dp,LOW);//熄灭小数点DP 段}void digital_2(void) //显示数字2{unsigned char j;digitalWrite(b,HIGH);digitalWrite(a,HIGH);for(j=9;j<=11;j++)digitalWrite(j,HIGH);digitalWrite(dp,LOW);digitalWrite(c,LOW);digitalWrite(f,LOW);}void digital_3(void) //显示数字3{unsigned char j;digitalWrite(g,HIGH);digitalWrite(d,HIGH);for(j=5;j<=7;j++)digitalWrite(j,HIGH);digitalWrite(dp,LOW);digitalWrite(f,LOW);digitalWrite(e,LOW);}void digital_4(void) //显示数字4{digitalWrite(c,HIGH);digitalWrite(b,HIGH);digitalWrite(f,HIGH);digitalWrite(g,HIGH);digitalWrite(dp,LOW);digitalWrite(a,LOW);digitalWrite(e,LOW);digitalWrite(d,LOW);}void digital_5(void) //显示数字5{unsigned char j;for(j=7;j<=9;j++)digitalWrite(j,HIGH);digitalWrite(c,HIGH);digitalWrite(d,HIGH);digitalWrite(dp,LOW);digitalWrite(b,LOW);digitalWrite(e,LOW);}void digital_6(void) //显示数字6{unsigned char j;for(j=7;j<=11;j++)digitalWrite(j,HIGH);digitalWrite(c,HIGH);digitalWrite(dp,LOW);digitalWrite(b,LOW);}void digital_7(void) //显示数字7{unsigned char j;for(j=5;j<=7;j++)digitalWrite(j,HIGH);digitalWrite(dp,LOW);for(j=8;j<=11;j++)digitalWrite(j,LOW);}void digital_8(void) //显示数字8{unsigned char j;for(j=5;j<=11;j++)digitalWrite(j,HIGH);digitalWrite(dp,LOW);}void digital_9(void) //显示数字9{unsigned char j;for(j=5;j<=11;j++)digitalWrite(j,HIGH);digitalWrite(e,LOW);digitalWrite(dp,LOW);}void digital_0(void) //显示数字0{unsigned char j;for(j=5;j<=11;j++)digitalWrite(j,HIGH);digitalWrite(g,LOW);digitalWrite(dp,LOW);}void digital_00(void) //显示数字.{unsigned char j;for(j=5;j<=11;j++)digitalWrite(j,LOW);digitalWrite(dp,HIGH);}这些子程序虽然能实现显示数字,但是这个程序显得过于“朴实”,没能充分利用计算机的优势,写代码的量太大,我们要考虑优化。
我们知道要点亮一个LED灯,我们要写入HIGH(1)高电平(共阴极),那么我们就会考虑用0和1的数组阵列或者一个字节的16进制数来组成显示不同数字的“图案”:
1,使用2维数组图案方式,我们定义一个2维数组,数组的每一行代表一个数字,每一列的数字1代表要点亮的灯管,比如显示数字0,要使用第一行数组,其中abcdef灯管要点亮,gh灯管不亮
unsigned char num[17][8] = {//a b c d e f g h{1, 1, 1, 1, 1, 1, 0, 0}, //0{0, 1, 1, 0, 0, 0, 0, 0}, //1{1, 1, 0, 1, 1, 0, 1, 0}, //2{1, 1, 1, 1, 0, 0, 1, 0}, //3{0, 1, 1, 0, 0, 1, 1, 0}, //4{1, 0, 1, 1, 0, 1, 1, 0}, //5{1, 0, 1, 1, 1, 1, 1, 0}, //6{1, 1, 1, 0, 0, 0, 0, 0}, //7{1, 1, 1, 1, 1, 1, 1, 0}, //8{1, 1, 1, 1, 0, 1, 1, 0}, //9{1, 1, 1, 0, 1, 1, 1, 1}, //A{1, 1, 1, 1, 1, 1, 1, 1}, //B{1, 0, 0, 1, 1, 1, 0, 1}, //C{1, 1, 1, 1, 1, 1, 0, 1}, //D{1, 0, 0, 1, 1, 1, 1, 1}, //E{1, 0, 0, 0, 1, 1, 1, 1}, //F{0, 0, 0, 0, 0, 0, 0, 1}, //.};2,使用1个字节的16进制数字来对应一个需要显示的10进制数字
const unsigned char LED8Code[]={0x3f, // 00x06, // 10x5b, // 20x4f, // 30x66, // 40x6d, // 50x7d, // 60x07, // 70x7f, // 80x6f, // 90x77, // A0x7c, // B0x39, // C0x5e, // D0x79, // E0x71 // F};这个是怎么来的呢,解释如下:on和off可以作为1和0
也就是我们把a-g(a-h)灯管按照高低或者低高的顺序排列,对应的要显示10进制数字需要点亮的短管按照0/1来排列组成的一个字节代表的16进制数字,有了这个"图案",我们在后面编程的时候,只需要从“图案”调取一个16进制数字,把对应的二进制位高低写入对应的灯管引脚就可以了。
下面是完整示例程序:一位数码管的显示程序,使用数组型图案
// 定义一个数组,存储针脚编号对应灯管abcdefghint pinLED[8] = {2,3,4,5,6,7,8,9};//a,b,c,d,e,f,g,h// 定义一个二维数组,作为显示数字的“图案”// 每一行代表一个显示的数字,每一列代表要显示这个数字需要点亮或者关闭的灯脚// 1代表点亮,0代表关闭(共阴极数码管,共阳极数码管正好反过来)int num_array[10][8] = { { 1,1,1,1,1,1,0,0 }, // 0{ 0,1,1,0,0,0,0,0 }, // 1{ 1,1,0,1,1,0,1,0 }, // 2{ 1,1,1,1,0,0,1,0 }, // 3{ 0,1,1,0,0,1,1,0 }, // 4{ 1,0,1,1,0,1,1,0 }, // 5{ 1,0,1,1,1,1,1,0 }, // 6{ 1,1,1,0,0,0,0,0 }, // 7{ 1,1,1,1,1,1,1,0 }, // 8{ 1,1,1,0,0,1,1,0 } // 9};void setup() { // 初始化针脚为输出模式,利用循环语句,一个循环代替多行语句for(int i = 0; i <= 7; i++){pinMode(pinLED[i], OUTPUT);digitalWrite(pinLED[i], HIGH); //初始化点亮所有灯管测试}}//void loop(){ //循环显示0-9数字 for (int counter = 0; counter < 10; counter++) {delay(1000); Num_Write(counter); //counter是要显示的数字传递给子函数Num_Write()}delay(3000);}void Num_Write(int number) //number为要显示的数字{for (int j=0; j <= 7; j++) { digitalWrite(pinLED[j], num_array[number][j]); //对数字对应的数组行的8个针脚写入对应的列的0或者1,实现显示数字}}
下面是完整示例程序:一位数码管的显示程序,使用一个字节16进制数字图案
const unsigned char LED8Pin[] = {2, 3, 4, 5, 6, 7, 8, 9};//A B C D E F G Dp// 一个无符号数据类型占用1个字节的内存。与byte的数据类型相同。// 定义一个字节数据类型的数组,数组中每一个16进制数字,代表需要显示的数字/字母// 这个一个字节的16进制数字的每一位(一个字节共8位)的2进制值代表需要点亮或者关闭灯脚const unsigned char LED8Code[] = {0x3f, // 00x06, // 10x5b, // 20x4f, // 30x66, // 40x6d, // 50x7d, // 60x07, // 70x7f, // 80x6f, // 90x77, // A0x7c, // B0x39, // C0x5e, // D0x79, // E0x71 // F};
void setup() {for (int i = 0; i < 8; i++){pinMode(LED8Pin[i], OUTPUT);digitalWrite(LED8Pin[i], HIGH);}delay(1000);}
void loop() {for (int a = 0; a < 16; a++) {LED8Show(a);delay(1000);}}
void LED8Show(int data) {for (int i = 0; i < 8; i++) { bool j = bitRead(LED8Code[data], i); // bitRead(byte X,int i),读取一个字节数据X中的第i位值digitalWrite(LED8Pin[i], j);// 依次读取一个16进制数的8位值,写入灯管针脚}}
上面这2个示例,使用了循环,数组“图案”等模式,整个程序,比起最前面那个简单的逐个子程序对GPIO针脚进行写入操作的方式,程序要简洁明了很多,语句少了一大半。
下面是实物连接图和运行视频,我没有一位的数码管,所以使用4位数码管接最后一位来使用。
一位数码管的硬件连接,软件编程,示例到这里全部结束了。
下面需要思考的是4位数码管是如何实现:
如果我们4个数码管按照一位数码管方式来使用,那么需要4x8个GPIO针脚,arduino可没这么多针脚,而且这样使用也太浪费资源
因此4位数码管,所有灯管的阳极全部并联,每一位数码管使用一个公共阴极(共阴极是这样,共阳极的正好反过来)。
4个公共阴极(共阴极),将接入4个GPIO数字输出针脚,而不是接在GND针脚,我们通过选择性对这4个针脚写入高低电平,决定哪一位数码管点亮。
内部线路类似这样的:
这样就带来一个问题,4位数码管如果同时显示,只能显示同一个数字,如果要显示不同的4位数字,那么4个数字就不能同时点亮,我们平时看到的数码管显示可是同时显示4个数字的,这里边有一个技巧,就是利用了人的视觉暂留特性,让4个数码管依次显示不同数字,时间很短,然后快速的循环,由于视觉暂留特性,我们看到的就是4个同时显示的数字了。
如下视频:
4位数码管的程序,比一位数码管的程序要复杂一些,因为需要有选位显示,4位数字拆分给每一位数码管的语句等,有需要的留言
本篇总结:
数码管实质上还是LED灯的控制操作,利用GPIO针脚输出高低电平点亮和关闭LED灯。
对7个LED灯的点亮和关闭,对于8字型的LED灯条,能够显示不同的数字。
为了简化程序,要学会使用循环来完成简单重复的工作,要使用数组“图案”储存特定的LED点亮和关闭的次序,来达到快速简洁的查询和实现显示数字的功能。
实际上这一个方式,对于复杂的控制单元,比如需要控制一个3维运动机构,一个机器人的运动关节,需要同时控制很多个步进电机或者伺服电机运转来实现特定的肢体动作,都需要采用这种循环和“图案”方式来驱动。
一个复杂的系统,都是由一个个简单的环节组合而成的,学习简单的环节就是为了实现复杂的系统做准备,有了把复杂问题简化成一个个简单的环节的思维,才能处理解决复杂系统问题。
--end--
声明:本文章由网友投稿作为教育分享用途,如有侵权原作者可通过邮件及时和我们联系删除:freemanzk@qq.com