moriya9n.github.io

View My GitHub Profile

ソフトバンクのサイバー大学 雑誌読み放題!月額380円で200誌以上が読み放題 デザイナー専用の転職支援サービス【ReDesigner】

組み込みとC言語に関する雑な説明

by moriya9n

IoT 用デバイスや、リアルタイム性が要求されるような機器を開発する時、 ワンチップマイコンが使われることが依然としてあるかと思う。

組み込み系開発の経験の無い人に教え欲しいと言われることが たまにあるので、自分の知っている範囲でおおまかに説明してみたいと思う。

マイコンの構成について

ワンチップマイコンは、CPU、プログラム用フラッシュメモリ、RAM、タイマ、 入出力機能(I/O)、 通信機能(シリアル通信など)などがワンチップに 収められたものだ。

レジスタ

CPU には、制御用のレジスタと、汎用レジスタがある。

制御用のレジスタには、典型的には、以下のようなものがある。

プログラムカウンタはプログラムの実行中のアドレスを、スタックポインタは現在のスタックのアドレスを、 ステータスレジスタは、割り込みの状態や演算結果などを持っている。

汎用レジスタは、数個あって、計算やメモリの参照に使う。汎用レジスタは CPU にもよるが、 レジスタによって扱える処理が違うことが多いと思う。 レジスタは高速に動作するが、足りない時はRAMを使う。(レジスタに比べると遅い。)

メモリ空間

0番地から始まるアドレスから

のように配置されるが、配置はマイコンによって異なる。 SFR はタイマ、I/O、通信などの制御とデータの読み書き行うための特殊機能レジスタ領域。

典型的には、プログラム用フラッシュメモリの先頭に先頭に、プログラム用のフラッシュメモリがあり、 ここに割り込みベクタと、プログラムが配置される。

割り込みベクタの先頭は一般には、リセットベクターで、電源投入後実行する プログラムの番地が書かれている。

CPU はサブルーチンコール、または、割り込み発生時に、 プログラムカウンタ(と割り込みの場合はステータスワード)をスタックに積み、 リターンすると(割り込みの場合はステータスワードをスタックから戻し)プログラムカウンタを スタックから戻し、サブルーチンコール/割り込み発生の前の状態に復帰する。

スタックは、また、汎用レジスタの一時的な退避(push)、復旧(pop)に使われる。

C 言語 tips

CPU はメモリから機械語(インストラクションコード)を読み出して実行するが、 この機械語と対応づいているものがアセンブリ言語である。

インストラクションコードは各メーカー毎に違うが、それでは移植性も悪く、 記述もしづらいため、可能な部分はC言語を使用して記述するのが一般的であろう。

メモリ、スタック等の初期化をして C 言語の main ルーチンを呼び出すまでの部分 (スタートアップルーチン)は、アセンブラで記述する。

メモリ割り当て

組み込み系で少し注意を要するのが C 言語の定数、変数であろうか。

定数、グローバル/スタティック変数、初期化の付いたグローバル/スタティック変数は 自動的には割り当てられず、開発環境(IDE)のほうで、指定が必要となる。(セクション、と呼ばれる)

例えば、Renesasのデータとプログラムのセクション割り当て にある、「セクション再配置属性」。プログラムは、.text 領域に、const は const に、グローバル/スタティック変数は、data(初期化あり) または bss(初期化なし) に割り当てられる。 事前にどこに置くか計画しておいたほうがよいだろう。

ローカル変数はスタック上に確保される。 スタック領域を超えてもチェックされず、変数領域を壊すので、 どの程度のメモリをスタックに使うのか、事前の計画が必要。 巨大なローカル変数を宣言するとプログラムが暴走するだろう。

SFR へのアクセス

C 言語でタイマ、I/O、通信などの SFR にアクセスするには、ポインタを使う。 SFR は書き込み結果が読めるとは限らず、最適化すると問題になるので volatile uint8_t * のような宣言にする。

型宣言

int のサイズは CPU によって違うが、組み込み系では顕著だ。私は stdint.h をインクルードして uint32_t などとするようにしている。

printf

デバッグ用に printf を使いたいと思うかもしれないが、まず、シリアル通信機能などを使って、getchar、putchar を 実装することが必要。

RTOS(リアルタイムOS)

簡単な機能のものであれば、main 内でループして、OSを使わないという選択肢も多いと思う。

しかし機能が複雑な場合は、uITRON や freertos などといった RTOS を使って タスクに分割するほうがわかりやすいし、保守性、拡張性も高い。

RTOS は、以下のような機能を持っている。

uITRON だと、タスクが起動し、タイマ、セマフォ、メッセージなどのイベント待ちになり、 イベントを受けてタスクが実行される。 同時に待ちがあると、優先度に応じてタスクが選ばれる。 (割り込みでなければ、次の待ちになるまで他のタスクに切り替わったりしない。)

タスク間はメッセージキューなどでデータの受け渡しを行う。

デバッグ

プログラムをコンパイルした後、デバッガでターゲットに書き込む。 IDE から実行し、ブレークポイントを指定して止めることができる。

ブレークポイントはハードブレークポイトとソフトブレークポイントがある。 ソフトブレークポイントは、メモリをブレーク用の命令に書き換えてブレークする。 ハードブレークポイントは、指定のアドレスにマッチすると止まるが、いくつかしか設定できない。

ウォッチドッグタイマを使っている場合は、デバッグの時だけ define などで無効になるようにしてデバッガを使う。(ブレークしている間にリセットされるので)

このエントリーをはてなブックマークに追加
tags: