Getting PIT to trigger SPI DMA transmit to a DAC (i.mxrt 1060)

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

Getting PIT to trigger SPI DMA transmit to a DAC (i.mxrt 1060)

Jump to solution
857 Views
matt0x000C
Contributor II

Hi,

My goal is to setup a DAC to output a value with a known, consistent update rate. 

I'm trying to use the Config Tools, Peripherals tool to setup a SPI transmit via DMA on a Periodic Interrupt Timer (PIT). 

So far I've figured out:

  1. Using the config tools Peripherals tool, I have my LPSPI channel configured, my DMA channel configured, and a PIT configured. It generates a nice peripherals.c for me: 

 

const lpspi_master_config_t LPSPI4_config = {
  .baudRate = 500000UL,
  .bitsPerFrame = 16UL,
  .cpol = kLPSPI_ClockPolarityActiveHigh,
  .cpha = kLPSPI_ClockPhaseFirstEdge,
  .direction = kLPSPI_MsbFirst,
  .pcsToSckDelayInNanoSec = 1000UL,
  .lastSckToPcsDelayInNanoSec = 1000UL,
  .betweenTransferDelayInNanoSec = 1000UL,
  .whichPcs = kLPSPI_Pcs0,
  .pcsActiveHighOrLow = kLPSPI_PcsActiveLow,
  .pinCfg = kLPSPI_SdiInSdoOut,
  .dataOutConfig = kLpspiDataOutRetained
};
edma_handle_t LPSPI4_RX_Handle;
edma_handle_t LPSPI4_TX_Handle;
lpspi_master_edma_handle_t LPSPI4_handle;

static void LPSPI4_init(void) {
  LPSPI_MasterInit(LPSPI4_PERIPHERAL, &LPSPI4_config, LPSPI4_CLOCK_FREQ);
  /* Set the source kDmaRequestMuxLPSPI4Rx request in the DMAMUX */
  DMAMUX_SetSource(LPSPI4_RX_DMAMUX_BASEADDR, LPSPI4_RX_DMA_CHANNEL, LPSPI4_RX_DMA_REQUEST);
  /* Enable the channel 3 in the DMAMUX */
  DMAMUX_EnableChannel(LPSPI4_RX_DMAMUX_BASEADDR, LPSPI4_RX_DMA_CHANNEL);
  /* Set the source kDmaRequestMuxLPSPI4Tx request in the DMAMUX */
  DMAMUX_SetSource(LPSPI4_TX_DMAMUX_BASEADDR, LPSPI4_TX_DMA_CHANNEL, LPSPI4_TX_DMA_REQUEST);
  /* Set the DMA channel 2 periodic trigger */
  DMAMUX_EnablePeriodTrigger(LPSPI4_TX_DMAMUX_BASEADDR, LPSPI4_TX_DMA_CHANNEL);
  /* Enable the channel 2 in the DMAMUX */
  DMAMUX_EnableChannel(LPSPI4_TX_DMAMUX_BASEADDR, LPSPI4_TX_DMA_CHANNEL);
  /* Create the eDMA LPSPI4_RX_Handle handle */
  EDMA_CreateHandle(&LPSPI4_RX_Handle, LPSPI4_RX_DMA_BASEADDR, LPSPI4_RX_DMA_CHANNEL);
  /* Create the eDMA LPSPI4_TX_Handle handle */
  EDMA_CreateHandle(&LPSPI4_TX_Handle, LPSPI4_TX_DMA_BASEADDR, LPSPI4_TX_DMA_CHANNEL);
  LPSPI_MasterTransferCreateHandleEDMA(LPSPI4_PERIPHERAL, &LPSPI4_handle, LPSPI4_MasterUserCallback, NULL, &LPSPI4_RX_Handle, &LPSPI4_TX_Handle);​

 

  • I've looked at the lpspi_edma_b2b_transfer_master SDK example, and from that I see 

 

LPSPI_MasterTransferEDMA(EXAMPLE_LPSPI_MASTER_BASEADDR, &g_m_edma_handle, &masterXfer);​

 

 

My question is, do I use LPSPI_Master_transferEDMA() or a similar function somewhere to initiate the transfers that should be triggered by the PIT interrupt? If so, where does that function get placed (for example, does it belong in a PIT interrupt handler?) Or is one of the pointers setup in peripherals.c already intended to point to the transfer data?

 

iMXRT1060 

 

Labels (1)
0 Kudos
1 Solution
695 Views
matt0x000C
Contributor II

Hi @Miguel04 

I have this now working as expected. 

My question is, do I use LPSPI_Master_transferEDMA()or a similar function somewhere to initiate the transfers that should be triggered by the PIT interrupt? If so, where does that function get placed (for example, does it belong in a PIT interrupt handler?) Or is one of the pointers setup in peripherals.c already intended to point to the transfer data?

So, the answer is yes, use LPSPI_Master_transferEDMA() in the application code. With the PIT peripheral configured, and the SPI peripheral set to Periodic Trigger Enable, the data will begin transferring when the PIT is started with PIT_StartTimer(). Once started, the SPI packets will be transmitted with the CS asserting at the rate specified in the PIT configuration. This all seems to work without any interrupt service routine code. 

The problem I was encountering was:

lpspi_transfer_t masterXfer;
masterXfer.configFlags = kLPSPI_Pcs0;

and it seems it should have been:

masterXfer.configFlags = kLPSPI_MasterPcs0;

 

View solution in original post

Tags (5)
0 Kudos
4 Replies
696 Views
matt0x000C
Contributor II

Hi @Miguel04 

I have this now working as expected. 

My question is, do I use LPSPI_Master_transferEDMA()or a similar function somewhere to initiate the transfers that should be triggered by the PIT interrupt? If so, where does that function get placed (for example, does it belong in a PIT interrupt handler?) Or is one of the pointers setup in peripherals.c already intended to point to the transfer data?

So, the answer is yes, use LPSPI_Master_transferEDMA() in the application code. With the PIT peripheral configured, and the SPI peripheral set to Periodic Trigger Enable, the data will begin transferring when the PIT is started with PIT_StartTimer(). Once started, the SPI packets will be transmitted with the CS asserting at the rate specified in the PIT configuration. This all seems to work without any interrupt service routine code. 

The problem I was encountering was:

lpspi_transfer_t masterXfer;
masterXfer.configFlags = kLPSPI_Pcs0;

and it seems it should have been:

masterXfer.configFlags = kLPSPI_MasterPcs0;

 

Tags (5)
0 Kudos
834 Views
Miguel04
NXP TechSupport
NXP TechSupport

Hi @matt0x000C 

Yes you can call the LPSPI_Master_transferEDMA() function on the PIT interrupt handler or you can use a boolean variable that is toggled when the PIT interrupt happens and then call LPSPI_Master_transferEDMA().

My recommendation are:

  1. To avoid any configuration issues, use the SDK drivers and configuration.
  2. Test indiviudally every peripheral to find pissible errors easier and then do your tests with all the project together.

Let me know if you have another question.

Best Regards, Miguel.

 

0 Kudos
810 Views
matt0x000C
Contributor II

Hi, thanks for getting back to me!

Ok, so if I use a boolean set by the PIT interrupt handler, I have something like this:

		if (PIT_GetStatusFlags(PIT, kPIT_Chnl_0) == kPIT_TimerFlag) // if the interrupt triggered...
		{
			DAC_Load_Pin_Toggle();

			// Send the next DAC value to its register over SPI
			isTransferCompleted = false;
			LPSPI_MasterTransferEDMA(LPSPI4_PERIPHERAL, &LPSPI4_handle, &masterXfer);
			PIT_ClearStatusFlags(PIT, kPIT_Chnl_0, kPIT_TimerFlag); // clear the interrupt flag

			// Wait until transfer completed
			while (!isTransferCompleted) 
			{
			}
		}

 

The problem with this method is the time from when the DAC_Load_Toggle() to when the SPI transfer initiates is about 100us. So, if I want to set a GPIO bit and send 16bits via SPI, it seems I will be limited to a 100us update rate (unless I'm doing something wrong?).

Looking at this forum post it seems like there is a way to just link the DMA to the SPI output via the PIT without processor intervention, but I can't seem to get this method to work.

0 Kudos
776 Views
Miguel04
NXP TechSupport
NXP TechSupport

Hi @matt0x000C 

I'll look into this and let you know what I found, meanwhile if you can share with me more details of what you did and why you think you are missing would help. Also I found this other posts that could help you.

AN4639

Post1

Post2

Post3

Best Regards, Miguel.

0 Kudos