How to enable interrupts for a I2C eDMA transaction

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

How to enable interrupts for a I2C eDMA transaction

1,273 Views
chrisdetka
Contributor II

Hello.

I'm using an MKL28Z512XXX7 processor.

I have SDK 2.2.

I'm trying to use DMA to send messages out LpI2C2 (transmit only).

With a logic analyzer connected to SCK and SDA, I see that the I2C message is sent.

My problem is that I do not get the callback for my transaction.

Through investigation, I've found that no interrupt fires after the completion of the DMA transfer (at least I can find no signs of an interrupt)

I followed the example in lpi2c_edma_transfer.c

Importantly, after transmission occurs (seen on the logic analyzer), I stop the processor and inspect the DMA0_INT register, I see a value of 1 which, to me, indicates that the DMA transfer is complete. I would expect an interrupt to occur since my DMAx_TCDn_CSR register has a value of 0x8a which indicates that the transfer is done and since INTMAJOR is set, I would expect an interrupt. 

If I call LPI2C_MasterTransferEDMA twice, the second call returns the "BUSY" error code. This is further evidence that I never get the transmit complete interrupt.

Can you offer any advice on how to debug this? Why do I not see interrupts? Which interrupt handler might you expect to be called? LPI2C2_DriverIRQHandler? DMA0_xx_DriverIRQHandler? INTMUX0_x_DriverIRQHandler?

I've been at this for a while so my code includes a lot to "ideas", but none seem helpful. That said, here's the initialization code, feel free to point out where I've gone wrong:

CLOCK_EnableClock(kCLOCK_Lpi2c2); 
LPI2C_MasterGetDefaultConfig(&ledI2cConfig); 
ledI2cConfig.baudRate_Hz = WSNINIT_LED_I2C_BAUD_RATE; 
/* Initialize the LPI2C master peripheral */ 
LPI2C_MasterInit(((LPI2C_Type *)LPI2C2_BASE), &ledI2cConfig, CLOCK_GetIpFreq(kCLOCK_Lpi2c2));
// LPI2C_MasterInit(((LPI2C_Type *)LPI2C2_BASE), &ledI2cConfig, CLOCK_GetFreq(kCLOCK_BusClk));
// CLOCK_EnableClock(kCLOCK_Dma0); 
// CLOCK_EnableClock(kCLOCK_Intmux0); 
// NVIC_SetPriority(DMA0_04_IRQn, 2); 
INTMUX_Init(INTMUX0); 
// INTMUX_SetChannelMode(INTMUX0, 0, kINTMUX_ChannelLogicOR); 
// INTMUX_SetChannelMode(INTMUX0, 1, kINTMUX_ChannelLogicOR); 
// INTMUX_SetChannelMode(INTMUX0, 2, kINTMUX_ChannelLogicOR); 
// INTMUX_SetChannelMode(INTMUX0, 3, kINTMUX_ChannelLogicOR); 
// INTMUX_EnableInterrupt(INTMUX0, 1, DMA0_26_IRQn); 
// INTMUX_EnableInterrupt(INTMUX0, 1, DMA0_37_IRQn); 
INTMUX_EnableInterrupt(INTMUX0, 0, LPI2C2_IRQn); 
// INTMUX_EnableInterrupt(INTMUX0, 1, LPI2C2_IRQn); 
// INTMUX_EnableInterrupt(INTMUX0, 2, LPI2C2_IRQn); 
// INTMUX_EnableInterrupt(INTMUX0, 3, LPI2C2_IRQn); 
for (uint8_t ich = 0; ich < 4; ich++) { 
  for (uint8_t intr = 32; intr < 64; intr++) { 
    INTMUX_EnableInterrupt(INTMUX0, ich, intr); 
  } 
} 
// /* DMAMux init and EDMA init */ 
DMAMUX_Init(DMAMUX0); 
DMAMUX_SetSource(DMAMUX0, 0U, (uint8_t)kDmaRequestMux0LPI2C2Tx); 
DMAMUX_EnableChannel(DMAMUX0, 0); 
DMAMUX_SetSource(DMAMUX0, 1U, (uint8_t)kDmaRequestMux0LPI2C2Tx); 
DMAMUX_SetSource(DMAMUX0, 2U, (uint8_t)kDmaRequestMux0LPI2C2Tx); 
DMAMUX_SetSource(DMAMUX0, 3U, (uint8_t)kDmaRequestMux0LPI2C2Tx); 
DMAMUX_SetSource(DMAMUX0, 4U, (uint8_t)kDmaRequestMux0LPI2C2Tx); 
DMAMUX_SetSource(DMAMUX0, 5U, (uint8_t)kDmaRequestMux0LPI2C2Tx); 
DMAMUX_SetSource(DMAMUX0, 6U, (uint8_t)kDmaRequestMux0LPI2C2Tx); 
DMAMUX_SetSource(DMAMUX0, 7U, (uint8_t)kDmaRequestMux0LPI2C2Tx); 
DMAMUX_EnableChannel(DMAMUX0, 1); 
DMAMUX_EnableChannel(DMAMUX0, 2); 
DMAMUX_EnableChannel(DMAMUX0, 3); 
DMAMUX_EnableChannel(DMAMUX0, 4); 
DMAMUX_EnableChannel(DMAMUX0, 5); 
DMAMUX_EnableChannel(DMAMUX0, 6); 
DMAMUX_EnableChannel(DMAMUX0, 7); 
EDMA_GetDefaultConfig(&ledEdmaConfig); 
EDMA_Init(DMA0, &ledEdmaConfig); 
/* Create the EDMA channel handles */ 
EDMA_CreateHandle(&ledEdmaRxHandle, DMA0, 7); 
EDMA_CreateHandle(&ledEdmaTxHandle, DMA0, 0); 
EnableIRQ(LPI2C2_IRQn); 
CLOCK_SetIpSrc(kCLOCK_Lpi2c2, kCLOCK_IpSrcSircAsync); /
* Create the LPI2C master DMA driver handle */ 
LPI2C_MasterCreateEDMAHandle(((LPI2C_Type *)LPI2C2_BASE), &ledEdmaMasterHandle, &ledEdmaRxHandle, &ledEdmaTxHandle, &UiTaskFactory_LedCallback, NULL); 
// LPI2C_MasterCreateEDMAHandle(((LPI2C_Type *)LPI2C2_BASE), &ledEdmaMasterHandle, NULL, &ledEdmaTxHandle, &UiTaskFactory_LedCallback, NULL); 
// LPI2C_MasterEnableInterrupts(((LPI2C_Type *)LPI2C2_BASE), 0x1U); 

Then I have this code to send a message:

ledTransfer.slaveAddress = ISSIDRIVER_I2C_SLAVE_ADDRESS; 
ledTransfer.direction = kLPI2C_Write; 
ledTransfer.subaddress = 0; 
ledTransfer.subaddressSize = 0; 
ledTransfer.data = message; 
ledTransfer.dataSize = length; 
ledTransfer.flags = kLPI2C_TransferDefaultFlag; 
/* Send master non-blocking data to slave */ 
status_t reVal = LPI2C_MasterTransferEDMA((LPI2C_Type *)LPI2C2_BASE, &ledEdmaMasterHandle, &ledTransfer); 
if (reVal != kStatus_Success) { while (1); }
Labels (1)
0 Kudos
4 Replies

1,063 Views
chrisdetka
Contributor II

Hello all. Thank you Alexis and Mark for your time and thoughtful answers. 

I was able to get the I2C DMA working on my system. The problem, I'm embarrassed to say, was that the processor had been configured to globally disable interrupts. When I corrected this configuration, things started working as expected.

Sorry to have troubled you with such a silly oversight.

0 Kudos

1,063 Views
mjbcswitzerland
Specialist V

Hi

It sounds like you have missed enabling the DMA channel's interrupt in the NVIC.

I suspect

EnableIRQ(LPI2C2_IRQn);

is intended enabling the interrupt for the LPI2C2, but I don't now whether works in the KSD library since this is an interrupt uses the INTMUX.

Quite certainly however you need an interrupt from DMA0 so it may just be missing:

EnableIRQ(DMA0_IRQn);

Therefore you'll be interested in the DMA channel handler.

You can get further background details about the MUX interrupt (potentially also needed by your LPI2C) in this video: https://www.youtube.com/watch?v=zKa5BoOhBrg

Regards

Mark


Complete KL28 solutions, training and support:http://www.utasker.com/kinetis.html
- http://www.utasker.com/kinetis/FRDM-KL28Z.html

For solutions, rather than just examples: https://github.com/uTasker/uTasker-Kinetis

1,063 Views
chrisdetka
Contributor II

Thank you for your comments, Mark. I believe your feedback is bringing me closer.

Still not getting the interrupt to conclude the DMA transfer.

Here are more details that you or others might use to guide me further. After an attempted transaction, I pause execution and inspect registers.

The NVIC ISER register has the value 0xF020E003 

- DMA0_04 and DMA0_15 are enabled

- All four INTMUX channels are enabled

The NVIC ISPR register has the value 0xF0000002

- All INTMUX channels have pending interrupts

- DMA0_15 has a pending interrupt

The LPI2C2 registers have these values:

- MIER = 0x3E03 <- Transmit and Receive interrupt enabled

NOTE: I modified the fsl_lpi2c_edma.c driver file to enable LPI2C interrupts when starting a transfer.

The INTMUX registers have these values:

- CHn_CSR = 0x80000000 <- Interrupt pending (this value appears on channel 0 and 1)

- CHn_IER_31_2 = 0xFFFFFFFF <- I got carried away and enabled all interrupts

- CHn_IPR_31_2 = 0x80  <- That's the LPI2C2 interrupt!

- CHn_Vec = 0xDC

The DMA registers are as follows:

- INT = 0x02 <- Interrupt requested on channel 1 (that's the I2C Tx channel that I'm using)

- TCD[1].CSR = 0x8A <- INTMAJOR is enabled and the channel is done

The DMAMUX has these register values:

CHCFG[0] = 0x8d   <- LPI2C2Rx

CHCFG[1] = 0x8e   <- LPI2C2Tx

Based upon my reading of the SDK 2.2 code, I am expecting the EDMA_HandleIRQ to be called. This function maps to DMA0_15_IRQHandler. All indication is that the ISR should be called, but it is not. 

Can you (or anyone reading) suggest why I'm missing this interrupt?

Thank you very much,

Chris

0 Kudos

1,063 Views
Alexis_A
NXP TechSupport
NXP TechSupport

Hi Chris,

The new SDK 2.5 version is already available. I will suggest to check it.

Also, I note that you're selecting the same source for more than one source. I will suggest to only link a DMA channel to his respective source. Linking more than one channel to a single source could cause errors in your implementation.

Best Regards,

Alexis Andalon