In my prior blog ( Multi-Voice Synthesizer - Part 6 - The Pieces are Coming Together. ) I covered the bring-up and demonstrations of my new Moduals for my Multi-Voice Synthesizer project. Today, I circling back to the Noise Source and integrating it into the new Voice Modules. When I started adding the Noise Source into the Voice Modules, I thought this would be a simple task. In the Voice Module firmware I used some #defines and #ifdefs to switch between a Note generator and a Noise generator. I started by recompiling the firmware, using the "#define NOISE_GEN 1" to build the Noise firmware. I loaded the firmware onto the 4th Voice Module, and then strange things happened. The serial commands being sent from the Control Panel, via the Main Board, suddenly started to act strangely. At first no data was passing through the Noise Generator. I tried several attempts at fixing the code, before determining that the register assignments were not working correctly.
It seems that the compiler was using a lot of auto assigned variables to registers, and the register assignments that I had used in my early work were are all scrambled. I rewrote the assembly code that managed the Noise Generation function in the timer interrupt. Now I was correctly generating Noise, but I still had a bunch of communication errors. I decided to skip over the assembly language implementation of the Noise function and coded a slightly simpler version in 'C'. Here is the code for the new Noise function:
union LFSR32 { unsigned long int LFSReg; unsigned char lfsr[4]; };
First I defined a shorter version of the LFSR shift register (32 bits version the 40 bits I had used before.
LFSR.lfsr[0] = (unsigned char) rand(); LFSR.lfsr[1] = (unsigned char) rand(); LFSR.lfsr[2] = (unsigned char) rand(); LFSR.lfsr[3] = (unsigned char) rand();
Using the unsigned char array of the union, I initialized the LFSR in the main function.
cTemp = (LFSR.lfsr[3] & 0x40) ? 1 : 0; cTemp += (LFSR.lfsr[3] & 0x4) ? 1 : 0; cTemp &= 1; LFSR.LFSReg <<= 1; LFSR.lfsr[0] += cTemp;
In the timer interrupt I processed the LFSR feedback, sampling the bit 30 and 26, effectively EXORing them, shifting the register and placing the result into the LSB of the register. This seemed to work in both creating a Noise Source and not breaking the serial command path. With the Noise functioning, I went to work on creating a simple method of updating my control panel to introduce Noise into my Chord sequencer ("Play All Cords" Button). Here are some oscilloscope captures of the results:
First we have the normal chord sequence.
And here is the chord sequence with a noise burst added to the sequence. The normal notes are quarter notes, while the noise burst is an eighth note. You can see the noise riding on top of the of the normal notes in the first half of the chord. The ADSR for the noise burst, is a quick rise and fall, giving a percussive edge.
And here is a short video of the noise source added to a sequence of playing a series of chords:
I am out of time. Tomorrow morning I fly out to California to visit with my Mom (who recently had a stroke, but seems to be recovering nicely). This will be my final update on the Music Time project14 contest. Again, Thanks for following along with my blogs documenting my entry into the project14 'Music Time' competition! This has been a really fun and exciting project for me and it has been a pleasure to share with the members of the element14 community. Thanks for the encouraging words along the way.
Final bits and pieces
Here are the BOMs for the two Modules that make up my Multi-Voice Synthesizer:
Here are the updated source modules that were modified (or created) in fixing the Noise Generator functions:
/************************************************************************* Project : Multi-Voice Synthesizer - Voice Module File : SynthVoice.h Version : 0.1 Date : 4/10/2023 Author : Gene Breniman Company : Young Embedded Systems Comments: Chip type : ATtiny1614 Program type : Application AVR Core Clock frequency: 20.000000 MHz Memory model : Small Data Stack size : 512 Revisions: 4-10-23 - Initial (Pre-Release) *************************************************************************/ #ifndef _SYNTHVOICE_INCLUDED_ #define _SYNTHVOICE_INCLUDED_ union LFSR32 { unsigned long int LFSReg; unsigned char lfsr[4]; }; #endif
/************************************************************************* Project : Multi-Voice Synthesizer - Voice Module File : main.c Version : 0.1 Date : 2/19/2023 Author : Gene Breniman Company : Young Embedded Systems Comments: Chip type : ATtiny1614 Program type : Application AVR Core Clock frequency: 20.000000 MHz Memory model : Small Data Stack size : 512 Revisions: 4-7-23 - Initial (Pre-Release) *************************************************************************/ // I/O Registers definitions #include <tiny1614.h> #include <ctype.h> #include <stdlib.h> #include "SynthVoice.h" // Clock System initialization function #include "clock_init.h" // I/O Ports initialization function #include "ports_init.h" // Timers/Counters initialization functions #include "timers_init.h" // USARTs initialization functions #include "usarts_init.h" // DAC initialization functions #include "dac_init.h" // TWI initialization functions #include "twi_init.h" unsigned char TWI_Xmit[8]; unsigned char TWI_Recv[8]; #ifdef NOTE_GEN unsigned char moduleType = 'V'; #endif #ifdef NOISE_GEN unsigned char moduleType = 'N'; #endif unsigned char VCA_CV; #define VCA_MIN 25 #ifdef NOTE_GEN #define VCA_MAX 255 //register unsigned char PhaseAC1; //register unsigned char PhaseAC2; //register unsigned char PhaseAC3; //register unsigned char StepSize1; //register unsigned char StepSize2; //register unsigned char StepSize3; //register unsigned char WavePtrMSB; //bit LowOrHighBuf; #endif #ifdef NOISE_GEN #define VCA_MAX 160 //register unsigned char LFSR1; //register unsigned char LFSR2; //register unsigned char LFSR3; //register unsigned char LFSR4; //register unsigned char LFSR5; //register unsigned char TEMP; #endif #define MAX_POT 2 #define MAX_MUX 4 unsigned char Effects_ready; unsigned char ADSR_State; unsigned char Attack; unsigned char Decay; unsigned char Sustain; unsigned char Release; unsigned char Address; unsigned char CommMode; #define COMM_IDLE 0 #define COMM_ACTIVE 1 #define COMM_RELAY 2 #define COMM_REPLY 3 unsigned char sendRequested; unsigned char myBytes; unsigned char globalCommand; union LFSR32 LFSR; unsigned char *processIncomingCommand(unsigned char *inchars, unsigned char n); unsigned char *hexToCharVal(unsigned char *cPtr, unsigned char numBits, unsigned char *val); unsigned char *decToCharVal(unsigned char *cPtr, unsigned char numBits, unsigned char *val); void processADSR(void); void processTremolo(void); void processVibrato(void); void main(void) { // Declare your local variables here unsigned char n; unsigned char c; unsigned char inchars[16]; unsigned char outchars[16]; unsigned char *inPtr; // Interrupt system initialization // Optimize for speed #pragma optsize- // Make sure the interrupts are disabled #asm("cli") // Round-robin scheduling for level 0 interrupt: Off // The interrupt vectors will be placed at the start of the Application FLASH section n = 0; CPU_CCP = CCP_IOREG_gc; CPUINT.CTRLA = n; // Restore optimization for size if needed #pragma optsize_default // The vectors with lower addresses will have // higher interrupt level 0 priority (default) CPUINT.LVL0PRI = 0; // The higher interrupt priority level 1 is not used CPUINT.LVL1VEC = 0; // System clocks initialization system_clocks_init(); // Brown-Out Detector and Voltage Level Monitor initialization // The settings below are applied to the BODCFG fuse // that will be programmed if the // Project|Configure|After Build|Action: Program the Chip|Program Fuses // menu option is enabled in the IDE // BOD operation in Active or Idle modes: Disabled // BOD operation in Standby or Power-Down sleep modes: Disabled // BOD level: 3.70V // BODCFG=0xA0 // I/O Ports initialization ports_init(); PORTA.OUT = 0x06; // MUX enable - ENV_OUT #ifdef NOTE_GEN VCA_CV = 0; // Debug set #endif // Timer/Counter TCA0 initialization tca0_init(); tcb0_init(); tcb1_init(); // USART0 initialization usart0_init(); // TWI0 initialization twi0_init(); // DAC0 initialization dac0_init(); #ifdef NOISE_GEN // LFSR1 = (unsigned char) rand(); // LFSR2 = (unsigned char) rand(); // LFSR3 = (unsigned char) rand(); // LFSR4 = (unsigned char) rand(); // LFSR5 = (unsigned char) rand(); // TEMP = 0; LFSR.lfsr[0] = (unsigned char) rand(); LFSR.lfsr[1] = (unsigned char) rand(); LFSR.lfsr[2] = (unsigned char) rand(); LFSR.lfsr[3] = (unsigned char) rand(); #endif CommMode = COMM_IDLE; // Globally enable interrupts #asm("sei") n = 0; TWI_Xmit[0] = 0x13; TWI_Xmit[1] = 0x20; twi_master_trans(&twi0_master, 0x28, TWI_Xmit, 2, TWI_Recv, 0); twi_master_trans(&twi0_master, 0x29, TWI_Xmit, 2, TWI_Recv, 0); while (1) { if (GetSerial0InputCount()) { c = getchar_usart0(); switch (CommMode) { case COMM_IDLE: if (c == '|') { CommMode = COMM_ACTIVE; globalCommand = 0; myBytes = 0; } else if (c == '!') { CommMode = COMM_ACTIVE; globalCommand = 1; putchar_usart0(c); // send marker to next module myBytes = 0; } break; case COMM_ACTIVE: if (c == '|' || c == '/') { if (myBytes == 1 && isdigit(inchars[0])) { // is an Identify string Address = inchars[0] - '0'; sendRequested = 1; outchars[0] = moduleType; } else { // take action on incoming command if (myBytes) { sendRequested = 0; inPtr = inchars; inchars[myBytes] = 0; // mark end of command string will null byte do { inPtr = processIncomingCommand(inPtr, n); } while (*inPtr); } } if (globalCommand) { CommMode = COMM_IDLE; } else { CommMode = COMM_RELAY; } putchar_usart0(c); // send marker to next module } else { inchars[myBytes++] = c; if (globalCommand) { putchar_usart0(c); // send data to next module } } break; case COMM_RELAY: if (c == '/') { if (sendRequested) { if (Address == 0) { putchar_usart0('/'); // send 'end' marker to next module putchar_usart0('\\'); // send 'reply' marker to next module for (n = 0; n < myBytes ;n++) { putchar_usart0(outchars[n]); // send data to next module } CommMode = COMM_IDLE; } else { CommMode = COMM_REPLY; } } else { CommMode = COMM_IDLE; } } putchar_usart0(c); // send comm data to next module break; case COMM_REPLY: if (c == '/') { putchar_usart0('\\'); // send 'reply' marker to next module for (n = 0; n < myBytes ;n++) { putchar_usart0(outchars[n]); // send marker to next module } putchar_usart0('/'); // send marker to next module CommMode = COMM_IDLE; } else { putchar_usart0(c); // send comm data to next module } break; } } if (Effects_ready) { processADSR(); processTremolo(); processVibrato(); Effects_ready = 0; } } } unsigned char *processIncomingCommand(unsigned char *inchars, unsigned char n) { unsigned char var1; unsigned char var2; n--; switch(*inchars++) { case 'A': // ADSR params - 'A'AADDSSRR inchars = hexToCharVal(inchars, 2, &Attack); inchars = hexToCharVal(inchars, 2, &Decay); inchars = hexToCharVal(inchars, 2, &Sustain); inchars = hexToCharVal(inchars, 2, &Release); break; case 'D': // Key Down - 'D'po inchars = hexToCharVal(inchars, 1, &var1); inchars = decToCharVal(inchars, 1, &var2); #ifdef NOTE_GEN if (var1 && var1 <= 12) { var1--; if (var2 > 1 && var2 < 9) { var2 -= 2; } setNote(var1, var2); } #endif ADSR_State = 1; VCA_CV = VCA_MIN; break; case 'M': // Filter MUX select inchars = decToCharVal(inchars, 1, &var1); if (var1 < MAX_MUX) { PORTA.OUT = var1 << 1; // MUX enable - ENV_OUT } break; case 'P': // POT adjust (filter) inchars = decToCharVal(inchars, 1, &var1); inchars = hexToCharVal(inchars, 2, &var2); TWI_Xmit[0] = (var1 == 0) ? 0x11 : 0x12; TWI_Xmit[1] = var2; twi_master_trans(&twi0_master, 0x28, TWI_Xmit, 2, TWI_Recv, 0); break; case 'U': // Key Up - 'U' ADSR_State = 3; break; case 'V': // VCA level - debug inchars = hexToCharVal(inchars, 2, &var1); VCA_CV = var1; break; case 'W': // Waveform Select - 'W'x inchars = decToCharVal(inchars, 1, &var1); if (var1 < MAX_WAVE) { #ifdef NOTE_GEN setWaveType(var1); #endif } break; default: // note processing, first back up to recover the pitch variable inchars--; inchars = hexToCharVal(inchars, 1, &var1); inchars = decToCharVal(inchars, 1, &var2); #ifdef NOTE_GEN if (var1 && var1 <= 12) { var1--; if (var2 > 1 && var2 < 9) { var2 -= 2; } setNote(var1, var2); } else { } #endif break; } return inchars; } unsigned char *hexToCharVal(unsigned char *cPtr, unsigned char numBits, unsigned char *val) { unsigned char value; unsigned char c; value = 0; do { c = *cPtr++; value <<= 4; if (isdigit(c)) { value |= c - '0'; } else if (islower(c)) { value |= 10 + (c - 'a'); } else if (isupper(c)) { value |= 10 + (c - 'A'); } } while (--numBits); *val = value; return cPtr; } unsigned char *decToCharVal(unsigned char *cPtr, unsigned char numBits, unsigned char *val) { unsigned char value; unsigned char c; value = 0; do { c = *cPtr++; value *= 10; if (isdigit(c)) { value += c - '0'; } } while (--numBits); *val = value; return cPtr; } void processADSR(void) { switch(ADSR_State) { case 1: if ((VCA_MAX - VCA_CV) < Attack) // Will this increment reach or exceed max level? { VCA_CV = VCA_MAX; // Yes, set level to max ADSR_State = 2; // Advance state to 'DECAY' } else { VCA_CV += Attack; // No, increment level } break; case 2: if ((VCA_CV - Decay) < Sustain) // Will this decrement reach or exceed SUSTAIN level? { VCA_CV = Sustain; // Yes, set level to SUSTAIN level } else { VCA_CV -= Decay; // No, deccrement level } break; case 3: if ((VCA_CV - VCA_MIN) < Release) // Will this decrement reach or exceed VCA_MIN level? { VCA_CV = VCA_MIN; // Yes, set level to SUSTAIN level ADSR_State = 0; // set ADSR state to IDLE } else { VCA_CV -= Release; // No, deccrement level } break; } } void processTremolo(void) { } void processVibrato(void) { }
/************************************************************************* Project : Multi-Voice Synthesizer - Voice Module File : timers_init.c Version : 0.1 Date : 2/19/2023 Author : Gene Breniman Company : Young Embedded Systems Comments: Chip type : ATtiny1614 Program type : Application AVR Core Clock frequency: 20.000000 MHz Memory model : Small Data Stack size : 512 Revisions: 4-7-23 - Initial (Pre-Release) *************************************************************************/ /******************************************************* Timers/Counters initialization created by the CodeWizardAVR V3.48b Automatic Program Generator © Copyright 1998-2022 Pavel Haiduc, HP InfoTech S.R.L. http://www.hpinfotech.ro Project : *******************************************************/ // I/O Registers definitions #include <tiny1614.h> #include "SynthVoice.h" extern unsigned char ADSR_State; extern unsigned char Effects_ready; const unsigned char SineWave[128] = {127,133,139,146,152,158,164,170,176,181,187,192,198,203,208,212,217,221,225,229,233,236,239,242,244,247,249,250,252,253,253,254,254,254, 253,253,252,250,249,247,244,242,239,236,233,229,225,221,217,212,208,203,198,192,187,181,176,170,164,158,152,146,139,133,127, 121,115,108,102,96,90,84,78,73,67,62,56,51,46,42,37,33,29,25,21,18,15,12,10,7,5,4,2,1,1,0,0,0, 1,1,2,4,5,7,10,12,15,18,21,25,29,33,37,42,46,51,56,62,67,73,78,84,90,96,102,108,115,121}; const unsigned char FoldedSineWave[128] = {127,140,153,166,178,189,200,210,219,227,234,239,244,247,249,250,250,249,247,245,242,238,234,230,226,222,219,215,212,210,208,207, 207,207,208,210,212,215,219,222,226,230,234,238,242,245,247,249,250,250,249,247,244,239,234,227,219,210,200,189,178,166,153,140, 127,114,101,88,76,65,54,44,35,27,20,15,10,7,5,4,4,5,7,9,12,16,20,24,28,32,35,39,42,44,46,47, 47,47,46,44,42,39,35,32,28,24,20,16,12,9,7,5,4,4,5,7,10,15,20,27,35,44,54,65,76,88,101,114}; static unsigned long int NoteTable[6][12] = {{5487L, 5813L, 6159L, 6525L, 6913L, 7324L, 7759L, 8221L, 8710L, 9227L, 9776L, 10357L}, {10973L, 11626L, 12317L, 13049L, 13825L, 14647L, 15519L, 16442L, 17419L, 18455L, 19552L, 20715L}, {21947L, 23252L, 24634L, 27777L, 27651L, 29296L, 31037L, 32883L, 34838L, 36910L, 39104L, 41430L}, {43893L, 46504L, 49269L, 52198L, 55302L, 58591L, 62075L, 65766L, 69677L, 73820L, 78210L, 82860L}, {87787L, 93007L, 98538L, 104397L, 110605L, 117181L, 124150L, 131532L, 139353L, 147640L, 156419L, 165719L}, {175574L, 186014L, 197075L, 208794L, 221209L, 234363L, 248299L, 263063L, 278706L, 295279L, 312837L, 331440L}}; unsigned char WaveTable[256]; union PtrInt { unsigned char *ptr; unsigned int idata; unsigned char cdata[2]; } NewWave; union PtrInt WaveData; extern unsigned char VCA_CV; #ifdef NOTE_GEN flash unsigned char *wavePtr; static unsigned long int stepSize; union cintL { unsigned long int Acc; unsigned char LowHigh[4]; } phaseACC; //union cintL step; unsigned char changeFreq; void setNote(unsigned char Note, unsigned char Octave); void setWaveType(unsigned char waveType); //extern unsigned char PhaseAC1; //extern unsigned char PhaseAC2; //extern unsigned char PhaseAC3; //extern unsigned char StepSize1; //extern unsigned char StepSize2; //extern unsigned char StepSize3; //extern unsigned char WavePtrMSB; //extern bit LowOrHighBuf; #endif #ifdef NOISE_GEN //extern unsigned char LFSR1; //extern unsigned char LFSR2; //extern unsigned char LFSR3; //extern unsigned char LFSR4; //extern unsigned char LFSR5; //extern unsigned char TEMP; extern union LFSR32 LFSR; #endif // Disable a Timer/Counter type A void tca_disable(TCA_t *ptca) { // Timer/Counter TCA is disabled ptca->SINGLE.CTRLA = 0<<TCA_SINGLE_ENABLE_bp; // Operate in 16-bit mode ptca->SINGLE.CTRLD = 0<<TCA_SINGLE_SPLITM_bp; // Issue a reset command ptca->SINGLE.CTRLECLR = TCA_SINGLE_CMD_RESET_gc; } // Timer/Counter TCA0 initialization void tca0_init(void) { // unsigned char i; // unsigned char *cPtr; // First disable and reset the Timer/Counter TCA0 // Use 16-bit mode tca_disable(&TCA0); // Clock divider: 1 // Clock frequency: 20000.000 kHz TCA0.SINGLE.CTRLA = TCA_SINGLE_CLKSEL_DIV1_gc; // Operating mode: Normal 16-bit OVF=TOP TCA0.SINGLE.CTRLB = TCA_SINGLE_WGMODE_NORMAL_gc; // Set the Timer Counter register TCA0.SINGLE.CNT = 0x00; // Set the Timer Period register // Specified period: 0.010 ms // Obtained: 0.010 ms, 0.0 % error TCA0.SINGLE.PER = 0xC7; // Set the Event Control register // Event input: No action TCA0.SINGLE.EVCTRL = (0<<TCA_SINGLE_CNTEI_bp); // Set TCA0 interrupts: // Overflow interrupt: On // Compare Channel 0 interrupt: Off // Compare Channel 1 interrupt: Off // Compare Channel 2 interrupt: Off TCA0.SINGLE.INTCTRL = (1<<TCA_SINGLE_OVF_bp) + (0<<TCA_SINGLE_CMP0_bp) + (0<<TCA_SINGLE_CMP1_bp) + (0<<TCA_SINGLE_CMP2_bp); // Initialization finished, enable TCA0 TCA0.SINGLE.CTRLA |= TCA_SINGLE_ENABLE_bm; #ifdef NOTE_GEN phaseACC.Acc = 0; changeFreq = 0; // setNote(0, 0); setWaveType(0); // assign ptr and fix array to 128 byte boundary // NewWave.ptr = WaveTable; // NewWave.idata += 128; // NewWave.cdata[0] &= 0x7F; // WaveData.ptr = NewWave.ptr; // WavePtrMSB = NewWave.cdata[1]; // LowOrHighBuf = (NewWave.cdata[0] & 0x80) ? 1 : 0; // cPtr = NewWave.ptr; // // Fill wavetable array with sinewave // for (i = 0; i < 128; i++) // { // *cPtr++ = SineWave[i]; // } #endif // PORTA.DIR |= PIN4_bm; } // Timer/Counter TCA0 Overflow/Underflow interrupt service routine interrupt [TCA0_OVF_vect] void tca0_ovf_isr(void) { #ifdef NOTE_GEN unsigned char offset; flash unsigned char *ptr; // PORTA.OUT |= PIN2_bm; // Ensure that the Overflow/Underflow interrupt flag is cleared TCA0.SINGLE.INTFLAGS = TCA_SINGLE_OVF_bm; offset = phaseACC.LowHigh[2] & 0x7f; ptr = wavePtr + offset; DAC0.DATA = *ptr; //// WaveData.cdata[0] |= offset; //// DAC0.DATA = *WaveData.ptr; phaseACC.Acc += stepSize; // PORTA.OUT &= ~PIN2_bm; #endif #ifdef NOISE_GEN unsigned char cTemp; // PORTA.OUT |= PIN2_bm; // Ensure that the Overflow/Underflow interrupt flag is cleared TCA0.SINGLE.INTFLAGS = TCA_SINGLE_OVF_bm; // DAC0.DATA = LFSR5; DAC0.DATA = LFSR.lfsr[3]; cTemp = (LFSR.lfsr[3] & 0x40) ? 1 : 0; cTemp += (LFSR.lfsr[3] & 0x4) ? 1 : 0; cTemp &= 1; LFSR.LFSReg <<= 1; LFSR.lfsr[0] += cTemp; //#asm //; ;Generate the feedback from bits 39,35 (NOTE! Hill uses 1,2,3,4 numbering) //; clr R6 ;TEMP = 0 //; sbrc R7,6 ;test b39 //; inc R6 ;ITEMP //; sbrc R7,2 ;test b35 //; inc R6 ;ITEMP2 //; ror R6 ;put bit 0 into carry //; ;Shift everyone over, and the carry into the register. //; ;Bits Hill's bits //; rol R3 ;7-0 8-1 //; rol R2 ;15-8 16-9 //; rol R5 ;23-16 and so on //; rol R4 ;31-17 //; rol R7 ;39-32 40-33 // ;Generate the feedback from bits 39,35 (NOTE! Hill uses 1,2,3,4 numbering) // clr R8 ;TEMP = 0 // sbrc R9,6 ;test b39 // inc R8 ;ITEMP // sbrc R9,2 ;test b35 // inc R8 ;ITEMP2 // ror R8 ;put bit 0 into carry // ;Shift everyone over, and the carry into the register. // ;Bits Hill's bits // rol R5 ;7-0 8-1 // rol R4 ;15-8 16-9 // rol R7 ;23-16 and so on // rol R6 ;31-17 // rol R9 ;39-32 40-33 //#endasm // PORTA.OUT &= ~PIN2_bm; #endif } #ifdef NOTE_GEN void setNote(unsigned char Note, unsigned char Octave) { // phaseACC.Acc = 0; stepSize = NoteTable[Octave][Note]; // step.Acc = NoteTable[Octave][Note]; // StepSize1 = step.LowHigh[0]; // StepSize2 = step.LowHigh[1]; // StepSize3 = step.LowHigh[2]; } #endif #ifdef NOTE_GEN void setWaveType(unsigned char waveType) { switch (waveType) { case 0: wavePtr = SineWave; wavePtr = (flash unsigned char *) 0x3000; break; case 1: wavePtr = FoldedSineWave; wavePtr = (flash unsigned char *) 0x3080; break; case 2: wavePtr = FoldedSineWave; wavePtr = (flash unsigned char *) 0x3100; break; } } #endif // Timer/Counter TCB0 initialization void tcb0_init(void) { // Clock divider: 2 // Clock frequency: 10000.000 kHz // TCB0 runs in standby: Off TCB0.CTRLA=TCB_CLKSEL_CLKDIV2_gc+(0<<TCB_RUNSTDBY_bp); // Operating mode: 8-bit Pulse Width Modulation // WO output: On // WO initial value: 0 TCB0.CTRLB=TCB_CNTMODE_PWM8_gc+(1<<TCB_CCMPEN_bp)+(0<<TCB_CCMPINIT_bp); // WO output: PORTA, Pin 5 PORTMUX.CTRLD&= ~PORTMUX_TCB0_bm; // Note: The configuration for the waveform output signal WO // is set in the ports_init function from ports_init.c // Set the Timer Counter register TCB0.CNT=0x00; // Set the PWM period, specified by the CCMPL register // Specified PWM period for the WO output: 25.6 us // Obtained: 25.6 us, 0.00 % error TCB0.CCMPL=0xFF; // Set the duty cycle: 50.00 %, // specified by the CCMPH register TCB0.CCMPH=0x80; // Set the Event Control register // The capture event input is disabled TCB0.EVCTRL=(0<<TCB_CAPTEI_bp); // TCB0 capture interrupt: On // The interrupt is triggered when the counter // reaches the value of the CCMPL register TCB0.INTCTRL=(1<<TCB_CAPT_bp); // Clear the interrupt flags TCB0.INTFLAGS=TCB0.INTFLAGS; // Initialization finished, enable TCB0.CTRLA|=TCB_ENABLE_bm; } // Timer/Counter TCB0 interrupt service routine // The interrupt is triggered when the counter // reaches the value of the CCMPL register interrupt [TCB0_INT_vect] void tcb0_isr(void) { // Clear the interrupt flags TCB0.INTFLAGS=TCB0.INTFLAGS; // Write your code here TCB0.CCMPH = VCA_CV; } // Timer/Counter TCB1 initialization void tcb1_init(void) { // Clock divider: 1 // Clock frequency: 20000.000 kHz (10000.000) // TCB1 runs in standby: Off // TCB1.CTRLA=TCB_CLKSEL_CLKDIV1_gc+(0<<TCB_RUNSTDBY_bp); TCB1.CTRLA=TCB_CLKSEL_CLKDIV2_gc+(0<<TCB_RUNSTDBY_bp); // Operating mode: Periodic Interrupt TCB1.CTRLB=TCB_CNTMODE_INT_gc; // Set the Timer Counter register TCB1.CNT=0x00; // Set the timer period, specified by the CCMP register // Specified period: 1 ms (5 ms) // Obtained: 1 ms, 0.00 % error // TCB1.CCMPL=0x1F; // TCB1.CCMPH=0x4E; TCB1.CCMPL=0x4F; TCB1.CCMPH=0xC3; // Set the Event Control register // The capture event input is disabled TCB1.EVCTRL=(0<<TCB_CAPTEI_bp); // TCB1 capture interrupt: On // The interrupt is triggered when the counter // reaches the value of the CCMP register TCB1.INTCTRL=(1<<TCB_CAPT_bp); // Clear the interrupt flags TCB1.INTFLAGS=TCB1.INTFLAGS; // Initialization finished, enable TCB1 TCB1.CTRLA|=TCB_ENABLE_bm; Effects_ready = 0; ADSR_State = 0; } // Timer/Counter TCB1 interrupt service routine // The interrupt is triggered when the counter // reaches the value of the CCMP register interrupt [TCB1_INT_vect] void tcb1_isr(void) { // Clear the interrupt flags TCB1.INTFLAGS=TCB1.INTFLAGS; Effects_ready = 1; }