
/**
 ******************************************************************************
 *
 * @file        BSP_DAC_TH217A.c
 * @brief       Audio output by DAC control.
 *
 * @par         Project
 *              MG32
 * @version     V1.00
 * @date        2022/02/14
 * @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.
 *******************************************************************************
 *******************************************************************************
 */

/*==============================================================================
                                 User NOTES
How To use this function:
-----------------------
   + BSP_DACOutput_Init() - Initial DAC+DMA+TM10 to DAC audio output.
   
   Then it will auto updata DAC output with DMA+TM10 peripheral.
   DMA use M2P mode. Move rawData to DAC peripheral.
   TM10 generate 8KHz period to update DMA peripheral.
   
Driver architecture:
--------------------
   + MG32_DAC_DRV
   + MG32_DMA_DRV
   + MG32_TM_DRV

Known Limitations:
------------------
   1- Run in 48MHz. (CSC control).
   2- GPIO PB2 must be AIO mode.
   3- Enable MG32__IRQHandler.c in ChipInit_Wizard.
   4- Check 'Enable DMA NCIV' option in IRQ_Handler.
==============================================================================*/

/*==============================================================================
                                 Require parameter
Require module : DAC / CSC / GPIO / DMA / TM10

DAC Module : 
    Clock source                : CK_APB 
    Clock frequency             : 48MHz
                                
CSC Module :
    Clock source                : CK_IHRCO (12M Hz)
    Clock frequency             : CK_MAIN select CK_PLLO (PLL to 48MHz)
    CK_APB frequency            : 48MHz
    CK_AHB frequency            : 48MHz
    Clock enable in 'On Mode'   : DAC / GPIOB / DMA / TM10

GPIO pin configuration : 
    Pin / IO mode / AFS
    ---  --------  -----
    PB2 / AIO     / GPB2 (Default)
           
DMA Module :
    DMA Channel                 : DMAChannel0
    Function mode               : M2P (Memory to DAC peripheral)
    Interrupt                   : Enable
                                
TM10 Module :                   
    TM10 counter period         : 8KHz (in APB=48MHz)
    TRGO event                  : Update event( TM10 overflow event)
    
==============================================================================*/



/* Includes ------------------------------------------------------------------*/
#include "MG32_DAC_DRV.h"
#include "MG32_DMA_DRV.h"
#include "MG32_TM_DRV.h"

/* Wizard menu ---------------------------------------------------------------*/
/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
static DMA_BaseInitTypeDef      mDACDMA;
static uint32_t                 LastCycleEvent = 0; // The last DMA access cycle event.
static uint32_t                 AudioBufIDX;        // AudioBufIDX : Audio Buffer source index.
static __ALIGNED(4) uint8_t     AudioBuf1[500];     // Double Buffer : Audio buffer1.
static __ALIGNED(4) uint8_t     AudioBuf2[500];     // Double Buffer : Audio buffer2.

/* Private function prototypes -----------------------------------------------*/
void DMA_IRQ(void);                                 // interrupt routine.
void DMA_ConvHalfCpltCallback(void);                // Copy rawData to AutioBufx.
void DMA_ConvCpltCallback(void);                    // Set Last Last Cycle.

/* Exported variables --------------------------------------------------------*/
/* Exported functions --------------------------------------------------------*/
/* External vairables --------------------------------------------------------*/
/* audio file is Audio_raw_data.c' */
extern uint8_t      rawData[];                      // Audio raw data array.


/**
 *******************************************************************************
 * @brief       Initial DAC Audio output (DAC + DMA + Timer). 
 * @return		None
 *******************************************************************************
 */
void BSP_DACOutput_Init(void)
{
    TM_TimeBaseInitTypeDef TM_TimeBase_InitStruct;
    
    // ------------------------------------------------------------------------
    // Config clock 
    // ------------------------------------------------------------------------
    // config CK_APB=CK_AHB=48MHz
    // Enable and select DAC / DMA / TM10 module source clock.
    // config in MG32_CSC_Init.h
    
    // ------------------------------------------------------------------------
    // Config PB2 for DAC output. (AIO mode)
    // ------------------------------------------------------------------------
    // config in MG32_GPIO_Init.h
    
    // ------------------------------------------------------------------------
    // 1.Initial TM10 timer : counter range is 8K in 48MHz.
    // ------------------------------------------------------------------------
    TM_TimeBaseStruct_Init(&TM_TimeBase_InitStruct);    
    {
        // modify parameter
        TM_TimeBase_InitStruct.TM_Period = 6000 - 1;    // Main counter : 48M / 6000 = 8K, so input 5999 to count to 8KHz
        TM_TimeBase_InitStruct.TM_Prescaler = 0;        // Prescaler counter is zero.
        TM_TimeBase_InitStruct.TM_CounterMode = Cascade;// TM10 in cacade mode.
    }
    TM_TimeBase_Init(TM10, &TM_TimeBase_InitStruct);    // Initial TM10 with TM_TimeBase_InitStruct struct. 
    
    // ------------------------------------------------------------------------
    // 2.Initial TM10 TRGO configuration.
    // ------------------------------------------------------------------------
    TM_TRGO_Select(TM10, TRGO_UEV);                     // TM10 trigger out source from TM10 update event
    TM_UEV_Config(TM10, UEV_TOF);                       // TM10 update event from TM10 overflow event
    TM_UEV_Cmd(TM10, ENABLE);                           // Enable TM10 update event
        
    // ------------------------------------------------------------------------
    // 3.Start TM10.
    // ------------------------------------------------------------------------
    TM_Timer_Cmd(TM10, ENABLE);                         // Enable TM10

    // ------------------------------------------------------------------------
    // 4.Initial DAC. 
    // ------------------------------------------------------------------------
    DAC_DeInit(DAC);                                    // Deinitial DAC module
    DAC_TriggerSource_Select(DAC, DAC_TM10_TRGO);       // Config DAC trigger source from TM10
    DAC_TriggerEdge_Select(DAC, DAC_AcceptRisingEdge);  // Config DAC trigger edge from TM10 rising edge 
    
    DAC_DataResolution_Select(DAC, DAC_10BitData);      // Accept 10bit of Audio raw data
    DAC_DataAlignment_Select(DAC, DAC_LeftJustified);   // Config DAC to accept left-justtified
    DAC_SetDAT0(DAC, 0);                                // DAC output 0 (default)
    DAC_OutputBuffer_Cmd(DAC, ENABLE);                  // Enable DAC output buffer (Only apply MG32F02A128/U128/A064/U064)
    DAC_Cmd(DAC, ENABLE);                               // Enable DAC
    
    // ------------------------------------------------------------------------
    // 5.initial DMA. (M2P)
    // ------------------------------------------------------------------------
    DMA_DeInit(DMA);                                    // Deinitial DMA module
    
    DMA_Cmd(ENABLE);                                    // Enable DMA module
    
    // DMA (M2P) configuration
    DMA_BaseInitStructure_Init(&mDACDMA);
    {
        mDACDMA.DMAChx          = DMAChannel0;          // Use DMAChannel0 - channel 0
        mDACDMA.ExtTrgMode      = DMA_DisableExtTrg;    // Disable external trigger pin function
        
        mDACDMA.SrcSymSel       = DMA_MEM_Read;         // DMA source peripheral is Memory
        mDACDMA.SrcSINCSel      = ENABLE;               // DMA source memory transfer address auto increased    
        mDACDMA.DMASourceAddr   = &AudioBuf1;           // Config DMA source address
        
        mDACDMA.DestSymSel      = DMA_DAC0_OUT;         // DMA destination peripheral is DAC
        
        mDACDMA.DMALoopCmd      = ENABLE;               // Enable DMA loop mode        
        mDACDMA.BurstDataSize   = DMA_BurstSize_2Byte;  // Burst size select 2 bytes.
        mDACDMA.DMATransferNUM  = 500;                  // DMA access 500 bytes then DMA will assert DMA complete event    
    }
    DMA_Base_Init(&mDACDMA);

    DMA_Channel_Cmd(DMAChannel0, ENABLE);               // Enable DMAChannel0    
    
    // ------------------------------------------------------------------------
    // Enable Cortex-M0 DMA interrupt
    #if !defined(MG32_DMA_IRQ_)
        NVIC_EnableIRQ(DMA_IRQn);                       // Enable DMA interrupt
        NVIC_SetPriority(DMA_IRQn, 0x0);                // Set DMA priority - the highest
    #endif
    
    // Enable interrupt control of DMA - Half & Complete interrupt
    DMA_IT_Config(DMAChannel0, DMA_Half_ITE | DMA_Complete_ITE, ENABLE);
    DMA_ITEA_Cmd(DMA, ENABLE);
    
    // ------------------------------------------------------------------------
    // 7.Update Audio buffer.
    // ------------------------------------------------------------------------
    AudioBufIDX = 0;                                    // Audio Buffer Index = 0
    DMA_ConvHalfCpltCallback();                         // Copy audio raw data to AudioBuf1 
    
    DAC_DMA_Cmd(DAC, ENABLE);                           // Enable DMA in DAC module
    DMA_StartRequest(DMAChannel0);                      // Enable DMA channel 0

}



/**
 *******************************************************************************
 * @brief       Update AudioBuf from rawData.c
 * @return		None
 *******************************************************************************
 */
void DMA_ConvHalfCpltCallback(void)
{
    uint32_t            i;
    volatile uint32_t   *PosIDX;                        // The index of SRAM audio
    uint32_t            *DestAddr;   
    
    //---------------------------------------------------------------------
    // copy rawData data to audio buffer
    //---------------------------------------------------------------------
    PosIDX = (uint32_t *) &rawData;                     // Base address from rawData
    i = AudioBufIDX * 125;                              // 125 * 4 = 500 byte
    PosIDX += i;                                        // PosIDX is Word align (4 bytes)
    
    // ------------------------------------------------------------------------
    // decide AudioBuf1 or AudioBuf2 ? (by AudioBufIDX bit 0)
    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.
    }
    
    // ------------------------------------------------------------------------
    // update DMA source address for next DMA transmission
    DMA_SetSourceAddress(DMAChannel0, (uint32_t *) DestAddr);
    
    // ------------------------------------------------------------------------
    // is the last cycle ?
    AudioBufIDX++;
    if(AudioBufIDX == 128)                              // the AudioBufIDX reach the last raw data
    {                                                   
        AudioBufIDX = 0;                                // reset AudioBufIDX
        LastCycleEvent = 1;                             // set 'LastCycleEvent=1' for DMA complete evnet
    }
        
}


/**
 *******************************************************************************
 * @brief       Conversion complete callback in non blocking mode for DAC. 
 * @return		None
 *******************************************************************************
 */
void DMA_ConvCpltCallback(void)
{
    if(LastCycleEvent == 1)
    {
        DMA_ClearChannelFlag(DMAChannel0, DMA_Chx_TCF); // Clear TCF flag first.
                                                        // If 'DMA_LastCycle_Cmd' routine finds TCF 
                                                        // flag then it will stop this transfer.                                                      
        
        DMA_LastCycle_Cmd(DMAChannel0, ENABLE);         // Set DMA Last transmission - Then DMA will 
                                                        // then stop on the next transfer.
        LastCycleEvent = 0;
    }
}


/**
 *******************************************************************************
 * @brief       DMA interrupt routine
 * @return		None
 *******************************************************************************
 */
#if !(defined(MG32_IRQ_Handler_))
void DMA_IRQHandler(void)                               // Origin 'DMA_IRQHandler' interrupt routine.
#else
void DMA_IRQ(void)                                      // Call 'DMA_IRQ' routine in 'MG32__IRQHandler.c'.
#endif
{
    // Assert 'Half flag' - Copy raw data to AudioBuf1 or AudioBuf2
    if (DMA_GetChannelFlag(DMAChannel0, DMA_Chx_THF) == DRV_Happened)
    {
        DMA_ConvHalfCpltCallback();                     // Copy rawData to AudioBufx
    }
    
    // Assert 'Complete flag' - Set Last Cycle when bottom is reached
    if (DMA_GetChannelFlag(DMAChannel0, DMA_Chx_TCF) == DRV_Happened)
    {
        DMA_ConvCpltCallback();                         // Set 'Last Cycle' ?
    }
    
    // clear DMAChannel0 flags
    DMA_ClearChannelFlag(DMAChannel0, (DMA_ChannelFlagDef) (DMA_Chx_THF | DMA_Chx_TCF));
    
    
}
