

/**
 ******************************************************************************
 *
 * @file        MG32_DACOutput_API.c
 * @brief       Audio output by DAC control.
 *
 * @par         Project
 *              MG32
 * @version     V1.03
 * @date        2023/03/29
 * @author      Megawin Software Center
 * @copyright   Copyright (c) 2017 MegaWin Technology Co., Ltd.
 *              All rights reserved.
 *
 ******************************************************************************* 
 * @par Disclaimer
 * The Demo software is provided "AS IS" without any warranty, either
 * expressed or implied, including, but not limited to, the implied warranties
 * of merchantability and fitness for a particular purpose. The author will
 * not be liable for any special, incidental, consequential or indirect
 * damages due to loss of data or any other reason.
 * These statements agree with the world wide and local dictated laws about
 * authorship and violence against these laws.
 *******************************************************************************
 *******************************************************************************
 */

/* audio file is Untitled_222.c' */


/* Includes ------------------------------------------------------------------*/
#include "MG32_DACOutput_API.h"



/* Private typedef -----------------------------------------------------------*/
typedef enum {
    HalfUpdateComplete,
    HalfUpdating,
} HalfUpdateEvent;

/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
static DAC_HandleTypeDef   mDAC;
static DMA_HandleTypeDef   mDMA;
static TM_HandleTypeDef    mTM10;
static uint32_t            AudioBufIDX;                // AudioBufIDX : Audio Buffer source index.
static __ALIGNED(4) uint8_t  AudioBuf1[500];           // SRAM audio buffer.
static __ALIGNED(4) uint8_t  AudioBuf2[500];           // SRAM audio buffer.
static volatile uint32_t   *DestAddr;

/* Private function prototypes -----------------------------------------------*/
void API_DACOutput_Init(void);
void API_DAC_PrepareData(void);
void DMA_IRQHandler(void);

/* Private functions ---------------------------------------------------------*/
/* External vairables --------------------------------------------------------*/
extern uint8_t      rawData[];


/**
 *******************************************************************************
 * @brief       Initial DAC Audio output (DAC + DMA + Timer). 
 * @return		None
 *******************************************************************************
 */
void API_DACOutput_Init(void)
{
    TM_ClockConfigTypeDef   CKConfig;
    TM_MasterConfigTypeDef  TM_TRGOConfig;
    
    // ------------------------------------------------------------------------
    // Config PB2 for DAC output. (AIO mode)
    // ------------------------------------------------------------------------
    // config in MG32_GPIO_Init.h
    
    // ------------------------------------------------------------------------
    // 1.Initial TM10 timer : counter range is 8K in 48MHz.
    // ------------------------------------------------------------------------
    mTM10.Instance                      = TM10;
    mTM10.Init.TM_CounterMode           = TM_CASCADE_UP;
    mTM10.Init.TM_Period                = 6000 - 1;             
    mTM10.Init.TM_Prescaler             = 0;
    MID_TM_Base_Init(&mTM10);
    
    // ------------------------------------------------------------------------
    // 2.Initial TM10 clock mode.
    // ------------------------------------------------------------------------
    CKConfig.TM_ClockSource             = TM_INTERNAL_CLOCK;
    CKConfig.TM_ExternalClockSource     = 0;
    CKConfig.TM_INTClockDivision        = TM_INTERNALCLOCK_DIVDER_DIV1;
    CKConfig.TM_InternalClockSource     = TM_INTERNALCLOCK_PROC;
    MID_TM_ConfigClockSource(&mTM10, &CKConfig);  

    // ------------------------------------------------------------------------
    // 3.Initial TM10 TRGO configuration.
    // ------------------------------------------------------------------------
    TM_TRGOConfig.MasterOutputTrigger   = TM_TRGO_UPDATE;
    TM_TRGOConfig.MasterOutputPolarity  = TM_MASTEROUTPUT_BYPASS;
    TM_TRGOConfig.MasterUpdateEvent     = TM_UPDATE_OVERFLOW;
    MID_TM_MasterConfigSynchronization(&mTM10, &TM_TRGOConfig);

    // ------------------------------------------------------------------------
    // 4.Start TM10.
    // ------------------------------------------------------------------------
    MID_TM_Base_Start(&mTM10);      
    
    // ------------------------------------------------------------------------
    // 5.Initial DAC. 
    // ------------------------------------------------------------------------
    mDAC.Instance                       = DAC;
    mDAC.Init.TriggerConverionSrc       = DAC_TRIGGERSOURCE_TM10_TRGO;  // DAC update source from TM10 TRGO
    mDAC.Init.TriggerConverionEdge      = DAC_RISING_EDGE_UPDATE;       // DAC accept rising edge of TM10_TRGO to update DAC
    mDAC.Init.DataAlign                 = DAC_ALIGN_10B_L;              // DAC data is 10bit /w left justif
    mDAC.Init.OutputLevel               = DAC_OUTPUTCURRENT_MODE2;      // DAC output ~0.5mA 
    mDAC.Init.InterruptMode             = DAC_IT_READY;                 // DAC will trigger INT when READY flag happened.
    
    MID_DAC_Init(&mDAC);
    
    // ------------------------------------------------------------------------
    // 6.initial DMA. (M2P)
    // ------------------------------------------------------------------------
    mDMA.Instance                       = DMA;
	mDMA.DMAChannelIndex                = DMAChannel1;                  // only DMA channel0 can support M2M mode  
	mDMA.Init.SrcPeri	                = MID_DMA_MEMORY_READ;          // Source symbol is memory
	mDMA.Init.DesPeri	                = MID_DMA_DAC_WRITE;            // Destination symbol is DAC
	mDMA.Init.BSize 	                = DMA_BSIZE_2BYTE;              // Burst size is 1 byte
	mDMA.Init.MEMMode	                = MID_DMA_MEMORY_NORMAL;        // Normal memory access mode    
    mDMA.Init.LoopMode	                = DMA_LOOP_ENABLE;              // ENABLE Loop mode
    mDMA.Parent                         = &mDAC;
    
	MID_DMA_Init(&mDMA);
    // ------------------------------------------------------------------------
    // 7.Update Audio buffer.
    // ------------------------------------------------------------------------
    AudioBufIDX                         = 0;                            // Audio Buffer source index
    mDAC.DMA_Handle                     = &mDMA;
    
    API_DAC_PrepareData();                                                  // prepare audio data and trigger DMA
    
    // ------------------------------------------------------------------------
    // 8. Output DAC by DMA module.
    // ------------------------------------------------------------------------
    if ((AudioBufIDX & 0x0001) == 0)
        MID_DAC_Start_DMA(&mDAC, (uint32_t *) &AudioBuf2, 500);
    else 
        MID_DAC_Start_DMA(&mDAC, (uint32_t *) &AudioBuf1, 500);
    
    // Enable DMA IRQ
    NVIC_EnableIRQ(DMA_IRQn);
    
}



/**
 *******************************************************************************
 * @brief       Copy Audio data from Untitled_222.c to SRAM
 * @return		None
 *******************************************************************************
 */
void API_DAC_PrepareData(void)
{
    uint32_t i;
    volatile uint32_t *PosIDX;                              // The index of SRAM audio
    
    // ------------------------------------------------------------------------
    // copy rawData data of 'Untitled_222.c' to SRAM audio buffer
    // MG32F02A132 must be copy (manual)
    PosIDX = (uint32_t *) &rawData;
    i = AudioBufIDX * 125;                                  // 125 * 4 = 500 byte
    PosIDX += i;
    
    // ------------------------------------------------------------------------
    // AudioBuf1 or AudioBuf2
    if ((AudioBufIDX & 0x0001) == 0)
        DestAddr = (uint32_t *) &AudioBuf1;
    else 
        DestAddr = (uint32_t *) &AudioBuf2;
    
    // ------------------------------------------------------------------------
    // copy from flash to SRAM. Size is 125*4 = 500 bytes.
    for(i=0;i<125;i++, PosIDX++)
    {
        DestAddr[i] = ((uint32_t ) *PosIDX)+0x80008000;     // Audio data is 2byte with 2's complement
                                                            // DAC needs 0~0x3FF, So Audio needs transfer.
    }
    
    // ------------------------------------------------------------------------
    // is the last cycle ?
    AudioBufIDX++;
    if(AudioBufIDX == 128)
    {
        __DRV_DMA_DISABLE_IT(mDMA.DMAChannelIndex, DMA_IT_HT);      // Disable DMA Half interrupt
        
        __DRV_DMA_CLEAR_FLAG(mDMA.DMAChannelIndex, (DMA_CH_FLAG_COMPLETE | DMA_CH_FLAG_HALF));   // clear flag
        __DRV_DMA_SET_LASTCYCLE(mDMA.DMAChannelIndex);              // Set the Last transmission cycle (Stop DMA loop mode)
        AudioBufIDX = 0;
        
    }
    
        
}



/**
 *******************************************************************************
 * @brief       DMA IRQ handler routine.
 * @return		None
 *******************************************************************************
 */
void DMA_IRQHandler(void)
{
    // ------------------------------------------------------------------------
    // Half event
    if (__DRV_DMA_GET_FLAG(mDMA.DMAChannelIndex, DMA_CH_FLAG_HALF) == DRV_Happened)
    {
        // update Audio data
        API_DAC_PrepareData();
        
        // Updata DMA source address
        if ((AudioBufIDX & 0x0001) == 0)
        {
            // Only update DMAChannelIndex Source Address (DMA in loop mode)
            __DRV_DMA_UPDATE_SSA(mDMA.DMAChannelIndex, (uint32_t) &AudioBuf2);

        }
        else 
        {
            // Only update DMAChannelIndex Source Address (DMA in loop mode)
            __DRV_DMA_UPDATE_SSA(mDMA.DMAChannelIndex, (uint32_t) &AudioBuf1);
        }
        
        // clear half flag
        __DRV_DMA_CLEAR_FLAG(mDMA.DMAChannelIndex, DMA_CH_FLAG_HALF);
        
        // Disable Half interrupt
        __DRV_DMA_DISABLE_IT(mDMA.DMAChannelIndex, DMA_IT_HT);
        
        return;
    }
    
    // ------------------------------------------------------------------------
    // Complete event
    if (__DRV_DMA_GET_FLAG(mDMA.DMAChannelIndex, DMA_CH_FLAG_COMPLETE) == DRV_Happened)
    {
        // clear complete flag
        __DRV_DMA_CLEAR_FLAG(mDMA.DMAChannelIndex, DMA_CH_FLAG_COMPLETE);
    
        // Enable Half interrupt
        __DRV_DMA_ENABLE_IT(mDMA.DMAChannelIndex, DMA_IT_HT);
        
        return;
    }
        
}

