STM32でlibopencm3を使ってUART

libopencm3を使ってUART出力をしてみました。

[libopencm3](http://www.libopencm3.org/)は、Cortex Mシリーズ向けの LGPLv3なライブラリです。

Macで作業しました。(あまりMacに依存する内容ではないです。)

#使用ハードウェア
* ST [NUCLEO-F103RB](http://www.st.com/content/st_com/ja/products/evaluation-tools/product-evaluation-tools/mcu-eval-tools/stm32-mcu-eval-tools/stm32-mcu-nucleo/nucleo-f103rb.html)
 * 秋月電子で購入
 * 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チカ](http://qiita.com/eggman/items/9f75aebf69033527e0b9)を見てください。

# サンプルコード
* 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()で１バイト送信する。

```c: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で動作することを確認しました。