libopencm3を使ってUART出力をしてみました。
libopencm3は、Cortex Mシリーズ向けの LGPLv3なライブラリです。
Macで作業しました。(あまりMacに依存する内容ではないです。)
使用ハードウェア
- ST NUCLEO-F103RB
- 秋月電子で購入
- STM32F103RBT6が載っている。
- ボードのバージョンはMB1136 C-03
- ST-LINKが内蔵されている。 ST-LINKのFWを最新版に上げてから使いました。
- LEDはPA5に接続されている。
- UARTはUSART2がST-LINKに繋がっています。ST-LINKのUSBシリアル経由でアクセスができます。USART2のTXはPA2です。
コンパイラなどのインストール
- コンパイラはarm-none-eabi-gccを使いました。
- STM32でlibopencm3を使ってLチカを見てください。
サンプルコード
- UARTのサンプルは https://github.com/libopencm3/libopencm3-examples/blob/master/examples/stm32/f1/stm32-h103/usart/usart.c を参考にしました。
- コードの説明
- rcc_clock_setup_in_hse_8mhz_out_72mhz() HSEを使って内部クロックを72MHzに設定。
- rcc_periph_clock_enable() PORTAとUSART2にクロックを供給。
- gpio_set_mode() PA2を、GPIO_CNF_OUTPUT_ALTFN_PUSHPULLに設定。 こうするとUSART2_TXの機能にに割り当てられる。 STM32では、ALTFN/AF/オルタナティブファンクション に設定すると、PINがUARTなどの機能に割り当てられる。 ピンが多いパッケージの場合はさらにピンのリマップができる。
- usart_set_ で始まる関数でUARTの設定を行う。
- usart_enable()で USART2を有効化
- usart_send_blocking()で1バイト送信する。
uart.c
# include <libopencm3/stm32/rcc.h>
# include <libopencm3/stm32/gpio.h>
# include <libopencm3/stm32/usart.h>
static void clock_setup(void)
{
rcc_clock_setup_in_hse_8mhz_out_72mhz();
/* Enable GPIOA clock. */
rcc_periph_clock_enable(RCC_GPIOA);
/* Enable clocks for USART2. */
rcc_periph_clock_enable(RCC_USART2);
}
static void usart_setup(void)
{
/* Setup GPIO pin GPIO_USART2_TX. */
gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_50_MHZ,
GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, GPIO_USART2_TX);
/* Setup UART parameters. */
usart_set_baudrate(USART2, 38400);
usart_set_databits(USART2, 8);
usart_set_stopbits(USART2, USART_STOPBITS_1);
usart_set_mode(USART2, USART_MODE_TX);
usart_set_parity(USART2, USART_PARITY_NONE);
usart_set_flow_control(USART2, USART_FLOWCONTROL_NONE);
/* Finally enable the USART. */
usart_enable(USART2);
}
static void gpio_setup(void)
{
gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_2_MHZ,
GPIO_CNF_OUTPUT_PUSHPULL, GPIO5);
}
int main(void)
{
int i, j = 0, c = 0;
clock_setup();
gpio_setup();
usart_setup();
/* Blink the LED (PA5) on the board with every transmitted byte. */
while (1) {
gpio_toggle(GPIOA, GPIO5); /* LED on/off */
usart_send_blocking(USART2, c + '0'); /* USART2: Send byte. */
c = (c == 9) ? 0 : c + 1; /* Increment c. */
if ((j++ % 80) == 0) { /* Newline after line full. */
usart_send_blocking(USART2, '\r');
usart_send_blocking(USART2, '\n');
}
for (i = 0; i < 800000; i++) /* Wait a bit. */
__asm__("nop");
}
return 0;
}
コンパイル
- makefile
# Makefile
###############################################################################
PROGRAM = stm32-uart
CROSS ?= arm-none-eabi
OBJS = uart.o
###############################################################################
ARCH_FLAGS = -DSTM32F1 -mthumb -mcpu=cortex-m3 -msoft-float -mfix-cortex-m3-ldrd
LDSCRIPT = libopencm3/lib/stm32/f1/stm32f103xb.ld
LIBOPENCM3 = libopencm3/lib/libopencm3_stm32f1.a
OPENCM3_MK = lib/stm32/f1
###############################################################################
CC = $(CROSS)-gcc
LD = $(CROSS)-ld
OBJCOPY = $(CROSS)-objcopy
OBJDUMP = $(CROSS)-objdump
SIZE = $(CROSS)-size
ELF = $(PROGRAM).elf
BIN = $(PROGRAM).bin
MAP = $(PROGRAM).map
DMP = $(PROGRAM).out
all:
@make firmware
CFLAGS += -O3 -Wall -g
CFLAGS += -fno-common -ffunction-sections -fdata-sections
CFLAGS += $(ARCH_FLAGS) -Ilibopencm3/include/ $(EXTRA_CFLAGS)
LIBC = $(shell $(CC) $(CFLAGS) --print-file-name=libc.a)
LIBNOSYS = $(shell $(CC) $(CFLAGS) --print-file-name=libnosys.a)
LIBGCC = $(shell $(CC) $(CFLAGS) --print-libgcc-file-name)
# LDPATH is required for libopencm3 ld scripts to work.
LDPATH = libopencm3/lib/
LDFLAGS += -L$(LDPATH) -T$(LDSCRIPT) -Map $(MAP) --gc-sections
LDLIBS += $(LIBOPENCM3) $(LIBC) $(LIBNOSYS) $(LIBGCC)
firmware: $(LIBOPENCM3) $(BIN) $(DMP) size
$(ELF): $(LDSCRIPT) $(OBJS)
$(LD) -o $@ $(LDFLAGS) $(OBJS) $(LDLIBS)
$(DMP): $(ELF)
$(OBJDUMP) -d $< > $@
%.bin: %.elf
$(OBJCOPY) -S -O binary $< $@
%.o: %.c board.h
$(CC) $(CFLAGS) -c $< -o $@
$(LIBOPENCM3):
CFLAGS="$(CFLAGS)" make -C libopencm3 $(OPENCM3_MK) V=1
.PHONY: clean size
clean:
rm -f $(OBJS) $(ELF) $(BIN) $(MAP) $(DMP)
size: $(PROGRAM).elf
@echo ""
@$(SIZE) $(PROGRAM).elf
@echo ""
- libopencm3をgitで持ってくる。
$ git clone https://github.com/libopencm3/libopencm3.git
- makeする。 (makeするとlibopencm3もコンパイルするが、ログは割愛)
$ make
arm-none-eabi-gcc -O3 -Wall -g -fno-common -ffunction-sections -fdata-sections -DSTM32F1 -mthumb -mcpu=cortex-m3 -msoft-float -mfix-cortex-m3-ldrd -Ilibopencm3/include/ -c uart.c -o uart.o
arm-none-eabi-ld -o stm32-uart.elf -Llibopencm3/lib/ -Tlibopencm3/lib/stm32/f1/stm32f103xb.ld -Map stm32-uart.map --gc-sections uart.o libopencm3/lib/libopencm3_stm32f1.a /usr/local/Cellar/gcc-arm-none-eabi-49/20150609/bin/../lib/gcc/arm-none-eabi/4.9.3/../../../../arm-none-eabi/lib/armv7-m/libc.a /usr/local/Cellar/gcc-arm-none-eabi-49/20150609/bin/../lib/gcc/arm-none-eabi/4.9.3/../../../../arm-none-eabi/lib/armv7-m/libnosys.a /usr/local/Cellar/gcc-arm-none-eabi-49/20150609/bin/../lib/gcc/arm-none-eabi/4.9.3/armv7-m/libgcc.a
arm-none-eabi-objcopy -S -O binary stm32-uart.elf stm32-uart.bin
arm-none-eabi-objdump -d stm32-uart.elf > stm32-uart.out
text data bss dec hex filename
1300 12 0 1312 520 stm32-uart.elf
ダウンロード
- st-flashを使ってstm32-uart.binをflashのある0x8000000に書き込む。
$ st-flash write stm32-uart.bin 0x8000000
2016-10-29T07:43:25 INFO /tmp/stlink-20160608-18806-p6zk82/stlink-1.2.0/src/stlink-common.c: Loading device parameters....
2016-10-29T07:43:25 INFO /tmp/stlink-20160608-18806-p6zk82/stlink-1.2.0/src/stlink-common.c: Device connected is: F1 Medium-density device, id 0x20036410
2016-10-29T07:43:25 INFO /tmp/stlink-20160608-18806-p6zk82/stlink-1.2.0/src/stlink-common.c: SRAM size: 0x5000 bytes (20 KiB), Flash: 0x20000 bytes (128 KiB) in pages of 1024 bytes
2016-10-29T07:43:25 INFO /tmp/stlink-20160608-18806-p6zk82/stlink-1.2.0/src/stlink-common.c: Attempting to write 1312 (0x520) bytes to stm32 address: 134217728 (0x8000000)
Flash page at addr: 0x08000400 erased
2016-10-29T07:43:25 INFO /tmp/stlink-20160608-18806-p6zk82/stlink-1.2.0/src/stlink-common.c: Finished erasing 2 pages of 1024 (0x400) bytes
2016-10-29T07:43:25 INFO /tmp/stlink-20160608-18806-p6zk82/stlink-1.2.0/src/stlink-common.c: Starting Flash write for VL/F0/F3 core id
2016-10-29T07:43:25 INFO /tmp/stlink-20160608-18806-p6zk82/stlink-1.2.0/src/stlink-common.c: Successfully loaded flash loader in sram
1/1 pages written
2016-10-29T07:43:25 INFO /tmp/stlink-20160608-18806-p6zk82/stlink-1.2.0/src/stlink-common.c: Starting verification of write complete
2016-10-29T07:43:25 INFO /tmp/stlink-20160608-18806-p6zk82/stlink-1.2.0/src/stlink-common.c: Flash written and verified! jolly good!
動作確認
- ST-LINKのUSBシリアルのデバイス名は、 ls してみて、USBを挿したら表示されるデバイスを探してください。
$ls -l /dev/tty.*
/dev/tty.usbmodem1423
- UART出力を確認するのにcuを使いました。速度を38400bpsに設定します。
$ sudo cu -l /dev/tty.usbmodem1423 -s 38400
Connected.
6789012345678901234567890123456789012345678901234567890
12345678901234567890123456789012345678901234567890123456789012345678901234567890
12345678901234567890123456789012345678901234567890123456789012345678901234567890
- cuを停止するときは ~. を入力します。
その他
- はじめ、基板のPA2のコネクタを見ていたら、UART出力されないなぁと思って、チップのPA2の端子を見たら、出力されているのを見つけて、NUCLEOのドキュメントを読んだらUSBシリアルにつながっていると書いてあるのを発見しました。ちゃんとドキュメントを読みましょう。
- libopencm3はサンプルコードがたくさんあるのと、githubから持ってきてgccでコンパイルが通るのが良いです。
- PA2の端子に直結した場合、UART出力は2Mbpsで動作することを確認しました。