RaspberryPiで有機ELディスプレイ(1)
SSD1306を動かしてみる
どもです。
前回のエントリまで、Arduinoで有機ELディスプレイSSD1306を動かしてきました。
今回は、このSSD1306をRaspberryPi/i2cで動かしてみます。
なお、ネットで調べてみると、pythonで動かしているサイトはたくさんあります。
しかし、C言語/pigpioを使用している例は見つけられませんでした。
それもあり、今回はC言語/pigpioを使用します!
1. 開発環境
今回のエントリは、以下の環境で開発を行っています。
項目 | 内容 |
---|---|
OS | Windows10 Pro(1909) |
CPU | i7-8700 |
メモリ | 16GB |
IDE | Eclipse Ver.2019-06(4.12.0) Build id 20190614-1200 |
また、RaspebrryPi/pigpioの環境は以下の通りです。
項目 | 内容 |
---|---|
HW | RaspberryPi3 model B |
OS | Raspbian GNU/Linux buster |
ライブラリ | Version 71 (gpioVersion()関数で確認) |
2. 配線
まず、RaspberryPiとSSD1306の接続/配線です。
配線は、下図の通りです。
RaspberryPiは、i2cのI/Fが2つ用意されています。
しかし、デフォルトで有効になっているのは、i2c1(BSC1)のみです。
ピン番号は、「2」と「3」です。
このピンを、SSD1306と接続します。
3. i2cの設定
接続が完了したら、次にRaspberryPiのi2cを有効化します。
既に設定済みの場合には、ココはスキップしてください。
i2cの有効化は、以下の手順で実施します。
-
RaspberryPiのターミナル上で、以下のコマンドを実施
sudo raspi-config
- 表示される画面の「interfacing Options」を選択する。
- 「I2C」を選択
- 「Would you like the ARM I2C interafaceto be enabled?」と表示されるので、「はい」(または「yes」)を選択する。
以上で、i2cの有効化は完了です。
RaspberryPiの再起動は、必要ありません。
4. i2cでSSD1306の制御
環境が整ったので、実際にSSD1306の制御を行います。
なお、SSD1306の制御に関する情報は、データシートを参照しています。
データシートは、秋月電商さんの購入ページから取得できます。
4.1. データフォーマット
SSD1306とi2cで通信する場合のデータフォーマットは、前述のデータシートのP.20(Figure 8-7)に記載されています。
このデータ構造は、SMBus Version 2.0 の仕様に一致しています。
また、pigpioのi2c用関数も、このバージョンに対応しています。
そのため、新たにこのフォーマットに準拠した通信を行うための関数を新規に実装する必要はありません。
4.1.1. Control byte
通信データ内の「Control byte」です。
「Control byte」は、「Co」ビットと「D/C#」ビット、および「0」6ビットから構成されます。
「Co」ビットと「D/C#」ビットの値と意味は、以下の通りです。
項目 | 内容 |
---|---|
Co | 「Continuation bit」継続ビット 0 → 送信される情報は、1バイトの「Data byte」 1 → 送信される情報は、2バイト以上の「Data byte」 |
D/C# | データ/コマンドの選択ビット 0 → 送信される「Data byte」が「コマンド」 1 → 送信される「Data byte」データが「データ」 |
4.1.2. Data byte
送信するコマンドまたはデータを設定します。
コマンドについては、データシートあるいは以降のページで記載する内容を参照してください。
4.2. SSD1306の設定
4.2.1. 動作設定の手順
SSD1306の動作の設定は、データシートの末尾に実行手順が記載されています。
具体的には、以下の手順/コマンドを実行します。
順番 | コマンド名前 | コマンド (バイトデータ) |
データ | 備考 |
---|---|---|---|---|
1 | Set MUX Ratio | A8h | 3Fh | マルチプレックス比 0~64まで設定可能 デフォルトは「63」 |
2 | Set Display Offset | D3h | 00h | 描画開始位置の縦方向のオフセット 0~63まで設定可能 デフォルトは「0」 |
3 | Set Display Start Line | 40h | 描画開始行 0~63まで設定可能 デフォルトは「0」 |
|
4 | Set Segment re-map | (A0h/)A1h | 描画開始列の位置 A0h→左側(左から右方向に描画) A1h→右側(右側から左側に描画) |
|
5 | Set COM Output Scan Direction | (C0h/)C8h | 描画開始行の位置 C0h→上側(上から下方向に描画) C8h→下側(下側から上側に描画) |
|
6 | Set COM Pins hardware configuration | DAh | 12h | データシート見ても、よくわからない…。 とりあえず、12hを指定。 |
7 | Set Contrast Control | 81h | 7Fh | 画面の明るさ(コントラスト) 1~256まで設定可能 デフォルトは7Fh |
8 | Disable Entire Display On | A4h | 画面表示を再開する RAMの内容を、ディスプレイに反映する |
|
9 | Set Normal Display | A6h | 画面表示方法の設定 ビットの値が 0:消灯 1:点灯 |
|
10 | Set Osc Frequency | D5h | 80h | 周波数設定 デフォルトは80h |
11 | Enable charge pump regulator | 8Dh | 14h | チャージポンプレギュレータの設定 デフォルトは10h 14hに設定する |
12 | Dispaly ON | AFh | ディスプレイ設定 |
4.2.2. 動作設定の実装
前述のシーケンスを実装します。
実装の際には、上記コマンドを1つずつではなく、全て一気に送信します。
具体的は、以下のコードとなります。
int ssd1306_initialize(uint handle)
{
int ret = 0;
uint8_t initCommand[33] = {
0x00, 0xA8, 0x3F, //SetMultiplexRatioコマンド - 0x3F(63)を指定
0x00, 0xD3, 0x00, //SetDisplayOffsetコマンド - 0x00(0)を指定
0x80, 0x40, //SetDisplayStartlineコマンド
0x80, 0xA1, //SetSegmentRemapコマンド//画面左上を原点に設定
0x80, 0xC8, //SetCOMOutputScanDirectionコマンド上→下方向へ、走査を実施
0x00, 0xDA, 0x12, //SetCOMPingsHardwareConfigurationコマンド
0x00, 0x81, 0x3F, //SetCntrastControlコマンド - 0x7Fはリセット
0x80, 0xA4, //EntireDisplayONコマンド - RAMコンテンツ表示に戻す
0x80, 0xA6, //SetNormalDisplayコマンド - ノーマルコマンド
0x00, 0xD5, 0x80, //SetOscFrequencyコマンド - 周波数の初期化
0x00, 0x8D, 0x14, //EnableChargePumpRegulator - チャージポンプを有効に設定
0x80, 0xAF, //Display ON!
};
int i2cWriteRes = i2cWriteDevice(handle, (char*)initCommand, 33);
if (0 != i2cWriteRes) {
ELOG("i2cWriteDevice() failed : %d", i2cWriteRes);
ret = -1;
} else {
ILOG("i2cWriteDevice() succeeded");
ret = 0;
}
return ret;
}
上記コードの中で、各コマンドの前に「0x00」または「0x80」を付与しています。
これが「Control byte」です。
また、「i2cWriteDevice()」はpigpioが提供しているI/Fです。
この関数を使用することで、SMBus Version 2.0に準拠してデータが送信されます。
4.2. 描画
SSD1306の設定が完了したので、画面に描画してみます。
4.2.1. 描画の手順
画面の描画の際には、以下のコマンドを実行します。
順番 | コマンド名前 | コマンド (バイトデータ) |
データ | 備考 |
---|---|---|---|---|
1 | Entire Dispaly ON | A5h | 画面に表示するデータを格納したRAMのデータの転送を停止する。 | |
2 | Set Column Address | 21h | 0, 127 | 描画の開始烈と終了列の番号を指定 0~127まで設定可能 |
3 | Set Page Address | 22h | 0, 7 | 描画の開始「ページ」と終了「ページ」を指定 0~7まで設定可能 |
4 | データの送信 | 40h | – | 「Control byte」に0x40を指定 |
5 | Entire Display ON | A4h | RAMの内容の描画を再開 |
4.2.2. 描画の実装
実際に描画を行うコードは、以下のようになります。
int ssd1306_initialize(uint handle)
{
uint8_t startDraw[] = { 0x80, 0xA5 }; //Entire display ON
uint8_t areaCommand[] = {
0x00, 0x21, 0, 127,
0x00, 0x22, 0, 7
};
uint8_t endDraw[] = { 0x80, 0xA4 }; //Reresume to RAM content display.
uint8_t sampleData[1024 + 1];
sampleData[0] = 0x40;
for (int index = 1; index < 1024 + 1; index++) {
sampleData[index] = (uint8_t)(index - 1);
}
i2cWriteDevice(handle, (char*)startDraw, 8);
i2cWriteDevice(handle, (char*)areaCommand, 8);
i2cWriteDevice(handle, (char*)sampleData, 1024 + 1);
i2cWriteDevice(handle, (char*)endDraw, 2);
}
上記関数を実行すると、SSD1306の表示は、下図のようになります。
この通り、画面左上を原点として(何かの)画像が描画されていることが分かります。
4.2.3. データと画像
SSD1306に送信したデータと、実際に描画される画像について書きます。
前述のコードでは、1024バイトの配列に、先頭から1バイトずつ0~255までの値を設定しています。
このデータに従って、SSD1306の各ドットの消灯/点灯が制御されます。
つまり、各データのビットが「0」になっていた場合には、対応するドットを消灯し、「1」になっていた場合にはドットを点灯します。
(設定によっては、消灯/点灯を逆にすることも可能です。)
またデータ1バイトが、ページの1列分のデータになります。
SSD1306に送信しているデータ(上述のコードでは「sampleData」)の各ビットが、ページのビット(0~7)に対応しています。
即ち、sampleDataのLSBがページの一番上、MSBがページの一番下のドットにそれぞれ対応しています。
またsampleData[]の各要素がページの列に対応しており、sampleData[0]が一番左の列に対応しており、配列のインデックスが増加するごとに、対応する列も右に移動します。
配列のインデックスが127になった場合には、対応するページが「PAGE 1」に切り換わり、対応する列が「0」に戻ります。
このようにして、128×64の画面に対して画像が描画されます。
5. まとめ
今回は、RaspberryPiでSSD1306への描画に挑戦しました。
とりあえず、基本的な処理と描画の仕組みは分かりました。
しかし、arduinoを使った場合とは異なり、それほど自由に描画はできません。
そのあたりは、今後の課題かなぁ、と考えています。
ではっ!
ディスカッション
コメント一覧
まだ、コメントがありません