成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Go語(yǔ)言在極小硬件上的運(yùn)用(三)

開發(fā) 后端
在本系列的 第一 和 第二 部分中討論的大多數(shù)示例都是以某種方式閃爍的 LED。起初它可能很有趣,但是一段時(shí)間后變得有些無(wú)聊。讓我們做些更有趣的事情……讓我們點(diǎn)亮更多的 LED!

Go語(yǔ)言在極小硬件上的運(yùn)用(三)

在本系列的 第一 和 第二 部分中討論的大多數(shù)示例都是以某種方式閃爍的 LED。起初它可能很有趣,但是一段時(shí)間后變得有些無(wú)聊。讓我們做些更有趣的事情……

…讓我們點(diǎn)亮更多的 LED!

 

STM32F030F4P6

WS281x LED

WS281x RGB LED(及其克隆品)非常受歡迎。你可以以單個(gè)元素購(gòu)買、鏈成長(zhǎng)條或組裝成矩陣、環(huán)或其他形狀。

 

WS2812B

它們可以串聯(lián)連接,基于這個(gè)事實(shí),你可以只用 MCU 的單個(gè)引腳就可以控制一個(gè)很長(zhǎng)的 LED 燈條。不幸的是,它們的內(nèi)部控制器使用的物理協(xié)議不能直接適用于你在 MCU 中可以找到的任何外圍設(shè)備。你必須使用 位脈沖bit-banging或以特殊方式使用可用的外設(shè)。

哪種可用的解決方案最有效取決于同時(shí)控制的 LED 燈條數(shù)量。如果你必須驅(qū)動(dòng) 4 到 16 個(gè)燈條,那么最有效的方法是 使用定時(shí)器和 DMA(請(qǐng)不要忽略這篇文章末尾的鏈接)。

如果只需要控制一個(gè)或兩個(gè)燈條,請(qǐng)使用可用的 SPI 或 UART 外設(shè)。對(duì)于 SPI,你只能在發(fā)送的一個(gè)字節(jié)中編碼兩個(gè) WS281x 位。由于巧妙地使用了起始位和停止位,UART 允許更密集的編碼:每發(fā)送一個(gè)字節(jié) 3 位。

我在 此站點(diǎn) 上找到了有關(guān) UART 協(xié)議如何適用于 WS281x 協(xié)議的最佳解釋。如果你不懂波蘭語(yǔ),這里是 英文翻譯

基于 WS281x 的 LED 仍然是最受歡迎的,但市場(chǎng)上也有 SPI 控制的 LED:APA102SK9822。關(guān)于它們的三篇有趣的文章在這里:123

LED 環(huán)

市場(chǎng)上有許多基于 WS2812 的環(huán)。我有一個(gè)這樣的:

 

WS2812B

它具有 24 個(gè)可單獨(dú)尋址的 RGB LED(WS2812B),并暴露出四個(gè)端子:GND、5V、DI 和 DO。通過將 DI(數(shù)據(jù)輸入)端子連接到上一個(gè)的 DO(數(shù)據(jù)輸出)端子,可以鏈接更多的環(huán)或其他基于 WS2812 的東西。

讓我們將這個(gè)環(huán)連接到我們的 STM32F030 板上。我們將使用基于 UART 的驅(qū)動(dòng)程序,因此 DI 應(yīng)連接到 UART 接頭連接器上的 TXD 引腳。 WS2812B LED 需要至少 3.5V 的電源。 24 個(gè) LED 會(huì)消耗大量電流,因此在編程/調(diào)試期間,最好將環(huán)上的 GND 和 5V 端子直接連接到 ST-LINK 編程器上可用的 GND 和 5V 引腳:

 

WS2812B

我們的 STM32F030F4P6 MCU 和整個(gè) STM32 F0、F3、F7、L4 系列具有 F1、F4、L1 MCU 不具備的一項(xiàng)重要功能:它可以反轉(zhuǎn) UART 信號(hào),因此我們可以將環(huán)直接連接到 UART TXD 引腳。如果你不知道我們需要這種反轉(zhuǎn),那么你可能沒有讀過我上面提到的 文章

因此,你不能以這種方式使用流行的 Blue Pill 或 STM32F4-DISCOVERY。使用其 SPI 外設(shè)或外部反相器。有關(guān)使用 SPI 的 NUCLEO-F411RE,請(qǐng)參見 圣誕樹燈 項(xiàng)目作為 UART + 逆變器的示例或 WS2812示例

順便說(shuō)一下,大多數(shù) DISCOVERY 板可能還有一個(gè)問題:它們?cè)?VDD = 3V 而不是 3.3V 的情況下工作。 對(duì)于高 DI,WS281x 至少要求電源電壓 * 0.7。如果是 5V 電源,則為 3.5V;如果是 4.7V 電源,則為 3.3V;可在 DISCOVERY 的 5V 引腳上找到。如你所見,即使在我們的情況下,第一個(gè) LED 的工作電壓也低于規(guī)格 0.2V。對(duì)于 DISCOVERY 板,如果供電 4.7V,它將工作在低于規(guī)格的 0.3V 下;如果供電 5V,它將工作在低于規(guī)格 0.5V 下。

讓我們結(jié)束這段冗長(zhǎng)的介紹并轉(zhuǎn)到代碼:

  1. package main
  2.  
  3. import (
  4. "delay"
  5. "math/rand"
  6. "rtos"
  7.  
  8. "led"
  9. "led/ws281x/wsuart"
  10.  
  11. "stm32/hal/dma"
  12. "stm32/hal/gpio"
  13. "stm32/hal/irq"
  14. "stm32/hal/system"
  15. "stm32/hal/system/timer/systick"
  16. "stm32/hal/usart"
  17. )
  18.  
  19. var tts *usart.Driver
  20.  
  21. func init() {
  22. system.SetupPLL(8, 1, 48/8)
  23. systick.Setup(2e6)
  24.  
  25. gpio.A.EnableClock(true)
  26. tx := gpio.A.Pin(9)
  27.  
  28. tx.Setup(&gpio.Config{Mode: gpio.Alt})
  29. tx.SetAltFunc(gpio.USART1_AF1)
  30.  
  31. d := dma.DMA1
  32. d.EnableClock(true)
  33.  
  34. tts = usart.NewDriver(usart.USART1, d.Channel(2, 0), nil, nil)
  35. tts.Periph().EnableClock(true)
  36. tts.Periph().SetBaudRate(3000000000 / 1390)
  37. tts.Periph().SetConf2(usart.TxInv)
  38. tts.Periph().Enable()
  39. tts.EnableTx()
  40.  
  41. rtos.IRQ(irq.USART1).Enable()
  42. rtos.IRQ(irq.DMA1_Channel2_3).Enable()
  43. }
  44.  
  45. func main() {
  46. var rnd rand.XorShift64
  47. rnd.Seed(1)
  48. rgb := wsuart.GRB
  49. strip := wsuart.Make(24)
  50. black := rgb.Pixel(0)
  51. for {
  52. c := led.Color(rnd.Uint32()).Scale(127)
  53. pixel := rgb.Pixel(c)
  54. for i := range strip {
  55. strip[i] = pixel
  56. tts.Write(strip.Bytes())
  57. delay.Millisec(40)
  58. }
  59. for i := range strip {
  60. strip[i] = black
  61. tts.Write(strip.Bytes())
  62. delay.Millisec(20)
  63. }
  64. }
  65. }
  66.  
  67. func ttsISR() {
  68. tts.ISR()
  69. }
  70.  
  71. func ttsDMAISR() {
  72. tts.TxDMAISR()
  73. }
  74.  
  75. //c:__attribute__((section(".ISRs")))
  76. var ISRs = [...]func(){
  77. irq.USART1: ttsISR,
  78. irq.DMA1_Channel2_3: ttsDMAISR,
  79. }

導(dǎo)入部分

與前面的示例相比,導(dǎo)入部分中的新內(nèi)容是 rand/math 包和帶有 led/ws281x 子樹的 led 包。 led 包本身包含 Color 類型的定義。 led/ws281x/wsuart 定義了 ColorOrderPixel 和 Strip 類型。

我想知道如何使用 image/color 中的 Color 或 RGBA 類型,以及如何以它將實(shí)現(xiàn) image.Image 接口的方式定義 Strip。 但是由于使用了 gamma 校正 和 大開銷的 color/draw 包,我以簡(jiǎn)單的方式結(jié)束:

  1. type Color uint32
  2. type Strip []Pixel

使用一些有用的方法。然而,這種情況在未來(lái)可能會(huì)改變。

init 函數(shù)

init 函數(shù)沒有太多新穎之處。 UART 波特率從 115200 更改為 3000000000/1390 ≈ 2158273,相當(dāng)于每個(gè) WS2812 位 1390 納秒。 CR2 寄存器中的 TxInv 位設(shè)置為反轉(zhuǎn) TXD 信號(hào)。

main 函數(shù)

XorShift64 偽隨機(jī)數(shù)生成器用于生成隨機(jī)顏色。 XORSHIFT 是目前由 math/rand 包實(shí)現(xiàn)的唯一算法。你必須使用帶有非零參數(shù)的 Seed 方法顯式初始化它。

rgb 變量的類型為 wsuart.ColorOrder,并設(shè)置為 WS2812 使用的 GRB 顏色順序(WS2811 使用 RGB 順序)。然后用于將顏色轉(zhuǎn)換為像素。

wsuart.Make(24) 創(chuàng)建 24 像素的初始化條帶。它等效于:

  1. strip := make(wsuart.Strip, 24)
  2. strip.Clear()

其余代碼使用隨機(jī)顏色繪制類似于 “Please Wait…” 微調(diào)器的內(nèi)容。

strip 切片充當(dāng)幀緩沖區(qū)。 tts.Write(strip.Bytes()) 將幀緩沖區(qū)的內(nèi)容發(fā)送到環(huán)。

中斷

該程序由處理中斷的代碼組成,與先前的 UART 示例 中的代碼相同。

讓我們編譯并運(yùn)行:

  1. $ egc
  2. $ arm-none-eabi-size cortexm0.elf
  3. text data bss dec hex filename
  4. 14088 240 204 14532 38c4 cortexm0.elf
  5. $ openocd -d0 -f interface/stlink.cfg -f target/stm32f0x.cfg -c 'init; program cortexm0.elf; reset run; exit'

我跳過了 openocd 的輸出。下面的視頻顯示了該程序的工作原理:

 

 

讓我們做些有用的事情...

在 第一部分 的開頭,我曾問過:“Go 能深入到多低層,而還能做一些有用的事情?”。 我們的 MCU 實(shí)際上是一種低端設(shè)備(8 比特的人可能會(huì)不同意我的看法),但到目前為止,我們還沒有做任何有用的事情。

所以... 讓我們做些有用的事情... 讓我們做個(gè)時(shí)鐘!

在互聯(lián)網(wǎng)上有許多由 RGB LED 構(gòu)成的時(shí)鐘示例。讓我們用我們的小板子和 RGB 環(huán)制作自己的時(shí)鐘。我們按照下面的描述更改先前的代碼。

導(dǎo)入部分

刪除 math/rand 包,然后添加 stm32/hal/exti

全局變量

添加兩個(gè)新的全局變量:btn 和 btnev

  1. var (
  2. tts *usart.Driver
  3. btn gpio.Pin
  4. btnev rtos.EventFlag
  5. )

它們將用來(lái)處理那些用于設(shè)置時(shí)鐘的 “按鈕”。我們的板子除了重置之外沒有其他按鈕,但是如果沒有它,我們?nèi)匀豢梢酝ㄟ^某種方式進(jìn)行管理。

init 函數(shù)

將這段代碼添加到 init 函數(shù):

  1. btn = gpio.A.Pin(4)
  2.  
  3. btn.Setup(&gpio.Config{Mode: gpio.In, Pull: gpio.PullUp})
  4. ei := exti.Lines(btn.Mask())
  5. ei.Connect(btn.Port())
  6. ei.EnableFallTrig()
  7. ei.EnableRiseTrig()
  8. ei.EnableIRQ()
  9.  
  10. rtos.IRQ(irq.EXTI4_15).Enable()

在內(nèi)部上拉電阻pull-up resistor啟用的情況下,將 PA4 引腳配置為輸入。它已連接至板載 LED,但這不會(huì)妨礙任何事情。更重要的是它位于 GND 引腳旁邊,所以我們可以使用任何金屬物體來(lái)模擬按鈕并設(shè)置時(shí)鐘。作為獎(jiǎng)勵(lì),我們還有來(lái)自板載 LED 的其他反饋。

我們使用 EXTI 外設(shè)來(lái)跟蹤 PA4 狀態(tài)。它被配置為在發(fā)生任何更改時(shí)都會(huì)產(chǎn)生中斷。

btnWait 函數(shù)

定義一個(gè)新的輔助函數(shù):

  1. func btnWait(state int, deadline int64) bool {
  2. for btn.Load() != state {
  3. if !btnev.Wait(1, deadline) {
  4. return false // timeout
  5. }
  6. btnev.Reset(0)
  7. }
  8. delay.Millisec(50) // debouncing
  9. return true
  10. }

它等待 “按鈕” 引腳上的指定狀態(tài),但只等到最后期限出現(xiàn)。這是稍微改進(jìn)的輪詢代碼:

  1. for btn.Load() != state {
  2. if rtos.Nanosec() >= deadline {
  3. // timeout
  4. }
  5. }

我們的 btnWait 函數(shù)不是忙于等待 state 或 deadline,而是使用 rtos.EventFlag 類型的 btnev 變量休眠,直到有事情發(fā)生。你當(dāng)然可以使用通道而不是 rtos.EventFlag,但是后者便宜得多。

main 函數(shù)

我們需要全新的 main 函數(shù):

  1. func main() {
  2. rgb := wsuart.GRB
  3. strip := wsuart.Make(24)
  4. ds := 4 * 60 / len(strip) // Interval between LEDs (quarter-seconds).
  5. adjust := 0
  6. adjspeed := ds
  7. for {
  8. qs := int(rtos.Nanosec() / 25e7) // Quarter-seconds since reset.
  9. qa := qs + adjust
  10.  
  11. qa %= 12 * 3600 * 4 // Quarter-seconds since 0:00 or 12:00.
  12. hi := len(strip) * qa / (12 * 3600 * 4)
  13.  
  14. qa %= 3600 * 4 // Quarter-seconds in the current hour.
  15. mi := len(strip) * qa / (3600 * 4)
  16.  
  17. qa %= 60 * 4 // Quarter-seconds in the current minute.
  18. si := len(strip) * qa / (60 * 4)
  19.  
  20. hc := led.Color(0x550000)
  21. mc := led.Color(0x005500)
  22. sc := led.Color(0x000055)
  23.  
  24. // Blend the colors if the hands of the clock overlap.
  25. if hi == mi {
  26. hc |= mc
  27. mc = hc
  28. }
  29. if mi == si {
  30. mc |= sc
  31. sc = mc
  32. }
  33. if si == hi {
  34. sc |= hc
  35. hc = sc
  36. }
  37.  
  38. // Draw the clock and write to the ring.
  39. strip.Clear()
  40. strip[hi] = rgb.Pixel(hc)
  41. strip[mi] = rgb.Pixel(mc)
  42. strip[si] = rgb.Pixel(sc)
  43. tts.Write(strip.Bytes())
  44.  
  45. // Sleep until the button pressed or the second hand should be moved.
  46. if btnWait(0, int64(qs+ds)*25e7) {
  47. adjust += adjspeed
  48. // Sleep until the button is released or timeout.
  49. if !btnWait(1, rtos.Nanosec()+100e6) {
  50. if adjspeed < 5*60*4 {
  51. adjspeed += 2 * ds
  52. }
  53. continue
  54. }
  55. adjspeed = ds
  56. }
  57. }
  58. }

我們使用 rtos.Nanosec 函數(shù)代替 time.Now 來(lái)獲取當(dāng)前時(shí)間。這樣可以節(jié)省大量的閃存,但也使我們的時(shí)鐘變成了不知道日、月、年的老式設(shè)備,最糟糕的是它無(wú)法處理夏令時(shí)的變化。

我們的環(huán)有 24 個(gè) LED,因此秒針的顯示精度可以達(dá)到 2.5 秒。為了不犧牲這種精度并獲得流暢的運(yùn)行效果,我們使用 1/4 秒作為基準(zhǔn)間隔。半秒就足夠了,但四分之一秒更準(zhǔn)確,而且與 16 和 48 個(gè) LED 配合使用也很好。

紅色、綠色和藍(lán)色分別用于時(shí)針、分針和秒針。這允許我們使用簡(jiǎn)單的“邏輯或操作”進(jìn)行顏色混合。我們 Color.Blend 方法可以混合任意顏色,但是我們閃存不多,所以我們選擇最簡(jiǎn)單的解決方案。

我們只有在秒針移動(dòng)時(shí)才重畫時(shí)鐘。

  1. btnWait(0, int64(qs+ds)*25e7)

上面的這行代碼等待的正是那一刻,或者是按鈕的按下。

每按一下按鈕就會(huì)把時(shí)鐘向前調(diào)一調(diào)。按住按鈕一段時(shí)間會(huì)加速調(diào)整。

中斷

定義新的中斷處理程序:

  1. func exti4_15ISR() {
  2. pending := exti.Pending() & 0xFFF0
  3. pending.ClearPending()
  4. if pending&exti.Lines(btn.Mask()) != 0 {
  5. btnev.Signal(1)
  6. }
  7. }

并將 irq.EXTI4_15: exti4_15ISR 條目添加到 ISR 數(shù)組。

該處理程序(或中斷服務(wù)程序)處理 EXTI4_15 IRQ。 Cortex-M0 CPU 支持的 IRQ 明顯少于其較大的同類兄弟處理器,因此你經(jīng)常可以看到一個(gè) IRQ 被多個(gè)中斷源共享。在我們的例子中,一個(gè) IRQ 由 12 個(gè) EXTI 線共享。

exti4_15ISR 讀取所有掛起的位,并從中選擇 12 個(gè)更高的有效位。接下來(lái),它清除 EXTI 中選中的位并開始處理它們。在我們的例子中,僅檢查第 4 位。 btnev.Signal(1) 引發(fā) btnev.Wait(1, deadline) 喚醒并返回 true

你可以在 Github 上找到完整的代碼。讓我們來(lái)編譯它:

  1. $ egc
  2. $ arm-none-eabi-size cortexm0.elf
  3. text data bss dec hex filename
  4. 15960 240 216 16416 4020 cortexm0.elf

這里所有的改進(jìn)只得到 184 個(gè)字節(jié)。讓我們?cè)俅沃匦聵?gòu)建所有內(nèi)容,但這次在 typeinfo 中不使用任何類型和字段名:

  1. $ cd $HOME/emgo
  2. $ ./clean.sh
  3. $ cd $HOME/firstemgo
  4. $ egc -nf -nt
  5. $ arm-none-eabi-size cortexm0.elf
  6. text data bss dec hex filename
  7. 15120 240 216 15576 3cd8 cortexm0.elf

現(xiàn)在,有了千字節(jié)的空閑空間,你可以改進(jìn)一些東西。讓我們看看它是如何工作的: 

 

我不知道我是怎么精確打到 3:00 的!?

以上就是所有內(nèi)容!在第 4 部分(本系列的結(jié)束)中,我們將嘗試在 LCD 上顯示一些內(nèi)容。(LCTT 譯注:然而爛尾了,第三篇寫于 2018 年,整個(gè)博客當(dāng)年就停更了。)

 

責(zé)任編輯:龐桂玉 來(lái)源: Linux中國(guó)
相關(guān)推薦

2019-09-25 17:46:13

Go語(yǔ)言編程語(yǔ)言

2020-10-25 06:30:48

Go語(yǔ)言編程語(yǔ)言

2020-10-24 18:00:28

Go語(yǔ)言編程語(yǔ)言

2020-08-12 08:51:19

Go語(yǔ)言Concurrency后臺(tái)

2024-05-10 08:36:40

Go語(yǔ)言對(duì)象

2014-04-09 09:32:24

Go并發(fā)

2023-12-13 07:19:01

微服務(wù)架構(gòu)Golang

2010-01-27 13:53:40

強(qiáng)大的CC++編譯器

2021-09-17 10:51:01

Linuxlspci命令

2012-10-08 09:25:59

GoGo語(yǔ)言開發(fā)語(yǔ)言

2013-07-30 09:27:32

Go云計(jì)算語(yǔ)言

2013-07-30 09:23:43

VMwareGoogle云平臺(tái)

2018-03-12 22:13:46

GO語(yǔ)言編程軟件

2012-05-04 11:28:41

虛擬化KVM高性能

2023-08-15 08:01:07

Go 語(yǔ)言排序

2012-11-21 11:48:23

i-NVMM加密密碼

2023-12-30 10:22:57

Go語(yǔ)言函數(shù)開發(fā)

2014-10-31 09:48:36

Go語(yǔ)言

2022-05-31 16:00:46

Go 編程語(yǔ)言復(fù)制文件Go 標(biāo)準(zhǔn)庫(kù)

2023-04-20 17:26:40

FreeDOSC 語(yǔ)言
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 国产成人精品综合 | 免费看91 | 国产高清在线观看 | 成人毛片网站 | 我想看国产一级毛片 | 久久高清| 99精品国自产在线 | 91精品综合久久久久久五月天 | 美国一级黄色片 | 国产精品国产a级 | 欧美a在线看 | 日韩精品久久久久 | 国产亚洲欧美另类一区二区三区 | 超碰97av| 国产女人精品视频 | 久久久久久久97 | 日韩av免费在线观看 | 国产欧美日韩一区二区三区在线观看 | 欧美高清视频 | 欧美日韩精品一区二区三区蜜桃 | 天天操夜夜操 | h视频网站在线观看 | 在线观看亚洲 | 国产清纯白嫩初高生在线播放视频 | 中文区中文字幕免费看 | 精品成人 | 日本精品一区二区 | 国产精品国产精品国产专区不卡 | 懂色中文一区二区在线播放 | 久久99精品久久久久久噜噜 | 亚洲欧美在线观看 | 成人网在线 | 日本精品在线一区 | 国产一区二区在线免费视频 | 亚洲精品美女视频 | 日韩网站在线观看 | aaaaaaa片毛片免费观看 | 欧日韩在线观看 | 日本一区二区三区免费观看 | 成人免费淫片aa视频免费 | 日韩av第一页|