デバイスドライバとは
組み込みシステムのアプリケーションを作っている場合、CPUの機能だけを使うことはほぼないと言っていいでしょう。単純な計算だけだとしても、何らかの入力が必要であり、何らかの出力が存在します。通常、ハードウェアの操作を行うために用意するソフトウェアを「デバイスドライバ」と呼んでおり、パソコンなどでも頻繁に利用されています。
組み込みシステムにおいても同様で、さまざまなハードウェア・インタフェースを利用する場合に、デバイスドライバを利用します。ただ、パソコンのようなシステムで使うデバイスドライバと異なり、組み込みシステムの場合は、ON/OFFなどの操作からパケットの送信/受信といった抽象度の高い操作まで幅広く扱うものが多く、組み込みシステムのアプリケーションを組む上では、デバイスドライバの特性も理解しておく必要があります。そして、上流のトップダウン設計だけではうまく実装できないのも、ハードウェアを使用した組み込み機器ならでは。ぜひその辺りの勘所を掴んでもらえればと思います。
CPUコアやハードウェアに依存しないという考え方
RTOSやデバイスドライバの多くは、ハードウェアの差異を吸収する部分を持っており、大きく分けて2つの「依存部」が存在します。1つは、今回のようにCortex-AであるとかRISC-VとかCortex-RといったCPUコアに依存した部分のことを指す「CPU依存部」。もう1つが、デバイスの周辺機能や割り込みコントローラに対応した「デバイス依存部」です。この依存部が存在する理由は、アプリケーション開発において、極力CPUやデバイスに依存しないように作ることが組み込みシステムの基本的な考え方であり、デバイスが変わったとしてもアプリケーションの機能を維持することが可能だからです。
RTOSは、主にCPUのコア依存部を吸収することと、デバイスの依存部分を吸収することで、上位のアプリケーション部における依存部を減らすことに重きが置かれています。(図1)
デバイスドライバは、大きく分けると3つのタイプが存在します。1バイトずつのデータを扱う【キャラクタ型デバイス】、複数バイトデータ単位のデータで扱う【ブロック型デバイス】、【それ以外のデバイスドライバ】です。
- 【キャラクタ型デバイス】UART、I2C、SPIなど
- 【ブロック型デバイス】Ethernet、USB、I2Sなど
- 【それ以外のデバイスドライバ】割り込み、タイマ、RTC、PWMなど
Cortex-Aに代表されるレジスタが32ビット長のCPUが一般的になってから、周辺機能用のレジスタも32ビットに拡張されてきましたが、機能によっては、8ビットや16ビットのみが有効といったレジスタも存在します。
また、提唱のCMSISに代表されるようなデバイスドライバのテンプレート化により、ベンダー各社のオリジナルボードや周辺機能においても、標準化の流れが一般的になっており、「BSP(Board Support Package)」という形で、提供してくれる状況になっています。
BSP(Board Support Package)の使い方
BSPは、組み込みシステムボード(ハードウェア)に搭載されている機能を使うために用意されたAPIを使うことが目的です。
今回のSOLID Starter Kit for RZ/A1Hには、ボードが付属していますが、CPUがもっている機能を全て試すことができるかというと、まだ十分とは言えません。RZ/A1Hはものすごく多くの機能を搭載しています。それを名刺サイズのボードにすることはできても、すべての機能を使えるようにすることとは異なります。今回SOLIDで用意しているBSP(図2)も、すべての機能を準備することは、とても大変です。
ですので、ボードに見合ったBSPを用意するのが一般的です。それ以外に必要な機能は、機能的に近いAPIに倣って、自分で作成すればいいのです。
自分で作成するメリットは、コードサイズの調整がしやすいことと、ハードウェアに対して、どのような操作をするか自分で決められることです。どんな順番でハードウェアにアクセスするか、どのようにしたら期待通りの動きになってくれるのか?を調べたり、突き詰めていくことが、組み込みシステムの醍醐味です。
この例は、led.cのコードです。GPIOの先につながっているLEDを駆動するだけなので、CPUからのアクセスは書き込みのみで実装されています。
もう少し深く見ていきましょう。
GPIOには、RZA_IO_RegWrite_16
というAPIが使われています。16ビット長で書き込むためのAPIであることが理解できます。同じく、RZA_IO_RegRead_16
という読み出し専用のAPIも用意されています。これらは、io_reg.h
に定義されているので、8ビット・16ビット・32ビット長のAPIが用意されています。
また、IO空間に関しては、動画で説明した通り、物理アドレスから仮想アドレスに変換されて扱われています。SOLIDが提供しているマクロには、事前にこの設定が反映されてるので、RZA_IO_RegRead/RegWrite
のAPIを使用すれば、仮想アドレスを基準にしたIOレジスタアクセスが可能になっています。
宿題
bspフォルダ内のrza_io_regrw.c
に実際の定義がありますので、どのように作られているのか調べて見ましょう。
RZ/A1H用のGPIO出力操作
実際は、どのようなAPIでGPIOが操作するのかみてみましょう。
GPIOを動かす前に、SOLID-IDE上にある「ソリューションエクスプローラー」をみてください。driver、boot、bsp、SOLIDApplicationの項目があります。そのなかのbspをクリックし、led.cを開きます。
LedInit()
GPIOを使った初期化関数LedOn()
GPIO経由でLEDを点灯させる関数LedOff()
GPIO経由でLEDを消灯させる関数
この3つのAPIで、LEDは操作しています。回路としては、ActiveLowになっています。詳しくは下記を参照ください。
LEDによるActive HighとActive Low回路の違い
LEDをデバイスから点灯させるには、GPIOのピンに「1」あるいは「0」を書き込む必要があります。ここで注意しなければならないのが、「1」だから点灯する訳ではないということです。GPIOなどの汎用入出力ピンは、2種類の駆動方法があります。
上図のような電源とGPIOの間にLEDが入る場合を、「アクティブ・ロー(Active Low)」と呼びます。この場合は、GPIOに1を設定しても、電源とGPIOの出力電圧が変わらないため、電流変化がなく、LEDは点灯しません。GPIOに0を設定すると、電源とGPIOの間で電位差が発生します。その電位差を抵抗RでLEDを点灯させるのに必要な電流にして、GPIOの0出力端子に流れ込みます。
上図のようなGPIOの先に、LEDとGNDがつながった場合を、「アクティブ・ハイ(Active High)」と呼びます。この場合は、GPIOを1を設定すると、GPIOとGND間で電位差が発生します。Active Lowの0出力の時と同様に、電位差からLED駆動電流を抵抗Rに流してLEDを点灯させることができます。GPIOを0に設定すると、GPIOとGND間で電位差が生じないため、電流が流れません。そのため、LEDも点灯しません。
void LedInit(void) { /* ---- P1_10 : LED1 ---- */ /* Port initialize */ RZA_IO_RegWrite_16(&GPIO.PIBC1, 0, GPIO_PIBC1_PIBC14_SHIFT, GPIO_PIBC1_PIBC14); RZA_IO_RegWrite_16(&GPIO.PBDC1, 0, GPIO_PBDC1_PBDC14_SHIFT, GPIO_PBDC1_PBDC14); RZA_IO_RegWrite_16(&GPIO.PM1, 1, GPIO_PM1_PM14_SHIFT, GPIO_PM1_PM14); RZA_IO_RegWrite_16(&GPIO.PMC1, 0, GPIO_PMC1_PMC14_SHIFT, GPIO_PMC1_PMC14); RZA_IO_RegWrite_16(&GPIO.PIPC1, 0, GPIO_PIPC1_PIPC14_SHIFT, GPIO_PIPC1_PIPC14); /* Mode : Port mode */ /* Terminal output level : High level output */ /* Port mode : Output mode */ RZA_IO_RegWrite_16(&GPIO.PBDC1, 0, GPIO_PBDC1_PBDC14_SHIFT, GPIO_PBDC1_PBDC14); RZA_IO_RegWrite_16(&GPIO.P1, 1, GPIO_P1_P14_SHIFT, GPIO_P1_P14); RZA_IO_RegWrite_16(&GPIO.PM1, 0, GPIO_PM1_PM14_SHIFT, GPIO_PM1_PM14); } void LedOn(void) { RZA_IO_RegWrite_16(&GPIO.P1, 0, GPIO_P1_P14_SHIFT, GPIO_P1_P14); } void LedOff(void) { RZA_IO_RegWrite_16(&GPIO.P1, 1, GPIO_P1_P14_SHIFT, GPIO_P1_P14); }
RZ/A1H用のGPIO入力操作
次に、GPIOピンを入力として機能するように設定してみましょう。GPIOには、単純な入出力以外にも、多くの機能が割り当てられています。どのような機能で使うかは、RZ/A1Hのハードウェアリファレンスマニュアルで確認してください。
GPIOは、Cortex-A9から見ると、内部バスに接続された機能ブロックとして見えています。そして、機能ブロックが配置されている場所には、アドレスが割り付けられています。CPUからアクセスする場合には、このアドレスに対してアクセスを行います。通常、機能ブロックとして割り付けられたベースアドレスの設定を確認します。SOLID Starter Kit for RZ/A1Hは、MMUを使用していますので、物理アドレス表記から仮想アドレスへの変換をして、アドレスを読み替える必要があります。
- 物理ベースアドレス
PORTn 0xFCFE3000 JPORT 0xFCFE7B00
- 仮想ベースアドレス
PORTn 0xF1203000 JPORT 0xF1207B00
n
はポート番号 0から11番まである。
そして、GPIOのポート番号と各レジスタに合わせたアドレスにアクセスする必要があります。
略語 | レジスタ名 | オフセットアドレス |
---|---|---|
Pn | ポートレジスタ | PORT + 0000H + nx4 |
PSRn | ポートセット/リセットレジスタ | PORT + 0100H + nx4 |
PPRn | ポート端子リードレジスタ | PORT + 0200H + nx4 |
PMn | ポートモードレジスタ | PORT + 0300H + nx4 |
PMCn | ポートモード制御レジスタ | PORT + 0400H + nx4 |
PFCn | ポート機能制御レジスタ | PORT + 0500H + nx4 |
PFCEn | ポート機能制御拡張レジスタ | PORT + 0600H + nx4 |
PNOTn | ポートNOTレジスタ | PORT + 0700H + nx4 |
PMSRn | ポートモードセット/リセットレジスタ | PORT + 0800H + nx4 |
PMCSRn | ポートモード制御セット/リセットレジスタ | PORT + 0900H + nx4 |
PFCAEn | ポート機能制御追加拡張レジスタ | PORT + 0A00H + nx4 |
PIBCn | ポート入力バッファ制御レジスタ | PORT + 4000H + nx4 |
PBDCn | ポート双方向制御レジスタ | PORT + 4000H + nx4 |
PIPCn | ポートIP制御レジスタ | PORT + 4000H + nx4 |
JPPR0 | ポート端子リードレジスタ | JPORT + 0020H |
JPMC0 | ポートモード制御レジスタ | JPORT + 0040H |
JPMCSR0 | ポートモード制御/リセットレジスタ | JPORT + 0090H |
JPIBC0 | ポート入力バッファ制御レジスタ | JPORT + 0400H |
SNCR | シリアルサウンドインタフェース/ノイズキャンセラ制御 | JPORT + 0C00H |
例えば、ポート1のPPR1レジスタにアクセスするには、PPR1
=仮想ベースアドレス+0x0200
+1x4
=0xf1203204
にアクセスしなければいけません。
レジスタアクセスはできそうですね。では、ポートの初期化を見ていきましょう。GPIOの初期化手順や設定方法は、RZA/1Hのハードウェアリファレンスマニュアルに記載されていますが、概ね下記の設定をしています。
初期化
- PIBCx ポート入力バッファ制御レジスタ
- PBDCx ポート双方向制御レジスタ
- PMx ポートモードレジスタ
- PMCx ポートモード制御レジスタ
- PIPCx ポートIP制御レジスタ
モード設定
- PBDCx ポート双方向制御レジスタ
- Px ポートレジスタ
- PMx ポートモードレジスタ
実際に動画で使っているSW入力は、下記の設定をしています。P1_8
とPI_9
の2つを使用しています。まずは、ポートの初期化です。
RZA_IO_RegWrite_16(&GPIO.PIBC1, 0, GPIO_PIBC1_PIBC18_SHIFT, GPIO_PIBC1_PIBC18); RZA_IO_RegWrite_16(&GPIO.PIBC1, 0, GPIO_PIBC1_PIBC19_SHIFT, GPIO_PIBC1_PIBC19); RZA_IO_RegWrite_16(&GPIO.PBDC1, 0, GPIO_PBDC1_PBDC18_SHIFT, GPIO_PBDC1_PBDC18); RZA_IO_RegWrite_16(&GPIO.PIBC1, 0, GPIO_PIBC1_PIBC19_SHIFT, GPIO_PIBC1_PIBC19); RZA_IO_RegWrite_16(&GPIO.PM1, 1, 8, GPIO_PMC1_PMC18); RZA_IO_RegWrite_16(&GPIO.PM1, 1, 9, GPIO_PMC1_PMC19); RZA_IO_RegWrite_16(&GPIO.PMC1, 0, GPIO_PMC1_PMC18_SHIFT, GPIO_PMC1_PMC18); RZA_IO_RegWrite_16(&GPIO.PMC1, 0, GPIO_PMC1_PMC19_SHIFT, GPIO_PMC1_PMC19); RZA_IO_RegWrite_16(&GPIO.PIBC1, 0, GPIO_PIBC1_PIBC18_SHIFT, GPIO_PIBC1_PIBC18); RZA_IO_RegWrite_16(&GPIO.PIBC1, 0, GPIO_PIBC1_PIBC19_SHIFT, GPIO_PIBC1_PIBC19);
次に、入力モードへの設定です(最終的にはIRQとして使用したいため、IRQ機能の設定にしています)。
RZA_IO_RegWrite_16(&GPIO.PMC1, 1, GPIO_PMC1_PMC18_SHIFT, GPIO_PMC1_PMC18); RZA_IO_RegWrite_16(&GPIO.PMC1, 1, GPIO_PMC1_PMC19_SHIFT, GPIO_PMC1_PMC19); RZA_IO_RegWrite_16(&GPIO.PM1, 1, 8, GPIO_PMC1_PMC18); RZA_IO_RegWrite_16(&GPIO.PM1, 1, 9, GPIO_PMC1_PMC19); RZA_IO_RegWrite_16(&GPIO.PIBC1, 1, GPIO_PIBC1_PIBC18_SHIFT, GPIO_PIBC1_PIBC18); RZA_IO_RegWrite_16(&GPIO.PIBC1, 1, GPIO_PIBC1_PIBC19_SHIFT, GPIO_PIBC1_PIBC19); RZA_IO_RegWrite_16(&GPIO.PFCAE1, 0, GPIO_PFCAE1_PFCAE18_SHIFT, GPIO_PFCAE1_PFCAE18); RZA_IO_RegWrite_16(&GPIO.PFCAE1, 0, GPIO_PFCAE1_PFCAE19_SHIFT, GPIO_PFCAE1_PFCAE19); RZA_IO_RegWrite_16(&GPIO.PFCE1, 1, GPIO_PFCE1_PFCE18_SHIFT, GPIO_PFCE1_PFCE18); RZA_IO_RegWrite_16(&GPIO.PFCE1, 1, GPIO_PFCE1_PFCE19_SHIFT, GPIO_PFCE1_PFCE19); RZA_IO_RegWrite_16(&GPIO.PFC1, 0, GPIO_PFC1_PFC18_SHIFT, GPIO_PFC1_PFC18); RZA_IO_RegWrite_16(&GPIO.PFC1, 0, GPIO_PFC1_PFC19_SHIFT, GPIO_PFC1_PFC19);
これで、P1_8
とP1_9
は入力として使用することができるようになりました。では、ハードウェアとしてSWをつないでみましょう。
SW入力回路
SWは、ボタンの状態によって信号レベルを変えるために使用することができます。
図4は、SW入力回路の代表的な接続方法です。GPI(General Porpuse Input)は、汎用入力ポートの入力のみで使用するという意味です。SWはGPIに入力として接続しています(図4)。SOLID Starter KitのRZ/A1Hでは、CN1にP1_8
とP1_9
が引き出されていますので、そのピンを接続しています。
SWが押されていなければ、電流IはGNDへは流れませんので、Lowを認識します。SWが押されて入れば、Vccから抵抗Rを通してGNDに向かって電流Iが流れるので、Highとして認識します。
デバイスによって、Highの認識レベルおよびLowの認識レベルが異なります。実装では、Rに2KOhmを使用しています。GPIには、約1.65mAの電流が流入します。RZ/A1Hの電気的特性のDC特性では、最大2mAとなっていますので、仕様内であると言えます。
デバッガとは
SOLIDを使用して、デバッグを行うにはアプリケーションのビルドが完了していなければいけません。ビルドができていないものをデバッグはできません。
コンパイラの機能で、文法的な誤りを検出して、コーディングのミスを防ぐようにはできますが、プログラムの流れやロジック、APIの呼び出しタイミングなど、実行させないとわからないことがでてきます。そんな時に役に立つツールが「デバッガ」です。
デバッガは、ソフトウェアツールの名称ですが、組み込みシステム機器開発の場合、ターゲットと呼ばれるボードと接続します。デバッガとターゲットを接続する機器が「ICE(アイス)」と呼ばれています。今は、ほとんどの機器がJTAG経由で接続することになっているので、「JTAG-ICE(ジェータグアイス)」と呼んでいます(図5)。
SOLID Starter Kit for RZ/A1Hには、京都マイクロコンピュータ社製JTAG-ICE「PARTNER-Jet2 Model10」が同梱されています。国内のデバッガベンダーでも屈指の機能を兼ね備えている大変高機能なJTAG-ICEなのです。これを同梱してあるということは、できないことはほとんどないと言っていいくらいです。
動画では、デバッガの使い方を紹介しています。デバッガモードに移行すると、SOLID-IDEは画面が切り替わります。この時、ソースコードの編集はできないように保護されます。そして、ソースコードの行番号の箇所で、「Ctrl+8」かダブルクリックをしてみましょう。●がつきます。「ブレークポイント」と呼ばれる目印で、このポイントの直前までが実行されて、このポイントでプログラムが一時停止します(図6)。
通常、このようにブレークポイントをいくつも設定することで、プログラムの流れやシーケンスが期待した通りの動きをしているかの確認や、その時の変数の値やメモリの内容を確認します。
JTAG-ICEを使わない方法でデバッグすると、いわゆる「printfデバッグ」と呼ばれる方法で、変数をシリアル経由で表示することになります。ただし、表示するだけの機能になるので、変数を書き換えてシーケンスを変えたりすることはできません。
PARTNER コマンド ウィンドウを使いこなそう!
デバッガを使ってデバイスドライバのデバッグをしていると、レジスタの値が正しい値なのかどうか知りたくなる時があります。そんな時に役に立つのが、「PARTNER コマンド ウィンドウ」です。デバッガのメニューから、選択します(図7)。
PARTNER コマンド ウィンドウの中では、下記のコマンドが使用できます。
- pib 8ビットレジスタに対する読み込みコマンド
- piw 16ビットレジスタに対する読み込みコマンド
- pid 32ビットレジスタに対する読み込みコマンド
- pob 8ビットレジスタに対する書き込みコマンド
- pow 16ビットレジスタに対する書き込みコマンド
- pod 32ビットレジスタに対する書き込みコマンド
今回は、デバイスドライバおよびデバッガについて学んでみました。デバッガについては講座の後半でもう少し詳細に解説する予定です。組み込み機器の特性上、ハードウェア・ソフトウェアの両方に対してアプローチすることが重要であることを理解してもらえたらと思います。
SOLID Starter Kit for RZ/A1H
今回使用するSOLID Starter Kit for RZ/A1Hは、評価ボード、デバッガ、IDEなどが、すぐに使えるAll in One Packageになっています。
“SOLID”は、コンパイラ、RTOS、デバッガ、IDE全てが含まれ、またそれぞれが専用設計された、京都マイクロコンピュータの組み込み向けソフトウェア開発プラットフォーム。ITRONベースでありながら、本格的なプログラムローダーや、バグの実行時自動検出機能などを搭載します。
最新情報をメーカーサイトで見る
Amazon.co.jpで買う
こちらも是非
“もっと見る” Cortex-A+RTOS編
ローダーを使いこなして、分割開発の悩みを解決
組み込みシステムにおけるローダーとは、電源が入り、RESET信号が解除されて動き出す最初のプログラムです。ローダーの役割は、プログラムをメモリ上に読み込むためにメモリコントローラの初期化を行い、プログラムが保存されているフラッシュメモリからSRAMやDRAMなどのメモリに展開して実行するまでの動作を行います。
デバッグの効率化とRTOSタスク実装
組み込みアプリ開発において、リアルタイムOS(RTOS)を使うメリットをこれまでに十分説明してきました。今回は、いよいよタスクの実装です。組み込みアプリを構築する上で、最も重要な部分です。使用する「SOLID-OS」は、Toppers/ASP3をベースにしたμITRON準拠のRTOSです。
キャッシュを有効活用するためのMMU設定
「システムが暴走してRESETしなければいけなくなった」という経験は誰にでもあるかと思いますが、組み込みシステムでは、そのリスクを限りなく0にする必要があります。WDTなど周辺機能がサポートしてくれることもありますが、その時点で中核となるソフトウェアが停止してしまっている状況は、誰にとってもメリットは少ないものです。