/************************************************************************************************** * UG-2864HSWEG01 OLEDモジュール駆動用ライブラリー * 制御IC:SSD1306 * * 原典:グラフィック描画関数の原典先は「mgo-tec電子工作」さん→ ESP-WROOM使用(AVRマイコン系) * * 2017.11.1 N.Ishii **************************************************************************************************/ #include "p33FJ64GP802.h" #include "SSD1306_OLEDlib_dsPIC33F.h" #include "ASCII_font.h" extern unsigned char SegmentBuffer[128][8]; // メインモジュールで宣言しているこの変数を使う。 /**************************************************************************** * UG-2864HSWEG01 OLEDモジュール制御IC SSD1306を 初期設定をする *****************************************************************************/ void OLED_init() { //// OLED初期設定 SPI_RES = 0; // LCD リセット指令 関数呼び出し Delay1m(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; } /// SSD1306の仕様で、クロック周期= 100nSminなので、Nop 1個入れた→ クロック幅:100nS+50nSで、周期は約150nS(単純計算値) Nop(); // 約50nS SPI_CLK = 1; d <<= 1; /// SSD1306の仕様で、クロック幅= 20nSminなので、多目にNop 2個入れた Nop(); // 約50nS Nop(); // 約50nS 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=ドットを打つ座標 * color=ドット色:白=WHITE・黒=BLACK * 備考:表示RAM上は左上が原点であるが、数学的に素直な左下が、原点位置になるように変換 171028 ****************************************************************************************************/ void Draw_Pixel(unsigned char x0, unsigned char y0, char color){ // unsigned char page = y0 / 8; // y座標を、セグメントのピクセル数=8で割った商が、ページNoになる。 unsigned char page = 7-(y0/8); unsigned char b; // b = 0x01 << (y0 % 8); // y座標を、セグメントのピクセル数=8で割った余りが、セグメント(バンク)内の、ドット位置になる。 b = 0x01 << 7-(y0%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){ 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) { 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; } } /****************************************** * 遅延関数群 ******************************************/ /** 1msec Delay SubFunction at 40MIPS ***/ void Delay1m(int time){ while(time > 0){ Delay1u(1000); time--; } } /** 1usec Delay SubFunction at 40MIPS ****/ void Delay1u(int time){ while(time > 0){ time--; Delay200n(); Delay200n(); Delay200n(); Delay200n(); Nop(); Nop(); Nop(); Nop(); } } /****** 200nsec delay sub function ***/ void Delay200n(void){ }