質問するログイン新規登録

回答編集履歴

1

追記

2017/04/04 02:41

投稿

nullbot
nullbot

スコア910

answer CHANGED
@@ -2,4 +2,91 @@
2
2
 
3
3
  必要なのはメモリーマップとペリフェラル、あとはCで書こうとするなら割り込みハンドラの設定にattributeディレクティブを使うのでコンパイラのドキュメント。
4
4
 
5
- 私は一から書いたはありません。ベクターテーブルの設定をしたり、リンカースクリプトをいじる程度です。開発環境で用意されたもの以外使ってません。HEWとかCS+とかでは生成されますよね。
5
+ 私は一から書いたはありません。ベクターテーブルの設定をしたり、リンカースクリプトをいじる程度です。開発環境で用意されたもの以外使ってません。HEWとかCS+とかでは生成されますよね。
6
+
7
+ ## 2017/04/04追記
8
+ あまりピンと来てないようなので実例としてSTM32F103についてみてみました。
9
+ 結構時間をかけたのでめんどくさがらずに見てもらえると嬉しいです。
10
+ 間違いがあったらごめんなさい。
11
+
12
+ [STM32F103データシート](http://www.st.com/content/ccc/resource/technical/document/datasheet/01/c1/9e/ad/b3/21/4b/d3/CD00253742.pdf/files/CD00253742.pdf/jcr:content/translations/en.CD00253742.pdf)
13
+
14
+ このマイコンは内蔵でFlashとSRAMがあるので、とりあえず内蔵Flashからブートさせることを考えます。
15
+ P39のメモリマップをみるとSRAMが0x20000000 - 0x20017FFF、FLASHが0x08080000 - 0x080FFFFF, 0x08000000 - 0x0807FFFF, 0x00000000 - 0x000FFFFFにあります。
16
+
17
+ Power on reset時の挙動を確認します。[CortexM3のドキュメント](http://infocenter.arm.com/help/topic/com.arm.doc.ddi0337e/DDI0337E_cortex_m3_r1p1_trm.pdf)を見ます。(適当に拾ったドキュメントなので最新版かはわかりません)
18
+
19
+ 5-20にReset時の挙動が書いてあります。ベクターテーブルのoffset0番地を読んでSP(スタックポインタ)とSP_main(メインスタックポインタ)を設定。PC(プログラムカウンタ)をベクターテーブルのoffsetから読み出す、LR(リンクレジスタ)を0xFFFFFFFFに。
20
+
21
+ 5.9.1にベクターテーブルの記述例が書かれてます。先の説明通りスタックポインタのトップ番地(をスタックサイズから計算してる)次にPowerISR(リセット時の割り込みハンドラ, スタートアップルーチン)、その他割り込みハンドラの記述。
22
+
23
+
24
+ ついでに5.9.2にブート時の処理が書かれてます。
25
+ 変数の初期化。.bss(初期値が0の変数が割り付けられる)セクションを0クリアして.data(初期値が0でない変数が割り付けられる)をRAMにコピーします。
26
+ その他もろもろをして割り込み許可。main関数へジャンプ。
27
+
28
+
29
+ つづいてリンカースクリプトの記述。メモリマップ見ながら適当に。(といいつつ他人のを拝借)
30
+
31
+ [ソース](https://github.com/bjornfor/stm32-test/blob/master/stm32.ld#L1)
32
+
33
+ はじめに定数定義をして、ENTRY()でエントリーポイントの指定。あとはベクターテーブル分があって、.text, .rodataを>flashに。あとは.data, .bss, .stackを >ramに割り付けます。
34
+ LMAコメントしてあるところがありますが、変数なのでRAM上に割り付けますが、初期値があるのでflashからコピーしてこないとプログラムが誤動作します。のでこれをスタートアップルーチンで初期化してやります。
35
+
36
+ ようやくスタートアップルーチンの記述。
37
+ Cで書くなら[コンパイラ](http://infocenter.arm.com/help/topic/com.arm.doc.dui0491gj/DUI0491GJ_arm_compiler_reference.pdf)にリンカースクリプトで書いたように配置してもらう必要があるのでディレクティブを使います。
38
+
39
+ 5-59に__attribute__((section("name"))) 関数属性というのがありますこれを使ってそれぞれ書いていきます。
40
+ あと__attribute__((used))をつけておくと安心みたいです。(詳しいことは知りません)
41
+
42
+ ```
43
+ //リンカースクリプトで定義した変数
44
+ extern unsigned long _sidata, _sdata, _edata, _sbss, _ebss, _stack_size;
45
+
46
+ //スタック
47
+ unsigned char stack_base[_stack_size] __attribute__ ((section ("._stack")));
48
+
49
+ //main関数
50
+ extern int main();
51
+
52
+ //リセットハンドラ
53
+ void ResetISR(void);
54
+
55
+ /* ベクターテーブル */
56
+ void (* const vectors[])(void) __attribute__ ((section(".isr_vector"))) = {
57
+ stack_base + sizeof(stack_base),
58
+ ResetISR,
59
+ NmiSR,
60
+ FaultISR,
61
+ 0, // Populate if using MemManage (MPU)
62
+ 0, // Populate if using Bus fault
63
+ 0, // Populate if using Usage Fault
64
+ 0, 0, 0, 0, // reserved slots
65
+ SVCallISR,
66
+ 0, // Populate if using a debug monitor
67
+ 0, // Reserved
68
+ 0, // Populate if using pendable service request
69
+ 0, // Populate if using SysTick
70
+ // external interrupts start here
71
+ Timer1ISR,
72
+ GpioInISR
73
+ GpioOutISR,
74
+ I2CIsr
75
+ };
76
+
77
+ /* スタートアップルーチン */
78
+ void ResetISR() __attribute__ ((used)) {
79
+ //clear BSS
80
+ for (unsigned long *dest=&_sbss; dest<&_ebss;) *(dest++) = 0;
81
+
82
+ //copy initialized data
83
+ for (unsigned long *dst=&_sdata, *src=&_sidata; dst<&_edata;) *(dst++) = *(src++);
84
+
85
+ //call main
86
+ main();
87
+ }
88
+ ```
89
+
90
+ これだけやれば最低限動きますし、ここに割り込み許可とかCPUの機能の有効化、ペリフェラルの設定を入れてもいいですし、main関数でやってもいいです。
91
+
92
+ 実際書いてみるとほとんど使い回せて、変更する部分ってスタックサイズだとかベースアドレス、あとはベクターテーブルくらいですよね。