Code:
//Teensy361 - Generate precise 211Hz and 531 Hz
//=============================================
//Author: TelephoneBill
//Date: Thu 12 OCT 2017
//240 MHz CPU speed = 60 MHz Peripheral Clock speed (16.667 nS cycle).
//FTM page in Reference Manual - Page 783.
//NOTES
//-----
//Teensy PINS:
//Pin16 = output - 500 KHz reference sq wave.
//Pin20 = output - 1 MHz strobe pulse (ISR time) - ISR fires on both +ve and -ve edges of FTM1.
//Pin21 = 211 Hz output.
//Pin22 = 531 Hz output.
//Keyboard Command Keys: (case sensitive) Entered via serial monitor
// "q2000" will set 211 Hz to 2 mSec "on" duty cyle
// "q" alone will print current 211 Hz duty cycle (uSecs)
// "a1000" will set 531 Hz to 1 mSec "on" duty cyle
// "a" alone will print current 531 Hz duty cycle (uSecs)
//declarations
#include <stdint.h>
#define LED_ON GPIOC_PSOR=(1<<5)
#define LED_OFF GPIOC_PCOR=(1<<5)
bool ControlOn, DisplayOn; //false=0x00, true=0x01
int Byte1, Byte2, Byte3, Byte4, Byte5; // for Keyboard input via incoming serial data
volatile unsigned int MilliSecs, Secs, DispSecs, MOD1Value, FTM1CountOVFlow, IntCount, dIntCount, LoopCount, dFTM1CountOVFlow, dLoopCount;
volatile unsigned int Output211Count, Output531Count, Output211Control, Output531Control;
//Main Program Setup
//------------------
void setup() {
//initialise general hardware
PORTC_PCR5 = PORT_PCR_MUX(0x1); //LED PC5 pin 13, config GPIO alt = 1
GPIOC_PDDR = (1<<5); //make this an output pin
LED_OFF; //start with LED off
pinMode(20, OUTPUT); //pin 20 as digital output
pinMode(21, OUTPUT); //pin 21 as digital output
pinMode(22, OUTPUT); //pin 22 as digital output
digitalWriteFast(20, 0); //set pin 20 low
digitalWriteFast(21, 0); //set pin 21 low
digitalWriteFast(22, 0); //set pin 22 low
Serial.begin(250000); //setup serial port
//initialise Flextimer 1
MOD1Value = 59; //modulus = 59 (gives count = 120 on each half cycle roll-over) - 60 MHz divided by 120 = 500 KHz)
FTM1_MODE = 0x05; //set write-protect disable (WPDIS) bit to modify other registers
FTM1_SC = 0x00; //set status/control to zero = disabled (enabled in main loop)
FTM1_CNT = 0x0000; //reset count to zero
FTM1_MOD = MOD1Value; //set modulus value
FTM1_C0SC = 0x14; // CHF=0, CHIE=0 (disable interrupt, use software polling), MSB=0 MSA=1, ELSB=0 ELSA=1 (output compare - toggle), 0, DMA=0
FTM1_C1SC = 0x0c; // CHF=0, CHIE=0 (disable interrupt, use software polling), MSB=0 MSA=0, ELSB=1 ELSA=1 (input capture - rising or falling edge), 0, DMA=0
FTM1_C0V = 0; //compare value = 0 for CH0
//enable FTM1 interrupt within NVIC table
NVIC_ENABLE_IRQ(IRQ_FTM1);
//lower Systick priority and raise IRQ_FTM priority
SCB_SHPR3 = 0x20200000; //Systick = priority 32 (default was zero - highest priority)
NVIC_SET_PRIORITY(IRQ_FTM1, 0); //raise FTM1 IRQ to priority 0
//configure Teensy FTM1 output compare channels
PORTB_PCR0 |= 0x300; //MUX = alternative function 3 on FTM1_CH0 = Teensy Pin 16
//(Note - This makes TEENSY Pin 16 a 1 MHz output reference signal)
//enable system clock (60 MHz), no prescale
FTM1_SC = 0x48; // (Note - FTM1_SC [TOF=0 TOIE=1 CPWMS=0 CLKS=01 (System Clock 60 MHz) PS=000 [no prescale divide])
//initialise general variables
OSC0_CR = 0x02; //decade incremental slower digital capacitor setting (0x02 = -3.109)
FTM1CountOVFlow = 0;
IntCount = 0;
DisplayOn = true;
Output211Control = 2370; //default control value for 211 Hz
Output531Control = 941; //default control value for 531 Hz
//blink 4 times
Blink();
delay(400);
Blink();
delay(400);
Blink();
delay(400);
Blink();
delay(400);
//initialise counters and controls
} //end of setup
// ISR routine for FlexTimer1 //FASTRUN puts this code into RAM to run twice as fast
//---------------------------
FASTRUN void ftm1_isr(void) {
//ISR latency time = 80 nanoSecs after interrupt flag set by FTM1 Timer Overflow
digitalWriteFast(20, 1); //set pin 20 high
//reset FTM1 overflow flag
if ((FTM1_SC & FTM_SC_TOF) != 0) { //read the timer overflow flag (TOF in FTM1_SC)
FTM1_SC &= ~FTM_SC_TOF; //if set, clear overflow flag
}
//adjust FTM1 counters
FTM1CountOVFlow++; //increment overflow counter (shows total number of interrupts)
IntCount++; //increment interrupt counter
//adjust Output211 and Output531 counters
Output211Count++;
if (Output211Count>=4739) {
Output211Count = 0;
digitalWriteFast(21, 1);
}
if (Output211Count>=Output211Control) {
digitalWriteFast(21, 0);
}
Output531Count++;
if (Output531Count>=1883) {
Output531Count = 0;
digitalWriteFast(22, 1);
}
if (Output531Count>=Output531Control) {
digitalWriteFast(22, 0);
}
//test for reset of interrupt counter (and update Secs, milliSecs)
if (IntCount>=1000) { //max value (999)
IntCount = 0; //reset interrupt counter
MilliSecs++; //increment MilliSecs
if (MilliSecs>=1000) { //MilliSecs has reached max value
Secs++; //increment Secs
MilliSecs = 0; //reset MilliSecs counter
}
}
//ISR end timing
digitalWriteFast(20, 0); //set pin 20 low
}
//Main Program Loop
//-----------------
void loop() {
//read key input (if any)
KeyInput(); //read key input (if any)
//update loop counter
LoopCount++;
//display data on each new second
if (Secs!=DispSecs) { //check for Secs update to display data
DispSecs = Secs;
CopyData(); //freeze a copy in case data changes before being displayed
if (DisplayOn) {
DisplayData(); //display data in serial monitor
}
Blink();
}
} //end of main loop
//SUBROUTINES
//===========
//Blink Routine
void Blink() {
//blink the LED
LED_ON;
delay(10);
LED_OFF;
delay(10);
}
//Copy Data Routine
void CopyData() {
dIntCount = IntCount;
dFTM1CountOVFlow = FTM1CountOVFlow;
dLoopCount = LoopCount;
}
//Display Routine
void DisplayData() {
//print to serial monitor
Serial.print("Secs ");
Serial.print(Secs);
Serial.print(", OVC = ");
Serial.print(dFTM1CountOVFlow);
Serial.print(", LC = ");
Serial.print(dLoopCount);
Serial.println();
Blink();
}
//KeyInput Routine
void KeyInput() {
//blink the LED
if (Serial.available()>0) {
//read the incoming byte
Byte1 = Serial.read();
if (Byte1>0x40) {
switch (Byte1) {
case 'q': //q - set duty cycle on period for 211 Hz in microSecs
//task goes here...
if (Serial.available()>=4) { //watch out for CRLF chars included
Byte2 = Serial.read();
Byte3 = Serial.read();
Byte4 = Serial.read();
Byte5 = Serial.read();
Output211Control = ((Byte2-48)*1000) + ((Byte3-48)*100) + ((Byte4-48)*10) + (Byte5-48);
if (Output211Control>4739) {Output211Control = 4739;}
}
Serial.print("211 Hz on period (uSecs) = "); Serial.println(Output211Control);
Blink();
break;
case 'a': //a - set duty cycle on period for 531 Hz in microSecs
//task goes here...
if (Serial.available()>=4) { //watch out for CRLF chars included
Byte2 = Serial.read();
Byte3 = Serial.read();
Byte4 = Serial.read();
Byte5 = Serial.read();
Output531Control = ((Byte2-48)*1000) + ((Byte3-48)*100) + ((Byte4-48)*10) + (Byte5-48);
if (Output531Control>1882) {Output531Control = 1882;}
}
Serial.print("531 Hz on period (uSecs) = "); Serial.println(Output531Control);
Blink();
break;
} //end of switch statement
} //end of Byte1>0x40
} //end of if Serial Available
}