/* ---------------------------------------------------------------------------
関数 sg12864a

関数 display_function(int mode)から呼び出される関数。
LCD表示処理 16dot(BitMap情報32byte)、半角(16byte)を１文字書く
SUNLIKE社グラフィックLCDモジュール SG12864ASLB-GB-R01（以降SG12864A）用
SG12864Aは秋月電子で1700円で購入(081119時点)

１．使い方
 1)最初mode=0を指定して一回実行。
      mode=0   初期化  position_x, position_y, dot16x16dataは処理しない

 2)mode=1,2で文字を表示。
   mode=1   半角8x16文字
    表示位置はposition_x, position_y（グローバル変数）です。
    dot16x16data[]（グローバル変数）は前半16byteを使用、後半を無視します。

   mode=2   全角16x16文字
    表示位置はposition_x, position_y（グローバル変数）です。
    dot16x16data[]（グローバル変数）を使用します。

   mode=11   mome=1のスロー表示版（動作チェック用、効果用）
   mode=12   mome=2のスロー表示版（動作チェック用、効果用）

   modeの13〜100は反転、アンダーライン指定などのためにReserveしておく。

   mode=500 mode=500の説明はdisplay_functionのコメントにあり


２．呼び出しを行う関数
 本プログラムで使用する処理系依存の関数を説明します。
 本関数の汎用性を維持するための処置として仮想名称で、
 これらの関数本体はそれぞれの処理系で用意します。

 1)GPIO制御関数
    gpio_out_function( CLK_OUT    , 1 / 0 );
    gpio_out_function( DATA_OUT   , 1 / 0 );
    gpio_out_function( ENABLE_OUT , 1 / 0 );
 2)時間を空ける関数
    delay_msec_function( int mSec単位 );

３．この関数で使用するグローバル変数
    position_x, position_y, dot16x16data

 1)int position_x, position_y; // 座標 左上を0.0とし、横をx、縦をyとする。
                               // xは8dotが一単位、yは16dotが一単位
                               // mode=0 や mode=3 ならば無視

 2)unsigned char  dot16x16data[];// BitMap情報32byte文字列ポインタ
                                 // 半角ならば前半16byteを使用

 3)init_sg12864a[];              // SG12864Aの初期化設定情報が入っている
 ---------------------------------------------------------------------------*/

void sg12864a(mode)
int mode;
{
  int    d,i,j,k,l;
  if(mode == 0)
  {
    gpio_out_function( GPIO_OUT_INIT, 0 );// 各出力信号を初期化、出力に設定する
    gpio_out_function( CLK_OUT      , 0 );// 各出力信号を初期状態にする
    gpio_out_function( ENABLE_OUT   , 0 );
    gpio_out_function( DATA_OUT     , 0 );
    delay_msec_function(1);

    for(i=0; i<2; ++i) // CS
    {
      for(j=0; j<4; ++j) //
      {
        for(k=0; k<11; ++k) // 同期式シリアルデータ送付
        {  // bitデータ送付順序 CS2 CS1 D7 D6 D5 D4 D3 D2 D1 D0 D/I 計11bit
          if( k==0 ) /* CS2 */
          {
            if( i==1 ) d = 1;  else  d = 0;  //CS1or2
            gpio_out_function( DATA_OUT, d );
          }
          if( k==1 ) /* CS1 */
          {
            if( i==1 ) d = 0;  else  d = 1;  //CS1or2
            gpio_out_function( DATA_OUT, d );
          }
          if( k>1 && k<10 ) /* bitData */
          {
            d = ( init_sg12864a[j]>>(9-k) ) & 1;
            gpio_out_function( DATA_OUT, d );
          }
          if( k==10 ) /* D/I */
          {
            gpio_out_function( DATA_OUT, 0 );
          } // 以上で11bit送付完了
          gpio_out_function( CLK_OUT, 1 );
          gpio_out_function( CLK_OUT, 0 );
        }
        gpio_out_function( ENABLE_OUT, 1 );
        gpio_out_function( ENABLE_OUT, 0 );
      }
    }
  }

  if( mode==1 || mode==2 )// 描画 開始
  {
    // 各出力信号を初期状態にする
    gpio_out_function( CLK_OUT, 0 );
    gpio_out_function( ENABLE_OUT, 0 );
    gpio_out_function( DATA_OUT, 0 );
    delay_msec_function(1);

    // 座標指定処理 position_x=0-16 position_y=0-3
    // 桁：0-7はCS1指定つまりLCD左半分、8-15はCS2指定指定つまりLCD右半分
    // 行0-3

    // 全角を書く時は 1byte目の時に左半分を書いて、2byte目の時に右半分を書く。
    //そうすると液晶CS1で左半分表示、CS2で右半分表示が可能となる。

    for(l=0; l<mode; ++l)  // l=0の時は左8dot描画、l=1の時は右8dot描画
    {
      for(i=0; i<2; ++i) // i=0の時は上8dot描画、i=1の時は下8dot描画
      {                 // mode=1の時は半角なので l=0の処理のみを行う
        for(k=0; k<11; ++k) //同期式シリアルデータ送付 横座標指定
        { // bitデータ送付順序 CS2 CS1 D7 D6 D5 D4 D3 D2 D1 D0 D/I 計11bit
          if( k==0 ) /* CS2 */
          {
            if( (position_x+l)>7 ) d = 1; else  d = 0;//CS1or2
            gpio_out_function( DATA_OUT, d );
          }
          if( k==1 ) /* CS1 */
          {
            if( (position_x+l)>7 ) d = 0; else  d = 1;//CS1or2
            gpio_out_function( DATA_OUT, d );
          }
          if( k>1 && k<10 ) /* bitData */
          {
            if( (position_x+l)>7 ) d=(position_x+l)-8; else d=(position_x+l);
              d = ((init_sg12864a[2]+d*8)>>(9-k)) & 1;
                                    //Column Address Set  横方向1dot単位で指定
            gpio_out_function( DATA_OUT, d );
          }

          if( k==10 ) /* D/I=0は制御情報を処理する場合  */
          {
            gpio_out_function( DATA_OUT, 0 );
          } // 以上で11bit送付完了
          gpio_out_function( CLK_OUT, 1 );
          gpio_out_function( CLK_OUT, 0 );
        }
        gpio_out_function( ENABLE_OUT, 1 );
        gpio_out_function( ENABLE_OUT, 0 );

        for(k=0; k<11; ++k) //同期式シリアルデータ送付 縦座標指定
        {
          // bitデータ送付順序 CS2 CS1 D7 D6 D5 D4 D3 D2 D1 D0 D/I 計11bit
          if( k==0 ) /* CS2 */
          {
            if( (position_x+l)>7 ) d = 1; else d = 0;//CS1or2
            gpio_out_function( DATA_OUT, d );
          }
          if( k==1 ) /* CS1 */
          {
            if( (position_x+l)>7 ) d = 0; else d = 1;//CS1or2
            gpio_out_function( DATA_OUT, d );
          }
          if( k>1 && k<10 ) /* bitData */
          {
            d = ((init_sg12864a[3]+position_y*2+i)>>(9-k)) & 1;
                                  //*Page Address Set 縦方向 8dit単位の指定
            gpio_out_function( DATA_OUT, d );
          }
          if( k==10 ) /* D/I=0は制御情報を処理する場合  */
          {
            gpio_out_function( DATA_OUT, 0 );
          } // 以上で11bit送付完了
          gpio_out_function( CLK_OUT, 1 );
          gpio_out_function( CLK_OUT, 0 );
        }
        gpio_out_function( ENABLE_OUT, 1 );
        gpio_out_function( ENABLE_OUT, 0 );

        for(j=0; j<8; ++j)// bitシフト量指定
        {
          for(k=0; k<11; ++k) // 同期式シリアルデータ送付
          {  // bitデータ送付順序 CS2 CS1 D7 D6 D5 D4 D3 D2 D1 D0 D/I 計11bit
            if( k==0 ) /* CS2 */
            {
              if( (position_x+l)>7 ) d = 1;  else  d = 0;  //CS1or2判定
              gpio_out_function( DATA_OUT, d );
                
            }
            if( k==1 )
            { /* CS1 */
              if( (position_x+l)>7 ) d = 0;  else  d = 1;  //CS1or2判定
              gpio_out_function( DATA_OUT, d );
            }
            if( k>1 && k<10 )/* bitData */ 
            {             // i=0の時は上8dot描画、i=1の時は下8dot描画
              if(mode==1)
              {//半角
                d = ( dot16x16data[ (i*8) + (9-k) ]>>(7-j) ) & 1;
                gpio_out_function( DATA_OUT, d );
              }
              if(mode==2)
              {//全角
                d = ( dot16x16data[ (i*16) + ((9-k)*2+l) ]>>(7-j) ) & 1;
                gpio_out_function( DATA_OUT, d );
              }
            }
            if( k==10 )// D/I=1は描画情報を処理する場合の指定
            { 
              gpio_out_function( DATA_OUT, 1 );
            } // 以上で11bit送付完了
            gpio_out_function( CLK_OUT, 1 );
            gpio_out_function( CLK_OUT, 0 );
          }
          gpio_out_function( ENABLE_OUT, 1 );
          gpio_out_function( ENABLE_OUT, 0 );
        }
      }// l のループ終端　 l=0の時は左8dot描画、l=1の時は右8dot描画
    }
    // 各出力信号を初期状態にする
    delay_msec_function(1);
    gpio_out_function( CLK_OUT, 0 );
    gpio_out_function( ENABLE_OUT, 0 );
    gpio_out_function( DATA_OUT, 0 );
  }

  if( mode==11 || mode==12 )// 描画 開始
  {
    mode=mode-10;
    // 各出力信号を初期状態にする
    gpio_out_function( CLK_OUT, 0 );
    gpio_out_function( ENABLE_OUT, 0 );
    gpio_out_function( DATA_OUT, 0 );
    delay_msec_function(1);

    // 座標指定処理 position_x=0-16 position_y=0-3
    // 桁：0-7はCS1指定つまりLCD左半分、8-15はCS2指定指定つまりLCD右半分
    // 行0-3

    // 全角を書く時は 1byte目の時に左半分を書いて、2byte目の時に右半分を書く。
    //そうすると液晶CS1で左半分表示、CS2で右半分表示が可能となる。

    for(l=0; l<mode; ++l)  // l=0の時は左8dot描画、l=1の時は右8dot描画
    {
      for(i=0; i<2; ++i) // i=0の時は上8dot描画、i=1の時は下8dot描画
      {                 // mode=1の時は半角なので l=0の処理のみを行う
        for(k=0; k<11; ++k) //同期式シリアルデータ送付 横座標指定
        { // bitデータ送付順序 CS2 CS1 D7 D6 D5 D4 D3 D2 D1 D0 D/I 計11bit
          if( k==0 ) /* CS2 */
          {
            if( (position_x+l)>7 ) d = 1; else  d = 0;//CS1or2
            gpio_out_function( DATA_OUT, d );
          }
          if( k==1 ) /* CS1 */
          {
            if( (position_x+l)>7 ) d = 0; else  d = 1;//CS1or2
            gpio_out_function( DATA_OUT, d );
          }
          if( k>1 && k<10 ) /* bitData */
          {
            if( (position_x+l)>7 ) d=(position_x+l)-8; else d=(position_x+l);
              d = ((init_sg12864a[2]+d*8)>>(9-k)) & 1;
                                    //Column Address Set  横方向1dot単位で指定
            gpio_out_function( DATA_OUT, d );
          }

          if( k==10 ) /* D/I=0は制御情報を処理する場合  */
          {
            gpio_out_function( DATA_OUT, 0 );
          } // 以上で11bit送付完了
          gpio_out_function( CLK_OUT, 1 );
          gpio_out_function( CLK_OUT, 0 );
        }
        gpio_out_function( ENABLE_OUT, 1 );
        gpio_out_function( ENABLE_OUT, 0 );

        for(k=0; k<11; ++k) //同期式シリアルデータ送付 縦座標指定
        {
          // bitデータ送付順序 CS2 CS1 D7 D6 D5 D4 D3 D2 D1 D0 D/I 計11bit
          if( k==0 ) /* CS2 */
          {
            if( (position_x+l)>7 ) d = 1; else d = 0;//CS1or2
            gpio_out_function( DATA_OUT, d );
          }
          if( k==1 ) /* CS1 */
          {
            if( (position_x+l)>7 ) d = 0; else d = 1;//CS1or2
            gpio_out_function( DATA_OUT, d );
          }
          if( k>1 && k<10 ) /* bitData */
          {
            d = ((init_sg12864a[3]+position_y*2+i)>>(9-k)) & 1;
                                  //*Page Address Set 縦方向 8dit単位の指定
            gpio_out_function( DATA_OUT, d );
          }
          if( k==10 ) /* D/I=0は制御情報を処理する場合  */
          {
            gpio_out_function( DATA_OUT, 0 );
          } // 以上で11bit送付完了
          gpio_out_function( CLK_OUT, 1 );
          gpio_out_function( CLK_OUT, 0 );
        }
        gpio_out_function( ENABLE_OUT, 1 );
        gpio_out_function( ENABLE_OUT, 0 );

        for(j=0; j<8; ++j)// bitシフト量指定
        {
          for(k=0; k<11; ++k) // 同期式シリアルデータ送付
          {  // bitデータ送付順序 CS2 CS1 D7 D6 D5 D4 D3 D2 D1 D0 D/I 計11bit
            if( k==0 ) /* CS2 */
            {
              if( (position_x+l)>7 ) d = 1;  else  d = 0;  //CS1or2判定
              gpio_out_function( DATA_OUT, d );
                
            }
            if( k==1 )
            { /* CS1 */
              if( (position_x+l)>7 ) d = 0;  else  d = 1;  //CS1or2判定
              gpio_out_function( DATA_OUT, d );
            }
            if( k>1 && k<10 )/* bitData */ 
            {             // i=0の時は上8dot描画、i=1の時は下8dot描画
              if(mode==1)
              {//半角
                d = ( dot16x16data[ (i*8) + (9-k) ]>>(7-j) ) & 1;
                gpio_out_function( DATA_OUT, d );
              }
              if(mode==2)
              {//全角
                d = ( dot16x16data[ (i*16) + ((9-k)*2+l) ]>>(7-j) ) & 1;
                gpio_out_function( DATA_OUT, d );
              }
            }
            if( k==10 )// D/I=1は描画情報を処理する場合の指定
            { 
              gpio_out_function( DATA_OUT, 1 );
            } // 以上で11bit送付完了
            gpio_out_function( CLK_OUT, 1 );
//            delay_msec_function(30);// スロー制御用の時間を空ける処理
            gpio_out_function( CLK_OUT, 0 );
          }
          gpio_out_function( ENABLE_OUT, 1 );
//          delay_msec_function(30);// スロー制御用の時間を空ける処理
          gpio_out_function( ENABLE_OUT, 0 );
        }
      }// l のループ終端　 l=0の時は左8dot描画、l=1の時は右8dot描画
    }
    // 各出力信号を初期状態にする
    delay_msec_function(1);
    gpio_out_function( CLK_OUT, 0 );
    gpio_out_function( ENABLE_OUT, 0 );
    gpio_out_function( DATA_OUT, 0 );
  }

  if( mode==500 )// mode=500の説明はdisplay_functionのコメントにあり
  {
    gpio_out_function( ENABLE_OUT, 0 );
    gpio_out_function( DATA_OUT, 0 );

    gpio_out_function( CLK_OUT, 1 );
    delay_msec_function(1);
    gpio_out_function( CLK_OUT, 0 );
    delay_msec_function(1);
  }
}

/* ---------------------------------------------------------------------------
 関数 display_function(mode)

 LCD表示処理関数 sg12864a を呼ぶ。

  mode=0〜100の間ならば、そのまま sg12864a を呼ぶのみ。
  mode=101ならば、position_x, position_yを自動インクリメントして半角表示。
  (modeから100を引くと自動インクリメント解除)
  mode=102ならば、position_x, position_yを自動インクリメントして全角表示。
  (modeから100を引くと自動インクリメント解除)
  mode=400ならば、ディスプレイを全面クリア。  
  mode=500にてこの関数を常時実行し表示を継続させる。
       現在の表示を継続 position_x, position_y, dot16x16dataを無視
       この処理は表示器にVRAMがある場合不要であるが、VRAMの無い場合必須。
       SG12864A内にはVRAMがあるため処理は特に無い。
 ---------------------------------------------------------------------------*/

void display_function(mode)
int mode;
{
  int i;  char temp[32];

  if(mode==400)//LCD全域クリア
  {
    sg12864a(0/*mode*/);//LCD初期化。もし表示異常時でもここで自動復旧

    for( i=0; i<32; ++i ) dot16x16data[i] = 0;//空白作成用にクリア
    for( position_x=0; position_x<16; position_x=position_x+2 )
    {
      for( position_y=0; position_y<4; ++position_y )
      {
        sg12864a(2/*mode*/);//LCDクリア
      }
    }
    position_x=0; position_y=0;//開始位置に戻す
  }

  if(mode<101)
  {
    sg12864a(mode);//mode0〜100までは modeをそのまま渡すのみ
  }

  if(100<mode && mode<201)//mode101〜201は、表示位置を自動インクリメント
  {

    if(mode==102)//全角文字を書くmodeで15桁目ならば、次行に進む処理を行う
    {
      if( position_x==15 ){ position_x=0; position_y=position_y+1; }
    }

    if( position_y>3 )//最終行の次まで行っていたならLCD全域クリアを実施
    {
      delay_msec_function(STOP_TIME_OF_DISPLAY);//人間が読む時間分、表示を一時停止させる
      for( i=0; i<32; ++i ) temp[i]=dot16x16data[i];//今から表示するフォントを退避
      for( i=0; i<32; ++i ) dot16x16data[i] = 0;//空白作成用にクリア
      for( position_x=0; position_x<16; position_x=position_x+2 )
      {//LCD全域クリア
        for( position_y=0; position_y<4; ++position_y )
        {
          sg12864a(2/*mode*/);
        }
      }
      position_x=0; position_y=0;
      for( i=0; i<32; ++i ) dot16x16data[i]=temp[i];//退避させていたフォントを元に戻す
    }

    sg12864a(mode-100);//全角or半角をLCD表示

    if( mode == 101) position_x = position_x+1;
    if( mode == 102) position_x = position_x+2;
    if( position_x>15 )  { position_x=0; position_y=position_y+1; }
     // position_yが最終行の次まで行っていたならLCD全域クリアを実施して
     // position_y=0を実行するが、これは次回に文字を書く時に実施する。

  }
  if( mode == 500) sg12864a(mode);
}
