Help with TPM (External clock) on Teensy LC

Status
Not open for further replies.

SolwiseMD

Member
I am trying to read a synchronous serial stream from an odd source which provides a clock at about 1MHz to 5MHz but supplies and receives data at various divisions of this frequency, starting at about 9600 baud (a divisor of 372). This divisor can change by negotiation which take place in the stream of data.

I am trying to use TPM0 to do this using an initial modulus of 372 and no pre-scaling. I've had my head in the KL26 reference manual and been rattling around the forums (fora?) for several days now and would very much appreciate someone pointing out to me how stupid I am and more importantly how and where.

Here is my code so far followed by the test results it produces. I have a 3V signal running at about 300Hz on Pin 13 which flashes the LED (if I run the sig-gen slower). I have another LED on pin 23 (which just comes on and stays on). The delay(5000) statements just give me chance to start the serial monitor screen.

Code:
#include <print.h>
#include <avr/io.h>
#include <avr/interrupt.h>

uint16_t UICC_Li = 372;
bool LED_State = LOW;  // for debugging, NOT on Pin13
 
void setup () { //##################################################  Setup #######################################################
  Serial.begin(9600);  //USB serial port used for debugging
  pinMode(10, INPUT);   // Pin10:  SerialTX = HiZ for now                        
  pinMode(12,INPUT);    // SIM RESET Request
  pinMode(11, INPUT);   // SIM Clock
  pinMode(6, INPUT);    // SIM Powered (eg 1.8V)
  pinMode(5, OUTPUT);   // SIM Output Enable
  digitalWrite(5, LOW); // SIM Ourput Enable = False

  pinMode(23, OUTPUT); // LED for debugging
  LED_State = HIGH;
  digitalWrite(23, LED_State);

  delay(5000);  //Allow time to engage serial monitor
  Serial.println("Hello");
  
  dbgPrint(); // sends debugging information to usb serial
           //  INIT TPM0 for etu serial UICC IO clock
  //TPM0_SC = 0x0;   //clear
  
  TPM0_MOD = UICC_Li;  // fix the modulus for now (probably 372dec)
  // TMP0_CnV / TMP0_CnSC - None set for now, just using the raw counter overflow
  // TMP0_STATUS is only convenient copy of CnV and SC overflow flags (read-only?)
  TPM0_CONF = 0; //Clear  Don't think anything needs doing in here.

  TPM0_SC = 0;
  Serial.print("TMP0 Cleared ->  ");  
  dbgPrint();
  
  TPM0_SC = 0x50;;
  Serial.print("TMP0 Set x50 ->  ");  TPM0_SC = 0x50;
  dbgPrint();
  
  TPM0_SC |= (1<<7); //Clear Timer Overflow Flag
  Serial.print("TMP0TOF Clrd ->  ");  TPM0_SC = 0x50;
  dbgPrint();    
  
  //TPM0_SC |= (1<<6); // (BIT6) TOIE interrupt enable
  //TPM0_SC |= 0x10; // (BITS3&4) CMOD enabled (external pin) rising edge up counter 
  // so: TPM0 Enabled, NOT DMA, Interupt Enabled, Ext Clock, Up Count, no prescaling.    
  //  TPM0_SC |= (1<<7); //Clear Timer Overflow Flag   
  //CORE_PIN13_CONFIG = PORT_PCR_MUX(3);

  delay(1000);  
  TPM0_CNT = 0;
  Serial.println("Hello");
} // Setup section


void loop () { //##################################################  Loop #######################################################
  dbgPrint();
  delay(1000);
}

void FTM0_isr(void) 
{ 
    TPM0_SC |= (1<<7); //Clear Timer Overflow Flag
    LED_State = !LED_State;
    digitalWrite(23, LED_State);
    Serial.println(" Interrupt Handled!");
}

void dbgPrint(void)
{
  Serial.print("TPM0_SC : ");
  Serial.print(TPM0_SC, HEX);
  Serial.print("    TPM0_CNT : ");
  Serial.print(TPM0_CNT, HEX); 
  Serial.print("   TPM0_MOD : ");
  Serial.println(TPM0_MOD, HEX);
 
}


And the results:

Code:
Hello
TPM0_SC : 89    TPM0_CNT : B6CC   TPM0_MOD : BFFF
TMP0 Cleared ->  TPM0_SC : 81    TPM0_CNT : B947   TPM0_MOD : 174
TMP0 Set x50 ->  TPM0_SC : D0    TPM0_CNT : B947   TPM0_MOD : 174
TMP0TOF Clrd ->  TPM0_SC : D0    TPM0_CNT : B947   TPM0_MOD : 174
Hello
TPM0_SC : D0    TPM0_CNT : 0   TPM0_MOD : 174
TPM0_SC : D0    TPM0_CNT : 0   TPM0_MOD : 174
TPM0_SC : D0    TPM0_CNT : 0   TPM0_MOD : 174
TPM0_SC : D0    TPM0_CNT : 0   TPM0_MOD : 174
TPM0_SC : D0    TPM0_CNT : 0   TPM0_MOD : 174
TPM0_SC : D0    TPM0_CNT : 0   TPM0_MOD : 174
TPM0_SC : D0    TPM0_CNT : 0   TPM0_MOD : 174
TPM0_SC : D0    TPM0_CNT : 0   TPM0_MOD : 174
TPM0_SC : D0    TPM0_CNT : 0   TPM0_MOD : 174
 
I've never used the TPM timer this way. But from the manual, it seems PTE29 (the DAC pin) is the only pin available on Teensy LC which can be used as a clock for the TPM timer. According to page 177, you'll need to configure its pin mux for ALT4, as well as configuring the TPM timer to actually use it.

However, I have used the LPTMR timer to count external pulses. Look at the FreqCount library code for an example.
 
Hi Paul, thanks very much for the input. In my deep confusion I had conflated your LPTMRn stuff in FreqCounter with the TPMn stuff.

I don't think PTE29 is available on the LC, it is an unconnected pin (on the schematic), so I'm stuck with LPTMR0.

I have just tried converting my code to to use LPTMR0 and it doesn't seem to work. The first contact to the registers (even a mere read of LPTMR0_CSR) just crashes the program. Is it possible that my use of delay() conflicts?

Where would I find reference to the hardware being used by the TeensyDuino environment/libraries?
 
Current Code:

Code:
#include <print.h>
#include <avr/io.h>
#include <avr/interrupt.h>

uint16_t UICC_Li = 372;
bool LED_State = LOW;  // for debugging, NOT on Pin13
 
void setup () { //##################################################  Setup #######################################################
  Serial.begin(9600);  //USB serial port used for debugging
  pinMode(10, INPUT);   // Pin10:  SerialTX = HiZ for now                        
  pinMode(12,INPUT);    // SIM RESET Request
  pinMode(11, INPUT);   // SIM Clock
  pinMode(6, INPUT);    // SIM Powered (eg 1.8V)
  pinMode(5, OUTPUT);   // SIM Output Enable
  digitalWrite(5, LOW); // SIM Ourput Enable = False

  pinMode(23, OUTPUT); // LED for debugging
  LED_State = HIGH;
  digitalWrite(23, LED_State);

  delay(5000);  //Allow time to engage serial monitor
  Serial.println("Hello");
  
  dbgPrint(); // sends debugging information to usb serial
           //  INIT TPM0 for etu serial UICC IO clock
  SIM_SCGC5 |= SIM_SCGC5_LPTIMER;  
  LPTMR0_CSR = 0x0;   //clear
  LPTMR0_PSR = 0b00000100; // bypass prescaler/filter
  LPTMR0_CMR = UICC_Li;  // fix the modulus for now (probably 372dec)
  LPTMR0_CSR |= (1<<6);  // Interupt Enable
  LPTMR0_CSR = 0b00100110; // counter, input=alt2, free running mode
  CORE_PIN13_CONFIG = PORT_PCR_MUX(3);
  LPTMR0_CSR |= 1;
  LPTMR0_CSR |= (1<<7); //Clear Timer Overflow Flag

  delay(1000);  
  dbgPrint(); // sends debugging information to usb serial

  Serial.println("End Setup");
} // Setup section


void loop () { //##################################################  Loop #######################################################
  Serial.println("Loop");
  dbgPrint();
  delay(1000);
}

//void FTM0_isr(void) 
void LPTMR_isr(void)
{ 
    Serial.println(" Interrupt Handled!");
    //LPTMR0_CSR |= (1<<7); //Clear Timer Overflow Flag
    LED_State = !LED_State;
    digitalWrite(23, LED_State);
}

void dbgPrint(void)
{
Serial.print("LPTMR0_CSR : ");
 Serial.print(LPTMR0_CSR, HEX);
  Serial.print("    LPTMR0_CNR : ");
 Serial.print(LPTMR0_CNR, HEX); 
  Serial.print("   LPTMR0_CMR : ");
 Serial.println(LPTMR0_CMR, HEX);
 
}

Current output:

Code:
Hello
LPTMR0_CSR :
 
I haven't run your sketch but you need to clear the interrupt in LPTMR_isr() LPTMR0_CSR |= LPTMR_CSR_TCF;, otherwise the sketch will be in a tight loop spinning in the isr.

and you probably don't want to do Serial.print() in an isr
 
Thanks manitou. I think I was resetting the flag with a (1<<7) but I've substituted for your code now. The Serial.prints are just for debugging. I had a couple of other problems:

1. I had the name of the isr incorrectly capitalised.
2. I was missing the NVIC_ENABLE_IRQ(IRQ_LPTMR); line. (Not sure what this is, never found that in the 900 page datasheet! I only really read chapters 31 and 33).

But mainly ANY read to the LPTMR registers crashes the system, like the ones in my debug function. Funny that, it didn't happen with the TPM registers. It took me a while to get to testing that idea, even though I mentioned it earlier. I guess neither Paul or yourself have come across that one.

My test LED is now flashing gaily (and slowly).


Thanks to both of you for your crucial assistance.
 
But mainly ANY read to the LPTMR registers crashes the system, like the ones in my debug function. Funny that, it didn't happen with the TPM registers. It took me a while to get to testing that idea, even though I mentioned it earlier. I guess neither Paul or yourself have come across that one.
.
Your dbgPrint() comes BEFORE you've enabled the LPTMR (SIM_SCGC5 |= SIM_SCGC5_LPTIMER; ), dbgPrint() needs to be after that.

TPM/FTM timers are enabled as part of core startup.
 
Last edited:
Aaaah. That's another line that I copied from your code without really understanding it. Another page in the manual that I didn't find.

Thanks again for your excellent support.
 
Status
Not open for further replies.
Back
Top