Hi,
I am trying to interface TM4C123G (launch pad - EK-TM4C123GXL board) with 24 bit ADC (ADS1255). Since our application requires the ADC to run @ 7.5KSPS (min), I am trying to explore the possibility of using TIVA DMA controller to interface with the ADC (as against using an interrupt based approach)
I am using the following approach to first configure the ADC and then use DMA to transfer/receive data from the ADC
a) Configure the uDMA controller
b) Use SSI0 (without DMA enabled) to write and read the ADC registers. Set up the ADC for "Read Data Continuously Mode (RDATAC)".
c) Configure the Pin PF4 as DMA trigger enable (ADS1255 DRDYB pin is connected to PF4). In setupDMATrigger() - i have configured the DMA to transfer three bytes to the SSI0 TX register. I can see that this section of the code is working - could monitor the 24 clocks and serial data being shifted out in a scope.
d) The uDMAGPIOTriggerHandler continues to send 3 bytes through SSI0 TX interface every time it receives a trigger (DRDYB falling edge from ADC). No issues here as well.
e) I have setup DMA on SSI0 RX for ping-pong acquisition. The intent here is to receive the data from the ADC directly into the ping-pong buffers. The relevant section of the code is in the function setupSSI0DMA.
In step e), i am not receiving the ADC data into the ping pong buffers. In fact the SSI0RXhandler is not getting triggered at all.
Can you please help review and let me know what i could be possibly missing?
Best regards,
Sridhar
----- Code
#include <stdint.h> #include <stdbool.h> #include "inc/tm4c123gh6pm.h" #include "inc/hw_memmap.h" #include "inc/hw_types.h" #include "inc/hw_gpio.h" #include "inc/hw_uart.h" #include "inc/hw_ssi.h" #include "driverlib/fpu.h" #include "driverlib/sysctl.h" #include "driverlib/gpio.h" #include "driverlib/ssi.h" #include "driverlib/pin_map.h" #include "driverlib/interrupt.h" #include "driverlib/udma.h" // GLOBALS uint8_t prevDRDYBLevel; uint8_t Spi_write_data; // Cmd and Data to configure ADS1255 uint8_t ADCWREG[13] = {0x50, 0x0A, 0x02, 0x01, 0x27, 0xD0, 0xE1, 0x00, 0x00, 0x00, 0x08, 0xAC, 0x44}; // Split read registers command into two parts // Part 1: Command uint8_t ADCCMDREAD[2] = {0x10, 0x0A}; // Part 2: Actual Read uint8_t ADCRREG[11] = {0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; // Read Data Continuously uint8_t RDATCREG = {0x03}; // Ping-Pong Buffer definitions #define UART_TXBUF_SIZE 256 #define SPI_RXBUF_SIZE 256 static uint8_t g_pui8TxBuf[3] = {0xAA, 0xAA, 0xAA}; static uint8_t g_pui8RxPing[SPI_RXBUF_SIZE]; static uint8_t g_pui8RxPong[SPI_RXBUF_SIZE]; // Define errors counters static uint32_t g_ui32BadISR = 0; static uint32_t g_ui32DMAErrCount = 0; // Define transfer counter static uint32_t g_ui32MemXferCount = 0; // Transfer counters static uint32_t g_ui32RxPingCount = 0; static uint32_t g_ui32RxPongCount = 0; // The control table used by the uDMA controller. This table must be aligned to a 1024 byte boundary. #pragma DATA_ALIGN(pui8ControlTable, 1024) uint8_t pui8ControlTable[1024]; // Library error routine #ifdef DEBUG void __error__(char *pcFilename, uint32_t ui32Line) { } #endif // uDMA transfer error handler void uDMAErrorHandler(void) { uint32_t ui32Status; // Check for uDMA error bit ui32Status = uDMAErrorStatusGet(); // If there is a uDMA error, then clear the error and increment the error counter. if(ui32Status) { uDMAErrorStatusClear(); g_ui32DMAErrCount++; } } // PF4 (DRDYB) uDMA Trigger interrupt handler void uDMAGPIOTrigHandler(void) { uint32_t ui32Mode; GPIOIntClear(GPIO_PORTF_BASE, GPIO_INT_DMA); // Check for the primary control structure to indicate complete. ui32Mode = uDMAChannelModeGet(UDMA_CH15_GPIOF); if(ui32Mode == UDMA_MODE_STOP) { // Increment the count of completed transfers. g_ui32MemXferCount++; // Configure it for another transfer. uDMAChannelTransferSet(UDMA_CH15_GPIOF, UDMA_MODE_AUTO, g_pui8TxBuf, (void *)(SSI0_BASE + SSI_O_DR), sizeof(g_pui8TxBuf)); // Initiate another transfer. uDMAChannelEnable(UDMA_CH15_GPIOF); } // If the channel is not stopped, then something is wrong. else { g_ui32BadISR++; } } // SSI0 Receive Interrupt Handler void SSI0RXHandler(void) { uint32_t ui32Status; uint32_t ui32Mode; ui32Status = SSIIntStatus(SSI0_BASE, 1); SSIIntClear(SSI0_BASE, ui32Status); ui32Mode = uDMAChannelModeGet(UDMA_CHANNEL_SSI0RX | UDMA_PRI_SELECT); if(ui32Mode == UDMA_MODE_STOP) { g_ui32RxPingCount++; uDMAChannelTransferSet(UDMA_CHANNEL_SSI0RX | UDMA_PRI_SELECT, UDMA_MODE_PINGPONG, (void *)(SSI0_BASE + SSI_O_DR), g_pui8RxPing, sizeof(g_pui8RxPing)); } ui32Mode = uDMAChannelModeGet(UDMA_CHANNEL_SSI0RX | UDMA_ALT_SELECT); if(ui32Mode == UDMA_MODE_STOP) { g_ui32RxPongCount++; uDMAChannelTransferSet(UDMA_CHANNEL_SSI0RX | UDMA_ALT_SELECT, UDMA_MODE_PINGPONG, (void *)(SSI0_BASE + SSI_O_DR), g_pui8RxPong, sizeof(g_pui8RxPong)); } uDMAChannelEnable(UDMA_CHANNEL_SSI0RX); } void Spi_Read_write_8bit(unsigned char* PBuff,uint16_t Length,unsigned int uiBase) { uint32_t temp = 0; uint8_t temp_count = 0; while(SSIDataGetNonBlocking(SSI0_BASE, &temp)); for(temp_count = 0;temp_count < Length;temp_count++) { SSIDataPut(uiBase, *(PBuff)); SSIDataGet(uiBase, &temp); *(PBuff++) = temp; } } void setupADCUsingSSI0(void) { uint32_t temp; // SPI/SSI Configuration Code Start // Enable SSI0 Peripheral SysCtlPeripheralEnable(SYSCTL_PERIPH_SSI0); SysCtlPeripheralSleepEnable(SYSCTL_PERIPH_SSI0); // Enable the GPIO Port A, as we are using PA[5:2] SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA); SysCtlPeripheralSleepEnable(SYSCTL_PERIPH_GPIOA); SysCtlDelay(10000); // Configure pin muxing GPIOPinConfigure(GPIO_PA2_SSI0CLK); GPIOPinConfigure(GPIO_PA3_SSI0FSS); GPIOPinConfigure(GPIO_PA4_SSI0RX); GPIOPinConfigure(GPIO_PA5_SSI0TX); // Configure the GPIO settings for the SSI pins GPIOPinTypeSSI(GPIO_PORTA_BASE, GPIO_PIN_5 | GPIO_PIN_4 | GPIO_PIN_3 | GPIO_PIN_2); // Configure the SSI mode and setup as master SSIConfigSetExpClk(SSI0_BASE, SysCtlClockGet(), SSI_FRF_MOTO_MODE_1, SSI_MODE_MASTER, 2000000, 8); // Enable the SSI Module SSIEnable(SSI0_BASE); SysCtlDelay(3000000); // Read any residual data from the SSI Port while(SSIDataGetNonBlocking(SSI0_BASE, &temp)) { // Nothing to do here } // Issue ADC Reset Command // Step1: Take CSB Low GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_1, 0x00); Spi_write_data = 0xFE; Spi_Read_write_8bit(&Spi_write_data, 1, SSI0_BASE); SysCtlDelay(10000); // Step2: Write ADC Registers Spi_Read_write_8bit(ADCWREG, 13, SSI0_BASE); SysCtlDelay(10000); // Step3: Write the command to READ Registers Spi_Read_write_8bit(ADCCMDREAD, 2, SSI0_BASE); SysCtlDelay(10000); // Step 4: Read Registers Spi_Read_write_8bit(ADCRREG, 11, SSI0_BASE); SysCtlDelay(10000); //Step 5: Try to catch the DRDBYB falling edge without using interrupts while(!GPIOPinRead(GPIO_PORTF_BASE,GPIO_PIN_4)); prevDRDYBLevel = GPIOPinRead(GPIO_PORTF_BASE,GPIO_PIN_4); while(prevDRDYBLevel) { prevDRDYBLevel = GPIOPinRead(GPIO_PORTF_BASE,GPIO_PIN_4); SysCtlDelay(200); } Spi_Read_write_8bit(&RDATCREG, 1, SSI0_BASE); SysCtlDelay(10000); } void setupDMATrigger(void) { // Note DRDYB is the uDMA Trigger, PF4 // Configure and enable the GPIOF with DMA GPIOPinTypeGPIOInput(GPIO_PORTF_BASE,GPIO_PIN_4); GPIOIntTypeSet(GPIO_PORTF_BASE,GPIO_PIN_4, GPIO_FALLING_EDGE); GPIOPadConfigSet(GPIO_PORTF_BASE,GPIO_PIN_4,GPIO_STRENGTH_2MA,GPIO_PIN_TYPE_STD_WPU); GPIOIntClear(GPIO_PORTF_BASE, GPIO_PIN_4); GPIODMATriggerEnable(GPIO_PORTF_BASE, GPIO_PIN_4); GPIOIntEnable(GPIO_PORTF_BASE, GPIO_INT_DMA); IntEnable(INT_GPIOF); // Place the uDMA channel attributes in a known state. These should already be disabled by default. uDMAChannelAttributeDisable(UDMA_CH15_GPIOF, UDMA_ATTR_USEBURST | UDMA_ATTR_ALTSELECT | (UDMA_ATTR_HIGH_PRIORITY | UDMA_ATTR_REQMASK)); uDMAChannelAssign(UDMA_CH15_GPIOF); uDMAChannelControlSet(UDMA_CH15_GPIOF | UDMA_PRI_SELECT, UDMA_SIZE_8 | UDMA_SRC_INC_8 | UDMA_DST_INC_NONE | UDMA_ARB_4); uDMAChannelTransferSet(UDMA_CH15_GPIOF | UDMA_PRI_SELECT, UDMA_MODE_AUTO, g_pui8TxBuf, (void *)(SSI0_BASE + SSI_O_DR), sizeof(g_pui8TxBuf)); uDMAChannelEnable(UDMA_CH15_GPIOF); } void setupSSI0DMA(void) { // DMA enable for the SSI Module SSIDMAEnable(SSI0_BASE, SSI_DMA_RX); // Receive channel setup for ping and pong uDMAChannelAttributeDisable(UDMA_CHANNEL_SSI0RX, UDMA_ATTR_ALTSELECT | UDMA_ATTR_USEBURST | UDMA_ATTR_HIGH_PRIORITY | UDMA_ATTR_REQMASK); uDMAChannelAssign(UDMA_CH10_SSI0RX); uDMAChannelControlSet(UDMA_CHANNEL_SSI0RX | UDMA_PRI_SELECT, UDMA_SIZE_8 | UDMA_SRC_INC_NONE | UDMA_DST_INC_8 | UDMA_ARB_1); uDMAChannelControlSet(UDMA_CHANNEL_SSI0RX | UDMA_ALT_SELECT, UDMA_SIZE_8 | UDMA_SRC_INC_NONE | UDMA_DST_INC_8 | UDMA_ARB_1); uDMAChannelTransferSet(UDMA_CHANNEL_SSI0RX | UDMA_PRI_SELECT, UDMA_MODE_PINGPONG, (void *)(SSI0_BASE + SSI_O_DR), g_pui8RxPing, sizeof(g_pui8RxPing)); uDMAChannelTransferSet(UDMA_CHANNEL_SSI0RX | UDMA_ALT_SELECT, UDMA_MODE_PINGPONG, (void *)(SSI0_BASE + SSI_O_DR), g_pui8RxPong, sizeof(g_pui8RxPong)); // Enable SSI0 RX DMA uDMAChannelEnable(UDMA_CHANNEL_SSI0RX); SSIIntEnable(SSI0_BASE, SSI_DMARX); } int main(void) { // System clock frequency is 50 MHz => Period is 20ns SysCtlClockSet(SYSCTL_SYSDIV_4|SYSCTL_USE_PLL|SYSCTL_XTAL_16MHZ|SYSCTL_OSC_MAIN); SysCtlPeripheralClockGating(true); IntMasterDisable(); // Enable DMA SysCtlPeripheralEnable(SYSCTL_PERIPH_UDMA); SysCtlPeripheralSleepEnable(SYSCTL_PERIPH_UDMA); uDMAEnable(); IntEnable(INT_UDMAERR); uDMAControlBaseSet(pui8ControlTable); // Configure port F pin 4 as DMA trigger enable SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF); SysCtlPeripheralSleepEnable(SYSCTL_PERIPH_GPIOF); SysCtlDelay(10000); GPIOPinTypeGPIOInput(GPIO_PORTF_BASE,GPIO_PIN_4); // Setup and configure ADC in RDATAC Mode // Do not use DMA yet for SSI0 setupADCUsingSSI0(); // Setup SSI0 RX for Ping-Pong acquisition setupSSI0DMA(); // Setup DMA to have DRDYB enable DMA transfer to SSI0 TX port setupDMATrigger(); IntMasterEnable(); while (1) { } }