目的
Raspberry Pi PicoとOELDディスプレイ(コントローラIC SSD1306搭載)をI2C接続し、画面に文字を表示させます。文字データとしては美咲フォントを利用します。
実は以下の記事でC-Firstボードで同様の対応をしています。が、都合により初めて対応するていで進めていきます^^
情報
Raspberry Pi Pico
Raspberry Pi Picoについては以下の記事を参照ください。
使用するディスプレイ部品
似たような商品が複数見つかりましたが、今回は2個入りでコスパの高いこちらを購入しました。
SSD1306データシート
ディスプレイコントローラとしてSSD1306が搭載されています。
https://cdn-shop.adafruit.com/datasheets/SSD1306.pdf
※チップメーカのサイトから直接ダウンロードできなかったのでadafruitのショップサイトから拝借
Raspberry Pi Picoとの接続
※後でまた出てきますが、I2Cの端子はGPIO4番、5番を使う設定とする前提で接続しています。
ブレッドボードとジャンパーワイヤーを使用して接続した実際の様子は以下の通りです。
今回使用したブレッドボードとジャンパーワイヤーは以下のものになります。
SSD1306制御
SSD1306の制御プログラムは以下のものを参考にしました。ただしArduino用ですのRaspberry Pi Pico環境にポーティング(移植)する作業が必要です。
美咲フォント
美咲フォント(みさきフォント)は 8 × 8 ピクセルサイズのビットマップフォント。元々は、ポケットコンピュータ(ポケコン)用に使われていた恵梨沙フォントが見辛かったために、その代替フォントとして作られた、とのことです。
本家
素晴らしいフォントをありがとうございます。
Arduino用
今回はこちらのArduino用ライブラリを使用させていただきました。
作業
Raspberry Pi PicoでI2Cを使用する環境を整える
Raspberry Pi PicoのSDKでI2C制御APIが提供されているのでそちらを利用します。
お膳立て処理としてmain処理の最初に以下の処理を実装します。
#define I2C_SDA_PIN 4
#define I2C_SCL_PIN 5
int main(void)
{
// This example will use I2C0 on the default SDA and SCL pins (4, 5 on a Pico)
i2c_init(i2c_default, 100 * 1000);
gpio_set_function(I2C_SDA_PIN, GPIO_FUNC_I2C);
gpio_set_function(I2C_SCL_PIN, GPIO_FUNC_I2C);
gpio_pull_up(I2C_SDA_PIN);
gpio_pull_up(I2C_SCL_PIN);
// Make the I2C pins available to picotool
bi_decl(bi_2pins_with_func(I2C_SDA_PIN, I2C_SCL_PIN, GPIO_FUNC_I2C));
:
I2Cは2チャネルありますが、今回はch0を使用します。また、どのGPIO PINをI2Cの端子(SDA/SCL)として使用するかをいくつかの選択肢から選ぶことができますが、今回は4番、5番のGPIOを使用する設定とします。
SSD1306を制御するプログラムを実装する
重要な処理(関数)についてソースコードを紹介します。この時点では仮のフォントデータを定義して対応していますが詳細は省略します。
SSD1306ヘッダファイル(OLED_SSD1306.h)
#ifndef __OLED_SSD1306_H_
#define __OLED_SSD1306_H_
#define SSD1306_ADDR 0x3C
#define I2C_SPEED_STANDARD 100000
#define I2C_SPEED_DOUBLE 200000
#define I2C_SPEED_FAST 400000
// Control byte
#define SSD1306_CONTROL_CMD_SINGLE 0x80
#define SSD1306_CONTROL_CMD_STREAM 0x00
#define SSD1306_CONTROL_DATA_STREAM 0x40
// Fundamental commands (pg.28)
#define SSD1306_SET_CONTRAST 0x81
#define SSD1306_DISPLAY_ALL_ON_RESUME 0xA4
#define SSD1306_DISPLAY_ALL_ON_IGNORE 0xA5
#define SSD1306_NORMAL_DISPLAY 0xA6
#define SSD1306_INVERT_DISPLAY 0xA7
#define SSD1306_DISPLAY_OFF 0xAE
#define SSD1306_DISPLAY_ON 0xAF
// Scrolling #defines (pg.28-30)
#define SSD1306_RIGHT_HORIZONTAL_SCROLL 0x26
#define SSD1306_LEFT_HORIZONTAL_SCROLL 0x27
#define SSD1306_VERTICAL_AND_RIGHT_HORIZONTAL_SCROLL 0x29
#define SSD1306_VERTICAL_AND_LEFT_HORIZONTAL_SCROLL 0x2A
#define SSD1306_DEACTIVATE_SCROLL 0x2E
#define SSD1306_ACTIVATE_SCROLL 0x2F
#define SSD1306_SET_VERTICAL_SCROLL_AREA 0xA3
// Addressing Command Table (pg.30)
#define SSD1306_SET_LOW_COLUMN 0x00
#define SSD1306_SET_HIGH_COLUMN 0x10
#define SSD1306_SET_MEMORY_ADDR_MODE 0x20 // follow with 0x00 = HORZ mode = Behave like a KS108 graphic LCD
#define SSD1306_SET_COLUMN_RANGE 0x21 // can be used only in HORZ/VERT mode - follow with 0x00 and 0x7F = COL127
#define SSD1306_SET_PAGE_RANGE 0x22 // can be used only in HORZ/VERT mode - follow with 0x00 and 0x07 = PAGE7
#define SSD1306_SET_PAGE_START_ADDRESS 0xB0
// Hardware Config (pg.31)
#define SSD1306_SET_DISPLAY_START_LINE 0x40
#define SSD1306_SET_SEGMENT_REMAP_LOW 0xA0
#define SSD1306_SET_SEGMENT_REMAP_HIGH 0xA1
#define SSD1306_SET_MULTIPLEX_RATIO 0xA8 // follow with 0x3F = 64 MUX
#define SSD1306_SET_COM_SCAN_INC 0xC0
#define SSD1306_SET_COM_SCAN_DEC 0xC8
#define SSD1306_SET_DISPLAY_OFFSET 0xD3 // follow with 0x00
#define SSD1306_SET_COM_PIN_MAP 0xDA // follow with 0x12
// Timing and Driving Scheme (pg.32)
#define SSD1306_SET_DISPLAY_CLK_DIV 0xD5 // follow with 0x80
#define SSD1306_SET_PRECHARGE 0xD9 // follow with 0xF1
#define SSD1306_SET_VCOMH_DESELCT 0xDB // follow with 0x30
#define SSD1306_NOP 0xE3 // NOP
// Charge Pump (pg.62)
#define SSD1306_SET_CHARGE_PUMP 0x8D // follow with 0x14
#endif
SSD1306初期設定処理
static void OLED_SSD1306_init(void)
{
uint8_t buf[29];
buf[0] = SSD1306_CONTROL_CMD_STREAM; /* コントロールバイト */
/* Display off */
buf[1] = SSD1306_DISPLAY_OFF;
/* Set MUX Ratio A8h, 3Fh*/
buf[2] = SSD1306_SET_MULTIPLEX_RATIO;
buf[3] =0x3F;
/* Set Display Offset D3h, 00h */
buf[4] = SSD1306_SET_DISPLAY_OFFSET;
buf[5] = 0x00;
/* Set Display Start Line 40h */
buf[6] = SSD1306_SET_DISPLAY_START_LINE;
/* Set Segment re-map A0h/A1h */
buf[7] = SSD1306_SET_SEGMENT_REMAP_LOW;
/* Set COM Output Scan Direction C0h/C8h */
buf[8] = SSD1306_SET_COM_SCAN_INC;
/* Set COM Pins hardware configuration DAh, 02*/
buf[9] = SSD1306_SET_COM_PIN_MAP;
buf[10] = 0x12;
/* Set Contrast Control 81h, 7Fh */
buf[11] = SSD1306_SET_CONTRAST;
buf[12] = 0x7F;
/* Disable Entire Display On A4h */
buf[13] = SSD1306_DISPLAY_ALL_ON_RESUME;
/* Set Normal Display A6h */
buf[14] = SSD1306_NORMAL_DISPLAY;
/* Set Osc Frequency D5h, 80h */
buf[15] = SSD1306_SET_DISPLAY_CLK_DIV;
buf[16] = 0x80;
buf[17] = SSD1306_SET_MEMORY_ADDR_MODE;
buf[18] = 0x10;
buf[19] = SSD1306_SET_COLUMN_RANGE;
buf[20] = 0x00;
buf[21] = 0x7F;
buf[22] = SSD1306_SET_PAGE_RANGE;
buf[23] = 0x00;
buf[24] = 0x07;
buf[25] = SSD1306_DEACTIVATE_SCROLL;
/* Enable charge pump regulator 8Dh, 14h */
buf[26] = SSD1306_SET_CHARGE_PUMP;
buf[27] = 0x14;
/* Display On AFh */
buf[28] = SSD1306_DISPLAY_ON;
(void)i2c_write_blocking (i2c_default, I2C_LED_ADDR, buf, 29, false);
return;
}
ディスプレイ全クリア処理
static void OLED_SSD1306_clear(void)
{
uint8_t i,j;
uint8_t buf[9];
for(i = 0; i < 8; i++) {
buf[0] = SSD1306_CONTROL_CMD_STREAM; /* コントロールバイト */
buf[1] = SSD1306_SET_PAGE_START_ADDRESS | i; /* set page start address(B0~B7) */
buf[2] = SSD1306_SET_COLUMN_RANGE; /* set Column Address */
buf[3] = 0x00; /* Column Start Address(0-127) */
buf[4] = 0x7F; /* Column Stop Address(0-127) */
(void)i2c_write_blocking (i2c_default, I2C_LED_ADDR, buf, 5, false);
buf[0] = SSD1306_CONTROL_DATA_STREAM; /* コントロールバイト */
for(j = 0; j < 8; j++) {
buf[1 + j] = 0x00;
}
for(j = 0; j < 16; j++) {
(void)i2c_write_blocking (i2c_default, I2C_LED_ADDR, buf, 9, false);
}
}
g_x = 0;
g_y = 0;
return;
}
Arduino用美咲フォント環境をRaspberry Pi Pico環境にポーティングする
Arudino用美咲フォント環境から、以下の3ファイルをポーティングします。基本的にはそのままで難しい変更はしていないため詳細は割愛します。
- misakiUTF16.h
- misakiUTF16.c ※ポーティング元は.cpp。.c化もあわせて行う。
- misakiUTF16FontData.h
更に、上記に合わせて「1文字書き込み処理」及び「文字列表示処理」のマルチバイト文字対応を行います。
また、美咲フォントをそのまま書き込むと左に90度回転して表示されてしまいます。今回は1文字書き込み毎に右に90度回転させてから書き込むことで対応しました。
1文字書込み処理
static char* OLED_SSD1306_writeChar(char *data)
{
uint8_t buf[9];
if (*data == 0x0a) {
OLED_SSD1306_newLine();
data += 1;
} else if ((*data == 0x5c) && (*(data + 1) == 0x6e)) {
OLED_SSD1306_newLine();
data += 2;
} else if ((g_y < 8) && (g_x < 16)) {
/* コマンド */
buf[0] = SSD1306_CONTROL_CMD_STREAM; /* コントロールバイト */
buf[1] = SSD1306_SET_PAGE_START_ADDRESS | g_y; /* ページ指定, 垂直位置 */
buf[2] = SSD1306_SET_COLUMN_RANGE; /* サブアドレス */
buf[3] = g_x << 3; /* 水平位置 */
buf[4] = (g_x << 3) + 7; /* 折り返し */
(void)i2c_write_blocking (i2c_default, I2C_LED_ADDR, buf, 5, false);
/* データ */
buf[0] = SSD1306_CONTROL_DATA_STREAM; /* コントロールバイト */
data = getFontData(&buf[1], data, false);
(void)i2c_write_blocking (i2c_default, I2C_LED_ADDR, buf, 9, false);
g_x++;
} else {
;
}
return data;
}
改行制御処理
今回は最低限の簡易な処理としました。結果的に何も制御はしておりません^^
static void OLED_SSD1306_newLine(void)
{
g_x = 0;
if (g_y < 8) {
g_y++;
} else {
g_y = 0;
}
return;
}
文字列表示処理
static void OLED_SSD1306_println(char *data)
{
do {
data = OLED_SSD1306_writeChar(data);
} while(*data != '\0');
return;
}
フォントデータ右90度回転処理
static void rotateToRight(char *data)
{
uint8_t i,j;
uint8_t buf[8];
for(i = 0; i < 8; i++) {
buf[i] = 0;
}
for(i = 0 ; i < 8; i++) {
for(j = 0 ; j < 8; j++) {
buf[j] |= ((data[i] >> (7 - j)) & 1) << i;
}
}
for(i = 0; i < 8; i++) {
data[i] = buf[i];
}
return;
}
任意の文字列を表示する
できました!
最終プログラム
ビルド方法
以下記事相当の環境構築が行われていることが前提となります。
以下の配置とした場合を例として記載します。
c:/user/pico/pico-ssd1306-misaki/ ├── CMakeLists.txt ├── pico_sdk_import.cmake └── ssd1306-misaki ├── CMakeLists.txt ├── OLED_SSD1306.h ├── comdef.h ├── main.c ├── misakiUTF16.c ├── misakiUTF16.h └── misakiUTF16FontData.h
$ cd c:/user/pico/pico-ssd1306-misaki $ mkdir build $ cd build $ cmake .. -G "MSYS Makefiles" $ make
コメント