Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 5 of 5

Thread: Hardware triggered DMA data transfer setup problem

  1. #1

    Hardware triggered DMA data transfer setup problem

    Greetiungs fellow programmers.

    I encountered a problem, while trying to hook a DMA channel to an SPI DMA request on a Teensy 3.1 board. The program below is supposed to work in the following way:
    • On falling edge on the MISO pin, clock out 4 8-bit frames
    • Each time data is read into the RX FIFO make a DMA request
    • The DMA should move the frame from the top of the FIFO to the next buffer entry (buf)
    • Repeat until the end of the buffer is reached
    • Start overwriting buffer entries from the beginning

    Essentially I am trying to create an extended FIFO for my SPI data. The program runs smoothly until I try to connect the SPI DMA request to the DMA channel in this line:
    Code:
    DMAMUX0_CHCFG0 = DMAMUX_SOURCE_SPI0_RX | DMAMUX_ENABLE;
    Probably not the command itself is the problem, but rather some other configuration I missed. The problem is, upon inseting this line into the code, the device stops serial communication, so I can't really start debugging. It's the first time I've dealt with this kind of technology in microcontrollers, so I would not be surprised if I oversaw or misunderstood some aspects.

    I would be very grateful for any support or ideas on the issue.
    Thanks in advance and have a nice day,
    Chrono.

    Code:
    const int bufSize = 1024;	 // buffer size for receive buffer
    char buf[bufSize]; // receive buffer for async transfer
    int bufEntrySize = sizeof(*buf); // size of one buf entry in bit
     
    void portc_isr()
    {
    	SPI0_MCR |= SPI_MCR_CLR_RXF; // clear spi0 rx fifo
    	for (int i = 0; i < 4; i++)  // clock out 32 bit from slave
    	{
    		SPI0_SR |= SPI_SR_TCF;  // clear spi0 transfer complete flag
    		SPI0_PUSHR = 0xFF; // send "don't care" byte
    		while (!(SPI0_SR & SPI_SR_TCF)); // wait for spi transfer to complete
    	}
    
    	PORTC_ISFR = (1 << 7);	 // clear portc7 interrupt flag
    	NVIC_CLEAR_PENDING(IRQ_PORTC); // clear portc pending interrupts
    }
    
    void setup() 
    {
    	// digital pin config
    	PORTC_PCR7 = PORT_PCR_MUX(2) | PORT_PCR_IRQC(10); // configure portc7 as miso and interrupt on falling edge
    	NVIC_ENABLE_IRQ(IRQ_PORTC); // enable portc interrupt
    	GPIOC_PDDR = (1 << 5); // make portc5 an input
    	PORTC_PCR5 = PORT_PCR_DSE | PORT_PCR_MUX(2); // configure portc5 as sin
    
    	// spi config
    	SIM_SCGC6 |= SIM_SCGC6_SPI0; // activate spi0 clock
    	SPI0_MCR = SPI_MCR_MSTR;	 // enter spi0 master mode
    	SPI0_CTAR0 = SPI_CTAR_FMSZ(7) | SPI_CTAR_DBR; // spi0 frame size 8 bit and double baud rate
    	SPI0_RSER = SPI_RSER_RFDF_RE | SPI_RSER_RFDF_DIRS; // enable drain rx fifo dma request
    
    	// dma config	
    	SIM_SCGC7 |= SIM_SCGC7_DMA;	// enable dma clock
    	SIM_SCGC6 |= SIM_SCGC6_DMAMUX; // enable dmamux clock
    	DMAMUX0_CHCFG0 = DMAMUX_DISABLE; // disable dmamux
    	DMA_CR = 0; // use default settings
    	DMA_TCD0_SADDR = &SPI0_POPR; // set dma source to spi pop rx fifo register
    	DMA_TCD0_SOFF = 0; // don't increment source pointer
    	DMA_TCD0_ATTR = DMA_TCD_ATTR_SSIZE(DMA_TCD_ATTR_SIZE_8BIT) | // source data size is 8 bit
    					DMA_TCD_ATTR_DSIZE(DMA_TCD_ATTR_SIZE_8BIT); // destination data size is 8 bit
    	DMA_TCD0_NBYTES_MLNO = 1; // transfer one byte per request
    	DMA_TCD0_SLAST = 0; // don't change source pointer after major loop
    	DMA_TCD0_DADDR = buf; // set destination address to buffer
    	DMA_TCD0_DOFF = bufEntrySize; // increment destination pointer by 8 bit each time
    	DMA_TCD0_BITER_ELINKNO = DMA_TCD0_CITER_ELINKNO = bufSize; // do buf size major iterations
    	DMA_TCD0_DLASTSGA = -sizeof(buf); // return to beginning of buf after major loop finished
    	DMA_TCD0_CSR = DMA_TCD_CSR_DREQ; // clear request once served
    	DMAMUX0_CHCFG0 = DMAMUX_SOURCE_SPI0_RX | DMAMUX_ENABLE; // set spi0 as source of dma requests and enable mux	
    	DMA_SERQ = DMA_SERQ_SERQ(0); // enable requests for channel 0
    }
    
    void loop() 
    {
    
    }
    Last edited by Chronologist; 07-20-2015 at 07:27 AM. Reason: Fixed offset size and removed DMA interrupt

  2. #2
    Ok,
    I figured out one mistake. I gave the offset in bit instead of byte. That probably crashed the CPU. Still the DMA doesn't seem to resond to the SPIs requests.
    Last edited by Chronologist; 07-19-2015 at 07:01 PM.

  3. #3
    Senior Member
    Join Date
    Aug 2013
    Location
    Gothenburg, Sweden
    Posts
    419
    You enable the DMA intrerrupt, but I cannot see a interrupt handler for the DMA.

    The whole is a bit confusing, so the pin ISR writes a block of data to SPI and the DMA should
    transfer the data that is shifted in to the buffer, is that what you intend ?

    Do you need a DMA interrupt for that ?

  4. #4
    You are right about the DMA interrupt. I use it for debug purposes and forgot to take out the NVIC command/leave the ISR in.

    And yes, when the MISO port detects an edge it write one byte out on the SPI, which has no other effect than to cycle one byte out of my slave device. I already know that this part is working. What I am having trouble with is setting up the DMA/DMAMUX so that the SPI can request to have the incoming bytes pulled from the RX FIFO.

  5. #5
    Ok, seems I got it working now.
    Another mistake of mine was enabling DMA_TCD_CSR_DREQ, this cleared the request flag and thus prevented any work to be done. In case anyone is interested, I provide the working code.

    Greets,
    Chrono.

    Code:
    const int bufSize = 1024; // buffer size for receive buffer
    char buf[bufSize]; // receive buffer for async transfer
    int bufEntrySize = sizeof(*buf); // size of one buf entry in bit
    
    void portc_isr()
    {
    	SPI0_MCR |= SPI_MCR_CLR_RXF; // clear spi0 rx fifo
    	for (int i = 0; i < 4; i++) // clock out 24 bit from adc
    	{
    		SPI0_SR |= SPI_SR_TCF; // clear spi0 transfer complete flag
    		SPI0_PUSHR = 0xFF; // send "don't care" byte
    		while (!(SPI0_SR & SPI_SR_TCF)); // wait for spi transfer to complete
    	}
    
    	PORTC_ISFR = (1 << 7); // clear portc7 interrupt flag
    	NVIC_CLEAR_PENDING(IRQ_PORTC); // clear portc pending interrupts
    }
    
    void dma_ch0_isr() // this isr triggers whenever bufLen frames were transfered
    {
            // do some work with the data or set some flags
    	DMA_CINT = DMA_CINT_CAIR;
    }
    
    void setup() 
    {
    	__disable_irq();
    
    	// digital pin config
    	PORTC_PCR7 = PORT_PCR_MUX(2) | PORT_PCR_IRQC(10); // configure portc7 as miso and interrupt on falling edge
    	GPIOC_PDDR = (1 << 5); // make portc5 an input
    	PORTC_PCR5 = PORT_PCR_DSE | PORT_PCR_MUX(2); // configure portc5 as sin
    	NVIC_ENABLE_IRQ(IRQ_PORTC); // enable portc interrupt
    
    	// spi config
    	SIM_SCGC6 |= SIM_SCGC6_SPI0; // activate spi0 clock
    	SPI0_MCR = SPI_MCR_MSTR; // enter spi0 master mode
    	SPI0_CTAR0 = SPI_CTAR_FMSZ(7) | SPI_CTAR_DBR; // spi0 frame size 8 bit and double baud rate
    	SPI0_RSER = SPI_RSER_RFDF_RE | SPI_RSER_RFDF_DIRS; // enable drain rx fifo dma request
    
    	// dma config	
    	SIM_SCGC7 |= SIM_SCGC7_DMA; // enable dma clock
    	SIM_SCGC6 |= SIM_SCGC6_DMAMUX; // enable dmamux clock
    	DMAMUX0_CHCFG0 = DMAMUX_DISABLE; // disable dmamux
    	DMAMUX0_CHCFG0 = DMAMUX_SOURCE_SPI0_RX | DMAMUX_ENABLE; // set spi0 as source of dma requests and enable mux
    	DMA_SERQ = DMA_SERQ_SERQ(0); // enable requests for channel 0
    	DMA_TCD0_SADDR = &SPI0_POPR; // set dma source to spi pop rx fifo register
    	DMA_TCD0_SOFF = 0; // don't increment source pointer
    	DMA_TCD0_ATTR = DMA_TCD_ATTR_SSIZE(DMA_TCD_ATTR_SIZE_8BIT) | // source data size is 8 bit
    					DMA_TCD_ATTR_DSIZE(DMA_TCD_ATTR_SIZE_8BIT); // destination data size is 8 bit
    	DMA_TCD0_NBYTES_MLNO = 1; // transfer one byte per request
    	DMA_TCD0_SLAST = 0; // don't change source pointer after major loop
    	DMA_TCD0_DADDR = buf; // set destination address to buffer
    	DMA_TCD0_DOFF = bufEntrySize; // increment destination pointer by 1 byte each time
    	DMA_TCD0_BITER_ELINKNO = DMA_TCD0_CITER_ELINKNO = bufSize; // do buf size major iterations
    	DMA_TCD0_DLASTSGA = -sizeof(buf); // return to beginning of buf after major loop finished
    	DMA_TCD0_CSR = DMA_TCD_CSR_INTMAJOR; // clear request once served and activate interrupt on major loop completion
    	NVIC_ENABLE_IRQ(IRQ_DMA_CH0); // attach dma channel 0 interrupt		
    
    	__enable_irq();
    }
    
    void loop() 
    {
    
    }

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •