Go语言在极小硬件上的运用

io.Writer

STM32F030F4P6

并发 Blinky – 回顾

SetClearOnOffLED
  1. type LED struct {
  2. pin gpio.Pin
  3. }
  4.  
  5. func (led LED) On() {
  6. led.pin.Clear()
  7. }
  8.  
  9. func (led LED) Off() {
  10. led.pin.Set()
  11. }
led.On()led.Off()

在前面的所有示例中,我都尝试使用相同的 漏极开路配置open-drain configuration来避免代码复杂化。但是在最后一个示例中,对于我来说,将第三个 LED 连接到 GND 和 PA3 引脚之间并将 PA3 配置为推挽模式push-pull mode会更容易。下一个示例将使用以此方式连接的 LED。

LEDOpenDrainLEDPushPullLED
  1. type PushPullLED struct {
  2. pin gpio.Pin
  3. }
  4.  
  5. func (led PushPullLED) On() {
  6. led.pin.Set()
  7. }
  8.  
  9. func (led PushPullLED) Off() {
  10. led.pin.Clear()
  11. }

请注意,这两种类型都具有相同的方法,它们的工作方式也相同。如果在 LED 上运行的代码可以同时使用这两种类型,而不必注意当前使用的是哪种类型,那就太好了。 接口类型可以提供帮助:

  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. type LED interface {
  12. On()
  13. Off()
  14. }
  15.  
  16. type PushPullLED struct{ pin gpio.Pin }
  17.  
  18. func (led PushPullLED) On() {
  19. led.pin.Set()
  20. }
  21.  
  22. func (led PushPullLED) Off() {
  23. led.pin.Clear()
  24. }
  25.  
  26. func MakePushPullLED(pin gpio.Pin) PushPullLED {
  27. pin.Setup(&gpio.Config{Mode: gpio.Out, Driver: gpio.PushPull})
  28. return PushPullLED{pin}
  29. }
  30.  
  31. type OpenDrainLED struct{ pin gpio.Pin }
  32.  
  33. func (led OpenDrainLED) On() {
  34. led.pin.Clear()
  35. }
  36.  
  37. func (led OpenDrainLED) Off() {
  38. led.pin.Set()
  39. }
  40.  
  41. func MakeOpenDrainLED(pin gpio.Pin) OpenDrainLED {
  42. pin.Setup(&gpio.Config{Mode: gpio.Out, Driver: gpio.OpenDrain})
  43. return OpenDrainLED{pin}
  44. }
  45.  
  46. var led1, led2 LED
  47.  
  48. func init() {
  49. system.SetupPLL(8, 1, 48/8)
  50. systick.Setup(2e6)
  51.  
  52. gpio.A.EnableClock(false)
  53. led1 = MakeOpenDrainLED(gpio.A.Pin(4))
  54. led2 = MakePushPullLED(gpio.A.Pin(3))
  55. }
  56.  
  57. func blinky(led LED, period int) {
  58. for {
  59. led.On()
  60. delay.Millisec(100)
  61. led.Off()
  62. delay.Millisec(period - 100)
  63. }
  64. }
  65.  
  66. func main() {
  67. go blinky(led1, 500)
  68. blinky(led2, 1000)
  69. }
  70.  
LEDOnOffPushPullLEDOpenDrainLEDMake*LEDLEDLED
  1. led1 = MakeOpenDrainLED(gpio.A.Pin(4))
  2. led2 = MakePushPullLED(gpio.A.Pin(3))
led1OpenDrainLED{gpio.A.Pin(4)}OpenDrainLEDled1.On()
  1. led1.methods->On(led1.value)

如你所见,如果仅考虑函数调用的开销,这是相当廉价的抽象。

但是,对接口的任何赋值都会导致包含有关已赋值类型的大量信息。对于由许多其他类型组成的复杂类型,可能会有很多信息:

  1. $ egc
  2. $ arm-none-eabi-size cortexm0.elf
  3. text data bss dec hex filename
  4. 10356 196 212 10764 2a0c cortexm0.elf

如果我们不使用 反射,可以通过避免包含类型和结构字段的名称来节省一些字节:

  1. $ egc -nf -nt
  2. $ arm-none-eabi-size cortexm0.elf
  3. text data bss dec hex filename
  4. 10312 196 212 10720 29e0 cortexm0.elf

生成的二进制文件仍然包含一些有关类型的必要信息和关于所有导出方法(带有名称)的完整信息。在运行时,主要是当你将存储在接口变量中的一个值赋值给任何其他变量时,需要此信息来检查可赋值性。

我们还可以通过重新编译所导入的包来删除它们的类型和字段名称:

  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. 10272 196 212 10680 29b8 cortexm0.elf

让我们加载这个程序,看看它是否按预期工作。这一次我们将使用 st-flash 命令:

  1. $ arm-none-eabi-objcopy -O binary cortexm0.elf cortexm0.bin
  2. $ st-flash write cortexm0.bin 0x8000000
  3. st-flash 1.4.0-33-gd76e3c7
  4. 2018-04-10T22:04:34 INFO usb.c: -- exit_dfu_mode
  5. 2018-04-10T22:04:34 INFO common.c: Loading device parameters....
  6. 2018-04-10T22:04:34 INFO common.c: Device connected is: F0 small device, id 0x10006444
  7. 2018-04-10T22:04:34 INFO common.c: SRAM size: 0x1000 bytes (4 KiB), Flash: 0x4000 bytes (16 KiB) in pages of 1024 bytes
  8. 2018-04-10T22:04:34 INFO common.c: Attempting to write 10468 (0x28e4) bytes to stm32 address: 134217728 (0x8000000)
  9. Flash page at addr: 0x08002800 erased
  10. 2018-04-10T22:04:34 INFO common.c: Finished erasing 11 pages of 1024 (0x400) bytes
  11. 2018-04-10T22:04:34 INFO common.c: Starting Flash write for VL/F0/F3/F1_XL core id
  12. 2018-04-10T22:04:34 INFO flash_loader.c: Successfully loaded flash loader in sram
  13. 11/11 pages written
  14. 2018-04-10T22:04:35 INFO common.c: Starting verification of write complete
  15. 2018-04-10T22:04:35 INFO common.c: Flash written and verified! jolly good!
  16.  
-reset

 

Interfaces

st-flash

UART

UART(通用异步收发传输器Universal Aynchronous Receiver-Transmitter)仍然是当今微控制器最重要的外设之一。它的优点是以下属性的独特组合:

  • 相对较高的速度,
  • 仅两条信号线(在 半双工half-duplex 通信的情况下甚至一条),
  • 角色对称,
  • 关于新数据的 同步带内信令synchronous in-band signaling(起始位),
  • 在传输 words 内的精确计时。

这使得最初用于传输由 7-9 位的字组成的异步消息的 UART,也被用于有效地实现各种其他物理协议,例如被 WS28xx LEDs 或 1-wire 设备使用的协议。

但是,我们将以其通常的角色使用 UART:从程序中打印文本消息。

  1. package main
  2.  
  3. import (
  4. "io"
  5. "rtos"
  6.  
  7. "stm32/hal/dma"
  8. "stm32/hal/gpio"
  9. "stm32/hal/irq"
  10. "stm32/hal/system"
  11. "stm32/hal/system/timer/systick"
  12. "stm32/hal/usart"
  13. )
  14.  
  15. var tts *usart.Driver
  16.  
  17. func init() {
  18. system.SetupPLL(8, 1, 48/8)
  19. systick.Setup(2e6)
  20.  
  21. gpio.A.EnableClock(true)
  22. tx := gpio.A.Pin(9)
  23.  
  24. tx.Setup(&gpio.Config{Mode: gpio.Alt})
  25. tx.SetAltFunc(gpio.USART1_AF1)
  26. d := dma.DMA1
  27. d.EnableClock(true)
  28. tts = usart.NewDriver(usart.USART1, d.Channel(2, 0), nil, nil)
  29. tts.Periph().EnableClock(true)
  30. tts.Periph().SetBaudRate(115200)
  31. tts.Periph().Enable()
  32. tts.EnableTx()
  33.  
  34. rtos.IRQ(irq.USART1).Enable()
  35. rtos.IRQ(irq.DMA1_Channel2_3).Enable()
  36. }
  37.  
  38. func main() {
  39. io.WriteString(tts, "Hello, World!\r\n")
  40. }
  41.  
  42. func ttsISR() {
  43. tts.ISR()
  44. }
  45.  
  46. func ttsDMAISR() {
  47. tts.TxDMAISR()
  48. }
  49.  
  50. //c:__attribute__((section(".ISRs")))
  51. var ISRs = [...]func(){
  52. irq.USART1: ttsISR,
  53. irq.DMA1_Channel2_3: ttsDMAISR,
  54. }
  55.  
usart.Driver

STM32 USART 外设提供传统的 UART 及其同步版本。要将其用作输出,我们必须将其 Tx 信号连接到正确的 GPIO 引脚:

  1. tx.Setup(&gpio.Config{Mode: gpio.Alt})
  2. tx.SetAltFunc(gpio.USART1_AF1)
usart.Driver
  1. tts = usart.NewDriver(usart.USART1, d.Channel(2, 0), nil, nil)
WriteString
  1. $ cd $HOME/emgo
  2. $ ./clean.sh
  3. $ cd $HOME/firstemgo
  4. $ egc
  5. $ arm-none-eabi-size cortexm0.elf
  6. text data bss dec hex filename
  7. 12728 236 176 13140 3354 cortexm0.elf

要查看某些内容,你需要在 PC 中使用 UART 外设。

请勿使用 RS232 端口或 USB 转 RS232 转换器!

STM32 系列使用 3.3V 逻辑,但是 RS232 可以产生 -15 V ~ +15 V 的电压,这可能会损坏你的 MCU。你需要使用 3.3V 逻辑的 USB 转 UART 转换器。流行的转换器基于 FT232 或 CP2102 芯片。

 

UART

你还需要一些终端仿真程序(我更喜欢 picocom)。刷新新图像,运行终端仿真器,然后按几次复位按钮:

  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: 0x080016f4 msp: 0x20000a20
  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: 0x20000a20
  18. wrote 13312 bytes from file cortexm0.elf in 1.020185s (12.743 KiB/s)
  19. ** Programming Finished **
  20. adapter speed: 950 kHz
  21. $
  22. $ picocom -b 115200 /dev/ttyUSB0
  23. picocom v3.1
  24.  
  25. port is : /dev/ttyUSB0
  26. flowcontrol : none
  27. baudrate is : 115200
  28. parity is : none
  29. databits are : 8
  30. stopbits are : 1
  31. escape is : C-a
  32. local echo is : no
  33. noinit is : no
  34. noreset is : no
  35. hangup is : no
  36. nolock is : no
  37. send_cmd is : sz -vv
  38. receive_cmd is : rz -vv -E
  39. imap is :
  40. omap is :
  41. emap is : crcrlf,delbs,
  42. logfile is : none
  43. initstring : none
  44. exit_after is : not set
  45. exit is : no
  46.  
  47. Type [C-a] [C-h] to see available commands
  48. Terminal ready
  49. Hello, World!
  50. Hello, World!
  51. Hello, World!

每次按下复位按钮都会产生新的 “Hello,World!”行。一切都在按预期进行。

要查看此 MCU 的 双向bi-directional UART 代码,请查看 此示例。

io.Writer 接口

io.Writererror
  1. type Writer interface {
  2. Write(p []byte) (n int, err error)
  3. }
usart.Driverio.Writer
  1. tts.WriteString("Hello, World!\r\n")

  1. io.WriteString(tts, "Hello, World!\r\n")
ioimport
io.WriteString
  1. func WriteString(w Writer, s string) (n int, err error)
io.WriteStringio.WriterWriteStringWrite

让我们编译修改后的程序:

  1. $ egc
  2. $ arm-none-eabi-size cortexm0.elf
  3. text data bss dec hex filename
  4. 15456 320 248 16024 3e98 cortexm0.elf
io.WriteString

使用这个命令:

  1. arm-none-eabi-nm --print-size --size-sort --radix=d cortexm0.elf
awkdiff
  1. > 00000062 T stm32$hal$usart$Driver$DisableRx
  2. > 00000072 T stm32$hal$usart$Driver$RxDMAISR
  3. > 00000076 T internal$Type$Implements
  4. > 00000080 T stm32$hal$usart$Driver$EnableRx
  5. > 00000084 t errors$New
  6. > 00000096 R $8$stm32$hal$usart$Driver$$
  7. > 00000100 T stm32$hal$usart$Error$Error
  8. > 00000360 T io$WriteString
  9. > 00000660 T stm32$hal$usart$Driver$Read
usart.Driver.ReadDisableRxRxDMAISREnableRx
importstrconvio
  1. func main() {
  2. a := 12
  3. b := -123
  4.  
  5. tts.WriteString("a = ")
  6. strconv.WriteInt(tts, a, 10, 0, 0)
  7. tts.WriteString("\r\n")
  8. tts.WriteString("b = ")
  9. strconv.WriteInt(tts, b, 10, 0, 0)
  10. tts.WriteString("\r\n")
  11.  
  12. tts.WriteString("hex(a) = ")
  13. strconv.WriteInt(tts, a, 16, 0, 0)
  14. tts.WriteString("\r\n")
  15. tts.WriteString("hex(b) = ")
  16. strconv.WriteInt(tts, b, 16, 0, 0)
  17. tts.WriteString("\r\n")
  18. }
io.WriteStringstrconv.WriteIntio.Writer
  1. $ egc
  2. /usr/local/arm/bin/arm-none-eabi-ld: /home/michal/firstemgo/cortexm0.elf section `.rodata' will not fit in region `Flash'
  3. /usr/local/arm/bin/arm-none-eabi-ld: region `Flash' overflowed by 692 bytes
  4. exit status 1

这一次我们的空间超出的不多。让我们试着精简一下有关类型的信息:

  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. 15876 316 320 16512 4080 cortexm0.elf

很接近,但很合适。让我们加载并运行此代码:

  1. a = 12
  2. b = -123
  3. hex(a) = c
  4. hex(b) = -7b
strconvfmtWriteFormat
  1. func main() {
  2. b := -123
  3. strconv.WriteInt(tts, b, 10, 0, 0)
  4. tts.WriteString("\r\n")
  5. strconv.WriteInt(tts, b, 10, 6, ' ')
  6. tts.WriteString("\r\n")
  7. strconv.WriteInt(tts, b, 10, 6, '0')
  8. tts.WriteString("\r\n")
  9. strconv.WriteInt(tts, b, 10, 6, '.')
  10. tts.WriteString("\r\n")
  11. strconv.WriteInt(tts, b, 10, -6, ' ')
  12. tts.WriteString("\r\n")
  13. strconv.WriteInt(tts, b, 10, -6, '0')
  14. tts.WriteString("\r\n")
  15. strconv.WriteInt(tts, b, 10, -6, '.')
  16. tts.WriteString("\r\n")
  17. }

下面是它的输出:

  1. -123
  2. -123
  3. -00123
  4. ..-123
  5. -123
  6. -123
  7. -123..

Unix 流 和 莫尔斯电码Morse code

io.WriterFILE
  1. echo "Hello, World!" > file.txt
>|
trecho
  1. echo "Hello, World!" | tr a-z A-Z > file.txt
io.Writer
  1. io.WriteString(tts, "Hello, World!\r\n")

采用以下伪 unix 形式:

  1. io.WriteString "Hello, World!" | usart.Driver usart.USART1

下一个示例将显示如何执行此操作:

  1. io.WriteString "Hello, World!" | MorseWriter | usart.Driver usart.USART1

让我们来创建一个简单的编码器,它使用莫尔斯电码对写入的文本进行编码:

  1. type MorseWriter struct {
  2. W io.Writer
  3. }
  4.  
  5. func (w *MorseWriter) Write(s []byte) (int, error) {
  6. var buf [8]byte
  7. for n, c := range s {
  8. switch {
  9. case c == '\n':
  10. c = ' ' // Replace new lines with spaces.
  11. case 'a' <= c && c <= 'z':
  12. c -= 'a' - 'A' // Convert to upper case.
  13. }
  14. if c < ' ' || 'Z' < c {
  15. continue // c is outside ASCII [' ', 'Z']
  16. }
  17. var symbol morseSymbol
  18. if c == ' ' {
  19. symbol.length = 1
  20. buf[0] = ' '
  21. } else {
  22. symbol = morseSymbols[c-'!']
  23. for i := uint(0); i < uint(symbol.length); i++ {
  24. if (symbol.code>>i)&1 != 0 {
  25. buf[i] = '-'
  26. } else {
  27. buf[i] = '.'
  28. }
  29. }
  30. }
  31. buf[symbol.length] = ' '
  32. if _, err := w.W.Write(buf[:symbol.length+1]); err != nil {
  33. return n, err
  34. }
  35. }
  36. return len(s), nil
  37. }
  38.  
  39. type morseSymbol struct {
  40. code, length byte
  41. }
  42.  
  43. //emgo:const
  44. var morseSymbols = [...]morseSymbol{
  45. {1<<0 | 1<<1 | 1<<2, 4}, // ! ---.
  46. {1<<1 | 1<<4, 6}, // " .-..-.
  47. {}, // #
  48. {1<<3 | 1<<6, 7}, // $ ...-..-
  49.  
  50. // Some code omitted...
  51.  
  52. {1<<0 | 1<<3, 4}, // X -..-
  53. {1<<0 | 1<<2 | 1<<3, 4}, // Y -.--
  54. {1<<0 | 1<<1, 4}, // Z --..
  55. }
morseSymbols//emgo:constmorseSymbols

现在我们可以通过两种方式打印句子:

  1. func main() {
  2. s := "Hello, World!\r\n"
  3. mw := &MorseWriter{tts}
  4.  
  5. io.WriteString(tts, s)
  6. io.WriteString(mw, s)
  7. }
MorseWriter&MorseWriter{tts}MorseWriter{tts}MorseWriter
slicefloat64complex128

让我们编译此代码并查看其输出:

  1. $ egc
  2. $ arm-none-eabi-size cortexm0.elf
  3. text data bss dec hex filename
  4. 15152 324 248 15724 3d6c cortexm0.elf
  1. Hello, World!
  2. .... . .-.. .-.. --- --..-- .-- --- .-. .-.. -.. ---.

终极闪烁

Blinky 是等效于 “Hello,World!” 程序的硬件。一旦有了摩尔斯编码器,我们就可以轻松地将两者结合起来以获得终极闪烁程序:

  1. package main
  2.  
  3. import (
  4. "delay"
  5. "io"
  6.  
  7. "stm32/hal/gpio"
  8. "stm32/hal/system"
  9. "stm32/hal/system/timer/systick"
  10. )
  11.  
  12. var led gpio.Pin
  13.  
  14. func init() {
  15. system.SetupPLL(8, 1, 48/8)
  16. systick.Setup(2e6)
  17.  
  18. gpio.A.EnableClock(false)
  19. led = gpio.A.Pin(4)
  20.  
  21. cfg := gpio.Config{Mode: gpio.Out, Driver: gpio.OpenDrain, Speed: gpio.Low}
  22. led.Setup(&cfg)
  23. }
  24.  
  25. type Telegraph struct {
  26. Pin gpio.Pin
  27. Dotms int // Dot length [ms]
  28. }
  29.  
  30. func (t Telegraph) Write(s []byte) (int, error) {
  31. for _, c := range s {
  32. switch c {
  33. case '.':
  34. t.Pin.Clear()
  35. delay.Millisec(t.Dotms)
  36. t.Pin.Set()
  37. delay.Millisec(t.Dotms)
  38. case '-':
  39. t.Pin.Clear()
  40. delay.Millisec(3 * t.Dotms)
  41. t.Pin.Set()
  42. delay.Millisec(t.Dotms)
  43. case ' ':
  44. delay.Millisec(3 * t.Dotms)
  45. }
  46. }
  47. return len(s), nil
  48. }
  49.  
  50. func main() {
  51. telegraph := &MorseWriter{Telegraph{led, 100}}
  52. for {
  53. io.WriteString(telegraph, "Hello, World! ")
  54. }
  55. }
  56.  
  57. // Some code omitted...
  58.  
MorseWriter
  1. $ egc
  2. $ arm-none-eabi-size cortexm0.elf
  3. text data bss dec hex filename
  4. 11772 244 244 12260 2fe4 cortexm0.elf

 

Ultimate Blinky

反射

reflectfmt.Print
printlnfmt.Println
  1. package main
  2.  
  3. import (
  4. "debug/semihosting"
  5. "reflect"
  6. "strconv"
  7.  
  8. "stm32/hal/system"
  9. "stm32/hal/system/timer/systick"
  10. )
  11.  
  12. var stdout semihosting.File
  13.  
  14. func init() {
  15. system.SetupPLL(8, 1, 48/8)
  16. systick.Setup(2e6)
  17.  
  18. var err error
  19. stdout, err = semihosting.OpenFile(":tt", semihosting.W)
  20. for err != nil {
  21. }
  22. }
  23.  
  24. type stringer interface {
  25. String() string
  26. }
  27.  
  28. func println(args ...interface{}) {
  29. for i, a := range args {
  30. if i > 0 {
  31. stdout.WriteString(" ")
  32. }
  33. switch v := a.(type) {
  34. case string:
  35. stdout.WriteString(v)
  36. case int:
  37. strconv.WriteInt(stdout, v, 10, 0, 0)
  38. case bool:
  39. strconv.WriteBool(stdout, v, 't', 0, 0)
  40. case stringer:
  41. stdout.WriteString(v.String())
  42. default:
  43. stdout.WriteString("%unknown")
  44. }
  45. }
  46. stdout.WriteString("\r\n")
  47. }
  48.  
  49. type S struct {
  50. A int
  51. B bool
  52. }
  53.  
  54. func main() {
  55. p := &S{-123, true}
  56.  
  57. v := reflect.ValueOf(p)
  58.  
  59. println("kind(p) =", v.Kind())
  60. println("kind(*p) =", v.Elem().Kind())
  61. println("type(*p) =", v.Elem().Type())
  62.  
  63. v = v.Elem()
  64.  
  65. println("*p = {")
  66. for i := 0; i < v.NumField(); i++ {
  67. ft := v.Type().Field(i)
  68. fv := v.Field(i)
  69. println(" ", ft.Name(), ":", fv.Interface())
  70. }
  71. println("}")
  72. }
  73.  
semihosting.OpenFile:tt
println
  1. func println(args ...interface{})
interface{}println
  1. switch v := a.(type) {
  2. case string:
  3. stdout.WriteString(v)
  4. case int:
  5. strconv.WriteInt(stdout, v, 10, 0, 0)
  6. case bool:
  7. strconv.WriteBool(stdout, v, 't', 0, 0)
  8. case stringer:
  9. stdout.WriteString(v.String())
  10. default:
  11. stdout.WriteString("%unknown")
  12. }
stringerString()casevcase
reflect.ValueOf(p)pv.Elem()

让我们尝试编译这段代码。现在让我们看看如果编译时没有类型和字段名,会有什么结果:

  1. $ egc -nt -nf
  2. $ arm-none-eabi-size cortexm0.elf
  3. text data bss dec hex filename
  4. 16028 216 312 16556 40ac cortexm0.elf

闪存上只剩下 140 个可用字节。让我们使用启用了半主机的 OpenOCD 加载它:

  1. $ openocd -d0 -f interface/stlink.cfg -f target/stm32f0x.cfg -c 'init; program cortexm0.elf; arm semihosting enable; reset run'
  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: 0x08002338 msp: 0x20000a20
  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: 0x20000a20
  18. wrote 16384 bytes from file cortexm0.elf in 0.700133s (22.853 KiB/s)
  19. ** Programming Finished **
  20. semihosting is enabled
  21. adapter speed: 950 kHz
  22. kind(p) = ptr
  23. kind(*p) = struct
  24. type(*p) =
  25. *p = {
  26. X. : -123
  27. X. : true
  28. }

如果你实际运行此代码,则会注意到半主机运行缓慢,尤其是在逐字节写入时(缓冲很有用)。

*pX.-nt -nf
  1. $ egc
  2. $ arm-none-eabi-size cortexm0.elf
  3. text data bss dec hex filename
  4. 16052 216 312 16580 40c4 cortexm0.elf
main
  1. kind(p) = ptr
  2. kind(*p) = struct
  3. type(*p) = S
  4. *p = {
  5. A : -123
  6. B : true
  7. }

反射是任何易于使用的序列化库的关键部分,而像 JSON 这样的序列化 算法 在物联网IoT时代也越来越重要。

这些就是我完成的本文的第二部分。我认为有机会进行第三部分,更具娱乐性的部分,在那里我们将各种有趣的设备连接到这块板上。如果这块板装不下,我们就换一块大一点的。