Hello,
Working with a FRDM-KL25Z board (MKL25Z128VLK4 mcu) and mostly just CMSIS with some boilerplate code from Freescale.
What I'm wanting to achieve right now is this:
The goal here is to achieve a periodic toggling of a GPIO pin with interrupting the main loop or requiring an interrupt handler to be called. In other words, a blinky LED using just the PIT, DMA and GPIO ... no CPU, no interrupt handlers.
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 other boilerplate code from Freescale not shown here):
#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 assistance 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. The IRQ handlers are necessary but only serve to prepare the prepare the modules for each subsequent event:
#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 */
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. The IRQ handlers are necessary but only serve to prepare the prepare the modules for each subsequent event:
#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 */