/*
 * Rectangular Open Loop Control for Stepper Motors
 * February 2022
 * www.Freyer-Didactics.de
 * 
 * Generates rectangular waveforms to control two H-Bridges.
 * The switching frequency can be adjusted via a potentiometer.
 * The signals of the second H-Bridge are equal to the ones of the first H-Bridge, 
 * but shifted in time by 90° (quarter switching period duration).
 */


/*
 * Constants
 */
#define S1_S4_LEFT_BRIDGE_PIN 6
#define S2_S3_LEFT_BRIDGE_PIN 5
#define S1_S4_RIGHT_BRIDGE_PIN 9
#define S2_S3_RIGHT_BRIDGE_PIN 10
#define ADC_PIN A0
#define ADC_MAX (1<<10) - 1 // 2^n <=> 1<<n | n = ADC bits

#define F_MIN 1     // Minimum output frequency [Hz]
#define F_MAX 200   // Maximum output frequency [Hz]

const float scale = ((1000.0/F_MIN) - (1000.0/F_MAX)) / ((float) ADC_MAX);  // Intervall in which the period duration can be changed. 
                                                                            // Normalized to maximum value of ADC.
                                                                            // Multiplying with ADC value results in time [ms].

/*
 * Globals
 */
 unsigned long loop_time = 0;           // Contains last timestamp [ms]
 unsigned int T_period = 1000 / F_MIN;  // Period duration of rect-signal [ms]
 unsigned int T_quarter = T_period / 4; // Quarter of period duration [ms]
 unsigned int quarter_step_count = 0;   // Gets increased every quarter period duration (to handle 90° phase-shifted second strand)


void setup() {
  pinMode(S1_S4_LEFT_BRIDGE_PIN, OUTPUT);   // Set pin as output
  pinMode(S2_S3_LEFT_BRIDGE_PIN, OUTPUT);   
  pinMode(S1_S4_RIGHT_BRIDGE_PIN, OUTPUT);
  pinMode(S2_S3_RIGHT_BRIDGE_PIN, OUTPUT); 
}


void loop() {
  unsigned long delta_T = millis() - loop_time; // Calculate how much of the signal period has already passed [ms]

  if (delta_T >= T_quarter) // One quarter of a whole period has passed
  {
    switch (quarter_step_count)
    {
      case 0: // New period started
        T_period = (1000/F_MAX) + scale * analogRead(ADC_PIN);  // Calculate new period duration according to R_POT.
                                                                // (1000/f_max) sets the minimum period duration, as specified via F_MAX.
        T_quarter = T_period / 4; // Update quarter period duration

        // Set outputs:
        digitalWrite(S2_S3_LEFT_BRIDGE_PIN, LOW);   // Turn off S2 & S3 before turning on S1 & S4 to prevent shorts
        digitalWrite(S1_S4_LEFT_BRIDGE_PIN, HIGH);
        break;

      case 1: // First quarter passed
        digitalWrite(S2_S3_RIGHT_BRIDGE_PIN, LOW);   // Turn off S2 & S3 before turning on S1 & S4 to prevent shorts
        digitalWrite(S1_S4_RIGHT_BRIDGE_PIN, HIGH);
        break;

      case 2: // Second quarter passed
        digitalWrite(S1_S4_LEFT_BRIDGE_PIN, LOW);  
        digitalWrite(S2_S3_LEFT_BRIDGE_PIN, HIGH);
        break;

      case 3: // Third quarter passed
        digitalWrite(S1_S4_RIGHT_BRIDGE_PIN, LOW);   
        digitalWrite(S2_S3_RIGHT_BRIDGE_PIN, HIGH);
        break;
    }

    loop_time = millis(); // Update looptime
    quarter_step_count ++; // Increase period counter
    if (quarter_step_count > 3){ quarter_step_count = 0; // Reset period counter if one whole period has passed
    }
  }
}
