OLED 0.96インチで遊ぶ (5) - PICから表示
今回はPICから表示を行ってみます。
PICは秋月電子で購入したPIC16F18326を使用しました。
プログラム領域が多くて高機能、それでいて低価格(130円)という大変素晴らしいデバイスです。
部品はブレッドボードに実装しました。
RC0とRC1をI2Cバスとして使用してOLEDに接続しています。
I2Cのプルアップ抵抗は1KΩにしました。
回路図はこちらです。
続いてプログラムです。
クロックは内部発振の8MHzにして、I2Cクロックは20分周してSSD1306が動作できる最速の400KHzにしています。
#include <pic.h> #include <htc.h> #include <pic16f18326.h> // クロックサイクル #define _XTAL_FREQ 8000000 // I2Cデバイスアドレス #define DEV_OLED_W 0x78 // 0111_1000 // ======================================== // Config 1 ~ 4 // ======================================== #pragma config FCMEN = OFF // クロックモニタ #pragma config CSWEN = ON // クロック切り替え(NOSC & NDIV有効) #pragma config CLKOUTEN = OFF // クロック出力 #pragma config RSTOSC = HFINT1 // クロック選択(HFINTOSC(1MHz)) #pragma config FEXTOSC = OFF // 外部発振 #pragma config DEBUG = OFF // デバッグモード #pragma config STVREN = OFF // スタック異常リセット #pragma config PPS1WAY = OFF // PPSロック #pragma config BORV = HIGH // Brown-out Reset電圧設定 #pragma config BOREN = OFF // Brown-out Resetイネーブル #pragma config LPBOREN = OFF // Low-Power Brown-out Resetイネーブル #pragma config WDTE = OFF // ウオッチドッグタイマ イネーブル #pragma config PWRTE = ON // Power-up Timer イネーブル(電源ONから64ms後にプログラムを開始) #pragma config MCLRE = OFF // Master Clear イネーブル #pragma config LVP = OFF // 低電圧プログラム #pragma config WRT = OFF // プログラムメモリ プロテクト #pragma config CPD = OFF // EEPROM プロテクト #pragma config CP = OFF // コード プロテクト // ======================================== // 関数プロトタイプ // ======================================== void i2c_idle(void); void i2c_start(void); void i2c_stop(void); void i2c_send(unsigned char); unsigned char i2c_recv(void); void i2c_sack(void); void i2c_snack(void); void oled_init(void); void oled_display(unsigned char); // ======================================== // プログラム メイン // ======================================== void main(void){ OSCCON1 = 0b01100000; // NOSC=HFINTOSC(1MHz),NDIV=1 OSCFRQ = 0b00000100; // HFFRQ=8MHz TRISA = 0b00000000; TRISC = 0b00000011; // RC 0(SCL1),1(SDA1) PORTA = 0b00000000; PORTC = 0b00000000; LATA = 0b00000000; LATC = 0b00000011; ANSELA = 0b00000000; ANSELC = 0b00000000; SSP1CLKPPS = 0x10; // RC0 - 入力先 = SCL1 SSP1DATPPS = 0x11; // RC1 - 入力先 = SDA1 RC0PPS = 0x18; // RC0 - 出力源 = SCL1 RC1PPS = 0x19; // RC1 - 出力源 = SDA1 // I2C設定 SSP1ADD = 0b00000100; SSP1CON1 = 0b00101000; // [7] WCOL = 0 : ライト衝突無し // [6] SSPOV = 0 : 受信オーバーフロー無し // [5] SSPEN = 1 : SDA, SCL有効 // [4] CKP = 0 : クロック極性(未使用) // [3:0] SSPM = 1000 : I2Cマスターモード クロック=FOSC/(4*(SSP1ADD+1)) // 8MHz / (4*(4+1)) = 400KHz SSP1STAT = 0x00; INTCON = 0x00; // 割り込み無効 __delay_ms(100); oled_init(); while(1){ oled_display(0x01); oled_display(0x02); oled_display(0x04); oled_display(0x08); oled_display(0x10); oled_display(0x20); oled_display(0x40); oled_display(0x80); } } // ======================================== // I2C IDLE // ======================================== void i2c_idle(void){ while(SSP1CON2bits.SEN | SSP1CON2bits.PEN | SSP1CON2bits.RCEN | SSP1CON2bits.ACKEN | SSP1STATbits.R_nW){} } // ======================================== // I2C スタート送信 // ======================================== void i2c_start(void){ i2c_idle(); SSP1CON2bits.SEN = 1; // スタート出力 while(SSP1CON2bits.SEN){} // スタート終了待ち } // ======================================== // I2C ストップ送信 // ======================================== void i2c_stop(void){ SSP1CON2bits.PEN = 1; // ストップ出力 while(SSP1CON2bits.PEN){} // ストップ終了待ち i2c_idle(); } // ======================================== // I2C データ送信 // ======================================== void i2c_send(unsigned char data){ SSP1BUF = data; // データセット送信開始 while(SSP1STATbits.BF){} // 送信終了待ち while(SSP1CON2bits.ACKSTAT){} // ACK返信待ち i2c_idle(); // アイドル待ち } // ======================================== // I2C データ受信 // ======================================== unsigned char i2c_recv(void){ SSP1CON2bits.RCEN = 1; while(SSP1CON2bits.RCEN){} return(SSP1BUF); } // ======================================== // I2C Ack送信 // ======================================== void i2c_sack(void){ SSP1CON2bits.ACKDT = 0; SSP1CON2bits.ACKEN = 1; while(SSP1CON2bits.ACKEN){} } // ======================================== // I2C NAck送信 // ======================================== void i2c_snack(void){ SSP1CON2bits.ACKDT = 1; SSP1CON2bits.ACKEN = 1; while(SSP1CON2bits.ACKEN){} } // ======================================== // OLED初期化 // ======================================== void oled_init(void){ // 表示モード,アドレスモード指定 i2c_start(); i2c_send(DEV_OLED_W); i2c_send(0x00); // コマンドライト i2c_send(0xa0); // 表示モード設定 左右 - 通常:0xa0 / 逆転:0xa1 i2c_send(0xc0); // 表示モード設定 上下 - 通常:0xc0 / 逆転:0xc8 i2c_send(0x20); // アドレッシングモード指定 i2c_send(0x00); // → Horizontalモード i2c_send(0x21); // カラム指定 i2c_send(0x00); // → 開始位置(0) i2c_send(0x7f); // → 終了位置(127) i2c_send(0x22); // ページ指定 i2c_send(0x00); // → 開始ページ(0) i2c_send(0x07); // → 終了ページ(7) i2c_send(0x8d); // チャージポンプ設定 i2c_send(0x14); // → On i2c_send(0xaf); // 画面表示 On i2c_stop(); } // ======================================== // OLED表示 // ======================================== void oled_display(unsigned char data){ i2c_start(); i2c_send(DEV_OLED_W); i2c_send(0x40); // データライト for(int j=0; j<8; j++){ for(int i=0; i<128; i++){ i2c_send(data); } } i2c_stop(); }
XC8でコンパイル後、PICKit3を通して書き込みます。
8本のラインが上から下へスクロールするというシンプルなプログラムです。
PICのMSSPモジュールを使用したことによりI2C通信のプログラムは簡単に実装できました。
[参考記事]