如何使用 Adafruit FadeCandy 来控制 LED
概述
图片来源:Adafruit
Adafruit 与 Scanlime 旗下 Micah 合作推出了一款内置抖动功能的驱动板 Fadecandy,可以通过 USB 进行控制。Fadecandy 包括了硬件和软件,让 WS2811/WS2812 可寻址 LED 艺术项目变得更容易构建和控制,项目外观更漂亮,对微控制器资源的消耗也更少。FadeCandy 对初学者来说很容易上手,对专业人士来说也是一种高级工具。
Fadecandy 服务器软件可以与一块或几十块 Fadecandy 板通信。它可以在 Windows、Linux、Mac OS 或 Raspberry Pi 等嵌入式平台上运行。开放像素控制 (OPC) 协议是一种将像素数据传送到 Fadecandy 服务器上的简单方法。每块控制器板最多支持 512 个 LED,排列成 8 条,每条 64 个,但不支持 RGBW LED。在本文发表时,只支持 RGB LED。本文后面将讨论基本编程。
当下这个项目在八个列中各使用了 52 个 Adafruit NeoPixels,旨在吸引玩家参加夹娃娃机游戏,同时也充当倒计时器使用。使用大量可寻址 LED 和直接连接到 GPIO 的传统 Arduino 库会拖累处理器,导致游戏功能的延迟以及执行时间问题。由于 Raspberry Pi 3 已经安排给夹娃娃机提供声音效果,与 FadeCandy 的照明通信将添加到 Pi 3 任务列表中。
确定电源要求
在使用 LED 灯条时,一个重要的考虑因素是耗电量。虽然每个颜色的 LED 只用 20 毫安的电流,但以 Adafruit NeoPixel 为例,如果三个 LED 都处于激活状态,那么每个 Adafruit NeoPixel 有三种颜色,总共 60 毫安。乘以每灯条上并联的 52 个 NeoPixels,每个灯条的最大电流将超过 3 安培。将这一电流乘以 8 列,潜在电流为 25 安培,这还不包括项目周围的各种小灯条。但是,为什么一个项目会让每个 NeoPixel 中的所有 LED 都同时长时间开启呢?答案是不会,所以电源的选择就成了一个猜谜游戏。假设 75% 的 NeoPixels 在任何时候都处于开启状态,并且每个都只显示一种颜色,那么电流就会下降到 6 安培左右。对照明方案进行编程,并用仪表测试电流,是确定的唯一方法。
考虑使用分线板来解决设计和布线问题
在开始编程之前,使用 KiCad 设计一个定制 PCB,将 FadeCandy 连接分接到方便的端子块上,以便于进行连接和输出布局。对于夹娃娃机来说,面向后方的 LED 在一个通道上,面向右方、面向左方和面向前方的 LED 也在一个通道上,以保留通道。上部和下部奖品滑道 LED 单独分接,留下两个通道用于辅助连接,以防万一。
分线板必须包含能够处理电源总线潜在电流的印制线。为电路板选择 2 盎司铜片也有助于处理更大的电流。图 1 和图 2 显示了使用 KiCad 和 DigiKey 的 PCB Builder 工具创建的 PCB 实例。
图 1.厂家的裸板。
图 2.大电流电源总线印制线。
PCB Builder 工具准备
一旦完成开发,可使用 KiCad 中 PCB 设计工具中 File 菜单下的 Plot 功能导出定制 PCB。在 Plot 对话框中,选择 Generate Drill File 按钮,将 Gerber 钻孔文件保存到您选择的文件夹中。接下来,选择 Plot 按钮。KiCad 将创建另外的 Gerber 文件,并将其放在与钻孔文件相同的文件夹中。使用 Windows 文件管理器,导航到包含 Gerber 文件的文件夹。选择所有与 PCB 有关的文件;然后右击选中的文件,选择发送到压缩 (zipped) 文件夹。在原文件夹中会出现一个新的压缩文件夹。
DigiKey 基于 Web 的 PCB Builder 工具有助于订购定制 PCB,选项多,供应商选择范围广。一旦启动 PCB Builder 工具,选择 Upload Gerber File(上传 Gerber 文件)按钮;然后导航并选择先前创建的压缩文件夹。一个 PCB Tool Viewer 窗口打开,显示电路板的图像和生产中要包括的文件/层的列表,如图 3 所示。
图 3.PCB Builder Viewer;使用 DigiKey 的 PCB Builder 工具的第一步。
PCB Builder Viewer 提供了许多工具来检查提供的 PCB。将光标悬停在 PCB 图像上并滚动鼠标滚轮即可放大和缩小图像,同时光标变为手形,可以在各个方向上移动 PCB。通过切换图层列表中每个图层的眼形图标,可以有选择地查看图层。
选择 Finish Upload(完成上传)按钮,进入下一订购步骤。下一个窗口显示了 PCB 的统计数据,以及可供选择的选项清单,如颜色和铜厚(见图 4)。注意,进行选择会改变价格和供应商供货量,同时他们可能不提供所选择的选项。从一块电路板的数量开始,然后根据需要进行其他选择。
图 4.选择 PCB 规格、供应商和数量。
当所有选择都做完了,并且确定了首选的供应商,然后将板子的数量增加 1,观察价格。重复这一步骤,直到价格增加。这种方法可确定能以最低价格生产的最大板子数量。准备好订购时,选择 Add to Cart(添加至购物车)按钮。
组装定制的分线板
完成的 PCB 上有 LED 灯条端子块、电源端子块和一个 16 针针座。Adafruit FadeCandy 板上有排针,并与 3D 打印的垫片一起插入 PCB 上的针座,以支持电路板的 USB 端。见图 5。
图 5.填充完整的定制分线板。
为了完成该板并开始编程,我们用一个由 8 列 26 个 Adafruit Neopixels 组成的测试平台来演示这个概念,后来在实际的夹娃娃机器中升级到 52 个 Neopixels。
将 LED 灯条连接到绿色的端子块上,检查电源和信号连接是否正确。将 5 V 电源连接到黑色的端子块上,检查电源和信号连接是否正确。图 6 显示了使用分线板之前的 NeoPixel 布线,图 7 显示了布线改进。
图 6.使用分线板前的试验台接线。
图 7。使用分线板后的试验台接线。
在硬件和接线就位后,使用适当的 USB 电缆将 Raspberry Pi 3 连接到 FadeCandy;然后将 Raspberry Pi 连接到显示器、键盘和鼠标。给系统通电,开始编程。FadeCandy 被设置成一个客户端,通过 USB 从作为服务器的 Pi 那里接收数据。在这个设置中,Pi 也通过 USB 的串行连接与 Arduino Mega 进行通信。Mega 处理游戏机的所有输入和输出,并简单地告诉 Pi 游戏是否正在进行中。Pi 处理声光效果。
FadeCandy 的功能和应用范围很广。许多简单和复杂的例子都可以在网上找到,并且有更多的实例不断加入。下面的代码展示了一些非常基本的多线程功能,以满足这个项目照明的具体需要。Pi 被编程为用基色淹没 Neopixels,然后增加随机的闪光来突现这个领域。当游戏进行时,其中两个灯条被转换为可视化的倒计时时钟。参见视频 1。本项目使用的代码见下(清单 1)。
副本#Raspberry Pi Game Machine Script
import serial
import threading
import queue
import random
import opc, time
import pygame
#Initialize the sound mixer
pygame.mixer.init(44100, 16, 2)
#Create a game start sound effect object
Start_Sound = pygame.mixer.Sound("glass.wav")
Start_Sound.set_volume(1.0)
Start_Sound.play()
#Create a tick-tock sound object
Tick_Sound = pygame.mixer.Sound("ticktock.wav")
Tick_Sound.set_volume(1.0)
#Tick_Sound.play(maxtime=600)
#Create an end of game sound object
End_Sound = pygame.mixer.Sound("Buzzer-sound-16.wav")
End_Sound.set_volume(1.0)
#End_Sound.play()
#Build queue objects for transfer between threads
game_q = queue.Queue(1)
users_q = queue.Queue(1)
matrix_q = queue.Queue(1)
#State the NeoPixel array for the testbed
numLEDs = 8*26
pixels = [ (0,0,0) ] * numLEDs
#Set FadeCandy meter start pixel
meterStartPix = 130
#Create a serial communication object for the Mega
serMega = serial.Serial('/dev/ttyACM0', 115200)
#Create a client object for the Open Pixel server
client = opc.Client('localhost:7890')
#Define a function for the t1 thread that reads data from the Mega
def MegaData():
while True:
if serMega.inWaiting() > 0:
GameDuration = int(serMega.readline())
PlayFlag = int(serMega.readline())
game_q.put((GameDuration, PlayFlag))
TotalUsers = int(serMega.readline())
if not users_q.full():
users_q.put(TotalUsers)
time.sleep(0.001)
#Define a function for the t2 thread which runs the time meter Neopixels
def RunMeter():
while True:
GameDuration, PlayFlag = game_q.get()
matrix_q.put(PlayFlag)
SleepNum = (float(GameDuration)/100/27)
if PlayFlag == 1:
#Quickly fill the meter with green
meterPix = meterStartPix
Start_Sound.play()
for i in range(0, 26):
pixels[meterPix] = (0, 200, 0)
client.put_pixels(pixels)
time.sleep(.02)
meterPix = meterPix+1
#Fill the meter with red based on game timer
meterPix = meterStartPix + 25
for i in range(0, 26):
if not game_q.empty():
GameDuration, PlayFlag = game_q.get()
if PlayFlag == 1:
pixels[meterPix] = (200, 0, 0)
Tick_Sound.play(maxtime=600)
client.put_pixels(pixels)
time.sleep(SleepNum)
meterPix = meterPix-1
else:
break
#Wait a tad bit
time.sleep(.50)
End_Sound.play()
time.sleep(.50)
#Quickly Clear the meter with soft white
meterPix = meterStartPix
for i in range(0, 26):
pixels[meterPix] = (30, 30, 30)
client.put_pixels(pixels)
time.sleep(.01)
meterPix = meterPix+1
time.sleep(2)
else:
#Quickly Clear the meter with soft white
meterPix = meterStartPix
for i in range(0, 26):
pixels[meterPix] = (30, 30, 30)
client.put_pixels(pixels)
time.sleep(.01)
meterPix = meterPix+1
time.sleep(2)
time.sleep(0.001)
#Define a function for the t3 thread that controls the non-meter Neopixels
def RunMatrix():
numLEDs = 6*26
while True:
if not matrix_q.empty():
play_flag = matrix_q.get()
if play_flag == 1:
numLEDs = 5*26
else:
numLEDs = 6*26
r = random.randint(25,85)
g = random.randint(25,85)
b = random.randint(25,85)
Bright = 3
DotNum = 10
for j in range(5):
for h in range(10):
pixels = [ (r, g, b) ] * numLEDs
for g in range(DotNum):
p = random.randint(0,numLEDs-1)
pixels[p] = (r*Bright, g*Bright, b*Bright)
client.put_pixels(pixels)
if not matrix_q.empty():
play_flag = matrix_q.get()
if play_flag == 1:
numLEDs = 5*26
else:
numLEDs = 6*26
time.sleep(.1)
#Create thread objects
t1 = threading.Thread(target = MegaData)
t2 = threading.Thread(target = RunMeter)
t3 = threading.Thread(target = RunMatrix)
t1.start()
t2.start()
t3.start()
清单 1.用于控制夹娃娃机项目的 LED 的代码。
结语
使用可寻址 LED 是一项有效但具挑战性的工作。通常情况下,完美视觉效果所需的代码会干扰微控制器的其他操作。FadeCandy 板以及其他类型的专用 LED 驱动器可用来缓解此类问题,为无尽的照明方案打开了方便之门。伴随着合适的驱动器的出现,定制 PCB 成为了组织输入、输出和分配电力的好方法。
Have questions or comments? Continue the conversation on TechForum, Digi-Key's online community and technical resource.
Visit TechForum