This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

TM4C1294NCPDT SSI Slave (SPI lagacy mode)with interrupt based Rx and DMA based Tx

Other Parts Discussed in Thread: TM4C1294NCPDT, TM4C1290NCPDT

Hi,

My application needed  SPI Slave lagacy mode with interrupt based Rx and DMA based Tx.I used demo code UART DMA and modified as per my application needs and checked it on TI launch pad.When a Master(TM4C1294NCPDT lauch pad 1) sends a data to slave(TM4C1294NCPDT lauch pad 2) then the interrupt is getting triggered and able to receive the data from Master.But It is not able to sent(Transmit) data using DMA.It is always going to uDMAErrorHandler(ie there is a bus error)

Summary:

Interrupt based Receive is working but DMA based Transmit is not working

Master is sending a data in every 1sec

 Any help here would be greatly appreciated.

Please find the code below 


uint32_t g_ui32DataRxSSI0[];
uint32_t g_ui32IndexSSI0;
uint32_t g_ui32SPI0intflag;
static uint32_t g_ui32uDMAErrCount = 0;

uint8_t g_ui8TxBuf[]={'S','L','A','V','E',' ','O','N',' ','S','S','I','O'};


#ifdef DEBUG
void
__error__(char *pcFilename, uint32_t ui32Line)
{
}
#endif

//*****************************************************************************
//
// The control table used by the uDMA controller. This table must be aligned
// to a 1024 byte boundary.
//
//*****************************************************************************
#if defined(ewarm)
#pragma data_alignment=1024
uint8_t pui8ControlTable[1024];
#elif defined(ccs)
#pragma DATA_ALIGN(pui8ControlTable, 1024)
uint8_t pui8ControlTable[1024];
#else
uint8_t pui8ControlTable[1024] __attribute__ ((aligned(1024)));
#endif


//-------------------------------------------------------------------------------------------------------------------
void InitSPI0(void)
{

// The SSI0 peripheral must be enabled for use.
MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_SSI0);
MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);

// Configure the pin muxing for SSI0 functions on port A2, A3, A4, and A5.
MAP_GPIOPinConfigure(GPIO_PA2_SSI0CLK);
MAP_GPIOPinConfigure(GPIO_PA3_SSI0FSS);
MAP_GPIOPinConfigure(GPIO_PA4_SSI0XDAT0);
MAP_GPIOPinConfigure(GPIO_PA5_SSI0XDAT1);

//
// Configure the GPIO settings for the SSI pins. This function also gives
// control of these pins to the SSI hardware.
// The pins are assigned as follows:
// PA5 - SSI0Tx
// PA4 - SSI0Rx
// PA3 - SSI0Fss
// PA2 - SSI0CLK

MAP_GPIOPinTypeSSI(GPIO_PORTA_BASE, GPIO_PIN_5 | GPIO_PIN_4 | GPIO_PIN_3 | GPIO_PIN_2);


// Configure and enable the SSI port for SPI slave mode. Use SSI0,
// system clock supply, idle clock level low and active low clock in
// freescale SPI mode, slave mode, MHz SSI frequency, and 16-bit data.


MAP_SSIConfigSetExpClk(SSI0_BASE, DEF_SYSCLK, SSI_FRF_MOTO_MODE_1,SSI_MODE_SLAVE, DEF_SPI_RATE,DEF_BYTE);


MAP_SSIEnable(SSI0_BASE);


}


//-------------------------------------------------------------------------------------------------------------------
void InitSSI0DMATransfer(void)
{
ROM_SSIDMAEnable(SSI0_BASE,SSI_DMA_TX);

ROM_uDMAChannelAttributeEnable(UDMA_CHANNEL_SSI0TX, UDMA_ATTR_USEBURST);
ROM_uDMAChannelControlSet(UDMA_CHANNEL_SSI0TX | UDMA_PRI_SELECT,
UDMA_SIZE_8 | UDMA_SRC_INC_8 |
UDMA_DST_INC_NONE |
UDMA_ARB_4);

ROM_uDMAChannelTransferSet(UDMA_CHANNEL_SSI0TX | UDMA_PRI_SELECT,
UDMA_MODE_BASIC, g_ui8TxBuf,
HWREG(SSI0_BASE + SSI_O_DR),
13);//size of g_ui8TxBuf[]=13


// SSIIntEnable(SSI0_BASE,SSI_RXFF);//Interrupt enable for Rx

SSIIntEnable(SSI0_BASE,SSI_RXFF|SSI_DMATX);//Interrupt enable for Rx and DMA based Tx
IntEnable(INT_SSI0);

// ROM_uDMAChannelEnable(UDMA_CHANNEL_SSI0TX);
SSIDMAEnable(SSI0_BASE,SSI_DMA_TX);

}


//-------------------------------------------------------------------------------------------------------------------
void InitConsole(void)
{

MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);


MAP_GPIOPinConfigure(GPIO_PA0_U0RX);
MAP_GPIOPinConfigure(GPIO_PA1_U0TX);


MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);


MAP_UARTClockSourceSet(UART0_BASE, UART_CLOCK_PIOSC);


MAP_GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);

UARTStdioConfig(0, 115200, 16000000);
}


//-------------------------------------------------------------------------------------------------------------------
void uDMAErrorHandler(void)
{
uint32_t ui32Status;

//
// Check for uDMA error bit
//
ui32Status = ROM_uDMAErrorStatusGet();

//
// If there is a uDMA error, then clear the error and increment
// the error counter.
//
if(ui32Status)
{
ROM_uDMAErrorStatusClear();
g_ui32uDMAErrCount++;
}
}


//-------------------------------------------------------------------------------------------------------------------
void SSI0IntHandler(void)
{
uint32_t ui32tempIndex=0,ui32tempIndex2=0;
//SSI0_BASE=0x4000.8000,SSI_O_SR(offset)=0x00C,SSI_SR_RNE=4
g_ui32SPI0intflag=1;//semaphore
while((HWREG(SSI0_BASE + SSI_O_SR) & SSI_SR_RNE)==4)
{
SSIDataGetNonBlocking(SSI0_BASE, &g_ui32DataRxSSI0[ui32tempIndex++]);
}
g_ui32IndexSSI0=ui32tempIndex;

//DMA interrupt handler
//--------------------------------------------------------------------------------------

if(!ROM_uDMAChannelIsEnabled(UDMA_CHANNEL_SSI0TX))
{
//
// Start another DMA transfer to SSI0 TX.
//
ROM_uDMAChannelTransferSet(UDMA_CHANNEL_SSI0TX | UDMA_PRI_SELECT,
UDMA_MODE_BASIC, g_ui8TxBuf,
HWREG(SSI0_BASE + SSI_O_DR),
13);


ROM_uDMAChannelEnable(UDMA_CHANNEL_SSI0TX);

}
//------------------------------------------------------------------------------------------

}


//-------------------------------------------------------------------------------------------------------------------
int main(void) {


uint32_t ui32status,ui32count,ui32count1,ui32tempIndex1,ui32tempIndex2;

const uint32_t ui32SysClock = MAP_SysCtlClockFreqSet((SYSCTL_XTAL_25MHZ |SYSCTL_OSC_MAIN | SYSCTL_USE_PLL |SYSCTL_CFG_VCO_480), 120000000);//for TM4C129


InitSPI0();


IntMasterEnable();//put it in some other function in systemInit.c file
// DMA configuration
// Enable the uDMA controller at the system level.
MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_UDMA);

// Enable the uDMA controller.
MAP_uDMAEnable();

// Enable the uDMA controller error interrupt. This interrupt will occur if there is a bus error during a transfer.
MAP_IntEnable(INT_UDMAERR);

//
// Point at the control table to use for channel control structures.
//
MAP_uDMAControlBaseSet(pui8ControlTable);

InitConsole();

InitSSI0DMATransfer();



while(1)
{


if(g_ui32SPI0intflag)
{
g_ui32SPI0intflag=0;
UARTprintf("\n Received on SSI0:\n ");
for(ui32tempIndex2=0;ui32tempIndex2<g_ui32IndexSSI0;ui32tempIndex2++)
{
UARTprintf("'%c' ", g_ui32DataRxSSI0[ui32tempIndex2]);

}
}
else{;}

}

return 0;
}

  • Please post any code using the code formatter, it is borderline unreadable without proper indentation, the syntax highlighting also helps.
    You say it goes to the uDMA error handler, which hints that there is a misconfiguration. Check the channel configuration (source and end pointers, and the control "register") and see if it is what you expect it to be - check the uDMA section in the datasheet for details.
  • Dear Veikko,
    Thank you very much for your fast response
    I tried to use syntax highligher which is inbuild in this website for highlighting the syntax ,but it is not working .Also used online code formatter,it is working fine there but when i tried to copy that in to this forum all became black and while.

    Any way I also think the bus error is because of misconfiguration or specific order of the function calls.I also read datasheet for DMA and SPI and also Tivaware pheripheral library .but unfortunatly didnot get any clue.
    Any help here would be greatly appreciated.
  • The effective channel configuration can be seen in pui8ControlTable, at the proper offset that depends on your channel. The control table consists of 2x32x4 uint32_t's - 2 for primary and alternate controls, 32 for number of channels and 4 for tDMAControlTable. Then check the member .ui32Control of the appropriate tDMAControlTable struct and compare the bits with the datasheet register descriptions in chapter 9.5 uDMA Channel Control Structure.

    It's much easier to access the tDMAControlTable struct if you declare the control table a bit different from what the udma code example in the manual does. I'd suggest:

     tDMAControlTable uDMA_ControlTable[64];

    And the syntax highlighter does work, it just doesn't look like it when composing the message - it'll look better when the message is posted.

  • Dear Veikko,
    Please find the code below in which I used syntax highlighter.Currently iam working on DMA control table part ,will update you once it is done uint32_t g_ui32DataRxSSI0[]; uint32_t g_ui32IndexSSI0; uint32_t g_ui32SPI0intflag; static uint32_t g_ui32uDMAErrCount = 0; uint8_t g_ui8TxBuf[]={'S','L','A','V','E',' ','O','N',' ','S','S','I','O'}; #ifdef DEBUG void __error__(char *pcFilename, uint32_t ui32Line) { } #endif //***************************************************************************** // // The control table used by the uDMA controller. This table must be aligned // to a 1024 byte boundary. // //***************************************************************************** #if defined(ewarm) #pragma data_alignment=1024 uint8_t pui8ControlTable[1024]; #elif defined(ccs) #pragma DATA_ALIGN(pui8ControlTable, 1024) uint8_t pui8ControlTable[1024]; #else uint8_t pui8ControlTable[1024] __attribute__ ((aligned(1024))); #endif //------------------------------------------------------------------------------------------------------------------- void InitSPI0(void) { // The SSI0 peripheral must be enabled for use. MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_SSI0); MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA); // Configure the pin muxing for SSI0 functions on port A2, A3, A4, and A5. MAP_GPIOPinConfigure(GPIO_PA2_SSI0CLK); MAP_GPIOPinConfigure(GPIO_PA3_SSI0FSS); MAP_GPIOPinConfigure(GPIO_PA4_SSI0XDAT0); MAP_GPIOPinConfigure(GPIO_PA5_SSI0XDAT1); // // Configure the GPIO settings for the SSI pins. This function also gives // control of these pins to the SSI hardware. // The pins are assigned as follows: // PA5 - SSI0Tx // PA4 - SSI0Rx // PA3 - SSI0Fss // PA2 - SSI0CLK MAP_GPIOPinTypeSSI(GPIO_PORTA_BASE, GPIO_PIN_5 | GPIO_PIN_4 | GPIO_PIN_3 | GPIO_PIN_2); // Configure and enable the SSI port for SPI slave mode. Use SSI0, // system clock supply, idle clock level low and active low clock in // freescale SPI mode, slave mode, MHz SSI frequency, and 16-bit data. MAP_SSIConfigSetExpClk(SSI0_BASE, DEF_SYSCLK, SSI_FRF_MOTO_MODE_1,SSI_MODE_SLAVE, DEF_SPI_RATE,DEF_BYTE); MAP_SSIEnable(SSI0_BASE); } //------------------------------------------------------------------------------------------------------------------- void InitSSI0DMATransfer(void) { ROM_SSIDMAEnable(SSI0_BASE,SSI_DMA_TX); ROM_uDMAChannelAttributeEnable(UDMA_CHANNEL_SSI0TX, UDMA_ATTR_USEBURST); ROM_uDMAChannelControlSet(UDMA_CHANNEL_SSI0TX | UDMA_PRI_SELECT, UDMA_SIZE_8 | UDMA_SRC_INC_8 | UDMA_DST_INC_NONE | UDMA_ARB_4); ROM_uDMAChannelTransferSet(UDMA_CHANNEL_SSI0TX | UDMA_PRI_SELECT, UDMA_MODE_BASIC, g_ui8TxBuf, HWREG(SSI0_BASE + SSI_O_DR), 13);//size of g_ui8TxBuf[]=13 // SSIIntEnable(SSI0_BASE,SSI_RXFF);//Interrupt enable for Rx SSIIntEnable(SSI0_BASE,SSI_RXFF|SSI_DMATX);//Interrupt enable for Rx and DMA based Tx IntEnable(INT_SSI0); // ROM_uDMAChannelEnable(UDMA_CHANNEL_SSI0TX); SSIDMAEnable(SSI0_BASE,SSI_DMA_TX); } //------------------------------------------------------------------------------------------------------------------- void InitConsole(void) { MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA); MAP_GPIOPinConfigure(GPIO_PA0_U0RX); MAP_GPIOPinConfigure(GPIO_PA1_U0TX); MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0); MAP_UARTClockSourceSet(UART0_BASE, UART_CLOCK_PIOSC); MAP_GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1); UARTStdioConfig(0, 115200, 16000000); } //------------------------------------------------------------------------------------------------------------------- void uDMAErrorHandler(void) { uint32_t ui32Status; // // Check for uDMA error bit // ui32Status = ROM_uDMAErrorStatusGet(); // // If there is a uDMA error, then clear the error and increment // the error counter. // if(ui32Status) { ROM_uDMAErrorStatusClear(); g_ui32uDMAErrCount++; } } //------------------------------------------------------------------------------------------------------------------- void SSI0IntHandler(void) { uint32_t ui32tempIndex=0,ui32tempIndex2=0; //SSI0_BASE=0x4000.8000,SSI_O_SR(offset)=0x00C,SSI_SR_RNE=4 g_ui32SPI0intflag=1;//semaphore while((HWREG(SSI0_BASE + SSI_O_SR) & SSI_SR_RNE)==4) { SSIDataGetNonBlocking(SSI0_BASE, &g_ui32DataRxSSI0[ui32tempIndex++]); } g_ui32IndexSSI0=ui32tempIndex; //DMA interrupt handler //-------------------------------------------------------------------------------------- if(!ROM_uDMAChannelIsEnabled(UDMA_CHANNEL_SSI0TX)) { // // Start another DMA transfer to SSI0 TX. // ROM_uDMAChannelTransferSet(UDMA_CHANNEL_SSI0TX | UDMA_PRI_SELECT, UDMA_MODE_BASIC, g_ui8TxBuf, HWREG(SSI0_BASE + SSI_O_DR), 13); ROM_uDMAChannelEnable(UDMA_CHANNEL_SSI0TX); } //------------------------------------------------------------------------------------------ } //------------------------------------------------------------------------------------------------------------------- int main(void) { uint32_t ui32status,ui32count,ui32count1,ui32tempIndex1,ui32tempIndex2; const uint32_t ui32SysClock = MAP_SysCtlClockFreqSet((SYSCTL_XTAL_25MHZ |SYSCTL_OSC_MAIN | SYSCTL_USE_PLL |SYSCTL_CFG_VCO_480), 120000000);//for TM4C129 InitSPI0(); IntMasterEnable();//put it in some other function in systemInit.c file // DMA configuration // Enable the uDMA controller at the system level. MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_UDMA); // Enable the uDMA controller. MAP_uDMAEnable(); // Enable the uDMA controller error interrupt. This interrupt will occur if there is a bus error during a transfer. MAP_IntEnable(INT_UDMAERR); // // Point at the control table to use for channel control structures. // MAP_uDMAControlBaseSet(pui8ControlTable); InitConsole(); InitSSI0DMATransfer(); while(1) { if(g_ui32SPI0intflag) { g_ui32SPI0intflag=0; UARTprintf("\n Received on SSI0:\n "); for(ui32tempIndex2=0;ui32tempIndex2<g_ui32IndexSSI0;ui32tempIndex2++) { UARTprintf("'%c' ", g_ui32DataRxSSI0[ui32tempIndex2]); } } else{;} } return 0; }

  • Dear Veikko,
    I have done detailed study of DMA control table and compared bit by bit with datasheet ,unfortunately couldn't find any bug there.If you refer my code you will find that Iam using Tiva peripheral library functions to access control table,no direct access .So as per my observation probability of making error in control table is less likely(I may be wrong also because Iam new in Tiva series)

    I have a very basic question is this SPI will work with both interrupt and DMA at the same time(ie interrupt based Receive and DMA based Transfer) ?
    Iam using same SSI interrupt handler for both interrupt based Receive and DMA based Transfer.is this ok ?
  • Hello Satheesh,

    There are some defines in the code that is giving compilation error. Can you share the value of the defines?

    Also another suggestion would be to reset the peripherals before enabling them by using

    SysCtlPeripheralReset API call before the clock is enabled to clear away any status from the previous run.

    Regards
    Amit
  • Please insert following header files from  library "TivaWare_C_Series-2.1.0.12573" and also include source file of ssi.c,interrupt.c,uartstudio.c,uart.c
    from the same library

    //following macro is needed so that the ROM calls get correctly mapped at compiler time #define TARGET_IS_TM4C129_RA1 #include <stdbool.h> #include <stdint.h> #include "inc/hw_memmap.h" #include "inc/hw_ints.h" #include "driverlib/rom.h" #include "driverlib/rom_map.h" #include "driverlib/sysctl.h" #include "driverlib/gpio.h" #include "driverlib/pin_map.h" #include "driverlib/ssi.h" #include "driverlib/interrupt.h" #include "driverlib/timer.h" #include "driverlib/udma.h" #include "driverlib/watchdog.h" #include "driverlib/uart.h" #include "driverlib/debug.h" #include "utils/uartstdio.h"

    By including  this you will get rid of the compilation error.

  • Hello Satheesh,

    Alhough it is not the cause of the error you are noticing, as Veikko has suggested, it is better to use a declaration like the one shown below, as it is easy to access and debug the uDMA Control Table.

    tDMAControlTable psDMAControlTable[64];

    I believe some time has to lapse between enabling the uDMA peripheral and accessing it. Can you try to insert a few cycles delay as follows?

    MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_UDMA);
    MAP_SysCtlDelay(10);
    MAP_uDMAEnable();

    I could not find any reference to uDMA channel assignment. Although in this case it might work without explicitly calling the "uDMAChannelAssign()" API, it is recommended to use it.

    Also, I could not understand how the interrupts are being cleared.

    Thanks,

    Sai

  • Hello Sai,
    Here Iam giving you the details of test code ,which you must have got from "Advani, Karan" <karan.advani@ti.com>
     
     
    Hardware used
    1.Two Tiva C series 1294 Launch pad
     
    Configuration
    1.Master is configured in SPI Polling mode(For Tx and Rx) with 10Mbps and master is only doing the SPI task.SPI0 is configured for communication. USB is configured to print received and transmit frame at both side. FreeScale SPI frame format polarity 0, phase 1
     
    2.Slave is Configured in SPI DMA mode(For Tx and Rx)and Slave is only doing the SPI task.SPI0 is configured for communication. USB is configured to print received and transmit frame at both side. FreeScale SPI frame format polarity 0, phase 1

     

     
    Tasks should be performed by master
    It is transmitting 6 bytes of query to slave (0xF1, 0x10, 0x0D, 0x0A, 0xD8, 0xFF) and wait for one second and then write the 15 byte (0xFF) dummy data on line to collect the data from slave. This all is in the polling mode. This is repeated after every 5 second.
     
    Tasks should be performed by Slave
    It is receiving 6 bytes of query from master (0xF1, 0x10, 0x0D, 0x0A, 0xD8, 0xFF) and transmit the 15 byte of data (0xF1, 0x10, 0x0D, 0x0A, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x5E) in response to query. Slave uses the DMA in both Rx and Tx mode.
     
     
     

    When we download the program in both master and the slave, following are the observation

     

    1)      Master is transmitting the 6 bytes (0xF1, 0x10, 0x0D, 0x0A, 0xD8, 0xFF) to slave in polling mode.

    2)      Same bytes is received by the slave in DMA Rx (0xF1, 0x10, 0x0D, 0x0A, 0xD8, 0xFF)

    3)      Those bytes are print on the hyper terminal by slave

    4)      Then slave will transmit the 15 bytes to master in DMA Tx (0xF1, 0x10, 0x0D, 0x0A, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x5E)

    5)      Master receives the 15 bytes but not in correct format (0x02, 0xF1, 0x10, 0x0D, 0x0A, 0x01, 0x02, 0x03, 0x04, 0x0A, 0x5E, 0xF1, 0x10, 0x0D, 0x0A, 0x01)

    6)      Master print the data on hyper terminal

     

    What could be the solution for this to get the correct frame as transmitted by slave

     

     
     
    Constraints of our final product
     
    1.One TI SPI slave(TM4C1290NCPDT ) should communicate with Two Microchip SPI  master(PIC32MX695F512L) at a speed of 10Mbps.
     
    2.TI SPI slave should be work with DMA for both Tx and Rx.
     
    3.TI SPI slave should able to receive upto 105bytes in query and it should be able to transmit upto 105bytes of data in DMA mode.
     
    4.TI SPI slave should be able to communicate with master on FreeScale SPI frame format polarity 0, phase 1