/****
2021-11-24 /ico
Complete  serial debugging sysytem with two external functions:
(based on the example from C:\Texas Instruments\BLE-CC254x-1.4.0\CC2541_43_44_45_Peripherals_Software_Examples\software_examples\uart)
Transmit only using DMA for fast transmition with 115200 baud

Declerations:
    extern void init_serial_DMA(void)
    extern void UART_TxStr(uint8 *buf)

Instructions:
  call init_serial_DMA() to initialize the system
  call UART_TxStr(uint8 *buf) to write to the serial port

Resource used:
  DMA0
  UART0 Alt.1
  P0_2  Rx not used
  P0_3  Tx

*******************************************/

#include <hal_types.h>
#include <string.h>
#include <stdbool.h>
#include <dma.h>
#include "ioCC2541.h"


// DMAIRQ (0xD1) - DMA Interrupt Flag
#define DMAIRQ_DMAIF1                     0x02
#define DMAIRQ_DMAIF0                     0x01

// DMAARM (0xD6) - DMA Channel Arming
#define DMAARM_ABORT                      0x80
#define DMAARM_DMAARM1                    0x02
#define DMAARM_DMAARM0                    0x01

#define PERCFG_U0CFG                    0x01          // USART 0 I/O location
#define PERCFG_U0CFG_ALT1               0x00          // Alternative 1 location
#define P2DIR_PRIP0_USART0                (0x00 << 6) // USART 0 has priority, then USART 1, then Timer 1


// BIT definitions.
#define BIT7        0x80
#define BIT6        0x40
#define BIT5        0x20
#define BIT4        0x10
#define BIT3        0x08
#define BIT2        0x04
#define BIT1        0x02
#define BIT0        0x01


#define UART_BAUD_115200       0
#define UART_BAUD_57600        1

#define UART_BAUD_SELECTE       UART_BAUD_115200


#if (UART_BAUD_SELECTE==UART_BAUD_57600)
// Baudrate = 57.6 kbps (U0BAUD.BAUD_M = 216, U0GCR.BAUD_E = 10), given 32 MHz system clock.
#define UART_BAUD_M  216
#define UART_BAUD_E  10
#elif (UART_BAUD_SELECTE==UART_BAUD_115200)
// Baudrate = 115200 kbps (U0BAUD.BAUD_M = 216, U0GCR.BAUD_E = 11), given 32 MHz system clock.
#define UART_BAUD_M  216
#define UART_BAUD_E  11
#endif

#define UART_TEST_DATA "012345678901234567890\r\n"

// U0CSR (0x86) - USART 0 Control and Status
#define U0CSR_MODE                        0x80
#define U0CSR_RE                          0x40
#define U0CSR_SLAVE                       0x20
#define U0CSR_FE                          0x10
#define U0CSR_ERR                         0x08
#define U0CSR_RX_BYTE                     0x04
#define U0CSR_TX_BYTE                     0x02
#define U0CSR_ACTIVE                      0x01

// U0UCR (0xC4) - USART 0 UART Control
#define U0UCR_FLUSH                       0x80
#define U0UCR_FLOW                        0x40
#define U0UCR_D9                          0x20
#define U0UCR_BIT9                        0x10
#define U0UCR_PARITY                      0x08
#define U0UCR_SPB                         0x04
#define U0UCR_STOP                        0x02
#define U0UCR_START                       0x01


// U0GCR (0xC5) - USART 0 Generic Control
#define U0GCR_CPOL                        0x80
#define U0GCR_CPHA                        0x40
#define U0GCR_ORDER                       0x20
#define U0GCR_BAUD_E                      0x1F
  #define U0GCR_BAUD_E0                     0x01
  #define U0GCR_BAUD_E1                     0x02
  #define U0GCR_BAUD_E2                     0x04
  #define U0GCR_BAUD_E3                     0x08
  #define U0GCR_BAUD_E4                     0x10

#define SEND_MAX_LENGTH         50

static bool finishedTx=true;
// DMA descriptor for UART TX.
//static DMA_DESC __xdata uartDmaRxTxCh[2]; // this can expanded if Rx is required
//static uint8  __xdata uartTxBuffer[SEND_MAX_LENGTH]=UART_TEST_DATA;

static DMA_DESC __xdata  uartDmaTxChDesc; // DMA Channeldecriptor this can expanded if Rx is required
static uint8 __xdata  uartTxBuffer[SEND_MAX_LENGTH]=UART_TEST_DATA;
static uint8 uartDmaTxChanNo=0;
/******************************************************************************
  *     Configure I/Os
  *
**/ 

static void uart_io_cfg(void){
    // Setup I/O ports
    /*
    * Port and pins used by USART0 operating in UART-mode, at the Alternative 1
    * location for CC2541EM, CC2543EM and CC2545EM are:
    * RX     : P0_2
    * TX     : P0_3
    * CT/CTS : P0_4 not used
    * RT/RTS : P0_5 not used
    *
    * These pins must be set to function as peripheral I/O to be used by UART0.
    * The TX pin on the transmitter must be connected to the RX pin on the receiver.
    * If enabling hardware flow control (U0UCR.FLOW = 1) the CT/CTS (Clear-To-Send)
    * on the transmitter must be connected to the RS/RTS (Ready-To-Send) pin on the
    * receiver.
    */
    // Configure USART0 for Alternative 1 => Port P0 (PERCFG.U0CFG = 0).
    PERCFG = (PERCFG & ~PERCFG_U0CFG) | PERCFG_U0CFG_ALT1;    
    // Give priority to USART 0 over Timer 1 for port 0 pins.
    P2DIR &= P2DIR_PRIP0_USART0;
    // Set pins 2, 3 and 5 as peripheral I/O and pin 4 as GPIO output.
    P0SEL |=  BIT3 | BIT2;
    // Initialize P0_1 for SRF05EB S1 button
    P0SEL &= ~BIT1;           // Function as General Purpose I/O
    P0DIR &= ~BIT1;           // Input

}



static void uart_regs_cfg(void){
  
  // Initialise bitrate = 57.6 kbps.
    U0BAUD = UART_BAUD_M;
    U0GCR = (U0GCR&~U0GCR_BAUD_E) | UART_BAUD_E;

    // Initialise UART protocol (start/stop bit, data bits, parity, etc.):
    // USART mode = UART (U0CSR.MODE = 1).
    U0CSR |= U0CSR_MODE;

    // Start bit level = low => Idle level = high  (U0UCR.START = 0).
    U0UCR &= ~U0UCR_START;

    // Stop bit level = high (U0UCR.STOP = 1).
    U0UCR |= U0UCR_STOP;

    // Number of stop bits = 1 (U0UCR.SPB = 0).
    U0UCR &= ~U0UCR_SPB;

    // Parity = disabled (U0UCR.PARITY = 0).
    U0UCR &= ~U0UCR_PARITY;

    // 9-bit data enable = 8 bits transfer (U0UCR.BIT9 = 0).
    U0UCR &= ~U0UCR_BIT9;

    // Level of bit 9 = 0 (U0UCR.D9 = 0), used when U0UCR.BIT9 = 1.
    // Level of bit 9 = 1 (U0UCR.D9 = 1), used when U0UCR.BIT9 = 1.
    // Parity = Even (U0UCR.D9 = 0), used when U0UCR.PARITY = 1.
    // Parity = Odd (U0UCR.D9 = 1), used when U0UCR.PARITY = 1.
    U0UCR &= ~U0UCR_D9;

    // Flow control = disabled (U0UCR.FLOW = 0).
    U0UCR &= ~U0UCR_FLOW;

    // Bit order = LSB first (U0GCR.ORDER = 0).
    U0GCR &= ~U0GCR_ORDER;

  
  
}




/***********************************************************************************
* @fn          uart0StartTxDma
*
* @brief       Function which sets up a DMA channel for UART0 TX.
*
* @param       DMA_DESC *uartDmaTxDescr - pointer to DMA descriptor for UART TX
*              uint8 uartDmaTxChan - DMA channel number for UART TX
*              uint8* uartTxBuf - pointer to allocated UART TX buffer
*              uint16 uartTxBufSize - size of allocated UART TX buffer
*
* @return      void
*/
//static void uart0StartTxDmaChan( DMA_DESC *uartDmaTxDescr,
//                          uint8 uartDmaTxChan,
//                          uint8 *uartTxBuf,
//                          uint16 uartTxBufSize)

static void uart0StartTxDma(uint8 *uartTxBuf,uint16 uartTxBufSize){

    // Set source/destination pointer (UART TX buffer address) for UART TX DMA channel,
    // and total number of DMA word transfer (according to UART TX buffer size).
    uartDmaTxChDesc.SRCADDRH   = (uint16)(uartTxBuf + 1) >> 8;      // +1 since we're sending the first packet manually later on,
    uartDmaTxChDesc.SRCADDRL   = (uint16)(uartTxBuf + 1);           // to start the DMA
    uartDmaTxChDesc.DESTADDRH  = ((uint16)(&X_U0DBUF) >> 8) & 0x00FF;
    uartDmaTxChDesc.DESTADDRL  = (uint16)(&X_U0DBUF) & 0x00FF;
    uartDmaTxChDesc.LENH       = ((uartTxBufSize - 1) >> 8) & 0xFF;
    uartDmaTxChDesc.LENL       = (uartTxBufSize - 1) & 0xFF;
    uartDmaTxChDesc.VLEN       = DMA_VLEN_FIXED;    // Use fixed length DMA transfer count.

    // Perform 1-byte transfers
    uartDmaTxChDesc.WORDSIZE   = DMA_WORDSIZE_BYTE;

    // Transfer a single word after each DMA trigger
    uartDmaTxChDesc.TMODE      = DMA_TMODE_SINGLE;

    // DMA word trigger = USARTx TX complete
    uartDmaTxChDesc.TRIG       = DMA_TRIG_UTX0;

    uartDmaTxChDesc.SRCINC     = DMA_SRCINC_1;          // Increment source pointer by 1 word
                                                        // address after each transfer.
    uartDmaTxChDesc.DESTINC    = DMA_DESTINC_0;         // Do not increment destination pointer:
                                                        // points to USART UxDBUF register.
    uartDmaTxChDesc.IRQMASK    = DMA_IRQMASK_DISABLE; //DMA_IRQMASK_ENABLE;    // Enable DMA interrupt to the CPU
    uartDmaTxChDesc.M8         = DMA_M8_USE_8_BITS;     // Use all 8 bits for transfer count
    uartDmaTxChDesc.PRIORITY   = DMA_PRI_LOW;           // DMA memory access has low priority

 
    if (uartDmaTxChanNo < 1)
    {
        DMA0CFGH = (uint8)((uint16)&uartDmaTxChDesc >> 8);
        DMA0CFGL = (uint8)((uint16)&uartDmaTxChDesc & 0x00FF);
    } else {
        DMA1CFGH = (uint8)((uint16)&uartDmaTxChDesc >> 8);
        DMA1CFGL = (uint8)((uint16)&uartDmaTxChDesc & 0x00FF);
    }

    // Arm the relevant DMA channel for UART TX, and apply 9 NOPs
    // to allow the DMA configuration to load
    DMAARM = (1 << uartDmaTxChanNo);
    NOP();NOP();NOP();NOP();NOP();NOP();NOP();NOP();NOP(); // 9 Nops
    
   
    // Enable the DMA interrupt (IEN1.DMAIE = IEN0.EA = 1),
    // and clear potential pending DMA interrupt requests (IRCON.DMAIF = 0).
    EA = 1; DMAIE = 1; DMAIF = 0;
    DMAIRQ = ~DMAIRQ_DMAIF0;
    // Send the very first UART byte to trigger a UART TX session:
    U0DBUF = uartTxBuf[0];

    /* 
    *   At this point the UART peripheral generates a DMA trigger each time it has
    *   transmitted a byte, leading to a DMA transfer from the allocated source buffer
    *   to the UxDBUF register. Once the DMA controller has completed the defined
    *   range of transfers, the CPU vectors its execution to the DMA ISR. 
    */
}


/******************************************************************************
  *     send data with UART
  *
**/  
void serial_debugPrint(uint8 *buf)
{
      
    uint16 strLen= strlen((char *)buf);
    if(strLen>=SEND_MAX_LENGTH){
      memcpy(uartTxBuffer,"Too Long\r\n",11);
    }else{
       memcpy(uartTxBuffer,buf,strLen);
       uartTxBuffer[strLen]=0;
      //memcpy(uartTxBuffer,"0192837465\r\n",12);
    }
   // while(!finishedTx);
    while ( !(DMAIRQ & DMAIRQ_DMAIF0) );
     uart0StartTxDma(uartTxBuffer,strLen);      
      
    }

//----------------------------------------------
void serial_debugInit(void){

uart_io_cfg();
uart_regs_cfg();

}

/***********************************************************************************
* @fn          DMA_ISR
*
* @brief       DMA Interrupt Service Routine, called when DMA has finished
*              transferring one packet/buffer between memory and UxDBUF.
*
* @param       none
*
*/
#pragma vector = DMA_VECTOR
__interrupt void DMA_ISR(void)
{

    // Clear the main DMA interrupt Request Flag (IRCON.DMAIF = 0)
    DMAIF = 0;
  finishedTx=true;
 
    // Start a new UART RX session on DMA channel 1:
    if (DMAIRQ & DMAIRQ_DMAIF0)
    {
        

        // Clear DMA Channel 0 Interrupt Request Flag (DMAIRQ.DMAIF0 = 0).
        DMAIRQ = ~DMAIRQ_DMAIF0;

        // Re-arm DMA Channel 0 (DMAARM.DMAARM0 = 1).
        DMAARM |= DMAARM_DMAARM0;
    }

    // Start a new UART TX session on DMA channel 1:
    if (DMAIRQ & DMAIRQ_DMAIF1)
    {
        // Clear DMA Channel 1 Interrupt Request Flag (DMAIRQ.DMAIF1 = 0)
        DMAIRQ = ~DMAIRQ_DMAIF1;

        /* 
        *   In this particular software example a new UART/DMA TX session must
        *   be initialized outside this DMA ISR. However, it would also be possible
        *   to use this DMA ISR to start a new UART/DMA TX session by simply
        *   re-arming the DMA channel and sending the first UART TX byte.
        */
    }
}
