I realized I has been subtracting the wrong way which cause the debounce to not work. I've fixed this now. I also added a debounce to the arming pin.
277 lines
12 KiB
C++
277 lines
12 KiB
C++
/*
|
|
* Code Written by Quinn Henthorne: quinn.henthorne@gmail.com
|
|
* 10/7/2021
|
|
*
|
|
* Pinout List
|
|
* Dac0 - Waveform output
|
|
* Dac1 - Amplitude output
|
|
* D2 - Synch Pin Output (For debugging purposes)
|
|
* D22 - Ampltidue Encoder Input
|
|
* D23 - Amplitude Encoder Input
|
|
* D24 - Period Encoder Input
|
|
* D25 - Period Encoder Input
|
|
* D26 - Nothing
|
|
* D27 - Single pulse armed pin. toggles the armed state when it recieves a falling signal
|
|
* D28 - Single pulse enable pin (A FALLING signal toggles single pulse mode)
|
|
* D29 - Single pulse trigger. (LOW will cause the arduino to send 1 waveform pulse)
|
|
* D30 - Slow pulse flag (LOW enables a pulse rate of 3 pulses/second)
|
|
* D31 - Armed state indicator light output
|
|
* D32 - Fired state indicator light output
|
|
*/
|
|
|
|
|
|
// Define pinouts
|
|
#define waveform_pin DAC0
|
|
#define amplitude_pin DAC1
|
|
#define sync_pin 2
|
|
#define single_pulse_arm_pin 27
|
|
#define single_pulse_enable_pin 28
|
|
#define single_pulse_trig_pin 29
|
|
#define slow_pulse_flag_pin 30
|
|
#define armed_indicator_pin 31
|
|
#define fired_indicator_pin 32
|
|
|
|
//Include necessary libraries
|
|
#include <Arduino.h>
|
|
#include <Encoder.h>
|
|
#include <LiquidCrystal_I2C.h>
|
|
|
|
|
|
//uncomment to enable serial output for debugging
|
|
#define enable_serial_debug
|
|
//uncomment to enable lcd output
|
|
#define enable_lcd
|
|
|
|
/* Changes the number of array points to sample. If 1, it will sample every point.
|
|
* If 2, it will sample every other point,. If four it will sample every 4 points, etc.
|
|
*/
|
|
#define sample_resolution 4
|
|
|
|
// Number of waveforms available. Change this if you add or remove waveforms
|
|
#define waveform_num 4
|
|
// Array of waveform arrays. Currently holds four waveforms with 600 samples each.
|
|
int default_waveform[600] = {2048,4093,4091,4088,4085,4083,4080,4077,4075,4072,4069,4066,4064,4061,4058,4055,4052,4050,4047,4044,4041,4038,4036,4033,4030,4027,4024,4021,4018,4015,4012,4009,4007,4004,4001,3998,3995,3992,3989,3986,3983,3980,3977,3973,3970,3967,3964,3961,3958,3955,3952,3949,3946,3942,3939,3936,3933,3930,3926,3923,3920,3917,3913,3910,3907,3904,3900,3897,3894,3890,3887,3884,3880,3877,3874,3870,3867,3863,3860,3856,3853,3850,3846,3843,3839,3836,3832,3829,3825,3821,3818,3814,3811,3807,3803,3800,3796,3792,3789,3785,3781,3778,3774,3770,3767,3763,3759,3755,3751,3748,3744,3740,3736,3732,3728,3725,3721,3717,3713,3709,3705,3701,3697,3693,3689,3685,3681,3677,3673,3669,3665,3661,3657,3652,3648,3644,3640,3636,3632,3627,3623,3619,3615,3610,3606,3602,3598,3593,3589,3585,3580,3576,3571,3567,3563,3558,3554,3549,3545,3540,3536,3531,3527,3522,3518,3513,3508,3504,3499,3494,3490,3485,3480,3476,3471,3466,3462,3457,3452,3447,3442,3437,3433,3428,3423,3418,3413,3408,3403,3398,3393,3388,3383,3378,3373,3368,3363,3358,3353,3348,3342,3337,3332,3327,3322,3316,3311,3306,3301,3295,3290,3285,3279,3274,3268,3263,3258,3252,3247,3241,3236,3230,3225,3219,3213,3208,3202,3197,3191,3185,3180,3174,3168,3162,3157,3151,3145,3139,3133,3127,3122,3116,3110,3104,3098,3092,3086,3080,3074,3068,3062,3055,3049,3043,3037,3031,3025,3018,3012,3006,3000,2993,2987,2981,2974,2968,2961,2955,2948,2942,2935,2929,2922,2916,2909,2903,2896,2889,2883,2876,2869,2862,2856,2849,2842,2835,2828,2821,2815,2808,2801,2794,2787,2780,2773,2766,2758,2751,2744,2737,2048,0,3,5,8,11,13,16,19,22,24,27,30,33,35,38,41,44,47,49,52,55,58,61,64,66,69,72,75,78,81,84,87,90,93,96,99,102,105,108,111,114,117,120,123,126,129,132,135,138,142,145,148,151,154,157,161,164,167,170,173,177,180,183,186,190,193,196,200,203,206,210,213,216,220,223,227,230,233,237,240,244,247,251,254,258,261,265,268,272,276,279,283,286,290,294,297,301,305,308,312,316,319,323,327,331,334,338,342,346,350,353,357,361,365,369,373,377,381,384,388,392,396,400,404,408,412,416,420,425,429,433,437,441,445,449,453,458,462,466,470,474,479,483,487,491,496,500,504,509,513,518,522,526,531,535,540,544,549,553,558,562,567,571,576,580,585,590,594,599,604,608,613,618,622,627,632,637,641,646,651,656,661,666,670,675,680,685,690,695,700,705,710,715,720,725,730,735,741,746,751,756,761,766,772,777,782,787,793,798,803,809,814,819,825,830,836,841,847,852,858,863,869,874,880,885,891,897,902,908,914,919,925,931,937,943,948,954,960,966,972,978,984,990,996,1002,1008,1014,1020,1026,1032,1038,1044,1050,1056,1063,1069,1075,1081,1087,1094,1100,1106,1113,1119,1126,1132,1138,1145,1151,1158,1164,1171,1178,1184,1191,1197,1204,1211,1217,1224,1231,1238,1244,1251,1258,1265,1272,1279,1286,1293,1300,1307,1314,1321,1328,1335,1342,1349,2048
|
|
};
|
|
|
|
// A dynamic waveform that can change its values based on some scalar. This gets initialized at the start of the program
|
|
int dynamic_waveform[600];
|
|
|
|
//Waveform values
|
|
float waveform_vals[] = {1,2,3,4,5,6,7,8,9,10};
|
|
|
|
//Setup encoders and their corresponding variables
|
|
Encoder amp_encoder(22, 23);
|
|
long new_amp = 10;
|
|
long old_amp = 10;
|
|
Encoder period_encoder(24, 25);
|
|
long new_period = 4;
|
|
long old_period = 4;
|
|
|
|
|
|
//Setup values for arming and firing system
|
|
volatile boolean is_enabled = false;
|
|
volatile boolean is_armed = false;
|
|
boolean has_single_pulsed = false;
|
|
unsigned long debounce_timer = 0;
|
|
volatile unsigned long armed_debounce = 0;
|
|
|
|
// Toggles the armed state of single fire mode. (Only toggles is signle fire mdoe is already enabled.)
|
|
void arm_single_fire(){
|
|
if(is_enabled && millis()-armed_debounce > 100){
|
|
is_armed = !is_armed;
|
|
// Toggle the armed indicator light
|
|
digitalWrite(armed_indicator_pin, !digitalRead(armed_indicator_pin));
|
|
digitalWrite(fired_indicator_pin, LOW);
|
|
armed_debounce = millis();
|
|
}
|
|
}
|
|
|
|
// Toggles single fire mode
|
|
void enable_single_fire(){
|
|
is_enabled = !is_enabled;
|
|
}
|
|
|
|
//Create LCD object
|
|
LiquidCrystal_I2C lcd = LiquidCrystal_I2C(0x27, 16, 2);
|
|
|
|
/* Cycle through the waveform samples over a given period of time
|
|
* The time scalar argument changes how many samples it will skip.
|
|
* A value of 1 will not skip any samples, a value of 2 will use every 2nd sammple, and a value of 5 will use every 5th sample.
|
|
* This allows you to sacrifice resolution for speed. A value of 2 will take half the time to create a waveform pulse
|
|
*/
|
|
void generate_waveform(int time_scalar){
|
|
// The period of time each sample in the array should take in microseconds
|
|
// The 1000 converts from milliseconds to microseconds, and the 600 deivides by the number of samples in the array
|
|
// The -3 offset compensates for a 3 millisecond overhead created by the time it takes to do all of the calculations
|
|
unsigned int sample_period = (new_period-3)*1000/600;
|
|
|
|
// Timer to regulate the step width
|
|
int timer = 0;
|
|
|
|
//Loop through all of the samples in the array and output them to Dac 0
|
|
for(int i = 0; i < 600; i=i+time_scalar){
|
|
while(micros()-timer < sample_period);
|
|
analogWrite(waveform_pin, dynamic_waveform[i]);
|
|
timer = micros();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Recalculates the waveform given a new amplitude percentage
|
|
void recalc_waveform(float percent_amplitude){
|
|
float amp_scalar = float(percent_amplitude)/10; // Create ampltidue scalar between 0.1 and 1
|
|
float offset = 2048 - 2048*amp_scalar; //Generate offset to keep the waveform DC offset the same
|
|
|
|
// Scale the array and give it its new offset
|
|
for(int i = 0; i < 600; i++){
|
|
dynamic_waveform[i] = (int)(default_waveform[i]*amp_scalar + offset);
|
|
}
|
|
}
|
|
|
|
|
|
void setup() {
|
|
//Immediately set the analog output to the mid point
|
|
analogWrite(waveform_pin, 2048);
|
|
//The sync pin can be run into an oscilliscope's trig channel to easiy find the waveform
|
|
pinMode(sync_pin, OUTPUT);
|
|
// Setup the rest of the IO pins
|
|
pinMode(single_pulse_enable_pin, INPUT_PULLUP);
|
|
pinMode(single_pulse_arm_pin, INPUT_PULLUP);
|
|
pinMode(single_pulse_trig_pin, INPUT_PULLUP);
|
|
pinMode(slow_pulse_flag_pin, INPUT_PULLUP);
|
|
pinMode(armed_indicator_pin, OUTPUT);
|
|
pinMode(fired_indicator_pin, OUTPUT);
|
|
|
|
//Set up interrupts to simplify single fire mode:
|
|
// This will call the arm_single_fire() function whenever it detects a falling signal on this pin
|
|
attachInterrupt(digitalPinToInterrupt(single_pulse_arm_pin), arm_single_fire, FALLING);
|
|
// Will call the enable_single_fire function whenever it detects a falling signal on this pin
|
|
attachInterrupt(digitalPinToInterrupt(single_pulse_enable_pin), enable_single_fire, FALLING);
|
|
|
|
//Change the resolution of the analog ourput to its maximum (12 bit res)
|
|
analogWriteResolution(12);
|
|
|
|
// Initialize timers and encoders
|
|
amp_encoder.write(new_amp*4);
|
|
period_encoder.write(new_period*4);
|
|
|
|
// Only executes this block of code if serial debug is enabled
|
|
#ifdef enable_serial_debug
|
|
Serial.begin(115200);
|
|
Serial.print("Ampltidue: ");
|
|
Serial.println(waveform_vals[new_amp-1]);
|
|
Serial.print("Period: ");
|
|
Serial.println(new_period/sample_resolution);
|
|
#endif
|
|
|
|
// Loads currently selected waveform into the dynamic waveform
|
|
for(int i = 0; i < 600; i++) dynamic_waveform[i] = default_waveform[i];
|
|
|
|
//Init LCD
|
|
#ifdef enable_lcd
|
|
lcd.init();
|
|
lcd.backlight();
|
|
lcd.setCursor(0, 0);
|
|
lcd.print("Period:");
|
|
lcd.print((float)(new_period)/sample_resolution);
|
|
lcd.setCursor(0, 1);
|
|
lcd.print("Amplitude:");
|
|
lcd.print(waveform_vals[new_amp-1]);
|
|
#endif
|
|
}
|
|
|
|
|
|
|
|
void loop() {
|
|
// Read in encoder values
|
|
new_amp = amp_encoder.read()/4;
|
|
new_period = period_encoder.read()/4;
|
|
|
|
// Check to see if any of the encoder values have changed. If they have, update their values.
|
|
//Handles ampltidue encoder
|
|
if(new_amp != old_amp){
|
|
// Make sure the value is within a valid range
|
|
if(new_amp > 10){
|
|
//128 ampltidue steps should be enough granularity
|
|
new_amp = 10;
|
|
amp_encoder.write(new_amp*4);
|
|
}
|
|
if(new_amp < 1){
|
|
new_amp = 1;
|
|
amp_encoder.write(new_amp*4);
|
|
}
|
|
old_amp = new_amp;
|
|
//If serial is enabled, output some serial data
|
|
#ifdef enable_serial_debug
|
|
Serial.print("Ampltidue: ");
|
|
Serial.println(waveform_vals[new_amp-1]);
|
|
#endif
|
|
|
|
// If LCD output is enabled output to LCD
|
|
#ifdef enable_lcd
|
|
lcd.setCursor(10,1);
|
|
lcd.print(waveform_vals[new_amp-1]);
|
|
#endif
|
|
|
|
recalc_waveform(new_amp);
|
|
}
|
|
|
|
//Handles period encoder
|
|
if(new_period != old_period){
|
|
// Make sure the value is within a valid range
|
|
if(new_period > 100){
|
|
new_period = 100;
|
|
period_encoder.write(new_period*4);
|
|
}
|
|
//Anything less than three will cause errors
|
|
if(new_period < 4){
|
|
new_period = 4;
|
|
period_encoder.write(new_period*4);
|
|
}
|
|
old_period = new_period;
|
|
//If serial is enabled, output some serial data
|
|
#ifdef enable_serial_debug
|
|
Serial.print("Period (ms): ");
|
|
Serial.println((float)(new_period)/sample_resolution);
|
|
#endif
|
|
|
|
// If LCD output is enabled output to LCD
|
|
#ifdef enable_lcd
|
|
lcd.setCursor(7,0);
|
|
lcd.print((float)(new_period)/sample_resolution);
|
|
#endif
|
|
}
|
|
|
|
|
|
// Creates a synch signal so an oscilliscope can more easily read irregular pulses
|
|
digitalWrite(sync_pin, !digitalRead(sync_pin));
|
|
|
|
|
|
// Check to see if the single pulse pin has been pulled LOW
|
|
if(is_enabled){
|
|
if(is_armed && millis() - debounce_timer > 100){
|
|
if(!digitalRead(single_pulse_trig_pin) && has_single_pulsed == false){
|
|
generate_waveform(sample_resolution);
|
|
has_single_pulsed = true;
|
|
debounce_timer = millis();
|
|
digitalWrite(fired_indicator_pin, HIGH);
|
|
digitalWrite(armed_indicator_pin, LOW);
|
|
is_armed = false;
|
|
}
|
|
else if(digitalRead(single_pulse_trig_pin)) {
|
|
has_single_pulsed = false;
|
|
debounce_timer = millis();
|
|
}
|
|
}
|
|
if(millis()-debounce_timer>250){
|
|
digitalWrite(fired_indicator_pin, LOW);
|
|
}
|
|
}
|
|
else{
|
|
generate_waveform(sample_resolution);
|
|
// Slows down the output pulse if the slow pulse pin is pulled LOW
|
|
if(!digitalRead(slow_pulse_flag_pin)) delay(500);
|
|
}
|
|
|
|
} |