Good morning.
I'd like to use the TPM counter and its channel for some timing uses.
Prescale must be set at 128, so with a 24 mhz of bus clock I have 187, 5 Khz for tpm clock.
One channel (..ch2) must be set to get an interrupt break at every 1ms. I write the simple code that I use now.
It works properly for just 250 ms. After that, the TPM0 interrupt it does not work anymore and the output is fixed at one.
I don't understand this behavior. Probably something is missing in my code.
Look at CH2
Thanks in advance.
Attilio
#define TPM_CHANNEL_0 0 // tpm channel 0 mask
#define TPM_CHANNEL_1 1 // tpm channel 1 mask
#define TPM_CHANNEL_2 2 // tpm channel 2 mask
#define TPM_CHANNEL_3 3 // tpm channel 3 mask
#define TPM_CHANNEL_4 4 // tpm channel 4 mask
#define TPM_CHANNEL_5 5 // tpm channel 5 mask
#define TPM_CH0_FLAG 0x01 // channel 0 event flag
#define TPM_CH1_FLAG 0x02 // channel 1 event flag
#define TPM_CH2_FLAG 0x04 // channel 2 event flag
#define TPM_CH3_FLAG 0x08 // channel 3 event flag
#define TPM_CH4_FLAG 0x10 // channel 4 event flag
#define TPM_CH5_FLAG 0x20 // channel 5 event flag
#define TPM_OVERFLOW_COUNTER 0x100 // overflow counter flag
This the init function
void InitTimers(void)
{
SIM->SCGC6 |= SIM_SCGC6_TPM0_MASK; // clock gating
SIM->SOPT2 |= SIM_SOPT2_TPMSRC(0x10); // tpm clock source
TPM0->SC = TPM_SC_PS(0x07) | // prescaler divide 0x07 = 128 times
TPM_SC_CMOD(0x01); // tpm increment every tpm counter clock
TPM0->MOD = 46750; // timer period
// CHANNEL 2 SETTING
TPM0->CONTROLS[TPM_CHANNEL_2].CnV = 23375; // first int. after ~125ms
TPM0->CONTROLS[TPM_CHANNEL_2].CnSC = (OUT_COMPARE_TOGGLE + TPM_CnSC_CHIE_MASK);
PORTA->PCR[5] = PORT_PCR_MUX(ALTERNATE_FUNCTION_3); // PTA5 wired to out
ConnectInterruptToCortex(TPM0_IRQn); // set TPM0 interrupt
}
This the interrupt
void TPM0_IRQHandler(void)
{
UINT32 status;
UINT32 l;
status = TPM0->STATUS;
// OVERFLOW
if(status & TPM_OVERFLOW_COUNTER)
{
}
// CH0
if(status & TPM_CH0_FLAG)
{
}
// CH1
if(status & TPM_CH1_FLAG)
{
}
// CH2
if(status & TPM_CH2_FLAG)
{
l = TPM0->CNT;
l = l + 376;
l = l % 65535;
TPM0->CONTROLS[TPM_CHANNEL_2].CnV = (UINT16)l;
}
// CH3
if(status & TPM_CH3_FLAG)
{
}
// CH4
if(status & TPM_CH4_FLAG)
{
}
// CH5
if(status & TPM_CH5_FLAG)
{
}
TPM0->STATUS |= status;
}
Exactly what I needed! You just saved me several hours. Thanks!
Hi Attilio,
I can see you configure the TPM0 work as OUT_COMPARE_TOGGLE mode.
Since you just need the ch2 get an interrupt break at every 1ms, why not direct configure TPM0->MOD let the period =1ms. Then the fixed TPM0->CONTROLS[TPM_CHANNEL_2].CnV value will always get 1ms interrupt and toggle waveform.(There is no need to modify ch2.CnV so frequently in IRQ)
Best Regards,
Robin
-----------------------------------------------------------------------------------------------------------------------
Note: If this post answers your question, please click the Correct Answer button. Thank you!
-----------------------------------------------------------------------------------------------------------------------
Hi Robin.
Thanks for your interest.
You are right. But in reality the main purpose of this timer it's to generate a slow pwm frequency on channel 1.
So, I have to keep the MOD register at the desidered period. Usually it's 8 hz. (about 46875 value at 187,5 Khz).
But I wanted to use the same timer to get a 1 ms tick on another output.
Below, I send the total code that I'm using now, so may be more clear for you.
As I wrote the strange thing is at the beginning it works fine and properly. I can see the output toggle every 1ms (500 hz frequency) but suddendly afeter 250ms - 300 ms the TPM0 interrupt is no longer call.
Thanks
Attilio
void InitTimers(void)
{
float bus_tick, frequency, period, duty;
UINT8 duty_cycle;
/* TIMER 0 channels setting */
GetLampParameters(&frequency, &duty_cycle);
bus_tick = (1.0 / (BUSCLOCK / Power(2, 0x07)));
period = (1.0 / frequency) / bus_tick;
duty = (period / 100) * duty_cycle;
SIM->SCGC6 |= SIM_SCGC6_TPM0_MASK; // clock gating
SIM->SOPT2 |= SIM_SOPT2_TPMSRC(0x10); // tpm clock source
TPM0->SC = 0x00UL;
TPM0->SC |= TPM_SC_PS(0x07); // prescaler divide 0x07 = 128 times
TPM0->SC |= TPM_SC_CMOD(0x01); // tpm increment every tpm counter clock
TPM0->MOD = (UINT32)period; // timer period
// channel 1 setting
TPM0->CONTROLS[TPM_CHANNEL_1].CnV = (UINT32)duty; // timer duty cycle
TPM0->CONTROLS[TPM_CHANNEL_1].CnSC = PWM_EDGE_ALIGN_HIGH_PULSE; // pwm - no CHIE
PORTA->PCR[4] = PORT_PCR_MUX(ALTERNATE_FUNCTION_3); // PTA4 wired to manage lamp
// channel 2
TPM0->CONTROLS[TPM_CHANNEL_2].CnSC = OUT_COMPARE_TOGGLE +\
TPM_CnSC_CHIE_MASK; // ch2 - out.comp. - CHIE request
TPM0->CONTROLS[TPM_CHANNEL_2].CnV = 375; // first match after ~2ms
PORTA->PCR[5] = PORT_PCR_MUX(ALTERNATE_FUNCTION_3); // PTA5 wired to lamp
ConnectInterruptToCortex(TPM0_IRQn); // set TPM0 interrupt
}
void TPM0_IRQHandler(void)
{
UINT32 status;
UINT16 l;
static UINT8 break_test;
status = TPM0->STATUS;
// OVERFLOW
if(status & TPM_OVERFLOW_COUNTER)
{
break_test = 1;
}
// CH0
if(status & TPM_CH0_FLAG)
{
break_test = 1;
}
// CH1
if(status & TPM_CH1_FLAG)
{
break_test = 1;
}
// CH2
if(status & TPM_CH2_FLAG)
{
l = TPM0->CNT;
l = l + 375;
TPM0->CONTROLS[TPM_CHANNEL_2].CnV = (UINT32)l;
}
// CH3
if(status & TPM_CH3_FLAG)
{
break_test = 1;
}
// CH4
if(status & TPM_CH4_FLAG)
{
break_test = 1;
}
// CH5
if(status & TPM_CH5_FLAG)
{
break_test = 1;
}
TPM0->STATUS |= status;
}
Hi Attilio,
I find the value that you give CnV will greater than TPM0->MOD = 46750;
Please limit the value of CnV.
Best Regards,
Robin
-----------------------------------------------------------------------------------------------------------------------
Note: If this post answers your question, please click the Correct Answer button. Thank you!
-----------------------------------------------------------------------------------------------------------------------
Robin thank you very much.
Have a nice day.
Attilio