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.

UDMA SSI TX TRANSFER ON TM4C129

I am new to the Tiva Launchpad and I am working using the TM4C129 launchpad. I want to transfer some data (an array of 64 8-bit elements) using uDMA and the SSI transmit FIFO. I have never done uDMA before so I am not entirely sure if my understanding is correct. I want to transfer the data from the launchpad over SPI to an mbed board which is set up as an SPI slave. As a result, I have tried to configure SSI1 for use on uDMA Channel 11. I have the following code but cannot seem to transfer anything?

#include "spi.h"

uint32_t ucControlTable[256] __attribute__ ((aligned(1024)));

#define CH11PR (11*4)
#define CH11ALT (11*4+128)
#define SSI1_DR ((volatile uint32_t *)0x40009008)

uint8_t *SourcePt;             
volatile uint32_t *DestinationPt;
	
void SPI_Init(void){
	
	SYSCTL->RCGCSSI  |= (1UL<<1);     								
	SYSCTL->RCGCGPIO |= (1UL<<1); 										
	SYSCTL->RCGCGPIO |= (1UL<<4);
  while((SYSCTL->PRGPIO&(1UL<<1)) == 0){};				
  GPIOB_AHB->AFSEL |= ((1UL<<5)|(1UL<<4));           					
  GPIOE_AHB->AFSEL |= ((1UL<<5)|(1UL<<4));
	GPIOB_AHB->DEN   |= ((1UL<<5)|(1UL<<4));
	GPIOE_AHB->DEN   |= ((1UL<<5)|(1UL<<4));
		
  GPIOB_AHB->PCTL = (GPIOB_AHB->PCTL&0xFF00FFFF)+0x00FF0000;
	GPIOE_AHB->PCTL = (GPIOE_AHB->PCTL&0xFF00FFFF)+0x00FF0000;
		
  GPIOB_AHB->AMSEL = 0;               				
	GPIOE_AHB->AMSEL = 0;
  SSI1->CR1        &=~ (1UL<<1);           		
  SSI1->CR1        &=~ (1UL<<2);
		
  SSI1->CPSR = (SSI1->CPSR&~0x000000FF )+120;
  SSI1->CR0 &= ~(0x0000FF00  |       					
                 0x00000080  |         			
                 0x00000040);         					
																								
  SSI1->CR0 = (SSI1->CR0&~0x30)+0;              																							
  SSI1->CR0 = (SSI1->CR0&~0xF)+0x7;         
}

void DMA_SPI_Init (void){
	int i;
	volatile uint32_t delay; 
  for(i=0; i<256; i++){
    ucControlTable[i] = 0;
  }
  SYSCTL->RCGCDMA = 0x01;   
  delay = SYSCTL->RCGCDMA;   
  UDMA->CFG = 0x01;         
  UDMA->CTLBASE = (uint32_t)ucControlTable;
  UDMA->CHMAP1 = (UDMA->CHMAP1&0xFFFF00FF)|0x00001100;
  UDMA->PRIOCLR = ((1UL<<11)|(1UL<<10));    
  UDMA->ALTCLR = ((1UL<<11)|(1UL<<10));      
  UDMA->USEBURSTCLR = ((1UL<<11)|(1UL<<10)); 
  UDMA->REQMASKCLR = ((1UL<<11)|(1UL<<10));  
	
}

void SPI_Interrupt_Enable(void){
	SSI1->IM =(1UL<<5);
	SSI1->DMACTL =((1UL<<1)|(1UL<<0)); 	
	NVIC_EnableIRQ(SSI1_IRQn); 
	SSI1->CR1 |= 0X2; 	
} 

void static setRegular(void){
  ucControlTable[CH11PR]   = (uint32_t)SourcePt;           
  ucControlTable[CH11PR+1] = (uint32_t)DestinationPt;      
  ucControlTable[CH11PR+2] = 0xC00083F1;              
/* DMACHCTL          Bits    Value        Description
   DSTINC            31:30   11           no destination address increment
   DSTSIZE           29:28   00           8-bit destination data size
   SRCINC            27:26   00           8-bit source address increment
   SRCSIZE           25:24   00           8-bit source data size
   reserved          23:18   000000       N/A  
   ARBSIZE           17:14   0010     		Arbitrates after 4 transfer
   XFERSIZE          13:4    0000111111   Transfer count items
   NXTUSEBURST       3       0     			  N/A
   XFERMODE          2:0     001    			Use basic transfer mode
  */
}

void DMA_Start(uint8_t *source){
	
  SourcePt = source+63 ;
  DestinationPt = SSI1_DR;
  setRegular();  
	SPI_Interrupt_Enable();
  UDMA->ENASET |= (1UL<<11);
	
}

void DMA_Stop(void){
	
  UDMA->ENACLR = (1UL<<11);
	NVIC_DisableIRQ(SSI1_IRQn);  
	
}

void SSI1_Handler (void){

		SSI1->DMACTL &=~(1UL<<1);	
		SSI1->ICR = (1UL<<5); 	
		DMA_Stop();
	
}

And my main function simple does the following:

#include "definitions.h"
#include "spi.h"

uint8_t Test[64];
int main (void){
int i;
PLL_Init();
SysTick_Init();
UART_Init();
SPI_Init();
DMA_SPI_Init();
printf ("this is a simple dma spi test:");

for(i=0;i<64;i++){
Test[i]=i;
}
Delay_ms(1);
DMA_Start(Test);

while(1){
}

}

Any help would be greatly appreciated. 

  • Hi Elliott,

    Right now I don't have the time to analyse a code in direct register access. But here are some things:

    Can I ask why you are using Direct register access instead of using TivaWare? Direct register should only be used when speed is critical. TivaWare is easier and faster to use since it's in C and provides fail safes. Programming the registers directly gives a lot of power including power to do a lot of harm.

    Can I also advise you something that will help? Not only to other people analyzing the code but also to you. Comment the code. I doubt you will be able to quickly analyse your code if you leave it and go work on other projects for a week. For anyone else it can be a nightmare, even codes with high level of abstraction languages!
  • Hi Luis,

    It's for a university project that requires a solution that is as fast as possible. 

    Sorry about the lack of comments, I just haven't had time to do it yet. I've looked at the problem a little more closely and it seems that the interrupt handler is entered as soon as the interrupt is enabled. Upon further investigation, it is the uDMA Tx interrupt that is triggering the interrupt but even when I write to the ICR register before enabling the the interrupt, this does not seem to clear the uDMA Tx interrupt. Any thoughts?

  • I managed to fix the issue by using this piece of information in the datasheet:

    "If the μDMA is enabled and has completed a data transfer from the Tx FIFO, the DMATXRIS bit is
    set in the SSIRIS register and cannot be cleared by setting the DMATXIC bit in the SSI Interrupt
    Clear (SSIICR) register. In the DMA Completion Interrupt Service Routine, software must disable
    the μDMA transmit enable to the SSI by clearing the TXDMAE bit in the QSSI DMA Control
    (SSIDMACTL) register and then setting the DMATXIC bit in the SSIICR register. This clears the
    DMA completion interrupt. When the μDMA is needed to transmit more data, the TXDMAE bit must
    be set (enabled) again."
  • I was trying to find the problem and though of that but you do alredy that on the handler.
    SSI1->DMACTL &=~(1UL<<1);
    SSI1->ICR = (1UL<<5);


    So what exactly did you change?
    Also why do you have DMA_Stop(); being called on the handler?
  • So it was in the initial setup. The interrupt needed clearing before the interrupt was enabled.

    Here is the new code:
  • #include "spi.h"

    uint32_t ucControlTable[256] __attribute__ ((aligned(1024))); //Control Table for uDMA

    #define CH11PR (11*4) //Primary channel 11 element
    #define CH11ALT (11*4+128) //Alternate channel 11 element
    #define SSI1_DR ((volatile uint32_t *)0x40009008) //SS1 DATA REGISTER ADDRESS

    volatile uint8_t *SourcePt; //Pointer to Source Address for uDMA SSI transfer
    volatile uint32_t *DestinationPt; //Pointer to Destination Address for uDMA SSI transfer

    void SPI_Init(void){

    SYSCTL->RCGCSSI |= (1UL<<1); //Clock for SSI1
    SYSCTL->RCGCGPIO |= (1UL<<1); //Clock for Port B
    SYSCTL->RCGCGPIO |= (1UL<<4); //Clock for Port E
    while((SYSCTL->PRGPIO&(1UL<<1)) == 0){}; //Ready?
    GPIOB_AHB->AFSEL |= ((1UL<<5)|(1UL<<4)); //Alternate function enabled on PB4/5
    GPIOE_AHB->AFSEL |= ((1UL<<5)|(1UL<<4)); //Alternate function enaled on PE4/5
    GPIOB_AHB->DEN |= ((1UL<<5)|(1UL<<4)); //Digital Enable for PB4/5
    GPIOE_AHB->DEN |= ((1UL<<5)|(1UL<<4)); //Digital Enable for PE4/5

    GPIOB_AHB->PCTL = (GPIOB_AHB->PCTL&0xFF00FFFF)+0x00FF0000; //SSI1 Clk/Fss Enabled
    GPIOE_AHB->PCTL = (GPIOE_AHB->PCTL&0xFF00FFFF)+0x00FF0000; //SSI1 Data0/1 Enabled

    GPIOB_AHB->AMSEL = 0; //No Analog function Port B
    GPIOE_AHB->AMSEL = 0; //No Analog function Port E
    SSI1->CR1 &=~ (1UL<<1); //Disable SSI1
    SSI1->CR1 &=~ (1UL<<2); //Set SSI1 as master

    SSI1->CPSR = (SSI1->CPSR&~0x000000FF )+120; //1MHz Frequency
    SSI1->CR0 &= ~(0x0000FF00 | //SCR=0
    0x00000080 | //SPH=0
    0x00000040); //SP0=0

    SSI1->CR0 = (SSI1->CR0&~0x30)+0; //Freescale Format
    SSI1->CR0 = (SSI1->CR0&~0xF)+0x7; //8-bit data
    }


    void DMA_SPI_Init (void){
    int i;
    volatile uint32_t delay;
    for(i=0; i<256; i++){
    ucControlTable[i] = 0; //clear control table
    }
    SYSCTL->RCGCDMA = 0x01; //uDMA clock
    delay = SYSCTL->RCGCDMA; //wait
    UDMA->CFG = 0x01; //Enable uDMA controller
    UDMA->CTLBASE = (uint32_t)ucControlTable; //Base address for channel control
    UDMA->CHMAP1 = (UDMA->CHMAP1&0xFFFF00FF)|0x00001100; //Channel 11 and 10 used
    UDMA->PRIOCLR = ((1UL<<11)|(1UL<<10)); //Channel 11 and 10 default priority
    UDMA->ALTCLR = ((1UL<<11)|(1UL<<10)); //Channel 11 and 10 primary control
    UDMA->USEBURSTCLR = ((1UL<<11)|(1UL<<10)); //Allow burst and single request response
    UDMA->REQMASKCLR = ((1UL<<11)|(1UL<<10)); //recognise requests for channel 11 and 10

    }

    void SPI_Interrupt_Enable(void){

    SSI1->IM =(1UL<<5); //uDMA transmit interrupt enabled
    SSI1->DMACTL &=~(1UL<<1); //Transmit FIFO disabled to clear interrupt
    SSI1->ICR = (1UL<<5); //Clear interrupt
    SSI1->DMACTL =((1UL<<1)|(1UL<<0)); //Transmit and Receive FIFO enabled
    SSI1->CR1 |= 0X2; //Enable SSI1
    }


    void static setRegular(void){
    ucControlTable[CH11PR] = (uint32_t)SourcePt; //Test Array end pointer
    ucControlTable[CH11PR+1] = (uint32_t)DestinationPt; //SSI1 Data Register
    ucControlTable[CH11PR+2] = 0xC00083F1;
    /* DMACHCTL Bits Value Description
    DSTINC 31:30 11 no destination address increment
    DSTSIZE 29:28 00 8-bit destination data size
    SRCINC 27:26 00 8-bit source address increment
    SRCSIZE 25:24 00 8-bit source data size
    reserved 23:18 000000 N/A
    ARBSIZE 17:14 0010 Arbitrates after 4 transfer
    XFERSIZE 13:4 0000111111 Transfer count items
    NXTUSEBURST 3 0 N/A
    XFERMODE 2:0 001 Use basic transfer mode
    */
    }


    void DMA_Start(volatile uint8_t *source){
    SourcePt = source+63 ; //Source end pointer
    DestinationPt = SSI1_DR; //Pointer to SS1 Data Register
    setRegular(); //Set regular control table
    SPI_Interrupt_Enable(); //Initialise the interrupt
    UDMA->ENASET |= ((1UL<<11)|(1UL<<10)); //Enable channel 11 and 10 of uDMA
    NVIC_EnableIRQ(SSI1_IRQn); //Enable the interrupt for SSI1

    }

    void DMA_Stop(void){

    UDMA->ENACLR = ((1UL<<11)|(1UL<<10)); //Disable Channel 11 and 10 of uDMA
    NVIC_DisableIRQ(SSI1_IRQn); //Disable the interrupt for SSI1

    }

    void SSI1_Handler (void){
    SSI1->DMACTL &=~(1UL<<1); //Transmit FIFO disabled to clear interrupt
    SSI1->ICR = (1UL<<5); //Clear uDMA Tx interrupt
    SSI1->DMACTL =((1UL<<1)|(1UL<<0)); //Enble Channel 10 and 11 of uDMA
    if((ucControlTable[CH11PR+2]&0x0007)==0){ // regular buffer complete?
    setRegular(); // rebuild channel control structure
    DMA_Stop(); //Disable uDMA because transfer has occured
    }
    }
  • Thank you very much for sharing the code :D

    Very few people do that after fixing the issue.
  • Hello Elliot,

    In addition to Luis's VOT, Might I suggest a code code practice is to Reset the peripherals during debug phase. This clears a lot of raw settings in a device HW, that may be an issue in subsequent reruns. Using of SRxxx register in the SYSCTL address phase.

    Regards
    Amit