Hi everybody.
I'm using the FRDM-KL46z with MCUXpresso and I want to control a NEO-Pixel LED but I need to use the TPM and DMA.
I configure the DMA with config-tools as follow.
And the TPM is the same and as follow.
#include <stdio.h>
#include "board.h"
#include "peripherals.h"
#include "pin_mux.h"
#include "clock_config.h"
#include "fsl_gpio.h"
#include "fsl_port.h"
#include "MKL46Z4.h"
/* TODO: insert other include files here. */
#define LOGIC_LED_ON 0U
#define LOGIC_LED_OFF 1U
#ifndef BOARD_LED_RED_GPIO
#define BOARD_LED_RED_GPIO GPIOE
#endif
#ifndef BOARD_LED_RED_GPIO_PIN
#define BOARD_LED_RED_GPIO_PIN 29U
#endif
#define BOARD_TPM_CHANNEL 5U
#ifndef TPM_LED_ON_LEVEL
#define TPM_LED_ON_LEVEL kTPM_LowTrue
#endif
uint16_t Val_Cn = 10000; //the Value of duty cycle
tpm_status_flags_t flag_t = 0; //the flag of TPM status
dma_transfer_config_t TPM_CONF; //struct to config the DMA transfer to TPM
uint32_t flag_dma = 0; //the flag of DMA status.
/* TODO: insert other definitions and declarations here. */
void DMA_Callback(dma_handle_t * handle, void* param)
{
__asm volatile ("nop"); //only for a breakpoint
}
void DMA_1_DMA_CH_INT_DONE_0_IRQHANDLER()
{
__asm volatile ("nop"); //only for a breakpoint
flag_t = TPM_GetStatusFlags(TPM0);
flag_dma = DMA_GetChannelStatusFlags(DMA0, DMA_1_DMA_TPM_DMA_CHANNEL);
DMA_ClearChannelStatusFlags(DMA0, DMA_1_DMA_TPM_DMA_CHANNEL, flag_dma);
}
/*
* @brief Application entry point.
*/
int main(void) {
/* Init board hardware. */
BOARD_InitBootPins();
BOARD_InitBootClocks();
BOARD_InitBootPeripherals();
TPM0->SC |= TPM_SC_DMA(1); //Enable the DMA on TPM
TPM0->CONTROLS[5].CnSC |= TPM_CnSC_DMA(1); //Enable the DMA transfer on TPM channel 5
DMA0->DMA[0].DCR |= DMA_DCR_ERQ_MASK; //enable the peripheral request
DMA_PrepareTransfer(&TPM_CONF, &Val_Cn, sizeof(uint16_t),
&(TPM0->CONTROLS[5].CnV), sizeof(uint16_t), 2, kDMA_MemoryToPeripheral); //prepare the struct transfer
DMA_SubmitTransfer(&DMA_1_DMA_TPM_Handle, &TPM_CONF, kDMA_EnableInterrupt);//prepare the handle DMA_TPM
//DMA_StartTransfer(&DMA_1_DMA_TPM_Handle);
TPM_StartTimer(TPM0, kTPM_SystemClock); //Init the timer without interrupt request
/* Enter an infinite loop, just incrementing a counter. */
while(1) {
__asm volatile ("nop");//only for a breakpoint
flag_dma = DMA_GetChannelStatusFlags(DMA0, DMA_1_DMA_TPM_DMA_CHANNEL); //I check the status flag DMA
flag_t = TPM_GetStatusFlags(TPM0); //I check the status flag TPM
if(flag_t == (kTPM_TimeOverflowFlag | kTPM_Chnl5Flag)){
GPIO_PortToggle(BOARD_LED_RED_GPIO, 1U <<
BOARD_LED_RED_GPIO_PIN);
TPM_ClearStatusFlags(TPM0, kTPM_TimeOverflowFlag | kTPM_Chnl5Flag); //I clean the status flags
}
}
return 0 ;
}
But the overflow in TPM don't generate the request DMA to send the data to cnV counter of TPM only if I just not comment the Line 60 "DMA_StartTransfer" the counter register refresh it but this only execution it a once and I want to refresh the Counter register channel every time that an overflow occurs.
Thanks.
Best regards.
Hello nxf54944 thanks for your answer, I solved this problem some days ago, but now I don't use the overflow DMA request, I only use the the TPM channel request and my code is this.
#include <stdio.h>
#include "board.h"
#include "peripherals.h"
#include "pin_mux.h"
#include "clock_config.h"
#include "fsl_gpio.h"
#include "fsl_port.h"
#include "MKL46Z4.h"
/* TODO: insert other include files here. */
#define LOGIC_LED_ON 0U
#define LOGIC_LED_OFF 1U
#ifndef BOARD_LED_RED_GPIO
#define BOARD_LED_RED_GPIO GPIOE
#endif
#ifndef BOARD_LED_RED_GPIO_PIN
#define BOARD_LED_RED_GPIO_PIN 29U
#endif
#define BOARD_TPM_CHANNEL 5U
#ifndef TPM_LED_ON_LEVEL
#define TPM_LED_ON_LEVEL kTPM_LowTrue
#endif
#define Cntr_Val 1000
uint16_t Val_Cn[16] =
{
Cntr_Val*0, Cntr_Val*1, Cntr_Val*2, Cntr_Val*3,
Cntr_Val*4, Cntr_Val*5, Cntr_Val*6, Cntr_Val*7,
Cntr_Val*8, Cntr_Val*9, Cntr_Val*10, Cntr_Val*11,
Cntr_Val*12, Cntr_Val*13, Cntr_Val*14, Cntr_Val*15
}; //the Value of duty cycle
tpm_status_flags_t flag_t = 0; //the flag of TPM status
dma_transfer_config_t TPM_CONF; //struct to config the DMA transfer to TPM
uint32_t flag_dma = 0; //the flag of DMA status.
uint32_t bytes_endless = 0;
/* TODO: insert other definitions and declarations here. */
void DMA_Callback(dma_handle_t * handle, void* param)
{
__asm volatile ("nop"); //only for a breakpoint
}
void DMA_1_DMA_CH_INT_DONE_0_IRQHANDLER()
{
__asm volatile ("nop"); //only for a breakpoint
/*
* See the status flags of the timer and the DMA,
* it should be noted that the status flags should be
* cleaned once they are activated with any event ...
*/
flag_t = TPM_GetStatusFlags(TPM0);
TPM_ClearStatusFlags(TPM0, kTPM_TimeOverflowFlag | kTPM_Chnl5Flag);
flag_dma = DMA_GetChannelStatusFlags(DMA0, DMA_1_DMA_TPM_DMA_CHANNEL);
DMA_ClearChannelStatusFlags(DMA0, DMA_1_DMA_TPM_DMA_CHANNEL, flag_dma);
bytes_endless = DMA_GetRemainingBytes(DMA0, DMA_1_DMA_TPM_DMA_CHANNEL);
DMA_SetTransferConfig(DMA0, DMA_1_DMA_TPM_DMA_CHANNEL, &TPM_CONF);
}
void TPM_1_IRQHANDLER()
{
__asm volatile ("nop"); //only for a breakpoint
/*
* Initially this interruption was used by overflow,
* but it is not necessary when in this program the DMA is used,
* thus this way we release CPU load.
*/
flag_t = TPM_GetStatusFlags(TPM0);
//Si ambas banderas de status se activaron entonces se limpian ambas.
if( flag_t == (kTPM_TimeOverflowFlag | kTPM_Chnl5Flag) ){
GPIO_PortToggle(BOARD_LED_RED_GPIO, 1U << BOARD_LED_RED_GPIO_PIN);
TPM_ClearStatusFlags(TPM0, kTPM_TimeOverflowFlag | kTPM_Chnl5Flag);
}
}
/*
* @brief Application entry point.
*/
int main(void) {
/* Init board hardware. */
BOARD_InitBootPins();
BOARD_InitBootClocks();
BOARD_InitBootPeripherals();
/*
* The configurations are currently written to the peripheral register.
* Because these options cannot be configured from the "config-tools" interface.
*
* TODO: Maybe they can be configured in the configuration structure ...
*/
TPM0->SC |= TPM_SC_DMA(1);
TPM0->CONTROLS[5].CnSC |= TPM_CnSC_DMA(1);
/*
* A structure is created to configure the DMA transfer
*/
dma_transfer_config_t transferConfig;
memset(&transferConfig, 0, sizeof(transferConfig));
transferConfig.srcAddr = (uint32_t)&Val_Cn[0];
transferConfig.destAddr = (uint32_t)&(TPM0->CONTROLS[5].CnV);
transferConfig.enableSrcIncrement = true;
transferConfig.enableDestIncrement = false;
transferConfig.srcSize = kDMA_Transfersize16bits;
transferConfig.destSize = kDMA_Transfersize16bits;
transferConfig.transferSize = sizeof(uint16_t) * 16;
/*
* TODO: Yes... is stupid but works for now its ok...
*/
TPM_CONF = transferConfig;
DMA_SetModulo(DMA0, DMA_1_DMA_TPM_DMA_CHANNEL, 32, 0);
DMA_SetTransferSize(DMA0, DMA_1_DMA_TPM_DMA_CHANNEL, 2);
DMA_SetTransferConfig(DMA0, DMA_1_DMA_TPM_DMA_CHANNEL, &transferConfig);
DMA_EnableChannelRequest(DMA0, DMA_1_DMA_TPM_DMA_CHANNEL);
DMA_EnableAsyncRequest(DMA0, DMA_1_DMA_TPM_DMA_CHANNEL, true);
DMA0->DMA[0].DCR |= DMA_DCR_D_REQ(0);
/*
*
* The direct configuration of these records was changed to Enable Channel Request,
* EnableAsyncRequest ...
*
* DMA0->DMA[0].DCR |= DMA_DCR_ERQ(1);
* DMA0->DMA[0].DCR |= DMA_DCR_EADREQ(1);
*
**/
TPM_StartTimer(TPM0, kTPM_SystemClock);
/* Enter an infinite loop, just incrementing a counter. */
printf("DMA working without CPU");
while(1) {
/* 'Dummy' NOP to allow source level single stepping of
tight while() loop */
__asm volatile ("nop");
/*
* This was written to display the flags in "Real time" while debugging the program ...
*/
flag_dma = DMA_GetChannelStatusFlags(DMA0, DMA_1_DMA_TPM_DMA_CHANNEL);
flag_t = TPM_GetStatusFlags(TPM0);
bytes_endless = DMA_GetRemainingBytes(DMA0, DMA_1_DMA_TPM_DMA_CHANNEL);
if( flag_t == (kTPM_TimeOverflowFlag | kTPM_Chnl5Flag) ){
GPIO_PortToggle(BOARD_LED_RED_GPIO, 1U << BOARD_LED_RED_GPIO_PIN);
TPM_ClearStatusFlags(TPM0, kTPM_TimeOverflowFlag | kTPM_Chnl5Flag);
}
}
return 0 ;
}
And I disabled this parameter on DMA config.
I could tell that a MCUXpresso's function disabled my configuration on the DMA Control Register, and the fuction is...
status_t DMA_SubmitTransfer(dma_handle_t *handle, const dma_transfer_config_t *config, uint32_t options)
{
assert(handle != NULL);
assert(config != NULL);
/* Check if DMA is busy */
if (handle->base->DMA[handle->channel].DSR_BCR & DMA_DSR_BCR_BSY_MASK)
{
return kStatus_DMA_Busy;
}
DMA_ResetChannel(handle->base, handle->channel); //This clean the Control Register and any configuration!!
DMA_SetTransferConfig(handle->base, handle->channel, config);
if (options & kDMA_EnableInterrupt)
{
DMA_EnableInterrupts(handle->base, handle->channel);
}
return kStatus_Success;
}
And I changed this function for this other.
void DMA_SetTransferConfig(DMA_Type *base, uint32_t channel, const dma_transfer_config_t *config)
{
assert(channel < FSL_FEATURE_DMA_MODULE_CHANNEL);
assert(config != NULL);
uint32_t tmpreg;
/* Set source address */
base->DMA[channel].SAR = config->srcAddr;
/* Set destination address */
base->DMA[channel].DAR = config->destAddr;
/* Set transfer bytes */
base->DMA[channel].DSR_BCR = DMA_DSR_BCR_BCR(config->transferSize);
/* Set DMA Control Register */
tmpreg = base->DMA[channel].DCR;
tmpreg &= ~(DMA_DCR_DSIZE_MASK | DMA_DCR_DINC_MASK | DMA_DCR_SSIZE_MASK | DMA_DCR_SINC_MASK);
tmpreg |= (DMA_DCR_DSIZE(config->destSize) | DMA_DCR_DINC(config->enableDestIncrement) |
DMA_DCR_SSIZE(config->srcSize) | DMA_DCR_SINC(config->enableSrcIncrement));
base->DMA[channel].DCR = tmpreg;
}
And now I achieved make I wanted, I hope that this help someone
Best regards
Hello Abraham Rosas
Checking your configuration I noted that the TPM interruption is not set. Please try checking these boxes.
If the interruption is not set then the DMA will not know if the TPM has overflowed.
Let me know if this helps.
Best regards,
Omar