#include <FreqMeasureMulti.h>
#include <SPI.h>
#include <SD.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define W 6 // Single character width
#define H 8 // Single character height
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
#define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_ADDRESS 0x3C //< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
#define SCROLL_WIDTH 23
// Pins
#define OUT1_PIN 6
#define OUT2_PIN 5
#define OUT3_PIN 4
#define OUT4_PIN 3
#define IN1_INPUT_PIN 22
#define IN2_INPUT_PIN 24
#define DEBUG_PIN 7
#define NUM_PWM_PINS 13 // Pins 0-12 are PWM, usable in setFrequency function
enum Input_Mode {
ANALOG,
IN1,
IN2
};
// Global variables
bool pinState[NUM_PWM_PINS];
bool speedMode[NUM_PWM_PINS]; // 0 for slow, 1 for fast
bool zeros[NUM_PWM_PINS]; // For state checking
float frequencyRead = 0;
float frequencyToSet = 0;
float Analog_HzPerCount; // Will be overwritten during setup
FreqMeasureMulti freq1;
IntervalTimer setFrequencyTimer;
Filt::FiltVal<float> analogFiltVolts(0, ANALOG_MIN_THRESH, ANALOG_MAX_THRESH, ANALOG_DVALPERSEC); // Initial, Lower, Upper, DeltaPerSec
void setup() {
Serial.begin(115200);
// Display configuration
dispConfig();
int cnt = 1000; // Will wait for Serial to connect, but timeout if taking too long
while (!Serial && cnt--) {
delay(1);
}
SDInit_Config(); // Read in SD configuration
dispSetup(); // Display more useful information
if(inputMode == IN1){ // IN1 input mode
freq1.begin(IN1_INPUT_PIN);
}
else if(inputMode == IN2){ // IN2 clock input mode
freq1.begin(IN2_INPUT_PIN);
}
pinMode(OUT4_PIN, OUTPUT);
pinMode(OUT3_PIN, OUTPUT);
pinMode(OUT2_PIN, OUTPUT);
pinMode(OUT1_PIN, OUTPUT);
setFrequencyTimer.begin(setPinFrequencies, FREQ_SET_INTERVAL);
setFrequencyTimer.priority(0);
}
void loop() {
if (((inputMode == IN1) || (inputMode == IN2)) && freq1.available()) { // If in frequency mode and a reading is ready
freqModeRead();
}
else if(inputMode == ANALOG){ // Analog mode
analogModeRead();
}
noInterrupts();
frequencyToSet = frequencyRead; // Save a safe copy of frequencyRead for the interrupt to use
interrupts();
dispUpdate(frequencyRead);
}
void setPinFrequencies(){
setFrequency(OUT1_PIN, frequencyToSet*OUT1_Scale);
setFrequency(OUT2_PIN, frequencyToSet*OUT2_Scale);
setFrequency(OUT3_PIN, frequencyToSet*OUT3_Scale);
setFrequency(OUT4_PIN, frequencyToSet*OUT4_Scale);
}
// Read frequency data
void freqModeRead(){
static double sum=0;
static int count=0;
// average several reading together, copied from example
sum = sum + freq1.read();
count = count + 1;
if (count > 1) { // Average 2 samples
frequencyRead = freq1.countToFrequency(sum / count);
sum = 0;
count = 0;
Serial.print(F("Freq = "));
Serial.println(frequencyRead);
}
}
// Read analog to get frequency data
void analogModeRead(){
if(sinceAnalogSample >= ANALOG_SAMPLE_INTERVAL){
sinceAnalogSample = 0;
float analogVal = analogRead(IN2_INPUT_PIN);
analogFiltVolts.Update(analogVal/COUNTS_PER_VOLT);
if(analogFiltVolts.IsSaturated() == '+'){ // Top of scale
frequencyRead = ANALOG_MAXFREQ;
}
else if((analogFiltVolts.IsSaturated() == '-') || ((analogFiltVolts.Out - ANALOG_MIN_THRESH)*Analog_HzPerVolt) < MIN_FREQ){ // Bottom of scale or too slow to output
frequencyRead = 0;
}
else{ // Normal range
frequencyRead = (analogFiltVolts.Out - ANALOG_MIN_THRESH)*Analog_HzPerVolt;
}
}
}
// Set frequency of pin using analogWriteFrequency or call to setFrequencySlow, with hysteresis between them
void setFrequency(int pin, float frequencyToSet){
unsigned long currentMillis = millis(); // capture the latest value of millis() for using in slow mode
if(frequencyToSet > (FASTSLOW_THRESH + FASTSLOW_HYST)){ // Fast mode
analogWriteFrequency(pin, frequencyToSet); // set frequency
analogWrite(pin, 128); // reset duty cycle to 50%
speedMode[pin] = 1; // Remember we're in fast mode
} else if((frequencyToSet < FASTSLOW_THRESH - FASTSLOW_HYST) && (frequencyToSet > MIN_FREQ)){ // Slow mode but not stopped
setFrequencySlow(pin, frequencyToSet, currentMillis);
speedMode[pin] = 0; // Remember we're in slow mode
} else if(speedMode[pin] == 1){ // Inbetween, fast still
pinState[pin] = 1; // Start high coming out of fast mode
analogWriteFrequency(pin, frequencyToSet); // set frequency
analogWrite(pin, 128); // reset duty cycle to 50%
}
else if((speedMode[pin] == 0) && (frequencyToSet > MIN_FREQ)){ // Inbetween, slow still but not stopped
setFrequencySlow(pin, frequencyToSet, currentMillis);
}
else if(frequencyToSet <= MIN_FREQ){ // 0 speed, stop output
digitalWrite(pin, LOW);
}
}
// Bit-bang outputs at f<18 Hz, slower than analogWriteFrequency can do
void setFrequencySlow(int pin, float frequencyToSet, unsigned long currentMillis){
//[Bit-bang outputs at slow frequency]
}
// Output static info on display
void dispSetup(){
//[Display some basic info]
displayPrintConstants();
display.display();
}
// Initial display configuration
void dispConfig(){
Wire.setSDA(18); // Teensy 4.1
Wire.setSCL(19); // Teensy 4.1
// SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
Serial.println(F("SSD1306 allocation failed"));
for(;;); // Don't proceed, loop forever //TODO: Remove infinite loops, allow critical functions to work without display
}
// Dim a bit
display.ssd1306_command(SSD1306_SETCONTRAST); // 0x81
display.ssd1306_command(0x01);
// Clear the buffer
display.clearDisplay();
display.setTextSize(0); // Normal 1:1 pixel scale
//display.setFont(&FreeMono9pt7b);
//display.setTextColor(SSD1306_WHITE); // Draw white text with "transparent" background - text will glob over itself if updated
display.setTextColor(SSD1306_WHITE,SSD1306_BLACK); // Draw white text with black backgroud - changes a character
display.cp437(true); // Use real 256 char 'Code Page 437' font
display.setTextWrap(false);
display.display();
}
// Update useful info on display
void dispUpdate(float freqToUpdate){
if(sinceDisplayUpdate > DISP_UPDATE_INTERVAL){
sinceDisplayUpdate = 0;
//[Update a bunch of info using display.print()]
if(sinceDisplayConstUpdate > DISP_CONST_UPDATE_INTERVAL){
sinceDisplayConstUpdate = 0;
displayPrintConstants();
}
// DEBUG: Monitor how long display.display() takes
digitalWrite(DEBUG_PIN, HIGH);
display.display();
digitalWrite(DEBUG_PIN, LOW);
}
}
// Initialize SD card and read config
void SDInit_Config(){
//[Read in configuration from SD card]
}
// Re-update the scale and unchanging text on the display incase it gets corrupted over time, does not call display.display()
void displayPrintConstants(){
//[Update a bunch of info on the display, don't call display.display()]
}