讓我們做些更有趣的事情……讓我們點亮更多的 LED!
? 來源: ? 作者:Micha? ? 譯者: Gao ?
(本文字數:9841,閱讀時長大約:12 分鐘)
在本系列的 第一 和 第二 部分中討論的大多數示例都是以某種方式閃爍的 LED。起初它可能很有趣,但是一段時間后變得有些無聊。讓我們做些更有趣的事情……
…讓我們點亮更多的 LED!
LED
RGB LED(及其克隆品)非常受歡迎。你可以以單個元素購買、鏈成長條或組裝成矩陣、環或其他形狀。
它們可以串聯連接matlab數字信號發生器,基于這個事實,你可以只用 MCU 的單個引腳就可以控制一個很長的 LED 燈條。不幸的是matlab數字信號發生器,它們的內部控制器使用的物理協議不能直接適用于你在 MCU 中可以找到的任何外圍設備。你必須使用 位脈沖(bit-)或以特殊方式使用可用的外設。
哪種可用的解決方案最有效取決于同時控制的 LED 燈條數量。如果你必須驅動 4 到 16 個燈條,那么最有效的方法是 使用定時器和 DMA (請不要忽略這篇文章末尾的鏈接)。
如果只需要控制一個或兩個燈條,請使用可用的 SPI 或 UART 外設。對于 SPI,你只能在發送的一個字節中編碼兩個 位。由于巧妙地使用了起始位和停止位,UART 允許更密集的編碼:每發送一個字節 3 位。
我在 此站點 上找到了有關 UART 協議如何適用于 協議的最佳解釋。如果你不懂波蘭語,這里是 英文翻譯 。
基于 的 LED 仍然是最受歡迎的,但市場上也有 SPI 控制的 LED: 、 。關于它們的三篇有趣的文章在這里: 1 、 2 、 3 。
LED 環
市場上有許多基于 的環。我有一個這樣的:
它具有 24 個可單獨尋址的 RGB LED(),并暴露出四個端子:GND、5V、DI 和 DO。通過將 DI(數據輸入)端子連接到上一個的 DO(數據輸出)端子,可以鏈接更多的環或其他基于 的東西。
讓我們將這個環連接到我們的 板上。我們將使用基于 UART 的驅動程序,因此 DI 應連接到 UART 接頭連接器上的 TXD 引腳。 LED 需要至少 3.5V 的電源。 24 個 LED 會消耗大量電流,因此在編程/調試期間,最好將環上的 GND 和 5V 端子直接連接到 ST-LINK 編程器上可用的 GND 和 5V 引腳:
我們的 MCU 和整個 STM32 F0、F3、F7、L4 系列具有 F1、F4、L1 MCU 不具備的一項重要功能:它可以反轉 UART 信號,因此我們可以將環直接連接到 UART TXD 引腳。如果你不知道我們需要這種反轉,那么你可能沒有讀過我上面提到的 文章 。
因此,你不能以這種方式使用流行的 Blue Pill 或 - 。使用其 SPI 外設或外部反相器。有關使用 SPI 的 -,請參見 圣誕樹燈 項目作為 UART + 逆變器的示例或 示例 。
順便說一下,大多數 板可能還有一個問題:它們在 VDD = 3V 而不是 3.3V 的情況下工作。 對于高 DI, 至少要求電源電壓 * 0.7。如果是 5V 電源,則為 3.5V;如果是 4.7V 電源,則為 3.3V;可在 的 5V 引腳上找到。如你所見,即使在我們的情況下,第一個 LED 的工作電壓也低于規格 0.2V。對于 板,如果供電 4.7V,它將工作在低于規格的 0.3V 下;如果供電 5V,它將工作在低于規格 0.5V 下。
讓我們結束這段冗長的介紹并轉到代碼:
package main
import (
????"delay"
????"math/rand"
????"rtos"
????"led"
????"led/ws281x/wsuart"
????"stm32/hal/dma"
????"stm32/hal/gpio"
????"stm32/hal/irq"
????"stm32/hal/system"
????"stm32/hal/system/timer/systick"
????"stm32/hal/usart"
)
var tts *usart.Driver
func init() {
????system.SetupPLL(8, 1, 48/8)
????systick.Setup(2e6)
????gpio.A.EnableClock(true)
????tx := gpio.A.Pin(9)
????tx.Setup(&gpio.Config{Mode: gpio.Alt})
????tx.SetAltFunc(gpio.USART1_AF1)
????d := dma.DMA1
????d.EnableClock(true)
????tts = usart.NewDriver(usart.USART1, d.Channel(2, 0), nil, nil)
????tts.Periph().EnableClock(true)
????tts.Periph().SetBaudRate(3000000000 / 1390)
????tts.Periph().SetConf2(usart.TxInv)
????tts.Periph().Enable()
????tts.EnableTx()
????rtos.IRQ(irq.USART1).Enable()
????rtos.IRQ(irq.DMA1_Channel2_3).Enable()
}
func main() {
????var rnd rand.XorShift64
????rnd.Seed(1)
????rgb := wsuart.GRB
????strip := wsuart.Make(24)
????black := rgb.Pixel(0)
????for {
????????c := led.Color(rnd.Uint32()).Scale(127)
????????pixel := rgb.Pixel(c)
????????for i := range strip {
????????????strip[i] = pixel
????????????tts.Write(strip.Bytes())
????????????delay.Millisec(40)
????????}
????????for i := range strip {
????????????strip[i] = black
????????????tts.Write(strip.Bytes())
????????????delay.Millisec(20)
????????}
????}
}
func ttsISR() {
????tts.ISR()
}
func ttsDMAISR() {
????tts.TxDMAISR()
}
//c:__attribute__((section(".ISRs")))
var ISRs = [...]func(){
????irq.USART1:??????????ttsISR,
????irq.DMA1_Channel2_3: ttsDMAISR,
}
導入部分
與前面的示例相比,導入部分中的新內容是 rand/math 包和帶有 led/ 子樹的 led 包。 led 包本身包含 Color 類型的定義。 led// 定義了 、Pixel 和 Strip 類型。
我想知道如何使用 image/color 中的 Color 或 RGBA 類型,以及如何以它將實現 image.Image 接口的方式定義 Strip。 但是由于使用了 gamma 校正 和 大開銷的 color/draw 包,我以簡單的方式結束:
type Color uint32
type Strip []Pixel
使用一些有用的方法。然而,這種情況在未來可能會改變。
init 函數
init 函數沒有太多新穎之處。 UART 波特率從 更改為 /1390 ≈ ,相當于每個 位 1390 納秒。 CR2 寄存器中的 TxInv 位設置為反轉 TXD 信號。
main 函數
偽隨機數生成器用于生成隨機顏色。 是目前由 math/rand 包實現的唯一算法。你必須使用帶有非零參數的 Seed 方法顯式初始化它。
rgb 變量的類型為 .,并設置為 使用的 GRB 顏色順序( 使用 RGB 順序)。然后用于將顏色轉換為像素。
.Make(24) 創建 24 像素的初始化條帶。它等效于:
strip := make(wsuart.Strip, 24)
strip.Clear()
其余代碼使用隨機顏色繪制類似于 “ Wait…” 微調器的內容。
strip 切片充當幀緩沖區。 tts.Write(strip.Bytes()) 將幀緩沖區的內容發送到環。
中斷
該程序由處理中斷的代碼組成,與先前的 UART 示例 中的代碼相同。
讓我們編譯并運行:
$ egc
$ arm-none-eabi-size cortexm0.elf
?? text????data???? bss???? dec???? hex filename
??14088???? 240???? 204?? 14532????38c4 cortexm0.elf
$ openocd -d0 -f interface/stlink.cfg -f target/stm32f0x.cfg -c 'init; program cortexm0.elf; reset run; exit'
我跳過了 的輸出。下面的視頻顯示了該程序的工作原理: