/*
 * File      : adc.c
 * This file is part of RT-Thread RTOS
 * COPYRIGHT (C) 2006 - 2017, RT-Thread Development Team
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License along
 *  with this program; if not, write to the Free Software Foundation, Inc.,
 *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Change Logs:
 * Date           Author       Notes
 * 2017-12-04     Haley        the first version
 */

#include <rtdevice.h>
#include "am_mcu_apollo.h"
#include "board.h"

#ifdef RT_USING_ADC

/* sem define */
rt_sem_t adcsem = RT_NULL;

#define BATTERY_GPIO            29                        /* Battery */
#define BATTERY_ADC_PIN         AM_HAL_PIN_29_ADCSE1 
#define BATTERY_ADC_CHANNEL     AM_HAL_ADC_SLOT_CHSEL_SE1 /* BATTERY ADC�ɼ�ͨ�� */
#define BATTERY_ADC_CHANNELNUM  1                         /* BATTERY ADC�ɼ�ͨ���� */

#define ADC_CTIMER_NUM          3                         /* ADCʹ�ö�ʱ�� */

#define ADC_CHANNEL_NUM         1                         /* ADC�ɼ�ͨ������ */
#define ADC_SAMPLE_NUM          8                         /* ADC��������, NE_OF_OUTPUT */

rt_uint8_t bat_adc_cnt = (ADC_CHANNEL_NUM + 1)*ADC_SAMPLE_NUM;
rt_int16_t am_adc_buffer_pool[64];

rt_uint8_t am_adc_data_get(rt_int16_t *buff, rt_uint16_t size)
{
    /* wait adc interrupt release sem forever */	
    rt_sem_take(adcsem, RT_WAITING_FOREVER);

    /* copy the data */
    rt_memcpy(buff, am_adc_buffer_pool, size*sizeof(rt_int16_t));

    return 0;
}

void am_adc_start(void)
{
    /* adcsem create */
    adcsem = rt_sem_create("adcsem", 0, RT_IPC_FLAG_FIFO);

    /* Start the ctimer */
    am_hal_ctimer_start(ADC_CTIMER_NUM, AM_HAL_CTIMER_TIMERA);

    /* Trigger the ADC once */
    am_hal_adc_trigger();
}

void am_adc_stop(void)
{
    /* Stop the ctimer */
    am_hal_ctimer_stop(ADC_CTIMER_NUM, AM_HAL_CTIMER_TIMERA);

    /* adcsem delete */
    rt_sem_delete(adcsem);
}

/**
 * @brief Interrupt handler for the ADC
 *
 * This function is Interrupt handler for the ADC
 *
 * @return None.
 */
void am_adc_isr(void)
{
    uint32_t ui32Status, ui32FifoData;

    /* Read the interrupt status */
    ui32Status = am_hal_adc_int_status_get(true);

    /* Clear the ADC interrupt */
    am_hal_adc_int_clear(ui32Status);

    /* If we got a FIFO 75% full (which should be our only ADC interrupt), go ahead and read the data */
    if (ui32Status & AM_HAL_ADC_INT_FIFOOVR1)
    {
        do
        {
            /* Read the value from the FIFO into the circular buffer */
            ui32FifoData = am_hal_adc_fifo_pop();

            if(AM_HAL_ADC_FIFO_SLOT(ui32FifoData) == BATTERY_ADC_CHANNELNUM)
                am_adc_buffer_pool[bat_adc_cnt++] = AM_HAL_ADC_FIFO_SAMPLE(ui32FifoData);

            if(bat_adc_cnt > (ADC_CHANNEL_NUM + 1)*ADC_SAMPLE_NUM - 1)
            {
                /* shift data */
                rt_memmove(am_adc_buffer_pool, am_adc_buffer_pool + ADC_CHANNEL_NUM*ADC_SAMPLE_NUM, ADC_CHANNEL_NUM*ADC_SAMPLE_NUM*sizeof(rt_int16_t));
                bat_adc_cnt = (ADC_CHANNEL_NUM + 1)*ADC_SAMPLE_NUM;

                /* release adcsem */
                rt_sem_release(adcsem);
            }
        } while (AM_HAL_ADC_FIFO_COUNT(ui32FifoData) > 0);
    }
}

static void timerA3_for_adc_init(void)
{
    /* Start a timer to trigger the ADC periodically (1 second) */
    am_hal_ctimer_config_single(ADC_CTIMER_NUM, AM_HAL_CTIMER_TIMERA,
                                   AM_HAL_CTIMER_XT_2_048KHZ |
                                   AM_HAL_CTIMER_FN_REPEAT |
                                   AM_HAL_CTIMER_INT_ENABLE |
                                   AM_HAL_CTIMER_PIN_ENABLE);

    am_hal_ctimer_int_enable(AM_HAL_CTIMER_INT_TIMERA3);

    /* Set 512 sample rate */
    am_hal_ctimer_period_set(ADC_CTIMER_NUM, AM_HAL_CTIMER_TIMERA, 3, 1);

    /* Enable the timer A3 to trigger the ADC directly */
    am_hal_ctimer_adc_trigger_enable();

    /* Start the timer */
    //am_hal_ctimer_start(ADC_CTIMER_NUM, AM_HAL_CTIMER_TIMERA);
}

/**
 * @brief Initialize the ADC
 *
 * This function initialize the ADC
 *
 * @return None.
 */
int rt_hw_adc_init(void)
{
    am_hal_adc_config_t sADCConfig;

    /* timer for adc init*/
    timerA3_for_adc_init();

    /* Set a pin to act as our ADC input */
    am_hal_gpio_pin_config(BATTERY_GPIO, BATTERY_ADC_PIN);

    /* Enable interrupts */
    am_hal_interrupt_enable(AM_HAL_INTERRUPT_ADC);

    /* Enable the ADC power domain */
    am_hal_pwrctrl_periph_enable(AM_HAL_PWRCTRL_ADC);

    /* Set up the ADC configuration parameters. These settings are reasonable
       for accurate measurements at a low sample rate */
    sADCConfig.ui32Clock = AM_HAL_ADC_CLOCK_HFRC;
    sADCConfig.ui32TriggerConfig = AM_HAL_ADC_TRIGGER_SOFT;
    sADCConfig.ui32Reference = AM_HAL_ADC_REF_INT_2P0;
    sADCConfig.ui32ClockMode = AM_HAL_ADC_CK_LOW_POWER;
    sADCConfig.ui32PowerMode = AM_HAL_ADC_LPMODE_0;
    sADCConfig.ui32Repeat = AM_HAL_ADC_REPEAT;
    am_hal_adc_config(&sADCConfig);

    /* For this example, the samples will be coming in slowly. This means we
       can afford to wake up for every conversion */
    am_hal_adc_int_enable(AM_HAL_ADC_INT_FIFOOVR1);

    /* Set up an ADC slot */
    am_hal_adc_slot_config(BATTERY_ADC_CHANNELNUM, AM_HAL_ADC_SLOT_AVG_1 |
                              AM_HAL_ADC_SLOT_14BIT |
                              BATTERY_ADC_CHANNEL |
                              AM_HAL_ADC_SLOT_ENABLE);

    /* Enable the ADC */
    am_hal_adc_enable();

    rt_kprintf("adc_init!\n");

    return 0;
}
#ifdef RT_USING_COMPONENTS_INIT
INIT_BOARD_EXPORT(rt_hw_adc_init);
#endif

#endif
/*@}*/