LoginSignup
10
9

More than 3 years have passed since last update.

Lチカ! STM32 NucleoでBareMetal

Last updated at Posted at 2020-04-30

この記事は すごくなりたいがくせいぐるーぷ #GWアドベントカレンダー の 1日目(枠)の記事です。

すごくなりたいがくせいぐるーぷ | GWアドベントカレンダー

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

TL;DR

この記事は正直 too longです

  • Nucleo-F303K8上で、BareMetalからLチカができます。
  • BareMetalをやったことがあり、303k8でのやり方を探している方は、とりあえずYuseiito/stm32-blink見たら全部わかります。

Introduction

Hello,World!
Yusei Itoと申します。
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

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

STMicroinstrumentsによる製品です。
国内だと秋月電子通商なんかで手に入ります。

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

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

環境要件

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

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

スクリーンショット 2020-04-30 20.15.09.png

  • Homebrewを使います
$ 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はコンパイラです。ややこしいので気をつけてください。

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

stlink

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

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

$ brew install stlink

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

$ 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

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

各部の解説を優先したかったので、一部の入れ子構造などが不明瞭になっている部分があります。
適宜 リポジトリからコードを読んで理解してください。

条件

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

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

リンカスクリプトを書く

メモリレイアウトを決める

今回使うSTM32F303K8は、データシートの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
  • 割り込みベクタテーブルの設定は、最小でやる上では必要ないマイコンもありますが、今回の場合は必要です。理由は後述します。

今回はこれらを、以下のように分割します。
メモリマッピングの図
特に、isr_vector はメモリ上でのアドレスがデータシートで規定されているので、必ず先頭に配置される必要があります。

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

リンカスクリプトを書く

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

Entrypointを指定する

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

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

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

メモリ領域を設定する

ldscript.ld
MEMORY
{
 RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 12K
 FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 64K
}

次に、メモリの領域を設定します。

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

の形で指定します
リンカスクリプトを書く際は、イコールや演算子の両側に必ずスペースを入れてください。以前それでハマったことがあります。

セクションの分割

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

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

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

ようやくCの世界へ

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

GPIOの設定

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

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

IMG_4247.jpg

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

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

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

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

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

main.c
#define mem(ADDR) *((volatile uint32_t *)(ADDR))

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

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

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 のアドレスは、
スクリーンショット 2020-04-30 18.22.40.png

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

GPIOペリフェラルの説明部にある
スクリーンショット 2020-04-30 18.21.17.png

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

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

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

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

スクリーンショット 2020-04-30 18.39.31.png

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

main.c
    mem(GPIOB_MODER) = (0b01 << 2 * 3); // Set '01' to GPIOB port 3

くどくなるので以降画像を省略しますが、GPIOB_MODERはGPIOB上の各ピンに対して2bitつづつ対応しているので、2bitの01を3つシフトしてGPIO3の場所に格納します。

main.c
mem(GPIOB_OTYPER) = 0x0000;         //All-push oull

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

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

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


% 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.cmain.o をリンクしています。
ldscript.ldリンカスクリプトも忘れず指定してください。

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

最後に st-flashkernel.bin0x8000000番地から書き込んで完了です。

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を移植するつもりです。

情報について

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

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

ソースコード

文中でも紹介しましたが、GitHubにあります。
Yuseiito/stm32-blink

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

一次情報

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

title&link description 得られる情報
ARM®v7-M ArchitectureReference Manual アーキテクチャのリファレンスマニュアル ARM命令などアセンブリ言語について
Reference Manual : RM0316-STM32F303xB/C/D/E, STM32F303x6/8, STM32F328x8, STM32F358xC, STM32F398xEadvanced ARM®-based MCUs SoCのリファレンスマニュアル 各レジスタのアドレスなど
Datasheet:DS9866 - STM32F303x6/x8 Datasheet SoCのデータシート 記憶域の大きさやアドレスをさくっと把握したいときに
Nucleo 303K8 User manual Nucleo Boradのユーザーマニュアル オンボードLEDに繋がっているピンを把握する
Cortex-M4テクニカルリファレンスマニュアル CPUのリファレンスマニュアル 存在するレジスタについて。特にフラグレジスタなど。アセンブリ命令についても。
Cortex-M4 Devices Generic User Guide CPUのユーザガイド 上のより平たく書いてある。

n>1 次情報

title & link 得られる情報
.syntax, コメント、ローカルラベル|デバイスビジネス開拓団 Thumb命令とは何か、ローカルラベルの意味など
RaspberryPiでSTM32開発環境の構築 STM32の開発環境構築。
セクションとか.textとか 各セクションの役割など
リンカスクリプトの書き方 リンカスクリプトの書き方。特に ENTRY など初期設定の部分
debugging with gdb GDBでデバッグしていてわからなくなったときの心の支え
アセンブラ指令 - 2019年度 システムプログラミング .word擬似命令など、アセンブリ言語のディレクティブについて
フリーソフトウェア徹底活用講座(7) .weak擬似命令などについて
STM32 bare-metal made easy 近いことをやっているので流し読みしました。
“Bare Metal” STM32 Programming (同上)
オペレーション部の記述方法 | CS+ V4.01.00 .section 擬似命令の属性について
STM32 Nucleo Boardスタートアップルーチン ★特に参考にしました。スタートアップの仕方など
macOS】ARMのGCCコンパイル環境を構築する(brewから公式「GNU Arm Embedded Toolchain」をインストール) brewによるgcc-arm-none-eabi環境構築

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


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

10
9
5

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
9