Hi,
I am new to MCUExpresso, and I am having difficulty with a TPM Timer. I looked over the example and the SDK and attempted to implement it on my board, but I can not get the timer to count, let alone interrupt. I have attached a screen shot of the clocks table and below are the parts of my code dealing with the TPM module.
Thanks in advance for your help!
#include <stdio.h>
#include "MKL17Z644.h"
#include "board.h"
#include "peripherals.h"
#include "pin_mux.h"
#include "clock_config.h"
#include "fsl_gpio.h"
#include "fsl_spi.h"
#include "fsl_tpm.h"
#define BOARD_TPM TPM0
#define BOARD_TPM_IRQ_NUM TPM0_IRQn
#define BOARD_TPM_HANDLER TPM0_IRQHandler
//#define TPM_SOURCE_CLOCK (CLOCK_GetFreq(kCLOCK_McgIrc48MClk)/4)
#define TPM_SOURCE_CLOCK (CLOCK_GetFreq(kCLOCK_McgInternalRefClk))
volatile uint32_t tpmCounter = 0U;
void BOARD_TPM_HANDLER(void);
int main(void)
{
tpm_config_t tpmInfo;
/* Init board hardware. */
BOARD_InitBootPins();
BOARD_InitBootClocks();
BOARD_InitBootPeripherals();
InitGPIOs();
CLOCK_SetTpmClock(1U);
/* TPM Setup */
TPM_GetDefaultConfig(&tpmInfo);
#ifndef TPM_PRESCALER
#define TPM_PRESCALER kTPM_Prescale_Divide_4
#endif
//tpmInfo.prescale = TPM_PRESCALER; /* TPM clock divide by TPM_PRESCALER */
TPM_Init(BOARD_TPM, &tpmInfo); /* Initialize TPM module */
TPM_SetTimerPeriod(BOARD_TPM, USEC_TO_COUNT(1000U, TPM_SOURCE_CLOCK)); /* Set timer period. */
TPM_EnableInterrupts(BOARD_TPM, kTPM_TimeOverflowInterruptEnable);
EnableIRQ(BOARD_TPM_IRQ_NUM);
printf("Starting TPM Timer!\r\n");
tpmDelay(100);
printf("Returning from TPM Timer!\r\n");
}
void BOARD_TPM_HANDLER(void)
{
/* Clear interrupt flag.*/
TPM_ClearStatusFlags(BOARD_TPM, kTPM_TimeOverflowFlag);
tpmCounter++;
__DSB();
//__ISB(); //Needed?
}
uint8_t tpmDelay(uint32_t delayTime)
{
TPM_StartTimer(BOARD_TPM, kTPM_SystemClock);
tpmCounter = 0;
while(1)
{
if(delayTime <= tpmCounter)
{
TPM_StopTimer(BOARD_TPM);
printf("delayTime was: %d\r\n", delayTime);
printf("tpmtmrCounter was: %d\r\n", tpmCounter);
return 0;
}
//__WFI();
}
From clocks_config.c
#define SIM_TPM_CLK_SEL_MCGIRCLK_CLK 3U /*!< TPM clock select: MCGIRCLK clock */
CLOCK_SetTpmClock(SIM_TPM_CLK_SEL_MCGIRCLK_CLK);
Hi Evan Griffith,
What's the detail kinetis chip you are using? Please tell me the chip part number.
Do you design the board by yourself? Or the official hardware?
If it is your own designed board, what's the external osc you are using? Do you use the external OSC, if you are using it, whether the osc is working or not?
Just make sure your hardware is working.
About the code, you also can refer to the official SDK code, which can be downloaded from this link:
Welcome | MCUXpresso SDK Builder
Any updated information from your side, please kindly let me know.
Have a great day,
Kerry
-------------------------------------------------------------------------------
Note:
- If this post answers your question, please click the "Mark Correct" button. Thank you!
- We are following threads for 7 weeks after the last post, later replies are ignored
Please open a new thread and refer to the closed one, if you have a related question at a later point in time.
-------------------------------------------------------------------------------
Hi,
Thanks for the quick reply, please see the answers to your questions below.
What's the detail kinetis chip you are using? Please tell me the chip part number.
MKL17Z64VDA4
Do you design the board by yourself? Or the official hardware?
This is a custom PCB. I have verified the SPI and GPIO capability of the MCU on the PCB separately and they are working well.
If it is your own designed board, what's the external osc you are using? Do you use the external OSC, if you are using it, whether the osc is working or not?
I am not using the external OSC at the moment, it is intended for support of low power modes.
Hope this helps.
Thanks!
HI Evan Griffith,
Do you debug your project to check the TPM counter?
If yes, do you enable TPMx_CONF[DBGMODE]=1?
Please check it at first.
Any updated information from your side, please kindly let me know.
Have a great day,
Kerry
-------------------------------------------------------------------------------
Note:
- If this post answers your question, please click the "Mark Correct" button. Thank you!
- We are following threads for 7 weeks after the last post, later replies are ignored
Please open a new thread and refer to the closed one, if you have a related question at a later point in time.
-------------------------------------------------------------------------------
Hi Evan
This is the TPM interface used to generate a single-shot interrupt by uTaker users (works on any Kinetis part with Flex timer or TPM):
static TIMER_INTERRUPT_SETUP timer_setup = {0}; // interrupt configuration parameters
timer_setup.int_type = TIMER_INTERRUPT;
timer_setup.int_priority = PRIORITY_TIMERS; // interrupt priority
timer_setup.int_handler = timer_int; // user interrupt callback
timer_setup.timer_reference = 0; // TPM 0
timer_setup.timer_value = TIMER_US_DELAY(100); // 100us delay
fnConfigureInterrupt((void *)&timer_setup); // configure and start timer with interrupt
After the defined delay it calls back the user handler timer_int() and the timer is powered down again.
This is low level HAL KL17 code relevant to the operation (with values):
// Configure the TPM clock source
//
SIM_SOPT2 |= (SIM_SOPT2_PLLFLLSEL | SIM_SOPT2_TPMSRC_MCG); // devices with KINETIS_WITH_MCG_LITE and HIRC48M will use 48MHz
POWER_UP_ATOMIC(6, FTM0); // ensure that the FlexTimer module is powered up
FTM0_SC = 0; // ensure not operating
FTM0_CONF = FTM_CONF_BDMMODE_3; // set the debugging behaviour (whether the counter runs in debug mode and how the outputs react)
FTM0_MOD = 0x12c0; // set upper count value (100us value from HIRC48M)
fnEnterInterrupt(irq_TPM0_ID, PRIORITY_TIMERS, timer_int); // enter flex timer interrupt handler
FTM0_SC = (FTM_SC_CLKS_SYS | FTM_SC_TOF | FTM_SC_TOIE); // system clock with overflow interrupt enabled
// RAW values
//
SIM_SOPT2 |= 0x01000000;
SIM_SCGC6 |= 0x01000000;
FTM0_SC = 0;
FTM0_CONF = 0x000000c0; // allow counting to continue in debug mode
FTM0_MOD = 0x12c0;
FTM0_SC = 0x00c8;
If your library doesn't do the interrupt handling completely here is the code that has to be added to the interrupt routine to clear the interrupt (and power down the timer if not needed any more)
if ((FTM0_SC & FTM_SC_TOF) != 0) { // the overflow flag must be read when set before it can be cleared
FTM_SC = FTM_SC_TOF; // clear the interrupt flag stop further activity
POWER_DOWN_ATOMIC(6, FTM0); // power down timer
timer_int(); // call back to user handling
}
If your timer doesn't count, use the debugger and compare the registers with above to see what difference can explain the problem.
To make real progress - use the uTasker project for your professional developments and simulate your chip to avoid losing time.
Regards
Mark
Complete Kinetis solutions for professional needs, training and support:http://www.utasker.com/kinetis.html
Kinetis KL17/27:
- http://www.utasker.com/kinetis/FRDM-KL27Z.html
- http://www.utasker.com/kinetis/Capuccino-KL27.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
HW timers: http://www.utasker.com/docs/uTasker/uTaskerHWTimers.PDF
Open Source Version: https://github.com/uTasker/uTasker-Kinetis
Hi,
This basically looks like an alternative SDK, is that correct? I have a lot of code already written with the NXP SDK and do not want to run a new set or run both sets as I would guess it would cause bloat, is this correct?
For the life of me I can not find where the issue is with this, I checked my TPM settings against the user manual and they all look good. All I can figure is somewhere I have an issue with using the MCGIRCLK vs the MCGPCLK / HIRC. I have no need for 48MHz and may eventually even step down to the external clock to reduce power once things are dialed in.
Any help would be appreciated.
Thanks!
Hi Evan
If you want to use the 8MHz IRC as TPM clock make sure that you enable it and its clock gate.
The code to do this is
MCG_C1 |= (MCG_C1_IRCLKEN | MCG_C1_IREFSTEN); // enable internal reference clock and allow it to continue running in stop modes
MCG_SC = 0; // remove fast IRC divider
MCG_C2 |= MCG_C2_IRCS; // select fast internal reference clock (8MHz)
SIM_SOPT2 |= SIM_SOPT2_TPMSRC_MCGIRCLK; // use MCGIRCLK as timer clock source
or
// RAW binary version
//
MCG_C1 |= (0x02 | 0x01); // enable internal reference clock and allow it to continue running in stop modes
MCG_SC = 0; // remove fast IRC divider
MCG_C2 |= 0x01; // select fast internal reference clock (8MHz)
SIM_SOPT2 |= 0x03000000; // use MCGIRCLK as timer clock source
Yes the uTasker project is an alternative SDK if you like, designed to have a higher efficiency level (smaller code size and less developer problems, plus accurate peripheral simulation to avoid mistakes - the simulate already tells you if something is incorrectly set). This is why there are very few uTasker user questions on the forum (although there are over 1000 registered users and millions of devices using it in hundreds of different industrial and commercial products) - users just get the work completed rather than struggling with the standard methods.
To move from HIRC48M clock to 8MHz MCGIRCLK I just do
#define TPM_CLOCKED_FROM_MCGIRCLK // TPM clock is connected to MCGIRCLK (either 2MHz or 8MHz)
#define USE_FAST_INTERNAL_CLOCK // select fast internal clock (8MHz) rather than (2MHz)
and it gives me the above code and the simulation confirms that it runs. 2 minutes work and no potential problems...
I also attached a video of the simulation of such a TPM (running from MCGIRCLK) interrupt toggling an LED on a board!
Regards
Mark
P.S. I have worked with many companies who started using one of the standard SDKs. Often the problem is that they have invested a lot of time and resources (and money) in getting parts of a project completed and it is always difficult to change to a solution that is virtually turn-key since it they throws up question about why it wasn't done properly in the first place...
P.P.S. Here is an example of an almost complete (but unsuccessful) development being saved by uTasker:
- https://community.nxp.com/thread/496428
- http://www.utasker.com/forum/index.php?topic=2028.msg7570#msg7570
- http://www.utasker.com/forum/index.php?topic=2029.0
P.P.P.S. A further advantage is that the same application builds for almost any Kinetis part (no other libraries and redesigns need) [and even builds for i.MX RT parts if more power is found to be needed at some point in a development]
Hi,
Better late than never. The solution to this was disabling the NMI pin in startup_mkl17z644.c.
To do this you need to change the byte shown in the image from 0x3D to 0x3F within startup_mkl17z644.c
Thanks!