Skip to content

Instantly share code, notes, and snippets.

@jschoch
Last active December 31, 2024 01:00
Show Gist options
  • Save jschoch/6d10447a211f333098265ef4a2ec4d7f to your computer and use it in GitHub Desktop.
Save jschoch/6d10447a211f333098265ef4a2ec4d7f to your computer and use it in GitHub Desktop.
quadrature signal generator generated by qwen coder
#include <Arduino.h>
#include <cmath>
#include "driver/timer.h"
// Quadrature encoder pins
#define PIN_A (16)
#define PIN_B (17)
#define PIN_Z (18)
// Speed and revolutions variables
float speed = 10.0;
int revolutions = 0;
int revolution_count = 0;
bool reverseDirection = false;
int ppr = 360;
volatile int pulse_cntr = 0;
bool continuousMode = true;
// Z index to keep track of the number of revolutions
int z_index = 0;
hw_timer_t *timer = NULL;
bool timerRunning = false;
// Function to initialize quadrature encoder pins
void quad_encoder_init() {
pinMode(PIN_A, OUTPUT);
pinMode(PIN_B, OUTPUT);
pinMode(PIN_Z, OUTPUT);
}
// Function to calculate even timings for each pulse
int pulse_delay = 10000;
unsigned long calculate_pulse_interval(float rps, int ppr){
//return 1000000L / ((abs(rps) * ppr)); // Calculate interval in microseconds
// using 160 divisor so we halve this
return 500000L / ((abs(rps) * ppr)); // Calculate interval in microseconds
}
// Function to generate quadrature signals based on the speed and revolutions
void generate_quadrature_signal() {
if (revolution_count >= revolutions && !continuousMode){
return;
}
static unsigned long last_time = 0;
static int phase = 0;
delayMicroseconds(pulse_delay); // Delay to maintain the desired speed
pulse_cntr++;
phase++;
phase %= 4; // 4 phases in quadrature: A-HIGH, B-LOW; A-LOW, B-HIGH; A-LOW, B-LOW; A-HIGH, B-HIGH
unsigned long current_time = micros();
if (pulse_cntr % ppr == 0){
digitalWrite(PIN_Z, LOW);
revolution_count++;
//Serial.printf(".%d.",current_time);
}else{
digitalWrite(PIN_Z, HIGH);
}
// Reverse the phase if speed is negative
if (reverseDirection) {
switch (phase) {
case 0:
digitalWrite(PIN_A, HIGH);
digitalWrite(PIN_B, LOW);
break;
case 1:
digitalWrite(PIN_A, HIGH);
digitalWrite(PIN_B, HIGH);
break;
case 2:
digitalWrite(PIN_A, LOW);
digitalWrite(PIN_B, HIGH);
break;
case 3:
digitalWrite(PIN_A, LOW);
digitalWrite(PIN_B, LOW);
break;
}
} else {
switch (phase) {
case 0:
digitalWrite(PIN_A, LOW);
digitalWrite(PIN_B, HIGH);
break;
case 1:
digitalWrite(PIN_A, HIGH);
digitalWrite(PIN_B, HIGH);
break;
case 2:
digitalWrite(PIN_A, HIGH);
digitalWrite(PIN_B, LOW);
break;
case 3:
digitalWrite(PIN_A, LOW);
digitalWrite(PIN_B, LOW);
break;
}
}
}
void IRAM_ATTR onTimer(){
generate_quadrature_signal();
}
void setup() {
Serial.begin(115200);
quad_encoder_init();
Serial.println("setup done");
/* get rid of this, timer better
xTaskCreatePinnedToCore(
run_gen,
"I2S Clock Task",
4096,
NULL,
1,
NULL,
0
);
*/
// Get and print the CPU frequency
timer = timerBegin(1, 160, true); // Prescaler = 80 (80MHz / 80 = 1MHz timer clock)
timerAttachInterrupt(timer, &onTimer, true); // Attach the ISR
timerAlarmWrite(timer, pulse_delay, true); // Set the initial alarm value and auto-reload
timerAlarmEnable(timer); // Start the timer
bool timerRunning = true;
int cpuFrequency = getCpuFrequencyMhz();
Serial.print("CPU Frequency: ");
Serial.print(cpuFrequency);
Serial.println(" MHz");
Serial.printf("Starting with PPR: %d, Speed (RPS): %f, in continuous mode: %s\n", ppr, speed, continuousMode ? "true" : "false");
Serial.println("Type 'S' to stop generating signals.");
Serial.println("Type 'X,Y,Z' where X is speed (RPS), Y is revolutions, and Z is PPR. Example: 1,50,200");
Serial.println("set speed negative to reverse direction: -1,50,200");
}
void process_input(){
String input = Serial.readStringUntil('\n');
// Check for stop command
if (input == "S") {
speed = 0; // Stop the signal generation
continuousMode = false;
Serial.println("Stopped generating signals.");
//timerAlarmDisable();
//bool timerRunning = false;
return;
}
// Parse input for speed, revolutions, and PPR
int commaIndex1 = input.indexOf(',');
int commaIndex2 = input.lastIndexOf(',');
if (commaIndex1 == -1 || commaIndex2 == -1 || commaIndex2 <= commaIndex1) {
Serial.println("Invalid input format. speed (rps), revolutions ('I' for infinite), PPR pulses per revolution");
}
String speedStr = input.substring(0, commaIndex1);
String revolutionsStr = input.substring(commaIndex1 + 1, commaIndex2);
String pprStr = input.substring(commaIndex2 + 1);
if (!speedStr.toFloat() || !pprStr.toInt()) {
Serial.println("Invalid input values. Please ensure all inputs are numeric.");
return;
}
if(!revolutionsStr.toInt() && revolutionsStr == "I") {
Serial.println("Setting continuous mode.");
continuousMode = true;
} else {
if(revolutionsStr.toInt()){
continuousMode = false;
revolution_count = 0;
revolutions = revolutionsStr.toInt();
}
else{
Serial.println("invalid revolution input, try again");
return;
}
}
speed = speedStr.toFloat();
ppr = pprStr.toInt();
// Determine direction based on the sign of speed
reverseDirection = (speed < 0);
Serial.print("\nSpeed: ");
Serial.println(speed);
Serial.print("Revolutions: ");
Serial.println(revolutions);
Serial.print("Pulses per Revolution: ");
Serial.println(ppr);
Serial.print("Reverse Direction: ");
Serial.println(reverseDirection ? "Yes" : "No");
Serial.printf("delay in microseconds between pulses: %d\n", calculate_pulse_interval(speed, ppr));
if (!continuousMode) {
// If not in continuous mode, reset z_index
z_index = 0;
Serial.println("Started generating signals.");
}
pulse_delay = calculate_pulse_interval(speed, ppr);
timerAlarmWrite(timer, pulse_delay, true);
}
void loop() {
if (Serial.available() > 0) {
process_input();
}
//generate_quadrature_signal();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment