組み込みマイコン用 OLED(SSD1305/1315) ディスプレイ制御の C 言語実装ガイド

はじめに

本記事では、I²C(ソフトウェア位相) インタフェースを用いた 0.96 インチ OLED モジュール(SSD1305 または SSD1315 ドライバ対応)の制御ライブラリ構築方法を解説します。ハードウェア I²C が利用可能な環境でも、タイミング調整が必要な場合やポートビット制御が要求される場合に、以下の基本構造を応用して実装可能です。

初期化シーケンス

ディスプレイデバイスは、特定の順序でコマンドを送信することで動作モードを設定します。主要な機種である SSD1305 および SSD1315 の初期化フローは類似していますが、ピン配置やコントラスト設定レジスタが異なる場合があります。

SSD1305 用設定例

// SSD1305 標準初期化
void OLED_Init_1305(void) {
    SendCommand(0xA8); // マルチプレックス比設定
    SendCommand(0x3F); 
    
    SendCommand(0xD3); // 垂直オフセット設定
    SendCommand(0x00); // オフセットなし
    
    SendCommand(0x40); // 開始行アドレス設定
    
    SendCommand(0xA1); // セグメント再マップ(逆転)
    SendCommand(0xC8); // COM スキャン方向
    SendCommand(0xDA); // COM ピン構成設定
    SendCommand(0x02); 
    
    SendCommand(0x81); // コントラスト設定
    SendCommand(0x7F); 
    
    SendCommand(0xA4); // リアル RAM 出力有効
    SendCommand(0xA6); // 通常表示(反転無効)
    
    SendCommand(0xD5); // クロック周波数設定
    SendCommand(0x80); 
    
    SendCommand(0x8D); // チャージポンプ設定
    SendCommand(0x14); 
    SendCommand(0xAF); // 画面表示 ON
}

SSD1315 用設定例

// SSD1315 初期化フロー
void OLED_Init_1315(void) {
    SendCommand(0xAE); // ディスプレイ OFF
    
    SendCommand(0x00); // カラムアドレス低
    SendCommand(0x10); // カラムアドレス高
    SendCommand(0x40); // 開始ラインアドレス
    SendCommand(0xB0); // ページアドレス設定
    
    SendCommand(0x81); // コントラスト設定
    SendCommand(0xFF); 
    
    SendCommand(0xA1); // セグメント再マップ
    SendCommand(0xA6); // 通常表示
    SendCommand(0xC9); // COM ピン順相反転
    SendCommand(0xA8); // マルチプレックス比
    SendCommand(0x3F); 
    
    SendCommand(0xD3); // オフセットなし
    SendCommand(0x00); 
    
    SendCommand(0xD5); // クロック設定
    SendCommand(0x80); 
    
    SendCommand(0xD9); // プリーチャージ期間
    SendCommand(0xF1); 
    
    SendCommand(0xDA); // COM 構成
    SendCommand(0x12); 
    
    SendCommand(0xDB); // VCOMH レベル
    SendCommand(0x40); 
    
    SendCommand(0x8D); // チャージポンプ
    SendCommand(0x14); 
    
    SendCommand(0xAF); // ディスプレイ ON
}

I²C プロトコル実装の基本

ソフトウェア I²C は、GPIO を操作してクロックとデータを擬似的に生成します。以下の関数は、物理的なレベル制御に基づいて作成する必要があります。

接続開始・停止条件

// 開始信号(S)
void I2C_Begun(void) {
    gpio_high(OLED_SDA);
    gpio_delay_us(3);
    gpio_high(OLED_SCL);
    gpio_delay_us(3);
    gpio_low(OLED_SDA);
    gpio_low(OLED_SCL);
}

// 停止信号(P)
void I2C_Ended(void) {
    gpio_high(OLED_SCL);
    gpio_low(OLED_SDA);
    gpio_delay_us(5);
    gpio_high(OLED_SDA);
}

// アドレス書き込み確認(ACK)
void I2C_Acknowledge(void) {
    gpio_high(OLED_SDA);
    gpio_high(OLED_SCL);
    gpio_low(OLED_SCL);
}

データ送信ルーチン

I²C ではデータバイト単位での同期処理が必要です。MSB(最上位ビット)から順に送信し、ホストがアサート(ACK)を確認します。

1 バイト送信

void I2C_TransferByte(uint8_t data_byte) {
    uint8_t bit_count = 8;
    while(bit_count--) {
        if(data_byte & 0x80) 
            gpio_high(OLED_SDA);
        else 
            gpio_low(OLED_SDA);
            
        gpio_delay_us(1); // データ安定時間
        gpio_high(OLED_SCL);
        gpio_delay_us(1);
        gpio_low(OLED_SCL);
        
        data_byte <<= 1;
    }
}

コマンドとデータの区別

SSD130X シリーズでは、最初のバイトが コマンド かデータかを判定します。通常、データ通信では `0x00` を、メモリ読み書きには `0x40` を使用して制御します。

// コマンド送信
void OLED_CmdWrite(uint8_t val) {
    I2C_Begun();
    I2C_TransferByte(0x00); // コマンドモードフラグ
    I2C_Acknowledge();
    I2C_TransferByte(val);
    I2C_Ended();
}

// データ送信
void OLED_DataWrite(uint8_t val) {
    I2C_Begun();
    I2C_TransferByte(0x40); // データモードフラグ
    I2C_Acknowledge();
    I2C_TransferByte(val);
    I2C_Ended();
}

表示位置の管理

OLED の表示エリアはページ(Page)と列(Column)のマトリクスで管理されます。通常 8 ページ(0〜7)、64 或いは 128 セグメントです。

ページ切り替え

void OLED_PageSelect(uint8_t page_num) {
    // ページ番号 0 から始まるため、命令のベース値に加算
    OLED_CmdWrite(0xB0 | (page_num & 0x07));
}

カラム切り替え

漢字表示時には幅の考慮が必要です。

void OLED_ColumnSet(uint8_t col_addr) {
    OLED_CmdWrite(0x10 | ((col_addr >> 4) & 0x0F)); // High nibble
    OLED_CmdWrite(0x00 | (col_addr & 0x0F));       // Low nibble
}

描画機能実装

基本的な描画機能として、画面クリア、文字表示、数値変換などを提供します。

フルスクリーンクリア

指定された色(0: 消灯, 1: 点灯)で画面を均一に塗りつぶします。

void OLED_ClearScreen(uint8_t color_val) {
    for(int p = 0; p < 8; p++) {
        OLED_PageSelect(p);
        OLED_ColumnSet(0);
        for(int c = 0; c < 128; c++) {
            OLED_DataWrite(color_val);
        }
    }
}

文字フォントマッピング

ASCII 文字コードを内部フォントテーブルのインデックスに変換するロジックを実装します。ここでは簡易的なマッピングを示します。

void OLED_PrintChar(uint8_t p_idx, uint8_t c_idx, char ch) {
    uint16_t ascii_code = (unsigned char)ch;
    
    // 特殊記号・英数字のマッピング補正
    if(ch >= 'a' && ch <= 'z') {
        ascii_code += 41 - 'a';
    } else if(ch >= 'A' && ch <= 'Z') {
        ascii_code += 15 - 'A';
    } else {
        switch(ch) {
            case ':': ascii_code = 11; break;
            case '.': ascii_code = 10; break;
            default: ascii_code -= '0'; break;
        }
    }

    for(int row = 0; row < 2; row++) {
        OLED_ColumnSet(c_idx + row * 2);
        OLED_PageSelect(p_idx + row);
        
        // フォントテーブルから 8 ビット×2 ブロックを読み出し送信
        for(int k = 0; k < 8; k++) {
            OLED_DataWrite(font_table_16[ascii_code][row * 8 + k]);
        }
    }
}

// フォントデータ定義例(ROM 領域推奨)
static const uint8_t font_table_16[64][16] PROGMEM = {
    { /* 文字 '0' データ */ },
    { /* 文字 '1' データ */ },
    // ...
};

数値の表示

整数型を文字列に変換して描画します。

uint32_t Math_Pow(uint32_t base, uint32_t exp) {
    uint32_t res = 1;
    while(exp--) res *= base;
    return res;
}

void OLED_IntPrint(uint8_t p, uint8_t c, uint32_t num, uint8_t len) {
    for(uint8_t i = 0; i < len; i++) {
        uint32_t digit = (num / Math_Pow(10, len - 1 - i)) % 10;
        OLED_PrintChar(p, c + i, digit + '0');
    }
}

日本語・漢字表示

漢字フォントはバイナリデータで定義され、通常 16x16 ドット形式となります。インデックス方式でアクセスします。

void OLED_HanjiRender(uint8_t p, uint8_t c, uint8_t index) {
    for(uint8_t row = 0; row < 2; row++) {
        OLED_PageSelect(p + row);
        OLED_ColumnSet(c);
        
        for(uint8_t col = 0; col < 16; col++) {
            // 漢字データテーブルから読み出す
            OLED_DataWrite(hanzi_data[index * 2 + row][col]);
        }
    }
}

文字列表示の場合も同様にインデックス配列を走査して呼び出せば、連続した文字が表示可能になります。

タグ: SSD1305 OLED Driver I2C Protocol Embedded C Microcontroller Display

10月5日 03:19 投稿