/**
 * @file ad7999.c
 * @author: suikan
 * @brief AD799x I2C ADCからデータを読み込み、API経由でアプリケーションへ渡す
 */


#include <t_services.h>
#include <s_services.h>
#include <i2c_subsystem.h>
#include "kernel_id.h"
#include "uzume.h"
#include "ad7999.h"

/**
 * @brief AD7999のチャンネル数 UZUME_AD7999_I2C_ADRS_2が宣言されている場合にはチャンネル数を2にする。
 */
#ifdef UZUME_AD7999_I2C_ADRS_2
#define NUM_CH 8
#else
#define NUM_CH 4
#endif

/**
 * @brief ADC変換値保存用配列
 * @details
 * 変換値を保存する。アクセスは排他的でなければならない。値は符号付き左詰めである。
 * 整数部はないので、1ビットの符号の右に小数部が連なる。
 */
static fract32 values[NUM_CH];

/*
 * ADCのI2Cポート。
 * AD7999が接続されているI2Cペリフェラルのポート番号。デフォルトは0である。他のポートを使いたい時には
 * sys_config.hで宣言すると良い。なお、I2Cコントローラ・ペリフェラルがひとつしかない場合には0を指定
 * する。
 */
#ifndef UZUME_AD7999_I2C_PORT
#define UZUME_AD7999_I2C_PORT 0x00
#endif

/*
 * ADCのI2Cアドレス。
 * AD7999の7bit I2Cアドレスはデフォルトで0x29と決め打ちしている。他のアドレスを使いたい時には
 * sys_config.hで宣言すると良い
 */
#ifndef UZUME_AD7999_I2C_ADRS
#define UZUME_AD7999_I2C_ADRS 0x29
#endif

/**
 * @brief ADU999 制御タスク
 * @param exinf タスク引数。ADC799xが持つ7bitのI2Cアドレスを指定する。
 */
void ad7999_task(VP_INT exinf)
{
    unsigned int ch;
    unsigned char adcbuf[2];

    tslp_tsk(10);

    while(1)
    {
        i2ccall(i2c_master_read(UZUME_AD7999_I2C_PORT, UZUME_AD7999_I2C_ADRS, adcbuf, 2));

            /* 上位バイトのbit 5:4 をチャンネル番号として受け取る */
        ch = ( adcbuf[0] & 0x30 ) >> 4;
            /*上位バイトのbit 3;0と下位バイトから12bitのADCデータを作る。そのデータを32bitに左詰めする */

        /* 排他区間開始 */
        syscall(wai_sem(AD7999_SEM));
        values[ch] = ( (( adcbuf[0] & 0x0F )<<8 ) | adcbuf[1] ) << 19;
        syscall(sig_sem(AD7999_SEM));
        /* 排他区間終了 */

#ifdef UZUME_AD7999_I2C_ADRS_2
        i2ccall(i2c_master_read(UZUME_AD7999_I2C_PORT, UZUME_AD7999_I2C_ADRS_2, adcbuf, 2));

            /* 上位バイトのbit 5:4 をチャンネル番号として受け取る。2番目のADCなのでオフセットは4 */
        ch = (( adcbuf[0] & 0x30 ) >> 4) + 4;
            /*上位バイトのbit 3;0と下位バイトから12bitのADCデータを作る。そのデータを32bitに左詰めする */

        /* 排他区間開始 */
        syscall(wai_sem(AD7999_SEM));
        values[ch] = ( (( adcbuf[0] & 0x0F )<<8 ) | adcbuf[1] ) << 19;
        syscall(sig_sem(AD7999_SEM));
        /* 排他区間終了 */

#endif

        tslp_tsk(10);
    }
}

/**
 * @brief ボリューム値の取得
 * @param ch 0から始まるチャンネル指定番号。AD7999は3まで
 * @return 32bit符号付き固定小数点数のボリューム値
 */

fract32 ad7999_get_volume(unsigned int ch)
{
    fract32 retval;

        // パラメータチェック
    if (ch >= NUM_CH)
        ch = NUM_CH - 1;

    /* 排他区間開始 */
    syscall(wai_sem(AD7999_SEM));
    retval = values[ch];
    syscall(sig_sem(AD7999_SEM));
    /* 排他区間終了 */

    return retval;
}
