From c12b4716e39ad9c5a5ca9ca4acf978ab9b70d0fc Mon Sep 17 00:00:00 2001 From: Delio Brignoli Date: Tue, 2 Jan 2018 16:59:08 +0100 Subject: [PATCH 1/3] Setup and init PLL When USB is not init by Arduino core the PLL clocking the high speed timer and USB has to be setup by us. --- src/osc.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/osc.c b/src/osc.c index b6e270a..6e5fd1f 100644 --- a/src/osc.c +++ b/src/osc.c @@ -48,6 +48,9 @@ void osc_setup(void) osc_reset(); /* PWM setup using timer 4 */ PLLFRQ = 0b01011010; /* PINMUX:16MHz XTAL, PLLUSB:48MHz, PLLTM:1, PDIV:96MHz */ + PLLCSR = 0b00010010; + /* Wait for PLL lock */ + while (!(PLLCSR & 0x01)) {} TCCR4A = 0b01000010; /* PWM mode */ /* TCCR4B will be se to 0b00000001 for clock source/1, 96MHz/(OCR4C+1)/2 ~ 95703Hz */ TCCR4D = 0b00000001; /* Dual Slope PWM (the /2 in the eqn. above is because of dual slope PWM) */ From d5a4928470aa37cd950683761b964624a593f27f Mon Sep 17 00:00:00 2001 From: Delio Brignoli Date: Wed, 13 Dec 2017 08:52:09 +0000 Subject: [PATCH 2/3] osc: re-implement ISR in C, use a Galois LFSR and skip computation for osc with zero volume --- src/osc.c | 242 +++++++++++------------------------------------------- 1 file changed, 49 insertions(+), 193 deletions(-) diff --git a/src/osc.c b/src/osc.c index 6e5fd1f..fb922b7 100644 --- a/src/osc.c +++ b/src/osc.c @@ -140,200 +140,56 @@ static __attribute__((used)) void osc_tick_handler(void) } } -#define ASM_EOL "\n\t" - -ISR(TIMER3_COMPA_vect, ISR_NAKED) +ISR(TIMER3_COMPA_vect) { - asm volatile( -" push r2 " ASM_EOL -" in r2, __SREG__ " ASM_EOL -" push r18 " ASM_EOL -" push r27 " ASM_EOL -" push r26 " ASM_EOL -" push r0 " ASM_EOL -" push r1 " ASM_EOL - -"; Setup DC offset " ASM_EOL -" ldi r26 ,%[dcl] " ASM_EOL -" ldi r27 ,%[dch] " ASM_EOL - -"; OSC 3 noise generator " ASM_EOL -" ldi r18, 1 " ASM_EOL -" lds r0, osc_params_array+3*%[osz]+%[phi] " ASM_EOL -" lds r1, osc_params_array+3*%[osz]+%[phi]+1 " ASM_EOL -" add r0, r0 " ASM_EOL -" adc r1, r1 " ASM_EOL -" sbrc r1, 7 " ASM_EOL -" eor r0, r18 " ASM_EOL -" sbrc r1, 6 " ASM_EOL -" eor r0, r18 " ASM_EOL -" sts osc_params_array+3*%[osz]+%[phi], r0 " ASM_EOL -" sts osc_params_array+3*%[osz]+%[phi]+1, r1 " ASM_EOL -"; OSC 3 continued " ASM_EOL -" clr r18 " ASM_EOL -" lds r0, osc_params_array+3*%[osz]+%[vol] " ASM_EOL -" neg r1 " ASM_EOL -" brpl 1f " ASM_EOL -" com r18 " ASM_EOL -" neg r0 " ASM_EOL -" sbci r18,-1 " ASM_EOL -"1: " ASM_EOL -" add r26, r0 " ASM_EOL -" adc r27, r18 " ASM_EOL - -"; OSC 0 advance phase accumulator " ASM_EOL -" lds r18, osc_params_array+0*%[osz]+%[phi] " ASM_EOL -" lds r0, osc_pha_acc_array+0*%[asz] " ASM_EOL -" add r0, r18 " ASM_EOL -" sts osc_pha_acc_array+0*%[asz], r0 " ASM_EOL -" lds r18, osc_params_array+0*%[osz]+%[phi]+1 " ASM_EOL -" lds r0, osc_pha_acc_array+0*%[asz]+1 " ASM_EOL -" adc r0, r18 " ASM_EOL -" sts osc_pha_acc_array+0*%[asz]+1, r0 " ASM_EOL -"; OSC 0 square waveform " ASM_EOL -" lds r1, osc_params_array+0*%[osz]+%[mod] " ASM_EOL -" clr r18 " ASM_EOL -" cp r0, r1 " ASM_EOL -" lds r1, osc_params_array+0*%[osz]+%[vol] " ASM_EOL -" brcs 1f " ASM_EOL -" com r18 " ASM_EOL -" neg r1 " ASM_EOL -" sbci r18,-1 " ASM_EOL -"1: " ASM_EOL -" add r26, r1 " ASM_EOL -" adc r27, r18 " ASM_EOL - -"; OSC 1 advance phase accumulator " ASM_EOL -" lds r18, osc_params_array+1*%[osz]+%[phi] " ASM_EOL -" lds r0, osc_pha_acc_array+1*%[asz] " ASM_EOL -" add r0, r18 " ASM_EOL -" sts osc_pha_acc_array+1*%[asz], r0 " ASM_EOL -" lds r18, osc_params_array+1*%[osz]+%[phi]+1 " ASM_EOL -" lds r0, osc_pha_acc_array+1*%[asz]+1 " ASM_EOL -" adc r0, r18 " ASM_EOL -" sts osc_pha_acc_array+1*%[asz]+1, r0 " ASM_EOL -"; OSC 1 square waveform " ASM_EOL -" lds r1, osc_params_array+1*%[osz]+%[mod] " ASM_EOL -" clr r18 " ASM_EOL -" cp r0, r1 " ASM_EOL -" lds r1, osc_params_array+1*%[osz]+%[vol] " ASM_EOL -" brcs 1f " ASM_EOL -" com r18 " ASM_EOL -" neg r1 " ASM_EOL -" sbci r18,-1 " ASM_EOL -"1: " ASM_EOL -" add r26, r1 " ASM_EOL -" adc r27, r18 " ASM_EOL - -"; OSC 2 advance phase accumulator " ASM_EOL -" lds r18, osc_params_array+2*%[osz]+%[phi] " ASM_EOL -" lds r0, osc_pha_acc_array+2*%[asz] " ASM_EOL -" add r0, r18 " ASM_EOL -" sts osc_pha_acc_array+2*%[asz], r0 " ASM_EOL -" lds r18, osc_params_array+2*%[osz]+%[phi]+1 " ASM_EOL -" lds r0, osc_pha_acc_array+2*%[asz]+1 " ASM_EOL -" adc r0, r18 " ASM_EOL -" sts osc_pha_acc_array+2*%[asz]+1, r0 " ASM_EOL -"; OSC 2 square waveform " ASM_EOL -" lds r1, osc_params_array+2*%[osz]+%[mod] " ASM_EOL -" clr r18 " ASM_EOL -" cp r0, r1 " ASM_EOL -" lds r1, osc_params_array+2*%[osz]+%[vol] " ASM_EOL -" brcs 1f " ASM_EOL -" com r18 " ASM_EOL -" neg r1 " ASM_EOL -" sbci r18,-1 " ASM_EOL -"1: " ASM_EOL -" add r26, r1 " ASM_EOL -" adc r27, r18 " ASM_EOL -" sts %[regh], r27 " ASM_EOL -" sts %[reg], r26 " ASM_EOL -"; tick handler prescaler " ASM_EOL -" lds r18, osc_int_count " ASM_EOL -" dec r18 " ASM_EOL -" sts osc_int_count, r18 " ASM_EOL -" and r18, r18 " ASM_EOL -" ;check if a channel tick is due only once every " ASM_EOL -" ;8 interrupts " ASM_EOL -" brne isr_done " ASM_EOL -" ldi r18, %[div] " ASM_EOL -" sts osc_int_count, r18 " ASM_EOL -"; check if a tick is due for each OSC " ASM_EOL -" clr r27 " ASM_EOL -" lds r26, osc_cb+0*%[csz]+%[pre] " ASM_EOL -" subi r26, 1 " ASM_EOL -" ;Use the carry from r26-1 to make r27 undeflow " ASM_EOL -" ;instead of branching. (saves 9 cycles in total) " ASM_EOL -" sbci r27, 0 " ASM_EOL -" sts osc_cb+0*%[csz]+%[pre], r26 " ASM_EOL -" lds r26, osc_cb+1*%[csz]+%[pre] " ASM_EOL -" subi r26, 1 " ASM_EOL -" sbci r27, 0 " ASM_EOL -" sts osc_cb+1*%[csz]+%[pre], r26 " ASM_EOL -" ;r27 underflow means at least one channel's " ASM_EOL -" ;tick is due. " ASM_EOL -" brne call_playroutine " ASM_EOL -"isr_done: " ASM_EOL -" pop r1 " ASM_EOL -" pop r0 " ASM_EOL -" pop r26 " ASM_EOL -" pop r27 " ASM_EOL -" pop r18 " ASM_EOL -" out __SREG__, r2 " ASM_EOL -" pop r2 " ASM_EOL -" reti " ASM_EOL -"call_playroutine: " ASM_EOL -" lds r26, osc_isr_reenter " ASM_EOL -" cpi r26, 0 " ASM_EOL -" brne isr_done " ASM_EOL -" ;r27 is always nonzero when reaching this point " ASM_EOL -" sts osc_isr_reenter, r27 " ASM_EOL -" sei " ASM_EOL -" push r19 " ASM_EOL -" push r20 " ASM_EOL -" push r21 " ASM_EOL -" push r22 " ASM_EOL -" push r23 " ASM_EOL -" push r24 " ASM_EOL -" push r25 " ASM_EOL -" push r30 " ASM_EOL -" push r31 " ASM_EOL - -" clr r1 " ASM_EOL -" call osc_tick_handler " ASM_EOL + uint16_t pcm = OSC_DC_OFFSET; + struct osc_params *p = osc_params_array; + for (uint8_t i=0;i<3;i++,p++) { + const uint8_t vol = p->vol; + if (!vol) { + /* skip if volume is zero and save some cycles */ + continue; + } + const uint16_t pha = osc_pha_acc_array[i] + p->phase_increment; + osc_pha_acc_array[i] = pha; + if (OSC_HI(pha) > p->mod) { + pcm += vol; + } else { + pcm -= vol; + } + } -" sts osc_isr_reenter, r1 " ASM_EOL -" pop r31 " ASM_EOL -" pop r30 " ASM_EOL -" pop r25 " ASM_EOL -" pop r24 " ASM_EOL -" pop r23 " ASM_EOL -" pop r22 " ASM_EOL -" pop r21 " ASM_EOL -" pop r20 " ASM_EOL -" pop r19 " ASM_EOL + /* p == &osc_params_array[3] when leaving the loop above */ + { + const uint8_t vol = p->vol; + /* skip if volume is zero and save some cycles */ + if (vol) { + uint16_t shift_reg = p->phase_increment; + const uint8_t msb = OSC_HI(shift_reg) & 0x80; + shift_reg += shift_reg; + if (msb) { + pcm += vol; + p->phase_increment = shift_reg ^ 0x002D; + } else { + pcm -= vol; + } + } + } -" pop r1 " ASM_EOL -" pop r0 " ASM_EOL -" pop r26 " ASM_EOL -" pop r27 " ASM_EOL -" pop r18 " ASM_EOL -" out __SREG__, r2 " ASM_EOL -" pop r2 " ASM_EOL -" reti " ASM_EOL - :: - [t4e] "M" _SFR_MEM_ADDR(TCCR4E), - [reg] "M" _SFR_MEM_ADDR(OCR4A), - [regh] "M" _SFR_MEM_ADDR(TC4H), - [dch] "M" (OSC_HI(OSC_DC_OFFSET)), - [dcl] "M" (OSC_LO(OSC_DC_OFFSET)), - [div] "M" (OSC_ISR_PRESCALER_DIV), - [osz] "M" (sizeof(struct osc_params)), - [asz] "M" (sizeof(uint16_t)), - [csz] "M" (sizeof(struct callback_info)), - [pre] "M" (offsetof(struct callback_info, callback_prescaler_counter)), - [phi] "M" (offsetof(struct osc_params, phase_increment)), - [mod] "M" (offsetof(struct osc_params, mod)), - [vol] "M" (offsetof(struct osc_params, vol)) - ); + TC4H = OSC_HI(pcm); + OCR4A = OSC_LO(pcm); + + if (!(--osc_int_count)) { + osc_int_count = OSC_ISR_PRESCALER_DIV; + const uint8_t tick0_due = --osc_cb[0].callback_prescaler_counter == 255; + const uint8_t tick1_due = --osc_cb[1].callback_prescaler_counter == 255; + if (tick0_due || tick1_due) { + if (!osc_isr_reenter) { + osc_isr_reenter = 1; + sei(); + osc_tick_handler(); + osc_isr_reenter = 0; + } + } + } } From ed23f9a8c71335296eb47a292925feab008de3c8 Mon Sep 17 00:00:00 2001 From: Delio Brignoli Date: Sun, 7 Jan 2018 10:34:09 +0100 Subject: [PATCH 3/3] osc: fix LFSR (restore noise channel functionality) --- src/osc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/osc.c b/src/osc.c index fb922b7..06b40ae 100644 --- a/src/osc.c +++ b/src/osc.c @@ -169,10 +169,11 @@ ISR(TIMER3_COMPA_vect) shift_reg += shift_reg; if (msb) { pcm += vol; - p->phase_increment = shift_reg ^ 0x002D; + shift_reg ^= 0x002D; } else { pcm -= vol; } + p->phase_increment = shift_reg; } }