teensy3056
Active member
I am working to better understand the Teensy 3.6 microcontroller's many hidden capabilities.
I would like to get the PWM output from TPM1, channel 0 to pin 16 of the Teensy 3.6.
In the code below, I have been successful in implementing TPM1 overflow and channel 0 interrupts starting with the 4 MHz clock and dividing it to 62.5 kHz.
I cannot seem to get the PWM signal on pin 16. I have tried the code for TPM2 also to no avail.
There are a lot of comments and printf() statements so I could see what is happening. I may be missing the setting or clearing of 1 bit someplace.
The pdf. page numbers refer to the MK66FX1M0 Manual.pdf.
I would like to get the PWM output from TPM1, channel 0 to pin 16 of the Teensy 3.6.
In the code below, I have been successful in implementing TPM1 overflow and channel 0 interrupts starting with the 4 MHz clock and dividing it to 62.5 kHz.
I cannot seem to get the PWM signal on pin 16. I have tried the code for TPM2 also to no avail.
There are a lot of comments and printf() statements so I could see what is happening. I may be missing the setting or clearing of 1 bit someplace.
The pdf. page numbers refer to the MK66FX1M0 Manual.pdf.
Code:
/*
Name: Teensy_timers_14.ino
*/
#include <kinetis.h>
#include <core_pins.h>
//#define OVERFLOW_CONDITION
#define CHANNEL0_EVENT
//*****************************************************************************
// Prototypes
void ISR_TPM1(void);
//*****************************************************************************
// Global variables
volatile byte overflowOut = LOW;
volatile byte channel0Out = LOW;
volatile byte overflowFlag = 0;
volatile uint32_t resetValue = 0;
volatile uint32_t tempValue = 0;
//*****************************************************************************
void setup() {
Serial.begin(115200);
delay(1000);
Serial.printf("\nSerial print ready.\n");
// LED pin
pinMode(13, OUTPUT);
// TPM1, channel 0 pin
pinMode(CORE_TPM1_CH0_PIN, OUTPUT);
// Set MCG mode FEI, pdf. 646.
// Sets clock source, selects slow internal reference clock, pdf. 630.
MCG_C1 = 0b0000'0100;
// Sets FLL, pdf. 636.
MCG_C6 &= (~0b0100'0000);
//*********************************
// Choose oscillator for counter clock domain, pdf. 625.
// OSCCLK1/IRC48MCLK. Derived from internal 48 MHz oscillator.
MCG_C7 |= 0b10;
//Serial.printf("Here!!.\n");
//delay(20000);
// Setup timer, pdf. 630.
// Disable MCGIRCLK clock
MCG_C1 = 0b00000100;
// MCG_C2, MCG_C3, MCG_C4, MCG_C5, MCG_S left unchanged, pdf. 631ff.
// FCRDIV = 0b110 = divide by 64 -> 62.5 kHz.
MCG_SC = 0b00001100;
// MCG_C8, MCG_C9, (no MCG_C10), MCG_C11, MCG_C12 left in reset status, pdf. 641ff.
// Enable MCGIRCLK clock
MCG_C1 |= 0b10;
//*********************************
// Select MCGIRCLK clock as TPM counter clock domain, pdf. 238-9.
SIM_SOPT2 |= SIM_SOPT2_TPMSRC(3);
delay(1000);
Serial.printf("SIM_SOPT2 = %u\n", SIM_SOPT2);
// 50331648 has 0b11 in positions 25:24 and 0 elsewhere.
// The TPMSRC bit field is the two bits at positions 25:24.
tempValue = ((SIM_SOPT2 & 50331648) >> 24);
Serial.printf("SIM_SOPT2 bit field TPMSRC = %u\n", tempValue);
// Gate control for TPM1 & TPM2, gate open, pdf. 256.
SIM_SCGC2 |= (1 << 9) | (1 << 10);
//*********************************
// Settings for TPM1_SC, pdf. 1066ff
// Spin-wait for disablement of the TPM counter clock while options are set.
Serial.printf("Disable TPM counter clock for option setting.\n");
TPM1_SC &= (~0b1'0111'1111);
Serial.printf("01-TPM1_SC = %u\n", TPM1_SC);
while (0 != (TPM1_SC & 0b0'0001'1000)) {
TPM1_SC &= (~0b1'0111'1111);
Serial.printf("02-TPM1_SC = %u\n", TPM1_SC);
}
Serial.printf("TPM counter clock disabled.\n");
// Timer overflow interrupt, TOIE, enable.
TPM1_SC |= (1 << 6);
//TPM counter operates in up-counting mode, CPWMS.
TPM1_SC |= (0 << 5);
// Prescale factor, PS, initially set to 1.
TPM1_SC |= (0 << 0);
// Spin-wait for enablement of the TPM counter clock.
// Counter increments on every TPM counter clock, CMOD.
// I do not think the spin-wait is strictly necessary for enablement.
TPM1_SC |= (1 << 3);
Serial.printf("03-TPM1_SC = %u\n", TPM1_SC);
while (8 != (TPM1_SC & 0b0'0000'1000)) {
TPM1_SC |= (1 << 3);
}
Serial.printf("TPM counter clock is enabled.\n");
//*********************************
// Highest value and overflow value for the TPM counter.
TPM1_MOD = 62'500;
//*********************************
// Set and enable edge-aligned PWM
// Disable channel
Serial.printf("Spin-wait for channel 0 to be disabled.\n");
TPM1_C0SC &= ~0b0011'1100;
while (0 != (TPM1_C0SC & 0b00111100)) {
TPM1_C0SC &= ~0b0011'1100;
}
Serial.printf("Channel 0 disabled.\n");
// Changes - set to edge-aligned PWM,
// High-true pulses (clear Output on match, set Output on reload)
// pdf. 1069.
TPM1_C0SC |= 0b1110'1000;
Serial.printf("Channel 0 edge-aligned PWM set.\n");
// Channel 0 value to match, pdf. 1071.
TPM1_C0V = (uint16_t)32'768;
Serial.printf("Channel 0 match value set to: %u\n", TPM1_C0V);
//*********************************
attachInterruptVector(IRQ_TPM1, ISR_TPM1);
NVIC_ENABLE_IRQ(IRQ_TPM1);
Serial.printf("End of setup().\n");
delay(5000);
}
//*****************************************************************************
void loop() {
}
//*****************************************************************************
void ISR_TPM1(void) {
#ifdef OVERFLOW_CONDITION
// Overflow interrupt.
if (0b1'0000'0000 == (TPM1_STATUS & 0b1'0000'0000)) {
// Clears overflow interrupt flag.
TPM1_SC |= (1 << 7);
if (++resetValue > 0) {
resetValue = 0;
Serial.printf("%s\n", "Interrupt called.");
overflowOut = !overflowOut;
digitalWrite(13, overflowOut);
}
}
#endif
#ifdef CHANNEL0_EVENT
// Channel 0 event.
if (0b0'0000'0001 == (TPM1_STATUS & 0b0'0000'0001)) {
// Clear channel 0 event flag.
TPM1_STATUS |= (1 << 0);
TPM1_C0SC |= (1 << 7);
channel0Out = !channel0Out;
Serial.printf("%s\n", "Channel 0 interrupt called.");
digitalWrite(13, channel0Out);
}
#endif
}