自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

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

開發(fā) 后端
Go 語(yǔ)言,能在多低下的配置上運(yùn)行并發(fā)揮作用呢?我最近購(gòu)買了一個(gè)特別便宜的開發(fā)板,我購(gòu)買它的理由有三個(gè)。為了這篇文章,我選用了這一系列中最低配置的開發(fā)板,整件事情就變得有趣起來(lái)了。

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

Go 語(yǔ)言,能在多低下的配置上運(yùn)行并發(fā)揮作用呢?

我最近購(gòu)買了一個(gè)特別便宜的開發(fā)板:

 

STM32F030F4P6

我購(gòu)買它的理由有三個(gè)。首先,我(作為程序員)從未接觸過(guò) STM320 系列的開發(fā)板。其次,STM32F10x 系列使用也有點(diǎn)少了。STM320 系列的 MCU 很便宜,有更新一些的外設(shè),對(duì)系列產(chǎn)品進(jìn)行了改進(jìn),問(wèn)題修復(fù)也做得更好了。最后,為了這篇文章,我選用了這一系列中最低配置的開發(fā)板,整件事情就變得有趣起來(lái)了。

硬件部分

STM32F030F4P6 給人留下了很深的印象:

  • CPU: Cortex M0 48 MHz(最低配置,只有 12000 個(gè)邏輯門電路)
  • RAM: 4 KB,
  • Flash: 16 KB,
  • ADC、SPI、I2C、USART 和幾個(gè)定時(shí)器

以上這些采用了 TSSOP20 封裝。正如你所見,這是一個(gè)很小的 32 位系統(tǒng)。

軟件部分

如果你想知道如何在這塊開發(fā)板上使用 Go 編程,你需要反復(fù)閱讀硬件規(guī)范手冊(cè)。你必須面對(duì)這樣的真實(shí)情況:在 Go 編譯器中給 Cortex-M0 提供支持的可能性很小。而且,這還僅僅只是第一個(gè)要解決的問(wèn)題。

我會(huì)使用 Emgo,但別擔(dān)心,之后你會(huì)看到,它如何讓 Go 在如此小的系統(tǒng)上盡可能發(fā)揮作用。

在我拿到這塊開發(fā)板之前,對(duì) stm32/hal 系列下的 F0 MCU 沒(méi)有任何支持。在簡(jiǎn)單研究參考手冊(cè)后,我發(fā)現(xiàn) STM32F0 系列是 STM32F3 削減版,這讓在新端口上開發(fā)的工作變得容易了一些。

如果你想接著本文的步驟做下去,需要先安裝 Emgo

  1. cd $HOME
  2. git clone https://github.com/ziutek/emgo/
  3. cd emgo/egc
  4. go install

然后設(shè)置一下環(huán)境變量

  1. export EGCC=path_to_arm_gcc # eg. /usr/local/arm/bin/arm-none-eabi-gcc
  2. export EGLD=path_to_arm_linker # eg. /usr/local/arm/bin/arm-none-eabi-ld
  3. export EGAR=path_to_arm_archiver # eg. /usr/local/arm/bin/arm-none-eabi-ar
  4.  
  5. export EGROOT=$HOME/emgo/egroot
  6. export EGPATH=$HOME/emgo/egpath
  7.  
  8. export EGARCH=cortexm0
  9. export EGOS=noos
  10. export EGTARGET=f030x6

更詳細(xì)的說(shuō)明可以在 Emgo 官網(wǎng)上找到。

要確保 egc 在你的 PATH 中。 你可以使用 go build 來(lái)代替 go install,然后把 egc 復(fù)制到你的 $HOME/bin/usr/local/bin 中。

現(xiàn)在,為你的第一個(gè) Emgo 程序創(chuàng)建一個(gè)新文件夾,隨后把示例中鏈接器腳本復(fù)制過(guò)來(lái):

  1. mkdir $HOME/firstemgo
  2. cd $HOME/firstemgo
  3. cp $EGPATH/src/stm32/examples/f030-demo-board/blinky/script.ld .

最基本程序

main.go 文件中創(chuàng)建一個(gè)最基本的程序:

  1. package main
  2.  
  3. func main() {
  4. }

文件編譯沒(méi)有出現(xiàn)任何問(wèn)題:

  1. $ egc
  2. $ arm-none-eabi-size cortexm0.elf
  3. text data bss dec hex filename
  4. 7452 172 104 7728 1e30 cortexm0.elf

第一次編譯可能會(huì)花點(diǎn)時(shí)間。編譯后產(chǎn)生的二進(jìn)制占用了 7624 個(gè)字節(jié)的 Flash 空間(文本 + 數(shù)據(jù))。對(duì)于一個(gè)什么都沒(méi)做的程序來(lái)說(shuō),占用的空間有些大。還剩下 8760 字節(jié),可以用來(lái)做些有用的事。

不妨試試傳統(tǒng)的 “Hello, World!” 程序:

  1. package main
  2.  
  3. import "fmt"
  4.  
  5. func main() {
  6. fmt.Println("Hello, World!")
  7. }

不幸的是,這次結(jié)果有些糟糕:

  1. $ egc
  2. /usr/local/arm/bin/arm-none-eabi-ld: /home/michal/P/go/src/github.com/ziutek/emgo/egpath/src/stm32/examples/f030-demo-board/blog/cortexm0.elf section `.text' will not fit in region `Flash'
  3. /usr/local/arm/bin/arm-none-eabi-ld: region `Flash' overflowed by 10880 bytes
  4. exit status 1

“Hello, World!” 需要 STM32F030x6 上至少 32KB 的 Flash 空間。

fmt 包強(qiáng)制包含整個(gè) strconvreflect 包。這三個(gè)包,即使在精簡(jiǎn)版本中的 Emgo 中,占用空間也很大。我們不能使用這個(gè)例子了。有很多的應(yīng)用不需要好看的文本輸出。通常,一個(gè)或多個(gè) LED,或者七段數(shù)碼管顯示就足夠了。不過(guò),在第二部分,我會(huì)嘗試使用 strconv 包來(lái)格式化,并在 UART 上顯示一些數(shù)字和文本。

閃爍

我們的開發(fā)板上有一個(gè)與 PA4 引腳和 VCC 相連的 LED。這次我們的代碼稍稍長(zhǎng)了一些:

  1. package main
  2.  
  3. import (
  4. "delay"
  5.  
  6. "stm32/hal/gpio"
  7. "stm32/hal/system"
  8. "stm32/hal/system/timer/systick"
  9. )
  10.  
  11. var led gpio.Pin
  12.  
  13. func init() {
  14. system.SetupPLL(8, 1, 48/8)
  15. systick.Setup(2e6)
  16.  
  17. gpio.A.EnableClock(false)
  18. led = gpio.A.Pin(4)
  19.  
  20. cfg := &gpio.Config{Mode: gpio.Out, Driver: gpio.OpenDrain}
  21. led.Setup(cfg)
  22. }
  23.  
  24. func main() {
  25. for {
  26. led.Clear()
  27. delay.Millisec(100)
  28. led.Set()
  29. delay.Millisec(900)
  30. }
  31. }

按照慣例,init 函數(shù)用來(lái)初始化和配置外設(shè)。

system.SetupPLL(8, 1, 48/8) 用來(lái)配置 RCC,將外部的 8 MHz 振蕩器的 PLL 作為系統(tǒng)時(shí)鐘源。PLL 分頻器設(shè)置為 1,倍頻數(shù)設(shè)置為 48/8 =6,這樣系統(tǒng)時(shí)鐘頻率為 48MHz。

systick.Setup(2e6) 將 Cortex-M SYSTICK 時(shí)鐘作為系統(tǒng)時(shí)鐘,每隔 2e6 次納秒運(yùn)行一次(每秒鐘 500 次)。

gpio.A.EnableClock(false) 開啟了 GPIO A 口的時(shí)鐘。False 意味著這一時(shí)鐘在低功耗模式下會(huì)被禁用,但在 STM32F0 系列中并未實(shí)現(xiàn)這一功能。

led.Setup(cfg) 設(shè)置 PA4 引腳為開漏輸出。

led.Clear() 將 PA4 引腳設(shè)為低,在開漏設(shè)置中,打開 LED。

led.Set() 將 PA4 設(shè)為高電平狀態(tài),關(guān)掉LED。

編譯這個(gè)代碼:

  1. $ egc
  2. $ arm-none-eabi-size cortexm0.elf
  3. text data bss dec hex filename
  4. 9772 172 168 10112 2780 cortexm0.elf

正如你所看到的,這個(gè)閃爍程序占用了 2320 字節(jié),比最基本程序占用空間要大。還有 6440 字節(jié)的剩余空間。

看看代碼是否能運(yùn)行:

  1. $ openocd -d0 -f interface/stlink.cfg -f target/stm32f0x.cfg -c 'init; program cortexm0.elf; reset run; exit'
  2. Open On-Chip Debugger 0.10.0+dev-00319-g8f1f912a (2018-03-07-19:20)
  3. Licensed under GNU GPL v2
  4. For bug reports, read
  5. http://openocd.org/doc/doxygen/bugs.html
  6. debug_level: 0
  7. adapter speed: 1000 kHz
  8. adapter_nsrst_delay: 100
  9. none separate
  10. adapter speed: 950 kHz
  11. target halted due to debug-request, current mode: Thread
  12. xPSR: 0xc1000000 pc: 0x0800119c msp: 0x20000da0
  13. adapter speed: 4000 kHz
  14. ** Programming Started **
  15. auto erase enabled
  16. target halted due to breakpoint, current mode: Thread
  17. xPSR: 0x61000000 pc: 0x2000003a msp: 0x20000da0
  18. wrote 10240 bytes from file cortexm0.elf in 0.817425s (12.234 KiB/s)
  19. ** Programming Finished **
  20. adapter speed: 950 kHz

在這篇文章中,這是我第一次,將一個(gè)短視頻轉(zhuǎn)換成動(dòng)畫 PNG。我對(duì)此印象很深,再見了 YouTube。 對(duì)于 IE 用戶,我很抱歉,更多信息請(qǐng)看 apngasm。我本應(yīng)該學(xué)習(xí) HTML5,但現(xiàn)在,APNG 是我最喜歡的,用來(lái)播放循環(huán)短視頻的方法了。

 

STM32F030F4P6

更多的 Go 語(yǔ)言編程

如果你不是一個(gè) Go 程序員,但你已經(jīng)聽說(shuō)過(guò)一些關(guān)于 Go 語(yǔ)言的事情,你可能會(huì)說(shuō):“Go 語(yǔ)法很好,但跟 C 比起來(lái),并沒(méi)有明顯的提升。讓我看看 Go 語(yǔ)言的通道和協(xié)程!”

接下來(lái)我會(huì)一一展示:

  1. import (
  2. "delay"
  3.  
  4. "stm32/hal/gpio"
  5. "stm32/hal/system"
  6. "stm32/hal/system/timer/systick"
  7. )
  8.  
  9. var led1, led2 gpio.Pin
  10.  
  11. func init() {
  12. system.SetupPLL(8, 1, 48/8)
  13. systick.Setup(2e6)
  14.  
  15. gpio.A.EnableClock(false)
  16. led1 = gpio.A.Pin(4)
  17. led2 = gpio.A.Pin(5)
  18.  
  19. cfg := &gpio.Config{Mode: gpio.Out, Driver: gpio.OpenDrain}
  20. led1.Setup(cfg)
  21. led2.Setup(cfg)
  22. }
  23.  
  24. func blinky(led gpio.Pin, period int) {
  25. for {
  26. led.Clear()
  27. delay.Millisec(100)
  28. led.Set()
  29. delay.Millisec(period - 100)
  30. }
  31. }
  32.  
  33. func main() {
  34. go blinky(led1, 500)
  35. blinky(led2, 1000)
  36. }

代碼改動(dòng)很小: 添加了第二個(gè) LED,上一個(gè)例子中的 main 函數(shù)被重命名為 blinky 并且需要提供兩個(gè)參數(shù)。 main 在新的協(xié)程中先調(diào)用 blinky,所以兩個(gè) LED 燈在并行使用。值得一提的是,gpio.Pin 可以同時(shí)訪問(wèn)同一 GPIO 口的不同引腳。

Emgo 還有很多不足。其中之一就是你需要提前規(guī)定 goroutines(tasks) 的最大執(zhí)行數(shù)量。是時(shí)候修改 script.ld 了:

  1. ISRStack = 1024;
  2. MainStack = 1024;
  3. TaskStack = 1024;
  4. MaxTasks = 2;
  5.  
  6. INCLUDE stm32/f030x4
  7. INCLUDE stm32/loadflash
  8. INCLUDE noos-cortexm

棧的大小需要靠猜,現(xiàn)在還不用關(guān)心這一點(diǎn)。

  1. $ egc
  2. $ arm-none-eabi-size cortexm0.elf
  3. text data bss dec hex filename
  4. 10020 172 172 10364 287c cortexm0.elf

另一個(gè) LED 和協(xié)程一共占用了 248 字節(jié)的 Flash 空間。

 

STM32F030F4P6

通道

通道是 Go 語(yǔ)言中協(xié)程之間相互通信的一種推薦方式。Emgo 甚至能允許通過(guò)中斷處理來(lái)使用緩沖通道。下一個(gè)例子就展示了這種情況。

  1. package main
  2.  
  3. import (
  4. "delay"
  5. "rtos"
  6.  
  7. "stm32/hal/gpio"
  8. "stm32/hal/irq"
  9. "stm32/hal/system"
  10. "stm32/hal/system/timer/systick"
  11. "stm32/hal/tim"
  12. )
  13.  
  14. var (
  15. leds [3]gpio.Pin
  16. timer *tim.Periph
  17. ch = make(chan int, 1)
  18. )
  19.  
  20. func init() {
  21. system.SetupPLL(8, 1, 48/8)
  22. systick.Setup(2e6)
  23.  
  24. gpio.A.EnableClock(false)
  25. leds[0] = gpio.A.Pin(4)
  26. leds[1] = gpio.A.Pin(5)
  27. leds[2] = gpio.A.Pin(9)
  28.  
  29. cfg := &gpio.Config{Mode: gpio.Out, Driver: gpio.OpenDrain}
  30. for _, led := range leds {
  31. led.Set()
  32. led.Setup(cfg)
  33. }
  34.  
  35. timer = tim.TIM3
  36. pclk := timer.Bus().Clock()
  37. if pclk < system.AHB.Clock() {
  38. pclk *= 2
  39. }
  40. freq := uint(1e3) // Hz
  41. timer.EnableClock(true)
  42. timer.PSC.Store(tim.PSC(pclk/freq - 1))
  43. timer.ARR.Store(700) // ms
  44. timer.DIER.Store(tim.UIE)
  45. timer.CR1.Store(tim.CEN)
  46.  
  47. rtos.IRQ(irq.TIM3).Enable()
  48. }
  49.  
  50. func blinky(led gpio.Pin, period int) {
  51. for range ch {
  52. led.Clear()
  53. delay.Millisec(100)
  54. led.Set()
  55. delay.Millisec(period - 100)
  56. }
  57. }
  58.  
  59. func main() {
  60. go blinky(leds[1], 500)
  61. blinky(leds[2], 500)
  62. }
  63.  
  64. func timerISR() {
  65. timer.SR.Store(0)
  66. leds[0].Set()
  67. select {
  68. case ch <- 0:
  69. // Success
  70. default:
  71. leds[0].Clear()
  72. }
  73. }
  74.  
  75. //c:__attribute__((section(".ISRs")))
  76. var ISRs = [...]func(){
  77. irq.TIM3: timerISR,
  78. }

與之前例子相比較下的不同:

  1. 添加了第三個(gè) LED,并連接到 PA9 引腳(UART 頭的 TXD 引腳)。
  2. 時(shí)鐘(TIM3)作為中斷源。
  3. 新函數(shù) timerISR 用來(lái)處理 irq.TIM3 的中斷。
  4. 新增容量為 1 的緩沖通道是為了 timerISRblinky 協(xié)程之間的通信。
  5. ISRs 數(shù)組作為中斷向量表,是更大的異常向量表的一部分。
  6. blinky 中的 for 語(yǔ)句被替換成 range 語(yǔ)句。

為了方便起見,所有的 LED,或者說(shuō)它們的引腳,都被放在 leds 這個(gè)數(shù)組里。另外,所有引腳在被配置為輸出之前,都設(shè)置為一種已知的初始狀態(tài)(高電平狀態(tài))。

在這個(gè)例子里,我們想讓時(shí)鐘以 1 kHz 的頻率運(yùn)行。為了配置 TIM3 預(yù)分頻器,我們需要知道它的輸入時(shí)鐘頻率。通過(guò)參考手冊(cè)我們知道,輸入時(shí)鐘頻率在 APBCLK = AHBCLK 時(shí),與 APBCLK 相同,反之等于 2 倍的 APBCLK。

如果 CNT 寄存器增加 1 kHz,那么 ARR 寄存器的值等于更新事件(重載事件)在毫秒中的計(jì)數(shù)周期。 為了讓更新事件產(chǎn)生中斷,必須要設(shè)置 DIER 寄存器中的 UIE 位。CEN 位能啟動(dòng)時(shí)鐘。

時(shí)鐘外設(shè)在低功耗模式下必須啟用,為了自身能在 CPU 處于休眠時(shí)保持運(yùn)行: timer.EnableClock(true)。這在 STM32F0 中無(wú)關(guān)緊要,但對(duì)代碼可移植性卻十分重要。

timerISR 函數(shù)處理 irq.TIM3 的中斷請(qǐng)求。timer.SR.Store(0) 會(huì)清除 SR 寄存器里的所有事件標(biāo)志,無(wú)效化向 NVIC 發(fā)出的所有中斷請(qǐng)求。憑借經(jīng)驗(yàn),由于中斷請(qǐng)求無(wú)效的延時(shí)性,需要在程序一開始馬上清除所有的中斷標(biāo)志。這避免了無(wú)意間再次調(diào)用處理。為了確保萬(wàn)無(wú)一失,需要先清除標(biāo)志,再讀取,但是在我們的例子中,清除標(biāo)志就已經(jīng)足夠了。

下面的這幾行代碼:

  1. select {
  2. case ch <- 0:
  3. // Success
  4. default:
  5. leds[0].Clear()
  6. }

是 Go 語(yǔ)言中,如何在通道上非阻塞地發(fā)送消息的方法。中斷處理程序無(wú)法一直等待通道中的空余空間。如果通道已滿,則執(zhí)行 default,開發(fā)板上的LED就會(huì)開啟,直到下一次中斷。

ISRs 數(shù)組包含了中斷向量表。//c:__attribute__((section(".ISRs"))) 會(huì)導(dǎo)致鏈接器將數(shù)組插入到 .ISRs 節(jié)中。

blinkyfor 循環(huán)的新寫法:

  1. for range ch {
  2. led.Clear()
  3. delay.Millisec(100)
  4. led.Set()
  5. delay.Millisec(period - 100)
  6. }

等價(jià)于:

  1. for {
  2. _, ok := <-ch
  3. if !ok {
  4. break // Channel closed.
  5. }
  6. led.Clear()
  7. delay.Millisec(100)
  8. led.Set()
  9. delay.Millisec(period - 100)
  10. }

注意,在這個(gè)例子中,我們不在意通道中收到的值,我們只對(duì)其接受到的消息感興趣。我們可以在聲明時(shí),將通道元素類型中的 int 用空結(jié)構(gòu)體 struct{} 來(lái)代替,發(fā)送消息時(shí),用 struct{}{} 結(jié)構(gòu)體的值代替 0,但這部分對(duì)新手來(lái)說(shuō)可能會(huì)有些陌生。

讓我們來(lái)編譯一下代碼:

  1. $ egc
  2. $ arm-none-eabi-size cortexm0.elf
  3. text data bss dec hex filename
  4. 11096 228 188 11512 2cf8 cortexm0.elf

新的例子占用了 11324 字節(jié)的 Flash 空間,比上一個(gè)例子多占用了 1132 字節(jié)。

采用現(xiàn)在的時(shí)序,兩個(gè)閃爍協(xié)程從通道中獲取數(shù)據(jù)的速度,比 timerISR 發(fā)送數(shù)據(jù)的速度要快。所以它們?cè)谕瑫r(shí)等待新數(shù)據(jù),你還能觀察到 select 的隨機(jī)性,這也是 Go 規(guī)范所要求的。

 

STM32F030F4P6

開發(fā)板上的 LED 一直沒(méi)有亮起,說(shuō)明通道從未出現(xiàn)過(guò)溢出。

我們可以加快消息發(fā)送的速度,將 timer.ARR.Store(700) 改為 timer.ARR.Store(200)。 現(xiàn)在 timerISR 每秒鐘發(fā)送 5 條消息,但是兩個(gè)接收者加起來(lái),每秒也只能接受 4 條消息。

 

STM32F030F4P6

正如你所看到的,timerISR 開啟黃色 LED 燈,意味著通道上已經(jīng)沒(méi)有剩余空間了。

第一部分到這里就結(jié)束了。你應(yīng)該知道,這一部分并未展示 Go 中最重要的部分,接口。

協(xié)程和通道只是一些方便好用的語(yǔ)法。你可以用自己的代碼來(lái)替換它們,這并不容易,但也可以實(shí)現(xiàn)。接口是Go 語(yǔ)言的基礎(chǔ)。這是文章中 第二部分所要提到的.

在 Flash 上我們還有些剩余空間。 

責(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-11-03 08:31:53

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

2018-03-12 22:13:46

GO語(yǔ)言編程軟件

2012-11-21 11:48:23

i-NVMM加密密碼

2024-05-10 08:36:40

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

2012-11-08 09:36:10

Google Go

2023-12-13 07:19:01

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

2014-04-09 09:32:24

Go并發(fā)

2021-09-17 10:51:01

Linuxlspci命令

2020-10-12 08:03:51

Go語(yǔ)言編程

2010-01-27 13:53:40

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

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-19 17:40:10

Python區(qū)塊鏈

2022-03-27 23:11:39

Go語(yǔ)言函數(shù)

2012-05-04 11:28:41

虛擬化KVM高性能

2021-06-24 06:35:00

Go語(yǔ)言進(jìn)程

2021-04-15 12:10:42

Go語(yǔ)言Go開發(fā)者
點(diǎn)贊
收藏

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