【C-First】OLEDディスプレイ(SSD1306)に文字を表示する

ワンボードマイコン

目的

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

情報

C-First

C-Firstボード及びサンプルプログラムや開発環境が収録されたDVDがセットになった解説書籍です。

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

C-Firstについては以下の記事を参照ください。

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

似たような商品が複数見つかりましたが、今回は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のショップサイトから拝借

C-Firstとの接続

C-FirstとOLEDディスプレイモジュールは以下のように接続します。

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

注意点が1点、本OLEDディスプレイモジュールは3.3V動作品です。C-FirstのI2C端子(SDA/SCL)は5V用と3.3V用が分かれています。最初からピンコネクタが実装されている端子は5V用です。3.3V用は中央付近のスルーホールで、SDA/SCLとシルク表示されている部分です(前述の接続図参照)。ピンコネクタが実装されていないため必要に応じて実装しましょう(私は実装しました)。

Amazon | ジェネリック Voocye  10pcs シングル40ピン オス+メスストレートタイプ ピンヘッダー PCB用 2.54 mm | 基板 | 産業・研究開発用品 通販
ジェネリック Voocye  10pcs シングル40ピン オス+メスストレートタイプ ピンヘッダー PCB用 2.54 mmが基板ストアでいつでもお買い得。当日お急ぎ便対象商品は、当日お届け可能です。アマゾン配送商品は、通常配送無料(一部...

※尚、5Vでも動作するという情報を見かけましたが試してはいません

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

Amazon | サンハヤト SAD-101 ニューブレッドボード | ブレッドボード | 産業・研究開発用品 通販
サンハヤト SAD-101 ニューブレッドボードがブレッドボードストアでいつでもお買い得。当日お急ぎ便対象商品は、当日お届け可能です。アマゾン配送商品は、通常配送無料(一部除く)。
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用ですのC-First環境にポーティング(移植)する作業が必要です。

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

美咲フォント

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

本家

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

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

Arduino用

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

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

作業

C-FirstでI2Cを使用する環境を整える

I2Cにて加速度センサを読み出しLEDを点灯する以下のサンプルソースをベースとします。

  • リスト13-16_c08-i2c1

※付属DVDに収録されています。

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;
    
    i2c_flag = 0;
    R_IICA0_Master_Send(I2C_LED_ADDR, buf, 29, 1);      /* I2C送信実行 */
    while (i2c_flag == 0) ;                             /* 送信完了待ち */
    R_IICA0_StopCondition();                            /* I2C通信終了 */
    while (SPD0 == 0) ;                                 /* ストップ・コンディション検出待ち */
    
    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) */
        
        i2c_flag = 0;
        R_IICA0_Master_Send(I2C_LED_ADDR, buf, 5, 1);   /* I2C送信実行 */
        while (i2c_flag == 0) ;                         /* 送信完了待ち */
        R_IICA0_StopCondition();                        /* I2C通信終了 */
        while (SPD0 == 0) ;                             /* ストップ・コンディション検出待ち */
        
        buf[0] = SSD1306_CONTROL_DATA_STREAM;           /*  コントロールバイト */
        for(j = 0; j < 8; j++) {
            buf[1 + j] = 0x00;
        }
        for(j = 0; j < 16; j++) {
            i2c_flag = 0;
            R_IICA0_Master_Send(I2C_LED_ADDR, buf, 9, 1);   /* I2C送信実行 */
            while (i2c_flag == 0) ;                     /* 送信完了待ち */
            R_IICA0_StopCondition();                    /* I2C通信終了 */
            while (SPD0 == 0) ;                         /* ストップ・コンディション検出待ち */
        }
    }
    g_x = 0;
    g_y = 0;
    
    return;
}

改行制御処理

今回は最低限の簡易な処理としました。結果的に何も制御はしておりません^^

static void OLED_SSD1306_newLine(void)
{
    g_x = 0;
    if(g_y < 8) {
        g_y++;
    } else {
        g_y = 0;
    }
    
    return;
}

1文字書込み処理

static void OLED_SSD1306_writeChar(uint8_t data)
{
    uint8_t i;
    uint8_t buf[29];
    
    if (data == '\n') {
        OLED_SSD1306_newLine();
    } else if (((data >= 0x20) && (data <=0x7F))
        && ((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;                        /* 折り返し */
        i2c_flag = 0;
        R_IICA0_Master_Send(I2C_LED_ADDR, buf, 5, 1);   /* I2C送信実行 */
        while (i2c_flag == 0) ;                         /* 送信完了待ち */
        R_IICA0_StopCondition();                        /* I2C通信終了 */
        while (SPD0 == 0) ;                             /* ストップ・コンディション検出待ち */
        
        /* データ */
        buf[0] = SSD1306_CONTROL_DATA_STREAM;           /* コントロールバイト */
        for(i = 0; i < 8; i++) {
            buf[1 + i] = FONT8X16[data - 0x20][i];
        }
        
        i2c_flag = 0;
        R_IICA0_Master_Send(I2C_LED_ADDR, buf, 9, 1);   /* I2C送信実行 */
        while (i2c_flag == 0) ;                         /* 送信完了待ち */
        R_IICA0_StopCondition();                        /* I2C通信終了 */
        while (SPD0 == 0) ;                             /* ストップ・コンディション検出待ち */
        g_x++;
    } else {
        ;
    }
    return;
}

文字列表示処理

static void OLED_SSD1306_println(char *data)
{
    char *p;
    p = data;
    
    do {
        OLED_SSD1306_writeChar((uint8_t)*p);
        p++;
    } while(*p != '\0');
    
    return;
}

Arduino用美咲フォント環境をC-First環境にポーティングする

Arudino用美咲フォント環境から、以下の3ファイルをポーティングします。基本的にはそのままで難しい変更はしていないため詳細は割愛します。

  • misakiUTF16.h
  • misakiUTF16.c  ※ポーティング元は.cpp。.c化もあわせて行う。
  • misakiUTF16FontData.h

更に、上記に合わせて「1文字書き込み処理」及び「文字列表示処理」のマルチバイト文字対応を行います。

また、美咲フォントをそのまま書き込むと左に90度回転して表示されてしまいます。今回は1文字書き込み毎に右に90度回転させてから書き込むことで対応しました。

1文字書込み処理(UTF-8対応)

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;                        /* 折り返し */
        i2c_flag = 0;
        R_IICA0_Master_Send(I2C_LED_ADDR, buf, 5, 1);   /* I2C送信実行 */
        while (i2c_flag == 0) ;                         /* 送信完了待ち */
        R_IICA0_StopCondition();                        /* I2C通信終了 */
        while (SPD0 == 0) ;                             /* ストップ・コンディション検出待ち */
        
        /* データ */
        buf[0] = SSD1306_CONTROL_DATA_STREAM;           /* コントロールバイト */
        data = getFontData(&buf[1], data, false);
        
        i2c_flag = 0;
        R_IICA0_Master_Send(I2C_LED_ADDR, buf, 9, 1);   /* I2C送信実行 */
        while (i2c_flag == 0) ;                         /* 送信完了待ち */
        R_IICA0_StopCondition();                        /* I2C通信終了 */
        while (SPD0 == 0) ;                             /* ストップ・コンディション検出待ち */
        g_x++;
    } else {
        ;
    }
    
    return data;
}

文字列表示処理(UTF-8対応)

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;
}

任意の文字列を表示する

できました!

最終プログラム

SSD1306_misaki.zip

コメント

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