From 4889725c74d3a09afb93a84077e21476613c280a Mon Sep 17 00:00:00 2001 From: erebusnz Date: Sat, 28 Sep 2024 22:39:22 -0700 Subject: [PATCH 1/8] Update apple-pie-firmware.ino Refactor for interrupt using clock pin --- apple-pie-firmware.ino | 115 +++++++++++++++++++++++------------------ 1 file changed, 65 insertions(+), 50 deletions(-) diff --git a/apple-pie-firmware.ino b/apple-pie-firmware.ino index 7965132..a6bd1e1 100644 --- a/apple-pie-firmware.ino +++ b/apple-pie-firmware.ino @@ -3,33 +3,37 @@ // Pin Definitions const int GATE_OUT1 = 2; const int GATE_OUT2 = 3; -const int CLOCK_IN = 9; +const int XTAL = 7; +const int CLOCK_PIN = 9; const int LOCK_PIN = A4; const int CV_1 = A0; const int CV_2 = A1; +const int STEPS_PIN = A2; +const int SWITCH_PIN = A3; +const int DAC_PIN = 10; -// State Variable for Clock -volatile bool clockState1 = LOW; -volatile bool clockState2 = LOW; +MCP4822 dac(DAC_PIN); +// Global Setting Values +volatile uint16_t lockValueA = 0; +volatile uint16_t lockValueB = 0; +volatile uint8_t shiftRegisterLength = 16; +volatile bool switchPosition = true; // Shift Register Arrays -uint16_t shiftRegister1 = 0x0000; // Example initial value -uint16_t shiftRegister2 = 0x0000; // Example initial value +volatile uint16_t shiftRegister1 = 0x0000; +volatile uint16_t shiftRegister2 = 0x0000; +uint16_t last_cv_1 = -1; +uint16_t last_cv_2 = -1; -// Function to Read Analog Input -uint16_t readAnalogInput(uint8_t pin) { - return analogRead(pin); -} - -// Function to Read Analog Input +// Function to read switch position bool readSwitchPosition() { - return analogRead(A3) > 512; + return analogRead(SWITCH_PIN) > 512; } uint8_t getShiftRegisterLength() { - // Read the analog value from pin A2 (range 0 to 1023) - uint16_t analogValue = analogRead(A2); + // Read the analog value from STEPS_PIN (range 0 to 1023) + uint16_t analogValue = analogRead(STEPS_PIN); // Map the analog value to one of the specific return values: 2, 4, 8, or 16 if (analogValue < 256) { @@ -54,14 +58,30 @@ uint16_t clearNthLeftBit(uint16_t value, uint8_t n) { return value & mask; } - -MCP4822 dac(10); +void doClockCycle() { + // Rising edge + if (!digitalRead(CLOCK_PIN)) { + + digitalWrite(GATE_OUT1, shiftRegister1 >= 0x8000); + shiftRegister1 = clearNthLeftBit((shiftRegister1 << 1), shiftRegisterLength) | (lockValueA < random(0,2048) ? (shiftRegister1 >> (shiftRegisterLength - 1)) : (~shiftRegister1 >> (shiftRegisterLength - 1))); + digitalWrite(GATE_OUT2, shiftRegister2 >= 0x8000); + // Invert functionality of lockValue when switch is in reverse position + if (switchPosition) { + shiftRegister2 = clearNthLeftBit((shiftRegister2 << 1), shiftRegisterLength) | (lockValueB < random(0,2048) ? (shiftRegister2 >> (shiftRegisterLength - 1)) : (~shiftRegister2 >> (shiftRegisterLength - 1))); + } else { + shiftRegister2 = clearNthLeftBit((shiftRegister2 << 1), shiftRegisterLength) | ((1024 - lockValueB) < random(0,2048) ? (shiftRegister2 >> (shiftRegisterLength - 1)) : (~shiftRegister2 >> (shiftRegisterLength - 1))); + } + } else { + digitalWrite(GATE_OUT1, LOW); + digitalWrite(GATE_OUT2, LOW); + } +} void setup() { // Initialize Pin Modes pinMode(GATE_OUT1, OUTPUT); pinMode(GATE_OUT2, OUTPUT); - pinMode(CLOCK_IN, INPUT); + pinMode(CLOCK_PIN, INPUT); dac.init(); dac.turnOnChannelA(); @@ -69,40 +89,35 @@ void setup() { dac.setGainA(MCP4822::High); dac.setGainB(MCP4822::High); - randomSeed(analogRead(7)); + randomSeed(analogRead(XTAL)); + + // set up Arduino interrupt + attachInterrupt(digitalPinToInterrupt(CLOCK_PIN), doClockCycle, CHANGE); } void loop() { - // Read the clock input state - bool currentClockState = digitalRead(CLOCK_IN); - int lockValue = readAnalogInput(LOCK_PIN); - int cvA = readAnalogInput(CV_1) * 1.7; - int cvB = readAnalogInput(CV_2) * 1.7; - uint16_t lockValueA = (uint16_t)(constrain((int) lockValue + (int) cvA - 525, 0, 1023)); - uint16_t lockValueB = (uint16_t)(constrain((int) lockValue + (int) cvB - 525, 0, 1023)); - uint8_t shiftRegisterLength = getShiftRegisterLength(); - - // Detect rising edge - if (currentClockState == HIGH && clockState1 == LOW) { - clockState1 = HIGH; - - dac.setVoltageA(shiftRegister1 >> 4); - dac.setVoltageB(shiftRegister2 >> 4); - dac.updateDAC(); - - digitalWrite(GATE_OUT1, shiftRegister1 >= 0x8000); - shiftRegister1 = clearNthLeftBit((shiftRegister1 << 1), shiftRegisterLength) | (lockValueA < random(0,2048) ? (shiftRegister1 >> (shiftRegisterLength - 1)) : (~shiftRegister1 >> (shiftRegisterLength - 1))); - digitalWrite(GATE_OUT2, shiftRegister2 >= 0x8000); - // Invert functionality of lockValue when switch is in reverse position - if (readSwitchPosition()) { - shiftRegister2 = clearNthLeftBit((shiftRegister2 << 1), shiftRegisterLength) | (lockValueB < random(0,2048) ? (shiftRegister2 >> (shiftRegisterLength - 1)) : (~shiftRegister2 >> (shiftRegisterLength - 1))); - } else { - shiftRegister2 = clearNthLeftBit((shiftRegister2 << 1), shiftRegisterLength) | ((1024 - lockValueB) < random(0,2048) ? (shiftRegister2 >> (shiftRegisterLength - 1)) : (~shiftRegister2 >> (shiftRegisterLength - 1))); - } - } else if (currentClockState == LOW) { - // Reset the state when the clock goes low - clockState1 = LOW; - digitalWrite(GATE_OUT1, LOW); - digitalWrite(GATE_OUT2, LOW); + int lockValue = analogRead(LOCK_PIN); + int cvA = analogRead(CV_1) * 1.7; + int cvB = analogRead(CV_2) * 1.7; + + //Set lock values + lockValueA = (uint16_t)(constrain((int) lockValue + (int) cvA - 525, 0, 1023)); + lockValueB = (uint16_t)(constrain((int) lockValue + (int) cvB - 525, 0, 1023)); + + //Length of sequence (2,4,8,16) selected by STEPS_PIN + shiftRegisterLength = getShiftRegisterLength(); + switchPosition = readSwitchPosition(); + + //Send new CV if shiftRegisters are changed + uint16_t new_cv_1 = shiftRegister1 >> 4; + uint16_t new_cv_2 = shiftRegister2 >> 4; + if (last_cv_1 != new_cv_1) { + last_cv_1 = new_cv_1; + dac.setVoltageA(last_cv_1); + } + if (last_cv_2 != new_cv_2) { + last_cv_2 = new_cv_2; + dac.setVoltageB(new_cv_2); } + dac.updateDAC(); } From 9f2e8ebe4202496de223a881cdad79a4f20cb07a Mon Sep 17 00:00:00 2001 From: erebusnz Date: Sun, 29 Sep 2024 16:09:40 -0700 Subject: [PATCH 2/8] Update apple-pie-firmware.ino Updated to use YetAnotherPcInt for interrupt Fixed issue with clockstate not using shiftregister Used define instead of constants to reduce memory requirements --- apple-pie-firmware.ino | 189 ++++++++++++++++++++++++----------------- 1 file changed, 110 insertions(+), 79 deletions(-) diff --git a/apple-pie-firmware.ino b/apple-pie-firmware.ino index a6bd1e1..f9dbc6f 100644 --- a/apple-pie-firmware.ino +++ b/apple-pie-firmware.ino @@ -1,16 +1,20 @@ #include +#include // Pin Definitions -const int GATE_OUT1 = 2; -const int GATE_OUT2 = 3; -const int XTAL = 7; -const int CLOCK_PIN = 9; -const int LOCK_PIN = A4; -const int CV_1 = A0; -const int CV_2 = A1; -const int STEPS_PIN = A2; -const int SWITCH_PIN = A3; -const int DAC_PIN = 10; +#define GATE_OUT1 2 +#define GATE_OUT2 3 +#define XTAL 7 +#define CLOCK_PIN 9 //PCINT22? +#define LOCK_PIN A4 +#define CV_1 A0 +#define CV_2 A1 +#define STEPS_PIN A2 +#define SWITCH_PIN A3 +#define DAC_PIN 10 + +//Debug via serial +#define DEBUG true MCP4822 dac(DAC_PIN); @@ -28,96 +32,123 @@ uint16_t last_cv_2 = -1; // Function to read switch position bool readSwitchPosition() { - return analogRead(SWITCH_PIN) > 512; + return analogRead(SWITCH_PIN) > 512; } uint8_t getShiftRegisterLength() { - // Read the analog value from STEPS_PIN (range 0 to 1023) - uint16_t analogValue = analogRead(STEPS_PIN); - - // Map the analog value to one of the specific return values: 2, 4, 8, or 16 - if (analogValue < 256) { - return 2; - } else if (analogValue < 512) { - return 4; - } else if (analogValue < 768) { - return 8; - } else { - return 16; - } + // Read the analog value from STEPS_PIN (range 0 to 1023) + uint16_t analogValue = analogRead(STEPS_PIN); + + // Map the analog value to one of the specific return values: 2, 4, 8, or 16 + if (analogValue < 256) { + return 2; + } else if (analogValue < 512) { + return 4; + } else if (analogValue < 768) { + return 8; + } else { + return 16; + } } uint16_t clearNthLeftBit(uint16_t value, uint8_t n) { - // Calculate the bit position from the left - uint8_t bitPosition = 16 - n; + // Calculate the bit position from the left + uint8_t bitPosition = 16 - n; - // Create a mask with all bits set to 1 except the nth leftmost bit - uint16_t mask = ~(1 << bitPosition); + // Create a mask with all bits set to 1 except the nth leftmost bit + uint16_t mask = ~(1 << bitPosition); - // Clear the nth leftmost bit by applying the mask - return value & mask; + // Clear the nth leftmost bit by applying the mask + return value & mask; } -void doClockCycle() { +void doClockCycle(bool clockstate) { // Rising edge - if (!digitalRead(CLOCK_PIN)) { - - digitalWrite(GATE_OUT1, shiftRegister1 >= 0x8000); - shiftRegister1 = clearNthLeftBit((shiftRegister1 << 1), shiftRegisterLength) | (lockValueA < random(0,2048) ? (shiftRegister1 >> (shiftRegisterLength - 1)) : (~shiftRegister1 >> (shiftRegisterLength - 1))); - digitalWrite(GATE_OUT2, shiftRegister2 >= 0x8000); - // Invert functionality of lockValue when switch is in reverse position - if (switchPosition) { - shiftRegister2 = clearNthLeftBit((shiftRegister2 << 1), shiftRegisterLength) | (lockValueB < random(0,2048) ? (shiftRegister2 >> (shiftRegisterLength - 1)) : (~shiftRegister2 >> (shiftRegisterLength - 1))); - } else { - shiftRegister2 = clearNthLeftBit((shiftRegister2 << 1), shiftRegisterLength) | ((1024 - lockValueB) < random(0,2048) ? (shiftRegister2 >> (shiftRegisterLength - 1)) : (~shiftRegister2 >> (shiftRegisterLength - 1))); - } + if (clockstate) { + digitalWrite(GATE_OUT1, shiftRegister1 >= 0x8000); + shiftRegister1 = clearNthLeftBit((shiftRegister1 << 1), shiftRegisterLength) | (lockValueA < random(0,2048) ? (shiftRegister1 >> (shiftRegisterLength - 1)) : (~shiftRegister1 >> (shiftRegisterLength - 1))); + digitalWrite(GATE_OUT2, shiftRegister2 >= 0x8000); + // Invert functionality of lockValue when switch is in reverse position + if (readSwitchPosition()) { + shiftRegister2 = clearNthLeftBit((shiftRegister2 << 1), shiftRegisterLength) | (lockValueB < random(0,2048) ? (shiftRegister2 >> (shiftRegisterLength - 1)) : (~shiftRegister2 >> (shiftRegisterLength - 1))); } else { - digitalWrite(GATE_OUT1, LOW); - digitalWrite(GATE_OUT2, LOW); + shiftRegister2 = clearNthLeftBit((shiftRegister2 << 1), shiftRegisterLength) | ((1024 - lockValueB) < random(0,2048) ? (shiftRegister2 >> (shiftRegisterLength - 1)) : (~shiftRegister2 >> (shiftRegisterLength - 1))); } + } else { + digitalWrite(GATE_OUT1, LOW); + digitalWrite(GATE_OUT2, LOW); + } } void setup() { - // Initialize Pin Modes - pinMode(GATE_OUT1, OUTPUT); - pinMode(GATE_OUT2, OUTPUT); - pinMode(CLOCK_PIN, INPUT); + if (DEBUG) Serial.begin(115200); + + // Initialize Pin Modes + pinMode(GATE_OUT1, OUTPUT); + pinMode(GATE_OUT2, OUTPUT); - dac.init(); - dac.turnOnChannelA(); - dac.turnOnChannelB(); - dac.setGainA(MCP4822::High); - dac.setGainB(MCP4822::High); + // set up Clock interrupt + pinMode(CLOCK_PIN, INPUT_PULLUP); + PcInt::attachInterrupt(CLOCK_PIN, doClockCycle, CHANGE); + + dac.init(); + dac.turnOnChannelA(); + dac.turnOnChannelB(); + dac.setGainA(MCP4822::High); + dac.setGainB(MCP4822::High); randomSeed(analogRead(XTAL)); - // set up Arduino interrupt - attachInterrupt(digitalPinToInterrupt(CLOCK_PIN), doClockCycle, CHANGE); + if (DEBUG) Serial.println("Starting..."); } void loop() { - int lockValue = analogRead(LOCK_PIN); - int cvA = analogRead(CV_1) * 1.7; - int cvB = analogRead(CV_2) * 1.7; - - //Set lock values - lockValueA = (uint16_t)(constrain((int) lockValue + (int) cvA - 525, 0, 1023)); - lockValueB = (uint16_t)(constrain((int) lockValue + (int) cvB - 525, 0, 1023)); - - //Length of sequence (2,4,8,16) selected by STEPS_PIN - shiftRegisterLength = getShiftRegisterLength(); - switchPosition = readSwitchPosition(); - - //Send new CV if shiftRegisters are changed - uint16_t new_cv_1 = shiftRegister1 >> 4; - uint16_t new_cv_2 = shiftRegister2 >> 4; - if (last_cv_1 != new_cv_1) { - last_cv_1 = new_cv_1; - dac.setVoltageA(last_cv_1); - } - if (last_cv_2 != new_cv_2) { - last_cv_2 = new_cv_2; - dac.setVoltageB(new_cv_2); - } - dac.updateDAC(); + // if (DEBUG) Serial.println("Checking pins"); + + int lockValue = analogRead(LOCK_PIN); + int cvA = analogRead(CV_1) * 1.7; + int cvB = analogRead(CV_2) * 1.7; + + //Set lock values + lockValueA = (uint16_t)(constrain((int) lockValue + (int) cvA - 525, 0, 1023)); + lockValueB = (uint16_t)(constrain((int) lockValue + (int) cvB - 525, 0, 1023)); + + //Length of sequence (2,4,8,16) selected by STEPS_PIN + shiftRegisterLength = getShiftRegisterLength(); + switchPosition = readSwitchPosition(); + +/** + if (DEBUG) { + Serial.print("Lock val: "); + Serial.print(lockValue); + Serial.print(" cvA: "); + Serial.print(cvA); + Serial.print(" cvB: "); + Serial.print(cvB); + Serial.println("...Done"); + } +**/ + + //Send new CV if shiftRegisters are changed + uint16_t new_cv_1 = shiftRegister1 >> 4; + uint16_t new_cv_2 = shiftRegister2 >> 4; + if (last_cv_1 != new_cv_1) { + dac.setVoltageA(new_cv_1); + last_cv_1 = new_cv_1; + } + if (last_cv_2 != new_cv_2) { + dac.setVoltageB(new_cv_2); + last_cv_2 = new_cv_2; + } + +/** + if (DEBUG) { + Serial.print("New CV1: "); + Serial.print(new_cv_1); + Serial.print(" new CV2: "); + Serial.print(new_cv_2); + Serial.println("...Done"); + } +**/ + dac.updateDAC(); } From 11294acb4c756c7e17298d65e9cd4cc30593ee93 Mon Sep 17 00:00:00 2001 From: erebusnz Date: Sun, 29 Sep 2024 16:43:43 -0700 Subject: [PATCH 3/8] Update apple-pie-firmware.ino Updated debug serial output for gates/CV --- apple-pie-firmware.ino | 47 ++++++++++++++++++------------------------ 1 file changed, 20 insertions(+), 27 deletions(-) diff --git a/apple-pie-firmware.ino b/apple-pie-firmware.ino index f9dbc6f..231def7 100644 --- a/apple-pie-firmware.ino +++ b/apple-pie-firmware.ino @@ -14,7 +14,7 @@ #define DAC_PIN 10 //Debug via serial -#define DEBUG true +#define DEBUG false MCP4822 dac(DAC_PIN); @@ -66,8 +66,14 @@ void doClockCycle(bool clockstate) { // Rising edge if (clockstate) { digitalWrite(GATE_OUT1, shiftRegister1 >= 0x8000); + #ifdef DEBUG + if (shiftRegister1 >= 0x8000) Serial.println("Sending Gate A"); + #endif shiftRegister1 = clearNthLeftBit((shiftRegister1 << 1), shiftRegisterLength) | (lockValueA < random(0,2048) ? (shiftRegister1 >> (shiftRegisterLength - 1)) : (~shiftRegister1 >> (shiftRegisterLength - 1))); digitalWrite(GATE_OUT2, shiftRegister2 >= 0x8000); + #ifdef DEBUG + if (shiftRegister2 >= 0x8000) Serial.println("Sending Gate B"); + #endif // Invert functionality of lockValue when switch is in reverse position if (readSwitchPosition()) { shiftRegister2 = clearNthLeftBit((shiftRegister2 << 1), shiftRegisterLength) | (lockValueB < random(0,2048) ? (shiftRegister2 >> (shiftRegisterLength - 1)) : (~shiftRegister2 >> (shiftRegisterLength - 1))); @@ -81,7 +87,10 @@ void doClockCycle(bool clockstate) { } void setup() { - if (DEBUG) Serial.begin(115200); + #ifdef DEBUG + Serial.begin(115200); + Serial.println("Starting..."); + #endif // Initialize Pin Modes pinMode(GATE_OUT1, OUTPUT); @@ -98,13 +107,9 @@ void setup() { dac.setGainB(MCP4822::High); randomSeed(analogRead(XTAL)); - - if (DEBUG) Serial.println("Starting..."); } void loop() { - // if (DEBUG) Serial.println("Checking pins"); - int lockValue = analogRead(LOCK_PIN); int cvA = analogRead(CV_1) * 1.7; int cvB = analogRead(CV_2) * 1.7; @@ -117,38 +122,26 @@ void loop() { shiftRegisterLength = getShiftRegisterLength(); switchPosition = readSwitchPosition(); -/** - if (DEBUG) { - Serial.print("Lock val: "); - Serial.print(lockValue); - Serial.print(" cvA: "); - Serial.print(cvA); - Serial.print(" cvB: "); - Serial.print(cvB); - Serial.println("...Done"); - } -**/ - //Send new CV if shiftRegisters are changed uint16_t new_cv_1 = shiftRegister1 >> 4; uint16_t new_cv_2 = shiftRegister2 >> 4; + if (last_cv_1 != new_cv_1) { dac.setVoltageA(new_cv_1); last_cv_1 = new_cv_1; + #ifdef DEBUG + Serial.print("New CV1: "); + Serial.println(new_cv_1); + #endif } if (last_cv_2 != new_cv_2) { dac.setVoltageB(new_cv_2); last_cv_2 = new_cv_2; + #ifdef DEBUG + Serial.print("New CV2: "); + Serial.println(new_cv_2); + #endif } -/** - if (DEBUG) { - Serial.print("New CV1: "); - Serial.print(new_cv_1); - Serial.print(" new CV2: "); - Serial.print(new_cv_2); - Serial.println("...Done"); - } -**/ dac.updateDAC(); } From e748d75d8d7c2e5926ed6af261898ebf89d3a4e8 Mon Sep 17 00:00:00 2001 From: erebusnz Date: Thu, 3 Oct 2024 21:42:21 -0700 Subject: [PATCH 4/8] Update apple-pie-firmware.ino --- apple-pie-firmware.ino | 102 +++++++++++++++++++++++------------------ 1 file changed, 58 insertions(+), 44 deletions(-) diff --git a/apple-pie-firmware.ino b/apple-pie-firmware.ino index 231def7..1f3a4bc 100644 --- a/apple-pie-firmware.ino +++ b/apple-pie-firmware.ino @@ -14,19 +14,21 @@ #define DAC_PIN 10 //Debug via serial -#define DEBUG false +//#define DEBUG true MCP4822 dac(DAC_PIN); // Global Setting Values -volatile uint16_t lockValueA = 0; -volatile uint16_t lockValueB = 0; -volatile uint8_t shiftRegisterLength = 16; -volatile bool switchPosition = true; +uint16_t lockValueA = 0; +uint16_t lockValueB = 0; +uint8_t shiftRegisterLength = 16; +bool switchPosition = true; +volatile bool clockLeading = false; +volatile bool clockTrailing = false; // Shift Register Arrays -volatile uint16_t shiftRegister1 = 0x0000; -volatile uint16_t shiftRegister2 = 0x0000; +uint16_t shiftRegister1 = 0x0000; +uint16_t shiftRegister2 = 0x0000; uint16_t last_cv_1 = -1; uint16_t last_cv_2 = -1; @@ -65,24 +67,11 @@ uint16_t clearNthLeftBit(uint16_t value, uint8_t n) { void doClockCycle(bool clockstate) { // Rising edge if (clockstate) { - digitalWrite(GATE_OUT1, shiftRegister1 >= 0x8000); - #ifdef DEBUG - if (shiftRegister1 >= 0x8000) Serial.println("Sending Gate A"); - #endif - shiftRegister1 = clearNthLeftBit((shiftRegister1 << 1), shiftRegisterLength) | (lockValueA < random(0,2048) ? (shiftRegister1 >> (shiftRegisterLength - 1)) : (~shiftRegister1 >> (shiftRegisterLength - 1))); - digitalWrite(GATE_OUT2, shiftRegister2 >= 0x8000); - #ifdef DEBUG - if (shiftRegister2 >= 0x8000) Serial.println("Sending Gate B"); - #endif - // Invert functionality of lockValue when switch is in reverse position - if (readSwitchPosition()) { - shiftRegister2 = clearNthLeftBit((shiftRegister2 << 1), shiftRegisterLength) | (lockValueB < random(0,2048) ? (shiftRegister2 >> (shiftRegisterLength - 1)) : (~shiftRegister2 >> (shiftRegisterLength - 1))); - } else { - shiftRegister2 = clearNthLeftBit((shiftRegister2 << 1), shiftRegisterLength) | ((1024 - lockValueB) < random(0,2048) ? (shiftRegister2 >> (shiftRegisterLength - 1)) : (~shiftRegister2 >> (shiftRegisterLength - 1))); - } + clockLeading = true; + clockTrailing = false; } else { - digitalWrite(GATE_OUT1, LOW); - digitalWrite(GATE_OUT2, LOW); + clockTrailing = true; + clockLeading = false; } } @@ -110,6 +99,7 @@ void setup() { } void loop() { + int lockValue = analogRead(LOCK_PIN); int cvA = analogRead(CV_1) * 1.7; int cvB = analogRead(CV_2) * 1.7; @@ -118,30 +108,54 @@ void loop() { lockValueA = (uint16_t)(constrain((int) lockValue + (int) cvA - 525, 0, 1023)); lockValueB = (uint16_t)(constrain((int) lockValue + (int) cvB - 525, 0, 1023)); - //Length of sequence (2,4,8,16) selected by STEPS_PIN - shiftRegisterLength = getShiftRegisterLength(); - switchPosition = readSwitchPosition(); - - //Send new CV if shiftRegisters are changed - uint16_t new_cv_1 = shiftRegister1 >> 4; - uint16_t new_cv_2 = shiftRegister2 >> 4; - - if (last_cv_1 != new_cv_1) { - dac.setVoltageA(new_cv_1); - last_cv_1 = new_cv_1; + if (clockLeading) + { #ifdef DEBUG - Serial.print("New CV1: "); - Serial.println(new_cv_1); + if (shiftRegister1 >= 0x8000) Serial.println("Sending Gate A"); #endif - } - if (last_cv_2 != new_cv_2) { - dac.setVoltageB(new_cv_2); - last_cv_2 = new_cv_2; + shiftRegister1 = clearNthLeftBit((shiftRegister1 << 1), shiftRegisterLength) | (lockValueA < random(0,2048) ? (shiftRegister1 >> (shiftRegisterLength - 1)) : (~shiftRegister1 >> (shiftRegisterLength - 1))); #ifdef DEBUG - Serial.print("New CV2: "); - Serial.println(new_cv_2); + if (shiftRegister2 >= 0x8000) Serial.println("Sending Gate B"); #endif + // Invert functionality of lockValue when switch is in reverse position + if (readSwitchPosition()) { + shiftRegister2 = clearNthLeftBit((shiftRegister2 << 1), shiftRegisterLength) | (lockValueB < random(0,2048) ? (shiftRegister2 >> (shiftRegisterLength - 1)) : (~shiftRegister2 >> (shiftRegisterLength - 1))); + } else { + shiftRegister2 = clearNthLeftBit((shiftRegister2 << 1), shiftRegisterLength) | ((1024 - lockValueB) < random(0,2048) ? (shiftRegister2 >> (shiftRegisterLength - 1)) : (~shiftRegister2 >> (shiftRegisterLength - 1))); + } + //Send new CV if shiftRegisters are changed + uint16_t new_cv_1 = shiftRegister1 >> 4; + uint16_t new_cv_2 = shiftRegister2 >> 4; + + if (last_cv_1 != new_cv_1) { + dac.setVoltageA(new_cv_1); + last_cv_1 = new_cv_1; + #ifdef DEBUG + Serial.print("New CV1: "); + Serial.println(new_cv_1); + #endif + } + if (last_cv_2 != new_cv_2) { + dac.setVoltageB(new_cv_2); + last_cv_2 = new_cv_2; + #ifdef DEBUG + Serial.print("New CV2: "); + Serial.println(new_cv_2); + #endif + } + dac.updateDAC(); + digitalWrite(GATE_OUT1, shiftRegister1 >= 0x8000); + digitalWrite(GATE_OUT2, shiftRegister2 >= 0x8000); + clockLeading = false; + } + else if (clockTrailing) + { + digitalWrite(GATE_OUT1, LOW); + digitalWrite(GATE_OUT2, LOW); + clockTrailing = false; } - dac.updateDAC(); + //Length of sequence (2,4,8,16) selected by STEPS_PIN + shiftRegisterLength = getShiftRegisterLength(); + switchPosition = readSwitchPosition(); } From 8c07dabd759dfb16a946288b58360574627db01a Mon Sep 17 00:00:00 2001 From: erebusnz Date: Thu, 3 Oct 2024 22:10:57 -0700 Subject: [PATCH 5/8] Update apple-pie-firmware.ino --- apple-pie-firmware.ino | 64 ++++++++++++++++-------------------------- 1 file changed, 24 insertions(+), 40 deletions(-) diff --git a/apple-pie-firmware.ino b/apple-pie-firmware.ino index 1f3a4bc..208b8d5 100644 --- a/apple-pie-firmware.ino +++ b/apple-pie-firmware.ino @@ -7,20 +7,17 @@ #define XTAL 7 #define CLOCK_PIN 9 //PCINT22? #define LOCK_PIN A4 -#define CV_1 A0 -#define CV_2 A1 #define STEPS_PIN A2 #define SWITCH_PIN A3 #define DAC_PIN 10 -//Debug via serial -//#define DEBUG true +//Debug A or B via serial +#define DEBUGA true +//#define DEBUGB true MCP4822 dac(DAC_PIN); // Global Setting Values -uint16_t lockValueA = 0; -uint16_t lockValueB = 0; uint8_t shiftRegisterLength = 16; bool switchPosition = true; volatile bool clockLeading = false; @@ -29,8 +26,6 @@ volatile bool clockTrailing = false; // Shift Register Arrays uint16_t shiftRegister1 = 0x0000; uint16_t shiftRegister2 = 0x0000; -uint16_t last_cv_1 = -1; -uint16_t last_cv_2 = -1; // Function to read switch position bool readSwitchPosition() { @@ -76,7 +71,7 @@ void doClockCycle(bool clockstate) { } void setup() { - #ifdef DEBUG + #if defined(DEBUGA) || defined(DEBUGB) Serial.begin(115200); Serial.println("Starting..."); #endif @@ -84,9 +79,9 @@ void setup() { // Initialize Pin Modes pinMode(GATE_OUT1, OUTPUT); pinMode(GATE_OUT2, OUTPUT); + pinMode(CLOCK_PIN, INPUT_PULLUP); // set up Clock interrupt - pinMode(CLOCK_PIN, INPUT_PULLUP); PcInt::attachInterrupt(CLOCK_PIN, doClockCycle, CHANGE); dac.init(); @@ -99,26 +94,17 @@ void setup() { } void loop() { - int lockValue = analogRead(LOCK_PIN); - int cvA = analogRead(CV_1) * 1.7; - int cvB = analogRead(CV_2) * 1.7; - - //Set lock values - lockValueA = (uint16_t)(constrain((int) lockValue + (int) cvA - 525, 0, 1023)); - lockValueB = (uint16_t)(constrain((int) lockValue + (int) cvB - 525, 0, 1023)); if (clockLeading) { - #ifdef DEBUG - if (shiftRegister1 >= 0x8000) Serial.println("Sending Gate A"); - #endif + //Set lock values + uint16_t lockValueA = (uint16_t)(constrain((int) lockValue, 0, 1023)); + uint16_t lockValueB = (uint16_t)(constrain((int) lockValue, 0, 1023)); shiftRegister1 = clearNthLeftBit((shiftRegister1 << 1), shiftRegisterLength) | (lockValueA < random(0,2048) ? (shiftRegister1 >> (shiftRegisterLength - 1)) : (~shiftRegister1 >> (shiftRegisterLength - 1))); - #ifdef DEBUG - if (shiftRegister2 >= 0x8000) Serial.println("Sending Gate B"); - #endif + // Invert functionality of lockValue when switch is in reverse position - if (readSwitchPosition()) { + if (switchPosition) { shiftRegister2 = clearNthLeftBit((shiftRegister2 << 1), shiftRegisterLength) | (lockValueB < random(0,2048) ? (shiftRegister2 >> (shiftRegisterLength - 1)) : (~shiftRegister2 >> (shiftRegisterLength - 1))); } else { shiftRegister2 = clearNthLeftBit((shiftRegister2 << 1), shiftRegisterLength) | ((1024 - lockValueB) < random(0,2048) ? (shiftRegister2 >> (shiftRegisterLength - 1)) : (~shiftRegister2 >> (shiftRegisterLength - 1))); @@ -127,24 +113,22 @@ void loop() { uint16_t new_cv_1 = shiftRegister1 >> 4; uint16_t new_cv_2 = shiftRegister2 >> 4; - if (last_cv_1 != new_cv_1) { - dac.setVoltageA(new_cv_1); - last_cv_1 = new_cv_1; - #ifdef DEBUG - Serial.print("New CV1: "); - Serial.println(new_cv_1); - #endif - } - if (last_cv_2 != new_cv_2) { - dac.setVoltageB(new_cv_2); - last_cv_2 = new_cv_2; - #ifdef DEBUG - Serial.print("New CV2: "); - Serial.println(new_cv_2); - #endif - } + dac.setVoltageA(shiftRegister1 >> 4); + dac.setVoltageB(shiftRegister2 >> 4); dac.updateDAC(); + #ifdef DEBUGA + if (shiftRegister1 >= 0x8000) { + Serial.print("Sending Gate A - "); + Serial.println(shiftRegister1 >> 4); + } + #endif digitalWrite(GATE_OUT1, shiftRegister1 >= 0x8000); + #ifdef DEBUGB + if (shiftRegister2 >= 0x8000) { + Serial.print("Sending Gate B - "); + Serial.println(shiftRegister1 >> 4); + } + #endif digitalWrite(GATE_OUT2, shiftRegister2 >= 0x8000); clockLeading = false; } From d621edb527660c5b5df94ea270d590c95cefafcb Mon Sep 17 00:00:00 2001 From: erebusnz Date: Sun, 15 Mar 2026 22:32:44 +1300 Subject: [PATCH 6/8] Added back in CVs and refactor for less jitter / more stable CV/gate outputs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Missing CV inputs - Added back in CV_1 (A0) and CV_2 (A1) Clock event handling (ISR refactor) - Replaced two separate volatile bool flags (clockLeading, clockTrailing) with a single volatile uint8_t clockEvent - The ISR now drives gates LOW directly via port manipulation on the falling edge, instead of setting a flag and letting loop() do a digitalWrite — this reduces falling-edge latency - loop() atomically reads and clears clockEvent using cli()/sei() LFSR improvements - Shift registers now start with interleaved patterns (0xAAAA / 0x5555) instead of 0x0000 — avoids the all-zero stuck state on boot so it drives new patterns faster - Added & 1 mask to the new-bit calculation — fixes a potential bug where more than one bit could leak into the LSB - random() range changed from (0, 2048) to (6, 1024) — the lower bound avoids the extreme "always-lock" edge and the upper bound matches the lock value range Gate output - Replaced digitalWrite with direct port manipulation (PORTD) for gate outputs — faster, lower latency - CV/DAC is now only updated when a gate fires (not every clock), so CV holds its last value on silent steps Sequencer control (steps/switch) - getShiftRegisterLength() now uses a debounce: the knob reading must be stable for 8 consecutive reads before changing — prevents glitchy length changes while turning the pot - Steps and switch are now only read in the else branch (between clock events), not every loop iteration --- apple-pie-firmware.ino | 152 +++++++++++++++++++++-------------------- 1 file changed, 77 insertions(+), 75 deletions(-) diff --git a/apple-pie-firmware.ino b/apple-pie-firmware.ino index 208b8d5..e80e0f6 100644 --- a/apple-pie-firmware.ino +++ b/apple-pie-firmware.ino @@ -4,28 +4,33 @@ // Pin Definitions #define GATE_OUT1 2 #define GATE_OUT2 3 -#define XTAL 7 -#define CLOCK_PIN 9 //PCINT22? +#define ENTROPY_PIN A7 +#define CLOCK_PIN 9 #define LOCK_PIN A4 #define STEPS_PIN A2 #define SWITCH_PIN A3 +#define CV_1 A0 +#define CV_2 A1 #define DAC_PIN 10 -//Debug A or B via serial -#define DEBUGA true -//#define DEBUGB true - MCP4822 dac(DAC_PIN); -// Global Setting Values +#define EVT_NONE 0 +#define EVT_LEADING 1 + +// CV input scaling: gain expands 0-1023 ADC range; neutral offset is the +// scaled CV value that produces zero effect on the lock (analogRead ~309, ~1.5V) +const float CV_GAIN = 1.7; +const int CV_NEUTRAL_POINT = 525; + +// Global State +volatile uint8_t clockEvent = EVT_NONE; uint8_t shiftRegisterLength = 16; bool switchPosition = true; -volatile bool clockLeading = false; -volatile bool clockTrailing = false; // Shift Register Arrays -uint16_t shiftRegister1 = 0x0000; -uint16_t shiftRegister2 = 0x0000; +uint16_t shiftRegister1 = 0xAAAA; +uint16_t shiftRegister2 = 0x5555; // Function to read switch position bool readSwitchPosition() { @@ -33,19 +38,28 @@ bool readSwitchPosition() { } uint8_t getShiftRegisterLength() { - // Read the analog value from STEPS_PIN (range 0 to 1023) - uint16_t analogValue = analogRead(STEPS_PIN); + static uint8_t confirmedLength = 16; + static uint8_t candidateLength = 16; + static uint8_t stableCount = 0; + const uint8_t STABLE_THRESHOLD = 8; - // Map the analog value to one of the specific return values: 2, 4, 8, or 16 - if (analogValue < 256) { - return 2; - } else if (analogValue < 512) { - return 4; - } else if (analogValue < 768) { - return 8; + uint16_t analogValue = analogRead(STEPS_PIN); + uint8_t reading; + if (analogValue < 256) reading = 2; + else if (analogValue < 512) reading = 4; + else if (analogValue < 768) reading = 8; + else reading = 16; + + // Confirm that potentiometer is stable (not moving) before read + if (reading == candidateLength) { + if (stableCount < STABLE_THRESHOLD) stableCount++; + if (stableCount == STABLE_THRESHOLD) confirmedLength = candidateLength; } else { - return 16; + candidateLength = reading; + stableCount = 0; } + + return confirmedLength; } uint16_t clearNthLeftBit(uint16_t value, uint8_t n) { @@ -60,26 +74,21 @@ uint16_t clearNthLeftBit(uint16_t value, uint8_t n) { } void doClockCycle(bool clockstate) { - // Rising edge if (clockstate) { - clockLeading = true; - clockTrailing = false; + clockEvent = EVT_LEADING; } else { - clockTrailing = true; - clockLeading = false; + // Drive gates low directly in the ISR using port manipulation (faster than + // digitalWrite) to minimise falling-edge latency to downstream eurorack modules. + // GATE_OUT1 = pin 2 = PD2, GATE_OUT2 = pin 3 = PD3. + PORTD &= ~((1 << PD2) | (1 << PD3)); } } void setup() { - #if defined(DEBUGA) || defined(DEBUGB) - Serial.begin(115200); - Serial.println("Starting..."); - #endif - // Initialize Pin Modes pinMode(GATE_OUT1, OUTPUT); pinMode(GATE_OUT2, OUTPUT); - pinMode(CLOCK_PIN, INPUT_PULLUP); + pinMode(CLOCK_PIN, INPUT_PULLUP); // Pullup prevents spurious interrupts when no clock is patched in // set up Clock interrupt PcInt::attachInterrupt(CLOCK_PIN, doClockCycle, CHANGE); @@ -90,56 +99,49 @@ void setup() { dac.setGainA(MCP4822::High); dac.setGainB(MCP4822::High); - randomSeed(analogRead(XTAL)); + randomSeed(analogRead(ENTROPY_PIN)); } void loop() { - int lockValue = analogRead(LOCK_PIN); - - if (clockLeading) - { - //Set lock values - uint16_t lockValueA = (uint16_t)(constrain((int) lockValue, 0, 1023)); - uint16_t lockValueB = (uint16_t)(constrain((int) lockValue, 0, 1023)); - shiftRegister1 = clearNthLeftBit((shiftRegister1 << 1), shiftRegisterLength) | (lockValueA < random(0,2048) ? (shiftRegister1 >> (shiftRegisterLength - 1)) : (~shiftRegister1 >> (shiftRegisterLength - 1))); + cli(); + uint8_t evt = clockEvent; + clockEvent = EVT_NONE; + sei(); + + if (evt == EVT_LEADING) { + // The active LFSR bits are 0..(shiftRegisterLength-1); the current output bit + // is the MSB of that window, i.e. bit (shiftRegisterLength-1). + bool gate1 = (shiftRegister1 >> (shiftRegisterLength - 1)) & 1; + bool gate2 = (shiftRegister2 >> (shiftRegisterLength - 1)) & 1; + + // Only update CV when the gate fires so CV holds its last value on silent steps, + // preventing unwanted pitch/mod changes on downstream modules between gates. + if (gate1) dac.setVoltageA(shiftRegister1 >> 4); + if (gate2) dac.setVoltageB(shiftRegister2 >> 4); + if (gate1 || gate2) dac.updateDAC(); + + // Drive gates using direct port manipulation. GATE_OUT1=pin2=PD2, GATE_OUT2=pin3=PD3. + if (gate1) PORTD |= (1 << PD2); else PORTD &= ~(1 << PD2); + if (gate2) PORTD |= (1 << PD3); else PORTD &= ~(1 << PD3); + + int lockValue = analogRead(LOCK_PIN); + + // Compute next state + int cvA = analogRead(CV_1) * CV_GAIN; + int cvB = analogRead(CV_2) * CV_GAIN; + uint16_t lockValueA = (uint16_t)(constrain((int)lockValue + cvA - CV_NEUTRAL_POINT, 0, 1023)); + uint16_t lockValueB = (uint16_t)(constrain((int)lockValue + cvB - CV_NEUTRAL_POINT, 0, 1023)); + shiftRegister1 = clearNthLeftBit(shiftRegister1 << 1, shiftRegisterLength) | (((lockValueA < random(6, 1024)) ? (shiftRegister1 >> (shiftRegisterLength - 1)) : (~shiftRegister1 >> (shiftRegisterLength - 1))) & 1); // Invert functionality of lockValue when switch is in reverse position if (switchPosition) { - shiftRegister2 = clearNthLeftBit((shiftRegister2 << 1), shiftRegisterLength) | (lockValueB < random(0,2048) ? (shiftRegister2 >> (shiftRegisterLength - 1)) : (~shiftRegister2 >> (shiftRegisterLength - 1))); + shiftRegister2 = clearNthLeftBit(shiftRegister2 << 1, shiftRegisterLength) | (((lockValueB < random(6, 1024)) ? (shiftRegister2 >> (shiftRegisterLength - 1)) : (~shiftRegister2 >> (shiftRegisterLength - 1))) & 1); } else { - shiftRegister2 = clearNthLeftBit((shiftRegister2 << 1), shiftRegisterLength) | ((1024 - lockValueB) < random(0,2048) ? (shiftRegister2 >> (shiftRegisterLength - 1)) : (~shiftRegister2 >> (shiftRegisterLength - 1))); - } - //Send new CV if shiftRegisters are changed - uint16_t new_cv_1 = shiftRegister1 >> 4; - uint16_t new_cv_2 = shiftRegister2 >> 4; - - dac.setVoltageA(shiftRegister1 >> 4); - dac.setVoltageB(shiftRegister2 >> 4); - dac.updateDAC(); - #ifdef DEBUGA - if (shiftRegister1 >= 0x8000) { - Serial.print("Sending Gate A - "); - Serial.println(shiftRegister1 >> 4); - } - #endif - digitalWrite(GATE_OUT1, shiftRegister1 >= 0x8000); - #ifdef DEBUGB - if (shiftRegister2 >= 0x8000) { - Serial.print("Sending Gate B - "); - Serial.println(shiftRegister1 >> 4); + shiftRegister2 = clearNthLeftBit(shiftRegister2 << 1, shiftRegisterLength) | ((((1024 - lockValueB) < random(6, 1024)) ? (shiftRegister2 >> (shiftRegisterLength - 1)) : (~shiftRegister2 >> (shiftRegisterLength - 1))) & 1); } - #endif - digitalWrite(GATE_OUT2, shiftRegister2 >= 0x8000); - clockLeading = false; - } - else if (clockTrailing) - { - digitalWrite(GATE_OUT1, LOW); - digitalWrite(GATE_OUT2, LOW); - clockTrailing = false; + } else { + //Length of sequence (2,4,8,16) selected by STEPS_PIN + shiftRegisterLength = getShiftRegisterLength(); + switchPosition = readSwitchPosition(); } - - //Length of sequence (2,4,8,16) selected by STEPS_PIN - shiftRegisterLength = getShiftRegisterLength(); - switchPosition = readSwitchPosition(); } From 98d45741a1eaee655f94633db5afb4d6863c0cdb Mon Sep 17 00:00:00 2001 From: erebusnz Date: Tue, 17 Mar 2026 23:03:11 +1300 Subject: [PATCH 7/8] Fixed shift register bug when length 16 selected --- apple-pie-firmware.ino | 91 ++++++++++++++++++++++++++++-------------- 1 file changed, 60 insertions(+), 31 deletions(-) diff --git a/apple-pie-firmware.ino b/apple-pie-firmware.ino index e80e0f6..0ac5b00 100644 --- a/apple-pie-firmware.ino +++ b/apple-pie-firmware.ino @@ -19,24 +19,34 @@ MCP4822 dac(DAC_PIN); #define EVT_LEADING 1 // CV input scaling: gain expands 0-1023 ADC range; neutral offset is the -// scaled CV value that produces zero effect on the lock (analogRead ~309, ~1.5V) -const float CV_GAIN = 1.7; -const int CV_NEUTRAL_POINT = 525; +// scaled CV value that produces zero effect on the lock (analogRead ~309, ~1.5V). +// Gain expressed as integer ratio (17/10 = 1.7) to avoid software float multiply on AVR. +const int CV_GAIN_NUM = 17; +const int CV_GAIN_DEN = 10; +const int CV_NEUTRAL_POINT = 525; + +// Lock threshold range. Small lower bound ensures the +// potentiometer can reliably achieve a fully-locked sequence at minimum position. +const uint16_t LOCK_MIN = 6; +const uint16_t LOCK_MAX = 1024; // Global State volatile uint8_t clockEvent = EVT_NONE; -uint8_t shiftRegisterLength = 16; +uint8_t shiftRegisterLength = 16; +uint16_t srMask = 0xFFFF; // bitmask for active LFSR window; kept in sync with shiftRegisterLength bool switchPosition = true; -// Shift Register Arrays +// Shift Register Arrays — initialised to complementary patterns so both +// channels start active and neither is stuck in an all-zero or all-one state. uint16_t shiftRegister1 = 0xAAAA; uint16_t shiftRegister2 = 0x5555; -// Function to read switch position +// Returns true if the mode switch is in the forward position. bool readSwitchPosition() { return analogRead(SWITCH_PIN) > 512; } +// Returns the active LFSR length (2, 4, 8, or 16) debounced from the steps potentiometer. uint8_t getShiftRegisterLength() { static uint8_t confirmedLength = 16; static uint8_t candidateLength = 16; @@ -62,17 +72,8 @@ uint8_t getShiftRegisterLength() { return confirmedLength; } -uint16_t clearNthLeftBit(uint16_t value, uint8_t n) { - // Calculate the bit position from the left - uint8_t bitPosition = 16 - n; - - // Create a mask with all bits set to 1 except the nth leftmost bit - uint16_t mask = ~(1 << bitPosition); - - // Clear the nth leftmost bit by applying the mask - return value & mask; -} +// ISR: flags a leading-edge event on rising clock, drives gates low immediately on falling edge. void doClockCycle(bool clockstate) { if (clockstate) { clockEvent = EVT_LEADING; @@ -84,6 +85,7 @@ void doClockCycle(bool clockstate) { } } +// Initialises pins, DAC, clock interrupt, and random seed. void setup() { // Initialize Pin Modes pinMode(GATE_OUT1, OUTPUT); @@ -102,6 +104,7 @@ void setup() { randomSeed(analogRead(ENTROPY_PIN)); } +// Main loop: advances LFSRs and updates gate/CV outputs on each clock edge. void loop() { cli(); uint8_t evt = clockEvent; @@ -109,16 +112,39 @@ void loop() { sei(); if (evt == EVT_LEADING) { - // The active LFSR bits are 0..(shiftRegisterLength-1); the current output bit - // is the MSB of that window, i.e. bit (shiftRegisterLength-1). + // NOTE: window placement differs from the original reference sketch. + // Here the active LFSR bits occupy the BOTTOM of the 16-bit register: + // bits 0..(shiftRegisterLength-1), with the gate/output bit at position + // (shiftRegisterLength-1). The reference places the window at the TOP + // (bits (16-n)..15, gate = bit 15), but its feedback path is also sourced + // from bit (n-1) and inserted at bit 0, so the new bit can never propagate + // into the top window for n < 16 — the sequence freezes after ~n clocks. + // The bottom-window approach used here is correct for all supported lengths + // (2, 4, 8, 16). bool gate1 = (shiftRegister1 >> (shiftRegisterLength - 1)) & 1; bool gate2 = (shiftRegister2 >> (shiftRegisterLength - 1)) & 1; + // Scale active LFSR window to 12-bit DAC range. + // Short sequences (n<=12) shift left to fill the range; n=16 shifts right. + uint16_t cv1 = (shiftRegisterLength <= 12) + ? ((shiftRegister1 & srMask) << (12 - shiftRegisterLength)) + : ((shiftRegister1 & srMask) >> (shiftRegisterLength - 12)); + uint16_t cv2 = (shiftRegisterLength <= 12) + ? ((shiftRegister2 & srMask) << (12 - shiftRegisterLength)) + : ((shiftRegister2 & srMask) >> (shiftRegisterLength - 12)); + // Only update CV when the gate fires so CV holds its last value on silent steps, // preventing unwanted pitch/mod changes on downstream modules between gates. - if (gate1) dac.setVoltageA(shiftRegister1 >> 4); - if (gate2) dac.setVoltageB(shiftRegister2 >> 4); - if (gate1 || gate2) dac.updateDAC(); + // Track last values so both channels can always be written before updateDAC, + // preventing uninitialised/stale state being latched on a single-channel fire. + static uint16_t lastCv1 = 0, lastCv2 = 0; + if (gate1 || gate2) { + if (gate1) lastCv1 = cv1; + if (gate2) lastCv2 = cv2; + dac.setVoltageA(lastCv1); + dac.setVoltageB(lastCv2); + dac.updateDAC(); + } // Drive gates using direct port manipulation. GATE_OUT1=pin2=PD2, GATE_OUT2=pin3=PD3. if (gate1) PORTD |= (1 << PD2); else PORTD &= ~(1 << PD2); @@ -126,22 +152,25 @@ void loop() { int lockValue = analogRead(LOCK_PIN); - // Compute next state - int cvA = analogRead(CV_1) * CV_GAIN; - int cvB = analogRead(CV_2) * CV_GAIN; + // Compute next state. + // Each LFSR shifts left within the active window (srMask), then feeds back + // its own output bit — either kept or inverted — based on lock probability. + int cvA = (analogRead(CV_1) * CV_GAIN_NUM) / CV_GAIN_DEN; + int cvB = (analogRead(CV_2) * CV_GAIN_NUM) / CV_GAIN_DEN; uint16_t lockValueA = (uint16_t)(constrain((int)lockValue + cvA - CV_NEUTRAL_POINT, 0, 1023)); uint16_t lockValueB = (uint16_t)(constrain((int)lockValue + cvB - CV_NEUTRAL_POINT, 0, 1023)); - shiftRegister1 = clearNthLeftBit(shiftRegister1 << 1, shiftRegisterLength) | (((lockValueA < random(6, 1024)) ? (shiftRegister1 >> (shiftRegisterLength - 1)) : (~shiftRegister1 >> (shiftRegisterLength - 1))) & 1); - // Invert functionality of lockValue when switch is in reverse position - if (switchPosition) { - shiftRegister2 = clearNthLeftBit(shiftRegister2 << 1, shiftRegisterLength) | (((lockValueB < random(6, 1024)) ? (shiftRegister2 >> (shiftRegisterLength - 1)) : (~shiftRegister2 >> (shiftRegisterLength - 1))) & 1); - } else { - shiftRegister2 = clearNthLeftBit(shiftRegister2 << 1, shiftRegisterLength) | ((((1024 - lockValueB) < random(6, 1024)) ? (shiftRegister2 >> (shiftRegisterLength - 1)) : (~shiftRegister2 >> (shiftRegisterLength - 1))) & 1); - } + uint16_t newBit1 = (lockValueA < random(LOCK_MIN, LOCK_MAX)) ? gate1 : !gate1; + shiftRegister1 = ((shiftRegister1 << 1) & srMask) | newBit1; + + // Switch inverts the lock probability for channel 2. + uint16_t threshold2 = switchPosition ? lockValueB : (1024 - lockValueB); + uint16_t newBit2 = (threshold2 < random(LOCK_MIN, LOCK_MAX)) ? gate2 : !gate2; + shiftRegister2 = ((shiftRegister2 << 1) & srMask) | newBit2; } else { //Length of sequence (2,4,8,16) selected by STEPS_PIN shiftRegisterLength = getShiftRegisterLength(); + srMask = (shiftRegisterLength < 16) ? (uint16_t)((1U << shiftRegisterLength) - 1) : 0xFFFF; switchPosition = readSwitchPosition(); } } From f4c550ae5ce973ada4e5118f10caf7700a1fc397 Mon Sep 17 00:00:00 2001 From: erebusnz Date: Tue, 17 Mar 2026 23:38:38 +1300 Subject: [PATCH 8/8] Replaced interrupt dependency with direct AVR / ISR code --- apple-pie-firmware.ino | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/apple-pie-firmware.ino b/apple-pie-firmware.ino index 0ac5b00..46e8e66 100644 --- a/apple-pie-firmware.ino +++ b/apple-pie-firmware.ino @@ -1,5 +1,4 @@ #include -#include // Pin Definitions #define GATE_OUT1 2 @@ -85,6 +84,10 @@ void doClockCycle(bool clockstate) { } } +ISR(PCINT0_vect) { + doClockCycle(PINB & (1 << PB1)); +} + // Initialises pins, DAC, clock interrupt, and random seed. void setup() { // Initialize Pin Modes @@ -92,8 +95,9 @@ void setup() { pinMode(GATE_OUT2, OUTPUT); pinMode(CLOCK_PIN, INPUT_PULLUP); // Pullup prevents spurious interrupts when no clock is patched in - // set up Clock interrupt - PcInt::attachInterrupt(CLOCK_PIN, doClockCycle, CHANGE); + // Enable pin-change interrupt on CLOCK_PIN (pin 9 = PB1 = PCINT1). + PCICR |= (1 << PCIE0); // enable PCINT[7:0] group (PORTB) + PCMSK0 |= (1 << PCINT1); // unmask PCINT1 (pin 9) only dac.init(); dac.turnOnChannelA();