【Raspberry Pi Pico】OLEDディスプレイ(SSD1306)に文字を表示する

ワンボードマイコン

目的

Raspberry Pi PicoとOELDディスプレイ(コントローラIC SSD1306搭載)をI2C接続し、画面に文字を表示させます。文字データとしては美咲フォントを利用します。

実は以下の記事でC-Firstボードで同様の対応をしています。が、都合により初めて対応するていで進めていきます^^

【C-First】OLEDディスプレイ(SSD1306)に文字を表示する
目的 C-FirstとOELDディスプレイ(コントローラIC SSD1306搭載)をI2C接続し、画面に文字を表示させます。文字データとしては美咲フォントを利用します。 情報 C-First C-Firstボード及びサンプルプログラムや開発...

情報

Raspberry Pi Pico

Raspberry Pi Picoについては以下の記事を参照ください。

格安でマイコン学習可能『Raspberry Pi Pico』
概要・特徴 Raspberry Pi財団が独自に開発したARM Cortex M0+デュアルコアのRP2040マイコンを搭載した開発基板です。 別の記事で紹介したいと思っていますが、Picoは2個用意して1個をエミュレータとして接続すること...
【Raspberry Pi Pico】環境構築
目的 Windows 10/64bitのパソコン上にRaspberry Pi Picoの開発環境を構築します。また、Picoを2個用意して1個をエミュレータとして接続することでブレークポイント・ステップ実行ベースのデバッグが可能となる環境を...

使用するディスプレイ部品

似たような商品が複数見つかりましたが、今回は2個入りでコスパの高いこちらを購入しました。

https://www.amazon.co.jp/gp/product/B081ZQ5Z97/ref=as_li_qf_asin_il_tl?ie=UTF8&tag=icankot-22&creative=1211&linkCode=as2&creativeASIN=B081ZQ5Z97&linkId=cadbf78c97445f86347ea133607a0856

SSD1306データシート

ディスプレイコントローラとしてSSD1306が搭載されています。

https://cdn-shop.adafruit.com/datasheets/SSD1306.pdf

※チップメーカのサイトから直接ダウンロードできなかったのでadafruitのショップサイトから拝借

Raspberry Pi Picoとの接続

※後でまた出てきますが、I2Cの端子はGPIO4番、5番を使う設定とする前提で接続しています。

ブレッドボードとジャンパーワイヤーを使用して接続した実際の様子は以下の通りです。

今回使用したブレッドボードとジャンパーワイヤーは以下のものになります。

https://www.amazon.co.jp/gp/product/B08YJY3J3D/ref=as_li_qf_asin_il_tl?ie=UTF8&tag=icankot-22&creative=1211&linkCode=as2&creativeASIN=B08YJY3J3D&linkId=ca110776378ec3f2ccec90ce3a6a230f
https://www.amazon.co.jp/gp/product/B083LT3759/ref=as_li_qf_asin_il_tl?ie=UTF8&tag=icankot-22&creative=1211&linkCode=as2&creativeASIN=B083LT3759&linkId=7fc6d0a2e2101d759f228271e4637633

SSD1306制御

SSD1306の制御プログラムは以下のものを参考にしました。ただしArduino用ですのRaspberry Pi Pico環境にポーティング(移植)する作業が必要です。

GitHub - askn37/OLED_SSD1306: OLED : SOLOMON SYSTECH SSD1306 (I2C) small driver for Arduino
OLED : SOLOMON SYSTECH SSD1306 (I2C) small driver for Arduino - askn37/OLED_SSD1306

美咲フォント

美咲フォント(みさきフォント)は 8 × 8 ピクセルサイズのビットマップフォント。元々は、ポケットコンピュータ(ポケコン)用に使われていた恵梨沙フォントが見辛かったために、その代替フォントとして作られた、とのことです。

本家

素晴らしいフォントをありがとうございます。

8×8ドット日本語フォント「美咲フォント」

Arduino用

今回はこちらのArduino用ライブラリを使用させていただきました。

Arduino用美咲フォントライブラリを作成しました - 猫にコ・ン・バ・ン・ワ
2016/07/05 更新、2018/05/18 追記先日、aitendo I2...

作業

Raspberry Pi PicoでI2Cを使用する環境を整える

Raspberry Pi PicoのSDKでI2C制御APIが提供されているのでそちらを利用します。

Page not found · GitHub Pages

お膳立て処理として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;
}

任意の文字列を表示する

できました!

最終プログラム

pico-ssd1306-misaki.zip

ビルド方法

以下記事相当の環境構築が行われていることが前提となります。

【Raspberry Pi Pico】環境構築
目的 Windows 10/64bitのパソコン上にRaspberry Pi Picoの開発環境を構築します。また、Picoを2個用意して1個をエミュレータとして接続することでブレークポイント・ステップ実行ベースのデバッグが可能となる環境を...

以下の配置とした場合を例として記載します。

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

コメント

タイトルとURLをコピーしました