标题: 2个74HC595控制2个8x8点阵,显示3位数字 [打印本页]
作者: dcjdcj 时间: 2020-12-30 23:35
标题: 2个74HC595控制2个8x8点阵,显示3位数字
想用2个74HC595控制2个8x8点阵,显示3位数字,但这3个数字要互不干扰,如图,显示2位数字会做,但是显示3位的就不知道怎么下手了,没有思路,就像控制8位数码管的小数点亮一下灭一下,而不影响其数字的显示一样,这个问题我也不知道怎么解决,求各位大佬给点思路,怎么写这个驱动程序,小弟不胜感激,谢谢各位。
-
屏幕截图 2020-12-30 232758.png
(17.6 KB, 下载次数: 87)
作者: dcjdcj 时间: 2020-12-30 23:37
3位数字可自由控制
作者: 人人学会单片机 时间: 2020-12-31 11:35
我很想知道,你两个595怎么驱动16*8点的?正反都驱动?
说说你的问题,你可以开辟一个16字节的数组,分别对应每一列。595就负责把每一列的字节显示到16*8点LED上去,这样,至于你显示什么图案,那就跟595无关了。你只需要往数组里面填充字节值就行了。
具体操作,建议你学习《人人学会单片机》视频教程,第13、14、15、16课程,就是这样操作的。
作者: dcjdcj 时间: 2020-12-31 20:39
行扫描我没有写出来,两个595是列扫描,如果2个595只显示2个数字,我会写驱动,但是2个595要显示3个数字,我没不知道怎么写了,请赐教
作者: suncat0504 时间: 2021-1-3 14:17
如果每个数字字符使用5*8的点阵表达(有一个空白列作为间隔),三个数字占用15列像素。两个595可以表达两个字节计16位,绝对是够用了。但这就要求你自己重新设计所有数字、字母的点阵数据了,以适应5*8的方式。你给的图片中1、2、3,使用了5*8的点阵方式.
我个人建议你建立4*7点阵模式,因为要考虑数字中间的空白间隔。使用5*8导致太紧凑,不好看。
-
5x7点阵数字.jpg
(209.23 KB, 下载次数: 94)
作者: dcjdcj 时间: 2021-1-3 20:32
2个595是16位,每次发8位,如果只显示2位数字,那好办,我不明白的是如何把16位原本显示两位数字的拆成显示3位数字,就是说我只能用一个595来控制显示1位数字,如果要让2个595来显示3位数,这个就不懂了,不知道怎么写这个驱动程序
作者: suncat0504 时间: 2021-1-4 22:04
本帖最后由 suncat0504 于 2021-1-5 18:41 编辑
原理我已经和你说了。点阵显示数字字符,无非就是逐行逐列把像素点的数据传递给显示部件。
一个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列是空白列。
作者: suncat0504 时间: 2021-1-4 22:20
自己组织点阵数据对应数字,需要预先把每个数字的点阵数据以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”就被显示出来了。
作者: suncat0504 时间: 2021-1-4 22:24
实际上,pix也可以被定义为一位数组,而一位数组的每个元素是另一个二维数组(对应4 x 8的点阵数据)。这样处理,可能更容易一些。
作者: suncat0504 时间: 2021-1-4 22:43
本帖最后由 suncat0504 于 2021-1-5 18:42 编辑
图片说明更容易理解,第五行16进制数错了,是0x20, 0x5E
-
4x8点阵数字的扫描.jpg
(191.17 KB, 下载次数: 81)
作者: suncat0504 时间: 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
这样的效果是一样的。(注意,移位操作,是不带进位的移位)
作者: dcjdcj 时间: 2021-1-5 22:00
#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
(59.34 KB, 下载次数: 82)
作者: dcjdcj 时间: 2021-1-5 22:12
比如5*7的字体,16列可显示3个数字,这3个数字用变量去控制其显示的内容
作者: suncat0504 时间: 2021-1-6 10:06
是啊,你想要的这个变量,不就是数组pix的以为元素的下标吗?
变量num1=2; 那么pix[num1]不就是pix[2]吗?编程要灵活一些。
作者: dcjdcj 时间: 2021-1-6 22:18
看了一些教程,说是可以开一个16字节缓存,再映射到每一列上,但是我这个是发一个字节是横向排列的,不改电路的情况下可以实现一列一列的扫描吗?如果这样,就可以控制每一列显示的内容了,就可以显示任意的内容了
作者: dcjdcj 时间: 2021-1-6 23:57
非常感谢您的耐心解答,这样是可以一个595显示两个字了,但是不能把1号595的数据移到2号上面,不能实现自由显示的目的
- u8 code Table1[10][8]=
- {
- {0,6,9,9,9,9,9,6}, //0
- {0,2,6,2,2,2,2,7}, //1
- {0,6,9,1,2,4,8,15}, //2
- {0,6,9,1,6,1,9,6}, //3
- {0,2,6,10,10,15,2,2}, //4
- {0,15,8,14,1,1,9,6}, //5
- {0,6,9,8,14,9,9,6}, //6
- {0,15,9,1,2,2,4,4}, //7
- {0,6,9,9,6,9,9,6}, //8
- {0,6,9,9,7,1,2,4}, //9
- };
- void Duan_Dis() //ÏÔê¾
- {
- static u8 i=0;
- if(Dis_Flag)
- {
- Dis_Flag=0;
- LED_HX=Duan_Xuan[i];
- DATA1=Table1[5][i]<<4|Table1[2][i];
- DATA2=Table1[9][i]<<4|Table1[0][i];
- i++;
- i%=8;
- }
- }
复制代码
作者: suncat0504 时间: 2021-1-7 14:40
显示原理通了,下一步就是怎么用程序用合理的逻辑实现的问题了,对吗?这不就是程序员要做到的事情吗?
作者: dcjdcj 时间: 2021-1-7 18:44
唉,刚入坑小白一枚,理论经验都不足
作者: suncat0504 时间: 2021-1-7 20:23
这是我刚刚测试过的程序,已经用proteus模拟过了。你可以试试。
-
-
595显示数字.zip
66.68 KB, 下载次数: 11
2个595显示3位数字
作者: suncat0504 时间: 2021-1-7 20:41
程序稍加改动,就可以变成驱动四位数字的。// 保存四位数字的数组变量
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.
作者: suncat0504 时间: 2021-1-7 20:45
把显示延时3毫秒改成1毫秒,proteus显示更稳定,不闪烁。
-
截图.jpg
(330.89 KB, 下载次数: 86)
proteus模拟的截图
作者: dcjdcj 时间: 2021-1-7 21:49
显示4位数字的已经实现了,但是太紧凑了,显示3位的问题是:如何将1号595的数据移一点在2号595上,这样就可以显示3位了,还可以每位数字之间间隔一列空的,但只能右移4次,超过的就移出去了,预想的是移到2号595上
-
屏幕截图 2021-01-07 213236.png
(21.47 KB, 下载次数: 78)
作者: dcjdcj 时间: 2021-1-7 22:14
3位的我也懂了,非常谢谢你,还想请教一个问题:就有源蜂鸣器怎么实现检查到按键按下响,长按就一直响直到松手为止,这个问题也困扰了我好久,网上也没有找到解决的方法,有一个多月了,现在也没有想出怎么驱动,难道是我不适合这条路吗
作者: suncat0504 时间: 2021-1-8 09:03
你下载的程序里,在主函数中不是有按键的监察处理吗?从按下到松开的整个过程,都有代码。你只要在防抖动检查的Key==0的判断之后,开启声音;在确认松开之后(while(Key==0);)架上停止声音的代码不就可以了吗?
if (Key==0) { // 检查按键是否按下
delayms(20); // 防抖的延时,防止因为异常干扰引起的误触发动作
if (Key==0) { // 在防抖之后,再次确认按键是否真的被按下
打开声音(); // 按键被按下,开始播放声音
while(Kewy==0); // 直到按键松开后,才执行“关闭声音();”开始处的代码
关闭声音(); // 按键已经被松开,停止i播放声音
... // 按键被按下后,执行对应的处理机能
}
}
作者: dcjdcj 时间: 2021-1-8 19:01
我学的按键检测是用状态机的方式实现的,网上说判断按键按下后,给一个标志位,蜂鸣器用npn管驱动,给高就响,低就不响,又不能像LED灯一样io口取反,给了1以后,多久给0,还是在按键松手后也给个标志位?
欢迎光临 (http://www.51hei.com/bbs/) |
Powered by Discuz! X3.1 |