//#############################################################################
//
// FILE:    spi_ex2_dma_loopback.c
//
// TITLE:   SPI Digital Loop Back with DMA Example.
//
// VERSION: 1.0.0
//
// DATE:    2025-01-15
//
//! \addtogroup bitfield_example_list
//! <h1>SPI Digital Loop Back with DMA (spi_loopback_dma)</h1>
//!
//!  This program uses the internal loop back test mode of the peripheral.
//!  Other then boot mode pin configuration, no other hardware configuration
//!  is required. Both DMA Interrupts and the SPI FIFOs are used.
//!
//!  A stream of data is sent and then compared to the received stream.
//!  The sent data looks like this: \n
//!  0000 0001 \n
//!  0001 0002 \n
//!  0002 0003 \n
//!  .... \n
//!  007E 007F \n
//!
//!  \b Watch \b Variables \n
//!  - \b sData - Data to send
//!  - \b rData - Received data
//!  - \b rData_point - Used to keep track of the last position in
//!    the receive stream for error checking
//!
//
//###########################################################################
//
//
// $Copyright:
// Copyright (C) 2024 Geehy Semiconductor - http://www.geehy.com/
// Copyright (C) 2023 Texas Instruments Incorporated - http://www.ti.com/
//
// Redistribution and use in source and binary forms, with or without 
// modification, are permitted provided that the following conditions 
// are met:
// 
//   Redistributions of source code must retain the above copyright 
//   notice, this list of conditions and the following disclaimer.
// 
//   Redistributions in binary form must reproduce the above copyright
//   notice, this list of conditions and the following disclaimer in the 
//   documentation and/or other materials provided with the   
//   distribution.
// 
//   Neither the name of Texas Instruments Incorporated nor the names of
//   its contributors may be used to endorse or promote products derived
//   from this software without specific prior written permission.
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// $
//
// Modifications:
// - 2024-07-08:
// 1. Updated SPI register names for new device.
// 2. Updated interrupt handling to use NVIC functions.
//
//###########################################################################

//
// Included Files
//
#include "g32r501_project.h"

//
// Defines
//
#define TRANSFER      15                 // [(MEM_BUFFER_SIZE/FIFO_LVL)-1]
#define BURST         (RX_FIFO_LVL-1)    // burst size should be less than 8

#define TX_FIFO_LVL      6               // Tx FIFO Interrupt Level
#define RX_FIFO_LVL      8               // Rx FIFO Interrupt Level

//
// Globals
//
__attribute__((section("sram1.share_data"))) uint16_t sData[128];
__attribute__((section("sram1.share_data"))) uint16_t rData[128];

uint16_t rData_point;    // Keep track of where we are
                       // in the data stream to check received data
volatile uint16_t *DMADest;
volatile uint16_t *DMASource;
volatile uint16_t done;

//
// Function Prototypes
//
void INT_DMA_CH5_IRQHandler(void);
void INT_DMA_CH6_IRQHandler(void);
void dma_init(void);
void spi_fifo_init(void);
void error();
void InitSpia(void);
void InitSpiaGpios(void);

//
// Main
//
void example_main(void)
{
    uint16_t i;

    //
    // Initialize System Control:
    // PLL, WatchDog, enable Peripheral Clocks
    // This example function is found in the <device>_sysctrl.c file.
    //
    InitSysCtrl();

    //
    // Initialize GPIO
    //
    InitGpio();
    InitSpiaGpios();

    //
    // Clear all interrupts:
    //
    DINT;

    //
    // Initialize the NVIC control registers to their default state.
    // The default state is all NVIC interrupts disabled and flags
    // are cleared.
    //
    Interrupt_initModule();

    //
    // Initialize the NVIC vector table with pointers to the shell Interrupt
    // Service Routines (ISR).
    //
    Interrupt_initVectorTable();

    //
    // Interrupts that are used in this example are re-mapped to
    // ISR functions found within this file.
    //
    Interrupt_setPriorityGroup(INTERRUPT_PRIGROUP_PREEMPT_7_6_SUB_5_0);

    Interrupt_setPriority(INT_DMA_CH5, 0, 0);
    Interrupt_setPriority(INT_DMA_CH6, 0, 1);

    Interrupt_register(INT_DMA_CH5, INT_DMA_CH5_IRQHandler);
    Interrupt_register(INT_DMA_CH6, INT_DMA_CH6_IRQHandler);

    Interrupt_enable(INT_DMA_CH5);
    Interrupt_enable(INT_DMA_CH6);

    //
    // Initialize the Device Peripherals:
    //
    dma_init();
    spi_fifo_init();     // Initialize the SPI FIFO


    //
    // Initialize the data buffers
    //
    for (i = 0; i < 128; i++)
    {
        sData[i] = i;
        rData[i]= 0;
    }
    rData_point = 0;

    //
    // Enable interrupts required for this example
    //

    EINT;                            // Enable Global Interrupts

    StartDMACH6();                   // Start SPI RX DMA channel
    StartDMACH5();                   // Start SPI TX DMA channel

    done = 0;                        // Test is not done yet

    //
    // Wait until the DMA transfer is complete
    //
    while (!done);

    //
    // when the DMA transfer is complete the program will stop here
    //
    ESTOP0;

    while (1);
}

//
// error - Halt debugger when error received
//
void error(void)
{
    ESTOP0;
    for (;;);
}

//
// spi_fifo_init - Initialize SPIA FIFO
//
void spi_fifo_init()
{
    //
    // Initialize SPI FIFO registers
    //
    SpiaRegs.SPIRXFIFO.all=0x2040;                // RX FIFO enabled, clear FIFO int
    SpiaRegs.SPIRXFIFO.bit.RXFFILSEL = RX_FIFO_LVL;  // Set RX FIFO level

    SpiaRegs.SPITXFIFO.all=0xE040;                // FIFOs enabled, TX FIFO released,
    SpiaRegs.SPITXFIFO.bit.TXFFILSEL = TX_FIFO_LVL;  // Set TX FIFO level

    //
    // Initialize core SPI registers
    //
    InitSpia();
}

//
// dma_init - DMA setup for both TX and RX channels.
//
void dma_init()
{
    //
    // Initialize DMA
    //
    DMAInitialize();

    DMASource = (volatile uint16_t *)sData;
    DMADest = (volatile uint16_t *)rData;

    //
    // configure DMACH5 for TX
    //
    DMACH5AddrConfig(&SpiaRegs.SPITXBUF,DMASource);
    DMACH5BurstConfig(BURST,1,0);         // Burst size, src step, dest step
    DMACH5TransferConfig(TRANSFER,1,0);   // transfer size, src step, dest step
    DMACH5ModeConfig(DMA_SPIATX,PERINT_ENABLE,SIGDIS_DISABLE,CONT_DISABLE,
                     SYNC_DISABLE,SYNC_SRC,OVRFLOW_DISABLE,SIXTEEN_BIT,
                     CHINT_END,CHINT_ENABLE);

    //
    // configure DMA CH6 for RX
    //
    DMACH6AddrConfig(DMADest,&SpiaRegs.SPIRXBUF);
    DMACH6BurstConfig(BURST,0,1);
    DMACH6TransferConfig(TRANSFER,0,1);
    DMACH6ModeConfig(DMA_SPIARX,PERINT_ENABLE,SIGDIS_DISABLE,CONT_DISABLE,
                     SYNC_DISABLE,SYNC_SRC,OVRFLOW_DISABLE,SIXTEEN_BIT,
                     CHINT_END,CHINT_ENABLE);
}

//
// local_D_INTCH5_ISR - DMA Channel 5 ISR
//
void INT_DMA_CH5_IRQHandler(void)
{
    WRPRT_DISABLE;  // NEED TO EXECUTE WRPRT_DISABLE INSIDE ISR !!!
    DmaRegs.CH5.CONTROL.bit.HALT=1;

    WRPRT_ENABLE;
    return;
}

//
// local_D_INTCH6_ISR - DMA Channel 6 ISR
//
void INT_DMA_CH6_IRQHandler(void)
{
    uint16_t i;

    WRPRT_DISABLE;  // NEED TO EXECUTE WRPRT_DISABLE INSIDE ISR !!!
    DmaRegs.CH6.CONTROL.bit.HALT = 1;

    WRPRT_ENABLE;

    for( i = 0; i<128; i++ )
    {
        //
        // check for data integrity
        //
        if(rData[i] != i)
        {
            error();
        }
    }

    done = 1;  // test done.
    return;
}


//
// InitSPI - This function initializes the SPI to a known state
//
void InitSpia(void)
{
    //
    // Initialize SPI-A
    //

    //
    // Set reset low before configuration changes
    // Clock polarity (0 == rising, 1 == falling)
    // 16-bit character
    // Enable loop-back
    //
    SpiaRegs.SPICFG.bit.SPIRDY = 0;
    SpiaRegs.SPICFG.bit.POLCFG = 0;
    SpiaRegs.SPICFG.bit.CLENSEL = (16 - 1);
    SpiaRegs.SPICFG.bit.LBEN = 1;

    //
    // Enable master (0 == slave, 1 == master)
    // Enable transmission (Talk)
    // Clock phase (0 == normal, 1 == delayed)
    // SPI interrupts are disabled
    //
    SpiaRegs.SPICTRL.bit.MSCFG = 1;
    SpiaRegs.SPICTRL.bit.TXEN = 1;
    SpiaRegs.SPICTRL.bit.PHASEL = 0;
    SpiaRegs.SPICTRL.bit.IEN = 0;

    //
    // Set the baud rate using a 1.25 MHz SPICLK
    // BRR = (LSPCLK / SPICLK) - 1
    //
    // SYSCLK = 200M
    // APBCLK = SYSCLK / 2 = 100M
    // LSPCLK = APBCLK / 4 = 25M
    //
    SpiaRegs.SPIBR.bit.BRSEL = ((25000000 / 1250000) - 1);

    //
    // Set EMUFREEEN bit
    // Halting on a breakpoint will not halt the SPI
    //
    SpiaRegs.SPIPRICTRL.bit.EMUFREEEN = 1;

    //
    // Release the SPI from reset
    //
    SpiaRegs.SPICFG.bit.SPIRDY = 1;
}



void InitSpiaGpios(void)
{
       WRPRT_DISABLE;

    //
    // Enable internal pull-up for the selected pins
    //
    // Pull-ups can be enabled or disabled by the user.
    // This will enable the pullups for the specified pins.
    //
    GpioCtrlRegs.GPAPUD.bit.GPIO16 = 0;  // Enable pull-up on GPIO16 (SPISIMOA)
    GpioCtrlRegs.GPAPUD.bit.GPIO17 = 0;  // Enable pull-up on GPIO17 (SPISOMIA)
    GpioCtrlRegs.GPBPUD.bit.GPIO56 = 0;  // Enable pull-up on GPIO56 (SPICLKA)
    GpioCtrlRegs.GPBPUD.bit.GPIO57 = 0;  // Enable pull-up on GPIO57 (SPISTEA)

    //
    // Set qualification for selected pins to asynch only
    //
    // This will select asynch (no qualification) for the selected pins.
    //
    GpioCtrlRegs.GPAQSEL2.bit.GPIO16 = 3; // Asynch input GPIO16 (SPISIMOA)
    GpioCtrlRegs.GPAQSEL2.bit.GPIO17 = 3; // Asynch input GPIO17 (SPISOMIA)
    GpioCtrlRegs.GPBQSEL2.bit.GPIO56 = 3; // Asynch input GPIO56 (SPICLKA)
    GpioCtrlRegs.GPBQSEL2.bit.GPIO57 = 3; // Asynch input GPIO57 (SPISTEA)

    //
    // Configure SPI-A pins
    //
    // This specifies which of the possible GPIO pins will be SPI functional
    // pins.
    //
    GpioCtrlRegs.GPAMUX2.bit.GPIO16 = 1; // Configure GPIO16 as SPISIMOA
    GpioCtrlRegs.GPAMUX2.bit.GPIO17 = 1; // Configure GPIO17 as SPISOMIA
    GpioCtrlRegs.GPBMUX2.bit.GPIO56 = 1; // Configure GPIO56 as SPICLKA
    GpioCtrlRegs.GPBMUX2.bit.GPIO57 = 1; // Configure GPIO57 as SPISTEA

    WRPRT_ENABLE;
}

//
// End of File
//
