Hello,
I'm a little confused about how PIT events and DMA relate, on the FRDM-KL25
What I'm looking to achieve is this ...
Ideally, on the expiration of a PIT timer I want to asynchronously do two things:
However, I'm not completely sure if this is possible.
Firstly, reading the application note "Application of Asynchronous DMA operation" (for the KL25), it lists in section 3.2 the modules that are capable of generating DMA requests, PIT is not among them.
Secondly, referring to pages 87-8 of the "KL25 Sub Family Reference Manual" it specifies that "... PIT generates periodic trigger events to the DMA channel mux ...".
So, can I or can't I use PIT events to initiate a DMA transfer?
I'm working mostly just with CMSIS and occasionally some Freescale libraries on this project.
Any help greatly appreciated!
Paul Swanson#
Solved! Go to Solution.
Hi Paul
PITs can trigger DMA, with the following restrictions/differences:
1. PIT0 can only trigger DMA channel 0, PIT1 can only trigger DMA channe 1, etc.
2. Its trigger source needs to be set to the "always enabled" DMA MUX channel and its channel trigger enabled
From a programming point of view to use PIT0 you must use DMA channel 0 and set the DMA MUX to (eg. for KL25) to 58 (0x3a) plus 0x40 (enable channel trigger) and enable the DMA mode in the PIT.
To make this more or less transparent, define the PIT0 trigger source as (58 | 0x40) and then standard DMA configuration can be used without needing to consider the special difference.
Regards
Mark
Complete Kinetis solutions for professional needs, training and support: http://www.utasker.com/kinetis.html
Kinetis KL25, KL26, KL27, KL28, KL43, KL46, KL82
- http://http://www.utasker.com/kinetis/FRDM-KL25Z.html
- http://www.utasker.com/kinetis/TWR-KL25Z48M.html
- http://www.utasker.com/kinetis/FRDM-KL26Z.html
- http://www.utasker.com/kinetis/TEENSY_LC.html
- http://www.utasker.com/kinetis/FRDM-KL27Z.html
- http://www.utasker.com/kinetis/Capuccino-KL27.html
- http://www.utasker.com/kinetis/FRDM-KL28Z.html
- http://www.utasker.com/kinetis/FRDM-KL43Z.html
- http://www.utasker.com/kinetis/TWR-KL43Z48M.html
- http://www.utasker.com/kinetis/FRDM-KL46Z.html
- http://www.utasker.com/kinetis/TWR-KL46Z48M.html
- http://www.utasker.com/kinetis/FRDM-KL82Z.html
uTasker: supporting >1'000 registered Kinetis users get products faster and cheaper to market
Request Free emergency remote desk-top consulting at http://www.utasker.com/services.html
Open Source version at https://github.com/uTasker/uTasker-Kinetis
Hi Paul
PITs can trigger DMA, with the following restrictions/differences:
1. PIT0 can only trigger DMA channel 0, PIT1 can only trigger DMA channe 1, etc.
2. Its trigger source needs to be set to the "always enabled" DMA MUX channel and its channel trigger enabled
From a programming point of view to use PIT0 you must use DMA channel 0 and set the DMA MUX to (eg. for KL25) to 58 (0x3a) plus 0x40 (enable channel trigger) and enable the DMA mode in the PIT.
To make this more or less transparent, define the PIT0 trigger source as (58 | 0x40) and then standard DMA configuration can be used without needing to consider the special difference.
Regards
Mark
Complete Kinetis solutions for professional needs, training and support: http://www.utasker.com/kinetis.html
Kinetis KL25, KL26, KL27, KL28, KL43, KL46, KL82
- http://http://www.utasker.com/kinetis/FRDM-KL25Z.html
- http://www.utasker.com/kinetis/TWR-KL25Z48M.html
- http://www.utasker.com/kinetis/FRDM-KL26Z.html
- http://www.utasker.com/kinetis/TEENSY_LC.html
- http://www.utasker.com/kinetis/FRDM-KL27Z.html
- http://www.utasker.com/kinetis/Capuccino-KL27.html
- http://www.utasker.com/kinetis/FRDM-KL28Z.html
- http://www.utasker.com/kinetis/FRDM-KL43Z.html
- http://www.utasker.com/kinetis/TWR-KL43Z48M.html
- http://www.utasker.com/kinetis/FRDM-KL46Z.html
- http://www.utasker.com/kinetis/TWR-KL46Z48M.html
- http://www.utasker.com/kinetis/FRDM-KL82Z.html
uTasker: supporting >1'000 registered Kinetis users get products faster and cheaper to market
Request Free emergency remote desk-top consulting at http://www.utasker.com/services.html
Open Source version at https://github.com/uTasker/uTasker-Kinetis
Hi Mark,
A couple of follow up questions, because I'm still not having any luck getting this working.
Firstly, I should have specified the MCU more precisely, it's the MKL25Z128VLK4.
Secondly, what I'm wanting to achieve right now is this:
The goal here is to effectively achieve a periodic toggling of a GPIO pin with interrupting the main loop or requiring an interrupt handler to be called.
I've no problem getting the PIT timer going. But right now, I can't seem to get the DMA transfer to happen as a result of the PIT timer expiring.
Here's a sample of the source code (just using CMSIS and some boilerplate code from Freescale):
#define LED1 7
const uint32_t LED1_DMA = (1UL << LED1);
// Configure clock for PORTC, DMA Multiplexer, DMA & PIT
SIM->SCGC5 |= SIM_SCGC5_PORTC(1);
SIM->SCGC6 |= SIM_SCGC6_DMAMUX(1) | SIM_SCGC6_PIT(1);
SIM->SCGC7 |= SIM_SCGC7_DMA(1);
// Disable DMAMUX channel and PIT timer
DMAMUX0->CHCFG[0] = 0;
PIT->CHANNEL[0].TCTRL &= ~PIT_TCTRL_TEN(1);
// Configure DMA Source and Destination addresses
DMA0->DMA[0].SAR = (uint32_t)&LED1_DMA;
DMA0->DMA[0].DAR = (uint32_t)&PTC->PTOR;
DMA0->DMA[0].DCR = DMA_DCR_ERQ(1) | DMA_DCR_CS(1);
// Configure PIT
PIT->CHANNEL[0].LDVAL = SystemCoreClock - 1; // 47999999 or one second
PIT->CHANNEL[0].TCTRL |= PIT_TCTRL_TEN(1) | PIT_TCTRL_TIE(1);
PIT->MCR = 0; /* Module enabled, don't freeze in debug */
// Enable DMAMUX0 Channel 0, always on and triggered
DMAMUX0->CHCFG[0] |= 60 | DMAMUX_CHCFG_ENBL(1) | DMAMUX_CHCFG_TRIG(1);
/* Set Port C Pin 7 to GPIO module */
PORTC->PCR[LED1] &= ~PORT_PCR_MUX_MASK; /* Clear MUX bits */
PORTC->PCR[LED1] |= PORT_PCR_MUX(1); /* Enable pin 7 for GPIO */
PTC->PDDR |= (1UL << LED1); /* Set pin 7 to output */
PTC->PCOR |= (1UL << LED1); /* Set pin 7 low */
My understanding is that PIT timer should triggering a DMA transfer of a single 32bit word, as configure, by this stage and that resulting in a GPIO pin toggling (blinking LED) once per second. The PIT is going, but the DMA module is doing nothing.
Any further thoughts would be greatly appreciated.
Regards,
Paul Swanson
Not to worry, I solved it!
(With the help of an excellent book, thank you Alexander Dean Textbooks - Efficient Embedded Systems Design and Programming – Arm )
Here's the solution for posterity's sake, how to toggle a GPIO pin using only PIT, DMA & GPIO:
#define LED1 7
const uint32_t LED1_DMA = (1UL << LED1);
uint32_t foo = 0;
void PIT_IRQHandler(void)
{
PIT->CHANNEL[0].TFLG |= PIT_TFLG_TIF(0);
}
void DMA0_IRQHandler(void)
{
DMA0->DMA[0].DSR_BCR |= DMA_DSR_BCR_DONE_MASK;
DMA0->DMA[0].SAR = (uint32_t)&LED1_DMA;
DMA0->DMA[0].DAR = (uint32_t)&PTC->PTOR;
DMA0->DMA[0].DSR_BCR = DMA_DSR_BCR_BCR(4);
DMA0->DMA[0].DSR_BCR &= ~DMA_DSR_BCR_DONE_MASK;
DMAMUX0->CHCFG[0] |= DMAMUX_CHCFG_ENBL(1);
}
/* The following is inside the main() * and implies board initialisation and loop etc */
// Configure clock for PORTC, DMA Multiplexer, DMA & PIT
SIM->SCGC5 |= SIM_SCGC5_PORTC(1);
SIM->SCGC6 |= SIM_SCGC6_DMAMUX(1) | SIM_SCGC6_PIT(1);
SIM->SCGC7 |= SIM_SCGC7_DMA(1);
/* Set Port C Pin 7 to GPIO module */
PORTC->PCR[LED1] &= ~PORT_PCR_MUX_MASK; /* Clear MUX bits */
PORTC->PCR[LED1] |= PORT_PCR_MUX(1); /* Enable pin 7 for GPIO */
PTC->PDDR |= (1UL << LED1); /* Set pin 7 to output */
PTC->PCOR |= (1UL << LED1); /* Set pin 7 low */
// Disable DMAMUX channel and PIT timer
DMAMUX0->CHCFG[0] = 0;
PIT->CHANNEL[0].TCTRL &= ~PIT_TCTRL_TEN(1);
// Configure DMA Source and Destination addresses
DMA0->DMA[0].DCR = DMA_DCR_ERQ(1) | DMA_DCR_CS(1) | DMA_DCR_EINT(1);
// Configure PIT
PIT->CHANNEL[0].LDVAL = SystemCoreClock - 1; // 47999999 or one second
PIT->CHANNEL[0].TCTRL |= PIT_TCTRL_TEN(1) | PIT_TCTRL_TIE(1);
PIT->MCR = 0; /* Module enabled, don't freeze in debug */
// Enable DMAMUX0 Channel 0, always on and triggered
DMAMUX0->CHCFG[0] |= DMAMUX_CHCFG_TRIG(1);
/* Set up interrupt controller */
NVIC_SetPriority(PIT_IRQn, 2);
NVIC_ClearPendingIRQ(PIT_IRQn);
NVIC_EnableIRQ(PIT_IRQn);
NVIC_SetPriority(DMA0_IRQn, 0);
NVIC_ClearPendingIRQ(DMA0_IRQn);
NVIC_EnableIRQ(DMA0_IRQn);
DMAMUX0->CHCFG[0] |= DMAMUX_CHCFG_SOURCE(60);
DMA0->DMA[0].SAR = (uint32_t)&LED1_DMA;
DMA0->DMA[0].DAR = (uint32_t)&PTC->PTOR;
DMA0->DMA[0].DSR_BCR = DMA_DSR_BCR_BCR(4);
DMA0->DMA[0].DSR_BCR &= ~DMA_DSR_BCR_DONE_MASK;
DMAMUX0->CHCFG[0] |= DMAMUX_CHCFG_ENBL(1);
/* LED toggles off and on once a second from this point on */
Paul
You don't need to enable a PIT interrupt since DMA does it all.
This is how it is done in the uTasker project (generating a 500kHz complimentary square wave on PTA0 and PTA1 using PIT1) :
void fnGeneratePIT_square_wave(void)
{
PIT_SETUP pit_setup; // PIT interrupt configuration parameters
pit_setup.int_type = PIT_INTERRUPT;
_CONFIG_DRIVE_PORT_OUTPUT_VALUE(A, (PORTA_BIT0 | PORTA_BIT1), (PORTA_BIT1), (PORT_SRE_SLOW | PORT_DSE_HIGH)); // set initial port states
pit_setup.ucPortRef = PORTA;
pit_setup.ulPortBits = (PORTA_BIT0 | PORTA_BIT1); // toggle PTA0/1 on each PIT trigger
pit_setup.ucPIT = 1; // use PIT1 (this will use DMA channel 1, 0 would use DMA channel 0, etc.)
pit_setup.count_delay = PIT_US_DELAY(1); // 1us period (for 500kHz square wave)
pit_setup.mode = (PIT_PERIODIC | PIT_OUTPUT_DMA_TRIG); // periodic with DMA trigger to control a port toggle
fnConfigureInterrupt((void *)&pit_setup); // configure PIT
}
The PIT method has a DMA port output capability built into it. In the lower layers it will look similar to what you have done for the KL25 but will also work on Kinetis parts with eDMA or LPIT so the user doesn't need to worry about the details. You will need to redevelop the same if you move to a part with eDMA.
Regards
Mark
Hi Mark,
That's interesting. Perhaps you can help me better understand your terminology.
On this MCU, I can see that PIT is able to (or does) trigger the first two DMA channels. However, I can not see any documentation in the Sub Family manual or other the suggests the PIT module itself contains any configurable options directly related to DMA; rather the DMAMUX / DMA modules can accept a trigger from the PIT if so configured.
When you say "the PIT method has a DMA port output capability built into it", is this ultimately what you're referring to (in the context of uTasker)?
Also, back to your first statement, is it not necessary to clear the PIT interrupt flag with an ISR for proper operation of the PIT?
Thanks as always,
Paul Swanson
Paul
The PIT triggers DMA each time it overflows - there is nothing to configure in the PIT to do this apart from setting it to a periodic operating mode.
You don't need to enable PIT interrupt and you don't need to handle a PIT interrupt (this would be counter productive if it were the case).
In the uTasker PIT driver the DMA port case is simply a set up for periodic mode with out interrupt, plus the following lines of code (which add the DMA setup and starts it):
if ((PIT_settings->mode & PIT_OUTPUT_DMA_TRIG) != 0) { // if the PIT is to trigger a port toggle by DMA
// Note that PIT channel number must match with the DMA channel (hardware requirement) and the DMA channel is chosen here to match the PIT
//
static unsigned long ulPortBits[_PITS_AVAILABLE] = {0};
ulPortBits[PIT_settings->ucPIT] = PIT_settings->ulPortBits; // the port bit(s) to be toggled
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)
fnDMA_BufferReset(PIT_settings->ucPIT, DMA_BUFFER_START);
}
Regards
Mark