Arudino–LED8*8点阵

之前已经用2个74HC595来控制16个LED灯的灭和亮,今天我们使用新的元器件,LED点阵显示。

用到的元器件:

2个74HC595移位寄存芯片:595

8个限流电阻:电阻

8*8LED点阵:88

电路连接,电路连接有点复杂,大家先要确定点阵是共阴还是共阳的,这个影响到后面的程序。大家千万要小心哦:

dianlu

为了让大家连接不出错,下面的表给出了移位寄存器与所连接的点阵显示器引脚对应关系:

移位寄存器1 移位寄存器2
行1 引脚15
行2 引脚1
行3 引脚2
行4 引脚3
行5 引脚4
行6 引脚5
行7 引脚6
行8 引脚7
列1 引脚15
列2 引脚1
列3 引脚2
列4 引脚3
列5 引脚4
列6 引脚5
列7 引脚6
列8 引脚7

根据上面的表和下面的原理图,例如,移位寄存器1的引脚15需要与点阵的第一行相连,因此要连到显示器的引脚9上。移位寄存器的引脚1需要与第二行相连,因此要连到点阵的14引脚上。

880

连接好以后就可以开始了。

先来测试一下连接是否有问题,因为我是采用的共阴极的,如果是共阳极的有些参数需要更改。因为本文是参考《Arduino从基础到实践》这本书的,但是在学习的过程中,有部分实验结果与书中的有出入,因此写出来,给有跟我一样的朋友参考,希望可以帮助到大家。

连接好以后,先检查一下是电路是不是连接的好,因为这个是重中之重,如果这个都没有确定的话,后面查错就很被动。我们先开始从第一排第一个开始亮,然后第二个,按顺序从头到尾点亮。代码如下:

int latchPin = 8;  //Arduino连接到74HC595(Latch)引脚12上的引脚 
int clockPin = 12; //Arduino连接到74HC595(Latch)引脚11上的引脚 
int dataPin = 11;  //Arduino连接到74HC595(Latch)引脚14上的引脚 

void setup() {
	
	pinMode(latchPin, OUTPUT);
	pinMode(clockPin, OUTPUT);
	pinMode(dataPin, OUTPUT);
}

void loop() {
	for (int i = 1; i < 255; i*=2) { int k = 1; for (int j = 254; j > 0; )
		{
			digitalWrite(latchPin, LOW); 
			shiftOut(dataPin, clockPin, MSBFIRST, j);
			shiftOut(dataPin, clockPin, MSBFIRST, i);
			digitalWrite(latchPin, HIGH);
			delay(250);
			j = j - k;
			k *= 2;
		}
	}
}

如果一切正常,那么恭喜你,连接正确,接下来我们开始做一些特殊的显示。

现在让我们想象一下,如果我们需要点亮如图所示的灯该怎么做呢?

0001

很明显,现在没有办法之点亮所需要的LED而不点亮不希望点亮的LED,因此我们需要使用一种多路复用技术。

多路复用是一种在同一时间只开一行显示的技术。选择包含你想点亮的LED的行和列给该行上电(或者可用于共阳极LED的其他方法),行中的LED将点亮。之后关闭该行,打开下一行,对所选择的列执行同样的操作,第二行中的LED将被点亮。重复以上操作,分别打开每一行直到最后一行。之后重新开始。

如果运行的足够快(大约100Hz,或者每秒100次),那么视觉暂留现象(已经消失的图像在视网膜上停留1/25s的现象)将静态的显示图像,尽管每行是按顺序交替开关的。

通过使用这项技术,我们点亮了单个LED而没有使在同一行或一列中的其他LED也被点亮。例如,要在显示器上显示如下图形:

aixin

那么每一行将像下图一样被点亮:

000001000002

通过非常快的速度(大于100Hz)向下扫描每一行,点亮这行相应的列中的LED,人类的眼睛将以静态的方式感知这个图像,因此在LED显示器上看到一个心形图像。

在《Arduino从基础到实践》这本书中,里面对应的代码并不是产生这样的效果,因此我在这里补充显示心形的代码如下:

#include <TimerOne.h> // 引入TimerOne.h这个库

int latchPin = 8; //Arduino连接到74HC595(Latch)引脚12上的引脚 
int clockPin = 12; //Arduino连接到74HC595(Latch)引脚11上的引脚
int dataPin = 11; //Arduino连接到74HC595(Latch)引脚14上的引脚 

byte led[8];  // 存储画面的8个元素无符号整型数组 

void setup() {
	
	pinMode(latchPin, OUTPUT);
	pinMode(clockPin, OUTPUT);
	pinMode(dataPin, OUTPUT);

	//输入二进制表示的图像
	led[0] = B10111011;
	led[1] = B00010001;
	led[2] = B00000001;
	led[3] = B00000001;
	led[4] = B00000001;
	led[5] = B10000011;
	led[6] = B11000111;
	led[7] = B11101111;

	// 设置定时长度为10000μs(1/100s)的定时器
	Timer1.initialize(10000);
        //连接定时中断服务程序到screenUpdate函数
	Timer1.attachInterrupt(screenUpdate);
}


void loop() {
	for (int i = 0; i<8; i++) {
		led[i] = ~led[i];  //取反,1变成0,0变成1
	}
	delay(500);
}

//显示图像的函数
void screenUpdate() {
	byte row = B10000000; // 第一行 
	for (byte k = 0; k < 8; k++) 
        { 
            digitalWrite(latchPin, LOW);  //将LatchPin设为低电平,准备接收数据
            shiftOut(dataPin, clockPin, LSBFIRST, led[k]); // 第一个595,用shiftOut函数输出数据 
            shiftOut(dataPin, clockPin, LSBFIRST, row); // 第二个595,输出行的数据 
            digitalWrite(latchPin, HIGH); // 
            row = row >> 1; //行右移一位。
	}
        //清屏
        digitalWrite(latchPin, LOW);
	shiftOut(dataPin, clockPin, LSBFIRST, 0);
	shiftOut(dataPin, clockPin, LSBFIRST, 0);	
	digitalWrite(latchPin, HIGH);
}

接下来我们来解释一下代码代表的含义:
这个项目代码使用了Atmega芯片的一个叫硬件时钟中断的功能。它是一个芯片上自带的计时器,可用于出发一个事件。在这个例子里设置每10ms触发一次ISR(中断服务程序),即每秒触发100次ISR。
该项目使用了一个叫做TimerOne的函数库,它简化了使用中断的过程。TimerOne使产生中断服务程序十分容易。只要简单地告诉函数中断间隔时间(此处是10ms)和当中断触发时你希望激活的函数的名称(在这个例子中,它是screenUpdate())即可。
TimerOne是一个外部库,因此需要加载到你编写的代码中,这需要使用include命令:
#include <TimerOne.h>

之后定义而精致形式的图像

	led[0] = B10111011;
	led[1] = B00010001;
	led[2] = B00000001;
	led[3] = B00000001;
	led[4] = B00000001;
	led[5] = B10000011;
	led[6] = B11000111;
	led[7] = B11101111;

通过观察以上数组可以看出要显示的图像是什么样子的。0的位置对应的是LED点亮(共阴和共阳有区别的)。如果没有达到你的目标,你可以通过调整1和0来实现自己想要的图像。

随后,使用了Timer1类。首先,这个类需要初始化激活它的频率。在这个例子里,所设置的周期是10000μs,即1/100s。一旦中断被初始化,需要连接这个中断到一个函数。当每个中断到来之时,执行这个函数。在这里连接的是screenUpdate()函数,所以该函数将每1/100s执行一次:

	// 设置定时长度为10000μs(1/100s)的定时器
	Timer1.initialize(10000);
        //连接定时中断服务程序到screenUpdate函数
	Timer1.attachInterrupt(screenUpdate);

在主循环中,通过for循环逐个访问LED数组中的8个元素,并用~或按位非操作符转换8个元素的内容。通过将所有1转换成0或将0转换成1将二进制图像翻转。等待500ms之后重复运行。

	for (int i = 0; i<8; i++) {
		led[i] = ~led[i];  //取反,1变成0,0变成1
	}
	delay(500);

接下来是screenUpdate()函数。这个函数每1/100s被中断激活一次。这个函数是非常重要的,因为它的任务是保证点阵数组中的LED显示正确。这是一个非常简单但是有效的函数。

void screenUpdate() {
	byte row = B10000000; // 第一行 
	for (byte k = 0; k < 8; k++) 
        { 
            digitalWrite(latchPin, LOW);  //将LatchPin设为低电平,准备接收数据
            shiftOut(dataPin, clockPin, LSBFIRST, led[k]); // 第一个595,用shiftOut函数输出数据 
            shiftOut(dataPin, clockPin, LSBFIRST, row); // 第二个595,输出行的数据 
            digitalWrite(latchPin, HIGH); // 
            row = row >> 1; //按位右移一位。
	}
        //清屏
        digitalWrite(latchPin, LOW);
	shiftOut(dataPin, clockPin, LSBFIRST, 0);
	shiftOut(dataPin, clockPin, LSBFIRST, 0);	
	digitalWrite(latchPin, HIGH);
}

声明一个叫row的字节变量并且用值B10000000初始化:

byte row = B10000000; // 第一行

现在从led数组开始循环,将数据送到移位寄存器,一旦送出当前行的8个比特,行中比特值转换到下一个位置。

读懂上面的项目,我们可以做本书中的源代码中的项目了。

下面我们就用点阵显示“回”字,代码如下:

#include  <TimerOne.h>

int latchPin = 8; //Pin connected to Pin 12 of 74HC595 (Latch) 
int clockPin = 12; //Pin connected to Pin 11 of 74HC595 (Clock) 
int dataPin = 11; //Pin connected to Pin 14 of 74HC595 (Data) 

byte led[8];  // 8 element unsigned integer array to hold the sprite 

void setup() {
	pinMode(latchPin, OUTPUT);
	pinMode(clockPin, OUTPUT);
	pinMode(dataPin, OUTPUT);
	led[0] = B00000000;
	led[1] = B01111110;
	led[2] = B01000010;
	led[3] = B01011010;
	led[4] = B01011010;
	led[5] = B01000010;
	led[6] = B01111110;
	led[7] = B00000000;

	Timer1.initialize(10000);
	Timer1.attachInterrupt(screenUpdate);
}

// invert each row of the binary image and wait 1/4 second 
void loop() {

	for (int i = 0; i<8; i++) {
		led[i] = ~led[i];
	}
	delay(500);
}

// Display the image 
void screenUpdate() {
	byte row = B10000000; // row 1 
	for (byte k = 0; k < 8; k++) 
        { 
            digitalWrite(latchPin, LOW); 
            shiftOut(dataPin, clockPin, LSBFIRST, led[k]); 
            shiftOut(dataPin, clockPin, LSBFIRST, row);  
            digitalWrite(latchPin, HIGH); 
            row = row >> 1;
	}
	shiftOut(dataPin, clockPin, LSBFIRST, 0);
	shiftOut(dataPin, clockPin, LSBFIRST, 0);
	digitalWrite(latchPin, LOW);
	digitalWrite(latchPin, HIGH);
}


上面的一些说明就不赘述了,大家应该能看懂的吧。要实现“回”字,还有其他方式如下代码:

int latchPin = 8; //Pin connected to Pin 12 of 74HC595 (Latch) 
int clockPin = 12; //Pin connected to Pin 11 of 74HC595 (Clock) 
int dataPin = 11; //Pin connected to Pin 14 of 74HC595 (Data) 

byte led[8];  // 8 element unsigned integer array to hold the sprite 

void setup() {
	// set the 3 digital pins to outputs
	pinMode(latchPin, OUTPUT);
	pinMode(clockPin, OUTPUT);
	pinMode(dataPin, OUTPUT);
	// Load the binary representation of the image into the array 
	led[0] = B00000000;
	led[1] = B01111110;
	led[2] = B01000010;
	led[3] = B01011010;
	led[4] = B01011010;
	led[5] = B01000010;
	led[6] = B01111110;
	led[7] = B00000000;

}

// invert each row of the binary image and wait 1/4 second 
void loop() {
	byte row = B10000000;
	for (int i = 0; i<8; i++) 
        { 
            digitalWrite(latchPin, LOW); // Turn all rows off until next interrupt               
            shiftOut(dataPin, clockPin, MSBFIRST, led[i]); 
            shiftOut(dataPin, clockPin, LSBFIRST, row); 
            digitalWrite(latchPin, HIGH);  
            row=row >> 1;
            delayMicroseconds(55);
	}
	
}
0 Comments
Leave a Reply