#include <Wire.h>
#include <Adafruit_MCP4725.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 20, 4); // Set LCD address 20 chars and 4 lines
Adafruit_MCP4725 dac;
//-------------PIN SETTING------------//
int freqModPinA = 2; // Interrupt0 connected to CLK
int freqModPinB = 0; // connected to DT
int freqCarrierPinA = 3; // Interrupt1 connected to CLK
int freqCarrierPinB = 1;// connected to DT
int countdownPinA = 5;
int countdownPinB = 4;
int mode1pin = A0; // continuous pin
int mode2pin = A1; // dense-disperse pin
int mode3pin = A2; // intermittent pin
int mode4pin = A3; // sine pin
int mode5pin = A4; // sawtooth pin
int modeRESETpin = A5; // RESET pin
int PWM_pin = 9; // PWM output pin
//------------PARAMETERS SETTING------------------//
volatile bool executeTimer1 = false;
float divider = 100; // Need to test the rotary resolution
float modFreqChange = 0;
float carrierFreqChange = 0;
float upperLimit = 10;
float lowerLimit = 0;
float countdownTime = 0; // in MINUTE unit
float lastCountdownA;
float countdownValueA;
unsigned long minute = 60000; // 1 minute -> 1000 = 1 sec
unsigned long lastTime = 0;
unsigned long countupTime = 0;
unsigned long timeLeft;
unsigned long SawWaveInterval = 10; // 10ms constant
unsigned long LastSawWaveTime = 0;
unsigned long SineWaveInterval = 2; // 2ms constant
unsigned long LastSineWaveTime = 0;
unsigned long LastTimeMode2 = 0;
unsigned long LastTimeMode3 = 0;
boolean TmCheckMode2 = true;
boolean TmCheckMode3 = true;
boolean stateMode1 = false;
boolean stateMode2 = false;
boolean stateMode3 = false;
boolean stateMode4 = false;
boolean stateMode5 = false;
boolean RESETMode = false;
boolean countdownTimeChange;
boolean stateTimer;
int modeSelect;
//------------DISPLAY VARIABLES---------------//
#define DAC_RESOLUTION (9) // 512 bits
// Lookup table for sine signal
const PROGMEM uint16_t DACLookup_FullSine_9Bit[512] = {
2048, 2073, 2098, 2123, 2148, 2174, 2199, 2224,
2249, 2274, 2299, 2324, 2349, 2373, 2398, 2423,
2448, 2472, 2497, 2521, 2546, 2570, 2594, 2618,
2643, 2667, 2690, 2714, 2738, 2762, 2785, 2808,
2832, 2855, 2878, 2901, 2924, 2946, 2969, 2991,
3013, 3036, 3057, 3079, 3101, 3122, 3144, 3165,
3186, 3207, 3227, 3248, 3268, 3288, 3308, 3328,
3347, 3367, 3386, 3405, 3423, 3442, 3460, 3478,
3496, 3514, 3531, 3548, 3565, 3582, 3599, 3615,
3631, 3647, 3663, 3678, 3693, 3708, 3722, 3737,
3751, 3765, 3778, 3792, 3805, 3817, 3830, 3842,
3854, 3866, 3877, 3888, 3899, 3910, 3920, 3930,
3940, 3950, 3959, 3968, 3976, 3985, 3993, 4000,
4008, 4015, 4022, 4028, 4035, 4041, 4046, 4052,
4057, 4061, 4066, 4070, 4074, 4077, 4081, 4084,
4086, 4088, 4090, 4092, 4094, 4095, 4095, 4095,
4095, 4095, 4095, 4095, 4094, 4092, 4090, 4088,
4086, 4084, 4081, 4077, 4074, 4070, 4066, 4061,
4057, 4052, 4046, 4041, 4035, 4028, 4022, 4015,
4008, 4000, 3993, 3985, 3976, 3968, 3959, 3950,
3940, 3930, 3920, 3910, 3899, 3888, 3877, 3866,
3854, 3842, 3830, 3817, 3805, 3792, 3778, 3765,
3751, 3737, 3722, 3708, 3693, 3678, 3663, 3647,
3631, 3615, 3599, 3582, 3565, 3548, 3531, 3514,
3496, 3478, 3460, 3442, 3423, 3405, 3386, 3367,
3347, 3328, 3308, 3288, 3268, 3248, 3227, 3207,
3186, 3165, 3144, 3122, 3101, 3079, 3057, 3036,
3013, 2991, 2969, 2946, 2924, 2901, 2878, 2855,
2832, 2808, 2785, 2762, 2738, 2714, 2690, 2667,
2643, 2618, 2594, 2570, 2546, 2521, 2497, 2472,
2448, 2423, 2398, 2373, 2349, 2324, 2299, 2274,
2249, 2224, 2199, 2174, 2148, 2123, 2098, 2073,
2048, 2023, 1998, 1973, 1948, 1922, 1897, 1872,
1847, 1822, 1797, 1772, 1747, 1723, 1698, 1673,
1648, 1624, 1599, 1575, 1550, 1526, 1502, 1478,
1453, 1429, 1406, 1382, 1358, 1334, 1311, 1288,
1264, 1241, 1218, 1195, 1172, 1150, 1127, 1105,
1083, 1060, 1039, 1017, 995, 974, 952, 931,
910, 889, 869, 848, 828, 808, 788, 768,
749, 729, 710, 691, 673, 654, 636, 618,
600, 582, 565, 548, 531, 514, 497, 481,
465, 449, 433, 418, 403, 388, 374, 359,
345, 331, 318, 304, 291, 279, 266, 254,
242, 230, 219, 208, 197, 186, 176, 166,
156, 146, 137, 128, 120, 111, 103, 96,
88, 81, 74, 68, 61, 55, 50, 44,
39, 35, 30, 26, 22, 19, 15, 12,
10, 8, 6, 4, 2, 1, 1, 0,
0, 0, 1, 1, 2, 4, 6, 8,
10, 12, 15, 19, 22, 26, 30, 35,
39, 44, 50, 55, 61, 68, 74, 81,
88, 96, 103, 111, 120, 128, 137, 146,
156, 166, 176, 186, 197, 208, 219, 230,
242, 254, 266, 279, 291, 304, 318, 331,
345, 359, 374, 388, 403, 418, 433, 449,
465, 481, 497, 514, 531, 548, 565, 582,
600, 618, 636, 654, 673, 691, 710, 729,
749, 768, 788, 808, 828, 848, 869, 889,
910, 931, 952, 974, 995, 1017, 1039, 1060,
1083, 1105, 1127, 1150, 1172, 1195, 1218, 1241,
1264, 1288, 1311, 1334, 1358, 1382, 1406, 1429,
1453, 1478, 1502, 1526, 1550, 1575, 1599, 1624,
1648, 1673, 1698, 1723, 1747, 1772, 1797, 1822,
1847, 1872, 1897, 1922, 1948, 1973, 1998, 2023
};
//-----------------------------------------------------------------------------------------------//
void setup() {
Serial.begin(9600);
lcd.init();
cli(); // Pause interrupts
//Timer1 Configuration
TCCR1A = 0;
TCCR1B = 0;
TCNT1 = 0;
OCR1A; // Set outputCompareRegisters
TCCR1B |= _BV(WGM12) | (1 << CS12) | (1 << CS10); // Set WGM = CTC and set prescalar CS = 1024 bits
TCCR1B &= ~(1 << CS11);
TIMSK1 |= _BV(OCIE1A); // Enable timer interrupt
sei(); // Enable interrupts
// Set the value that the counter has to reach before it trigger an interrupt
// Counter1 is 16-bit so the value must be less than or equal to 65535
// prescalar equals to 1024
// system clock freq = 16MHz Therefore, timer clock freq = 16M/1024 = 15625
dac.begin(0x60); // 1st D/A -> Set for sine and sawtooth wave
Wire.setClock(400000L);
pinMode(mode1pin, INPUT_PULLUP);
pinMode(mode2pin, INPUT_PULLUP);
pinMode(mode3pin, INPUT_PULLUP);
pinMode(mode4pin, INPUT_PULLUP);
pinMode(mode5pin, INPUT_PULLUP);
pinMode(countdownPinA, INPUT);
pinMode(countdownPinB, INPUT);
pinMode(freqCarrierPinA, INPUT);
pinMode(freqCarrierPinB, INPUT);
pinMode(freqModPinA, INPUT);
pinMode(freqModPinB, INPUT);
pinMode(PWM_pin, OUTPUT);
attachInterrupt(digitalPinToInterrupt(freqCarrierPinA), carrierFrequencyCount, FALLING);
attachInterrupt(digitalPinToInterrupt(freqModPinA), modulatedFrequencyCount, FALLING);
lastCountdownA = digitalRead(countdownPinA);
}
//---------------------------------------GENERATE PWM (FINISHED)----------------------------------------------//
ISR(TIMER1_COMPA_vect) // The MCU calls this func. whenever the timer resets itself
{
//-------------------------------------------------------------------------------------------------------------//
// continuous mode (FINISHED)
if ((stateMode1 || stateMode4 || stateMode5) && stateTimer) {
generateMode1();
executeTimer1 = false; // Reset the flag
}
//-------------------------------------------------------------------------------------------------------------//
// dense-disperse mode (FINISHED)
if (stateMode2 && stateTimer) {
generateMode2();
executeTimer1 = false; // Reset the flag
}
//------------------------------------------------------------------------------------------------------------//
// intermittent mode (FINISHED)
if (stateMode3 && stateTimer) {
generateMode3();
executeTimer1 = false;
}
}
//-------------------------------GENERATE CONTINUOUS PULSE(FINISHED)------------------------------------------//
void generateMode1() {
OCR1A = 4692 + (carrierFreqChange * 630.8); // OCR1A value | carrierFreqChange = 0-10 | OCR1A = 11000 -> 1.42Hz -> 0.7s | OCR1A = 4692 -> 3.33Hz -> 0.303s
digitalWrite(PWM_pin, HIGH);
delayMicroseconds(450); // constant 450 us pulse width
digitalWrite(PWM_pin, LOW);
// Delay 300-700ms from OCR1A setting
}
//-------------------------------GENERATE DENSE-DISPERSE PULSE(FINISHED)--------------------------------------//
void generateMode2() {
float Tm1Mode2 = (0.5 + (modFreqChange * 0.15))*1000; // modFreqChange = 0-10 | Tm1 = 0.5s-2s
float Tm2Mode2 = 1.5*Tm1Mode2; // Tm2 = 0.75s - 3s
unsigned long currentTimeMode2 = millis();
if (TmCheckMode2) { // Tm1 timing with Tc1
OCR1A = 2342 + (carrierFreqChange * 235); // OCR1A value | OCR1A = 2342 -> 6.67Hz -> 0.15s | OCR1A = 4692 -> 3.33Hz -> 0303s
if (currentTimeMode2 - LastTimeMode2 >= Tm1Mode2) {
TmCheckMode2 = !TmCheckMode2;
LastTimeMode2 = currentTimeMode2;
}
digitalWrite(PWM_pin, HIGH);
delayMicroseconds(450);
digitalWrite(PWM_pin, LOW);
}
else { // Tm2 timing with Tc2
OCR1A = 375; // OCR1A value -> OCR1A = 375 -> 41.67Hz -> 24ms constant
if (currentTimeMode2 - LastTimeMode2 >= Tm2Mode2) {
TmCheckMode2 = !TmCheckMode2;
LastTimeMode2 = currentTimeMode2;
}
digitalWrite(PWM_pin, HIGH);
delayMicroseconds(450);
digitalWrite(PWM_pin, LOW);
}
}
//-------------------------------GENERATE INTERMITTENT PULSE(FINISHED)------------------------------------------//
void generateMode3() {
OCR1A = 2342 + (carrierFreqChange * 235); // OCR1A value | OCR1A = 2342 -> 6.67Hz -> 0.15s | OCR1A = 4692 -> 3.33Hz -> 0303s
float Tm1Mode3 = (0.5 + (modFreqChange * 0.15))*1000; // Tm1 = 0.5s-2s
float Tm2Mode3 = 1.5*Tm1Mode3; // Tm2 = 0.75s - 3s
unsigned long currentTimeMode3 = millis();
if (TmCheckMode3) { // Tm1 timing
if (currentTimeMode3 - LastTimeMode3 >= Tm1Mode3) {
TmCheckMode3 = !TmCheckMode3;
LastTimeMode3 = currentTimeMode3;
}
digitalWrite(PWM_pin, HIGH);
delayMicroseconds(450);
digitalWrite(PWM_pin, LOW);
}
else { // Tm2 timing
if (currentTimeMode3 - LastTimeMode3 >= Tm2Mode3) {
TmCheckMode3 = !TmCheckMode3;
LastTimeMode3 = currentTimeMode3;
}
digitalWrite(PWM_pin, LOW);
}
}
//-----------------------------------GENERATE SINE WAVE (FINISHED)--------------------------------------------//
void generateSineWave() {
static float phaseSine = 0;
static float phaseIncrementSine = 0.1 + (modFreqChange * 0.09); // Adjust for desired frequency
// phaseIncrement = 0.1 -> 0.1Hz || phaseIncrement = 1 -> 1Hz
unsigned long currentTimeSine = millis();
if (currentTimeSine - LastSineWaveTime >= SineWaveInterval) {
LastSineWaveTime = currentTimeSine;
dac.setVoltage(pgm_read_word(&(DACLookup_FullSine_9Bit[static_cast<uint16_t>(phaseSine)])), false);
// Increment the sine wave phase for the next sample
phaseSine += phaseIncrementSine;
if (phaseSine >= 512) {
phaseSine -= 512; // Wrap around when reaching the end of the lookup table
}
}
}
//-------------------------------------GENERATE SAW WAVE (FINISHED)-------------------------------------------//
void generateSawWave() {
static float phaseSaw = 0;
static float phaseIncrementSaw = 0.1 + (modFreqChange * 0.09); // Adjust for desired frequency
// phaseIncrement = 0.1 -> 0.1Hz || phaseIncrement = 1 -> 1Hz
unsigned long currentTimeSaw = millis();
if (currentTimeSaw - LastSawWaveTime >= SawWaveInterval) {
LastSawWaveTime = currentTimeSaw;
dac.setVoltage(static_cast<uint16_t>(40 * phaseSaw), false);
phaseSaw += phaseIncrementSaw;
if (phaseSaw >= 4095) {
phaseSaw -= 4095;
}
}
}
//-----------------------------------INTERRUPT CARRIER FREQUENCY ROTARY--------------------------------------//
void carrierFrequencyCount() {
int carrierValueB = digitalRead(freqCarrierPinB);
// SET UPPER AND LOWER LIMIT OF CarrierFreqChange -> 0-10
if (carrierValueB == LOW && carrierFreqChange < upperLimit) { // Direction = CW
carrierFreqChange += 1/divider;
}
else if (carrierValueB != LOW && carrierFreqChange > lowerLimit) { // Direction = CCW
carrierFreqChange -= 1/divider;
}
else {
carrierFreqChange += 0;
}
Serial.println(carrierFreqChange);
}
//----------------------------------INTERRUPT MODULATED FREQUENCY ROTARY-------------------------------------//
void modulatedFrequencyCount() {
int modValueB = digitalRead(freqModPinB);
// NEED TO SET UPPER AND LOWER LIMIT OF ModFreqChange -> 0-10
if (modValueB == LOW && modFreqChange < upperLimit) { // Direction = CW
modFreqChange += 1/divider;
}
else if (modValueB != LOW && modFreqChange > lowerLimit) {
modFreqChange -= 1/divider;
}
else {
modFreqChange += 0;
}
Serial.println(modFreqChange);
}
//----------------------------------TIMER COUNTDOWN ROTARY-------------------------------------//
void countdownRotary() {
countdownValueA = digitalRead(countdownPinA);
if (countdownValueA != lastCountdownA) { // Rotating
if (digitalRead(countdownPinB) != countdownValueA && countdownTime < 60) { // Maximum 60 mins
countdownTime ++;
}
else if (digitalRead(countdownPinB) == countdownValueA && countdownTime > 0) { // Minimum 0 min
countdownTime --;
}
else {
countdownTime += 0;
}
countdownTimeChange = true;
}
lastCountdownA = countdownValueA;
}
//--------------------------------------TIMER COUNTDOWN----------------------------------------//
void timercountdown() {
if (countdownTimeChange) {
countupTime = 0;
countdownTimeChange = false;
}
if (countdownTime - countupTime > 0) {
unsigned long currentTime = millis();
if (currentTime - lastTime >= minute) {
lastTime = currentTime;
countupTime ++;
}
timeLeft = countdownTime - countupTime;
stateTimer = true;
Serial.println(timeLeft);
}
else {
stateTimer = false;
Serial.println("SET TIMER");
}
}
//---------------------------------------INTERFACE------------------------------------------------//
void display() {
lcd.backlight();
// Line 1
lcd.setCursor(0, 0);
lcd.print("STATUS:" + String());
// Line 2
lcd.setCursor(0, 1);
lcd.print("MODE:" + String());
lcd.setCursor(8, 1);
lcd.print("TIMER:" + String(timeLeft));
lcd.setCursor(16, 1);
lcd.print("mins");
// Line 3
lcd.setCursor(0, 2);
lcd.print("FREQ1:" + String());
// Line 4
lcd.setCursor(0, 3);
lcd.print("FREQ2:" + String());
}
//---------------------------------READ DATA-------------------------------------------//
void readData() {
stateMode1 = (digitalRead(mode1pin) == LOW) ? true : false; // true -> only stateMode1 works -> set other modes to false
stateMode2 = (digitalRead(mode2pin) == LOW) ? true : false; // true -> only stateMode2 works -> set other modes to false
stateMode3 = (digitalRead(mode3pin) == LOW) ? true : false; // true -> only stateMode3 works -> set other modes to false
stateMode4 = (digitalRead(mode4pin) == LOW) ? true : false; // true -> stateMode1 and stateMode4 work concurrently -> set other modes to false
stateMode5 = (digitalRead(mode5pin) == LOW) ? true : false; // true -> stateMode1 and stateMode5 work concurrently -> set other modes to false
RESETMode = (digitalRead(modeRESETpin) == LOW) ? true : false; // true -> set all modes to false
if (stateMode1) {
modeSelect = 1;
}
if (stateMode2) {
modeSelect = 2;
}
if (stateMode3) {
modeSelect = 3;
}
if (stateMode4) {
modeSelect = 4;
}
if (stateMode5) {
modeSelect = 5;
}
if (RESETMode) {
modeSelect = "S";
modFreqChange = 0;
carrierFreqChange = 0;
countdownTime = 0;
}
}
//----------------------------------LOOP--------------------------------------------//
void loop() {
readData();
// TIMER countdown
countdownRotary();
timercountdown();
// Generate modulated signal
if (stateMode4 && stateTimer) {
generateSineWave();
}
if (stateMode5 && stateTimer) {
generateSawWave();
}
// Display interface
display();
}
// frequency adjusting = sample rate / (512/phaseIncrement)
// frequency adjusting = 1 / (time interval*(numbers per cyle))
// CALCULATION
// phaseIncrement for sine = (frequency*512) / sample rate
// 1Hz; phaseIncrement = 512/50 = 10.24 = 10
// 0.1Hz; phaseIncrement = (0.1*512)/50 = 1
// phaseIncrement for tri = (frequency*4096) / sample rate
// 1Hz; phaseIncrement = 4096/50 = 82
// 0.1Hz phaseIncrement = (0.1*4096)/50 = 8
//---------------------------------------------------------------------------------------------------------------//