Arduino 平台 - 使用移位寄存器
初学者入门指南:在 Arduino 中使用移位寄存器
引言
继我之前在 CodeProject 上撰写的 Arduino 文章之后,本文将快速介绍移位寄存器。为什么呢?嗯,本文是我之前在自己网站上撰写的两篇小文章的结合,我认为在这里分享这些文章会很好,这样可以扩大文章的覆盖范围,并进一步丰富 CodeProject 上的 Arduino 文章合集。我网站上的这些文章是所有文章中最受欢迎的两篇,根据 Google 的分析数据,这些都是关于该主题的直接搜索结果。
在本文中,我们将从单个移位寄存器开始,然后增加到双移位寄存器,接着看看如何进一步扩展。
什么是移位寄存器,为什么我们需要它们?
移位寄存器是一种集成电路,可用于将串行数据流更改为并行数据流。它们也可以用于做其他事情,例如串行到串行、并行到串行等,但这取决于您使用的移位寄存器的类型。
在本文中,我们将只关注串行到并行的实现。下图显示了最基本的表示,串行数据被时钟输入到芯片中,代表位被放置在输出端。输出端保持在适当的状态,直到它们被改变。
在 Arduino 平台上,每种不同类型的板,例如 Duemilanove/Uno 或 Mega,都有不同数量的输入和输出可用于连接现实世界。通过使用移位寄存器,可以扩展可用的输出。在以下示例中,您将看到我们如何首先仅用 3 个输出控制 8 个 LED,然后如何用相同的 3 个输出控制 16 个 LED。当然,您可以根据项目需要进行扩展。
根据您的项目性质,例如,一个使用大量 LED 的项目,您可能希望从一开始就使用移位寄存器来设计您的项目,这样在将来添加更多 LED 会容易得多,或者将其他 IO 引脚用于开关或电机控制器等设备。
本文使用的硬件是什么?
在本示例中,使用的硬件包括
- Arduino Duemilanove 板
- 一堆典型的 5V LED
- 用于限流的合适电阻
- 移位寄存器类型 74HC595N
与任何电子项目一样,重要的是要了解任何电子设备的限制,并且电路构建者有责任检查他们是否在设备的设计限制内进行构建。制造商和供应商之间的细微差异可能导致设备损坏,因此请务必检查您正在使用的具体产品。归根结底,只有您知道您正在使用什么,而本文等仅供参考。
从简单开始 - 单个移位寄存器
好的,让我们从单个移位寄存器开始,看看使用 Arduino 运行它需要什么。下图是初始示例的原理图。
从原理图中可以看到,我们正在 Arduino 板上获取 +5V 和接地连接,数字 IO 引脚 2、3、4 用于数据、锁存和时钟。移位寄存器还需要连接几根线来将复位拉高,并将使能拉低,以使这些示例正常工作。当然,您可以使用 Arduino 控制这些引脚,但对于这些简单示例,没有必要这样做。一个更复杂的项目可能希望完全控制 IC,但这将取决于电路开发人员。
对于编程,我们将使用可在 Arduino 主页[^] 上获得的入门版 Arduino IDE。
该项目的代码与任何其他 Arduino 项目没有什么不同,并遵循相同的初始化、设置和循环结构。代码中不需要包含或初始化任何特殊库,并且只需要标准方法即可使其工作。基本结构如下:
int dataPin = 2; //Define which pins will be used for the Shift Register control
int latchPin = 3;
int clockPin = 4;
//Add any other variables used by the main program loop here
void setup()
{
pinMode(dataPin, OUTPUT); //Configure each IO Pin
pinMode(latchPin, OUTPUT);
pinMode(clockPin, OUTPUT);
}
void loop()
{
//Main program loop as required to go here
}
示例 1 - 单个移位寄存器 - 二进制计数器
第一个示例将简单地创建一个二进制计数器,并在 LED 上显示其递增。
为此,我们必须首先在初始化部分添加一个变量来存储计数器,然后添加一个标准的 for
循环来在主程序循环方法中执行递增。由于这是第一个示例,我将展示完整的程序,以便您可以看到变量和循环需要添加到哪里。
int dataPin = 2; //Define which pins will be used for the Shift Register control
int latchPin = 3;
int clockPin = 4;
int counter = 0; //The counter for storing the byte value
void setup()
{
pinMode(dataPin, OUTPUT); //Configure each IO Pin
pinMode(latchPin, OUTPUT);
pinMode(clockPin, OUTPUT);
}
void loop()
{
for (counter = 0; counter < 256; counter++)
{
digitalWrite(latchPin, LOW); //Pull latch LOW to start sending data
shiftOut(dataPin, clockPin, MSBFIRST, counter); //Send the data
digitalWrite(latchPin, HIGH); //Pull latch HIGH to stop sending data
delay(500);
}
}
正如您在主程序循环中看到的,一个简单的 for
循环递增计数器字节。当我们准备将新的字节值发送到移位寄存器时,我们必须首先将 latchPin 拉低。然后我们将数据发送到移位寄存器(以正确的位方向)。数据发送完成后,我们再次将 latchPin 拉高,以表示我们已完成。延迟指令只是让我们控制我们将字节写入寄存器的速度,在这种情况下,我们在每次递增之间等待 500 毫秒。
示例 2 - 单个移位寄存器 - LED 扫描
任何涉及 LED 和移位寄存器的文章,如果没有包含可靠的《霹雳游侠》KITT 灯光扫描,都将是不完整的!
为此,我们首先需要设置一个字节数组来存储扫描中的每个值。如果您考虑正在发生的事情,我们所做的只是依次打开和关闭每个位,所以它将是 10000000、01000000、00100000、00010000 等等。这些值然后依次写入移位寄存器。
int dataPin = 2; //Define which pins will be used for the Shift Register control
int latchPin = 3;
int clockPin = 4;
int seq[14] = {1,2,4,8,16,32,64,128,64,32,16,8,4,2}; //The byte sequence
void setup()
{
pinMode(dataPin, OUTPUT); //Configure each IO Pin
pinMode(latchPin, OUTPUT);
pinMode(clockPin, OUTPUT);
}
void loop()
{
for (int n = 0; n < 14; n++)
{
digitalWrite(latchPin, LOW); //Pull latch LOW to start sending data
shiftOut(dataPin, clockPin, MSBFIRST, seq[n]); //Send the data
digitalWrite(latchPin, HIGH); //Pull latch HIGH to stop sending data
delay(75);
}
}
这样,每隔 75 毫秒,序列中的下一个字节就会写入输出。
继续 - 双移位寄存器
移位寄存器的一个很好的特性是它们有一个名为 Overflow 的引脚,这基本上允许我们向第一个移位寄存器写入比它能处理的更多的数据,并且它会将这些多余的数据溢出到这个引脚。因此,要扩展链条,我们只需将第一个移位寄存器的 Overflow 连接到第二个移位寄存器的数据引脚。这样,如果我们向第一个移位寄存器写入 2 个字节,第一个字节会传递给第二个移位寄存器,而第二个字节则保留在第一个移位寄存器中。下面的原理图显示了这种级联是如何连接的,我还添加了 3 个额外的 LED,以便我们可以监控 DATA、CLOCK 和 OVERFLOW 引脚。
对于双移位寄存器设置,代码几乎相同,唯一的区别是我们添加了第二个字节数组变量和额外的一行来写入第二个字节。为了演示这一点,我们将二进制计数器示例从 8 位扩展到 16 位。
示例 3 - 双移位寄存器 - 16 位二进制增量
int dataPin = 2; //Define which pins will be used for the Shift Register control
int latchPin = 3;
int clockPin = 4;
int byte1 = 0; //The counter for storing the byte #1 value
int byte2 = 0; //The counter for storing the byte #2 value
void setup()
{
pinMode(dataPin, OUTPUT); //Configure each IO Pin
pinMode(latchPin, OUTPUT);
pinMode(clockPin, OUTPUT);
}
void loop()
{
for (byte2 = 0; byte2 < 256; byte2++) //Outer Loop
{
for (byte1 = 0; byte1 < 256; byte1++) //Inner Loop
{
digitalWrite(latchPin, LOW); //Pull latch LOW to start sending data
shiftOut(dataPin, clockPin, MSBFIRST, byte1); //Send the data byte 1
shiftOut(dataPin, clockPin, MSBFIRST, byte2); //Send the data byte 2
digitalWrite(latchPin, HIGH); //Pull latch HIGH to stop sending data
delay(250);
}
}
}
示例 4 - 双移位寄存器 - KITT 回来了!
就像电视节目一样,KITT 重新出现,但新版本在车头有一个双扫光。为了实现这一点,我们需要两个数组来存储左右(字节 1 + 2)值,并再次添加一行来写入第二个字节。
int dataPin = 2; //Define which pins will be used for the Shift Register control
int latchPin = 3;
int clockPin = 4;
int seq1[14] = {1,2,4,8,16,32,64,128,64,32,16,8,4,2}; //The array for storing the
// byte #1 value
int seq2[14] = {128,64,32,16,8,4,2,1,2,4,8,16,32,64}; //The array for storing the
// byte #2 value
void setup()
{
pinMode(dataPin, OUTPUT); //Configure each IO Pin
pinMode(latchPin, OUTPUT);
pinMode(clockPin, OUTPUT);
}
void loop()
{
for (int x = 0; x < 14; x++) //Array Index
{
digitalWrite(latchPin, LOW); //Pull latch LOW to start sending data
shiftOut(dataPin, clockPin, MSBFIRST, seq1[x]); //Send the data byte 1
shiftOut(dataPin, clockPin, MSBFIRST, seq2[x]); //Send the data byte 2
digitalWrite(latchPin, HIGH); //Pull latch HIGH to stop sending data
delay(75);
}
}
进一步拓展 - 3 个寄存器及更多
正如您可能已经发现的,您可以通过简单地根据需要将 OVERFLOW 连接到 DATA 来继续向链中添加更多寄存器。然后您需要考虑如何控制和处理字节,并以正确的顺序将它们写入。当然,您需要优化代码以适应 Arduino 的限制,但是,嘿,也许有一天您甚至可以构建一个(不,不是我的)8x8x8 Arduino LED 立方体[^] 或更大的东西!我甚至可能有一天会尝试制作一个……
其他
访问我的其他文章或我的网站,获取更多 Arduino 相关资料。在 CodeProject 上搜索 Arduino,还有其他人发布的更多内容。哦,为什么不在 Youtube 上搜索 Arduino 呢,那里有很多你可以尝试的项目。无论如何,这会让你忙上一阵子!
参考文献
历史
- 2011 年 1 月 10 日 - 文章初版