Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
120 changes: 82 additions & 38 deletions muxlicer/functions.ino
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ void read_clock_detect_input () {
if (first_no_clock_detect) {
first_no_clock_detect = false;
clk_in_mult = 0;
write_clock_in_mult_to_EEPROM ();
EEPROM_modified = true; /// defer the write; see service_pending_EEPROM_writes()
EEPROM_counter = current_micros;
}
}
else {
Expand Down Expand Up @@ -199,14 +200,6 @@ void read_encoder () {
}
}
}
if (EEPROM_modified) {
if (current_micros > EEPROM_counter + 3000000) {
EEPROM_modified = false;
write_tempo_to_EEPROM ();
write_clock_in_mult_to_EEPROM ();
}
}

}


Expand Down Expand Up @@ -252,11 +245,11 @@ void read_internal_clock_tap () {
bitWrite(encoder_button_state, 1, 0);
if (range_changed) {
range_changed = false;
write_range_to_EEPROM();
range_write_pending = true; /// defer the write; see service_pending_EEPROM_writes()
}
if (clk_out_mul_changed) {
clk_out_mul_changed = false;
write_clock_out_mult_to_EEPROM();
clock_out_mult_write_pending = true;
}
}
}
Expand Down Expand Up @@ -317,6 +310,28 @@ void write_no_clock_when_stop_to_EEPROM () {
EEPROM.write(15, no_clock_out_when_stop);
}

void service_pending_EEPROM_writes () {
/// EEPROM.write blocks ~3.3 ms per byte; a stall that long delays step,
/// gate and clock out timing, so pending writes are only performed while
/// the sequencer is stopped.
if (start_on) return;
if (EEPROM_modified) {
if (current_micros > EEPROM_counter + 3000000) {
EEPROM_modified = false;
write_tempo_to_EEPROM ();
write_clock_in_mult_to_EEPROM ();
}
}
if (range_write_pending) {
range_write_pending = false;
write_range_to_EEPROM ();
}
if (clock_out_mult_write_pending) {
clock_out_mult_write_pending = false;
write_clock_out_mult_to_EEPROM ();
}
}


void read_start_toggle () {
//// START STOP TOGGLE CONTROL
Expand Down Expand Up @@ -380,12 +395,18 @@ void read_start_toggle () {
void read_one_shot_reset_input () {
/// RESET CONTROL

reset_state = digitalRead(reset_input);
if ((reset_state == false) && (reset_first == false) ) { /// reset_input jack

reset_first = true;
bool do_reset = false;
noInterrupts();
if (reset_edge) { /// edge captured by the pin change ISR
reset_edge = false;
do_reset = true;
}
interrupts();
if (do_reset) { /// reset_input jack
if (start_on) {
address_counter = 7;
division_counter = -clk_in_mult; /// re-arm the clock divider so the next clock advances to step 1 right away
gate_out_window = 0; /// hold multiplied steps until the next clock edge re-opens the window
if (one_shot_state) one_shot_start = true;
}
else {
Expand All @@ -401,9 +422,6 @@ void read_one_shot_reset_input () {
division_counter = -clk_in_mult;
}
}
if ((reset_state == true) && (reset_first == true)) {
reset_first = false;
}
}

void read_one_shot_reset_toggle () {
Expand All @@ -413,6 +431,8 @@ void read_one_shot_reset_toggle () {
one_shot_first = true;
if (start_on) {
address_counter = 7;
division_counter = -clk_in_mult; /// re-arm the clock divider so the next clock advances to step 1 right away
gate_out_window = 0; /// hold multiplied steps until the next clock edge re-opens the window
if (one_shot_state) one_shot_start = true;
}
else {
Expand All @@ -426,8 +446,6 @@ void read_one_shot_reset_toggle () {
}
division_counter = -clk_in_mult;
}

//division_counter = -clk_in_mult;
}
if ((digitalRead(one_shot_switch)) && (one_shot_first == true)) {
one_shot_first = false;
Expand All @@ -437,6 +455,12 @@ void read_one_shot_reset_toggle () {

void read_address () {
///// ADDRESS CV/pot
/// analogRead blocks for ~112 us; reading once per loop pass dominated
/// the loop time and with it the external clock processing latency.
/// Reading every 2 ms (500 Hz) is still plenty for the address CV.
static unsigned long last_address_read = 0;
if (current_micros - last_address_read < 2000) return;
last_address_read = current_micros;
address_value = analogRead(address_input);
switch (address_value) {
case 0 ... 112:
Expand Down Expand Up @@ -682,10 +706,32 @@ void read_clock () {
current_micros = micros();

if (clock_detect) {
if ((!digitalRead(clock_input)) && (external_clock_first == false)) { /// IF THE CLOCK IS NOT HIGH AND IT IS THE FIRST TIME IT IS,
ext_clock = current_micros - old_external_clock;
old_external_clock = current_micros;
external_clock_first = true;
bool clock_edge = false;
unsigned long clock_edge_stamp = 0;
noInterrupts();
if (ext_clock_edge) { /// edge captured by the pin change ISR
ext_clock_edge = false;
clock_edge_stamp = ext_clock_edge_stamp;
clock_edge = true;
}
interrupts();
if (clock_edge) {
unsigned long clock_interval = clock_edge_stamp - old_external_clock;
old_external_clock = clock_edge_stamp;
/// The first edge after the external clock was stopped and restarted
/// (e.g. sequencer stop/play) measures the whole pause instead of the
/// tempo. Adopting that value stalls multiplied steps for a full clock
/// interval and leaves the playhead offset afterwards. Accept an
/// interval as the new period only if it is plausible (shorter than
/// 3x the current period) or confirmed by a second similar interval
/// (a real tempo change); otherwise keep the previous period.
if ((ext_clock == 0)
|| (clock_interval < ext_clock * 3)
|| ((clock_interval < last_clock_interval + (last_clock_interval >> 2))
&& (last_clock_interval < clock_interval + (last_clock_interval >> 2)))) {
ext_clock = clock_interval;
}
last_clock_interval = clock_interval;

if (one_shot_start) {
one_shot_start = false;
Expand All @@ -704,7 +750,7 @@ void read_clock () {
}
calculate_clock_out (); /// count the tics to create the clock out bearing in mind the clock out multiplier, triggering
if (clock_out_mult > 0) { /// CLOCK OUT MULTIPLIER
old_clock_out = current_micros;
old_clock_out = clock_edge_stamp;
if( clock_running ){//andyB ADDED CONDITION
next_clock_flag = true;
//digitalWrite(clock_out, LOW);
Expand All @@ -722,7 +768,7 @@ void read_clock () {
//
clock_out_state = HIGH;
//
old_clock_out = current_micros;
old_clock_out = clock_edge_stamp;
if( clock_running ){//andyB ADDED CONDITION
next_clock_flag = true;
//digitalWrite(clock_out, LOW);
Expand All @@ -733,7 +779,7 @@ void read_clock () {

}
else { //// CLOCK OUT NEUTRAL
old_clock_out = current_micros;
old_clock_out = clock_edge_stamp;
if( clock_running ){//andyB ADDED CONDITION
next_clock_flag = true;
//digitalWrite(clock_out, LOW);
Expand All @@ -752,9 +798,9 @@ void read_clock () {
gate_out_window = ext_clock;
next_address_flag = true;
next_step_flag = true;
old_micros_mult = current_micros;
gate_counter_old = current_micros;
next_address_stamp = current_micros;
old_micros_mult = clock_edge_stamp;
gate_counter_old = clock_edge_stamp;
next_address_stamp = clock_edge_stamp;
repetitions_counter = 0;
}
else if (clk_in_mult < 0) {
Expand All @@ -765,19 +811,19 @@ void read_clock () {
division_counter = 0;
next_address_flag = true;
next_step_flag = true;
old_micros_mult = current_micros;
gate_counter_old = current_micros;
next_address_stamp = current_micros;
old_micros_mult = clock_edge_stamp;
gate_counter_old = clock_edge_stamp;
next_address_stamp = clock_edge_stamp;
}
}
else {
ext_clock_mult = ext_clock;
gate_out_window = ext_clock_mult;
next_address_flag = true;
next_step_flag = true;
old_micros_mult = current_micros;
gate_counter_old = current_micros;
next_address_stamp = current_micros;
old_micros_mult = clock_edge_stamp;
gate_counter_old = clock_edge_stamp;
next_address_stamp = clock_edge_stamp;
}
}
}
Expand Down Expand Up @@ -847,8 +893,6 @@ void read_clock () {
}
}
}
if ((digitalRead(clock_input)) && (external_clock_first)) external_clock_first = 0;

}

void event_control () {
Expand Down
41 changes: 37 additions & 4 deletions muxlicer/muxlicer.ino
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,32 @@ void timerIsr() {
timer1_interrupt_flag = true;// so just flag it needs to be done
}

/// External clock edge capture via pin change interrupt (PD4 / PCINT20).
/// The clock input used to be polled in loop(), so edge detection jittered
/// by one loop pass and pulses could be missed entirely during blocking
/// calls (e.g. EEPROM writes). The ISR only stores a timestamp; all
/// processing stays in read_clock().
volatile bool ext_clock_edge = false;
volatile unsigned long ext_clock_edge_stamp = 0;

ISR(PCINT2_vect) {
if (!(PIND & (1 << PIND4))) { /// falling edge of the incoming clock (non-inverting input) - same edge the polled code used
ext_clock_edge_stamp = micros();
ext_clock_edge = true;
}
}

/// Reset input edge capture via pin change interrupt (PC3 / PCINT11),
/// for the same reason as the external clock above: a polled reset pulse
/// could be missed during blocking calls.
volatile bool reset_edge = false;

ISR(PCINT1_vect) {
if (!(PINC & (1 << PINC3))) { /// rising edge of the incoming reset (input stage inverts) - same edge the polled code used
reset_edge = true;
}
}


//// PORTS DEFINITION

Expand Down Expand Up @@ -114,6 +140,7 @@ bool start_stop_down_first = true;

unsigned long current_external_clock = 0;
unsigned long ext_clock = 0;
unsigned long last_clock_interval = 0;
unsigned long ext_clock_mult = 0;
unsigned long old_micros_address = 0;
unsigned long old_external_clock = 0;
Expand Down Expand Up @@ -145,9 +172,6 @@ bool second_gate = false;

bool no_gate_or_full = false;

bool external_clock_first = false;
bool reset_first = false;
bool reset_state = false;
bool one_shot_state = false;
bool one_shot_first = false;
bool one_shot_start = false;
Expand Down Expand Up @@ -183,6 +207,8 @@ int no_odd_clock_in_index = 0;

bool EEPROM_modified = false;
unsigned long EEPROM_counter = 0;
bool range_write_pending = false;
bool clock_out_mult_write_pending = false;


bool new_code = false;
Expand Down Expand Up @@ -232,6 +258,12 @@ void setup() {
pinMode (one_shot_switch, INPUT_PULLUP);
pinMode (clock_input, INPUT_PULLUP);

/// pin change interrupts for the external clock and reset inputs
PCICR |= (1 << PCIE2); /// enable pin change interrupt group 2 (PORTD)
PCMSK2 |= (1 << PCINT20); /// PD4 = clock_input
PCICR |= (1 << PCIE1); /// enable pin change interrupt group 1 (PORTC)
PCMSK1 |= (1 << PCINT11); /// PC3 (A3) = reset_input


pinMode (encoder_button, INPUT_PULLUP);
pinMode (encoder_A, INPUT_PULLUP);
Expand Down Expand Up @@ -396,7 +428,8 @@ void loop() {
gate_delay();
control_clock_out();
gate_to_low_control ();
if(timer1_interrupt_flag){//andyB
service_pending_EEPROM_writes ();
if(timer1_interrupt_flag){//andyB
encoder->service();//andyB, not essential to do this, but should help timing
timer1_interrupt_flag = false;
}
Expand Down