Other Parts Discussed in Thread: EK-TM4C123GXL, ADS1255
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)
{
}
}