ペリフェラル
組み込みシステムにおいて、ペリフェラルはマイクロコントローラー(MCU)の機能を拡張するハードウェアコンポーネントです。これにより、MCU は入力と出力、通信、タイミング処理などを担うことで、外部の世界とやり取りできるようになります。
CPU はプログラムロジックの実行を担当する一方で、ペリフェラルはハードウェアとのやり取りという実作業の大部分を担い、多くの場合 CPU の処理を肩代わりします。これにより、CPU は重要なタスクに集中でき、ペリフェラルは専用の機能を独立して、または最小限の監視のもとで処理できます。
オフロード
オフロードとは、特定のタスクを CPU 経由でソフトウェアとして直接実行するのではなく、ハードウェアペリフェラルに委譲する実践を指します。これにより、性能が向上し、消費電力が低減され、並行動作が可能になります。たとえば、次のような例があります。
- UART ペリフェラルは、CPU が他のロジックの処理を続けている間も、DMA(Direct Memory Access)を使ってバックグラウンドでデータの送受信を行えます。
- Timer は、CPU の介入なしに、正確な遅延や周期的な割り込みを生成するよう設定できます。
- PWM コントローラーは、CPU が継続的にピンをトグルし続けなくても、モーターを連続的に駆動できます。
オフロードは、限られた処理能力を効率的に活用するための、組み込みシステムにおける重要な設計戦略です。
一般的なペリフェラルの種類
以下は、組み込みシステムでよく使われる代表的なペリフェラルの種類です。
| Peripheral | Description |
|---|---|
| GPIO (General Purpose Input/Output) | ボタン、LED、センサーなどの外部ハードウェアとやり取りするために、入力または出力として設定できるデジタルピンです。 |
| UART (Universal Asynchronous Receiver/Transmitter) | デバイス間でデータを送受信するために使われるシリアル通信インターフェースで、デバッグや Bluetooth のようなモジュールの接続によく使われます。 |
| SPI (Serial Peripheral Interface) | マスター・スレーブ構成を用いて、マイクロコントローラーを SD カード、ディスプレイ、センサーなどのペリフェラルに接続するために使われる高速な同期通信プロトコルです。 |
| I2C (Inter-Integrated Circuit) | センサーやメモリチップのような低速ペリフェラルをマイクロコントローラーに接続するために使われる 2 線式シリアル通信プロトコルです。 |
| ADC (Analog-to-Digital Converter) | センサーやその他の信号源からのアナログ信号を、マイクロコントローラーが処理できるデジタル値に変換します。 |
| PWM (Pulse Width Modulation) | 電力供給を制御できる信号を生成し、LED の調光、モーターの速度制御、サーボの駆動によく使われます。 |
| Timer | 遅延の生成、時間間隔の測定、イベントのカウント、または特定の時刻での動作のトリガーに使われます。 |
| RTC (Real-Time Clock) | システムの電源がオフのときでも、通常はバッテリーによって保持され、現在の時刻と日付を追跡します。 |
Rust におけるペリフェラル
組み込み Rust では、ペリフェラルはシングルトンモデルを使ってアクセスします。Rust の中核的な目標の 1 つは安全性であり、それはハードウェアアクセスの管理方法にも及びます。プログラム内の 2 つの部分が同時に同じペリフェラルを誤って制御してしまわないようにするため、Rust はこのシングルトンアプローチによって排他的な所有権を強制します。
シングルトンパターン
シングルトンパターンは、各ペリフェラルのインスタンスがプログラム全体で 1 つしか存在しないことを保証します。これにより、複数のコードが同じハードウェアリソースを同時に変更しようとすることで生じる一般的なバグを防げます。
このパターンは、すでに BSP クレートで見てきました。そこでは、ボード全体(すべてのペリフェラル、ピン、設定を含む)がシングルトンとしてラップされています。
nRF hal でも、ペリフェラルはシングルトンオブジェクトとして公開されています。
コードを変更する
それでは、Rust のシングルトンモデルを使ってマイクロコントローラーのペリフェラルのインスタンスを作成するように、src/main.rs ファイルを変更してみましょう。
ステップ 1: Peripherals をインポートする
まず、src/main.rs を開いて、必要な import を追加します。
#![allow(unused)] fn main() { use nrf52833_hal::pac::Peripherals; }
ステップ 2: ペリフェラルの所有権を取得する
main 関数の中に、次の行を追加します。
#![allow(unused)] fn main() { let peripherals = Peripherals::take().unwrap(); }
この行は、次のことを行います。
- デバイスのペリフェラルの所有権を取得します。
Peripherals構造体のインスタンスを返します。この構造体には、GPIO などのすべてのハードウェアブロックへのアクセスが含まれています。- プログラムの存続期間中に呼び出せるのは 1 回だけです。もう一度呼び出すと、
Noneが返されます。