/**
 ******************************************************************************
 * @file    battery.c
 * @author  Lucien Senaneuch
 * @version V1.0
 * @date    16-mai-2016
 * @brief   Supervision de la tension batterie et detection de charge.
 *			Calcule le voltage de la batterie à interval régulier.
 *			Converti le voltage batterie en signaux de commande - 2 -1 - 0.
 *			Configure une interruption externe pour détecter le branchement
 *			du chargeur.
 ******************************************************************************
 ******************************************************************************
 */
#include <stm32f10x.h>
#include "battery.h"
#include "system_dumby.h"

DMA_InitTypeDef DMA_BAT_InitStructure;
uint16_t ADCConvertedValue[VOLTAGE_BUFFER_SIZE];

char cptMesureHigh=0;
char cptMesureLow=0;
char cptMesureDisable=0;

uint16_t vbatLowerVal;
uint16_t vbatHighVal;
uint16_t vbatDiff;

uint16_t testPostion=0;
uint32_t mesureVoltage;
uint32_t meanVoltage;

uint32_t cptMesureEmergencyHalt=0;

/** @addtogroup Projects
 * @{
 */

/** @addtogroup battery
 * @{
 */


/** @addtogroup Init_GPIO_DMA_IT_Battery
 * @{
 */

/**
 * @brief 		Défini les GPIO nécessaires pour la batterie.
 *
 * 				La fonction MAP_MotorPin va venir configurer le E/S du GPIO pour correspondre avec
 * 				le schéma electrique en ressource. La fonction initialise aussi l'interruption EXTI
 * 				de la détection du chargeur.
 *
 * @note		A3 en output alternate function.
 *				A0 et A4 en floating input.
 *				PB11 en floating input (EXTI)
 *
 * @param  		None
 * @retval 		None
 *
 */

void batteryConfigure(void)
{
    GPIO_InitTypeDef Init_Structure;
    NVIC_InitTypeDef NVIC_InitStructure;
    EXTI_InitTypeDef EXTI_InitStructure;
    ADC_InitTypeDef ADC_InitStructure;

    Init_Structure.GPIO_Pin = GPIO_Pin_3;
    Init_Structure.GPIO_Speed = GPIO_Speed_10MHz;
    Init_Structure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_Init(GPIOA, &Init_Structure);

    // Configure les PIN A0,A4 en input floating.
    Init_Structure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_4;
    Init_Structure.GPIO_Mode = GPIO_Mode_AIN;
    GPIO_Init(GPIOA, &Init_Structure);

    // Configure PB11 en input pullup
    Init_Structure.GPIO_Pin = GPIO_Pin_11;
    Init_Structure.GPIO_Mode = GPIO_Mode_IPU;
    GPIO_Init(GPIOB, &Init_Structure);

    GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource11);

    EXTI_InitStructure.EXTI_Line = EXTI_Line11;
    EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
    EXTI_InitStructure.EXTI_LineCmd = ENABLE;
    EXTI_Init(&EXTI_InitStructure);

    NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

    // Initialise la dma pour stocker les valeur dans ADCConvertedValue.

    DMA_DeInit(DMA1_Channel1);
    DMA_BAT_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&(ADC1->DR); //   ADC1_DR_Address;
    DMA_BAT_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&ADCConvertedValue;
    DMA_BAT_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
    DMA_BAT_InitStructure.DMA_BufferSize = VOLTAGE_BUFFER_SIZE; // voir schèmas ci dessus
    DMA_BAT_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    DMA_BAT_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
    DMA_BAT_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
    DMA_BAT_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
    DMA_BAT_InitStructure.DMA_Mode = DMA_Mode_Normal;
    DMA_BAT_InitStructure.DMA_Priority = DMA_Priority_High;
    DMA_BAT_InitStructure.DMA_M2M = DMA_M2M_Disable;
    DMA_Init(DMA1_Channel1, &DMA_BAT_InitStructure);

    DMA_Cmd(DMA1_Channel1, ENABLE);
    DMA_ITConfig(DMA1_Channel1, DMA_IT_TC, ENABLE);

    // Configuration et Calibration de l'ADC1 sur 1 channel.
    ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
    ADC_InitStructure.ADC_ScanConvMode = ENABLE;
    ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
    ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
    ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
    ADC_InitStructure.ADC_NbrOfChannel = 1;
    ADC_Init(ADC1, &ADC_InitStructure);

    /* ADC1 regular channel1 configuration */
    ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_71Cycles5);

    /* Start ADC1 Software Conversion */
    ADC_Cmd(ADC1, ENABLE);

    ADC_StartCalibration(ADC1);
    /* Check the end of ADC1 calibration */
    while(ADC_GetCalibrationStatus(ADC1));

    ADC_SoftwareStartConvCmd(ADC1, ENABLE);

    /* Enable the ADC1 DMA Interrupt */
    NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel1_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
}

/**
 * @}
 */

/** @addtogroup Lancer_acquisition
 * @{
 */
/**
 * @brief 		Demarrage des Acquisitions de la DMA.
 *
 * @param  		None
 * @retval 		None
 *
 */

void batteryStartAcquisition(void)
{
    ADC_DMACmd(ADC1, ENABLE);
    DMA_DeInit(DMA1_Channel1);
    DMA_Init(DMA1_Channel1, &DMA_BAT_InitStructure);
    DMA_Cmd(DMA1_Channel1, ENABLE);
    DMA_ITConfig(DMA1_Channel1, DMA_IT_TC, ENABLE);
}

/**
 * @brief 		Fonction de plus haut niveau qui initialisera la DMA  et qui lancera une nouvelle acquisition.
 * 				Cette fonction est appelée à intervalle régulier dans le systick. Cette fonction utilise startACQDMA.
 *
 * @param  		None
 * @retval 		None
 *
 */

void batteryRefreshData(void)
{
    DMA_BAT_InitStructure.DMA_BufferSize = VOLTAGE_BUFFER_SIZE;

    ADC_RegularChannelConfig(ADC1, ADC_Channel_4, 1, ADC_SampleTime_55Cycles5);
    ADC_Cmd(ADC1, ENABLE);
    batteryStartAcquisition();
}

/**
 * @brief       Appelé de manière régulière pour mettre à jour le niveau batterie
 *
 * @param       None
 * @retval      None
 */
void batteryManagement(void) {
    int k;

    if(Dumber.acquisition==VOLTAGE && Dumber.BatterieChecking==TRUE) {
        vbatLowerVal = 0xFFF;
        vbatHighVal = 0;

        for(k=0; k<VOLTAGE_BUFFER_SIZE; k++)
        {
            meanVoltage+=ADCConvertedValue[k];

            if (vbatLowerVal> ADCConvertedValue[k]) vbatLowerVal = ADCConvertedValue[k];
            if (vbatHighVal< ADCConvertedValue[k]) vbatHighVal = ADCConvertedValue[k];
        }

        vbatDiff = vbatHighVal - vbatLowerVal;

        meanVoltage= meanVoltage/VOLTAGE_BUFFER_SIZE;
        mesureVoltage = meanVoltage;

        Dumber.BatteryPercentage = mesureVoltage;
        Dumber.acquisition=FALSE;

        if(Dumber.BatteryPercentage >= VBAT_SEUIL_LOW)
        {
            cptMesureHigh++;
            if(cptMesureHigh >= COMPTEUR_SEUIL_HIGH)
            {
                if(Dumber.StateSystem == STATE_LOW)
                    systemChangeState(STATE_RUN);

                Dumber.stateBattery = 2;
                cptMesureHigh=0;
                cptMesureLow=0;
                cptMesureDisable=0;
                cptMesureEmergencyHalt=0;
            }
        }
        else if (Dumber.BatteryPercentage < VBAT_SEUIL_LOW && Dumber.BatteryPercentage >= VBAT_SEUIL_DISABLE)
        {
            cptMesureLow++;
            if(cptMesureLow >= COMPTEUR_SEUIL_LOW)
            {
                if(Dumber.StateSystem == STATE_RUN)
                    systemChangeState(STATE_LOW);

                Dumber.stateBattery =1;
                cptMesureHigh=0;
                cptMesureLow=0;
                cptMesureDisable=0;
            }
        }
        else // Dumber.BatteryPercentage < VBAT_SEUIL_DISABLE
        {
            cptMesureDisable++;

            if(cptMesureDisable >= COMPTEUR_SEUIL_DISABLE)
            {
                systemChangeState(STATE_DISABLE);
            }
        }
    }
}

/**
 * @}
 */



/** @addtogroup Handler
 * @{
 */


/**
 * @brief 		Interruption Handler. Qui va faire la moyenne des dernières
 * 				acquisitions lorsque la DMA à rempli son buffer.
 *
 * @param  		None
 * @retval 		None
 *
 */
void DMA1_Channel1_IRQHandler(void)
{
    //Test on DMA1 Channel1 Transfer Complete interrupt
    if (DMA_GetITStatus(DMA1_IT_TC1))
    {
        Dumber.BatterieChecking=TRUE;
        //Clear DMA1 Channel1 Half Transfer, Transfer Complete and Global interrupt pending bits
        DMA_ClearITPendingBit(DMA1_IT_GL1);
    }
}

/**
 * @brief 		Interruption qui donne l'ordre d'éteindre le robot.
 * 				L'IT se déclenche lorsque le chargeur est branché.
 *
 * @param  		None
 * @retval 		None
 *
 */
void EXTI15_10_IRQHandler(void)
{
    systemShutDown();
    while (1);
}

/**
 * @}
 */

/**
 * @}
 */
