/********************************************************
 *  カラーグラフィックLCD(128ｘ160ドット）用ライブラリ
 *     （ST7735S/Bコントローラ用　漢字フォント内蔵)
 *	lcd_Init();     // 初期化
 *	lcd_Clear();	// 全画面消去
 *	lcd_Pixel();	// 1ドット表示
 *	lcd_kanji();	// 漢字文字列表示
 *	lcd_ascii();	// ASCII半角文字列表示
 *	lcd_Line();     // 直線描画
 *	lcd_Circle();	// 円描画
 *  lcd_Image();    // イメージ表示
 *********************************************************/
#include "config/default/definitions.h"
#include "font_lib.h"
#include "clorlcd_lib.h"

uint32_t MSB, LSB, Adrs;
/************************************
* LCDコマンド、データ出力関数(SPI)
*************************************/
void lcd_cmd(uint8_t cmnd){
    uint8_t cbuf[2];
    cbuf[0] = cmnd;
    RS_Clear();                     // RS Command 
    CS1_Clear();                    // CS Low
    SERCOM4_SPI_Write(cbuf, 1);
    RS_Set();                       // CD High
    CS1_Set();                      // CS High
}
void lcd_data(uint8_t data){
    uint8_t dbuf[2];
    dbuf[0] = data;    
    RS_Set();                       // RS Data
    CS1_Clear();                    // CS Low
    SERCOM4_SPI_Write(dbuf, 1);
    CS1_Set();                      // CS High
}

/***********************************************
 * LCD初期化関数  
 * ST7735SとBでRGBの順序が異なる
 *　横向きの初期設定 
************************************************/
void lcd_Init(void){
    CS1_Set();                  // CS High
    RST_Set();                  // Reset off
    Delay_ms(10);
    RST_Clear();				
    Delay_ms(10);               // リセット出力
    RST_Set();
    Delay_ms(120);    
    lcd_cmd(SLPOUT);            // スリープ解除
    Delay_ms(120);              // ウェイクアップ待ち 
    lcd_cmd(VCOM4L);            //  
    lcd_data(0x40);
    lcd_data(0x03);
    lcd_data(0x1A);
    lcd_cmd(NVCTR1);			// Dot Inversion
    lcd_data(0x60);
    lcd_cmd(VMOFCTR);			//
    lcd_data(0x90);
    Delay_ms(20);
    lcd_cmd(FRMCTR1);			//Frame Rate in normal mode
    lcd_data(0x04);
    lcd_data(0x25);
    lcd_data(0x18);
    lcd_cmd(FRMCTR2);			//Frame Rate in Idle mode
    lcd_data(0x04);
    lcd_data(0x25);
    lcd_data(0x18);
    lcd_cmd(FRMCTR3);			//Frame Rate in Partial mode
    lcd_data(0x04);
    lcd_data(0x25);
    lcd_data(0x18);
    lcd_data(0x04);
    lcd_data(0x25);
    lcd_data(0x18);
    lcd_cmd(INVCTR);			//Display Inversion normal
    lcd_data(0x03);
    lcd_cmd(DISSET5);			//Gate overlap
    lcd_data(0x15);
    lcd_data(0x02);
    lcd_data(PWCTR1);			//Power control
    lcd_data(0x02);
    lcd_data(0x70);             //VRH=4.7V IB=1uA
    lcd_cmd(PWCTR2);			//Power control
    lcd_data(0x07);             //VGHH=6*VCI
    lcd_cmd(PWCTR3);			//Power control Normal mode
    lcd_data(0x0D);             //****SAP=Small, AP=Large
    lcd_data(0x00);             //****BCLK/1 BCLK/3
    lcd_cmd(PWCTR4);			//Power control Idle mode
    lcd_data(0x8D);             //****Medium
    lcd_data(0xEA);             //****BCL/4 BCLK/16
    lcd_cmd(PWCTR5);			//Power control Partial mode
    lcd_data(0x8D);             //****Medeum
    lcd_data(0xEE);             //****BCLK/2 BCLK/4
    lcd_cmd(PWCTR6);			//Power control Partioal+Idle
    lcd_data(0x05);             //**** -0.55
    lcd_data(0x17);             //
    lcd_cmd(VMCTR1);			//VCOM control
    lcd_data(0x3C);             //VCOMH=4.0
    lcd_data(0x4F);             //VCOML=-0.525
    lcd_cmd(MADCTL);			//Memory Data Access H refresh
    // ST7735S=0xX0 ST7735B=0xX8とする　0x70(0x78)で横向き
    lcd_data(0xB8);             //0xBxと0x7xで表示上下逆 
    lcd_cmd(COLMOD);
    lcd_data(0x05);             // Color 16bit mode
    lcd_cmd(GAMCTRP1);          //Gamma correction +pole
    lcd_data(0x06);
    lcd_data(0x0E);
    lcd_data(0x05);
    lcd_data(0x20);
    lcd_data(0x27);
    lcd_data(0x23);
    lcd_data(0x1C);
    lcd_data(0x21);
    lcd_data(0x20);
    lcd_data(0x1C);
    lcd_data(0x26);
    lcd_data(0x2F);
    lcd_data(0x00);
    lcd_data(0x03);
    lcd_data(0x00);
    lcd_data(0x24);
    lcd_cmd(GAMCTRN1);          //Gamma correction -pole
    lcd_data(0x06);
    lcd_data(0x10);
    lcd_data(0x05);
    lcd_data(0x21);
    lcd_data(0x27);
    lcd_data(0x22);
    lcd_data(0x1C);
    lcd_data(0x21);
    lcd_data(0x1F);
    lcd_data(0x1D);
    lcd_data(0x27);
    lcd_data(0x2F);
    lcd_data(0x05);
    lcd_data(0x03);
    lcd_data(0x00);
    lcd_data(0x3F);
    lcd_cmd(DISPON);
    Delay_ms(10); 
    lcd_cmd(SLPOUT);			// スリープ解除
    Delay_ms(120);              // ウェイクアップ待ち    
}
/**********************************
* 画面消去関数
* 消去用色指定あり
* 物理画面は160×128ドット
***********************************/
void lcd_Clear(uint16_t Color){
    int i;

    lcd_cmd(CASET);				// コラムアドレスセット
    lcd_data(0);
    lcd_data(0);				// 0番地から
    lcd_data(0);
    lcd_data(COLEDGE);			// 画面全体
    lcd_cmd(RASET);				// Rawアドレスセット
    lcd_data(0);					
    lcd_data(0);				// 0番地から
    lcd_data(0);
    lcd_data(PAGEEDGE);         // 画面全体
    lcd_cmd(RAMWR);				// RAM書き込みコマンド
    // 16ビットカラーで塗りつぶす
    for(i=0; i<(COLEDGE)*(PAGEEDGE); i++){
        lcd_data(Color >> 8);
        lcd_data(Color);
    }
}
/***********************************
*  １ピクセル表示関数
*  座標は(0,0)-(159,128)  
***********************************/
void lcd_Pixel(int Xpos, int Ypos, uint16_t Color){
    /* アドレス範囲チェックし範囲外なら何もしない */
    if((Xpos<=ENDCOL) && (Ypos<=ENDPAGE)){
        Xpos += 1;				// 物理アドレス補正
        Ypos += 2;
        lcd_cmd(RASET);			// 行（RAW)アドレスセット
        lcd_data(0);	
        lcd_data(Ypos);			// 開始アドレス
        lcd_data(0);
        lcd_data(Ypos);			// 終了アドレス
        lcd_cmd(CASET);			// 列（COLUM)アドレスセット
        lcd_data(0);	
        lcd_data(Xpos);			// 開始アドレス
        lcd_data(0);	
        lcd_data(Xpos);			// 終了アドレス
        lcd_cmd(RAMWR);			// 1ピクセル書き込み
        // 16ビットカラーで書き込み
        lcd_data((uint8_t)(Color >> 8));
        lcd_data((uint8_t)Color);
        lcd_cmd(DISPON);        // 表示オン
    }
}
/**************************************
*  直線描画関数
 * (x0, y0)から(x1, y1)の直線
 * 原点は左下
**************************************/
#define	abs(a)	(((a)>0) ? (a) : -(a))
void lcd_Line(int x0, int y0, int x1, int y1, uint16_t Color)
{
    int steep, t;
    int deltax, deltay, error;
    int x, y;
    int ystep;
    
    y0=ENDPAGE-y0 -1;               // Y座標反転
    y1=ENDPAGE-y1 -1;
    /// 差分の大きいほうを求める
    steep = (abs(y1 - y0) > abs(x1 - x0));
    /// ｘ、ｙの入れ替え
    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<=x1; x++) {
        if(steep) lcd_Pixel(y,x,Color); else lcd_Pixel(x,y,Color);
        error += deltay;
        if((error << 1) >= deltax) {
            y += ystep;
            error -= deltax;
        }
    }
}
/*************************************
*  円を描く関数
*  中心点と半径を指定
*  (Fussyさんのアルゴリズムを使用)
**************************************/
void lcd_Circle(int x0, int y0, int r, uint16_t color)
{
    int x = r;
    int y = 0;
    int F = -2 * r + 3;

    while(x >= y){
        lcd_Pixel(x0+x, y0+y, color);
        lcd_Pixel(x0-x, y0+y, color);		
        lcd_Pixel(x0+x, y0-y, color);
        lcd_Pixel(x0-x, y0-y, color);		
        lcd_Pixel(x0+y, y0+x, color);
        lcd_Pixel(x0-y, y0+x, color);		
        lcd_Pixel(x0+y, y0-x, color);
        lcd_Pixel(x0-y, y0-x, color);
        if(F >= 0){
            x--;
            F -= 4 * x;
        }
        y++;
        F += 4 * y + 2;
    }
}
/*********************************************************
* イメージ表示関数
*　 BMPデータは左下から右へ、下から上へ格納されている
*   160×128=2,5kバイト/画面
*   グラフィック色(Color1)と背景色(Color2)両方を指定
**********************************************************/
void lcd_Image(const uint8_t *ptr, uint16_t Color1, uint16_t Color2)
{
    uint8_t Mask;
    int i, vline, colum;

    ptr += 2540;                                // ポインタを最後のラインにずらす
    for(vline=0; vline<ENDPAGE; vline++){		// 全体は128ライン
        for(colum=0; colum<ENDCOL/8; colum++){	// 横は8x20=160ドットとする
            Mask = 0x80;                        // 上位から右へシフト
            for(i=0; i<8; i++){                 // 1バイト分処理
                if(*ptr & Mask)                 // ビットチェック
                    lcd_Pixel(colum*8+i, vline, Color1);
                else
                    lcd_Pixel(colum*8+i, vline, Color2);					
                Mask = Mask >> 1;               // ビット位置シフト
            } 
            ptr++;                              // ライン内の次のデータ
        }
        ptr -= 40;                              // ひとつ上のラインへ移動
    }
}
/*****************************************
*  ANK文字表示関数 8x8ドット
*  160/8=20文字/行　128/8=16行
*  (0, 0) - (19, 15)の範囲
******************************************/
void lcd_sChar(uint8_t colum, uint8_t line, const uint8_t letter, uint16_t Color1, uint16_t Color2){
	uint8_t j, i, Mask;

	if((colum < 20) && (line < 16)){		// 範囲チェック
		for(j=0; j<5; j++){                 // 横5ドット
			Mask = 0x80;					// 上位ビットから表示
			for(i=0; i<8; i++){             // 縦1ライン分表示
				if((Font[letter-0x20][j] & Mask) != 0)
					lcd_Pixel(colum*8+j, (line+1)*8-i, Color1);
				else
					lcd_Pixel(colum*8+j, (line+1)*8-i, Color2); //背景色	
				Mask = Mask >> 1;
			}
		}
		for(j=5; j<8; j++){
			lcd_Pixel(colum*8+j, (line+1)*8-i, Color2); //背景色
		}
	}
}
/*****************************************
*  文字表示関数 12x12ドット
*  128/14=9行　160/12=13文字/行
*  (0, 0) - (12, 9)の範囲, 文字色、背景色
******************************************/
void lcd_Char(uint8_t colum, uint8_t line, uint8_t letter, uint16_t Color1, uint16_t Color2){
    uint8_t j, i, Mask;

    if((colum < XChar) && (line < YLine)){							// 範囲チェック
        // ANK表示出力　3バイトの2ラインずつを6回繰り返す
        for(j=0; j<6; j++){
            // 8ドット連続部の表示
            Mask = 0x80;
            for(i=0; i<8; i++){
                // １ライン目前半8ドット表示
                if((ANKFont[letter][j*3] & Mask) != 0)
                    lcd_Pixel(colum*12+i+2, line*14+j*2+1, Color1);
                else
                    lcd_Pixel(colum*12+i+2, line*14+j*2+1, Color2); 	//背景色
                // 2ライン目後半8ドット表示
                if((ANKFont[letter][j*3+2] & Mask) != 0)
                    lcd_Pixel(colum*12+i+6, line*14+j*2+2, Color1);
                else
                    lcd_Pixel(colum*12+i+6, line*14+j*2+2, Color2); 	//背景色
                Mask = Mask >> 1;
            }
            // 分割部4ドットずつ表示
            Mask = 0x80;
            // 1ライン目後半4ドット表示
            for(i=0; i<4; i++){
                if((ANKFont[letter][j*3+1] & Mask) != 0)
                    lcd_Pixel(colum*12+i+10, line*14+j*2+1, Color1);
                else
                    lcd_Pixel(colum*12+i+10, line*14+j*2+1, Color2);	 //背景色
                Mask = Mask >> 1;
            }
            // 2ライン目前半4ドット表示
            for(i=4; i<8; i++){
                if((ANKFont[letter][j*3+1] & Mask) != 0)
                    lcd_Pixel(colum*12+i-2, line*14+j*2+2, Color1);
                else
                    lcd_Pixel(colum*12+i-2, line*14+j*2+2, Color2);       //背景色
                Mask = Mask >> 1;
            }							
        }
    }
}
/******************************************
*   漢字ASCII文字列描画関数　12x12ドット
*   13文字ｘ9行で指定
********************************************/
void lcd_Str(uint8_t colum, uint8_t line, uint8_t *s, uint16_t Color1, uint16_t Color2)
{
    while (*s != 0){
        lcd_Char(colum++, line, *s++, Color1, Color2);
        if(colum >= 13){
            line++;
            colum = 0;
            if(line >= 9)
                line = 0;
        }
    }
}
/****************************************
*   ANK文字列描画関数 5x7ドット
*   20文字ｘ16行で指定
****************************************/
void lcd_sStr(uint8_t colum, uint8_t line, const uint8_t *s, uint16_t Color1, uint16_t Color2)
{
    while (*s != 0){
        lcd_sChar(colum++, line, *s++, Color1, Color2);
        if(colum >= 20){
            line++;
            colum = 0;
            if(line >= 16)
                line = 0;
        }
    }
}
/*************************************
* 漢字表示　コード指定で表示
* 　2バイトコードで指定
*   表示位置は13文字/9行で指定
**************************************/
void KanjiCode(uint8_t colum, uint8_t line, uint16_t upcode, uint16_t lowcode, uint16_t Color1, uint16_t Color2)
{
	uint16_t upper, lower, i, j;
    uint8_t Mask;
	// 漢字コードから配列インデックス計算
    upper = (upcode -0x81)*188;					// xxFC-xx40+1-1(7F分)
    if(lowcode < 0x7F)						// コード7Fはスキップ
        lower = (lowcode - 0x40);
    else
        lower = (lowcode - 0x41);				// 7Fコードの1文字分を引く
    // 漢字表示出力　3バイトの2ラインずつを6回繰り返す
    for(j=0; j<6; j++){
        // 8ドット連続部の表示
        Mask = 0x80;
        for(i=0; i<8; i++){
            // １ライン目前半8ドット表示
            if((KanjiFont12b[upper+lower][j*3] & Mask) != 0)
                lcd_Pixel(colum*12+i+2, line*14+j*2+1, Color1);
            else
                lcd_Pixel(colum*12+i+2, line*14+j*2+1, Color2); //背景色
            // 2ライン目後半8ドット表示
            if((KanjiFont12b[upper+lower][j*3+2] & Mask) != 0)
                lcd_Pixel(colum*12+i+6, line*14+j*2+2, Color1);
            else
                lcd_Pixel(colum*12+i+6, line*14+j*2+2, Color2); //背景色
            Mask = Mask >> 1;
        }
        // 分割部4ドットずつ表示
        Mask = 0x80;
        // 1ライン目後半4ドット表示
        for(i=0; i<4; i++){
            if((KanjiFont12b[upper+lower][j*3+1] & Mask) != 0)
                lcd_Pixel(colum*12+i+10, line*14+j*2+1, Color1);
            else
                lcd_Pixel(colum*12+i+10, line*14+j*2+1, Color2); //背景色
            Mask = Mask >> 1;
        }
        // 2ライン目前半4ドット表示
        for(i=4; i<8; i++){
            if((KanjiFont12b[upper+lower][j*3+1] & Mask) != 0)
                lcd_Pixel(colum*12+i-2, line*14+j*2+2, Color1);
            else
                lcd_Pixel(colum*12+i-2, line*14+j*2+2, Color2); //背景色
            Mask = Mask >> 1;
        }							
    }
}	
/*********************************************************
* 漢字文字列表示
*  13文字×9行で指定
*********************************************************/
void Kanji_Str(uint8_t colum, uint8_t line, const uint8_t *Msg, uint16_t Color1, uint16_t Color2)
{
    const uint8_t *ptr;
    
    ptr = Msg;
	while(*ptr != 0){	  
		if((line <= 9) && (colum <= 13)){				// 画面内か？	
			KanjiCode(colum++, line, (uint16_t)*ptr, (uint16_t)*(ptr+1), Color1, Color2);
            ptr += 2;
			if(colum >= 13){
				line++;
				colum = 0;
				if(line >= 9)
					line = 0;
			}
		}
	}
}
/***************************************
 * msec単位遅延関数
 **************************************/
void Delay_ms(int time)
{
    int j;
    while(time != 0){
        for(j=0; j<10000; j++)
            j = j;
        time--;
    }
}
