一、8*8点阵结构
下面图片是8*8点阵内部结构原理图,一共有16根引脚,如果直接用8位单片机进行控制的话,需要占用单片机2个端口(如:P1,P2),实质上就是控制64个LED灯的亮灭,比如我想让左上角第一个LED灯亮起,其它灯全灭,那么需要让h1输出高电平,L1输出低电平就可以了。注意要将L2-L8保持在高电平,否则第一行的其它灯也会亮。
二、串并转换(74HC595)
上面这种控制方式虽然简单,但是占用单片机的IO口线过多,所以,我们经常会利用74HC595这样的串并转换芯片作为驱动芯片,一个595具有8个驱动输出端(QA-QH),那么我们这里需要2个595就够用了。通过第一片595的SQH管脚进行级联,将数据送至第二片595的数据输入端,这样单片机只需要3根线就可以实现对2片595的输出控制。
三、控制时序
对于编写595的驱动程序来说,看时序图是最直接了当的方式,手册上会说的比较多,简化来说,就是DATA_IN管脚用来输入数据(实际上就是高低电平),然后SHIFT-CLK管脚提供时钟,每当SHIFT-CLK的上升沿到来,595会读入DATA_IN管脚的电平状态,并存储在内部的锁存器中,当8个上升沿读取结束后,第一片595的8个数据已经全部读完,但是我们这里是级联的接法,所以还要继续读8个上升沿,然后数据从SQH管脚传给第二片595的DATA_IN管脚,到这时,16个上升沿的数据都读进595内部并存储起来了,注意此时还没有放到QA-QH这16个输出口线上,接下来需要LATCH-CLK给出一个下降沿,才将16个管脚的电平状态进行实际输出。
看下时许图,就可以一目了然:
四、程序编写(静态显示)
//先做宏定义,目的是给单片机的管脚起个别名,便于我们能够见名知义。共用到3个IO口。
//初始化时用到的端口
#define cBRDDOT_Port_Shift GPIO_P4
#define cBRDDOT_Pin_Shift GPIO_Pin_2
#define cBRDDOT_Port_DIn GPIO_P4
#define cBRDDOT_Pin_DIn GPIO_Pin_1
#define cBRDDOT_Port_Latch GPIO_P4
#define cBRDDOT_Pin_Latch GPIO_Pin_3
//定义让端口输出高低电平
#define cBRDDOT_Bit_Shift P42
#define cBRDDOT_Bit_DIn P41
#define cBRDDOT_Bit_Latch P43
//接下来做管脚的初始化,全部初始化为输出。
//设置Shift的管脚,处于输出模式
GPIO_SetMode(cBRDDOT_Port_Shift, cBRDDOT_Pin_Shift, GPIO_Mode_OUT_PP);
//设置DIn的管脚,处于输出模式
GPIO_SetMode(cBRDDOT_Port_DIn, cBRDDOT_Pin_DIn, GPIO_Mode_OUT_PP);
//设置Latch的管脚,处于输出模式
GPIO_SetMode(cBRDDOT_Port_Latch, cBRDDOT_Pin_Latch, GPIO_Mode_OUT_PP);
//接下来目标是点亮左上角第一个LED,下面是实现时序的代码
void DOT_ScanOut(void)
{
u8 V, i;
cBRDDOT_Bit_Latch = 1;//看时序图,latch管脚一开始为高电平,这里输出1
cBRDDOT_Bit_Shift = 0;//时钟shift-clk一开始为低电平
{
V = 0xfe;//这里是进行点阵的行选择,为什么是FE,11111110,最低位为0,目的是要选中第一行的8个LED,
for (i=0;i< 8;i++)
{
if (V & 0x80)//每次都将最高位取出来,1000000 & 8位二进制,只有最高位有效
cBRDDOT_Bit_DIn = 1;//如果最高位为高电平,则把DATA-IN输出高电平,这时候我们就把数据放上去了。
else
cBRDDOT_Bit_DIn = 0;//否则输出低电平
V < <= 1;//数据向左移一位,原来的次高位变为最高位,原来的最低为补0,直到8个位全部读完。
cBRDDOT_Bit_Shift = 0;
cBRDDOT_Bit_Shift = 1;//将SHIFT-CLK管脚电平由0变为1,上升沿产生,这时595会读入DATA-IN的电平状态并保存
cBRDDOT_Bit_Shift = 1;
cBRDDOT_Bit_Shift = 0;//恢复低电平
}
}
//上面的代码执行完,可以理解为我们已经选中了一行(第一行0XFE)
//接下来就是要把这一行要亮的灯点亮。
{
V = 0x80;//这里的逻辑是正的,1就代表亮,0代表灭。最左面的灯处于最高位位置。
for (i=0;i< 8;i++)
{
if (V & 0x80)
cBRDDOT_Bit_DIn = 1;
else
cBRDDOT_Bit_DIn = 0;
V < <= 1;
cBRDDOT_Bit_Shift = 0;
cBRDDOT_Bit_Shift = 1;
cBRDDOT_Bit_Shift = 1;
cBRDDOT_Bit_Shift = 0;
}
//8次循环结束,那么就将要这一行要显示的数据(要点亮的LED灯)也存到595内部了。
}
cBRDDOT_Bit_Latch = 0;//将LATCH-CLK拉成低电平,这时下降沿产生,595会把存储的数据真正给到输出口上,对应的LED会亮起。
cBRDDOT_Bit_Shift = 0;
}
五 、程序编写(动态扫描)****
//但是现在有个问题是,我们刚才的代码只能同时控制一行的LED亮灭,如果想同时控制8行来显示图案的话,就要用动态扫描的方式,从第1行到第8行快速切换,然后放入数据,形成视觉暂留的效果,让人眼误以为是同时在显示。
//也就是说,我们需要一个定时中断,每次中断过来我都要更新一行的数据。那么我们就要把上面的程序放到中断服务函数里面。
//先进行初始化设置,这里用的是timer2
Timer2_Init_AsTimer(False,0, 252, 112);
Timer2_EnaInterrupt();
Timer2_StartWork();
//全局变量
u8 code cBRDDOT_Colomn_Sel[8]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f};//每一行的模值
u8 xdata vBRDDOT_Values[8]={0x24,0x7e,0xff,0xff,0x7e,0x3e,0x1b,0x00};//心形图案
// 下面是中断服务程序
void BRDDOT_ScanOut(void) interrupt IntNo_Timer2
{
u8 V, i;
//这2行代码的目的是让vBRDDOT_ColIndex在0-7之间不断循环,
//比如当vBRDDOT_ColIndex为8时,二进制对应0000 10000,和0000 0111进行与运算,8变为0.
vBRDDOT_ColIndex++;
vBRDDOT_ColIndex &= 7;
cBRDDOT_Bit_Latch = 1;
cBRDDOT_Bit_Shift = 0;
//输出Col
{
//行选择,这里的V就不能是固定值了,因为要逐行显示,每一次进来会更新一行。
//需要在第1到第8行不断选择,所以cBRDDOT_Colomn_Sel数组里面应该放好进行每一行选择的IO模值
V = cBRDDOT_Colomn_Sel[vBRDDOT_ColIndex];
for (i=0;i< 8;i++)
{
if (V & 0x80)
cBRDDOT_Bit_DIn = 1;
else
cBRDDOT_Bit_DIn = 0;
V < <= 1;
cBRDDOT_Bit_Shift = 0;
cBRDDOT_Bit_Shift = 1;
cBRDDOT_Bit_Shift = 1;
cBRDDOT_Bit_Shift = 0;
}
}
{
//这里面vBRDDOT_Values要存储8行LED的显示内容,建议用取模软件去生成。
//每次中断进来都会换一行显示,因为vBRDDOT_ColIndex一直在变。
V = vBRDDOT_Values[vBRDDOT_ColIndex];
for (i=0;i< 8;i++)
{
if (V & 0x80)
cBRDDOT_Bit_DIn = 1;
else
cBRDDOT_Bit_DIn = 0;
V < <= 1;
cBRDDOT_Bit_Shift = 0;
cBRDDOT_Bit_Shift = 1;
cBRDDOT_Bit_Shift = 1;
cBRDDOT_Bit_Shift = 0;
}
}
cBRDDOT_Bit_Latch = 0;
cBRDDOT_Bit_Shift = 0;
}
全部0条评论
快来发表一下你的评论吧 !