Hello
I try would change a IO Pin very time syncron. For this I try to use DMA and the PIT.
To startup use the example twrke18f_edma_memory_to_memory. This example work.
Now I tried to expand this example with the working LPIT example and try to configure the LPIT to trigger the DMA.
But I don't work.
Were is my mistake?
/*******************************************************************************
* Definitions
******************************************************************************/
#define DMAMUX0 DMAMUX
#define EXAMPLE_DMA DMA0
#define EXAMPLE_DMAMUX DMAMUX0
#define BUFF_LENGTH 4U
#define DEMO_LPIT_BASE LPIT0
#define DEMO_LPIT_IRQn LPIT0_Ch0_IRQn
#define DEMO_LPIT_IRQHandler LPIT0_Ch0_IRQHandler
#define LPIT_SOURCECLOCK CLOCK_GetFreq(kCLOCK_ScgSircAsyncDiv2Clk)
/*******************************************************************************
* Prototypes
******************************************************************************/
/*******************************************************************************
* Variables
******************************************************************************/
edma_handle_t g_EDMA_Handle;
volatile bool g_Transfer_Done = false;
/*******************************************************************************
* Code
******************************************************************************/
/* User callback function for EDMA transfer. */
void EDMA_Callback(edma_handle_t *handle, void *param, bool transferDone, uint32_t tcds)
{
if (transferDone)
{
g_Transfer_Done = true;
}
}
void DEMO_LPIT_IRQHandler(void)
{
/* Clear interrupt flag.*/
LPIT_ClearStatusFlags(DEMO_LPIT_BASE, kLPIT_Channel0TimerFlag);
/* Add for ARM errata 838869, affects Cortex-M4, Cortex-M4F Store immediate overlapping
exception return operation might vector to incorrect interrupt */
#if defined __CORTEX_M && (__CORTEX_M == 4U)
__DSB();
#endif
}
AT_NONCACHEABLE_SECTION_INIT(uint32_t srcAddr[BUFF_LENGTH]) = {0x01, 0x02, 0x03, 0x04};
AT_NONCACHEABLE_SECTION_INIT(uint32_t destAddr[BUFF_LENGTH]) = {0x00, 0x00, 0x00, 0x00};
/*!
* @brief Main function
*/
int main(void)
{
uint32_t i = 0;
edma_transfer_config_t transferConfig;
edma_config_t userConfig;
/* Structure of initialize LPIT */
lpit_config_t lpitConfig;
lpit_chnl_params_t lpitChannelConfig;
BOARD_InitPins();
BOARD_BootClockRUN();
BOARD_InitDebugConsole();
/* Configure LPIT */
/* Set the source for the LPIT module */
CLOCK_SetIpSrc(kCLOCK_Lpit0, kCLOCK_IpSrcSircAsync);
/*
* lpitConfig.enableRunInDebug = false;
* lpitConfig.enableRunInDoze = false;
*/
LPIT_GetDefaultConfig(&lpitConfig);
/* Init lpit module */
LPIT_Init(DEMO_LPIT_BASE, &lpitConfig);
lpitChannelConfig.chainChannel = false;
lpitChannelConfig.enableReloadOnTrigger = false;
lpitChannelConfig.enableStartOnTrigger = false;
lpitChannelConfig.enableStopOnTimeout = false;
lpitChannelConfig.timerMode = kLPIT_PeriodicCounter;
/* Set default values for the trigger source */
lpitChannelConfig.triggerSelect = kLPIT_Trigger_TimerChn0;
lpitChannelConfig.triggerSource = kLPIT_TriggerSource_External;
/* Init lpit channel 0 */
LPIT_SetupChannel(DEMO_LPIT_BASE, kLPIT_Chnl_0, &lpitChannelConfig);
/* Set timer period for channel 0 */
LPIT_SetTimerPeriod(DEMO_LPIT_BASE, kLPIT_Chnl_0, USEC_TO_COUNT(500000U, LPIT_SOURCECLOCK));
/* Enable timer interrupts for channel 0 */
LPIT_EnableInterrupts(DEMO_LPIT_BASE, kLPIT_Channel0TimerInterruptEnable);
/* Enable at the NVIC */
EnableIRQ(DEMO_LPIT_IRQn);
/* Print source buffer */
PRINTF("EDMA memory to memory transfer example begin.\r\n\r\n");
PRINTF("Destination Buffer:\r\n");
for (i = 0; i < BUFF_LENGTH; i++)
{
PRINTF("%d\t", destAddr[i]);
}
/* Configure DMAMUX */
DMAMUX_Init(EXAMPLE_DMAMUX);
#if defined(FSL_FEATURE_DMAMUX_HAS_A_ON) && FSL_FEATURE_DMAMUX_HAS_A_ON
DMAMUX_EnableAlwaysOn(EXAMPLE_DMAMUX, 0, true);
#else
// DMAMUX_SetSource(EXAMPLE_DMAMUX, 0, 63);
DMAMUX_SetSource(EXAMPLE_DMAMUX, 0, 60); // LPIT0
#endif /* FSL_FEATURE_DMAMUX_HAS_A_ON */
DMAMUX_EnableChannel(EXAMPLE_DMAMUX, 0);
/* Configure EDMA one shot transfer */
/*
* userConfig.enableRoundRobinArbitration = false;
* userConfig.enableHaltOnError = true;
* userConfig.enableContinuousLinkMode = false;
* userConfig.enableDebugMode = false;
*/
EDMA_GetDefaultConfig(&userConfig);
EDMA_Init(EXAMPLE_DMA, &userConfig);
EDMA_CreateHandle(&g_EDMA_Handle, EXAMPLE_DMA, 0);
EDMA_SetCallback(&g_EDMA_Handle, EDMA_Callback, NULL);
EDMA_PrepareTransfer(&transferConfig, srcAddr, sizeof(srcAddr[0]), destAddr, sizeof(destAddr[0]),
sizeof(srcAddr[0]), sizeof(srcAddr), kEDMA_MemoryToMemory);
EDMA_SubmitTransfer(&g_EDMA_Handle, &transferConfig);
// EDMA_EnableChannelRequest(EXAMPLE_DMA, 0);
DMAMUX_EnablePeriodTrigger(EXAMPLE_DMAMUX, 0);
/* Start channel 0 */
PRINTF("\r\nStarting channel No.0 ...");
LPIT_StartTimer(DEMO_LPIT_BASE, kLPIT_Chnl_0);
// EDMA_StartTransfer(&g_EDMA_Handle);
/* Wait for EDMA transfer finish */
while (g_Transfer_Done != true)
{
}
/* Print destination buffer */
PRINTF("\r\n\r\nEDMA memory to memory transfer example finish.\r\n\r\n");
PRINTF("Destination Buffer:\r\n");
for (i = 0; i < BUFF_LENGTH; i++)
{
PRINTF("%d\t", destAddr[i]);
}
while (1)
{
}
}
I get the LPIT interrupt but never the DMA interrupt and the copy also don't work.
Thanks for your help.
Tobias
Now I have a problem with setup the DMA to use GPIO
EDMA_PrepareTransfer( &dma_transfer_config,
&port_output_value[0], sizeof(uint32_t),
(void *)&( GPIOC->PDOR ), sizeof(uint32_t),
sizeof(uint32_t),
sizeof(uint32_t),
kEDMA_MemoryToPeripheral);
Is this setup correct?
If I use the (void *)&( GPIOC->PDOR ), sizeof(uint32_t), as destination I get a hard fault at startup?
I don't know what I have changed but now it work with the same settings:
EDMA_PrepareTransfer( &dma_transfer_config,
&port_output_value[0], sizeof(uint32_t),
(void *) &( GPIOC->PDOR), sizeof(uint32_t),
sizeof(uint32_t),
sizeof(uint32_t),
kEDMA_MemoryToPeripheral);
The timer trigger the DMA and the DMA write to the port.
Tobias
The DMA setup solution used in the uTasker method works on any port as follows. I don't think &(GPIOC->PDOR) is correct but if you get a hard fault it means that the code doing the setup fails and not that the DMA fails - the DMA would fail if addresses are not correct but not with a hard fault but rather with a channel error.
fnConfigDMA_buffer(PIT_settings->ucPIT,
(DMAMUX0_DMA0_CHCFG_SOURCE_PIT0 + PIT_settings->ucPIT),
sizeof(ulPortBits[PIT_settings->ucPIT]),
&ulPortBits[PIT_settings->ucPIT],
(unsigned long *)(GPIO_BLOCK + (0x040 * PIT_settings->ucPortRef) + 0x00c),
(DMA_DIRECTION_OUTPUT | DMA_LONG_WORDS | DMA_FIXED_ADDRESSES | DMA_NO_MODULO),
0, 0); // source is the port bit and destination is the GPIO toggle register (DMA_FIXED_ADDRESSES and DMA_NO_MODULO are used only by KL parts)
Note that the advantage of fnConfigDMA_buffer() over EDMA_PrepareTransfer() is that it works on all DMA controllers and so existing and otherwise proven code doesn't need to be ported when moving between different Kinetis (or i.MX RT) families.
Regards
Mark
[uTasker project developer for Kinetis and i.MX RT]
Now, with the help of Mark I solved a part of my problem.
Here is my source code for all others with the same problem.
/*******************************************************************************
* Variables
******************************************************************************/
edma_handle_t g_EDMA_Handle;
volatile bool g_Transfer_Done = false;
/*******************************************************************************
* Code
******************************************************************************/
/* User callback function for EDMA transfer. */
void EDMA_Callback(edma_handle_t *handle, void *param, bool transferDone, uint32_t tcds)
{
if (transferDone)
{
g_Transfer_Done = true;
}
}
void DEMO_LPIT_IRQHandler(void)
{
/* Clear interrupt flag.*/
LPIT_ClearStatusFlags(DEMO_LPIT_BASE, kLPIT_Channel0TimerFlag);
/* Add for ARM errata 838869, affects Cortex-M4, Cortex-M4F Store immediate overlapping
exception return operation might vector to incorrect interrupt */
#if defined __CORTEX_M && (__CORTEX_M == 4U)
__DSB();
#endif
}
AT_NONCACHEABLE_SECTION_INIT(uint32_t srcAddr[BUFF_LENGTH]) = {0x01, 0x02, 0x03, 0x04};
AT_NONCACHEABLE_SECTION_INIT(uint32_t destAddr[BUFF_LENGTH]) = {0x00, 0x00, 0x00, 0x00};
/*!
* @brief Main function
*/
int main(void)
{
uint32_t i = 0;
edma_transfer_config_t transferConfig;
edma_config_t userConfig;
/* Structure of initialize LPIT */
lpit_config_t lpitConfig;
lpit_chnl_params_t lpitChannelConfig;
BOARD_InitPins();
BOARD_BootClockRUN();
BOARD_InitDebugConsole();
/* Configure LPIT */
/* Set the source for the LPIT module */
CLOCK_SetIpSrc(kCLOCK_Lpit0, kCLOCK_IpSrcSircAsync);
/*
* lpitConfig.enableRunInDebug = false;
* lpitConfig.enableRunInDoze = false;
*/
LPIT_GetDefaultConfig(&lpitConfig);
/* Init lpit module */
LPIT_Init(DEMO_LPIT_BASE, &lpitConfig);
lpitChannelConfig.chainChannel = false;
lpitChannelConfig.enableReloadOnTrigger = false;
lpitChannelConfig.enableStartOnTrigger = false;
lpitChannelConfig.enableStopOnTimeout = false;
lpitChannelConfig.timerMode = kLPIT_PeriodicCounter;
/* Set default values for the trigger source */
lpitChannelConfig.triggerSelect = kLPIT_Trigger_TimerChn0;
lpitChannelConfig.triggerSource = kLPIT_TriggerSource_External;
/* Init lpit channel 0 */
LPIT_SetupChannel(DEMO_LPIT_BASE, kLPIT_Chnl_0, &lpitChannelConfig);
/* Set timer period for channel 0 */
LPIT_SetTimerPeriod(DEMO_LPIT_BASE, kLPIT_Chnl_0, USEC_TO_COUNT(500000U, LPIT_SOURCECLOCK));
/* Enable timer interrupts for channel 0 */
//LPIT_EnableInterrupts(DEMO_LPIT_BASE, kLPIT_Channel0TimerInterruptEnable);
/* Enable at the NVIC */
//EnableIRQ(DEMO_LPIT_IRQn);
// trigger MUX
TRGMUX_SetTriggerSource(TRGMUX0, kTRGMUX_Dmamux0, kTRGMUX_TriggerInput0, kTRGMUX_SourceLpit0Ch0); // Connect the LPIT trigger source to the DMAMUX
/* Print source buffer */
PRINTF("EDMA memory to memory transfer example begin.\r\n\r\n");
PRINTF("Destination Buffer:\r\n");
for (i = 0; i < BUFF_LENGTH; i++)
{
PRINTF("%d\t", destAddr[i]);
}
/* Configure DMAMUX */
DMAMUX_Init(EXAMPLE_DMAMUX);
#if defined(FSL_FEATURE_DMAMUX_HAS_A_ON) && FSL_FEATURE_DMAMUX_HAS_A_ON
DMAMUX_EnableAlwaysOn(EXAMPLE_DMAMUX, 0, true);
#else
// DMAMUX_SetSource(EXAMPLE_DMAMUX, 0, 63);
DMAMUX_SetSource(EXAMPLE_DMAMUX, 0, 60); // LPIT0
DMAMUX_EnablePeriodTrigger(EXAMPLE_DMAMUX, 0);
#endif /* FSL_FEATURE_DMAMUX_HAS_A_ON */
DMAMUX_EnableChannel(EXAMPLE_DMAMUX, 0);
/* Configure EDMA one shot transfer */
/*
* userConfig.enableRoundRobinArbitration = false;
* userConfig.enableHaltOnError = true;
* userConfig.enableContinuousLinkMode = false;
* userConfig.enableDebugMode = false;
*/
EDMA_GetDefaultConfig(&userConfig);
EDMA_Init(EXAMPLE_DMA, &userConfig);
EDMA_CreateHandle(&g_EDMA_Handle, EXAMPLE_DMA, 0);
EDMA_SetCallback(&g_EDMA_Handle, EDMA_Callback, NULL);
EDMA_PrepareTransfer(&transferConfig, srcAddr, sizeof(srcAddr[0]), destAddr, sizeof(destAddr[0]),
sizeof(srcAddr[0]), sizeof(srcAddr), kEDMA_MemoryToMemory);
EDMA_SubmitTransfer(&g_EDMA_Handle, &transferConfig);
EDMA_EnableChannelRequest(EXAMPLE_DMA, 0);
//EDMA_EnableChannelInterrupts(EXAMPLE_DMA, 0, kEDMA_MajorInterruptEnable);
//EDMA_EnableChannelInterrupts(EXAMPLE_DMA, 0, kEDMA_ErrorInterruptEnable);
//EDMA_EnableChannelInterrupts(EXAMPLE_DMA, 0, kEDMA_HalfInterruptEnable);
/* Start channel 0 */
PRINTF("\r\nStarting channel No.0 ...");
LPIT_StartTimer(DEMO_LPIT_BASE, kLPIT_Chnl_0);
// EDMA_StartTransfer(&g_EDMA_Handle);
/* Wait for EDMA transfer finish */
while (g_Transfer_Done != true)
{
}
/* Print destination buffer */
PRINTF("\r\n\r\nEDMA memory to memory transfer example finish.\r\n\r\n");
PRINTF("Destination Buffer:\r\n");
for (i = 0; i < BUFF_LENGTH; i++)
{
PRINTF("%d\t", destAddr[i]);
}
while (1)
{
}
}
Hi
Have you respected that LPIT channel 0 can trigger only DMA on DMA channel 0 (LPIT 1 -> DMA channel 1; LPIT 2 -> DMA channel 2; LPIT 3 -> DMA channel 3;)?
Regards
Mark
[uTasker project developer for Kinetis and i.MX RT]
I hope I have done this with line 115
DMAMUX_SetSource(EXAMPLE_DMAMUX, 0, 60); // LPIT0
Ore how do I set and enable the DMA source to LPIT correct at MCUExpresso SDK
Schönen guten Tag Tobias
LPIT0 DMA trigger source is 60 but it also needs the trigger enabled (0x40) which means that it needs to be entered with
(60 | 0x40).
The next thing that is probably missing is connecting the LPIT trigger source to the DMAMUX via the TRGMUX.
Since I don't use the SDK you will possibly have to first find an example that shows using the TRGMUX and then add the relevant part to your configuration.
In the uTasker project there is a standardised method to toggle GPIOs from PIT DMA triggers so the details (and Kinetis part differences) don't need to be understood and controlled by users so I can do it using on any Kinetis device with PIT or LPIT (and DMA) or even on iMX RT parts..
PIT_SETUP pit_setup; // PIT interrupt configuration parameters
pit_setup.int_type = PIT_INTERRUPT;
pit_setup.mode = PIT_PERIODIC;
pit_setup.int_handler = 0; // no interrupt due to DMA
pit_setup.ulPortBits = (PORTD_BIT0 | PORTD_BIT7); // toggle PTD0 and PTD7 on each PIT trigger
pit_setup.ucPortRef = PORTD;
pit_setup.ucPIT = 1; // use PIT1
pit_setup.count_delay = PIT_MS_DELAY(20); // 20ms period
fnConfigureInterrupt((void *)&pit_setup); // configure PIT
In the case of TRGMUX being available it controls the routing with the following code (in its DMA driver) which may give you some ideas as to what is needed if you don't find anything relevant in the standard SDK.
if ((usDmaTriggerSource & DMAMUX_CHCFG_TRIG) != 0) { // triggered source (LPIT)
TRGMUX_DMAMUX0 |= ((TRGMUX_SEL_LPIT0_CHANNEL_0 + (usDmaTriggerSource - DMAMUX0_DMA0_CHCFG_SOURCE_PIT0)) << ((usDmaTriggerSource - DMAMUX0_DMA0_CHCFG_SOURCE_PIT0) * 8)); // Connect the LPIT trigger source to the DMAMUX
}
{ // set DMAMUX trigger
DMA_MUX_REGISTER *ptrDMAMUX = (DMA_MUX_REGISTER *)DMAMUX0_BLOCK;
ptrDMAMUX += ucDMA_channel;
*ptrDMAMUX = (DMA_MUX_REGISTER)(usDmaTriggerSource | DMAMUX_CHCFG_ENBL); // connect trigger source to DMA channel
}
In fact only line 2 is needed for the TRGMUX since the following lines are for the standard DMA MUX setting.
Grüsse aus der Schweiz
Mark
[uTasker project developer for Kinetis and i.MX RT]
Hallo Mark
vielen Dank für diene Hilfe.
The LPIT DMA trigger would be enabled with line 134.
DMAMUX_EnablePeriodTrigger(EXAMPLE_DMAMUX, 0);
static inline void DMAMUX_EnablePeriodTrigger(DMAMUX_Type *base, uint32_t channel)
{
assert(channel < (uint32_t)FSL_FEATURE_DMAMUX_MODULE_CHANNEL);
base->CHCFG[channel] |= DMAMUX_CHCFG_TRIG_MASK; // =>(0x40U)
}
The advice with the TRGMUX was very good. I added the line
// trigger MUX
TRGMUX_SetTriggerSource(TRGMUX0, kTRGMUX_Dmamux0, kTRGMUX_TriggerInput0, kTRGMUX_SourceLpit0Ch0); // Connect the LPIT trigger source to the DMAMUX
and also enabled the Channel at DMA
EDMA_EnableChannelRequest(EXAMPLE_DMA, 0);
And know the LPIT trigger the DMA.
Know I have to check how to use the GPIO with the DMA.
Vielen Danke für diene Hilfe.
Grüße
Tobias