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.
Solved! Go to Solution.
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.
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.
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:
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
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?
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.
Hi Earl
Thank you very much, I successfully got it working!
^Mathias
Glad to serve you :smileywink:.
Have a nice day Mathias,
Earl.