找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 3409|回复: 24
收起左侧

2个74HC595控制2个8x8点阵,显示3位数字

[复制链接]
ID:566214 发表于 2020-12-30 23:35 | 显示全部楼层 |阅读模式
想用2个74HC595控制2个8x8点阵,显示3位数字,但这3个数字要互不干扰,如图,显示2位数字会做,但是显示3位的就不知道怎么下手了,没有思路,就像控制8位数码管的小数点亮一下灭一下,而不影响其数字的显示一样,这个问题我也不知道怎么解决,求各位大佬给点思路,怎么写这个驱动程序,小弟不胜感激,谢谢各位。
屏幕截图 2020-12-30 232758.png
回复

使用道具 举报

ID:517466 发表于 2021-1-3 14:17 | 显示全部楼层
如果每个数字字符使用5*8的点阵表达(有一个空白列作为间隔),三个数字占用15列像素。两个595可以表达两个字节计16位,绝对是够用了。但这就要求你自己重新设计所有数字、字母的点阵数据了,以适应5*8的方式。你给的图片中1、2、3,使用了5*8的点阵方式.
我个人建议你建立4*7点阵模式,因为要考虑数字中间的空白间隔。使用5*8导致太紧凑,不好看。
5x7点阵数字.jpg

评分

参与人数 1黑币 +20 收起 理由
admin + 20 回帖助人的奖励!

查看全部评分

回复

使用道具 举报

ID:566214 发表于 2020-12-30 23:37 | 显示全部楼层
3位数字可自由控制
回复

使用道具 举报

ID:390416 发表于 2020-12-31 11:35 | 显示全部楼层
我很想知道,你两个595怎么驱动16*8点的?正反都驱动?
说说你的问题,你可以开辟一个16字节的数组,分别对应每一列。595就负责把每一列的字节显示到16*8点LED上去,这样,至于你显示什么图案,那就跟595无关了。你只需要往数组里面填充字节值就行了。
具体操作,建议你学习《人人学会单片机》视频教程,第13、14、15、16课程,就是这样操作的。
回复

使用道具 举报

ID:566214 发表于 2020-12-31 20:39 | 显示全部楼层
人人学会单片机 发表于 2020-12-31 11:35
我很想知道,你两个595怎么驱动16*8点的?正反都驱动?
说说你的问题,你可以开辟一个16字节的数组,分别 ...

行扫描我没有写出来,两个595是列扫描,如果2个595只显示2个数字,我会写驱动,但是2个595要显示3个数字,我没不知道怎么写了,请赐教
回复

使用道具 举报

ID:566214 发表于 2021-1-3 20:32 | 显示全部楼层
suncat0504 发表于 2021-1-3 14:17
如果每个数字字符使用5*8的点阵表达(有一个空白列作为间隔),三个数字占用15列像素。两个595可以表达两个 ...

2个595是16位,每次发8位,如果只显示2位数字,那好办,我不明白的是如何把16位原本显示两位数字的拆成显示3位数字,就是说我只能用一个595来控制显示1位数字,如果要让2个595来显示3位数,这个就不懂了,不知道怎么写这个驱动程序
回复

使用道具 举报

ID:517466 发表于 2021-1-4 22:04 | 显示全部楼层
本帖最后由 suncat0504 于 2021-1-5 18:41 编辑
dcjdcj 发表于 2021-1-3 20:32
2个595是16位,每次发8位,如果只显示2位数字,那好办,我不明白的是如何把16位原本显示两位数字的拆成显 ...

原理我已经和你说了。点阵显示数字字符,无非就是逐行逐列把像素点的数据传递给显示部件。
一个595对应一个字节,就是8个bit位。两个对应16个Bit位。当每个字符、数字占用一个字节8Bit位的时候,每个595一次只能显示一个一行8列的像素点,逐行扫描,视觉暂留现象,呈现出一个完整的字符。
当一个字符使用4x8方式显示的的时候。这个字符每一行占用4个像素点,共8行就能完整显示一个字符。那么两个595,就能同时显示4个字符了。实际使用,为了避免太紧凑,我加了一行空白行,就是全0的那行。数字之间间隔一个空白列,数字最后还有一个空白咧,就是在垂直方向上全都为0的那些列。
比如,使用我提供的点阵组合,对234这个数值的组合,我只用0和1来表达, 0表示不亮,1表示亮:
0 1234 5 6789 A BCDE F    // 这行是以16进制表示Bit位的编号
------------------------------
0 0000 0 0000 0 0000 0    // 第0行 从这行开始,表示的是像素数据,0-不亮;1-亮;这样是空白行。
0 0110 0 0110 0 0110 0    // 第1行
0 1001 0 1001 0 1010 0    // 第2行
0 0001 0 0001 0 1010 0    // 第3行
0 0010 0 0010 0 1010 0    // 第4行
0 0100 0 0001 0 1110 0    // 第5行
0 1000 0 1001 0 0010 0    // 第6行
0 1111 0 0110 0 0010 0    // 第7行
你注意看由1组成的图形是不是2、3、4?
按照这个点阵数据,你只需要把每行的数据拆分为两个字节,分别提供给595即可。
这样。为了显示234,把以下数据逐行提供给两个595
0x00 0x00    // 第0行,,对应000000000000
0x31 0x8C    // 第1行,对应0 0110 0 011 0 0 0110 0
0x4A 0x54    // 第2行 对应0 1001 0 1001 0 1010 0
0x08 0x54    // 第3行 对应0 0001 0 0001 0 1010 0
0x10 0x94    // 第4行 0 0010 0 0010 0 1010 0
0x20 0x5E    // 第5行 0 0100 0 0001 0 1110 0
0x42 0x44    // 第6行 0 1000 0 1001 0 0010 0
0x79 0x84    // 第7行 0 1111 0 0110 0 0010 0
那么,在16*8的点阵LED上现实的就是234数字了。其中第0、5、A、F列是空白列。

评分

参与人数 1黑币 +20 收起 理由
admin + 20 回帖助人的奖励!

查看全部评分

回复

使用道具 举报

ID:517466 发表于 2021-1-4 22:20 | 显示全部楼层
dcjdcj 发表于 2021-1-3 20:32
2个595是16位,每次发8位,如果只显示2位数字,那好办,我不明白的是如何把16位原本显示两位数字的拆成显 ...

自己组织点阵数据对应数字,需要预先把每个数字的点阵数据以0和1的方式建立出数组,对4列8行,每个数字张勇一个二维数组,比如对于数字2的点阵数据:
pix[2]={{0,0,0,0},
          {0,1,1,0},
          {1,0,0,1},
          {0,0,0,1},
          {0,0,1,0},
          {0,1,0,0},
          {1,0,0,0},
          {1,1,1,1}
         }
把所有数字弄到一起,组成一个三维数组,方便按照下标定位数字所在元素,组成字节数据。
当需要显示234时,
先取第一行像素数据,依次把0、pix[2]的第0行像素的四列,0,pix[3]的第0行像素的前两列,0,共计8Bit位,放到第一个595
再取pix[3]的第0行像素的后两列,0,pix[4]的第0行像素的四列,0,共计8Bit位,放到第二个595中,
这样234的第一行就都被设置好了;接着是其他各行的像素数据,也按照这种方式组织并显示,最终,“234”就被显示出来了。
回复

使用道具 举报

ID:517466 发表于 2021-1-4 22:24 | 显示全部楼层
实际上,pix也可以被定义为一位数组,而一位数组的每个元素是另一个二维数组(对应4 x 8的点阵数据)。这样处理,可能更容易一些。
回复

使用道具 举报

ID:517466 发表于 2021-1-4 22:43 | 显示全部楼层
本帖最后由 suncat0504 于 2021-1-5 18:42 编辑

图片说明更容易理解,第五行16进制数错了,是0x20, 0x5E
4x8点阵数字的扫描.jpg
回复

使用道具 举报

ID:517466 发表于 2021-1-4 23:20 | 显示全部楼层
本帖最后由 suncat0504 于 2021-1-4 23:26 编辑

如果不想用点阵模式保存,也可以用字节数据保存,比如
数字2的各行数值 pix[2] = {0, 6,9, 1, 2, 4, 8, 15}
数字3的各行数值 pix[3] = {0  6 , 9, 1, 2, 1, 9, 6 }
数字4的各行数值 pix[4] = {0, 6, 10, 10, 10, 15, 2, 2}
显示234的时候,第二行的点阵数据:
        第一个595 = (pix[2][1]<<3) | (pix[3][1]>>2) ;    // 6<<3 | 6>>2=49=0x31;
        第一个595 = (pix[3][1]<<6) | (pix[4][1]<<1);; // 6<<6 | 6<<1=140=0x8c
这样的效果是一样的。(注意,移位操作,是不带进位的移位)
回复

使用道具 举报

ID:566214 发表于 2021-1-5 22:00 | 显示全部楼层
suncat0504 发表于 2021-1-4 23:20
如果不想用点阵模式保存,也可以用字节数据保存,比如
数字2的各行数值 pix[2] = {0, 6,9, 1, 2, 4, 8, 1 ...

#include<reg52.h>
#include<intrins.h>
#define LED_HX P2
typedef unsigned int u16;        
typedef unsigned char u8;
bit Dis_Flag=0;                                    //扫描时间标志位
u8 DATA1,DATA2;                                //   595数据
u8 Duan_Xuan[8]={0x7f,0xbf,0xdf,0xef,0xf7,0xfb,0xfd,0xfe};//行扫描
u8 code Num_Table[3][8]=
{
        {0x00,0x0E,0x11,0x11,0x11,0x11,0x11,0x0E},        //     0
        {0x00,0x04,0x0C,0x04,0x04,0x04,0x04,0x0E},        //        1
//        {},        //  2
//        {},        //        3
//        {},        //        4
//        {},        //        5
//        {},        //        6
        {0x00,0x1F,0x01,0x02,0x04,0x04,0x04,0x04},        //        7
//        {},        //        8
//        {},        //        9
};

sbit SRCLK=P1^0;
sbit R_CLK=P1^1;
sbit SER=P1^2;

void Hc595SendByte()
{
        u8 i;
        SER=1;
        SRCLK=0;
        R_CLK=0;
                for(i=0;i<8;i++)
                {
                        SER=DATA1&0x80;
                        DATA1<<=1;
                        SRCLK=1;
                        _nop_();
                        _nop_();
                        SRCLK=0;        
                }

                for(i=0;i<8;i++)
                {
                        SER=DATA2&0x80;
                        DATA2<<=1;
                        SRCLK=1;
                        _nop_();
                        _nop_();
                        SRCLK=0;        
                }

                R_CLK=1;
                _nop_();
                _nop_();
                R_CLK=0;
}

void Duan_Dis()                //显示
{
        static u8 i=0;
        if(Dis_Flag)
        {
                Dis_Flag=0;
                LED_HX=Duan_Xuan[ i];     
                DATA1=Num_Table[0][ i];
                DATA2=Num_Table[2][ i];
                i++;
                i%=8;
        }
}

void Timer0Init(void)                //100us 22.1184.000MHz
{        
        TMOD &= 0xF0;               
        TMOD |= 0x02;               
        TL0 = 0x48;               
        TH0 = 0x48;               
        TF0 = 0;               
        TR0 = 1;               
        ET0=1;
        EA=1;
}

void main(void)
{
        Timer0Init();
        while(1)                        
        {
                Hc595SendByte();
                Duan_Dis();
        }
}

void Timer_Ser(void) interrupt 1
{
        static u8 i;
        i++;
        if(i>=5)
        {
                i=0;
                Dis_Flag=1;
        }
}


屏幕截图 2021-01-05 195407.png
回复

使用道具 举报

ID:566214 发表于 2021-1-5 22:12 | 显示全部楼层
suncat0504 发表于 2021-1-4 23:20
如果不想用点阵模式保存,也可以用字节数据保存,比如
数字2的各行数值 pix[2] = {0, 6,9, 1, 2, 4, 8, 1 ...

比如5*7的字体,16列可显示3个数字,这3个数字用变量去控制其显示的内容
回复

使用道具 举报

ID:517466 发表于 2021-1-6 10:06 | 显示全部楼层
dcjdcj 发表于 2021-1-5 22:12
比如5*7的字体,16列可显示3个数字,这3个数字用变量去控制其显示的内容

是啊,你想要的这个变量,不就是数组pix的以为元素的下标吗?
变量num1=2; 那么pix[num1]不就是pix[2]吗?编程要灵活一些。
回复

使用道具 举报

ID:566214 发表于 2021-1-6 22:18 | 显示全部楼层
suncat0504 发表于 2021-1-6 10:06
是啊,你想要的这个变量,不就是数组pix的以为元素的下标吗?
变量num1=2; 那么pix[num1]不就是pix[2]吗 ...

看了一些教程,说是可以开一个16字节缓存,再映射到每一列上,但是我这个是发一个字节是横向排列的,不改电路的情况下可以实现一列一列的扫描吗?如果这样,就可以控制每一列显示的内容了,就可以显示任意的内容了
回复

使用道具 举报

ID:566214 发表于 2021-1-6 23:57 | 显示全部楼层
suncat0504 发表于 2021-1-4 23:20
如果不想用点阵模式保存,也可以用字节数据保存,比如
数字2的各行数值 pix[2] = {0, 6,9, 1, 2, 4, 8, 1 ...

非常感谢您的耐心解答,这样是可以一个595显示两个字了,但是不能把1号595的数据移到2号上面,不能实现自由显示的目的

  1. u8 code Table1[10][8]=
  2. {
  3.         {0,6,9,9,9,9,9,6},                //0
  4.         {0,2,6,2,2,2,2,7},                //1        
  5.         {0,6,9,1,2,4,8,15},                //2
  6.         {0,6,9,1,6,1,9,6},                //3
  7.         {0,2,6,10,10,15,2,2},        //4
  8.         {0,15,8,14,1,1,9,6},        //5
  9.         {0,6,9,8,14,9,9,6},                //6
  10.         {0,15,9,1,2,2,4,4},                //7
  11.         {0,6,9,9,6,9,9,6},                //8
  12.         {0,6,9,9,7,1,2,4},                //9
  13. };
  14. void Duan_Dis()                //ÏÔê¾
  15. {
  16.         static u8 i=0;
  17.         if(Dis_Flag)
  18.         {
  19.                 Dis_Flag=0;
  20.                 LED_HX=Duan_Xuan[i];     
  21.                 DATA1=Table1[5][i]<<4|Table1[2][i];
  22.                 DATA2=Table1[9][i]<<4|Table1[0][i];
  23.                 i++;
  24.                 i%=8;
  25.         }
  26. }
复制代码
回复

使用道具 举报

ID:517466 发表于 2021-1-7 14:40 | 显示全部楼层
dcjdcj 发表于 2021-1-6 23:57
非常感谢您的耐心解答,这样是可以一个595显示两个字了,但是不能把1号595的数据移到2号上面,不能实现自 ...

显示原理通了,下一步就是怎么用程序用合理的逻辑实现的问题了,对吗?这不就是程序员要做到的事情吗?
回复

使用道具 举报

ID:566214 发表于 2021-1-7 18:44 | 显示全部楼层
suncat0504 发表于 2021-1-7 14:40
显示原理通了,下一步就是怎么用程序用合理的逻辑实现的问题了,对吗?这不就是程序员要做到的事情吗?

唉,刚入坑小白一枚,理论经验都不足
回复

使用道具 举报

ID:517466 发表于 2021-1-7 20:23 | 显示全部楼层
dcjdcj 发表于 2021-1-7 18:44
唉,刚入坑小白一枚,理论经验都不足

这是我刚刚测试过的程序,已经用proteus模拟过了。你可以试试。

595显示数字.zip

66.68 KB, 下载次数: 11

2个595显示3位数字

回复

使用道具 举报

ID:517466 发表于 2021-1-7 20:41 | 显示全部楼层
dcjdcj 发表于 2021-1-7 18:44
唉,刚入坑小白一枚,理论经验都不足

程序稍加改动,就可以变成驱动四位数字的。// 保存四位数字的数组变量
u8 v[4]={2,3,4,5};


void getValue(void) {
        v[0]=val/1000;
        v[1]=(val%1000)/100;
        v[2]=(val%100)/10;
    v[3]=val%10;
}

void main() {
    // 初始化595控制管脚
        DATA=0;
        CLK=0;
        LOCK=0;
        getValue(); // 获得显示数值
        while(1) {
                // 按键按下,累加数据
                if (Key==0) {
                        delayms(5); // 防抖
                        if (Key==0) {
                            while(Key==0); // 等待按键松开
                                val++; // 累加1
                                getValue(); // 获得显示数值
                        }
                }
                disp();
        }
}


disp函数中,计算行显示数据的地方:
        // 循环行
        for (row=0; row<8; row++) {
                // 根据显示数值,计算每行的点阵数据。一行两个字节,16Bit位对应16列
                // col_data[row][0]对应第一个595,col_data[row][1]对应第二个595.
                // 因为是级联,所以从col_data[row][1]到col_data[row][0],从Bit0到Bit7串行输出
                //col_data[row][0]=(Table[v[0]][row]<<3) | (Table[v[1]][row]>>2);
                //col_data[row][1]=(Table[v[1]][row]<<6) | (Table[v[2]][row]<<1);
                col_data[row][0]=(Table[v[0]][row]<<4) | (Table[v[1]][row]);
                col_data[row][1]=(Table[v[2]][row]<<4) | (Table[v[3]][row]);
        }

编译、执行proteus模拟后,就会看到显示结果。每按动一次按钮,显示数据+1.
回复

使用道具 举报

ID:517466 发表于 2021-1-7 20:45 | 显示全部楼层
把显示延时3毫秒改成1毫秒,proteus显示更稳定,不闪烁。

proteus模拟的截图

proteus模拟的截图
回复

使用道具 举报

ID:566214 发表于 2021-1-7 21:49 | 显示全部楼层
suncat0504 发表于 2021-1-7 20:41
程序稍加改动,就可以变成驱动四位数字的。// 保存四位数字的数组变量
u8 v[4]={2,3,4,5};

显示4位数字的已经实现了,但是太紧凑了,显示3位的问题是:如何将1号595的数据移一点在2号595上,这样就可以显示3位了,还可以每位数字之间间隔一列空的,但只能右移4次,超过的就移出去了,预想的是移到2号595上
屏幕截图 2021-01-07 213236.png
回复

使用道具 举报

ID:566214 发表于 2021-1-7 22:14 | 显示全部楼层
suncat0504 发表于 2021-1-7 20:41
程序稍加改动,就可以变成驱动四位数字的。// 保存四位数字的数组变量
u8 v[4]={2,3,4,5};

3位的我也懂了,非常谢谢你,还想请教一个问题:就有源蜂鸣器怎么实现检查到按键按下响,长按就一直响直到松手为止,这个问题也困扰了我好久,网上也没有找到解决的方法,有一个多月了,现在也没有想出怎么驱动,难道是我不适合这条路吗
回复

使用道具 举报

ID:517466 发表于 2021-1-8 09:03 | 显示全部楼层
dcjdcj 发表于 2021-1-7 22:14
3位的我也懂了,非常谢谢你,还想请教一个问题:就有源蜂鸣器怎么实现检查到按键按下响,长按就一直响直 ...

你下载的程序里,在主函数中不是有按键的监察处理吗?从按下到松开的整个过程,都有代码。你只要在防抖动检查的Key==0的判断之后,开启声音;在确认松开之后(while(Key==0);)架上停止声音的代码不就可以了吗?
if  (Key==0) {      // 检查按键是否按下
    delayms(20);    // 防抖的延时,防止因为异常干扰引起的误触发动作
    if  (Key==0) {  // 在防抖之后,再次确认按键是否真的被按下
        打开声音();         // 按键被按下,开始播放声音
        while(Kewy==0); // 直到按键松开后,才执行“关闭声音();”开始处的代码
        关闭声音();         // 按键已经被松开,停止i播放声音
        ...                      // 按键被按下后,执行对应的处理机能
    }
}
回复

使用道具 举报

ID:566214 发表于 2021-1-8 19:01 | 显示全部楼层
suncat0504 发表于 2021-1-8 09:03
你下载的程序里,在主函数中不是有按键的监察处理吗?从按下到松开的整个过程,都有代码。你只要在防抖动 ...

我学的按键检测是用状态机的方式实现的,网上说判断按键按下后,给一个标志位,蜂鸣器用npn管驱动,给高就响,低就不响,又不能像LED灯一样io口取反,给了1以后,多久给0,还是在按键松手后也给个标志位?
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表