目的
C-FirstとOELDディスプレイ(コントローラIC SSD1306搭載)をI2C接続し、画面に文字を表示させます。文字データとしては美咲フォントを利用します。
情報
C-First
C-Firstボード及びサンプルプログラムや開発環境が収録されたDVDがセットになった解説書籍です。
C-Firstについては以下の記事を参照ください。
使用するディスプレイ部品
似たような商品が複数見つかりましたが、今回は2個入りでコスパの高いこちらを購入しました。
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とシルク表示されている部分です(前述の接続図参照)。ピンコネクタが実装されていないため必要に応じて実装しましょう(私は実装しました)。
※尚、5Vでも動作するという情報を見かけましたが試してはいません
今回使用したブレッドボードとジャンパーワイヤーは以下のものになります。
SSD1306制御
SSD1306の制御プログラムは以下のものを参考にしました。ただしArduino用ですのC-First環境にポーティング(移植)する作業が必要です。
美咲フォント
美咲フォント(みさきフォント)は 8 × 8 ピクセルサイズのビットマップフォント。元々は、ポケットコンピュータ(ポケコン)用に使われていた恵梨沙フォントが見辛かったために、その代替フォントとして作られた、とのことです。
本家
素晴らしいフォントをありがとうございます。
Arduino用
今回はこちらのArduino用ライブラリを使用させていただきました。

作業
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;
}
任意の文字列を表示する
できました!

コメント