/*
 * ILI9341_SPI_Draw.c
 *
 *  Created on: 2024/11/21
 *      Author: egaono-electronics
 */

#include "ILI9341_SPI_Draw.h"

#include def_H_Periph_SPI



int8_t GLB_int8_Semaphore_LCD = 0;
                            // LCD表示のためのSemaphore



//----------------------------------------------------------
// LCD画面 Font 48 x 48 文字 1行分のBufferを含む
// 320 dot x 240 line : 40byte x 240 line 分のBuffer
//----------------------------------------------------------
uint8_t GLB_uint8_aDrawBuffer_LCD[
                    (uint16_t)def_Size_DrawBuffer_LCD];

uint8_t GLB_uint8_aBuffer_1Line_LCD[
                    (uint16_t)def_Number_Send_1Line_LCD];
                            // LCDに送信する1Line分のBuffer

uint8_t GLB_uint8_aWork_1Line_LCD[
                    (uint16_t)def_Number_Send_1Line_LCD * 2];
                                // LCDに送信する1Line分のWork



//----------------------------------------------------------
// ILI9341のRegister設定
//----------------------------------------------------------
// 引数;
// uint8_t *puint8_JobNum : 処理番号を格納する変数のポインタ

// int16_t int16_NSS_OnOff :
//                      設定データ送信終了時NSS ON/OFF選択
//    0 : 送信後 NSSをOFFにする。
//    1 : 送信後 NSSをONのままにする。

// uint8_t uint8_Address : Address of Register
// uint16_t uint16_DataLength : 書き込みデータ数
// uint8_t *puint8_aData :
//              書き込みデータが格納されているBufferのポインタ


// 戻り値;
//   -1 : 処理中
//    0 : OK
//    1 : NG
//----------------------------------------------------------
int16_t ILI9341_SPI_WriteRegisteer(uint8_t *puint8_JobNum,
                                   int16_t int16_NSS_OnOff,
                                   uint8_t uint8_Address,
                                   uint16_t uint16_DataLength,
                                   uint8_t *puint8_aData)
{
  static uint8_t STC_uint8_JobNum_SPI;  // SPI処理番号

  static int16_t STC_int16_Value_Wait_SPI_TxRx;
                                    // SPI送受信待ちの値


  int16_t int16_Return;
  uint16_t uint16_I;


  switch(*puint8_JobNum)
  {
  case 0:
    STC_uint8_JobNum_SPI = 0;  // SPI処理番号初期化

    //----------------------------------------------------------
    // レジスタアドレス送信
    //----------------------------------------------------------
    // RegisterのAddressをセット
    GLB_uint8_aSPI1_TxBuffer[0] = uint8_Address;

    // LCD : DCピンに 0 を出力 : Command
    HAL_GPIO_WritePin(def_GPIOx_LCD_DC,
                      def_GPIO_PIN_x_LCD_DC,
                      GPIO_PIN_RESET);

    // 送信データ数セット
    GLB_structParam_Handle_SPI1.int16_vSPI_TxNumber = 1;

    (*puint8_JobNum)++;  // 次の処理番号に移行する。
    int16_Return = -1;   // 処理継続
    break;

  case 1:
    //----------------------------------------------------------
    // SPIx  Master送受信
    //----------------------------------------------------------
    // 引数;
    // uint8_t *puint8_JobNum : 処理番号を格納する変数のポインタ

    // SPI_HandleTypeDef *pHandle_SPIx : SPI handler declaration

    // IRQn_Type DMA_Stream_IRQn_Transmit_SPIx : SPI DMA Transmit IRQn
    // IRQn_Type DMA_Stream_IRQn_Receive_SPIx : SPI DMA Receive IRQn

    // struct sParam_Handle_SPIx *pstructParam_Handle_SPIx :
    //              Handle SPI用パラメータの構造体のポインタ

    // GPIO_TypeDef *pGPIOx_SPIx_NSS : SPIx_NSS のGPIOポート
    // uint16_t uint16_GPIO_Pin_x_SPIx_NSS : SPIx_NSS のGPIOピン

    // int16_t int16_Value_Wait_SPI_TxRx : SPI送受信待ちの値

    // int16_t int16_NSS_OnOff : 送信終了時NSS ON/OFF選択
    //    0 : 送信後 NSSをOFFにする。
    //    1 : 送信後 NSSをONのままにする。


    // 戻り値;
    //   -1 : 処理中
    //    0 : OK
    //    1 : NG
    //----------------------------------------------------------
    int16_Return = SPIx_Master_TxRxByte(&STC_uint8_JobNum_SPI,
                                        &Handle_SPI1,
                                        DMA2_Stream3_IRQn,
                                        DMA2_Stream2_IRQn,
                                        &GLB_structParam_Handle_SPI1,
                                        def_GPIOx_SPI1_NSS,
                                        def_GPIO_PIN_x_SPI1_NSS,
                                        STC_int16_Value_Wait_SPI_TxRx,
                                        0);
    if(int16_Return == -1){
      // 処理中
      break;  // 処理番号維持 : 処理継続 : 現在の処理番号をループ
    }

    if(int16_Return != 0){
      *puint8_JobNum = 0;  // 処理番号初期化
      break;  // NG
    }


    if(uint16_DataLength == 0){
      // 書き込みData数が 0 の場合、ここで抜ける。
      *puint8_JobNum = 0;
      int16_Return = 0;  // OK終了
      break;
    }


    //------------------------------------------------------
    // 設定データ送信
    //------------------------------------------------------
    // 設定データをセット
    for(uint16_I = 0;
                uint16_I < uint16_DataLength; uint16_I++){
      GLB_uint8_aSPI1_TxBuffer[uint16_I]
                                = puint8_aData[uint16_I];
    }

    // LCD : DCピンに 1 を出力 : Data
    HAL_GPIO_WritePin(def_GPIOx_LCD_DC,
                      def_GPIO_PIN_x_LCD_DC,
                      GPIO_PIN_SET);

    // 送信データ数セット
    GLB_structParam_Handle_SPI1.int16_vSPI_TxNumber
                                        = uint16_DataLength;

    (*puint8_JobNum)++;  // 次の処理番号に移行する。
    int16_Return = -1;   // 処理継続
    break;

  case 2:
    //----------------------------------------------------------
    // SPIx  Master送受信
    //----------------------------------------------------------
    // 引数;
    // uint8_t *puint8_JobNum : 処理番号を格納する変数のポインタ

    // SPI_HandleTypeDef *pHandle_SPIx : SPI handler declaration

    // IRQn_Type DMA_Stream_IRQn_Transmit_SPIx : SPI DMA Transmit IRQn
    // IRQn_Type DMA_Stream_IRQn_Receive_SPIx : SPI DMA Receive IRQn

    // struct sParam_Handle_SPIx *pstructParam_Handle_SPIx :
    //              Handle SPI用パラメータの構造体のポインタ

    // GPIO_TypeDef *pGPIOx_SPIx_NSS : SPIx_NSS のGPIOポート
    // uint16_t uint16_GPIO_Pin_x_SPIx_NSS : SPIx_NSS のGPIOピン

    // int16_t int16_Value_Wait_SPI_TxRx : SPI送受信待ちの値

    // int16_t int16_NSS_OnOff : 送信終了時NSS ON/OFF選択
    //    0 : 送信後 NSSをOFFにする。
    //    1 : 送信後 NSSをONのままにする。


    // 戻り値;
    //   -1 : 処理中
    //    0 : OK
    //    1 : NG
    //----------------------------------------------------------
    int16_Return = SPIx_Master_TxRxByte(&STC_uint8_JobNum_SPI,
                                        &Handle_SPI1,
                                        DMA2_Stream3_IRQn,
                                        DMA2_Stream2_IRQn,
                                        &GLB_structParam_Handle_SPI1,
                                        def_GPIOx_SPI1_NSS,
                                        def_GPIO_PIN_x_SPI1_NSS,
                                        STC_int16_Value_Wait_SPI_TxRx,
                                        int16_NSS_OnOff);
    if(int16_Return == -1){
      // 処理中
      break;  // 処理番号維持 : 処理継続 : 現在の処理番号をループ
    }

    *puint8_JobNum = 0;  // 処理番号初期化
    // int16_Return : 結果
    break;

  default:
    *puint8_JobNum = 0;  // 処理番号初期化
    int16_Return = 1;    // NG
    break;
  }

  return(int16_Return);
}


//----------------------------------------------------------
// 表示Frame設定 : ILI9341
//----------------------------------------------------------
// 表示する向きと表示位置を設定する。
//----------------------------------------------------------
// 引数;
// uint8_t *puint8_JobNum : 処理番号を格納する変数のポインタ

// uint16_t uint16_Oblong : 縦長/横長 選択
//    0 : Lengthwise : 縦長 左
//    1 : Lengthwise : 縦長 右
//    2 : WidthwiseL : 横長 左
//    3 : WidthwiseR : 横長 右

// struct sFrame_LCD *pstructFrame_LCD :
//                          表示パラメータ構造体のポインタ


// 戻り値;
//   -1 : 処理中
//    0 : OK
//    1 : NG
//----------------------------------------------------------
int16_t ILI9341_SPI_SetFrame(uint8_t *puint8_JobNum,
                             uint16_t uint16_Oblong,
                        struct sFrame_LCD *pstructFrame_LCD)
{
  static uint8_t STC_uint8_JobNum;
                            // レジスタ書き込み処理番号

  static uint8_t STC_uint8_aData[8];

  int16_t int16_Return;


  switch(*puint8_JobNum)
  {
  case 0:
    STC_uint8_JobNum = 0;  // レジスタ書き込み処理番号初期化

    //------------------------------------------
    // 36 レジスタ : Memory Access Control
    //------------------------------------------
    // 表示する向きの設定
    //------------------------------------------
    if(uint16_Oblong == def_LengthwiseL){
      STC_uint8_aData[0] = 0x40;  // Lengthwiseの場合 : 縦長 左
    }
    else if(uint16_Oblong == def_LengthwiseR){
      STC_uint8_aData[0] = 0x80;  // Lengthwiseの場合 : 縦長 右
    }
    else if(uint16_Oblong == def_WidthwiseL){
      STC_uint8_aData[0] = 0xE0;  // WidthwiseLの場合 : 横長 左
    }
    else{
      STC_uint8_aData[0] = 0x20;  // WidthwiseRの場合 : 横長 右
    }

    //------------------------------------------------------
    // 仕様書の説明と食い違うが、RGBの順番で送信したとき
    // BGR = 1 でないと指定した色にならない。
    //------------------------------------------------------
    STC_uint8_aData[0] |= 0x08;  // BGR = 1
    //------------------------------------------------------

    (*puint8_JobNum)++;  // 次の処理番号に移行する。
    int16_Return = -1;   // 処理継続
    break;

  case 1:
    //----------------------------------------------
    // 36 レジスタ書き込み : Memory Access Control
    //----------------------------------------------
    int16_Return = ILI9341_SPI_WriteRegisteer(&STC_uint8_JobNum,
                                              0,
                                              (uint8_t)0x36,
                                              1,
                                              STC_uint8_aData);
    if(int16_Return == -1){
      // 処理中
      break;  // 処理番号維持 : 処理継続 : 現在の処理番号をループ
    }

    if(int16_Return != 0){
      *puint8_JobNum = 0;  // 処理番号初期化
      break;  // NG終了
    }


    //----------------------------------------------
    // 2A レジスタ : Column Address Set
    // に設定するデータをセット
    //----------------------------------------------
    STC_uint8_aData[0]
        = (uint8_t)((pstructFrame_LCD->uint16_SC & 0xFF00) >> 8);
                                                    // SC[15:8]
    STC_uint8_aData[1]
        = (uint8_t)(pstructFrame_LCD->uint16_SC & 0x00FF);
                                                    // SC[7:0]
    STC_uint8_aData[2]
        = (uint8_t)((pstructFrame_LCD->uint16_EC & 0xFF00) >> 8);
                                                    // EC[15:8]
    STC_uint8_aData[3]
        = (uint8_t)(pstructFrame_LCD->uint16_EC & 0x00FF);
                                                    // EC[7:0]

    (*puint8_JobNum)++;  // 次の処理番号に移行する。
    int16_Return = -1;   // 処理継続
    break;

  case 2:
    //----------------------------------------------
    // 2A レジスタ書き込み : Column Address Set
    //----------------------------------------------
    int16_Return = ILI9341_SPI_WriteRegisteer(&STC_uint8_JobNum,
                                              0,
                                              (uint8_t)0x2A,
                                              4,
                                              STC_uint8_aData);
    if(int16_Return == -1){
      // 処理中
      break;  // 処理番号維持 : 処理継続 : 現在の処理番号をループ
    }

    if(int16_Return != 0){
      *puint8_JobNum = 0;  // 処理番号初期化
      break;  // NG終了
    }

    //----------------------------------------------
    // 2B レジスタ : Page Address Set
    // に設定するデータをセット
    //----------------------------------------------
    STC_uint8_aData[0]
        = (uint8_t)((pstructFrame_LCD->uint16_SP & 0xFF00) >> 8);
                                                    // SP[15:8]
    STC_uint8_aData[1]
        = (uint8_t)(pstructFrame_LCD->uint16_SP & 0x00FF);
                                                    // SP[7:0]
    STC_uint8_aData[2]
        = (uint8_t)((pstructFrame_LCD->uint16_EP & 0xFF00) >> 8);
                                                    // EP[15:8]
    STC_uint8_aData[3]
        = (uint8_t)(pstructFrame_LCD->uint16_EP & 0x00FF);
                                                    // EP[7:0]

    (*puint8_JobNum)++;  // 次の処理番号に移行する。
    int16_Return = -1;   // 処理継続
    break;

  case 3:
    //----------------------------------------------
    // 2B レジスタ書き込み : Page Address Set
    //----------------------------------------------
    int16_Return = ILI9341_SPI_WriteRegisteer(&STC_uint8_JobNum,
                                              0,
                                              (uint8_t)0x2B,
                                              4,
                                              STC_uint8_aData);
    if(int16_Return == -1){
      // 処理中
      break;  // 処理番号維持 : 処理継続 : 現在の処理番号をループ
    }

    *puint8_JobNum = 0;  // 処理番号初期化
    break;

  default:
    *puint8_JobNum = 0;  // 処理番号初期化
    int16_Return = 1;    // NG
    break;
  }

  return(int16_Return);
}


//----------------------------------------------------------
// ILI9341のレジスタ設定と表示Data送信
//----------------------------------------------------------
// 引数;
// uint8_t *puint8_JobNum : 処理番号を格納する変数のポインタ

// int16_t int16_NSS_OnOff :
//                      設定データ送信終了時NSS ON/OFF選択
//    0 : 送信後 NSSをOFFにする。
//    1 : 送信後 NSSをONのままにする。

// uint16_t uint16_Color : 色
// uint16_t uint16_Length : 文字数
// uint8_t *puint8_aData :
//              送信データが格納されているBufferのポインタ


// 戻り値;
//   -1 : 処理中
//    0 : OK
//    1 : NG
//----------------------------------------------------------
int16_t ILI9341_SPI_SendData(uint8_t *puint8_JobNum,
                             int16_t int16_NSS_OnOff,
                             uint16_t uint16_Length,
                             uint8_t *puint8_aData)
{
  int16_t int16_Return;


  //------------------------------------------------
  // 2C レジスタ書き込み : Memory Write
  //------------------------------------------------
  int16_Return = ILI9341_SPI_WriteRegisteer(puint8_JobNum,
                                            int16_NSS_OnOff,
                                            (uint8_t)0x2C,
                                            uint16_Length,
                                            puint8_aData);

  return(int16_Return);
}



//----------------------------------------------------------
// LCD表示用送信データ作成(カラーコード指定)  : RGB 2byte
//----------------------------------------------------------
// 24bitのカラーコードから 2byte のRGBデータを作成する。
//----------------------------------------------------------
// 引数:
// uint32_t uint32_ColorCode : カラーコード

// uint16_t uint16_Width : 表示領域の幅
// uint16_t uint16_Position : DrawDataの位置
// uint8_t *puint8_aDrawData :
//                  Drawデータが格納されたBufferのポインタ
// uint8_t *puint8_aSendData :
//                  送信データを格納するBufferのポインタ


// 戻り値;
//    なし
//----------------------------------------------------------
void MakeSendData_1Line_ColorCode_2byte(
                                uint32_t uint32_ColorCode,
                                uint16_t uint16_Width,
                                uint16_t uint16_Position,
                                uint8_t *puint8_aDrawData,
                                uint8_t *puint8_aSendData)
{
  uint16_t uint16_I;
  uint16_t uint16_J;

  uint16_t uint16_SendLength;
  uint16_t uint16_Remain;
  uint16_t uint16_CountJ;
  uint16_t uint16_WritePosition = 0;

  uint8_t uint8_Graph;

  uint8_t *puint8_ColorCode = (uint8_t *)&uint32_ColorCode;


  uint8_t uint8_R;
  uint8_t uint8_G;
  uint8_t uint8_B;

  uint8_t uint8_Code_H;
  uint8_t uint8_Code_L;


  uint8_R = puint8_ColorCode[2];
  uint8_G = puint8_ColorCode[1];
  uint8_B = puint8_ColorCode[0];

  uint16_SendLength = uint16_Width / 8;  // byte数算出
  uint16_Remain = uint16_Width % 8;  // 1byte未満の余りを取得

  uint16_SendLength++;
        // 1byte未満の余りがあってもなくても1byte分加算する。

  //------------------------------------------------------------
  // 表示データ指定分だけ転送 :
  //            1Line : uint16_Width(dot) x (RGB : 3byte)
  //------------------------------------------------------------
  for(uint16_I = 0; uint16_I < uint16_SendLength; uint16_I++){
    if(uint16_I == uint16_SendLength - 1){
      // 最後の1byteの処理
      if(uint16_Remain == 0){
        // 1byte未満の余りがない場合終了
        break;
      }

      uint16_CountJ = uint16_Remain;
                        // 1byte未満の余りをカウント数にセット
    }
    else{
      // 最後の1byteでない場合
      uint16_CountJ = 8;
                    // 1byte分のbit数 8 をカウント数にセット
    }


    // 表示データを1バイトセット
    uint8_Graph = puint8_aDrawData[uint16_Position + uint16_I];

    for(uint16_J = 0; uint16_J < uint16_CountJ; uint16_J++){
      // 表示ドットの有無を判定
      if((uint8_Graph & 0x80) != 0){
        // 表示ドットあり
        //--------------------------------------------------
        // 2byte RGBデータの作成とセット
        //--------------------------------------------------
        uint8_Code_H = uint8_R & 0xF8;
        uint8_Code_H |= (uint8_G >> 5);

        uint8_Code_L = (uint8_G << 3) & 0xE0;
        uint8_Code_L |= (uint8_B >> 3);

        puint8_aSendData[uint16_WritePosition++] = uint8_Code_H;
        puint8_aSendData[uint16_WritePosition++] = uint8_Code_L;
      }
      else{
        // 表示ドットなし
        //--------------------------------------------------
        // 書き込み位置のみ更新
        //--------------------------------------------------
        uint16_WritePosition++;
        uint16_WritePosition++;
      }

      uint8_Graph <<= 1;
    }
  }
}
