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

取消
显示结果 
显示  仅  | 搜索替代 
您的意思是: 
已解决

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

跳至解决方案
1,415 次查看
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 

 

标签 (1)
0 项奖励
回复
1 解答
1,253 次查看
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;

 

在原帖中查看解决方案

标记 (5)
0 项奖励
回复
4 回复数
1,254 次查看
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;

 

标记 (5)
0 项奖励
回复
1,392 次查看
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 项奖励
回复
1,368 次查看
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 项奖励
回复
1,334 次查看
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 项奖励
回复