In my prior blog ( Multi-Voice Synthesizer - Part 5 - Let's Make Some Noise. ) I covered the work on my Noise source for my Multi-Voice Synthesizer project. Today, I am putting the pieces together and playing some samples.
The PCBs all arrived last week and I have been busy building and testing boards. The good news is that the latest PCBs were great. There were no functional issues at all. I have been working to get some firmware written to allow me to exercise all off the functions on the Voice Modules and new Main Board with Echo processor included. The only bad news is that I am running out of parts and time. With all the rework I did on the earlier PCBs, I burnt through stock on a lot of the components, but mainly the Op-amps. I only had enough stock on hand to build four of the Voice Modules. The running out of time part is that I only have a couple of days before I fly off to go visit with my Mom (who recently had a stroke, but seems to be recovering nicely). I will attempt to complete a couple of more items off my "to do" list and write another blog, it time remains. Otherwise, this will have to stand as my final blog.
New Boards
As I mentioned above, the new PCBs worked out great. I was successful in removing all of the mistakes that I made in the initial design, and everything worked out fine. Although I had added the external crystal oscillator to the PCB design, I did not have any of these parts on hand, so I built them without the oscillator. Here are some images of the new Voice Module boards.
And here are some images of the new Main Board (with onboard echo):
The wires on the top side of the board are two (red) flying leads, which are not rework, but to attach scope probes (pre and post Echo signals). The wires plugged into the 2x12 headers are a ground connection for the scope probes (green) and a daisy chain jumper (orange) to route the communications path back to the receive input of the UART (in place of the last two modules).
Here are a couple of images of the completed stack (Main Board and 4 Voice Modules):
Communications
All of the Voice Modules are connected in a daisy chain format, the TX output of the Main Boards UART is connected to the first module and its output is passed to the next module and so on, until the last modules output is routed back to the RX input of the UART. A custom communication protocol has been created to allow commands to be sent to specific boards. All commands are sent as ASCII characters, with all of the command characters limited to the alphanumeric characters (0-9 and A-Z). Four other characters are used as header, global message header, separator and end of message characters.
A sample message string would look something like this: "|U|U|U|V00|V00|V00/". The '|' character acts as a header and a command separator, while the '/' acts as an end-of-message indicator. Each Voice module in the chain reads commands between the first and second '|' characters, then sends out the remainder of the message (including the second '|' character) out, while waiting for the final '/' character (which is also sent out). If there is a request for data, the first Voice Module sends data out, with a reply header '\', its data and a '/' end-of message indicator. The next module, echos all of the incoming reply message and inserts its reply by replacing the '/' with a '\' and appending its reply, ending with a '\'.
A complete request and reply sequence (for 4 of the 6 Voice Modules) looks like this "|0|1|2|3|4|5/" (outbound) and "|4|5/\V\V\V\V/". Since there was no Voice Modules in the last two slots the final two fields on the outbound message are receive at the Main Module UART, along with the replies from the first four Voice Modules. This communications sequence is used to allow each Voice Module to have its address set and to reply back with their firmware type ('V' for standard Voice Module and 'N' for Noise Module). In normal processing the Main Board will send various setup or play messages to each module (the above message "|U|U|U|V00|V00|V00/" is the Key release, or Up message, plus a mute message to any unused channels - V00 is set volume to zero).
The final message type is is a global message, which uses the '!' character to imply the all modules will process this message, and echo all characters through to the remaining modules. An example of this message is "!W0/" which tells all of the Voice Modules to use waveform 0 or the Sinewave.
Attack, Decay, Sustain and Release (ADSR) functions
The Attack, Decay, Sustain and Release functions of the Voice Module are used to shape the Wave Generator (digital lookup oscillator) output to add character to the notes. The ADSR function is updated every 5 mS, to move through a state machine to process each of the segments of the key cycle by driving the PWM output used to control the Voltage Controlled Amplifier (VCA). When a 'Key Down' message is received "Dxx" the Wave Generator is reprogrammed using the 'xx' part of the command (Note and Octive respectively) and the ADSR sets the control voltage to the VCA_MIN level (25 or roughly 0.5 volts). Then on each ADSR update, the VCA level will be increased by the ATTACK value (between 1 and 255), until the VCA level reaches 255. Then the state machine is switched into DECAY mode. In DECAY mode, on each ADSR update, the VCA level is decreased by the DECAY value (between 1 and 255), until the VCA level reaches the SUSTAIN level. When the SUSTAIN level is reached, the state machine waits for a 'Key Up' message to switch states to the RELEASE mode. In the RELEASE mode, on each ADSR update, the VCA level is decreased by the RELEASE value (between 1 and 255), until the VCA level is at the VCA_MIN level and the state machine is switched to idle.
Here is a picture of a typical ADSR cycle:
And here is a scope capture of a single note being played:
In this image, the ATTACK setting is pretty low, slowly climbing to full volume, the DECAY setting is a bit higher and the SUSTAIN level is at ~2/3 volume. The RELEASE setting is a roughly equal to the DECAY rate, tampering off quickly towards minimum volume. (This a forth octive, 'A' note, which was measured at 439 Hz, just off a bit from the expected 440 Hz)
Here is the code that I using to process the ADSR functions:
In the main idle loop (Effects_ready is set in a timer interrupt at a 5 mS rate):
if (Effects_ready) { processADSR(); processTremolo(); processVibrato(); Effects_ready = 0; }
The command processor detects the 'Key Down' and 'Key Up' actions and sets the ADSR_state to '1' or '3', respectively. Here is the processADSR() function:
void processADSR(void) { switch(ADSR_State) { case 1: if ((0xff - VCA_CV) < Attack) // Will this increment reach or exceed max level? { VCA_CV = 0xFF; // 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; } }
Here is a video, letting you hear the differences in sound based on ADSR settings:
Filtering
Each Voice Module has an onboard filter to help shape the output waveform. There are Low Pass, High Pass, and Band Pass filters with a selector to choose the desired filtering or the raw waveform. Here are some samples of the filtering options, shown with a harmonic rich waveform (in this case folded Sine). First is the RAW output of the waveform generator passed through the ADSR/VCA:
The gold trace is the raw waveform and the blue is the envelope shaped output.
This time a low pass filter is selected and is set for slight filtering (about 75% setting):
Here the 'fold' is mostly removed from the folded sinewave, causing the waveform to look more like a squarewave.
This time a low pass filter is selected and is set for more aggressive filtering (about 50% setting):
Now the fold is completely gone and the input sinewave is beginning to be attenuated by the filter (the filter cutoff is now near the fundamental the the waveform or ~440 Hz) . The waveform during the ATTACK phase looks almost triangular due to the constantly changing gain adjustments of the ADSR / VCA functions.
This time a low pass filter is selected and is set for more aggressive filtering (about 1% setting):
Now the fold is completely gone and the input sinewave is quite attenuated by the filter (the filter cutoff is now lower the fundamental the the waveform < 440 Hz) .
Now we will use the same waveform and apply the High Pass filtering to the waveform. The filter cut off is set to maximum.
Here the High Pass filter has virtually suppress all of the fundamental of the waveform, leaving only the 'fold'. I feel that I will need to change the capacitor in the high pass filter a bit so as to not so severally filter the signal. High Pass filter currently has a high frequency cutoff of ~312 Hz. I am thinking of moving that closer to 3120 Hz (1 nF feedback instead of the current 10 nF). This is something to add to my 'to do' list.
The filtering does add a lot of room to modify the output waveform, generating interesting effects in the sound of the synthesizer.
Here is a video, letting you hear the differences in sound based on filtering:
Echo Processor
The Echo Processor (PT2399, located on the Main Board), uses a ADC, RAM-based delay and a DAC to produce a audio delay line. Using a mixer and feeding the delayed signal back in to the input (along with the incoming audio) creates an echo effect. Here is a 'heavy' echo effect showed with the incoming waveform. The oscillations created are a result of the feedback signal mixing with the input, creating a beat frequency as the two signals go in and out of phase with each other.
By decreasing the sampling delay and the mix of the input and output we can change the beat frequency and the amplitude of the beat.
Here is a video, letting you hear the differences in sound based on echo processing:
Chords and Scales
To demonstrate the sounds of the Multi-Voice Synthesizer I updated my control panel to allow me to control multiple voices and to simulate the 'Key Down' and 'Key Up' sequencing. Not know any music theory, I resorted to searching the web for chords and scales. Here is my update control panel:
The Key combobox allows the selection of some of the minor and major keys for a piano. You can select either quarter, half or whole notes and you can play a chord ( "Play Chord" button - three notes) or the scales ("Play Scale" button) based on the selected key. The Play all Chords button plays each of the list chords, one at a time going down the list and then one at a time going back up the list.
Here is a video showing the multiple voices playing:
Noise Source
I had posted this video before, but I am not sure that I had done it correctly (or timely as I edited the entry several times). This is a short demo of my Noise Source implementation of my Voice Module. I still need to try this out on my latest board to see how well it works with the ADSR and Filtering.
To Do List
It is unclear if I will be able to complete much if any of the list prior to the contests deadline, but here are some of the things that I need to do to complete this project:
- I want to look at adding tremolo and vibrato effects into the Voice Module (varying amplitude and varying pitch via software control of the attenuator and the oscillator).
- Fully integrate the Noise Source into the Synthesizer (utilize the ADSR / VCA and Filtering) to add percussive Noise.
- Port / Finish the MIDI functions, utilizing the Main Board EEPROM, to play music.
- Design and 3D Print an enclosure or frame to wrap the Voice Modules, Main Board and Speaker.
- In the future I would like to re-visit the board designs to clean up some noise issues. There seems to be some bleed through of some high frequency noise in to the audio path. While this noise does give the synthesizer some edginess, it can be a little grating at times. My initial thought is that the PWM signal driving the VCA could use some additional or more aggressive filtering.
Project Summary
In Multi-Voice Synthesizer - Initial Design I shared the project background, initial designs and some of my early testing on the Voice Module.
In Multi-Voice Synthesizer - Part 2 - Voice Module rework. I shared my initial design failures (several) and my breadboarding of potential fixes, along with my new design for the Voice Module.
In Multi-Voice Synthesizer - Part 3 - Main Board rework and Echo Module bring-up. I shared more design failures related to the Main Board, efforts to correct those issues and the bringing up of the Echo Processor board.
In Multi-Voice Synthesizer - Part 4 - New schematics and layouts. I shared the updated schematics and layouts for the Voice Module and the Main Board with Echo Processor. These are the final updates on the these designs.
In Multi-Voice Synthesizer - Part 5 - Let's Make Some Noise. I described the Noise Source firmware for the Voice Module, allowing me to generate Noise for the Multi-Voice Synthesizer.
In this blog I have tied all the pieces together and demonstrated the working of the Synthesizer and added the zip files containing the sources for the Voice Module and Main Board firmware.
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.
Note: 4/11/2023 - I have modified and reloaded the images (reduced size). Please let me know if this has successfully side-step the image problem.
Below are attachments containing source code for the Voice Modules and the Main Board.
The Zip file attachment of the source files failed, so here are a series of source code files, first for the Voice Module:
Source Files:
/************************************************************************* 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> // 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]; 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; #ifdef NOTE_GEN unsigned char moduleType = 'V'; #endif #ifdef NOISE_GEN unsigned char moduleType = 'N'; #endif #ifdef NOTE_GEN unsigned char VCA_CV; #define VCA_MIN 25 //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 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 *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; #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 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); if (var1 && var1 <= 12) { var1--; if (var2 > 1 && var2 < 9) { var2 -= 2; } #ifdef NOTE_GEN ADSR_State = 1; VCA_CV = VCA_MIN; setNote(var1, var2); #endif } 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); if (var1 && var1 <= 12) { var1--; if (var2 > 1 && var2 < 9) { var2 -= 2; } #ifdef NOTE_GEN setNote(var1, var2); #endif } else { } 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 ((0xff - VCA_CV) < Attack) // Will this increment reach or exceed max level? { VCA_CV = 0xFF; // 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 : clock_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) *************************************************************************/ /******************************************************* System clock 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> // Standard definitions #include <stddef.h> void system_clocks_init(void) { unsigned char n; unsigned char s; // Optimize for speed #pragma optsize- // Save interrupts enabled/disabled state s = CPU_SREG; // Disable interrupts #asm("cli") // The 20 MHz internal oscillator is selected by the OSCCFG.FREQSEL fuse bits=0x02 // Main clock source: 20 MHz Internal RC Oscillator // Peripheral clock output on CLKOUT (PORTB, Pin 5): Off n = CLKCTRL_CLKSEL_OSC20M_gc | (0<<CLKCTRL_CLKOUT_bp); CPU_CCP = CCP_IOREG_gc; CLKCTRL.MCLKCTRLA = n; // Main clock prescaler division ratio: 1 // CPU and Peripheral clock: 20000.000 kHz n = 0; CPU_CCP = CCP_IOREG_gc; CLKCTRL.MCLKCTRLB = n; // Restore interrupts enabled/disabled state CPU_SREG = s; // Restore optimization for size if needed #pragma optsize_default }
/************************************************************************* Project : Multi-Voice Synthesizer - Voice Module File : dac_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) *************************************************************************/ /******************************************************* DAC 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> // DAC0 initialization void dac0_init(void) { // Select the voltage reference used by DAC0: 4.340 V, DAC0 Voltage reference always enabled: On VREF.CTRLA = (VREF.CTRLA & ~VREF_DAC0REFSEL_gm) | VREF_DAC0REFSEL_4V34_gc; VREF.CTRLB |= VREF_DAC0REFEN_bm; // DAC0 output on PORTA, Pin 6: On, Note: PORTA, Pin 6 logic output driver is disabled in the ports_init function. // Allow DAC0 to operate in standby mode: Off, Enable DAC0 DAC0.CTRLA = (1<<DAC_OUTEN_bp) + (0<<DAC_RUNSTDBY_bp) + (1<<DAC_ENABLE_bp); DAC0.DATA = 128; }
/************************************************************************* Project : Multi-Voice Synthesizer - Voice Module File : ports_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) *************************************************************************/ /******************************************************* I/O Ports 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> // Ports initialization void ports_init(void) { // PORTA initialization, OUT register PORTA.OUT = 0x06; // Pin0: Input, Pin1: Output, Pin2: Output, Pin3: Output, Pin4: Output, Pin5: Output, Pin6: Input, Pin7: Output PORTA.DIR = 0xBE; // Pin0 Inverted: Off, Pullup on input: Off, Input/Sense configuration: Int. disabled, Inp. buffer enabled PORTA.PIN0CTRL = (0<<PORT_INVEN_bp) | (0<<PORT_PULLUPEN_bp) | PORT_ISC_INTDISABLE_gc; // Pin1 Inverted: Off, Pullup on input: Off, Pin1 Input/Sense configuration: Int. disabled, Inp. buffer enabled PORTA.PIN1CTRL = (0<<PORT_INVEN_bp) | (0<<PORT_PULLUPEN_bp) | PORT_ISC_INTDISABLE_gc; // Pin2 Inverted: Off, Pullup on input: Off, Input/Sense configuration: Int. disabled, Inp. buffer enabled PORTA.PIN2CTRL = (0<<PORT_INVEN_bp) | (0<<PORT_PULLUPEN_bp) | PORT_ISC_INTDISABLE_gc; // Pin3 Inverted: Off, Pullup on input: Off, Input/Sense configuration: Int. disabled, Inp. buffer enabled PORTA.PIN3CTRL = (0<<PORT_INVEN_bp) | (0<<PORT_PULLUPEN_bp) | PORT_ISC_INTDISABLE_gc; // Pin4 Inverted: Off, Pullup on input: Off, Input/Sense configuration: Int. disabled, Inp. buffer enabled PORTA.PIN4CTRL = (0<<PORT_INVEN_bp) | (0<<PORT_PULLUPEN_bp) | PORT_ISC_INTDISABLE_gc; // Pin5 Inverted: Off, Pullup on input: Off,5 Input/Sense configuration: Int. disabled, Inp. buffer enabled PORTA.PIN5CTRL = (0<<PORT_INVEN_bp) | (0<<PORT_PULLUPEN_bp) | PORT_ISC_INTDISABLE_gc; // Pin6 Inverted: Off, Pullup on input: Off, Input/Sense configuration: Int. disabled, Inp. buffer disabled PORTA.PIN6CTRL = (0<<PORT_INVEN_bp) | (0<<PORT_PULLUPEN_bp) | PORT_ISC_INPUT_DISABLE_gc; // Pin7 Inverted: Off, Pullup on input: Off, Input/Sense configuration: Int. disabled, Inp. buffer enabled PORTA.PIN7CTRL = (0<<PORT_INVEN_bp) | (0<<PORT_PULLUPEN_bp) | PORT_ISC_INTDISABLE_gc; // PORTB initialization, OUT register PORTB.OUT = 0x04; // Pin0: Input, Pin1: Input, Pin2: Output, Pin3: Input PORTB.DIR = 0x04; // Pin0 Inverted: Off,Pullup on input: Off, Input/Sense configuration: Int. disabled, Inp. buffer enabled PORTB.PIN0CTRL = (0<<PORT_INVEN_bp) | (0<<PORT_PULLUPEN_bp) | PORT_ISC_INTDISABLE_gc; // Pin1 Inverted: Off, Pullup on input: Off, Input/Sense configuration: Int. disabled, Inp. buffer enabled PORTB.PIN1CTRL = (0<<PORT_INVEN_bp) | (0<<PORT_PULLUPEN_bp) | PORT_ISC_INTDISABLE_gc; // Pin2 Inverted: Off, Pullup on input: Off, Input/Sense configuration: Int. disabled, Inp. buffer enabled PORTB.PIN2CTRL = (0<<PORT_INVEN_bp) | (0<<PORT_PULLUPEN_bp) | PORT_ISC_INTDISABLE_gc; // Pin3 Inverted: Off, Pullup on input: Off, Input/Sense configuration: Int. disabled, Inp. buffer enabled PORTB.PIN3CTRL = (0<<PORT_INVEN_bp) | (0<<PORT_PULLUPEN_bp) | PORT_ISC_INTDISABLE_gc; }
/************************************************************************* 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> 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; #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 VCA_CV; //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; #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 // PORTA.OUT |= PIN2_bm; // Ensure that the Overflow/Underflow interrupt flag is cleared TCA0.SINGLE.INTFLAGS = TCA_SINGLE_OVF_bm; DAC0.DATA = LFSR5; #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 #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 #ifdef NOTE_GEN TCB0.CCMPH = VCA_CV; #endif } // 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; }
/************************************************************************* Project : Multi-Voice Synthesizer - Voice Module File : twi_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) *************************************************************************/ /******************************************************* TWI 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> // TWI initialization functions #include "twi_init.h" // TWI0 initialization // Structure that holds information used by the TWI0 Master // for performing a TWI bus transaction TWI_MASTER_INFO_t twi0_master; void twi0_init(void) { // General TWI0 initialization // SDA Setup time: 4 Clock Cycles, SDA Hold: Off, Fast Mode+ : Off twi_init(&TWI0, TWI_SDASETUP_4CYC_gc, TWI_SDAHOLD_OFF_gc, false); // The TWI0 signals are not remapped: Master: SDA: PORTB.1, SCL: PORTB.0 PORTMUX.CTRLB &= PORTMUX_TWI0_DEFAULT_gc; // TWI0 Master initialization // Peripheral Clock frequency: 20000000 Hz, SCL Rate: 100000 bps (Real Rate: 99751 bps, Error: 0.2 %), SCL Rise time: 25 ns, Inactive bus timeout: Disabled (I2C) twi_master_init(&twi0_master, &TWI0, TWI_BAUD_REG(20000000, 200000, 1), TWI_TIMEOUT_DISABLED_gc); // TWI0 Slave is disabled TWI0.SCTRLA = 0; } // TWI0 Master interrupt service routine #pragma optsize- // optimize for speed interrupt [TWI0_TWIM_vect] void twi0_master_isr(void) { twi_master_int_handler(&twi0_master); } #pragma optsize_default
/************************************************************************* Project : Multi-Voice Synthesizer - Voice Module File : usarts_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) *************************************************************************/ // I/O Registers definitions #include <tiny1614.h> // USARTs initialization functions #include "usarts_init.h" // Returns the corrected value of the BAUD register based on the calibration // values from SIGROW when using the 20 MHz Internal RC Oscillator // The chip is powered at 5.0 V unsigned int calibrate_baud(unsigned int baud_reg) { return (unsigned int) (((unsigned long) baud_reg*(1024+(signed char) SIGROW.OSC20ERR5V))>>10); } // USART0 initialization void usart0_init(void) { // Note: The correct PORTB direction for the USART signals // is configured in the ports_init function. PORTMUX.CTRLB &= ~PORTMUX_USART0_ALTERNATE_gc; // The USART0 signals are not remapped: RxD: PORTB.3, TxD: PORTB.2 PORTB.OUTSET = 0x04; // Transmitter is enabled Set TxD=1 USART0.CTRLC = USART_CMODE_ASYNCHRONOUS_gc | USART_PMODE_DISABLED_gc | USART_SBMODE_1BIT_gc | USART_CHSIZE_8BIT_gc; // USART0 Mode: Async,Data bits=8,Stop bits=1,No Parity USART0.CTRLA = (1<<USART_RXCIE_bp) | USART_RS485_OFF_gc; // Receive complete interrupt: On, RS485 Mode: RS485 not used. Note: The transmitter Data Register Empty interrupt will be enabled later USART0.BAUD = calibrate_baud(0x411B);// Required Baud rate: 9600, Real Baud Rate: 9599.8 (x2 Mode), Error: 0.0 %, correct the BAUD register based on the calibration values from SIGROW when using the 20 MHz Internal RC Oscillator USART0.CTRLB = (1<<USART_RXEN_bp) | (1<<USART_TXEN_bp) | (0<<USART_ODME_bp) | USART_RXMODE_CLK2X_gc | (0<<USART_MPCM_bp);// Receiver: On, Transmitter: On,TxD open drain: Off,Double transmission speed mode: On,ulti-processor communication mode: Off } // Note: RX_BUFFER_SIZE_USART0 is #define-d in 'usarts_init.h' with the value 32 char rx_buffer_usart0[RX_BUFFER_SIZE_USART0]; volatile unsigned char rx_wr_index_usart0 = 0; volatile unsigned char rx_rd_index_usart0 = 0; volatile unsigned char rx_counter_usart0 = 0; bit rx_buffer_overflow_usart0 = 0; // This flag is set on USART0 Receiver buffer overflow // USART0 Receiver interrupt service routine interrupt [USART0_RXC_vect] void usart0_rx_isr(void) { unsigned char status; char data; status = USART0.RXDATAH; data = USART0.RXDATAL; if ((status & (USART_FERR_bm | USART_PERR_bm | USART_BUFOVF_bm)) == 0) { rx_buffer_usart0[rx_wr_index_usart0++] = data; if (rx_wr_index_usart0 == RX_BUFFER_SIZE_USART0) { rx_wr_index_usart0 = 0; } if (++rx_counter_usart0 == RX_BUFFER_SIZE_USART0) { rx_counter_usart0 = 0; rx_buffer_overflow_usart0 = 1; } } } // Receive a character from USART0 #pragma used+ char getchar_usart0(void) { char data; if (rx_counter_usart0 == 0) { return 0; } data = rx_buffer_usart0[rx_rd_index_usart0++]; if (rx_rd_index_usart0 == RX_BUFFER_SIZE_USART0) { rx_rd_index_usart0 = 0; } #asm("cli") --rx_counter_usart0; #asm("sei") return data; } #pragma used- unsigned char GetSerial0InputCount(void) { return rx_counter_usart0; } char tx_buffer_usart0[TX_BUFFER_SIZE_USART0]; // Note: TX_BUFFER_SIZE_USART0 is #define-d in 'usarts_init.h' with the value 32 volatile unsigned char tx_wr_index_usart0 =0 ; volatile unsigned char tx_rd_index_usart0 = 0; volatile unsigned char tx_counter_usart0 = 0; // USART0 Transmitter interrupt service routine interrupt [USART0_DRE_vect] void usart0_tx_isr(void) { if (tx_counter_usart0) { --tx_counter_usart0; USART0.TXDATAL = tx_buffer_usart0[tx_rd_index_usart0++]; if (tx_rd_index_usart0 == TX_BUFFER_SIZE_USART0) { tx_rd_index_usart0 = 0; } } else { USART0.CTRLA&= ~USART_DREIE_bm; } } // Write a character to the USART0 Transmitter buffer #pragma used+ void putchar_usart0(char c) { if (tx_counter_usart0 == TX_BUFFER_SIZE_USART0) { return; } #asm("cli") if (tx_counter_usart0 || ((USART0.STATUS & USART_DREIF_bm) == 0)) { tx_buffer_usart0[tx_wr_index_usart0++] = c; if (tx_wr_index_usart0 == TX_BUFFER_SIZE_USART0) { tx_wr_index_usart0 = 0; } ++tx_counter_usart0; } else { USART0.CTRLA |= USART_DREIE_bm; USART0.TXDATAL = c; } #asm("sei") } #pragma used- unsigned char GetSerial0BufferCount(void) { return (TX_BUFFER_SIZE_USART0 - tx_counter_usart0); }
Include Files:
/************************************************************************* Project : Multi-Voice Synthesizer - Voice Module File : clock_init.h 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) *************************************************************************/ /******************************************************* System clock 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 : *******************************************************/ #ifndef _CLOCK_INIT_INCLUDED_ #define _CLOCK_INIT_INCLUDED_ void system_clocks_init(void); #endif /************************************************************************* Project : Multi-Voice Synthesizer - Voice Module File : dac_init.h 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) *************************************************************************/ /******************************************************* DAC 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 : *******************************************************/ #ifndef _DAC_INIT_INCLUDED_ #define _DAC_INIT_INCLUDED_ // DAC0 initialization void dac0_init(void); // Output data to DAC0 #define dac0_out(data) {DAC0.DATA = (data);} #endif /************************************************************************* Project : Multi-Voice Synthesizer - Voice Module File : ports_init.h 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 Ports 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 : *******************************************************/ #ifndef _PORTS_INIT_INCLUDED_ #define _PORTS_INIT_INCLUDED_ // Ports initialization void ports_init(void); // Port A defines #define MUX_MASK 0x06 #define MUX_EN (CLRBIT(PORTA.OUT,7)) #define MUX_DIS (SETBIT(PORTA.OUT,7)) #endif /************************************************************************* Project : Multi-Voice Synthesizer - Voice Module File : timers_init.h 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 : *******************************************************/ #ifndef _TIMERS_INIT_INCLUDED_ #define _TIMERS_INIT_INCLUDED_ #define MAX_WAVE 3 // Disable a Timer/Counter type A void tca_disable(TCA_t *ptca); // Timer/Counter TCA0 initialization void tca0_init(void); void tcb0_init(void); void tcb1_init(void); #ifdef NOTE_GEN void setNote(unsigned char Note, unsigned char Octave); void setWaveType(unsigned char waveType); #endif #endif /************************************************************************* Project : Multi-Voice Synthesizer - Voice Module File : twi_init.h 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) *************************************************************************/ /****************************************************************************** TWI driver library for the CodeVisionAVR C V3 Compiler Copyright (C) 2010-2022 Pavel Haiduc, HP InfoTech S.R.L., All rights reserved. *******************************************************************************/ #ifndef _TWI_INCLUDED_ #define _TWI_INCLUDED_ #include <stdbool.h> #ifdef _AVR8X_DEVICE_ // TWI functions for AVR8X devices (ATtiny416/417/814/816/817, ATmega3208/3209/4808/4809, etc.) #include <string.h> #include <io.h> // TWI module general initialization // module - pointer to the TWI peripheral module to be initialized // sda_setup - specify the SDA setup time of 4 or 8 cycles while // reading from the slave // sda_hold - specify the internal hold time on SDA with respect to the // SCL negative edge // fast_mode_plus - enable using the Fast Mode Plus 1MHz bus speed void twi_init(TWI_t *module,TWI_SDASETUP_t sda_setup,TWI_SDAHOLD_t sda_hold, bool fast_mode_plus); // TWI master definitions & functions // structure that holds information used by the TWI master // for performing a TWI bus transaction typedef struct { TWI_t *module; // ptr. to the used TWI interface module unsigned char slave_address; // I2C slave address unsigned char *tx_buffer; // ptr. to transmit buffer unsigned char bytes_to_tx; // number of bytes to transmit to the slave unsigned char tx_counter; // number of transmitted bytes unsigned char *rx_buffer; // ptr. to receive buffer unsigned char bytes_to_rx; // number of bytes to receive from the slave unsigned char rx_counter; // number of received bytes unsigned char result; // transaction result } TWI_MASTER_INFO_t; // TWI master transaction result values #define TWIM_RES_UNKNOWN 0 #define TWIM_RES_OK 1 #define TWIM_RES_BUFFER_OVERFLOW 2 #define TWIM_RES_ARBITRATION_LOST 3 #define TWIM_RES_BUS_ERROR 4 #define TWIM_RES_NACK_RECEIVED 5 #define TWIM_RES_FAIL 6 // macro used for calculating the value of the BAUD register for the desired // SCL clock bit rate SCL_RATE [bps] based on the Peripheral Clock PER_CLK [Hz] and // SCL signal rise time SCL_RISE_TIME [ns] #define TWI_BAUD_REG(PER_CLK,SCL_RATE,SCL_RISE_TIME) ((int)((PER_CLK)*(1e9/(SCL_RATE)-(SCL_RISE_TIME))/2e9-4.5)) // TWI master initialization // twi - pointer to the structure that holds information used by the TWI master // for performing a TWI bus transaction // module - pointer to the TWI peripheral module to be initialized as TWI master // baud_reg - initialization value for the TWI master BAUD register // timeout - time period after which the bus state logic will enter the Idle state: // TWI_TIMEOUT_DISABLED_gc - bus timeout disabled // TWI_TIMEOUT_50US_gc - 50us @ SCL=100kbps // TWI_TIMEOUT_100US_gc - 100us @ SCL=100kbps // TWI_TIMEOUT_200US_gc - 200us @ SCL=100kbps void twi_master_init( TWI_MASTER_INFO_t *twi, TWI_t *module, unsigned char baud_reg, TWI_TIMEOUT_t timeout); // TWI master interrupt handler // twi - pointer to the structure that holds information used by the TWI master void twi_master_int_handler(TWI_MASTER_INFO_t *twi); // function used for performing a TWI master transaction // twi - pointer to the structure that holds information used by the TWI master // slave_addr - 7 bit address of the TWI slave with which the transaction must be performed // tx_data - pointer to the buffer that holds the data to be transmitted to the slave // tx_count - number of bytes that must be transmitted to the slave during the transaction // rx_data - pointer to the buffer that holds the data received from the slave // rx_count - number of bytes that must be received from the slave during the transaction // returns true on success bool twi_master_trans( TWI_MASTER_INFO_t *twi, unsigned char slave_addr, unsigned char *tx_data, unsigned char tx_count, unsigned char *rx_data, unsigned char rx_count); // TWI slave definitions & functions // structure that holds information used by the TWI slave // for performing a TWI bus transaction typedef struct { TWI_t *module; // ptr. to the used TWI interface module unsigned char *rx_buffer; // ptr. to receive buffer unsigned char rx_buffer_size; // receive buffer size unsigned char rx_index; // index in the receive buffer of the last received byte unsigned char *tx_buffer; // ptr. to transmit buffer unsigned char tx_index; // index in the transmit buffer of the last transmitted byte unsigned char bytes_to_tx; // number of bytes to transmit to the master void (*twi_trans) (void); // pointer to TWI slave transaction processing function unsigned result; // transaction result } TWI_SLAVE_INFO_t; // TWI slave transaction result values #define TWIS_RES_UNKNOWN 0 #define TWIS_RES_OK 1 #define TWIS_RES_ADDR_MATCH 2 #define TWIS_RES_BUFFER_OVERFLOW 3 #define TWIS_RES_TRANSMIT_COLLISION 4 #define TWIS_RES_BUS_ERROR 5 #define TWIS_RES_FAIL 6 #define TWIS_RES_HALT 7 // TWI slave initialization // twi - pointer to the structure that holds information used by the TWI slave // for performing a TWI bus transaction // module - pointer to the TWI peripheral module to be initialized as TWI slave // match_any_addr - if true, the slave match address logic responds to all received addresses // addr - 7 bit address of the TWI slave // addr_mask_reg - value for the SADDRMASK register: // if bit 0=0 -> bits 1..7 = slave address bit mask // if bit 0=1 -> bits 1..7 = second slave address // rx_buffer - pointer to the slave receive buffer // rx_buffer_size - size of the slave receive buffer // tx_buffer - pointer to the slave transmit buffer // twi_slave_trans - pointer to the TWI slave transaction processing function void twi_slave_init( TWI_SLAVE_INFO_t *twi, TWI_t *module, bool match_any_addr, unsigned char addr, unsigned char addr_mask_reg, unsigned char *rx_buffer, unsigned char rx_buffer_size, unsigned char *tx_buffer, void (*twi_slave_trans)(void)); // TWI slave interrupt handler // twi - pointer to the structure that holds information used by the TWI slave void twi_slave_int_handler(TWI_SLAVE_INFO_t *twi); // function used to halt a TWI slave transaction // twi - pointer to the structure that holds information used by the TWI slave void twi_slave_halt_trans(TWI_SLAVE_INFO_t *twi); #pragma library twi8x.lib #else // TWI functions for AVR8 devices // TWI transaction result values #define TWI_RES_OK 0 #define TWI_RES_BUFFER_OVERFLOW 1 #define TWI_RES_ARBITRATION_LOST 2 #define TWI_RES_BUS_ERROR 3 #define TWI_RES_NACK_RECEIVED 4 #define TWI_RES_BUS_TIMEOUT 5 #define TWI_RES_FAIL 6 #define TWI_RES_UNKNOWN 7 extern unsigned char twi_tx_index; // data index in the transmit buffer extern unsigned char twi_rx_index; // data index in the receive buffer extern unsigned char twi_result; // holds the result of the last TWI transaction // TWI master initialization // bit_rate - SCL bit rate [kHz] void twi_master_init(unsigned int bit_rate); // function used for performing a TWI master transaction // slave_addr - 7 bit address of the TWI slave with which the transaction must be performed // tx_data - pointer to the buffer that holds the data to be transmitted to the slave // tx_count - number of bytes that must be transmitted to the slave during the transaction // rx_data - pointer to the buffer that holds the data received from the slave // rx_count - number of bytes that must be received from the slave during the transaction // returns true on success bool twi_master_trans( unsigned char slave_addr, unsigned char *tx_data, unsigned char tx_count, unsigned char *rx_data, unsigned char rx_count); // TWI slave initialization // match_any_addr - if true, the slave match address logic responds to all received addresses // addr - 7 bit address of the TWI slave // rx_buffer - pointer to the slave receive buffer // rx_buffer_size - size of the slave receive buffer // tx_buffer - pointer to the slave transmit buffer // slave_rx_handler - pointer to the TWI slave receive processing function // slave_tx_handler - pointer to the TWI slave transmit processing function void twi_slave_init( bool match_any_addr, unsigned char addr, unsigned char *rx_buffer, unsigned char rx_buffer_size, unsigned char *tx_buffer, bool (*slave_rx_handler)(bool rx_complete), unsigned char (*slave_tx_handler)(bool tx_complete) ); // 10062019_1, 04042022_1 #if defined(_CHIP_ATMEGA324PB_) || defined(_CHIP_ATMEGA328PB_) // TWI1 support for ATmega324PB/328PB extern unsigned char twi1_tx_index; // data index in the transmit buffer extern unsigned char twi1_rx_index; // data index in the receive buffer extern unsigned char twi1_result; // holds the result of the last TWI1 transaction // TWI1 master initialization // bit_rate - SCL bit rate [kHz] void twi1_master_init(unsigned int bit_rate); // function used for performing a TWI1 master transaction // slave_addr - 7 bit address of the TWI slave with which the transaction must be performed // tx_data - pointer to the buffer that holds the data to be transmitted to the slave // tx_count - number of bytes that must be transmitted to the slave during the transaction // rx_data - pointer to the buffer that holds the data received from the slave // rx_count - number of bytes that must be received from the slave during the transaction // returns true on success bool twi1_master_trans( unsigned char slave_addr, unsigned char *tx_data, unsigned char tx_count, unsigned char *rx_data, unsigned char rx_count); // TWI1 slave initialization // match_any_addr - if true, the slave match address logic responds to all received addresses // addr - 7 bit address of the TWI slave // rx_buffer - pointer to the slave receive buffer // rx_buffer_size - size of the slave receive buffer // tx_buffer - pointer to the slave transmit buffer // slave_rx_handler - pointer to the TWI slave receive processing function // slave_tx_handler - pointer to the TWI slave transmit processing function void twi1_slave_init( bool match_any_addr, unsigned char addr, nsigned char *rx_buffer, unsigned char rx_buffer_size, unsigned char *tx_buffer, bool (*slave_rx_handler)(bool rx_complete), unsigned char (*slave_tx_handler)(bool tx_complete) ); #endif #pragma library twi.lib #endif #endif /************************************************************************* Project : Multi-Voice Synthesizer - Voice Module File : usarts_init.h 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) *************************************************************************/ /******************************************************* USARTs 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 : *******************************************************/ #ifndef _USARTS_INIT_INCLUDED_ #define _USARTS_INIT_INCLUDED_ // USART0 receive function char getchar_usart0(void); unsigned char GetSerial0InputCount(void); // USART0 transmit function void putchar_usart0(char c); unsigned char GetSerial0BufferCount(void); // USART0 initialization void usart0_init(void); // USART0 Receiver buffer #define RX_BUFFER_SIZE_USART0 32 extern char rx_buffer_usart0[RX_BUFFER_SIZE_USART0]; extern volatile unsigned char rx_wr_index_usart0; extern volatile unsigned char rx_rd_index_usart0; extern volatile unsigned char rx_counter_usart0; // This flag is set on USART0 Receiver buffer overflow extern bit rx_buffer_overflow_usart0; // USART0 Transmitter buffer #define TX_BUFFER_SIZE_USART0 32 extern char tx_buffer_usart0[TX_BUFFER_SIZE_USART0]; extern volatile unsigned char tx_wr_index_usart0; extern volatile unsigned char tx_rd_index_usart0; extern volatile unsigned char tx_counter_usart0; #endif
Main Board Source files:
/************************************************************************* Project : Multi-Voice Synthesizer - Main Board File : SynthMain.c Version : 0.1 Date : 2/19/2023 Author : Gene Breniman Company : Young Embedded Systems Comments: Chip type : ATtiny3226 Program type : Application AVR Core Clock frequency: 20.000000 MHz Memory model : Small Data Stack size : 768 Revisions: 4-7-23 - Initial (Pre-Release) *************************************************************************/ // I/O Registers definitions #include <tiny3226.h> #include <ctype.h> // USART0 is used as the default input // device by the 'getchar' function. #define _ALTERNATE_GETCHAR_ // USART0 is used as the default output // device by the 'putchar' function. #define _ALTERNATE_PUTCHAR_ // Standard Input/Output functions #include <stdio.h> // Clock System initialization function #include "clock_init.h" // I/O Ports initialization function #include "ports_init.h" // USARTs initialization functions #include "usarts_init.h" // TWI initialization functions #include "twi_init.h" // Declare your global variables here unsigned char TWI_Xmit[8]; unsigned char TWI_Recv[8]; void processIncomingCommand(unsigned char *indat, unsigned char size); void main(void) { unsigned char n; unsigned char SerialInputMode; unsigned char c; unsigned char indata[10]; unsigned char i; // 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_init(); ports_init(); usart0_init(); usart1_init(); twi0_init(); SerialInputMode = 0; // Globally enable interrupts #asm("sei") // init gain potentiometer TWI_Xmit[0] = 0x13; TWI_Xmit[1] = 0x33; twi_master_trans(&twi0_master, 0x29, TWI_Xmit, 2, TWI_Recv, 0); while (1) { if (GetSerial0InputCount()) { c = getchar_usart0(); switch (SerialInputMode) { case 0: if (c == 'M') { SerialInputMode = 1; i = 0; } else if (c == 'V') { SerialInputMode = 2; } break; case 1: if (c == '\n') { // process local data, return to idle state processIncomingCommand(indata, i); SerialInputMode = 0; } else { indata[i++] = c; } break; case 2: if (c == '\n') { // pass data on to voice module, return to idle state SerialInputMode = 0; } putchar_usart1(c); break; } } if (GetSerial1InputCount()) { c = getchar_usart1(); putchar_usart0(c); } } } void processIncomingCommand(unsigned char *indat, unsigned char size) { unsigned char value; unsigned char c; size--; switch(*indat++) { case 'G': if (size == 3) { value = 0; size--; while (size) { c = *indat; if (isdigit(c)) { c -= '0'; } else if (isupper(c)) { c = (10 + c - 'A'); } value = (value << 4) + c; size--; } // send data to digital potentiometer (Mixer Gain) TWI_Xmit[0] = 0x12; TWI_Xmit[1] = value; twi_master_trans(&twi0_master, 0x29, TWI_Xmit, 2, TWI_Recv, 0); putchar_usart0('G'); } break; case 'E': if (size == 2) { value = (*indat == '0') ? 0 : 1; PORTC.OUT = value; putchar_usart0('E'); } break; case 'D': if (size == 3) { value = 0; size--; while (size) { c = *indat; if (isdigit(c)) { c -= '0'; } else if (isupper(c)) { c = (10 + c - 'A'); } value = (value << 4) + c; size--; } // send data to digital potentiometer (Echo Delay) TWI_Xmit[0] = 0x12; TWI_Xmit[1] = value; twi_master_trans(&twi0_master, 0x2A, TWI_Xmit, 2, TWI_Recv, 0); putchar_usart0('D'); } break; case 'M': if (size == 3) { value = 0; size--; while (size) { c = *indat; if (isdigit(c)) { c -= '0'; } else if (isupper(c)) { c = (10 + c - 'A'); } value = (value << 4) + c; size--; } // send data to digital potentiometer (Echo Mix) TWI_Xmit[0] = 0x11; TWI_Xmit[1] = value; twi_master_trans(&twi0_master, 0x2A, TWI_Xmit, 2, TWI_Recv, 0); putchar_usart0('D'); } break; case 'V': if (size == 3) { value = 0; size--; while (size) { c = *indat; if (isdigit(c)) { c -= '0'; } else if (isupper(c)) { c = (10 + c - 'A'); } value = (value << 4) + c; size--; } // send data to digital potentiometer (volume) TWI_Xmit[0] = 0x11; TWI_Xmit[1] = value; twi_master_trans(&twi0_master, 0x29, TWI_Xmit, 2, TWI_Recv, 0); putchar_usart0('V'); } break; } }
/************************************************************************* Project : Multi-Voice Synthesizer - Main Board File : clock_init.c Version : 0.1 Date : 2/19/2023 Author : Gene Breniman Company : Young Embedded Systems Comments: Chip type : ATtiny3226 Program type : Application AVR Core Clock frequency: 20.000000 MHz Memory model : Small Data Stack size : 768 Revisions: 4-7-23 - Initial (Pre-Release) *************************************************************************/ /******************************************************* System clock initialization created by the CodeWizardAVR V3.51 Automatic Program Generator © Copyright 1998-2023 Pavel Haiduc, HP InfoTech S.R.L. http://www.hpinfotech.ro Project : *******************************************************/ // I/O Registers definitions #include <tiny3226.h> // Standard definitions #include <stddef.h> void system_clocks_init(void) { unsigned char n; unsigned char s; // Optimize for speed #pragma optsize- // Save interrupts enabled/disabled state s = CPU_SREG; // Disable interrupts #asm("cli") // The 20 MHz internal oscillator is selected by the OSCCFG.FREQSEL fuse bits=0x02 // Main clock source: 20 MHz Internal RC Oscillator // Peripheral clock output on CLKOUT (PORTB, Pin 5): Off n = CLKCTRL_CLKSEL_OSC20M_gc | (0<<CLKCTRL_CLKOUT_bp); CPU_CCP = CCP_IOREG_gc; CLKCTRL.MCLKCTRLA = n; // Main clock prescaler division ratio: 1 // CPU and Peripheral clock: 20000.000 kHz n = 0; CPU_CCP = CCP_IOREG_gc; CLKCTRL.MCLKCTRLB=n; // Restore interrupts enabled/disabled state CPU_SREG = s; // Restore optimization for size if needed #pragma optsize_default }
/************************************************************************* Project : Multi-Voice Synthesizer - Main Board File : ports_init.c Version : 0.1 Date : 2/19/2023 Author : Gene Breniman Company : Young Embedded Systems Comments: Chip type : ATtiny3226 Program type : Application AVR Core Clock frequency: 20.000000 MHz Memory model : Small Data Stack size : 768 Revisions: 4-7-23 - Initial (Pre-Release) *************************************************************************/ // I/O Registers definitions #include <tiny3226.h> // Ports initialization void ports_init(void) { // PORTA initialization // OUT register PORTA.OUT = 0x02; // Pin0: Input, Pin1: Output, Pin2: Input, Pin3: Input, Pin4: Input, Pin5: Input, Pin6: Input, Pin7: Input PORTA.DIR = 0x02; // Slew Rate Limitation: Off PORTA.PORTCTRL = (0<<PORT_SRL_bp); // Pin0 Inverted: Off, Pullup on input: Off, Input/Sense configuration: Int. disabled, Inp. buffer enabled PORTA.PIN0CTRL = (0<<PORT_INVEN_bp) | (0<<PORT_PULLUPEN_bp) | PORT_ISC_INTDISABLE_gc; // Pin1 Inverted: Off, Pullup on input: Off, Input/Sense configuration: Int. disabled, Inp. buffer enabled PORTA.PIN1CTRL = (0<<PORT_INVEN_bp) | (0<<PORT_PULLUPEN_bp) | PORT_ISC_INTDISABLE_gc; // Pin2 Inverted: Off, Pullup on input: Off, Input/Sense configuration: Int. disabled, Inp. buffer enabled PORTA.PIN2CTRL = (0<<PORT_INVEN_bp) | (0<<PORT_PULLUPEN_bp) | PORT_ISC_INTDISABLE_gc; // Pin3 Inverted: Off, Pullup on input: Off, Input/Sense configuration: Int. disabled, Inp. buffer enabled PORTA.PIN3CTRL = (0<<PORT_INVEN_bp) | (0<<PORT_PULLUPEN_bp) | PORT_ISC_INTDISABLE_gc; // Pin4 Inverted: Off, Pullup on input: Off, Input/Sense configuration: Int. disabled, Inp. buffer enabled PORTA.PIN4CTRL = (0<<PORT_INVEN_bp) | (0<<PORT_PULLUPEN_bp) | PORT_ISC_INTDISABLE_gc; // Pin5 Inverted: Off, Pullup on input: Off, Input/Sense configuration: Int. disabled, Inp. buffer enabled PORTA.PIN5CTRL = (0<<PORT_INVEN_bp) | (0<<PORT_PULLUPEN_bp) | PORT_ISC_INTDISABLE_gc; // Pin6 Inverted: Off, Pullup on input: Off, Input/Sense configuration: Int. disabled, Inp. buffer enabled PORTA.PIN6CTRL = (0<<PORT_INVEN_bp) | (0<<PORT_PULLUPEN_bp) | PORT_ISC_INTDISABLE_gc; // Pin7 Inverted: Off, Pullup on input: Off, Input/Sense configuration: Int. disabled, Inp. buffer enabled PORTA.PIN7CTRL = (0<<PORT_INVEN_bp) | (0<<PORT_PULLUPEN_bp) | PORT_ISC_INTDISABLE_gc; // PORTB initialization // OUT register PORTB.OUT = 0x04; // Pin0: Input, Pin1: Input, Pin2: Output, Pin3: Input, Pin4: Input, Pin5: Input PORTB.DIR = 0x04; // Slew Rate Limitation: Off PORTB.PORTCTRL = (0<<PORT_SRL_bp); // Pin0 Inverted: Off, Pullup on input: Off, Input/Sense configuration: Int. disabled, Inp. buffer enabled PORTB.PIN0CTRL = (0<<PORT_INVEN_bp) | (0<<PORT_PULLUPEN_bp) | PORT_ISC_INTDISABLE_gc; // Pin1 Inverted: Off, Pullup on input: Off, Input/Sense configuration: Int. disabled, Inp. buffer enabled PORTB.PIN1CTRL = (0<<PORT_INVEN_bp) | (0<<PORT_PULLUPEN_bp) | PORT_ISC_INTDISABLE_gc; // Pin2 Inverted: Off, Pullup on input: Off, Input/Sense configuration: Int. disabled, Inp. buffer enabled PORTB.PIN2CTRL = (0<<PORT_INVEN_bp) | (0<<PORT_PULLUPEN_bp) | PORT_ISC_INTDISABLE_gc; // Pin3 Inverted: Off, Pullup on input: Off, Input/Sense configuration: Int. disabled, Inp. buffer enabled PORTB.PIN3CTRL = (0<<PORT_INVEN_bp) | (0<<PORT_PULLUPEN_bp) | PORT_ISC_INTDISABLE_gc; // Pin4 Inverted: Off, Pullup on input: Off, Input/Sense configuration: Int. disabled, Inp. buffer enabled PORTB.PIN4CTRL = (0<<PORT_INVEN_bp) | (0<<PORT_PULLUPEN_bp) | PORT_ISC_INTDISABLE_gc; // Pin5 Inverted: Off, Pullup on input: Off, Input/Sense configuration: Int. disabled, Inp. buffer enabled PORTB.PIN5CTRL = (0<<PORT_INVEN_bp) | (0<<PORT_PULLUPEN_bp) | PORT_ISC_INTDISABLE_gc; // PORTC initialization // OUT register PORTC.OUT = 0x00; // Pin0: Output, Pin1: Input, Pin2: Input, Pin3: Input PORTC.DIR = 0x01; // Slew Rate Limitation: Off PORTC.PORTCTRL = (0<<PORT_SRL_bp); // Pin0 Inverted: Off, Pullup on input: Off, Input/Sense configuration: Int. disabled, Inp. buffer enabled PORTC.PIN0CTRL = (0<<PORT_INVEN_bp) | (0<<PORT_PULLUPEN_bp) | PORT_ISC_INTDISABLE_gc; // Pin1 Inverted: Off, Pullup on input: Off, Input/Sense configuration: Int. disabled, Inp. buffer enabled PORTC.PIN1CTRL = (0<<PORT_INVEN_bp) | (0<<PORT_PULLUPEN_bp) | PORT_ISC_INTDISABLE_gc; // Pin2 Inverted: Off, Pullup on input: Off, Input/Sense configuration: Int. disabled, Inp. buffer enabled PORTC.PIN2CTRL = (0<<PORT_INVEN_bp) | (0<<PORT_PULLUPEN_bp) | PORT_ISC_INTDISABLE_gc; // Pin3 Inverted: Off, Pullup on input: Off, Input/Sense configuration: Int. disabled, Inp. buffer enabled PORTC.PIN3CTRL = (0<<PORT_INVEN_bp) | (0<<PORT_PULLUPEN_bp) | PORT_ISC_INTDISABLE_gc; }
/************************************************************************* Project : Multi-Voice Synthesizer - Main Board File : twi_init.c Version : 0.1 Date : 2/19/2023 Author : Gene Breniman Company : Young Embedded Systems Comments: Chip type : ATtiny3226 Program type : Application AVR Core Clock frequency: 20.000000 MHz Memory model : Small Data Stack size : 768 Revisions: 4-7-23 - Initial (Pre-Release) *************************************************************************/ // I/O Registers definitions #include <tiny3226.h> // TWI initialization functions #include "twi_init.h" // TWI0 initialization // Structure that holds information used by the TWI0 Master // for performing a TWI bus transaction TWI_MASTER_INFO_t twi0_master; void twi0_init(void) { // General TWI0 initialization // SDA Setup time: 4 Clock Cycles, SDA Hold: Off, Fast Mode+ : Off twi_init(&TWI0, TWI_SDASETUP_4CYC_gc, TWI_SDAHOLD_OFF_gc, false); // TWI0 Master initialization // Peripheral Clock frequency: 20000000 Hz, SCL Rate: 100000 bps (Real Rate: 99751 bps, Error: 0.2 %), SCL Rise time: 25 ns, Inactive bus timeout: Disabled (I2C) twi_master_init(&twi0_master, &TWI0, TWI_BAUD_REG(20000000, 100000, 25), TWI_TIMEOUT_DISABLED_gc); // TWI0 Slave is disabled TWI0.SCTRLA = 0; } // TWI0 Master interrupt service routine #pragma optsize- // optimize for speed interrupt [TWI0_TWIM_vect] void twi0_master_isr(void) { twi_master_int_handler(&twi0_master); } #pragma optsize_default
/************************************************************************* Project : Multi-Voice Synthesizer - Main Board File : twi_init.h Version : 0.1 Date : 2/19/2023 Author : Gene Breniman Company : Young Embedded Systems Comments: Chip type : ATtiny3226 Program type : Application AVR Core Clock frequency: 20.000000 MHz Memory model : Small Data Stack size : 768 Revisions: 4-7-23 - Initial (Pre-Release) *************************************************************************/ // I/O Registers definitions #include <tiny3226.h> // USARTs initialization functions #include "usarts_init.h" // USART0 initialization void usart0_init(void) { PORTMUX.USARTROUTEA &= ~PORTMUX_USART0_gm; // The USART0 signals are not remapped: RxD: PORTB.3, TxD: PORTB.2, XDIR: PORTB.0 PORTB.OUTSET = 0x04; // Transmitter is enabled - Set TxD=1 USART0.CTRLC = USART_CMODE_ASYNCHRONOUS_gc | USART_PMODE_DISABLED_gc | USART_SBMODE_1BIT_gc | USART_CHSIZE_8BIT_gc; // mode: Async USART, Data bits: 8, Stop bits: 1, Parity: Disabled USART0.CTRLA = (1<<USART_RXCIE_bp) | USART_RS485_DISABLE_gc; // Receive complete interrupt: On, Note: The transmitter Data Register Empty interrupt will be enabled later, RS485 Mode: RS485 Disabled USART0.BAUD = 0x411B; // Required Baud rate: 9600, eal Baud Rate: 9599.8 (x2 Mode), Error: 0.0 % USART0.CTRLB = (1<<USART_RXEN_bp) | (1<<USART_TXEN_bp) | (0<<USART_ODME_bp) | USART_RXMODE_CLK2X_gc | (0<<USART_MPCM_bp);// Recv: On, Tran: On, TxD open drain: Off, x2: On, Multi-processor communication mode: Off } // Note: RX_BUFFER_SIZE_USART0 is #define-d in 'usarts_init.h' with the value 8 char rx_buffer_usart0[RX_BUFFER_SIZE_USART0]; volatile unsigned char rx_wr_index_usart0 = 0; volatile unsigned char rx_rd_index_usart0 = 0; volatile unsigned char rx_counter_usart0 = 0; // This flag is set on USART0 Receiver buffer overflow bit rx_buffer_overflow_usart0 = 0; // USART0 Receiver interrupt service routine interrupt [USART0_RXC_vect] void usart0_rx_isr(void) { unsigned char status; char data; status = USART0.RXDATAH; data = USART0.RXDATAL; if ((status & (USART_FERR_bm | USART_PERR_bm | USART_BUFOVF_bm)) == 0) { rx_buffer_usart0[rx_wr_index_usart0++] = data; if (rx_wr_index_usart0 == RX_BUFFER_SIZE_USART0) { rx_wr_index_usart0 = 0; } if (++rx_counter_usart0 == RX_BUFFER_SIZE_USART0) { rx_counter_usart0 = 0; rx_buffer_overflow_usart0 = 1; } } } // Receive a character from USART0 // USART0 is used as the default input device by the 'getchar' function // #define _ALTERNATE_GETCHAR_ is inserted for this purpose // in the main program source file before #include <stdio.h> #pragma used+ char getchar_usart0(void) { char data; if (rx_counter_usart0 == 0) { return 0; } data = rx_buffer_usart0[rx_rd_index_usart0++]; if (rx_rd_index_usart0 == RX_BUFFER_SIZE_USART0) { rx_rd_index_usart0 = 0; } #asm("cli") --rx_counter_usart0; #asm("sei") return data; } #pragma used- unsigned char GetSerial0InputCount(void) { return rx_counter_usart0; } // Note: TX_BUFFER_SIZE_USART0 is #define-d in 'usarts_init.h' with the value 8 char tx_buffer_usart0[TX_BUFFER_SIZE_USART0]; volatile unsigned char tx_wr_index_usart0 = 0; volatile unsigned char tx_rd_index_usart0 = 0; volatile unsigned char tx_counter_usart0 = 0; // USART0 Transmitter interrupt service routine interrupt [USART0_DRE_vect] void usart0_tx_isr(void) { if (tx_counter_usart0) { --tx_counter_usart0; USART0.TXDATAL = tx_buffer_usart0[tx_rd_index_usart0++]; if (tx_rd_index_usart0 == TX_BUFFER_SIZE_USART0) { tx_rd_index_usart0 = 0; } } else { USART0.CTRLA &= ~USART_DREIE_bm; } } // Write a character to the USART0 Transmitter buffer // USART0 is used as the default output device by the 'putchar' function // #define _ALTERNATE_PUTCHAR_ is inserted for this purpose // in the main program source file before #include <stdio.h> #pragma used+ void putchar_usart0(char c) { if (tx_counter_usart0 == TX_BUFFER_SIZE_USART0) { return; } #asm("cli") if (tx_counter_usart0 || ((USART0.STATUS & USART_DREIF_bm) == 0)) { tx_buffer_usart0[tx_wr_index_usart0++] = c; if (tx_wr_index_usart0 == TX_BUFFER_SIZE_USART0) tx_wr_index_usart0=0; ++tx_counter_usart0; } else { USART0.CTRLA |= USART_DREIE_bm; USART0.TXDATAL = c; } #asm("sei") } #pragma used- // USART1 initialization void usart1_init(void) { PORTMUX.USARTROUTEA &= ~PORTMUX_USART1_gm; // The USART1 signals are not remapped: RxD: PORTA.2, TxD: PORTA.1, XDIR: PORTA.4 PORTA.OUTSET=0x02; // Transmitter is enabled, Set TxD=1 USART1.CTRLC = USART_CMODE_ASYNCHRONOUS_gc | USART_PMODE_DISABLED_gc | USART_SBMODE_1BIT_gc | USART_CHSIZE_8BIT_gc; // Async USART, Data bits: 8, Stop bits: 1, Parity: Disabled USART1.CTRLA = (1<<USART_RXCIE_bp) | USART_RS485_DISABLE_gc; // Receive complete interrupt: On // Note: The transmitter Data Register Empty interrupt will be enabled later // RS485 Mode: RS485 Disabled USART1.BAUD = 0x411B; // Required Baud rate: 9600 // Real Baud Rate: 9599.8 (x2 Mode), Error: 0.0 % USART1.CTRLB = (1<<USART_RXEN_bp) | (1<<USART_TXEN_bp) | (0<<USART_ODME_bp) | USART_RXMODE_CLK2X_gc | (0<<USART_MPCM_bp); // Recv: On, Tran: On, TxD open drain: Off, 2x speed: On, Multi-processor communication mode: Off } // Note: RX_BUFFER_SIZE_USART1 is #define-d in 'usarts_init.h' with the value 8 char rx_buffer_usart1[RX_BUFFER_SIZE_USART1]; volatile unsigned char rx_wr_index_usart1 = 0; volatile unsigned char rx_rd_index_usart1 = 0; volatile unsigned char rx_counter_usart1 = 0; // This flag is set on USART1 Receiver buffer overflow bit rx_buffer_overflow_usart1=0; // USART1 Receiver interrupt service routine interrupt [USART1_RXC_vect] void usart1_rx_isr(void) { unsigned char status; char data; status = USART1.RXDATAH; data = USART1.RXDATAL; if ((status & (USART_FERR_bm | USART_PERR_bm | USART_BUFOVF_bm)) == 0) { rx_buffer_usart1[rx_wr_index_usart1++] = data; if (rx_wr_index_usart1 == RX_BUFFER_SIZE_USART1) { rx_wr_index_usart1 = 0; } if (++rx_counter_usart1 == RX_BUFFER_SIZE_USART1) { rx_counter_usart1 = 0; rx_buffer_overflow_usart1 = 1; } } } // Receive a character from USART1 #pragma used+ char getchar_usart1(void) { char data; if (rx_counter_usart1 == 0) { return 0; } data = rx_buffer_usart1[rx_rd_index_usart1++]; if (rx_rd_index_usart1 == RX_BUFFER_SIZE_USART1) { rx_rd_index_usart1 = 0; } #asm("cli") --rx_counter_usart1; #asm("sei") return data; } #pragma used- unsigned char GetSerial1InputCount(void) { return rx_counter_usart1; } // Note: TX_BUFFER_SIZE_USART1 is #define-d in 'usarts_init.h' with the value 8 char tx_buffer_usart1[TX_BUFFER_SIZE_USART1]; volatile unsigned char tx_wr_index_usart1=0,tx_rd_index_usart1=0; volatile unsigned char tx_counter_usart1=0; // USART1 Transmitter interrupt service routine interrupt [USART1_DRE_vect] void usart1_tx_isr(void) { if (tx_counter_usart1) { --tx_counter_usart1; USART1.TXDATAL = tx_buffer_usart1[tx_rd_index_usart1++]; if (tx_rd_index_usart1 == TX_BUFFER_SIZE_USART1) { tx_rd_index_usart1 = 0; } } else { USART1.CTRLA &= ~USART_DREIE_bm; } } // Write a character to the USART1 Transmitter buffer #pragma used+ void putchar_usart1(char c) { if (tx_counter_usart1 == TX_BUFFER_SIZE_USART1) { return; } #asm("cli") if (tx_counter_usart1 || ((USART1.STATUS & USART_DREIF_bm)==0)) { tx_buffer_usart1[tx_wr_index_usart1++] = c; if (tx_wr_index_usart1 == TX_BUFFER_SIZE_USART1) { tx_wr_index_usart1 = 0; } ++tx_counter_usart1; } else { USART1.CTRLA |= USART_DREIE_bm; USART1.TXDATAL = c; } #asm("sei") } #pragma used-
Include Files:
/************************************************************************* Project : Multi-Voice Synthesizer - Main Board File : clock_init.h Version : 0.1 Date : 2/19/2023 Author : Gene Breniman Company : Young Embedded Systems Comments: Chip type : ATtiny3226 Program type : Application AVR Core Clock frequency: 20.000000 MHz Memory model : Small Data Stack size : 768 Revisions: 4-7-23 - Initial (Pre-Release) *************************************************************************/ /******************************************************* System clock initialization created by the CodeWizardAVR V3.51 Automatic Program Generator © Copyright 1998-2023 Pavel Haiduc, HP InfoTech S.R.L. http://www.hpinfotech.ro Project : *******************************************************/ #ifndef _CLOCK_INIT_INCLUDED_ #define _CLOCK_INIT_INCLUDED_ void system_clocks_init(void); #endif /************************************************************************* Project : Multi-Voice Synthesizer - Main Board File : ports_init.h Version : 0.1 Date : 2/19/2023 Author : Gene Breniman Company : Young Embedded Systems Comments: Chip type : ATtiny3226 Program type : Application AVR Core Clock frequency: 20.000000 MHz Memory model : Small Data Stack size : 768 Revisions: 4-7-23 - Initial (Pre-Release) *************************************************************************/ /******************************************************* I/O Ports initialization created by the CodeWizardAVR V3.51 Automatic Program Generator © Copyright 1998-2023 Pavel Haiduc, HP InfoTech S.R.L. http://www.hpinfotech.ro Project : *******************************************************/ #ifndef _PORTS_INIT_INCLUDED_ #define _PORTS_INIT_INCLUDED_ // Ports initialization void ports_init(void); #endif /************************************************************************* Project : Multi-Voice Synthesizer - Main Board File : twi_init.h Version : 0.1 Date : 2/19/2023 Author : Gene Breniman Company : Young Embedded Systems Comments: Chip type : ATtiny3226 Program type : Application AVR Core Clock frequency: 20.000000 MHz Memory model : Small Data Stack size : 768 Revisions: 4-7-23 - Initial (Pre-Release) *************************************************************************/ /******************************************************* TWI initialization created by the CodeWizardAVR V3.51 Automatic Program Generator © Copyright 1998-2023 Pavel Haiduc, HP InfoTech S.R.L. http://www.hpinfotech.ro Project : *******************************************************/ #ifndef _TWI_INIT_INCLUDED_ #define _TWI_INIT_INCLUDED_ // TWI bus interface functions #include <twi.h> // General TWI0 initialization void twi0_init(void); // Structure that holds information used by the TWI0 Master // for performing a TWI bus transaction extern TWI_MASTER_INFO_t twi0_master; #endif /************************************************************************* Project : Multi-Voice Synthesizer - Main Board File : usarts_init.h Version : 0.1 Date : 2/19/2023 Author : Gene Breniman Company : Young Embedded Systems Comments: Chip type : ATtiny3226 Program type : Application AVR Core Clock frequency: 20.000000 MHz Memory model : Small Data Stack size : 768 Revisions: 4-7-23 - Initial (Pre-Release) *************************************************************************/ /******************************************************* USARTs initialization created by the CodeWizardAVR V3.51 Automatic Program Generator © Copyright 1998-2023 Pavel Haiduc, HP InfoTech S.R.L. http://www.hpinfotech.ro Project : *******************************************************/ #ifndef _USARTS_INIT_INCLUDED_ #define _USARTS_INIT_INCLUDED_ // USART0 is used as the default input device by the 'getchar' function // #define _ALTERNATE_GETCHAR_ is inserted for this purpose // in the main program source file before #include <stdio.h> char getchar_usart0(void); unsigned char GetSerial0InputCount(void); // USART0 is used as the default output device by the 'putchar' function // #define _ALTERNATE_PUTCHAR_ is inserted for this purpose // in the main program source file before #include <stdio.h> void putchar_usart0(char c); // USART0 initialization void usart0_init(void) ; // USART0 Receiver buffer #define RX_BUFFER_SIZE_USART0 32 extern char rx_buffer_usart0[RX_BUFFER_SIZE_USART0]; extern volatile unsigned char rx_wr_index_usart0; extern volatile unsigned char rx_rd_index_usart0; extern volatile unsigned char rx_counter_usart0; // This flag is set on USART0 Receiver buffer overflow extern bit rx_buffer_overflow_usart0; // USART0 Transmitter buffer #define TX_BUFFER_SIZE_USART0 32 extern char tx_buffer_usart0[TX_BUFFER_SIZE_USART0]; extern volatile unsigned char tx_wr_index_usart0; extern volatile unsigned char tx_rd_index_usart0; extern volatile unsigned char tx_counter_usart0; // USART1 receive function char getchar_usart1(void); unsigned char GetSerial1InputCount(void); // USART1 transmit function void putchar_usart1(char c); // USART1 initialization void usart1_init(void); // USART1 Receiver buffer #define RX_BUFFER_SIZE_USART1 32 extern char rx_buffer_usart1[RX_BUFFER_SIZE_USART1]; extern volatile unsigned char rx_wr_index_usart1; extern volatile unsigned char rx_rd_index_usart1; extern volatile unsigned char rx_counter_usart1; // This flag is set on USART1 Receiver buffer overflow extern bit rx_buffer_overflow_usart1; // USART1 Transmitter buffer #define TX_BUFFER_SIZE_USART1 32 extern char tx_buffer_usart1[TX_BUFFER_SIZE_USART1]; extern volatile unsigned char tx_wr_index_usart1; extern volatile unsigned char tx_rd_index_usart1; extern volatile unsigned char tx_counter_usart1; #endif