SPI DMA, with fixed timing for DAC

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

SPI DMA, with fixed timing for DAC

Jump to solution
1,402 Views
mathiaskoch
Contributor I

Hi guys

I am currently trying to work out how to get the SPI on my k20 to work with TX only DMA for a DAC

The thing is, i have a buffer containing a signal sampled at some frequency, fs, and i need to recreate this signal using my SPI dac, DAC7811 from TI.

It is a 12 bit dac with 4 control bits, eg i need to do DMA transfers of 16 bits.

But i need to write these 16 bits to the DAC at the same frequency as it is sampled? How can i do this? Until now i have been trying to use a PIT with a periodic trigger of FS and then use an always enabled source for the DMA. It seems to work, but i can quite work out how if i really have to start the SPI module every time in my DMA interrupt? Eg.

SPI0_MCR &= ~SPI_MCR_HALT;

while(!(SPI0_SR & SPI_SR_EOQF));

SPI0_SR |= SPI_SR_EOQF | SPI_SR_TCF ;

SPI0_MCR |= SPI_MCR_HALT;

The perfect end result would be a circular DMA read from my buffer (using SMOD?), that i can start once, it would transfer the entire buffer through DMA (at specified FS) and it would stop itself at the end of the buffer and reload, ready  to be started again.

I really hope you can help me.

Best regards

Mathias.

Labels (1)
0 Kudos
1 Solution
908 Views
EarlOrlando
Senior Contributor II

Hi Mathias,

So you are now able to start a SPI transference through writing a data in the SPIx_PUSHR register? If yes the only missing thing is the DMA configuration.

Please try with the following function with the DMASourceAddress as your buffer address, DMADestinationAddress as the two low bits in the SPIx_PUSHR, SourceIncrement as 2 bytes, DestinationIncrement as 0 bytes, SourceOffset as the size in bytes of your buffer, DestinationOffset as 0, SizeElements as 1 for 2 bytes, Iterations as the amount of samples in your buffer, BytesPerTransaction two bytes and disableRequest as 0 for keep always the transactions enabled:

/*

*     DMAMUX_Channel          Select the DMA channel that you want to configure.

*     SourceTrigger           The trigger to start a transference.

*     DMASourceAddress        The source address.

*     DMADestinationAddress   The destination address.

*     SourceIncrement         The increment in bytes in the source address every time a transaction is done.

*     DestinationIncrement    The increment in bytes in the destination address every time a transaction is done.

*     SourceOffset            The amount of bytes to add or subtract to the source address after a completion of a major loop (could be negative).

*     DestinationOffset       The amount of bytes to add or subtract to the destination address after a completion of a major loop (could be negative).

*     SizeElements            The size of the data to transfer (0 -> 1 byte, 1 -> 2 bytes, 2 -> 4 bytes, 4 -> 16 bytes, 5 -> 32 bytes).

*     Iterations              How many minor loops are in a major loop?

*     BytesPerTransaction     How many bytes are transferred in a minor loop?

*     disableRequest          Do you want to disable the channel requests after a major loop?

*/

void DMA_InitChannel(uint8_t DMAMUX_Channel, uint8_t SourceTrigger, uint32_t DMASourceAddress, uint32_t DMADestinationAddress, \

        uint8_t SourceIncrement, uint8_t DestinationIncrement, int32_t SourceOffset, int32_t DestinationOffset,            \

        uint8_t SizeElements, uint16_t Iterations, uint16_t BytesPerTransaction, uint8_t disableRequest)

{

    SIM_SCGC6 |= SIM_SCGC6_DMAMUX_MASK;

    SIM_SCGC7 |= SIM_SCGC7_DMA_MASK;

    DMAMUX_CHCFG(DMAMUX_Channel) |= DMAMUX_CHCFG_ENBL_MASK | DMAMUX_CHCFG_SOURCE(SourceTrigger);    //Enable the channel and select the source trigger.

    DMA_SERQ |= DMA_SERQ_SERQ(DMAMUX_Channel);            //Enable the request register for this channel.

    DMA_SADDR(DMAMUX_Channel) = DMASourceAddress;        //Select the source address.

    DMA_DADDR(DMAMUX_Channel) = DMADestinationAddress;    //Select the destination address.

    DMA_SOFF(DMAMUX_Channel) = SourceIncrement;            //Source offset (i.e. SOFF = 2 points to elements 0, 2, 4, etc.).

    DMA_ATTR(DMAMUX_Channel) = DMA_ATTR_SSIZE(SizeElements) | DMA_ATTR_DSIZE(SizeElements);    //Select elemnt's size.

    DMA_NBYTES_MLNO(DMAMUX_Channel) = BytesPerTransaction;    //Bytes per transaction.

    DMA_SLAST(DMAMUX_Channel) = SourceOffset;            //Source offset after a major iteration count.

    DMA_DOFF(DMAMUX_Channel) = DestinationIncrement;    //Destination offset (i.e. DOFF = 2 points to elements 0, 2, 4, etc.).

    DMA_CITER_ELINKNO(DMAMUX_Channel) = DMA_CITER_ELINKNO_CITER(Iterations);    //Iterations (minor loops).

    DMA_DLAST_SGA(DMAMUX_Channel) = DestinationOffset;    //Destination offset after a major iteration count.

    DMA_BITER_ELINKNO(DMAMUX_Channel) = DMA_BITER_ELINKNO_BITER(Iterations);    //Iterations (minor loops).

    DMA_CSR(DMAMUX_Channel) = (disableRequest & 0x01) << DMA_CSR_DREQ_SHIFT;

}

You need to configure the eDMA with the operation mode of periodic trigger (where the PIT 0 triggers the eDMA channel 0, the PIT 1 triggers the eDMA channel 1, etc.) through the register DMAMUX_CHCFGn[TRIG] (please see the description of this register).

Best regards,

Earl Orlando.

View solution in original post

0 Kudos
6 Replies
908 Views
EarlOrlando
Senior Contributor II

Hello Mathias,

You can keep the SPI always enabled (i.e. SPIx_MCR[HALT] = 0) and disable the SPI TX FIFO (i.e. register SPIx_MCR[DIS_TXF] = 1). If the SPIx_PUSHR[TXDATA] register is empty there won't be a transference but when it is not empty a SPI transference will start so you can configure a DMA channel with the transfer triggered by the PIT from your buffer to the SPIx_PUSHR[TXDATA] register. This will generate a transference every time the pit triggers the DMA.

The circular buffer is in charge of the DMA, you can configure the DMA with a major loop containing your entire buffer and configure the DMA_TCDn_SLAST register with the negative value of the amount of bytes of your buffer, this will make that after the last transaction in the major loop the source address returns to the beginning of the buffer.

You can configure two interrupts for the DMA channel, one when the half of the buffer is transmitted (DMA_TCDn_CSR[INTHALF]) and another when the buffer is fully transmitted(DMA_TCDn_CSR[INTMAJOR]), this is useful to manage a double buffer.

Best regards,

Earl.

0 Kudos
908 Views
mathiaskoch
Contributor I

Hi Earl

Thank you for your answer!

But i cant seem to make it work with the SPI always enabled (i.e. SPIx_MCR[HALT] = 0). Even without adding in the DMA.

This is my setup code for the SPI:

  if ((SIM_SCGC6 & SIM_SCGC6_SPI0) == 0)

    SIM_SCGC6 |= SIM_SCGC6_SPI0;

  PORTC_PCR4 = PORT_PCR_MUX(2) | PORT_PCR_SRE | PORT_PCR_DSE; // CSn nRF24

  PORTD_PCR6 = PORT_PCR_MUX(2) | PORT_PCR_SRE | PORT_PCR_DSE; // CSn DAC

  PORTC_PCR3 |= PORT_PCR_MUX(1); // CE output

  GPIOC_PDDR |= _BV(3);

  /* PTC5, SPI0_SCK, ALT2 */

  PORTC_PCR5 = PORT_PCR_MUX(2) | PORT_PCR_SRE; // nRF24

  PORTD_PCR1 = PORT_PCR_MUX(2) | PORT_PCR_SRE; // DAC

  /* PTC6, SPI0_SOUT, ALT2, internall pullup */

  PORTC_PCR6 = PORT_PCR_MUX(2) | PORT_PCR_SRE | PORT_PCR_DSE; // nRF24

  PORTD_PCR2 = PORT_PCR_MUX(2) | PORT_PCR_SRE | PORT_PCR_DSE; // DAC

  /* PTC7, SPI0_SIN, ALT2 */

  PORTC_PCR7 = PORT_PCR_MUX(2) | PORT_PCR_PE; // nRF24

  SPI0_MCR = SPI_MCR_MDIS;

  SPI0_MCR = SPI_MCR_MSTR | SPI_MCR_MDIS | SPI_MCR_DIS_TXF | SPI_MCR_DIS_RXF | SPI_MCR_PCSIS(0x01) | SPI_MCR_PCSIS(0x08) | SPI_MCR_HALT;

  SPI0_CTAR1 = SPI_CTAR_FMSZ(7) | SPI_CTAR_PBR(0) | SPI_CTAR_BR(4) | SPI_CTAR_ASC(3); // 1.5 MHz clk

  SPI0_CTAR0 = SPI_CTAR_FMSZ(0xF) | SPI_CTAR_PBR(0) | SPI_CTAR_BR(0) | SPI_CTAR_ASC(0) | SPI_CTAR_CPHA; // 12 MHz clk

  SPI0_RSER = 0;

  /* enable DSPI clocks */

  SPI0_MCR &= ~SPI_MCR_MDIS;

  SPI0_MCR &= ~SPI_MCR_HALT;

And when i add in an entry, eg:

SPI0_PUSHR = (x | SPI_PUSHR_PCS(0x08) | SPI_PUSHR_EOQ | SPI_PUSHR_CTAS(0));

Nothing happens on the SPI pins?

Hope you can hep me -.-'

^Mathias

0 Kudos
908 Views
mathiaskoch
Contributor I

Nevermind about the SPI part, i got that working..

But i cant seem to make the DMA part to work with a PIT.. The PIT is running just fine, but what should i select as source for the DMA?

0 Kudos
909 Views
EarlOrlando
Senior Contributor II

Hi Mathias,

So you are now able to start a SPI transference through writing a data in the SPIx_PUSHR register? If yes the only missing thing is the DMA configuration.

Please try with the following function with the DMASourceAddress as your buffer address, DMADestinationAddress as the two low bits in the SPIx_PUSHR, SourceIncrement as 2 bytes, DestinationIncrement as 0 bytes, SourceOffset as the size in bytes of your buffer, DestinationOffset as 0, SizeElements as 1 for 2 bytes, Iterations as the amount of samples in your buffer, BytesPerTransaction two bytes and disableRequest as 0 for keep always the transactions enabled:

/*

*     DMAMUX_Channel          Select the DMA channel that you want to configure.

*     SourceTrigger           The trigger to start a transference.

*     DMASourceAddress        The source address.

*     DMADestinationAddress   The destination address.

*     SourceIncrement         The increment in bytes in the source address every time a transaction is done.

*     DestinationIncrement    The increment in bytes in the destination address every time a transaction is done.

*     SourceOffset            The amount of bytes to add or subtract to the source address after a completion of a major loop (could be negative).

*     DestinationOffset       The amount of bytes to add or subtract to the destination address after a completion of a major loop (could be negative).

*     SizeElements            The size of the data to transfer (0 -> 1 byte, 1 -> 2 bytes, 2 -> 4 bytes, 4 -> 16 bytes, 5 -> 32 bytes).

*     Iterations              How many minor loops are in a major loop?

*     BytesPerTransaction     How many bytes are transferred in a minor loop?

*     disableRequest          Do you want to disable the channel requests after a major loop?

*/

void DMA_InitChannel(uint8_t DMAMUX_Channel, uint8_t SourceTrigger, uint32_t DMASourceAddress, uint32_t DMADestinationAddress, \

        uint8_t SourceIncrement, uint8_t DestinationIncrement, int32_t SourceOffset, int32_t DestinationOffset,            \

        uint8_t SizeElements, uint16_t Iterations, uint16_t BytesPerTransaction, uint8_t disableRequest)

{

    SIM_SCGC6 |= SIM_SCGC6_DMAMUX_MASK;

    SIM_SCGC7 |= SIM_SCGC7_DMA_MASK;

    DMAMUX_CHCFG(DMAMUX_Channel) |= DMAMUX_CHCFG_ENBL_MASK | DMAMUX_CHCFG_SOURCE(SourceTrigger);    //Enable the channel and select the source trigger.

    DMA_SERQ |= DMA_SERQ_SERQ(DMAMUX_Channel);            //Enable the request register for this channel.

    DMA_SADDR(DMAMUX_Channel) = DMASourceAddress;        //Select the source address.

    DMA_DADDR(DMAMUX_Channel) = DMADestinationAddress;    //Select the destination address.

    DMA_SOFF(DMAMUX_Channel) = SourceIncrement;            //Source offset (i.e. SOFF = 2 points to elements 0, 2, 4, etc.).

    DMA_ATTR(DMAMUX_Channel) = DMA_ATTR_SSIZE(SizeElements) | DMA_ATTR_DSIZE(SizeElements);    //Select elemnt's size.

    DMA_NBYTES_MLNO(DMAMUX_Channel) = BytesPerTransaction;    //Bytes per transaction.

    DMA_SLAST(DMAMUX_Channel) = SourceOffset;            //Source offset after a major iteration count.

    DMA_DOFF(DMAMUX_Channel) = DestinationIncrement;    //Destination offset (i.e. DOFF = 2 points to elements 0, 2, 4, etc.).

    DMA_CITER_ELINKNO(DMAMUX_Channel) = DMA_CITER_ELINKNO_CITER(Iterations);    //Iterations (minor loops).

    DMA_DLAST_SGA(DMAMUX_Channel) = DestinationOffset;    //Destination offset after a major iteration count.

    DMA_BITER_ELINKNO(DMAMUX_Channel) = DMA_BITER_ELINKNO_BITER(Iterations);    //Iterations (minor loops).

    DMA_CSR(DMAMUX_Channel) = (disableRequest & 0x01) << DMA_CSR_DREQ_SHIFT;

}

You need to configure the eDMA with the operation mode of periodic trigger (where the PIT 0 triggers the eDMA channel 0, the PIT 1 triggers the eDMA channel 1, etc.) through the register DMAMUX_CHCFGn[TRIG] (please see the description of this register).

Best regards,

Earl Orlando.

0 Kudos
908 Views
mathiaskoch
Contributor I

Hi Earl

Thank you very much, I successfully got it working!

^Mathias

0 Kudos
908 Views
EarlOrlando
Senior Contributor II

Glad to serve you :smileywink:.

Have a nice day Mathias,

Earl.

0 Kudos