I am using KL27Z256 48-pin QFN, Keil Tools, SDK 2.2.
I have implemented TPM1Ch0 to trigger DMA from memory to DAC.
I used PWM example in SDK 2.2. The timer drives the DMA and produces a PWM output on a pin. It works.
Now I want to add another timer, TPM2ch0. It is a simple timer with no outputs to pins. I don't see any examples on using two separate TPM units, only one TPM and two channels.
When I try to set up TPM2, it causes failure with the DMA. It happens right after
TPM_SetTimerPeriod(BOARD_TPM2_BASEADDR, USEC_TO_COUNT(29000U, TPM_SOURCE_CLOCK));
or
TPM_SetupPwm(BOARD_TPM2_BASEADDR, &tpmParam, 1U, kTPM_EdgeAlignedPwm, 24000U, TPM_SOURCE_CLOCK);
Where can I find an example SDK that uses two TPM units (TPM1 and TPM2)?
Solved! Go to Solution.
Hi BC Stewart
Sorry for the late reply, I was out of Office. we have checked your code and found the problem:
This is not a multiple TPM related problem, this is more related with the execution time and the TPM1 that is already counting when you setup DMAMUX, let me explain, in your code:
TPM_StartTimer(TPM1, kTPM_SystemClock);
/* Configure tpm 2 channel 0 params with frequency 24kHZ */
//TPM_GetDefaultConfig(&tpmInfo);
TPM_Init(TPM0, &tpmInfo);
// PROBLEM: next line kills DAC output unless I change source in dmamux. Why?
TPM_SetupPwm(TPM0, &tpmParam, 1U, kTPM_EdgeAlignedPwm, 22050U, TPM_SOURCE_CLOCK);
//TPM2->CONTROLS[0].CnSC |= 1u ;// set DMA enable
TPM_StartTimer(TPM0, kTPM_SystemClock);
TPM_UpdatePwmDutycycle(TPM0,kTPM_Chnl_0, kTPM_EdgeAlignedPwm,50U); //set up for 50% duty cycle
//set up DMA MUX--not sure how to set up for PIT
DMAMUX_Init(DMAMUX0);
DMAMUX_SetSource(DMAMUX0, 0 ,kDmaRequestMux0TPM1Channel0); //will be using TPM2 channel 0,
DMAMUX_EnableChannel(DMAMUX0, 0); // inline function defined in header file.
Now, as you can see, when try to set DMAMUX, TPM1 is already counting (TPM_StartTimer), this will cause a DMA request before DMAMUX initialization, this will cause problems in subsequent DMA requests.
This is why when you didn't have the TPM0 init part your code works without problem, because the TPM0 initialization (or any other code) consumes time and TPM1 request a DMA transfer before DMA being completely set.
If you start TPM0 timer after you set DMA the problem should disappear, so please locate
TPM_StartTimer(TPM1, kTPM_SystemClock);
After
DMA0->DMA[0].DCR &= ~(1u << 7u); //clear DREQ
NOTE: you don't need line NVIC_EnableIRQ(DMA0_IRQn); because NVIC is set in the DMA_CreateHandle(&g_DMA_Handle, DMA0, 0); line
Hope this could resolve your problem.
Have a great day,
Jorge Alcala
-----------------------------------------------------------------------------------------------------------------------
Note: If this post answers your question, please click the Correct Answer button. Thank you!
-----------------------------------------------------------------------------------------------------------------------
Hi BC Stewart
Could you share how are you setting up the second TPM, there shouldn't be any problem with the if you set the TPM as you set it in the TPM1, the basic code two use two TPM is:
tpmParam.chnlNumber = 0;
tpmParam.level = kTPM_LowTrue;
tpmParam.dutyCyclePercent = updatedDutycycle;
TPM_GetDefaultConfig(&tpmInfo);
/* Initialize TPM module */
TPM_Init(TPM1, &tpmInfo);
TPM_Init(TPM2, &tpmInfo);
TPM_SetupPwm(TPM1, &tpmParam, 1U, kTPM_EdgeAlignedPwm, 24000U, TPM_SOURCE_CLOCK);
TPM_SetupPwm(TPM2, &tpmParam, 1U, kTPM_EdgeAlignedPwm, 24000U, TPM_SOURCE_CLOCK);
TPM_StartTimer(BOARD_TPM_BASEADDR, kTPM_SystemClock);
TPM_StartTimer(TPM2, kTPM_SystemClock);
then, for the PWM update you can use TPM_UpdatePwmDutycycle.
Now, if you are using DMA, please remember that your data and descriptors need to be aligned, if not, dma will not made transfers.
Hope this could help, please let me know if you have any question about this.
Best Regards
Jorge Alcala
-----------------------------------------------------------------------------------------------------------------------
Note: If this post answers your question, please click the Correct Answer button. Thank you!
-----------------------------------------------------------------------------------------------------------------------
Update: when the failure happens, the program never reaches the for(;;)
loop.
Also please note that I never had these problems with KL25 or K22F using PE.
--Brad
Hello Brad,
I'm trying to replicate your issue on a FRDM-KL43Z (same mask as KL27Z256) with the code you shared and I think I'm seeing the same behavior as you. When the second TPM instance is configured, the DMA seems to get stuck in the interrupt and the execution never reaches the infinite loop. I'll investigate what could be causing this behavior and see how this can be fixed.
Regards,
Gerardo
Any updates?
Hi BC Stewart
Sorry for the late reply, I was out of Office. we have checked your code and found the problem:
This is not a multiple TPM related problem, this is more related with the execution time and the TPM1 that is already counting when you setup DMAMUX, let me explain, in your code:
TPM_StartTimer(TPM1, kTPM_SystemClock);
/* Configure tpm 2 channel 0 params with frequency 24kHZ */
//TPM_GetDefaultConfig(&tpmInfo);
TPM_Init(TPM0, &tpmInfo);
// PROBLEM: next line kills DAC output unless I change source in dmamux. Why?
TPM_SetupPwm(TPM0, &tpmParam, 1U, kTPM_EdgeAlignedPwm, 22050U, TPM_SOURCE_CLOCK);
//TPM2->CONTROLS[0].CnSC |= 1u ;// set DMA enable
TPM_StartTimer(TPM0, kTPM_SystemClock);
TPM_UpdatePwmDutycycle(TPM0,kTPM_Chnl_0, kTPM_EdgeAlignedPwm,50U); //set up for 50% duty cycle
//set up DMA MUX--not sure how to set up for PIT
DMAMUX_Init(DMAMUX0);
DMAMUX_SetSource(DMAMUX0, 0 ,kDmaRequestMux0TPM1Channel0); //will be using TPM2 channel 0,
DMAMUX_EnableChannel(DMAMUX0, 0); // inline function defined in header file.
Now, as you can see, when try to set DMAMUX, TPM1 is already counting (TPM_StartTimer), this will cause a DMA request before DMAMUX initialization, this will cause problems in subsequent DMA requests.
This is why when you didn't have the TPM0 init part your code works without problem, because the TPM0 initialization (or any other code) consumes time and TPM1 request a DMA transfer before DMA being completely set.
If you start TPM0 timer after you set DMA the problem should disappear, so please locate
TPM_StartTimer(TPM1, kTPM_SystemClock);
After
DMA0->DMA[0].DCR &= ~(1u << 7u); //clear DREQ
NOTE: you don't need line NVIC_EnableIRQ(DMA0_IRQn); because NVIC is set in the DMA_CreateHandle(&g_DMA_Handle, DMA0, 0); line
Hope this could resolve your problem.
Have a great day,
Jorge Alcala
-----------------------------------------------------------------------------------------------------------------------
Note: If this post answers your question, please click the Correct Answer button. Thank you!
-----------------------------------------------------------------------------------------------------------------------
Yes. You are seeing the same thing I saw--the program is stuck in the DMA interrupt handler and never reaches the main infinite loop.
Hi Brad
Today is holiday in MX
Tomorrow we will answer you.
Regards
Vicente Gomez
OK.
Have a good "Dia de la Revolucion".
Brad
Hello Jorge,
I am doing what you describe.
I am using TPM1Ch0 to drive a PWM output which is filtered and applied
to DAC Vrefh (multiplying DAC function). It also triggers the DMA to
transfer buffer &data to the DAC in a continuous loop (for audio
output). The value read by the ADC sets the output level by modifying
the PWM (see function in the main for(;;) loop).
It works as designed until I try to enable TPM2Ch0. (I also tried
TPM0Ch0 with same results).
This line #108 causes the failure:
TPM_SetupPwm(TPM2, &tpmParam, 1U, kTPM_EdgeAlignedPwm, 22050U,
TPM_SOURCE_CLOCK);
I still get DMA interrupts, but no output on the DAC. The TPM2
registers seem to contain the correct initial values.
What is very strange is that if I manually change DMAMUX0 CHCFG0_SOURCE
in the debugger from 0x20 to 0x22, then back to 0x20, it works!
(program is halted between changing the value). Is this a hardware bug?
Also note that the SDK does not set the DMA DCR register. I had to do
this manually.
Attached is my code.
Brad