ARM(LPC1114)をUbuntu12.04で使う(その1)

.16 2012 ARM-setup comment(0) trackback(0)


いろいろ試行錯誤したが、まずは最小限のコードでLチカをする方法をまとめてみる。
変なGUIは使わなくて済むなら使わないに越したことは無いのでここでは以下の環境での開発を想定している。

- コンパイラ、標準ライブラリ、リンカ: gcc
- ライタ: lpc21isp
- デバイスライブラリ: 今回は使わない

ようするにお気に入りのエディタ(vimとかvimとか)でコーディングしてmake一発で書き込みまでできる環境を作るのが目標。

1. Compiler, library, linkerの準備
% sudo apt-get install gcc-arm-linux-gnueabi
似たようなパッケージにgcc-arm-linux-gnuabihfというのがある。これはHardware Floatの略で浮動小数演算をH/Wの演算器を使うようなバイナリを吐くもののようだ。
現状ではまだ不具合があるようなのでとりあえずHFでない方を使うことにした。

これでarm-gcc用の標準ライブラリ(libgcc1-armel-crossあたり)も一緒に入ると思う。


2. 最低限必要なコードを書く
とにかく手っ取り早く動作確認をするためにタイマーとか使わずに適当な周波数でLチカすることにする。
CLKも通常なら精度の高いシステムPLL用のCLKかそれをPLLで逓倍したものを使うのだが今回はリセット直後に選択されている方の内部RC発振器(12MHz)をそのまま使う。
10/18追記
MAINCLKSEL入力の内蔵RC発振器もSYSPLL入力の内蔵RC発振器も同一物っぽいので修正

まずはGPIO1DATA, GPIO1DIRの定義が必要なのでメモリマップを調べる。
www.nxp.com/documents/user_manual/UM10398.pdf

GPIOの定義でuint32_tを使うのでstdint.hをインクルードする。

#include <stdint.h>

#define GPIO1DIR		(*(volatile uint32_t*)0x50018000)
#define GPIO1DATA		(*(volatile uint32_t*)0x50013FFC)

int main()
{
	int i;
	GPIO1DIR |= 1 << 5;
	for (;;) {
		for (i = 0; i < 1024 * 1024; i++)
			GPIO1DATA = GPIO1DATA;
		GPIO1DATA ^= 1 << 5;
	}
}

メモリマップを見るとこんな感じになっている。
+-----------------+ 0xFFFF_FFFF
|   (Reserved)    |
+-----------------+ 0xE010_0000
|    Private      |
| Peripheral Bus  |
+-----------------+ 0xE000_0000
|   (Reserved)    |
+-----------------+ 0x5020_0000
| AHB Peripheral  |
+-----------------+ 0x5000_0000
|   (Reserved)    |
+-----------------+ 0x4008_0000
| APB Peripheral  |
+-----------------+ 0x4000_0000
|   (Reserved)    |
+-----------------+ 0x2000_0000
|   (Reserved)    |
+-----------------+ 0x1FFF_4000
|  16KB Boot ROM  |
+-----------------+ 0x1FFF_4000
|   (Reserved)    |
+-----------------+ 0x1000_1000
|    4KB SRAM     |
+-----------------+ 0x1000_0000
|   (Reserved)    |
+-----------------+ 0x0000_8000
|   16KB Flash    |
+-----------------+ 0x0000_0000

さらに16KB Flashの部分の先頭(0x0000_0000 - 0x0000_00C0)に書く内容は決まっている。

0x00(から32bit=4byte、つまり0x00-0x03): スタックポインタの初期値。スタックは下方向に伸びるようなので0x1000_1000を初期値にすればよさそう。
0x04: リセットベクタ。リセット後この番地に飛ぶ。今回はそのままmainに飛んで欲しいのでmain関数のアドレスを入れればよさそう。(普通は初期化ルーチンを指定してそこからmainを呼び出す)
ここから先(0x08 - 0xc0)は割り込みのハンドラのアドレスを延々書きこむようになっている。

0xC0以降は自由にプログラムや定数を置いてよい。

0x00-0xC0に置く定数だが、main.cに定数として定義してそれをリンカスクリプトで0番地に貼るようにするのが手っ取り早い。

main.cにこんな感じで追加して
void *const vector[] __atribute__ ((section(".VECTOR"))) = {
	0x10001000, main, ...(その他割り込みハンドラの指定; 略)...
};

リンカスクリプトをこんな感じに書く。
MEMORY
{
	ROM (rx) : ORIGIN = 0x00000000, LENGTH = (32k)
	RAM (w)  : ORIGIN = 0x10000000, LENGTH = (4k)
}

SECTIONS
{
	.text :
	{
		KEEP(*(.VECTOR))
		*(.text .text.*)
		*(.rodata .rotdata.*)
	} > ROM
} 

そういえばPICでは割り込みベクタがなかったような。
割り込み条件が満たされると一様に0x04に飛んでくるからそこで各フラグを調べてどの割り込みか判別するという面倒な方法をとっていた気がする。
PIC24シリーズではこの辺も変わってるのかな?

AVRでは先頭が割り込みベクタになっているがこちらは単純にアドレスが書いてあるわけではなく、
0番地 rjmp RESET
1番地 rjmp EXT_INT0
...
のようなコードの形で書き込まれる。
先頭がRESETの割り込みベクタなので実行開始時に0x00番地を実行してRESETのところに飛ぶという賢い方法になっている。
スタックポインタの設定はRESETに飛んだ先で行う。

その点ARMは標準的な方法かもしれない。

あとはmakeすればhexファイルができる。
main.c, LPC1114.ld, Makefileを[ここ]に置いた。


3. 書き込む

今回はライタとしてFT232RLを使った。
接続はデータシートのとおり。TESTピンをGNDに落とすのを忘れると動かない。

lpc21ispのソースを拾って一部加筆する。
lpcprog.cの116行目(LPCtypes配列の要素中)に次の一行を追加。
 { 0x1A40902B, "1114FN.../102", 32, 4, 8, 1024, SectorTable_17xx, CHIP_VARIANT_LPC11XX },

コンパイルすれば実行ファイルができるので書きこむ。
書きこむ前に一旦UART経由の書き込みモードでARMを再起動する。
具体的にはReset、PIO0_1をLに落としてReset解除、PIO0_1解除とするとUARTで飛んできたデータをFlashに書き込むモードになる。

先ほどのhexファイルをLPC1114のFlashに流しこむ。

% sudo lpc21isp -control obj/Lchick.hex /dev/ttyUSB0 115200 12000

/dev/ttyUSB0のところは環境によって違うかもしれない。
% dmesg | grep ttyUSB
とかで探すとよいのかな?

小人さんに祈りながら書きこむとこれでLチカ(14pin)が動くはず。

10/17追記
lpc21ispで書き込むとき、-controlを付けると書きこみ完了時にDTR、RTSがHになるのでライタのコネクタを挿し直さなくてもすぐに実行が開始される。
関連記事

  • comment
  • secret
  • 管理者にだけ表示を許可する

trackbackURL:http://yuranos.blog11.fc2.com/tb.php/224-c5831805