1. 程式人生 > >基於Docker環境開發、除錯嵌入式軟體(Embedded Software develop/Debug using Docker)

基於Docker環境開發、除錯嵌入式軟體(Embedded Software develop/Debug using Docker)

本文使用一個具體的簡單的Demo介紹此係列文章的Dev、Debug環境的使用方法,Dev、Debug環境都使用Docker技術提供OS層的環境隔離。以避免在搭建嵌入式開發環境上浪費時間,或者因搭建此嵌入式環境影響了其他開發環境。Docker技術可以解決以上這些困擾。開發環境使用Vi + GNU Cross toolchain,測試環境使用Qemu模擬STM32-P103這款開發板。使用模擬軟體模擬開發板也節省了大家購買硬體的開支,同時也方便在有即興Idea時,隨時進行驗證。

準備開發、除錯環境

開發、除錯環境依賴於Docker,首先安裝Docker。Docker有兩個版本CE(Community Edition)和 EE(Enterprise Edition),EE功能強大些,且穩定可靠,公司用,收費。我們學習用當然選擇CE版本啦。

官方安裝教程,不過Docker.com這個網站訪問需要翻牆。不想翻牆的同學可以使用國內的aliyun

Tag:使用Win10的bash的同學注意啦,由於Docker使用了Linux的cgroups和namespace等一些技術,Win10 bash並不完全支援。Docker安裝在Win10 bash上不能使用。如果真想在Win10上使用Docker,請直接安裝官方文件安裝Docker for Win10. 底層環境直接使用Virtualbox提供Linux環境虛擬環境,Docker執行在虛擬化的Linux環境裡。

安裝完成後,可以使用以下命令檢視是否安裝成功:

input:

docker version

output:

Client:
 Version:      17.09.0-ce
 API version:  1.32
 Go version:   go1.8.3
 Git commit:   afdb6d4
 Built:        Tue Sep 26 22:42:38 2017
 OS/Arch:      linux/amd64

Server:
 Version:      17.09.0-ce
 API version:  1.32 (minimum version 1.12)
 Go version:   go1.8.3
 Git commit:   afdb6d4
 Built:        Tue Sep 26
22:41:20 2017 OS/Arch: linux/amd64 Experimental: false

有了Docker ,就可以下載我們使用的開發、除錯環境的Docker image了。

For Dev:

Docker hub(官方映象源):

docker pull zhanglianpin/stm32_compile_env:latest

Aliyun hub(阿里雲映象源):

docker pull registry.cn-hangzhou.aliyuncs.com/bahutou/stm32_compile_env

For Debug:

Docker hub(官方映象源):

docker pull zhanglianpin/ucos_debug_env:latest

Aliyun hub(阿里雲映象源):

docker pull registry.cn-hangzhou.aliyuncs.com/bahutou/zhanglianpin/ucos_debug_env

下載完Dokcker image後,可以使用以下命令測試其是否下載成功:

input

docker image ls

output

REPOSITORY                       TAG                 IMAGE ID            CREATED             SIZE
zhanglianpin/stm32_compile_env   latest              48cd33a2339f        3 days ago          527MB
zhanglianpin/ucos_debug_env      latest              8d3c1b84e93f        3 days ago          203MB
ubuntu                           16.04               0b1edfbffd27        11 days ago         113MB
ubuntu                           14.04               8cef1fa16c77        11 days ago         223MB
linuxep/lepv0.1                  latest              92e0fb0aa864        7 months ago        785MB
hello-world                      latest              05a3bd381fc2        7 months ago        1.84kB

從上面的輸出我們可以看到,我的本地Docker iamge包含了 zhanglianpin/stm32_compile_env和zhanglianpin/ucos_debug_env這兩個image。

怎麼使用這兩個Docker image吶?看下面:

input:

docker run --rm  zhanglianpin/stm32_compile_env arm-none-eabi-gcc -v

output:

Using built-in specs.
COLLECT_GCC=arm-none-eabi-gcc
COLLECT_LTO_WRAPPER=/opt/gcc-arm-none-eabi-7-2017-q4-major/bin/../lib/gcc/arm-none-eabi/7.2.1/lto-wrapper
Target: arm-none-eabi
Configured with: /tmp/jenkins/jenkins-GCC-7-build_toolchain_docker-633_20171130_1512067137/src/gcc/configure --target=arm-none-eabi --prefix=/tmp/jenkins/jenkins-GCC-7-build_toolchain_docker-633_20171130_1512067137/install-native --libexecdir=/tmp/jenkins/jenkins-GCC-7-build_toolchain_docker-633_20171130_1512067137/install-native/lib --infodir=/tmp/jenkins/jenkins-GCC-7-build_toolchain_docker-633_20171130_1512067137/install-native/share/doc/gcc-arm-none-eabi/info --mandir=/tmp/jenkins/jenkins-GCC-7-build_toolchain_docker-633_20171130_1512067137/install-native/share/doc/gcc-arm-none-eabi/man --htmldir=/tmp/jenkins/jenkins-GCC-7-build_toolchain_docker-633_20171130_1512067137/install-native/share/doc/gcc-arm-none-eabi/html --pdfdir=/tmp/jenkins/jenkins-GCC-7-build_toolchain_docker-633_20171130_1512067137/install-native/share/doc/gcc-arm-none-eabi/pdf --enable-languages=c,c++ --enable-plugins --disable-decimal-float --disable-libffi --disable-libgomp --disable-libmudflap --disable-libquadmath --disable-libssp --disable-libstdcxx-pch --disable-nls --disable-shared --disable-threads --disable-tls --with-gnu-as --with-gnu-ld --with-newlib --with-headers=yes --with-python-dir=share/gcc-arm-none-eabi --with-sysroot=/tmp/jenkins/jenkins-GCC-7-build_toolchain_docker-633_20171130_1512067137/install-native/arm-none-eabi --build=x86_64-linux-gnu --host=x86_64-linux-gnu --with-gmp=/tmp/jenkins/jenkins-GCC-7-build_toolchain_docker-633_20171130_1512067137/build-native/host-libs/usr --with-mpfr=/tmp/jenkins/jenkins-GCC-7-build_toolchain_docker-633_20171130_1512067137/build-native/host-libs/usr --with-mpc=/tmp/jenkins/jenkins-GCC-7-build_toolchain_docker-633_20171130_1512067137/build-native/host-libs/usr --with-isl=/tmp/jenkins/jenkins-GCC-7-build_toolchain_docker-633_20171130_1512067137/build-native/host-libs/usr --with-libelf=/tmp/jenkins/jenkins-GCC-7-build_toolchain_docker-633_20171130_1512067137/build-native/host-libs/usr --with-host-libstdcxx='-static-libgcc -Wl,-Bstatic,-lstdc++,-Bdynamic -lm' --with-pkgversion='GNU Tools for Arm Embedded Processors 7-2017-q4-major' --with-multilib-list=rmprofile
Thread model: single
gcc version 7.2.1 20170904 (release) [ARM/embedded-7-branch revision 255204] (GNU Tools for Arm Embedded Processors 7-2017-q4-major) 

input:

docker run --rm  zhanglianpin/ucos_debug_env  qemu-system-gnuarmeclipse -v

output:

GNU ARM Eclipse 64-bits QEMU emulator version 2.8.0 (v2.8.0-644-ge45e0e1-dirty)
Copyright (c) 2003-2016 Fabrice Bellard and the QEMU Project developers

相信你應該有一些感覺了吧,使用這個Docker還是挺爽的,輕開銷地提供環境隔離,爽到爆。是不是你一直夢寐以求的類,反正我為各種開發、測試環境弄得煩躁的時候還是有的,這下終於可以心平氣和地做開發工作了。

一個簡單的不能再簡單的例子

Demo:使用STM32-P103這個開發板做一個最簡單的“5+4”, R0=0x05 R1=0x04 把結果0X09放到R2中 。
直接上程式碼(add.s):

/**
  ******************************************************************************
  * @file      add.s
  * @author    Linc Zhang
  * @version   V1.0.0
  * @date      02-05-2018
  * @brief     exec some cortex instruction for testing
  ******************************************************************************
**/
    .thumb                       @(same as saying '.code 16')
        .syntax unified

    .text
    .equ STACKINIT,   0x20005000
vectors:        
        .word  STACKINIT         @stack pointer value when stack is empty
        .word _start + 1         @reset vector (manually adjust to odd for thumb)
        .word _nmi_handler + 1   @
        .word _hard_fault  + 1   @ 
        .word _memory_fault + 1  @ 
        .word _bus_fault + 1     @ 
        .word _usage_fault + 1   @ 
_start:                          @ Label, not really required
        mov   r0, #5             @ Load register r0 with the value 5
        mov   r1, #4             @ Load register r1 with the value 4
        add   r2, r1, r0         @ Add r0 and r1 and store in r2

stop:   b stop                   @ Infinite loop to stop execution

_dummy:                          @if any int gets triggered, just hang in a loop
_nmi_handler:
_hard_fault:
_memory_fault:
_bus_fault:
_usage_fault:
        add r0, 1
        add r1, 1
        b _dummy 

使用的Link script:


SECTIONS {
         . = 0x00000000;
         .text : { * (.text); }
         end_code = .;
         . = 0x08000000;
         .data :AT(end_code) { * (.data); }
}

使用的Makefile:

AS = arm-none-eabi-as
CC = arm-none-eabi-gcc
LD = arm-none-eabi-ld
CFLAGS=

add.elf: 
    $(LD)  -T main.ld -o add.elf  add.o

add.o: 
    $(AS) -g -o add.o add.s 


.PHONY: clean

clean:
    rm -f *.o *~ core *.elf *.bin

Compile

input:

docker run --rm  -v $(pwd):/home/work  -w /home/work  zhanglianpin/stm32_compile_env make

output

arm-none-eabi-as -o add.o add.s 
arm-none-eabi-ld  -T main.ld -o add.elf  add.o

Debug

input:

docker run --rm -i  -v $(pwd):/home/work/  zhanglianpin/ucos_debug_env  qemu-system-gnuarmeclipse  --verbose --verbose --nographic  --board STM32-P103 --image add.elf

output:

GNU ARM Eclipse 64-bits QEMU v2.8.0 (qemu-system-gnuarmeclipse).
Board: 'STM32-P103' (Olimex Prototype Board for STM32F103RBT6).
Device file: '/opt/gnuarmeclipse/qemu/2.8.0-201703022210-head/share/qemu/devices/STM32F103xx-qemu.json'.
Device: 'STM32F103RB' (Cortex-M3 r0p1, MPU, 4 NVIC prio bits, 43 IRQs), Flash: 128 kB, RAM: 20 kB.
Image: 'add.elf'.
Command line: (none).
Load     52 bytes at 0x00000000-0x00000033.
Cortex-M3 r0p1 core initialised.
'/machine/mcu/stm32/RCC', address: 0x40021000, size: 0x0400
'/machine/mcu/stm32/FLASH', address: 0x40022000, size: 0x0400
'/machine/mcu/stm32/PWR', address: 0x40007000, size: 0x0400
'/machine/mcu/stm32/AFIO', address: 0x40010000, size: 0x0400
'/machine/mcu/stm32/EXTI', address: 0x40010400, size: 0x0400
'/machine/mcu/stm32/GPIOA', address: 0x40010800, size: 0x0400
'/machine/mcu/stm32/GPIOB', address: 0x40010C00, size: 0x0400
'/machine/mcu/stm32/GPIOC', address: 0x40011000, size: 0x0400
'/machine/mcu/stm32/GPIOD', address: 0x40011400, size: 0x0400
'/machine/mcu/stm32/GPIOE', address: 0x40011800, size: 0x0400
'/peripheral/led:red' 12*10 @(331,362) active low '/machine/mcu/stm32/GPIOC',12
QEMU 2.8.0 monitor - type 'help' for more information
(qemu) Cortex-M3 r0p1 core reset.

(qemu) 

qemu 提供了一個monitor的UI,使用這個CLI 輸入以下命令:

input:

info registers

output:

R00=00000005 R01=00000004 R02=00000009 R03=00000000
R04=00000000 R05=00000000 R06=00000000 R07=00000000
R08=00000000 R09=00000000 R10=00000000 R11=00000000
R12=00000000 R13=20005000 R14=00000000 R15=00000028
PSR=40000173 -Z-- T svc32
FPSCR: 00000000

可以看到,R00=0x05,R01=0x04,R02=0x09,確實執行了上面的 “5+4”的運算。

當然你還可以使用Gdb進行程式的除錯。

input:

docker run --rm -i  -v $(pwd):/home/work/   zhanglianpin/ucos_debug_env  qemu-system-gnuarmeclipse  --verbose --verbose --nographic  --board STM32-P103 --image add.elf   --gdb tcp::1234

以上命令啟動了一個Gdb server,等待gdb client 去連線除錯。

新開一個Linux的CLI,然後輸入:

input:

docker run --rm -i  -v $(pwd):/home/work  zhanglianpin/stm32_compile_env  arm-none-eabi-gdb

output:

GNU gdb (GNU Tools for Arm Embedded Processors 7-2017-q4-major) 8.0.50.20171128-git
Copyright (C) 2017 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "--host=x86_64-linux-gnu --target=arm-none-eabi".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word".
(gdb)

在Gdb CLI中輸入連線Gdb Server的命令:

target remote 172.17.0.2:1234

Tag: Docker container的IP Addr查詢方法:

docker inspect --format {% raw %}'{{ .NetworkSettings.IPAddress }}'{% endraw %} container_name_or_id

查詢docker container ID的方法:

docker ps

連線上Gdb Server之後,一切都那麼熟悉了,可以使用一切Gdb除錯方法了,比如你想看下Registers:

input:

info all-registers

output:

r0             0x5      5
r1             0x4      4
r2             0x9      9
r3             0x0      0
r4             0x0      0
r5             0x0      0
r6             0x0      0
r7             0x0      0
r8             0x0      0
r9             0x0      0
r10            0x0      0
r11            0x0      0
r12            0x0      0
sp             0x20005000       0x20005000
lr             0x0      0
pc             0x28     0x28
cpsr           0x40000173       1073742195
MSP            0x20005000       536891392
PSP            0x0      0
PRIMASK        0x0      0
BASEPRI        0x0      0
FAULTMASK      0x1      1
CONTROL        0x0      0

自己動手搞一遍,我相信你應該知道怎麼回事了,下面我撿那個重點說明一下,已經搞定的請繞行。以免耽誤自己的寶貴時間。

編譯的時候執行的那個命令

docker run --rm  -v $(pwd):/home/work  -w /home/work zhanglianpin/stm32_compile_env make

Docker run 的意思是執行一個Container,使用的映象是我精心為您準備的STM32系列CPU的編譯環境,–rm這個引數的含義是我執行結束後這個Container會自動刪除,不保留上次執行的狀態,因為我們只是單次編譯下而已,沒有必要儲存上次Container執行的狀態。 -v 這個引數的意思是讓Container裡面的編譯環境能共享HOST主機的目錄和檔案,/home/work/ 這個目錄是我設定的編譯環境Container的預設工作目錄,-w 是為Container裡面的編譯環境賦予寫許可權。zhanglianpin/stm32_compile_env是Docker iamge的名稱,後面的make 是執行起來這個Container之後要執行的命令。使用Container共享HOST的檔案還是很方便的,這樣我就只用編譯環境編譯,其他的你像編輯啥的工作還是放在HOST主機上來做好了。

你理解了上面的套路之後,其他的使用Docker命令應該很好理解了。

簡單的例子中蘊含的一些Key Point

本章節我只提出幾個問題,以引起大家的思考。這些問題會在後面blog中一一解開。
1. STM32-P103上電後從哪裡執行第一條指令?如何把需要執行的第一條指令放到STM32-P103需要的地方?(涉及到STM32F103RB的啟動知識,連結指令碼等等內容)
2. 如果程式裡有變化的量(Variable),該怎麼處理?這些變數下載到哪裡?儲存到哪裡?使用之前有什麼需要注意的地方?(“Load Addr” VS “Excute Addr”)
3. 使用編譯器編譯出來的ELF檔案內部到底什麼結構?從靜態的檔案到動態的程式之間需要做哪些工作?
4. 執行C語言需要哪些前提條件?為什麼有了高階語言,組合語言還存在?STM32-P103可以完全使用C語言開發嗎?

本人認為理解這些事情,比較重要,理解了這些在除錯一些程式BUG的時候能一針見血。

引用資料說明

我最早的時候思考過有必要用Docker搭建Embedded software develop environment 嗎?我是看了An Introduction to Docker for Embedded Developers打消了我的顧慮。

Docker 基本的概念、設計理念、使用方法最好的文件是Docker Docs

qemu-system-gnuarmeclipse 的使用方法見QEMU command

資源彙總