micro:bitの開発環境構築をつくる(C言語)
はじめに
nrfx で提供されているコードを利用して、LED点滅、UART出力を行う簡単なCのプログラムのビルドまでをする。
新規に作成する *.c ファイル、Makefileについては
nrfx/mdk/ 以下に作成するものとする(あんまり綺麗ではないですが)
環境
-
micro:bit V1.5
最新はV2ですが、ここではV1.5の話(V2持ってないので)。 - arm-none-gcc バージョンは以下
$ arm-none-eabi-gcc --version
arm-none-eabi-gcc (GNU Tools for Arm Embedded Processors 7-2018-q2-update) 7.3.1 20180622 (release) [ARM/embedded-7-branch revision 261907]
Copyright (C) 2017 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
リセットハンドラ
micro:bit(V1.5)にのっているプロセッサはNordic nRF51822-QFAA-R rev 3 なので、
以下のコードを使用する。
gcc_startup_nrf51.Sのアセンブル
gcc_startup_nrf51.S は以下のようにアセンブルをする。
(gcc_startup_nrf51.Sの中身を見ると、#define
という記載もあるので、正確にはプリプロセス->アセンブル)
$ arm-none-eabi-gcc -c -g -mcpu=cortex-m0 -o gcc_startup_nrf51.o gcc_startup_nrf51.S
補足:ここで、-c
オプションを付けているのはリンクは行わないようにするため。
3.14 Options for Linking
-c
-S
-E
If any of these options is used, then the linker is not run, and object file names should not be used as arguments.
system_nrf51.cのコンパイル
インクルードファイルを辿ると、core_cm0.h
というファイルをインクルードしている。これはCMSIS_5にある。
core_cm0.h
の場所はCMSIS/Core/Include/core_cm0.h
ちなみにCMSISはCortex Microcontroller Software Interface Standard のこと。
コンパイルは以下のようにするとできる。
Makefileで書くと以下のようになる。
CFLAGS
に書いてあるオプションの説明は省略。
CMSISPATH=<PATH to CMSIS_5>/CMSIS
NRFXPATH=<PATH to nrfx>
IPATH=-I $(CMSISPATH)/Core/Include/
IPATH+=-I $(NRFXPATH)/hal/
IPATH+=-I $(NRFXPATH)/drivers/include/
IPATH+=-I $(NRFXPATH)/
IPATH+=-I $(NRFXPATH)/templates/
IPATH+=-I $(NRFXPATH)/mdk/
CFLAGS = -DNRF51
CFLAGS += -mcpu=cortex-m0
CFLAGS += -mthumb -mabi=aapcs
CFLAGS += -Wall -Werror -O0 -g
CFLAGS += -mfloat-abi=soft
CFLAGS += -ffunction-sections -fdata-sections -fno-strict-aliasing
CFLAGS += -fno-builtin --short-enums
system_nrf51.o : system_nrf51.c
arm-none-eabi-gcc -c $(CFLAGS) $(IPATH) -o system_nrf51.o system_nrf51.c
main関数の作成
baremetal examples notmain.cを参考に
以下のようなコードを書いてみた。
LEDの一つを点滅して、hello!
をずっとURATに出力するだけのプログラム。
#include<nrfx.h>
#include<nrf_gpio.h>
#include<nrf_uart.h>
#include<nrfx_uart.h>
#define ROW3 15
#define ROW2 14
#define ROW1 13
#define COL9 12
#define COL8 11
#define COL7 10
#define COL6 9
#define COL5 8
#define COL4 7
#define COL3 6
#define COL2 5
#define COL1 4
static nrfx_uart_t app_uart_inst = NRFX_UART_INSTANCE(0);
int main ( void )
{
nrfx_uart_config_t p_config = NRFX_UART_DEFAULT_CONFIG(24, 25);
nrfx_uart_init(&app_uart_inst, &p_config, NULL);
nrf_gpio_pin_dir_set(ROW1, NRF_GPIO_PIN_DIR_OUTPUT);
nrf_gpio_pin_dir_set(COL9, NRF_GPIO_PIN_DIR_OUTPUT);
nrf_gpio_pin_clear(COL9);
char hello[] = "hello!\n";
while(1)
{
nrf_gpio_pin_clear(ROW1);
for(int i = 0; i<200000;i++);
nrf_gpio_pin_set(ROW1);
for(int i = 0; i<200000;i++);
nrfx_uart_tx(&app_uart_inst, (uint8_t *)hello, sizeof(hello));
}
return(0);
}
templates/nrfx_config_nrf51.hを以下のように変更。
diff --git a/templates/nrfx_config_nrf51.h b/templates/nrfx_config_nrf51.h
index 5f0e45c..9a4a4e3 100644
--- a/templates/nrfx_config_nrf51.h
+++ b/templates/nrfx_config_nrf51.h
@@ -1077,13 +1077,13 @@
// <e> NRFX_UART_ENABLED - nrfx_uart - UART peripheral driver
//==========================================================
#ifndef NRFX_UART_ENABLED
-#define NRFX_UART_ENABLED 0
+#define NRFX_UART_ENABLED 1
#endif
// <q> NRFX_UART0_ENABLED - Enable UART0 instance
#ifndef NRFX_UART0_ENABLED
-#define NRFX_UART0_ENABLED 0
+#define NRFX_UART0_ENABLED 1
#endif
// <o> NRFX_UART_DEFAULT_CONFIG_IRQ_PRIORITY - Interrupt priority
Makefileは以下
CMSISPATH=<PATH to CMSIS_5>/CMSIS
NRFXPATH=<PATH to nrfx>
IPATH=-I $(CMSISPATH)/Core/Include/
IPATH+=-I $(NRFXPATH)/hal/
IPATH+=-I $(NRFXPATH)/drivers/include/
IPATH+=-I $(NRFXPATH)/
IPATH+=-I $(NRFXPATH)/templates/
IPATH+=-I $(NRFXPATH)/mdk/
CFLAGS = -DNRF51
CFLAGS += -mcpu=cortex-m0
CFLAGS += -mthumb -mabi=aapcs
CFLAGS += -Wall -Werror -O0 -g
CFLAGS += -mfloat-abi=soft
CFLAGS += -ffunction-sections -fdata-sections -fno-strict-aliasing
CFLAGS += -fno-builtin --short-enums
LDFLAGS = -mthumb -mabi=aapcs
LDFLAGS += -mcpu=cortex-m0
LDFLAGS += -Wl,--gc-sections
LDFLAGS += --specs=nano.specs -lc -lnosys
all : main.hex
main.hex : main.elf
arm-none-eabi-objcopy main.elf main.hex -O ihex
main.elf : gcc_startup_nrf51.o system_nrf51.o main.o nrfx_uart.o
arm-none-eabi-gcc $(LDFLAGS) -T nrf51_xxab.ld -o main.elf gcc_startup_nrf51.o system_nrf51.o nrfx_uart.o main.o
system_nrf51.o : system_nrf51.c
arm-none-eabi-gcc -c $(CFLAGS) $(IPATH) -o system_nrf51.o system_nrf51.c
main.o : main.c
arm-none-eabi-gcc -c $(CFLAGS) $(IPATH) -o main.o main.c
gcc_startup_nrf51.o : gcc_startup_nrf51.S
arm-none-eabi-gcc -c -g -mcpu=$(MCPU) -o gcc_startup_nrf51.o gcc_startup_nrf51.S
nrfx_uart.o : <PATH to nrfx>/drivers/src/nrfx_uart.c
arm-none-eabi-gcc -c $(CFLAGS) $(IPATH) -o nrfx_uart.o <PATH to nrfx>/drivers/src/nrfx_uart.c
clean:
rm -f *.hex
rm -f *.o
rm -f *.elf
リンカスクリプト
nrf51_xxab.ld を使用。上記 Makefileで -T nrf51_xxab.ld
としてスクリプトを指定している。
qemu
qemuもVer4あたりからmicrobitに対応している。
make してできた main.hexを指定して動かすこともできる。
$ qemu-system-arm -M microbit -device loader,file=main.hex -serial stdio
hello!
hello!
hello!
hello!
hello!
hello!
hello!
<CTRL Cで終了>
gdb + Visual Studio Codeでデバッグ
以下のように-s -S
をつけると、デバッグが可能。
以下のように、qemuを実行
$ qemu-system-arm -M microbit -device loader,file=main.hex -serial stdio -s -S
-s -S
の意味は以下。
$ qemu-system-arm --help
QEMU emulator version 4.2.1 (Debian 1:4.2-3ubuntu6.8)
Copyright (c) 2003-2019 Fabrice Bellard and the QEMU Project developers
usage: qemu-system-arm [options] [disk_image]
<省略>
-S freeze CPU at startup (use 'c' to start execution)
<省略>
-s shorthand for -gdb tcp::1234
<省略>
Visual Studio Codeで以下のようにlaunch.jsonを作成し、デバッグの実行を行う。
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "(gdb) 起動",
"type": "cppdbg",
"request":"launch",
"program": "~/nrfx/mdk/main.elf",
"args": [],
"stopAtEntry": false,
"cwd": "${workspaceFolder}",
"environment": [],
"externalConsole": false,
"MIMode": "gdb",
"miDebuggerPath": "arm-none-eabi-gdb",
"miDebuggerServerAddress": "localhost:1234",
"setupCommands": [
{
"description": "gdb の再フォーマットを有効にする",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
]
}
]
}
以下のように、brak pointを貼ったところで処理を止めることができる。
Discussion