Lチカ! STM32 NucleoでBareMetal

**この記事は  [すごくなりたいがくせいぐるーぷ](https://qiita.com/organizations/sgg?page=1)  #GWアドベントカレンダー の 1日目(枠)の記事です。**

[すごくなりたいがくせいぐるーぷ | GWアドベントカレンダー](https://gw-advent.9wick.com/calendars/2020/87)

1日目にやって書こうと思ってたら執筆中にビルド環境がぶっ壊れて直してたら2日目でしたごめんなさい。

## TL;DR
### この記事は正直　too longです
- Nucleo-F303K8上で、BareMetalからLチカができます。
- BareMetalをやったことがあり、303k8でのやり方を探している方は、とりあえず[Yuseiito/stm32-blink](https://github.com/YuseiIto/stm32-blink)見たら全部わかります。




## Introduction
Hello,World! 
[Yusei Ito](https://github.com/yuseiito)と申します。
BareMetalを愉しむ系高校生です(←と言いながら先週までNuxt書いてたフロントエンド野郎は私ですごめんなさい)

私は過去に、セキュリティキャンプ全国大会で、BareMetalなRaspberry pi上で動くOSを書くゼミに参加してことがあり、それ以降ベアメタルのコンピュータを時々触って遊んでいます。

この記事では、**BareMetal超入門**として、初めてのBareMetalな方が **BareMetalなNucleo F303K8ボードで、LED Blink(Lチカ)を動かす**ことを目指します。

### BareMetal is 何
BareMetal(ベアメタル)とは、OSやドライバといったソフトウェアを介さずに、本当に何もソフトウェアが書き込まれていない状態のコンピュータを指します。
例えば、今回の場合、便利な Free RTOS,mbedOSのような便利なOS, HAL(Hardware Abstruction Layer)といったものを全く用いずに書いていきます。
組み込みOSの開発などはこの状態から行われます。
この体験を通して、main関数に届くまでに行われていることを理解し、コンピュータ(とりわけARM)のより深い理解ができるはずです。

### NucleoF303K8 is 何

![イメージ.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/200465/29573874-ea4f-e680-ec4d-30717b33a6f0.jpeg)

**NUCLEO-F303K8**は、STM32F303K8T6というSoC(System on Chip, 雑に言えばワンチップのコンピュータ)が載ったワンボードマイコンです。

STMicroinstrumentsによる製品です。
国内だと[秋月電子通商](http://akizukidenshi.com/catalog/g/gM-10172/)なんかで手に入ります。

ARMマイコンの一つで、 **ARMv7m**アーキテクチャの**Cortex-M4**というCPUがチップ内に搭載されています。

今回はこのマイコンで、LEDを点滅させる、いわゆる「**Lチカ**」を行います。

## 環境要件
この記事は、以下の環境で検証されています。

- Macbook Pro Late 2013　(2013つらwぴえん🥺)
- MacOS 10.15.3

<img width="698" alt="スクリーンショット 2020-04-30 20.15.09.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/200465/6447ffba-c909-7b8e-378e-3619839877d5.png">


- Homebrewを使います

```zsh
$ brew --version
Homebrew 2.2.13
Homebrew/homebrew-core (git revision b8af; last commit 2020-04-28)
Homebrew/homebrew-cask (git revision 511a2; last commit 2020-04-28)

```

- VSCode(を使ってほしいですこれは布教です)


###人間側の要件
- 日本語読解の技能
- C言語に対する一定程度の理解
- 気合い


##開発環境構築


### gcc-arm-none-eabi
ARM32用のGNUツールチェーンです。
パソコン上でマイコン用のコードをビルドするためのクロスコンパイル環境を作ります。

コンパイラ　`gcc` 、リンカ`ld`、 アセンブラ`as`、デバッガ　`gdb`などなどのツールが一気に入ります。

なお、 `gcc-arm-none-eabi`　はこのツールチェーンの総称、`arm-none-eabi-gcc`はコンパイラです。ややこしいので気をつけてください。


```zsh
$ brew update
$ brew upgrade
$ brew tap ArmMbed/homebrew-formulae
$ brew install gcc-arm-none-eabi
```

### stlink
Nucleoボード上に搭載されたROMライタ兼デバッガであるST-LINKV2のドライバ的なやつです。
使わなくてもUSBドライブに`.bin`を放り込めば使えるようにNucleoはできているので、なくてもいいのですが、**この記事ではバイナリの書き込みに使ってます**。

デバッグするときにGDBサーバとしても使うので、一応入れておくといいと思います。

```zsh
$ brew install stlink
```


これで準備は完了です。
参考までに、この記事を執筆している環境は

```zsh
$ 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.

$ arm-none-eabi-as --version
GNU assembler (GNU Tools for Arm Embedded Processors 7-2018-q2-update) 2.30.0.20180329
Copyright (C) 2018 Free Software Foundation, Inc.
This program is free software; you may redistribute it under the terms of
the GNU General Public License version 3 or later.
This program has absolutely no warranty.
This assembler was configured for a target of 'arm-none-eabi'.

$ arm-none-eabi-ld --version
GNU ld (GNU Tools for Arm Embedded Processors 7-2018-q2-update) 2.30.0.20180329
Copyright (C) 2018 Free Software Foundation, Inc.
This program is free software; you may redistribute it under the terms of
the GNU General Public License version 3 or (at your option) a later version.
This program has absolutely no warranty.

$ arm-none-eabi-objcopy --version
GNU objcopy (GNU Tools for Arm Embedded Processors 7-2018-q2-update) 2.30.0.20180329
Copyright (C) 2018 Free Software Foundation, Inc.
This program is free software; you may redistribute it under the terms of
the GNU General Public License version 3 or (at your option) any later version.
This program has absolutely no warranty.

$ st-util --version
v1.6.0-256-ge87bdff

```

環境ができたら、早速作業を初めましょう!
なお、この記事は読めばコードの全体像が再現できるようには **書かれていません**

各部の解説を優先したかったので、一部の入れ子構造などが不明瞭になっている部分があります。
適宜 [リポジトリ](https://github.com/YuseiIto/stm32-blink)からコードを読んで理解してください。


## 条件
この記事では、**Cで書けるところはなるべくCで書きつつ** 、**タイマ等は用いずに**できる範囲の 、**最小構成で**Lチカを実現することにします。

本当の最小構成にすればアセンブリだけで書けるのですが、辛いのでなるべくCで書きます。




## リンカスクリプトを書く
### メモリレイアウトを決める
今回使うSTM32F303K8は、[データシート](https://www.st.com/resource/en/datasheet/stm32f303k8.pdf)の1ページによれば64KBytesの **Flash memory**(ROM) と、12KBytesの**SRAM**(RAM) が搭載されています。
(このほかに、4KBytesのCCMもありますが、必須ではないので本稿では扱いません。)

それらを、C言語を実行する上で必要になるいくつかのセクションに分割します。


| section | description |location|
|---|---|---|
|isr_vector|割り込みベクタテーブルです。|Flash|
|text|機械語プログラム本体を格納するセクション。|Flash|
|bss|初期値のない(または0の)変数|RAM|
|rodata|定数|Flash|
|data|初期値のある変数|Flash→RAM|
|heap|いわゆるヒープ領域です。mallocとかするときに取られる部分ですね。|RAM|
|stack|いわゆるスタック領域です。レジスタの内容を保存しておくときに使用される部分です。|RAM|

- 割り込みベクタテーブルの設定は、最小でやる上では必要ないマイコンもありますが、今回の場合は必要です。理由は後述します。

今回はこれらを、以下のように分割します。
<img width="510" alt="メモリマッピングの図" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/200465/b7a31b5b-75a5-c537-1d9d-437943c7953a.png">
特に、`isr_vector` はメモリ上でのアドレスがデータシートで規定されているので、必ず先頭に配置される必要があります。

この記述には、リンカスクリプトを用います。
既存のOSなら勝手に準備されてるものですが、BareMetalなのでこれから準備していきます
。



### リンカスクリプトを書く
プロジェクトディレクトリ内に `.ld`　拡張子をつけたファイルで記述します。

#### Entrypointを指定する
まず、`ENTRY()` ディレクティブを使って**はじめに実行される位置**をリンカに伝えます。

```ld:ldscript.ld
/* Entry Point */
ENTRY(_start)
```


ここでは、あとで設定する `_start` というラベルの位置を、entrypointに設定しています。



#### メモリ領域を設定する


```ld:ldscript.ld
MEMORY
{
 RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 12K
 FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 64K
}
```
次に、メモリの領域を設定します。

```
領域名(アクセス種類) : ORIGIN = <領域の初めのアドレス>, LENGTH = <領域の長さ>
```

の形で指定します
リンカスクリプトを書く際は、**イコールや演算子の両側に必ずスペースを入れて**ください。[以前それでハマった](https://blog.yuseiito.com/2019/08/15/SecCampDay2/)ことがあります。


#### セクションの分割

次に、前述したセクションの分割をやります。
セクションの記述は、

```ldscript.ld
SECTIONS{

}
```
の中で行います。


#### `FLASH`(ROM)内の設定

```ldscript.ld
  .isr_vector :
  {
    . = ALIGN(4);
    KEEP(*(.isr_vector)) /* Startup code */
    . = ALIGN(4);
  } >FLASH

  .text :
  {
    . = ALIGN(4);
    _stext = .; /* Start of code */
    /* text sections (contains code) */
    *(.text)
    *(.text*)
    . = ALIGN(4);
    _etext = .; /* End of code */
  } >FLASH

  .rodata :
  {
    . = ALIGN(4);
    /* rodata sections(contains constants) */
    *(.rodata)
    *(.rodata*)
    . = ALIGN(4);
  } >FLASH


```
`.`は、その行が表すメモリ上の位置を表します。
左辺にあればカーソルを動かすようなイメージ、右辺にあればその位置をあるラベルで表すことにするようなイメージです。

ここで、` . = ALIGN(4);` はメモリアライメントの設定をしています。
マイコンなのであまり大きな領域があるわけでもないので、今回は`4`と小さくしています。
`4`の単位はByteです。

`*(.section_name*)`のような記述のうち、初めのアスタリスクはそのセクションのデータをここに配置することを表し、二つ目のアスタリスクはセクション名のワイルドカードとして機能します。


リンカスクリプトにおいて、 `/* */`は見ての通りコメントです。

それぞれかっこの後に`>FLASH`とすることで、`MEMORY`で指定した`FLASH`の領域上の話であることを示しています。



#### dataセクションの設定

```ldscript.ld
  _sidata = LOADADDR(.data);

  .data : 
  {
    . = ALIGN(4);
    _sdata = .; 
    *(.data) 
    *(.data*)

    . = ALIGN(4);
    _edata = .; 
  } >RAM AT> FLASH

```


`data` セクションは、初期値があります(=>ROMで保存しておきたい)が、**変数領域**なので`.rodata` などとは区別して扱われるセクションで、**実行時には書き換えが発生します**(=>RAM上にいて欲しい)。そこで、 **ROM上に持っておいて後からRAMにコピーする**という手段を取ります。

そういう取り扱いをすることを表すのが `>RAM AT> FLASH` です。
コピーする処理は、後からスタートアップコードとしてアセンブリで書きます。

そのときに、FLASH上の(初期値が保存された)位置が必要になるので、`_sidata = LOADADDR(.data);`　として　`_sidata`(Start of Initial DATA)ラベルをつけています。

`LOADADDR`は、そのセクションをRAMに読んでくる元になるFLASH上のアドレスを返します。

#### `RAM`上の設定

```ldscript.ld

  . = ALIGN(4);
  .bss :
  {
    _sbss = .;
    *(.bss)
    *(.bss*)
    . = ALIGN(4);
    _ebss = .; 
  } >RAM

  .heap : {
        _sheap = .;
        /* 1KiB heap */ 
        . = . + 1024 ;
        . = ALIGN(4);
        _eheap = .;
  }> RAM
 
  .stack : {
        _estack = .;
        _stack = _estack + 1024;
  }> RAM

```

ここまでのリンカスクリプトを読んできているので、特に難しいところはないと思います。
`stack`セクションは無論LIFOですから、アドレスが減る方向に生えていきます。(上に積んでいくため).
それゆえ、先に`_estack`がきてから、`_sstack`　がくるようになっています。


これで、リンカスクリプトは完成です。

## スタートアップコードを書く
これまでは設定という感じでしたが、ここからはARMのアセンブリを書いていきます。
といってもアセンブリは大変なので、なるべく減らして、残りは全てCからするように頑張ります。



```startup.s
.syntax unified
.cpu cortex-m4
.fpu softvfp
.thumb

.global _start
```
一行目から説明が厄介です。
これは、ARMv7mの命令セットとThumb命令が混ざった、統合アセンブリ言語の書き方をするときに使っているようです。私はこの書き方に関しては今回初めて知りました。

`.cpu` はプロセッサの指定、`.fpu` は浮動小数ユニットの種類を指定しています。
今回はデフォルトの、ソフトウェアを用いた浮動少数方式を用いています。
`.thumb`はこれ以降をthumb命令として解釈することを示します。

`.global`は外部に公開するラベルであることを示しています。
今回の場合、リンカスクリプトで記述した`_start`の位置を外部に公開するために用いています。


ここからしっかりアセンブリになります。
アセンブリ言語そのものの説明はここでは省略しますが、コメントを読んで理解してください。
**めちゃくちゃ丁寧につけてます。**　

---

#### 2020/ 5 /2追記
ここからのアセンブリ、あまりこなれたコードにはなってません。完全に私のアセンブリ力の不足が現れてます。
非常に有用な指摘をいただいているので、ぜひコメント欄の @fujitanozomu さんのコメントを参考になさってください。私も非常に勉強になりました。

私の「こなれてない」コードは悪い例として残しておきますが、一部書き方の問題ではなく不具合が含まれていた箇所があったため、それに関しては本日記事中でも修正を致しました。
@fujitanozomu さんのご指摘に感謝します。

--- 

また、[**末尾にある資料たち**](#参考にしたなるかも情報) を色々見ると役立つと思います。

```startup.s
.section .text.start                     /* specify section */
_start:
 ldr   sp, =_estack 
 bl data_init                            /* Initialize data section */
 bl bss_clear                            /* Zero-clear BSS section */
 bl main                                 /* Run main() */
LoopForever:
 b LoopForever                           /* Jump to LoopForever */

data_init:                               /* Initialize data section function */
 ldr r1, =_sdata                         /* load addreses to Rn registers */
 ldr r2, =_sidata
 ldr r3, =_edata
 movs r4, 0                              /* Set R4 as 0 */
 1:
  cmp r1,r3                              /* Compare R1 and R3 */
  beq 2f                                 /* When equal, jump to foward-closest '2' label */
  ldr r5, [r2, r4]                       /* Load the value at r2(_sidata)+r4  */
  str r5, [r1]                           /* Store the value at r2(_sdata)+r4  */
  adds    r4, r4, #4                     /* Increment offset */
  adds    r1, r1, #4                     /* Increment address */
  bl 1b                                  /* Loop. Jump to '1' label closest backward. */
 2:                                      /* if r1 and r3 was equal, comes here*/
  bx lr                                  /* Return */

bss_clear:                               /* Clear BSS section function */
  ldr r1, =_sbss                         /* Load address of begin to r1 */ 
  ldr r2, =_ebss                         /* Load address of end to r2 */ 
  1:                                     /* Label for loop */ 
   cmp r1,r2                             /* Compare r1 and r2 */ 
   beq 2f                                /* If equal, jump to label '2' foward */ 
   movs r3,0                             /* store zero to r3 */
   str r3,[r1]                           /* Store r3's value(=zero) to address where r1 points */
   add r1, r1,4                          /* increment addrsss */
   b 1b                                  /*Jump to '1' label backward (loop) */
  2:                                     /* Loop exit here */
  bx lr                                  /* Return */

.section .isr_vector,"a"                 /* Specify another section */
	.word	_estack                  /* SP(Stack pointer) initial address */
	.word	_start                   /* Entrypoint */
```

`.isr_vector`の理解が厄介です。
ここは割り込みベクタで、ある割り込みが入ったときに飛ぶ先を指定しておきます。
メモリの先頭に置かれる領域です。
ARMの場合は**はじめがスタックポインタ、次にエントリポイントのアドレスを置く**と決まっていて、それ以外は省略しても**他の割り込みがかからない限り**安全に動かせますが、これらは初めの**SP**(Stack pointer),**PC**(Program counter) の内容を指定する上で重要ですので、記述します。[^1]

[^1]: なお、正確な割り込みベクタの取り扱いは、 Cortex-M4のマニュアル+STM32F303k8のマニュアルでアドレスの番地と割り込みの対応を、STM32F303K8のデータシートでFlashの先頭アドレスを確認することで情報が得られます。

ここまでで、スタートアップコードが書き終わりました。

## ようやくCの世界へ
皆様、お疲れ様でした。
これで、アセンブリやリンカといった楽しいがやたら重い部分はとりあえず終わりです。
Cを使うのに必要な設定が終わったので、これから先はペリフェラルの設定などをCで書いていきます。
なお、この記事はあくまでベアメタル超入門で、C超入門ではないので、細かい文法にはいちいち触れません。**特に、ポインタの扱いは極めて重要なので、確認しておいてください。**

### GPIOの設定
Cの世界ではじめに呼ばれるのは、無論main()関数です。
startupからそう指定したので当然ですね。

今回Lチカするのに使うのは、 `PB_3`　ポートです。

![IMG_4247.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/200465/8f82de3c-18fc-fb81-9f7f-2d0bbb957ed1.jpeg)

この緑のLEDにつながっているのがPB_3ポートです。
このことは、NUCLEO-303K8のユーザマニュアルから確認できます。

このmain()関数内で、Lチカまでにやるべきことは、以下のことになります。

1. RCC(Reset and Clock Control) の設定からGPIOにクロックを供給
2. GPIO本体の設定

これらの設定は、全てあるアドレスのレジスタに値を書き込むようにして行います。


それを楽に実装するため、今回は以下のようなマクロ関数を用意しておきます。

```c:main.c
#define mem(ADDR) *((volatile uint32_t *)(ADDR))
```
これは、アドレスを `uint32_t` 型のポインタに変換し、その値へのアクセスを 外側の*()で取得しているようなマクロ関数です。


また、各アドレスも先にプリプロセッサ指令で書いてしまいます。
きちんと書くときは構造体などを使うことできれいに書けるのですが、今回は雑にいくので、各レジスタをプリプロセッサで記述します。


```c:main.c
#define GPIOB_PORT_BASE 0x48000400U
#define GPIOB_MODER GPIOB_PORT_BASE + (uint32_t)0x00
#define GPIOB_OTYPER GPIOB_PORT_BASE + (uint32_t)0x04
#define GPIOB_PUPDR GPIOB_PORT_BASE + (uint32_t)0x0C
#define GPIOB_ODR GPIOB_PORT_BASE + (uint32_t)0x14

#define RCC_BASE 0x40021000U
#define RCC_AHBENR RCC_BASE + (uint32_t)0x14

```



`*_BASE`というのはGPIOBやRCCといった各ペリフェラルのレジスタの初めのアドレスで、 **Boundary Address**としてSTM32F303K8のマニュアルなどに記載されています。

そして、その **Boundary Addresss**からのオフセットを加えることで各レジスタのアドレスを表現しています。

そのオフセットは、マニュアルの各レジスタの説明に書いてあります。

例えば、 `GPIOB_MODER`　のアドレスは、
<img width="450" alt="スクリーンショット 2020-04-30 18.22.40.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/200465/ae44ad96-c56a-a437-d1dd-95c97055af2c.png">

の部分でGPIOBの開始アドレス `0x4800 0400`を見つけることができ、


GPIOペリフェラルの説明部にある
<img width="454" alt="スクリーンショット 2020-04-30 18.21.17.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/200465/6d8c2233-a2a1-7cb6-c2c3-7f9358cdd219.png">

の部分から、 **Address Offset**が `0x00`であると知ることができるので、
記述するのは `#define GPIOB_MODER GPIO_PORT_BASE + 0x00`　というようになります。
ただし、ビット幅によって加算が意図したように行われないと困るので、実際の記述では `uint32_t`にキャストしています。



ここまで来れば、あとは各レジスタに値を登録するだけになります。



まずはRCCからGPIOBにクロックを供給します。

```c:main.c
    mem(RCC_AHBENR) |= (1 << 18);   
```

<img width="444" alt="スクリーンショット 2020-04-30 18.39.31.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/200465/4b527ebd-02df-524b-514b-34baa4181c35.png">

レジスタの説明によれば、18bit目にIOPBをENableする設定があるので、 18ビット左シフトした1を入れています。それ以外のビットの設定は既存のままにしておくので、 OR論理で `|=` としています。

```c:main.c
    mem(GPIOB_MODER) = (0b01 << 2 * 3); // Set '01' to GPIOB port 3
```
くどくなるので以降画像を省略しますが、`GPIOB_MODER`はGPIOB上の各ピンに対して2bitつづつ対応しているので、2bitの`01`を3つシフトしてGPIO3の場所に格納します。

```c:main.c
mem(GPIOB_OTYPER) = 0x0000;         //All-push oull
```
`GPIOB_OTYPER`は出力部分の回路構成を設定しています。
詳細は　「マイコン　プッシュプル　オープンドレイン」とかで調べれば出てくると思います。
今回はすべてのpinに対してプッシュプル構成になるようにしました。
下位ビットしか使わないレジスタなので16bit長になっています。

```c:main,c
mem(GPIOB_PUPDR) = 0x000000000;     //No pullup-pulldown
```

最後に設定するのは`GPIOB_PUPDR`で、プルアップやプルダウンの設定をしています。
今回はどちらにもPullしないことにしています。
これも回路的な話なので、興味のある方はググって解決してください。



### いざ、Lチカ

```main.c
 int i=0;

    while (1)
    {
        if (i >  500000)
        {
            i = 0;

            if (mem(GPIOB_ODR) & (1 << 3))
            {
                mem(GPIOB_ODR) &= ~(1 << 3);
            }
            else
            {
                mem(GPIOB_ODR) |= (1 << 3);
            }
        }
        i++;
    }

```

Lチカは、無限ループ内でカウントし、` 500000`　に達したらポートの出力を反転しているだけのシンプルなものです。
これまで読んできたコードに比べればどうってことないと思いませんか?
これをタイマーを使って実装するようにするとまた面白いのですが、今回は最小でやるので割愛です。


## Build & Run


先にやっておいた環境構築ができていれば、

```zsh

% arm-none-eabi-as -c startup.s -o startup.o
$ arm-none-eabi-gcc -c main.c -o main.o -mcpu=cortex-m4 -Os
$ arm-none-eabi-ld -T ldscript.ld main.o startup.o -o kernel.out
$ arm-none-eabi-objcopy kernel.out -I ihex -O binary kernel.bin 
$ st-flash write kernel.bin 0x8000000

```

のようにすることでビルド・書き込みできます。

一行目では、ASを用いて`startup.s`をアセンブルして、オブジェクトファイル`startup.o`を作っています。

次に二行目で、 GCCを使って `main.c`をプリプロセス&コンパイルして`main.o`を作っています。
**`-mcpu=cortex-m4`　を忘れると動作しない**ので気をつけてください。

三行目では LDを使って `main.c` と `main.o` をリンクしています。
`ldscript.ld`リンカスクリプトも忘れず指定してください。

リンクされると、 `kernel.out` が書き出されます。
が、これだけではボードに直接書き込めないので、 objcopyを使って `.bin`ファイルを作るのが、4行目です。

最後に `st-flash`で`kernel.bin`を`0x8000000`番地から書き込んで完了です。

```
0-04-30T19:55:20 INFO common.c: Starting Flash write for VL/F0/F3/F1_XL core id
2020-04-30T19:55:20 INFO flash_loader.c: Successfully loaded flash loader in sram
  1/1 pages written
2020-04-30T19:55:20 INFO common.c: Starting verification of write complete
2020-04-30T19:55:20 INFO common.c: Flash written and verified! jolly good!

```

みたいなログが出れば書き込み完了です。 **Lチカがはじまっている**ことと思います。

**おつかれさまでした。**



## 後記
今回は、タイマーも使わないLチカを、書いていてだるくなるくらい丁寧に説明しました。
実のところ、今回のコードにはかなり色々な問題があるのですが、それでも立派にベアメタルで動かしたことに変わりはありませんので、これが初めてのベアメタルの方は喜んでいただくといいかと思います。

高いレイヤの進化がかなり早いのは確かですが、こうした低レイヤの話はなかなか変わりませんので、これからも楽しんでやっていただけると幸いです。

長文をお読みいただき誠にありがとうございました。

### What's Next???
1. 内部割り込みを使えるようにしましょう
2. printfできるようにしてみましょう
3. echoできるようにしてみましょう 
4. そこまでできたらOSとか書いてみるといいんじゃないでしょうか。

私はここに[kozos](http://kozos.jp/kozos/)を移植するつもりです。

## 情報について
**正確性に万全を期してはおりません。**
ベアメタル**超**入門を目指して書いた記事ですので、とにかく簡単に理解できて、嘘を言っていない程度に正確なように書いています。
ぜひ、これをやってみてベアメタルに惹かれた方は、他のものできちんと入門されると良いと思います。

建設的な改善の提案や、明らかな誤りの指摘を歓迎します。

## ソースコード
文中でも紹介しましたが、GitHubにあります。
[Yuseiito/stm32-blink](https://github.com/YuseiIto/stm32-blink)


## 参考に(した||なるかも)情報

### 一次情報
ARMやSTM公式の情報。
とりあえずこれを信じてください。

|title&link|description | 得られる情報|
|---|---|---|
|[ARM®v7-M ArchitectureReference Manual](https://static.docs.arm.com/ddi0403/eb/DDI0403E_B_armv7m_arm.pdf)| アーキテクチャのリファレンスマニュアル|ARM命令などアセンブリ言語について|
|[Reference Manual : RM0316-STM32F303xB/C/D/E, STM32F303x6/8, STM32F328x8, STM32F358xC, STM32F398xEadvanced ARM®-based MCUs](https://www.stmcu.jp/download?dlid=51492_en)|SoCのリファレンスマニュアル|各レジスタのアドレスなど|
|[Datasheet:DS9866 - STM32F303x6/x8 Datasheet](https://www.stmcu.jp/download?dlid=51470_en)|SoCのデータシート | 記憶域の大きさやアドレスをさくっと把握したいときに|
|[Nucleo 303K8 User manual](https://www.st.com/resource/en/user_manual/dm00231744-stm32-nucleo32-boards-mb1180-stmicroelectronics.pdf)|Nucleo Boradのユーザーマニュアル| オンボードLEDに繋がっているピンを把握する |
|[Cortex-M4テクニカルリファレンスマニュアル](http://infocenter.arm.com/help/topic/com.arm.doc.ddi0439cj/DDI0439CJ_cortex_m4_r0p1_trm_ja-JP.pdf)|CPUのリファレンスマニュアル|存在するレジスタについて。特にフラグレジスタなど。アセンブリ命令についても。|
|[Cortex-M4 Devices Generic User Guide](http://infocenter.arm.com/help/topic/com.arm.doc.dui0553b/DUI0553.pdf)|CPUのユーザガイド|上のより平たく書いてある。|



### n>1 次情報

| title & link | 得られる情報 |
|---|---|
|[.syntax, コメント、ローカルラベル｜デバイスビジネス開拓団](https://jhalfmoon.com/dbc/2019/09/27/%E3%81%90%E3%81%A0%E3%81%90%E3%81%A0%E4%BD%8E%E3%83%AC%E3%83%99%E3%83%AB%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B07-syntax-%E3%82%B3%E3%83%A1%E3%83%B3%E3%83%88%E3%80%81/)|Thumb命令とは何か、ローカルラベルの意味など|
|[RaspberryPiでSTM32開発環境の構築](https://qiita.com/mitazet/items/bfa944c0583b88cf486f)| STM32の開発環境構築。|
|[セクションとか.textとか](http://www.ertl.jp/~takayuki/readings/info/no02.html)|各セクションの役割など|
|[リンカスクリプトの書き方](http://blueeyes.sakura.ne.jp/2018/10/31/1676/)|リンカスクリプトの書き方。特に `ENTRY` など初期設定の部分|
|[debugging with gdb](http://exp1gw.ec.t.kanazawa-u.ac.jp/PC/gdb.html)|GDBでデバッグしていてわからなくなったときの心の支え|
|[アセンブラ指令 - 2019年度 システムプログラミング](http://www.swlab.cs.okayama-u.ac.jp/~nom/lect/p3/what-is-directive.html)| `.word`擬似命令など、アセンブリ言語のディレクティブについて|
|[フリーソフトウェア徹底活用講座(7)](https://www.cqpub.co.jp/interface/column/freesoft/2003/200303/3.htm)|`.weak`擬似命令などについて|
|[STM32 bare-metal made easy](https://blog.aureliocolosimo.it/posts/stm32-bare-metal-made-easy/)|近いことをやっているので流し読みしました。|
|[“Bare Metal” STM32 Programming](https://vivonomicon.com/2018/04/02/bare-metal-stm32-programming-part-1-hello-arm/)|(同上)|
|[オペレーション部の記述方法 ｜ CS+ V4.01.00](http://tool-support.renesas.com/autoupdate/support/onlinehelp/ja-JP/csp/V4.01.00/CS+.chm/Compiler-CCRX.chm/Output/ccrx05c0104y.html)|`.section` 擬似命令の属性について|
|[STM32 Nucleo Boardスタートアップルーチン](https://qiita.com/mitazet/items/54713ec4e89f0b57ac6e)|★特に参考にしました。スタートアップの仕方など|
|[macOS】ARMのGCCコンパイル環境を構築する（brewから公式「GNU Arm Embedded Toolchain」をインストール） ](https://www.yokoweb.net/2018/05/16/macos-gcc-arm-brew-install/)|brewによるgcc-arm-none-eabi環境構築|

加えて、 @ tnishinaga さんによるセキュリティキャンプの講義資料も復習の参考にさせていただきました。ありがとうございました。