该项目是一个实体的便携式 LED 标志,形状为 Counter-Strike : Global Offensive (CS:GO) 团队 Astralis 的标志。它主要基于 FastLED 库以及可编程的 WB2812B LED 灯条。一切都由 Arduino Nano 和便携式移动电源供电。
这旨在遍历项目的各个部分,例如:
标志的主要部分由木质刨花板制成,并按照 Astralis 标志的轮廓切割而成。切口的高度为 50 厘米,宽度为 40 厘米。手柄由 2、1 厘米 x 2、1 厘米的木梁组成,用胶带包裹,以提高长时间握持时的舒适度。
连接物理接口的控制箱由makercase.com 制作,它可以生成给定形状的切口 - 在这种情况下,是一个打开的盒子 - 具有不同的测量值,可以导出为 SVG 文件。然后将该文件导入 Adobe Illustrator,在其中添加最终细节,例如控件的文本雕刻和按钮的剪切。然后用激光切割机切割控制箱,最后用木胶粘在一起。请参阅图像文件夹下的图像。
用于使标志工作的组件如下:
该项目的核心是能够以不同类型的照明序列照亮标志本身。WS2812B 可寻址 LED 灯条有多种型号,尺寸、密封剂或LED 密度不同。对于这个项目,我使用了 60 个 LED 的密度。仪表不防水,因为它将在比赛期间在室内使用。条带用双面胶带直接粘在板上。它直接从为该项目中的所有组件供电的移动电源提供 5v 电压,以及一个 1000 μF 的电容器,以节省灯带上的第一个 LED 以应对潜在的电涌,从而将它们煎炸。你可以在这里阅读更多。
该显示器内部只有 8 个 LED。它被分成称为 a、b、c、d、e、f、g 和 DP 的段,并且可以以不同的组合点亮以表示阿拉伯数字和点 (DP)。此 GIF 显示了每个 LED 的寻址方式:
为了快速识别标志正在运行的照明顺序,可以相当快地读出数字,因为它位于背面的顺序按钮旁边。当序列循环时,数字将从“1”开始,表示序列已重置。作为故障保险,如果发生错误,显示器被编程为显示“E”。
显示的数字取决于通过 74HC595 移位寄存器发送多少位,这将在下一节中详细说明。
您可以在此链接上进一步阅读 7 段显示器以及其他用途。
通常,为了控制 7 段显示器中的每个 LED,需要将每个引脚连接到 Arduino。这样,如果您要使用多个数字引脚,您将很快用完引脚。这就是移位寄存器派上用场的地方。通过串联一个移位寄存器,我们只需使用 3 个 I/O 引脚就可以完成控制多达 8 个 LED 的任务。不仅如此;如果您想控制更多显示器,您可以将更多的移位寄存器链接在一起,从而节省更多的引脚。对于这个项目,一个数字就足够了。
595 有两个寄存器(可以认为是“内存容器”),每个寄存器只有 8 位数据。根据您发送到 595 的数据,数据将首先被存储,并且只有在 595 被锁存后才传输到输出引脚。
您可以在此详细指南中阅读有关 74HC595 的更深入说明。
标志本身的想法是它存储了多个照明序列,您可以通过一个简单的按钮在它们之间切换。主要思想是,通过单次按下,一旦按钮被按下和释放,标志应该进入下一个照明序列。按钮有多种尺寸和形状,但在发送 HIGH 或 LOW 信号方面,它们中的许多都有相同的问题。这通常被称为“弹跳”信号,其中信号在短时间内(通常为几毫秒)快速上下跳跃多次。这可以通过实施所谓的“去抖动”来解决。下图是一个按钮被按下和释放的例子,在稳定之前反弹信号。
当您物理按下普通按钮时,两块金属会相互接触。如果这两个小金属片不是完全平坦或完全对齐(而且它们不是),那么它们可以建立和断开几次接触,然后再牢固地压在一起以使其始终导电。对于微控制器来说,当您认为您只按下了一次按钮时,该按钮似乎被多次按下且持续时间极短。去抖动就是要确保您和微控制器就按钮按下或释放事件发生的时间达成一致。这就是在没有任何形式的去抖动的情况下注册按钮按下的方式。
最简单的硬件解决方案(如上图所示)是使用 10K 欧姆电阻和 1μF 电容去抖动。对于这个项目,去抖动功能至关重要,因为标志存储了多个照明序列。如果按钮在一个按钮中记录了多个“推动”,则序列将快速循环,从而导致错误的循环。
有关更详细的说明,您可以在此处进一步阅读。
在控制界面的最左侧,可以控制 LED 的亮度。这是通过使用电位计完成的,该电位计充当可变电阻器,通常用于控制电气设备,例如音量控制,或者在这种情况下是照明。但是,根据电位器的转动程度,电阻器只允许总电流的一部分,可以通过模拟信号读取。软件部分将进一步解释如何读取和使用该信号。
有关电位器如何工作的更详细说明,您可以在此处阅读更多信息。
运行 WS2812B LED 灯条的代码是在FastLED库的帮助下编写的。FastLED 库是一种简单快捷的方法,可以按照您希望的顺序点亮单个 LED,并生成照明模式。
灯光效果主要基于这个网站,展示了一些效果以及代码和设置。这些效果基于for
循环的概念,是运行这些效果的好方法。但是,该项目实现了添加一个按钮以在效果之间进行切换,并且此功能带有一个问题。循环“for
占用”正在运行的代码,并且仅在循环完成后继续。因此,如果您在for
循环运行时按下按钮,则推送不会注册。
正因为如此,为了通过使用全局变量来解决这个问题并减少使用的循环数量,已经重写了所使用的效果。for
下面显示的两个示例具有相同的效果,但以两种不同的方式编写。第一个是原始效果,在运行时没有注册按钮按下,而第二个示例这样做是因为它使用了全局变量,可以在函数外部使用theaterChase.
示例一:
void theaterChase(byte red, byte green, byte blue, int SpeedDelay) {
for (int j=0; j<10; j++) {
//do 10 cycles of chasing
for (int q=0; q < 3; q++) {
for (int i=0; i < NUM_LEDS; i=i+3) {
setPixel(i+q, red, green, blue);
//turn every third pixel on
}
showStrip();
delay(SpeedDelay);
for (int i=0; i < NUM_LEDS; i=i+3) {
setPixel(i+q, 0,0,0); //turn every third pixel off
}
}
}
}
示例二:
int theaterDistance = 3;
int theaterDelay = 300;
int theaterJ = 0;
int theaterI = 0;
void theaterChase(byte red, byte green, byte blue) {
if (theaterI == 0) {
for (int i=0; i < NUM_LEDS; i++) {
setPixel(i, 0,0,0);
}
for (int i=0; i < NUM_LEDS; i+=theaterDistance) {
setPixel(i+theaterJ, red, green, blue);
//turn every theaterDistance pixel on
}
showStrip();
theaterJ = (theaterJ + 1) % theaterDistance;
}
theaterI = (theaterI + 1) % theaterDelay;
}
如前所述,LED 的亮度通过电位器进行控制。信号最初被读取为 0 到 1023 之间的值。FastLED 库仅使用 0 到 255 之间的数字来定义亮度。然后需要用map
函数映射数字,如下所示,然后用变量MIN_BRIGHTNESS
和进行约束MAX_BRIGHTNESS
,用作限制器。
int brightnessValue = map(analogRead(brightnessPin), 0, 1023, 0, 255);
FastLED.setBrightness(constrain(brightnessValue, MIN_BRIGHTNESS, MAX_BRIGHTNESS));
正如硬件中的去抖部分所讨论的,按钮在按下和释放时往往会产生“噪音”。下面的代码会在短时间内检查两次,以确保确实按下了按钮。
按钮按下后加sequenceNumber
一,并运行与数字对应的照明功能。它还清除应该保留在 LED 中的所有数据,setAll(0, 0, 0);
并将正确的数字转移到 74HC595 以点亮 7 段显示器上的相应序列号。
// LED lightning sequence control
buttonState = digitalRead(buttonPin);
if (buttonState != lastButtonState) {
if (buttonState == HIGH) {
sequenceNumber = (sequenceNumber+1) % 5;
writeAndShift(sequenceNumber + 1);
setAll(0, 0, 0);
}
} // save the current state as the last state, for next time through theloop
lastButtonState = buttonState;
最后,Arduino 需要知道根据sequenceNumber
变量运行哪个照明序列。这是通过使用switch
和case
语句完成的。这些工作就像if
语句一样,但更好看。switch(sequenceNumber)
用于定义语句应该使用哪个声明case
。如果等于 1,则将运行,如果case 1:
等于2 ,依此类推。在 every 下,您定义应该执行的代码。sequenceNumber
case 2:
sequenceNumber
case
switch(sequenceNumber){
case 0:
FadeInOut(0xff, 0x00, 0x00); // Only using red
break;
default:
case
如果值超出给定语句,则用作故障保护。与该语句非常相似else
,如果上述语句均不成立,则您定义应该执行的内容。
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
全部0条评论
快来发表一下你的评论吧 !