/**
 * @file  ada_if.c
 * @brief ADA-IF(I2Sオーディオ通信）ドライバ
 *
 * @version: $Id: lcd_1602_pin_def.h $
 *
 * 本ソフトウェアの著作権は作成元である(株)北斗電子が所有するものとし、
 * (株)北斗電子は、以下の (1)～(3) の条件を満たす場合に限り、
 * 本ソフトウェア（本ソフトウェアを改変したものを含む。以下同じ）を
 * 使用・複製・改変・再配布（以下、利用と呼ぶ）することを無償で許諾する。
 *
 * (1) 本ソフトウェアをソースコードの形で利用する場合には、下記の著作
 *     権表示、この利用条件が、そのままの形でソースコード中に含まれて
 *     いること。
 * (2) 本ソフトウェアの一部または全てを無断で転載することを禁止するもの
 *     とする。雑誌などへ紹介・収録の場合は(株)北斗電子に連絡願います。
 * (3) 本ソフトウェアの利用により直接的または間接的に生じるいかなる損害
 *     からも、(株)北斗電子は一切の責任を負わないものとする。
 *
 * Copyright (C) Hokuto denshi Co,Ltd. 2021
 */


/*----------------------------------------------------------------------
  インクルード
----------------------------------------------------------------------*/

#include "hal_data.h"

#include "ada_if.h"
#include "board/board.h"

/*----------------------------------------------------------------------
    関数プロトタイプ
----------------------------------------------------------------------*/

void ada_if_init (void);
int ada_if_start (void);
void ada_if_write_read (void);
int ada_if_input_index(int n);
int ada_if_output_index(int n);
int ada_if_input_index_diff(int n, int m);
int ada_if_output_index_diff(int n, int m);
void i2s_callback (i2s_callback_args_t * p_args);

/*----------------------------------------------------------------------
    グローバル変数
----------------------------------------------------------------------*/

PCM_float *g_pcm0 = (PCM_float *) 0x20000000; //128kB
PCM_float *g_pcm1 = (PCM_float *) 0x20020000; //128kB

int32_t g_stream_tx_buf[2][FIFO_BUF_SIZE];
int32_t g_stream_rx_buf[2][FIFO_BUF_SIZE];

volatile bool g_i2s_streaming_ready = false;
volatile unsigned long g_buffer_index = 0;

volatile int g_input_index, g_output_index;

PCM_float g_zero_data;

volatile unsigned long g_sw_state = 0;
volatile bool g_sw_push = false;
int g_sw_cancel = 0;

volatile bool g_ad_flag = 0;
volatile unsigned short g_ad_val[3] = {2048, 2048, 2048};

volatile unsigned long g_led_pattern = 0;
volatile unsigned long g_led_pattern_index = 0;


/*----------------------------------------------------------------------
    関数定義
----------------------------------------------------------------------*/

void ada_if_init (void)
{
    //初期化関数

    //引数：なし

    //戻り値：なし

#if (USE_CH == 1)
#elif (USE_CH == 2)
#else
#error "USE_CH -> 1 or 2."
#endif

    int i, j;
    volatile int x;

    //リセット処理
    RESET_PORT = 0;
    RESET_PORT_PDR = 1;

    /* 変数初期化 */

    g_zero_data.ch0 = 0.0f;

#if (USE_CH == 2)
    g_zero_data.ch1 = 0.0f;
#endif

    //バッファ初期化
    for(i=0; i<INPUT_BUFFER_SIZE; i++)
    {
        g_pcm0[i] = g_zero_data;    //入力バッファ
    }

    for(i=0; i<OUTPUT_BUFFER_SIZE; i++)
    {
        g_pcm1[i] = g_zero_data;    //出力バッファ
    }

    //FIFOバッファ初期化
    for(i=0; i<FIFO_BUF_SIZE; i++)
    {
        for(j=0; j<2; j++)
        {
            g_stream_tx_buf[j][i] = 0UL;
            g_stream_rx_buf[j][i] = 0UL;
        }
    }

    //リセット解除
    RESET_PORT_PDR = 0;
    for(x=0; x<10000; x++) __NOP();

#if (USE_ADC_PORT == 1) || (USE_LED == 1) || (SW_STATE_NUM != 0)
    //ADCかLEDかSWを使う場合はAGT0をスタート
    (void) R_AGT_Open(&g_agt0_ctrl, &g_agt0_cfg);
    (void) R_AGT_Start(&g_agt0_ctrl);
#endif

#if (USE_LED == 1)
    //LEDを使う場合
    LED_PORT = 0;
    LED_PORT_PDR = 1;
#endif

#if (SW_STATE_NUM != 0)
    //SWを使う場合
    (void) R_KINT_Open(&g_kint0_ctrl, &g_kint0_cfg);
    (void) R_KINT_Enable(&g_kint0_ctrl);
#endif

}

int ada_if_start (void)
{
    //動作開始関数

    //引数：なし

    //戻り値：
    //  0 : 正常終了
    //  1 : エラー

    fsp_err_t err = FSP_SUCCESS;

    err = R_SSI_Open(&g_i2s0_ctrl, &g_i2s0_cfg);

    if(err != FSP_SUCCESS) return 1;

    g_i2s_streaming_ready = true;

    ada_if_write_read();

    return 0;

}

void ada_if_write_read (void)
{
    //データ送受信関数

    //引数：なし

    //戻り値：なし

    int n;
    float dataf;

     fsp_err_t err = R_SSI_WriteRead(&g_i2s0_ctrl,
                                 (uint8_t *) &g_stream_tx_buf[g_buffer_index][0],
                                 (uint8_t *) &g_stream_rx_buf[g_buffer_index][0],
                                 TRANSFER_SIZE * 2 * sizeof(int32_t));

     if (FSP_SUCCESS == err)
     {
         g_buffer_index = !g_buffer_index;
     }
     else
     {
         __NOP();    //エラー処理（必要な場合）
     }

     //次のデータを準備
     for(n=0; n<TRANSFER_SIZE; n++)
     {
         //Lch
         dataf = (g_pcm1[g_output_index].ch0 + 1.0f) / 2.0f * 16777216.0f;

#if (OUTPUT_BUFFER_AFTER_SEND == 0)
         g_pcm1[g_output_index].ch0 = 0.0f;
#endif

         if (dataf > 16777215.0f)
         {
             dataf = 16777215.0f;
         }
         else if (dataf < 0.0f)
         {
             dataf = 0.0f;
         }

         g_stream_tx_buf[g_buffer_index][2*n] = ((long)(dataf + 0.5f) - 8388608) & 0x00ffffff;   //送信したいのは24bitデータ

#if (USE_CH == 1)
         g_stream_tx_buf[g_buffer_index][2*n + 1] = 0.0f;       //1ch使用の場合はRchは0とする
#else
         //Rch
         dataf = (g_pcm1[g_output_index].ch1 + 1.0f) / 2.0f * 16777216.0f;

#if (OUTPUT_BUFFER_AFTER_SEND == 0)
         g_pcm1[g_output_index].ch1 = 0.0f;
#endif

         if (dataf > 16777215.0f)
         {
             dataf = 16777215.0f;
         }
         else if (dataf < 0.0f)
         {
             dataf = 0.0f;
         }

         g_stream_tx_buf[g_buffer_index][2*n + 1] = ((long)(dataf + 0.5f) - 8388608) & 0x00ffffff;   //送信したいのは24bitデータ
#endif

         g_output_index++;
         if(g_output_index >= OUTPUT_BUFFER_SIZE) g_output_index = 0;
     }

     //受信データを処理
     for(n=0; n<TRANSFER_SIZE; n++)
     {
         //Lch
         g_pcm0[g_input_index].ch0 = (float)(g_stream_rx_buf[g_buffer_index][2*n] << 8) / 2.147484e9f;       //2^31 vs 4byte float : 誤差163.9e-9 (1/2^24=59.6e-9, 1/2^23=119.2e-9, 1/2^22=238.4e-9)

         //Rch
#if (USE_CH == 2)
         g_pcm0[g_input_index].ch1 = (float)(g_stream_rx_buf[g_buffer_index][2*n + 1] << 8) / 2.147484e9f;       //2^31 vs 4byte
#endif

         g_input_index++;
         if(g_input_index >= INPUT_BUFFER_SIZE) g_input_index = 0;
     }
}

int ada_if_input_index(int n)
{
    //g_inbuf_indexを有効範囲内にセットする関数

    //引数：
    //  int n インデックス（負の数やINPUT_BUFFER_SIZEを超える値を受け入れる）

    //戻り値：
    //  0~INPUT_BUFFER_SIZE-1に納まる数を返す

    if(n < 0)
    {
        n += INPUT_BUFFER_SIZE;

        if(n < 0)
        {
            n = n % INPUT_BUFFER_SIZE;
            return n;
        }
        else return n;
    }
    else if(n >= INPUT_BUFFER_SIZE)
    {
        n -= INPUT_BUFFER_SIZE;

        if(n >= INPUT_BUFFER_SIZE)
        {
            n = n % INPUT_BUFFER_SIZE;
            return n;
        }
        else return n;
    }
    else return n;
}

int ada_if_output_index(int n)
{
    //g_inbuf_indexを有効範囲内にセットする関数

    //引数：
    //  int n インデックス（負の数やOUTPUT_BUFFER_SIZEを超える値を受け入れる）

    //戻り値：
    //  0~OUTPUT_BUFFER_SIZE-1に納まる数を返す

    if(n < 0)
    {
        n += OUTPUT_BUFFER_SIZE;

        if(n < 0)
        {
            n = n % OUTPUT_BUFFER_SIZE;
            return n;
        }
        else return n;
    }
    else if(n >= OUTPUT_BUFFER_SIZE)
    {
        n -= OUTPUT_BUFFER_SIZE;

        if(n >= OUTPUT_BUFFER_SIZE)
        {
            n = n % OUTPUT_BUFFER_SIZE;
            return n;
        }
        else return n;
    }
    else return n;
}

int ada_if_input_index_diff(int n, int m)
{

    //g_inbuf_indexの差分を計算する関数

    //引数：
    //  int n 基準となるインデックス値
    //  int m 差分を求めるインデックス値

    //戻り値：
    //  nに対しmが先行しているときは正の値を返します
    //  mが遅れている場合は負の値を返します
    //　INPUT_BUFFER_SIZE/2より絶対値で小さな値（リングバッファで考え2つの経路の近い方）を求めます

    int tmp;

    if((n < 0) || (n >= INPUT_BUFFER_SIZE)) n = ada_if_input_index(n);
    if((m < 0) || (m >= INPUT_BUFFER_SIZE)) m = ada_if_input_index(m);

    tmp = m - n;

    if(abs(tmp) < (INPUT_BUFFER_SIZE/2)) return tmp;
    else if(tmp < 0) return tmp + INPUT_BUFFER_SIZE;
    else return tmp - INPUT_BUFFER_SIZE;
}

int ada_if_output_index_diff(int n, int m)
{

    //g_outpuf_indexの差分を計算する関数

    //引数：
    //  int n 基準となるインデックス値
    //  int m 差分を求めるインデックス値

    //戻り値：
    //  nに対しmが先行しているときは正の値を返します
    //  mが遅れている場合は負の値を返します
    //　INPUT_BUFFER_SIZE/2より絶対値で小さな値（リングバッファで考え2つの経路の近い方）を求めます

    int tmp;

    if((n < 0) || (n >= OUTPUT_BUFFER_SIZE)) n = ada_if_output_index(n);
    if((m < 0) || (m >= OUTPUT_BUFFER_SIZE)) m = ada_if_output_index(m);

    tmp = m - n;

    if(abs(tmp) < (OUTPUT_BUFFER_SIZE/2)) return tmp;
    else if(tmp < 0) return tmp + OUTPUT_BUFFER_SIZE;
    else return tmp - OUTPUT_BUFFER_SIZE;
}

//割込みコールバック関数
void i2s_callback (i2s_callback_args_t * p_args)
{
    if ((I2S_EVENT_TX_EMPTY == p_args->event) || (I2S_EVENT_IDLE == p_args->event))
    {
        if (g_i2s_streaming_ready == true)
        {
            ada_if_write_read();
        }
        else
        {
            __NOP();
        }
    }

    if(I2S_EVENT_TX_EMPTY == p_args->event)
    {
        __NOP();
    }

    if(I2S_EVENT_IDLE == p_args->event)
    {
        __NOP();
    }

    if(I2S_EVENT_RX_FULL == p_args->event)
    {
        __NOP();
    }
}
