/* * dma.c * * Created on: Feb 11, 2014 * Author: jal * * Routines for transmit/receive of serial port data via DMA * */ //#include "board.h" #include "chip.h" #include "FreeRTOS.h" #include "task.h" #include #include #include "dma.h" #include "uart.h" // Functions provided in io.c extern void UsbWaitTx(void); // Serial Receive DMA buffers volatile uint8_t UART0Buffer[512]; volatile uint8_t *UART0RxRdPtr = UART0Buffer; volatile uint8_t UART1Buffer[512]; volatile uint8_t *UART1RxRdPtr = UART1Buffer; /* * Global variables related to the serial dma handling */ // Function to get character from UART int UART0_Getchar() { int c; if ( (uint32_t)UART0RxRdPtr == LPC_GPDMA->CH[2].DESTADDR) return(-1); // Nothing received so just block c = (int)(*UART0RxRdPtr++); // Read Receiver buffer register if (UART0RxRdPtr >= (UART0Buffer + sizeof(UART0Buffer))) UART0RxRdPtr = UART0Buffer; return c; } int isUART0CharAvail(void) { return(((uint32_t)UART0RxRdPtr == LPC_GPDMA->CH[2].DESTADDR) ? 0 : 1); // Nothing received so just block } // Utility function to flush Rx buffer... void UART1_FlushRx(void) { // jal - removed for debug // UART1RxRdPtr = LPC_GPDMA->CH[0].DESTADDR; // Nothing received so just block } int UART1_Getchar() { int c; if ( (int)UART1RxRdPtr == LPC_GPDMA->CH[0].DESTADDR) return(-1); // Nothing received so just block c = (int)(*UART1RxRdPtr++); // Read Receiver buffer register if (UART1RxRdPtr >= (UART1Buffer + sizeof(UART1Buffer))) UART1RxRdPtr = UART1Buffer; return c; } int isUART1CharAvail(void) { return(((int)UART1RxRdPtr == LPC_GPDMA->CH[0].DESTADDR) ? 0 : 1); // Nothing received so just block } /* * Handler for GPDMA completion interrupts... */ void GPDMA_IRQHandler (void) { /* Clear the interrupt */ /* Identify the channel requesting service */ /* Call the channel specific handler routine ..if any */ /* Clear pending interrupts ... */ LPC_GPDMA->INTERRCLR = LPC_GPDMA->INTERRSTAT; LPC_GPDMA->INTTCCLEAR = LPC_GPDMA->INTTCSTAT; } /* * initDMA: Initialize GPDMA controller to transfer intial buffer * and Linked List Item. * * Arguments: pList - a pointer to a linked list node which will * be used after the initial transfer is complete. * */ dmaLinkedListNode dmaUART1Rx; // (jal) // These fields should be constant. If the DMA is active and we get reset and the dmaLinkedListNode is in bss // then the C startup code will zero the node and the DMAC will load the dma registers the next time the buffer wraps // around const dmaLinkedListNode dmaUART0Rx = { (int)&(LPC_UART0->RBR), // dmaUART0Rx.sourceAddr = (int)&(LPC_UART0->RBR); (int)UART0Buffer, // dmaUART0Rx.destAddr = (int)UART0Buffer; (int)&dmaUART0Rx, // dmaUART0Rx.nextNode = (int)&dmaUART0Rx; (sizeof(UART0Buffer) | (0x00 <<18) | (0x00<<21) | (1<<27) | (1<<31)) // dmaUART0Rx.dmaControl = sizeof(UART0Buffer) | (0x00 <<18) | (0x00<<21) | (1<<27) | (1<<31); }; // // Utility functions to trigger serial message transmissions using dma // // // Dedicated UART0 dma transmit function: // Always uses DMA Channel 3. // Will hang waiting for a prior message to be transmitted. // NOTE: Caller must not re-use the msg string until DMA completes // void dmaUart0SendString(char *msg) { #if 0 // Wait for a prior operation to be done.... while ((LPC_GPDMA->ENBLDCHNS & (1<<3)) && ((LPC_GPDMA->RAWINTTCSTAT & (1<<3)) == 0)) vTaskDelay(0); #else // Wait for a prior operation to be done.... while ((LPC_GPDMA->ENBLDCHNS & (1<<3)) && ((LPC_GPDMA->RAWINTTCSTAT & (1<<3)) == 0)); #endif // Clear the terminal count if it was pending..we are about to queue another xmit LPC_GPDMA->INTTCCLEAR = (1<<3); LPC_GPDMA->INTERRCLR = (1<<3); //Disable the channel - LPC_GPDMA->CH[3].CONFIG &= ~(1<<0); LPC_GPDMA->CH[3].SRCADDR = (int)msg; LPC_GPDMA->CH[3].DESTADDR = (int)&(LPC_UART0->THR); // 256 bytes, 32 bit wide source/ 8 bit dest width, source increment // Src/dest burst size is one LPC_GPDMA->CH[3].CONTROL = (strlen(msg)) | (0x00 <<18) | (0x00<<21) | (1<<26) | (1<<31); LPC_GPDMA->CH[3].LLI = 0; //Lower bits must be 0 //Set up all relevant bits //SrcPeripheral = 0, DestPeripheral = UART0.TX LPC_GPDMA->CH[3].CONFIG = (8<<6) | (1<<11); // UART0.TX //Finally, enable the channel - LPC_GPDMA->CH[3].CONFIG |= 1<<0; // // Wait for a prior operation to be done.... // We do this here in case the caller's message was on the stack. If // this routine returns before the entire messages is loaded into the tx // fifo then the caller may pop the stack and corrupt the outbound message. // while ((LPC_GPDMA->ENBLDCHNS & (1<<3)) && ((LPC_GPDMA->RAWINTTCSTAT & (1<<3)) == 0)); // jal - try busy waiting instead vTaskDelay(0); } // // Dedicated UART1 dma transmit function: // Always uses DMA Channel 1. // Will hang waiting for a prior message to be transmitted. // NOTE: Caller must not re-use the msg string until DMA completes // void dmaUart1SendBytes(char *msg, int len) { // Wait for a prior operation to be done.... while ((LPC_GPDMA->ENBLDCHNS & (1<<1)) && ((LPC_GPDMA->RAWINTTCSTAT & (1<<1)) == 0)) vTaskDelay(0); // Clear the terminal count if it was pending..we are about to queue another xmit LPC_GPDMA->INTTCCLEAR = (1<<1); LPC_GPDMA->INTERRCLR = (1<<1); //Disable the channel - LPC_GPDMA->CH[1].CONFIG &= ~(1<<0); LPC_GPDMA->CH[1].SRCADDR = (int)msg; LPC_GPDMA->CH[1].DESTADDR = (int)&(LPC_UART1->THR); // 256 bytes, 32 bit wide source/ 8 bit dest width, source increment // Src/dest burst size is one LPC_GPDMA->CH[1].CONTROL = (len) | (0x00 <<18) | (0x00<<21) | (1<<26) | (1<<31); LPC_GPDMA->CH[1].LLI = 0; //Lower bits must be 0 //Set up all relevant bits //SrcPeripheral = 0, DestPeripheral = UART1.TX LPC_GPDMA->CH[1].CONFIG = (10<<6) | (1<<11); // UART1.TX //Finally, enable the channel - LPC_GPDMA->CH[1].CONFIG |= 1<<0; // // Wait for a prior operation to be done.... // We do this here in case the caller's message was on the stack. If // this routine returns before the entire message is loaded into the tx // fifo then the caller may pop the stack and corrupt the outbound message. // while ((LPC_GPDMA->ENBLDCHNS & (1<<1)) && ((LPC_GPDMA->RAWINTTCSTAT & (1<<1)) == 0)); // jal - try busy wait instead? vTaskDelay(0); } void dmaUart1SendString(char *msg) { dmaUart1SendBytes(msg,strlen(msg)); } // // Initialize the General Purpose DMA controller subsystem. // This function only handles general configuration. The use and configuration of // specific channels is handled by the subsystems that use DMA. // // Individual Channel configuration register // 0 BACnet receive (UART1 RX) // 1 BACnet transmit(UART1 TX) // 2 USB/UI receive (UART0 RX) // 3 USB/UI transmit(UART0 TX) // 4 // 5 // 6 // 7 void GPDMAInit(void) { // Basic GPDMA subsystem initialization LPC_SYSCON->PCONP |= 1<<29; //Power GPDMA module LPC_GPDMA->CONFIG = 1; //Enable GPDMA //Clear any previous interrupts LPC_GPDMA->INTTCCLEAR = 0xFF; LPC_SYSCON->DMAREQSEL = 0x00000000; // All sources are serial port LPC_GPDMA->SYNC = 0x00000000; // Synchronization enabled all channels LPC_GPDMA->INTERRCLR = 0xFF; // Clear any pending error bits #if 0 // Use constant values from flash so the LLI content cannot be trashed. // Setup UART0 RX Buffer DMA dmaUART0Rx.destAddr = (int)UART0Buffer; dmaUART0Rx.sourceAddr = (int)&(LPC_UART0->RBR); // sizeof(UART0Buffer) bytes, 8 bit wide dest./ 8 bit src width, dest. increment // Src/dest burst size is one // TC interrupt enabled from this channel dmaUART0Rx.dmaControl = sizeof(UART0Buffer) | (0x00 <<18) | (0x00<<21) | (1<<27) | (1<<31); // Link back to self dmaUART0Rx.nextNode = (int)&dmaUART0Rx; #endif LPC_GPDMA->CH[2].CONFIG = 0; // Disable for now LPC_GPDMA->CH[2].SRCADDR = dmaUART0Rx.sourceAddr; LPC_GPDMA->CH[2].DESTADDR = dmaUART0Rx.destAddr; LPC_GPDMA->CH[2].CONTROL = dmaUART0Rx.dmaControl; LPC_GPDMA->CH[2].LLI = (uint32_t)&dmaUART0Rx; //Lower bits must be 0 //Set up all relevant bits //SrcPeripheral = UART0.RX, DestPeripheral = 0, Transfer type = peripheral -> Memory LPC_GPDMA->CH[2].CONFIG = (9<<1) | (2<<11); // | (1<<14) | (1<<15); // UART0.RX, IE, ITC enabled UART0RxRdPtr = UART0Buffer; //Finally, enable the channel - LPC_GPDMA->CH[2].CONFIG |= 1<<0; // Setup UART1 RX Buffer DMA dmaUART1Rx.destAddr = (int)UART1Buffer; dmaUART1Rx.sourceAddr = (int)&(LPC_UART1->RBR); // sizeof(UART1Buffer) bytes, 8 bit wide dest./ 8 bit src width, dest. increment // Src/dest burst size is one // TC interrupt enabled from this channel dmaUART1Rx.dmaControl = sizeof(UART1Buffer) | (0x00 <<18) | (0x00<<21) | (1<<27) | (1<<31); dmaUART1Rx.nextNode = (int)&dmaUART1Rx; LPC_GPDMA->CH[0].SRCADDR = dmaUART1Rx.sourceAddr; LPC_GPDMA->CH[0].DESTADDR = dmaUART1Rx.destAddr; LPC_GPDMA->CH[0].CONTROL = dmaUART1Rx.dmaControl; LPC_GPDMA->CH[0].LLI = (int)&dmaUART1Rx; //Lower bits must be 0 //Set up all relevant bits //SrcPeripheral = UART1.RX, DestPeripheral = 0, Transfer type = peripheral -> Memory LPC_GPDMA->CH[0].CONFIG = (11<<1) | (2<<11); // UART1.RX //Finally, enable the channel - UART1RxRdPtr = UART1Buffer; LPC_GPDMA->CH[0].CONFIG |= 1<<0; // Enable Interrupts from DMAC // NVIC_EnableIRQ(DMA_IRQn); } const typeSerialPort UsbPort = { dmaUart0SendString, // Function to send a string NULL, // Function to send a buffer of n bytes UART0_Getchar, // Function to receive a char isUART0CharAvail, // Function to check for availability of a char UsbWaitTx // Function to wait for all xmit to complete... };