/************************************************************************************************** * UG-2864HSWEG01 OLEDモジュール駆動用ライブラリー * 制御IC:SSD1306 * * 原典:グラフィック描画関数の原典先は「mgo-tec電子工作」さん→ ESP-WROOM使用(AVRマイコン系) * * 2017.10.25 N.Ishii **************************************************************************************************/ #include "p24FJ64GA002.h" #include "SSD1306_OLEDlibPIC24F.h" #include "ASCII_font.h" extern unsigned char SegmentBuffer[128][8]; // メインモジュールで宣言しているこの変数を使う。 /**************************************************************************** * UG-2864HSWEG01 OLEDモジュール制御IC SSD1306を 初期設定をする *****************************************************************************/ void OLED_init() { //// OLED初期設定 SPI_RES = 0; // LCD リセット指令 関数呼び出し delay_ms(10); // LCD リセット保持 SPI_RES = 1; // LCD リセット解除 関数呼び出し SPI_DC = 0; // 関数を呼び出してポートをL。他の出力端子は補償される SPI_CS = 0; // LCD チップイネーブル→ SCEからCSに変更した SPI_CLK = 0; // 初期設定 /// コマンド・セット SPI_tx_byte(0xAE, 0); // Display Off SPI_tx_byte(0x00, 0); // set low column address SPI_tx_byte(0x10, 0); // set high column address SPI_tx_byte(0x40, 0); // set start line address SPI_tx_byte(0x20, 0); // set memory addressing mode 171019 SPI_tx_byte(0x00, 0); // horizontal addressing mode SPI_tx_byte(0xD5, 0); // set display clock divide ratio/oscillator frequency SPI_tx_byte(0x80, 0); // set divide ratio SPI_tx_byte(0xA8, 0); // set multiplex ratio(1 to 64) SPI_tx_byte(0x3F, 0); // 1/64 duty SPI_tx_byte(0xD3, 0); // set display offset SPI_tx_byte(0x00, 0); // not offset SPI_tx_byte(0x8D, 0); // set Charge Pump SPI_tx_byte(0x14, 0); // VCC Generated by Internal DC/DC Circuit SPI_tx_byte(0xA1, 0); // set segment re-map 95 to 0 SPI_tx_byte(0xC8, 0); // Set COM Output Scan Direction SPI_tx_byte(0xDA, 0); // set com pins hardware configuration SPI_tx_byte(0x12, 0); SPI_tx_byte(0xDA, 0); // set com pins hardware configuration SPI_tx_byte(0x12, 0); SPI_tx_byte(0x81, 0); // set contrast control register SPI_tx_byte(0xCF, 0); SPI_tx_byte(0xD9, 0); // set pre-charge period SPI_tx_byte(0xF1, 0); SPI_tx_byte(0xDB, 0); // set vcomh SPI_tx_byte(0x40, 0); SPI_tx_byte(0xA4, 0); // set entire display on/off SPI_tx_byte(0xA6, 0); // set normal display /// 全画面(表示RAM)クリア+表示ON OLED_clear_all(BLACK); // 全画面クリア SPI_tx_byte(0xAF, 0); // Display On } /**************************************************************************** * OLEDの書き込み位置を X、Y に設定する *---------------------------------------------------------------------------- * 入力: unsigned char x,unsigned char y * 出力: 無し *---------------------------------------------------------------------------- * 備考: 0 <= x <= 127, 0 <= y <= 7 *    左上が原点。 *****************************************************************************/ void OLED_locate(unsigned char x,unsigned char y) { SPI_tx_byte(0x21, 0); // set column address register SPI_tx_byte(x, 0); // set x (column strat address) SPI_tx_byte(127, 0); // set column end address= 127→ これが無いと正しくロケートされない。 SPI_tx_byte(0x22, 0); // set page address register SPI_tx_byte(y, 0); // set y (page strat address) SPI_tx_byte(7, 0); // set page end address= 0→ これが無いと正しくロケートされない。 } /********************************************************************************* * 現在の書き込み位置から指定バイトのデータ列を書込む *---------------------------------------------------------------------------- * 入力: unsigned char *datap,int len * 出力: 無し *---------------------------------------------------------------------------- * 備考: このコマンドはlenの回数分x位置を増やしながら横へ * 1バンク分指定されたバイトデータを書き込む **********************************************************************************/ void OLED_set_data(unsigned char *datap,int len) { for (;len>0;len--) SPI_tx_byte(*(datap++), 1); } /**************************************************************************** * 現在の書き込み位置から指定バイトの表示クリア *---------------------------------------------------------------------------- * 入力: unsigned int len * char color * 出力: 無し *---------------------------------------------------------------------------- * 備考: このコマンドはlenの回数分x位置を増やしながら横へ * 1バンク分クリアする *****************************************************************************/ void OLED_clear(unsigned int len,char color) { for (;len>0;len--){ if(color == BLACK) SPI_tx_byte(0, 1); else SPI_tx_byte(0xFF, 1); } } /**************************************************************************** * 全画面クリア *---------------------------------------------------------------------------- * 入力: char color * 出力: 無し *---------------------------------------------------------------------------- * 備考: VRAM全エリアを指定色で塗りつぶす *****************************************************************************/ void OLED_clear_all(char color) { unsigned char i,j; unsigned char clear_data; OLED_locate(0,0); // 原点位置に、セット OLED_clear(OLED_WIDTH * OLED_BANKS,color); // 128x64全エリア・クリア /// SRAM上のセグメントバッファ オールクリア if(color == BLACK) clear_data = 0x00; else clear_data = 0xFF; for(i=0; i<128; i++){ for(j=0; j<8; j++){ SegmentBuffer[i][j] = clear_data; } } } /**************************************************************************** * Data/Command を指定して、LCDに1バイトを送信 *---------------------------------------------------------------------------- * 入力: unsigned char d,int dc * 出力: 無し *---------------------------------------------------------------------------- * 備考: dc = 0 : Command, dc = 1 : Data *****************************************************************************/ void SPI_tx_byte(unsigned char d,int dc) { char Loop; SPI_CS = 0; if (dc) { SPI_DC = 1; } else { SPI_DC = 0; } for (Loop=0;Loop<8;Loop++){ SPI_DATA = 0; if ((d & (unsigned char)0x80) != 0){ SPI_DATA = 1; } delay_us(3); SPI_CLK = 1; d <<= 1; delay_us(6); SPI_CLK = 0; } SPI_CS = 1; } /********************************* * OLEDに一文字表示する * 追加関数:N.Ishii **********************************/ void OLED_char (unsigned char dat){ unsigned char i,chptn; dat -= 0x20; //配列アドレスを計算→ フォントテーブルのトップ[0][5]からスペースコードの20hが格納されている for(i=0; i<5; i++) { //コラムデータを順に取得して、GDDRAMに転送 chptn = chrom[dat][i]; SPI_tx_byte(chptn, 1); //コラムデータを転送 } SPI_tx_byte(0, 1); //文字間隔を空ける } /**************************** * Rom 文字列出力 * 追加関数:N.Ishii *****************************/ void OLED_ROMstr(const char *str){ while(*str) //文字列終端(00)まで継続 OLED_char(*str++); //文字出力しポインタ+1 } /*********************************************************************** * X方向(Column)始点アドレスと、終点アドレスを設定する * 引数:x0=始点x座標 * x1=終点x座標 * page=ページNo ************************************************************************/ void Column_Page_Set(unsigned char x0, unsigned char x1, unsigned char page){ SPI_tx_byte(0xB0 | page, 0); // set GDDRAM page start address SPI_tx_byte(0x21, 0); // set column address register SPI_tx_byte(x0, 0); // set x (column strat address) SPI_tx_byte(x1, 0); // set column end address } /************************************************************************************* * 水平線描画関数 * 引数:x0,y0=始点座標 * x1=終点x座標 * color=線色:白=WHITE・黒=BLACK **************************************************************************************/ void Draw_Horizontal_Line(unsigned char x0, unsigned char y0, unsigned char x1, char color){ /// 座標制限 if(x0 > 127) x0 = 127; if(x1 > 127) x1 = 127; if(y0 > 63) y0 = 63; if(x0 == x1) return; int i; unsigned char X0 = x0, X1 = x1; unsigned char page = y0 / 8; // y座標を、セグメントのピクセル数=8で割った商が、ページNoになる。 unsigned char b; if(x1 < x0){ X0 = x1; X1 = x0; } b = 0x01 << (y0 % 8); // y座標を、セグメントのピクセル数=8で割った余りが、セグメント(バンク)内の、ドット位置になる。 Column_Page_Set(X0, X1, page); for(i=X0; i< (X1+1); i++){ //column = 8byte x 16 /// SegmentBuffer上に、出力パターンを書込む if(color == BLACK) SegmentBuffer[i][page] = SegmentBuffer[i][page] & ~b; else SegmentBuffer[i][page] = SegmentBuffer[i][page] | b; OLED_set_data(&SegmentBuffer[i][page], 1); // GDDRAMへ8ビットパターンを転送 } } /************************************************************************************* * 垂直線描画関数 * 引数:x0,y0=始点座標 * y1=終点y座標 * color=線色:白=WHITE・黒=BLACK **************************************************************************************/ void Draw_Vertical_Line(unsigned char x0, unsigned char y0, unsigned char y1, char color){ /// 座標制限 if(x0 > 127) x0 = 127; if(y0 > 63) y0 = 63; if(y1 > 63) y1 = 63; if(y0 == y1) return; int i; unsigned char Y0 = y0, Y1 = y1; if(y1 < y0){ Y0 = y1; Y1 = y0; } unsigned char pageA = Y0 / 8; unsigned char pageB = Y1 / 8; unsigned char pA = (unsigned char)(0xFF << (Y0 % 8)); unsigned char pB = ~( (unsigned char)(0xFF << ((Y1 % 8)+1)) ); if(pageA == pageB){ if(color == BLACK) SegmentBuffer[x0][pageA] = SegmentBuffer[x0][pageA] & ~(pA & pB); else SegmentBuffer[x0][pageA] = SegmentBuffer[x0][pageA] | (pA & pB); Column_Page_Set(x0, x0, pageA); OLED_set_data(&SegmentBuffer[x0][pageA], 1); }else{ if(color == BLACK){ SegmentBuffer[x0][pageA] = SegmentBuffer[x0][pageA] & ~pA; SegmentBuffer[x0][pageB] = SegmentBuffer[x0][pageB] & ~pB; } else{ SegmentBuffer[x0][pageA] = SegmentBuffer[x0][pageA] | pA; SegmentBuffer[x0][pageB] = SegmentBuffer[x0][pageB] | pB; } Column_Page_Set(x0, x0, pageA); OLED_set_data(&SegmentBuffer[x0][pageA], 1); for(i=(pageA+1); i 127) x0 = 127; if(y0 > 63) y0 = 63; unsigned char page = y0 / 8; // y座標を、セグメントのピクセル数=8で割った商が、ページNoになる。 unsigned char b; b = 0x01 << (y0 % 8); // y座標を、セグメントのピクセル数=8で割った余りが、セグメント(バンク)内の、ドット位置になる。 if(color == WHITE) SegmentBuffer[x0][page] = SegmentBuffer[x0][page] | b; if(color == BLACK) SegmentBuffer[x0][page] = SegmentBuffer[x0][page] & ~b; Column_Page_Set(x0, x0, page); OLED_set_data(&SegmentBuffer[x0][page], 1); } /************************************************************************** * 直線描画関数(原典先:後閑さんアルゴリズム) * 引数:x0,y0=始点座標 * x1,y1=終点座標 * color=線色:白=WHITE・黒=BLACK ***************************************************************************/ #define abs(a) (((a)>0) ? (a) : -(a)) void Draw_Line(int x0, int y0, int x1, int y1, char color){ /// 座標制限 if(x0 > 127) x0 = 127; if(x1 > 127) x1 = 127; if(y0 > 63) y0 = 63; if(y1 > 63) y1 = 63; if(x0 == x1 && y0 == y1) return; int steep, t; int deltax, deltay, error; int x, y; int ystep; /// 差分の大きいほうを求める steep = (abs(y1 - y0) > abs(x1 - x0)); /// x、yの入れ替え if(steep){ t = x0; x0 = y0; y0 = t; t = x1; x1 = y1; y1 = t; } if(x0 > x1) { t = x0; x0 = x1; x1 = t; t = y0; y0 = y1; y1 = t; } deltax = x1 - x0; // 傾き計算 deltay = abs(y1 - y0); error = 0; y = y0; /// 傾きでステップの正負を切り替え if(y0 < y1) ystep = 1; else ystep = -1; /// 直線を点で描画 for(x=x0; x= deltax) { y += ystep; error -= deltax; } } } /************************************* * 円を描く関数 * 中心点と半径を指定 * (Fussyさんのアルゴリズムを使用) **************************************/ void Draw_Circle(int x0, int y0, int r, char color) { /// 座標制限 if(x0 > 127) x0 = 127; if(y0 > 63) y0 = 63; int x = r; int y = 0; int F = -2 * r + 3; while(x >= y){ Draw_Pixel(x0+x, y0+y, color); Draw_Pixel(x0-x, y0+y, color); Draw_Pixel(x0+x, y0-y, color); Draw_Pixel(x0-x, y0-y, color); Draw_Pixel(x0+y, y0+x, color); Draw_Pixel(x0-y, y0+x, color); Draw_Pixel(x0+y, y0-x, color); Draw_Pixel(x0-y, y0-x, color); if(F >= 0){ x--; F -= 4 * x; } y++; F += 4 * y + 2; } } /**************************************************************************** * usec単位ディレイ関数 *---------------------------------------------------------------------------- * 入力: 符号無し16ビット整数 * 出力: 無し *---------------------------------------------------------------------------- * 備考: CLOCK:MIPS of this PIC ******************************************************************************/ //void delay_us(unsigned char usec){ void delay_us(int usec){ // usec = (unsigned char)(CLOCK*usec)/ 10; usec = (int)(CLOCK*usec)/ 10; while(usec) { asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP"); usec--; } } /**************************************************************************** * msec単位ディレイ関数 *---------------------------------------------------------------------------- * 入力: 符号無し16ビット整数 * 出力: 無し *---------------------------------------------------------------------------- * 備考: ******************************************************************************/ //void delay_ms(unsigned char msec){ void delay_ms(int msec){ // unsigned char i; int i; for(i=0; i< msec; i++) delay_us(1000); }