element14 Community
element14 Community
    Register Log In
  • Site
  • Search
  • Log In Register
  • About Us
  • Community Hub
    Community Hub
    • What's New on element14
    • Feedback and Support
    • Benefits of Membership
    • Personal Blogs
    • Members Area
    • Achievement Levels
  • Learn
    Learn
    • Ask an Expert
    • eBooks
    • element14 presents
    • Learning Center
    • Tech Spotlight
    • STEM Academy
    • Webinars, Training and Events
    • Learning Groups
  • Technologies
    Technologies
    • 3D Printing
    • FPGA
    • Industrial Automation
    • Internet of Things
    • Power & Energy
    • Sensors
    • Technology Groups
  • Challenges & Projects
    Challenges & Projects
    • Design Challenges
    • element14 presents Projects
    • Project14
    • Arduino Projects
    • Raspberry Pi Projects
    • Project Groups
  • Products
    Products
    • Arduino
    • Avnet Boards Community
    • Dev Tools
    • Manufacturers
    • Multicomp Pro
    • Product Groups
    • Raspberry Pi
    • RoadTests & Reviews
  • Store
    Store
    • Visit Your Store
    • Choose another store...
      • Europe
      •  Austria (German)
      •  Belgium (Dutch, French)
      •  Bulgaria (Bulgarian)
      •  Czech Republic (Czech)
      •  Denmark (Danish)
      •  Estonia (Estonian)
      •  Finland (Finnish)
      •  France (French)
      •  Germany (German)
      •  Hungary (Hungarian)
      •  Ireland
      •  Israel
      •  Italy (Italian)
      •  Latvia (Latvian)
      •  
      •  Lithuania (Lithuanian)
      •  Netherlands (Dutch)
      •  Norway (Norwegian)
      •  Poland (Polish)
      •  Portugal (Portuguese)
      •  Romania (Romanian)
      •  Russia (Russian)
      •  Slovakia (Slovak)
      •  Slovenia (Slovenian)
      •  Spain (Spanish)
      •  Sweden (Swedish)
      •  Switzerland(German, French)
      •  Turkey (Turkish)
      •  United Kingdom
      • Asia Pacific
      •  Australia
      •  China
      •  Hong Kong
      •  India
      •  Korea (Korean)
      •  Malaysia
      •  New Zealand
      •  Philippines
      •  Singapore
      •  Taiwan
      •  Thailand (Thai)
      • Americas
      •  Brazil (Portuguese)
      •  Canada
      •  Mexico (Spanish)
      •  United States
      Can't find the country/region you're looking for? Visit our export site or find a local distributor.
  • Translate
  • Profile
  • Settings
Music Time
  • Challenges & Projects
  • Project14
  • Music Time
  • More
  • Cancel
Music Time
P14 Music Time Blog Multi-Voice Synthesizer - Part 7 - Adding Noise Back into the Mix.
  • Blog
  • Forum
  • Documents
  • Events
  • Polls
  • Files
  • Members
  • Mentions
  • Sub-Groups
  • Tags
  • More
  • Cancel
  • New
Join Music Time to participate - click to join for free!
  • Share
  • More
  • Cancel
Group Actions
  • Group RSS
  • More
  • Cancel
Engagement
  • Author Author: genebren
  • Date Created: 11 Apr 2023 10:57 PM Date Created
  • Views 5628 views
  • Likes 9 likes
  • Comments 1 comment
  • ADSR
  • musictimech
  • VCA
  • noise
  • Echo Processor
  • MIDI synthesizer
Related
Recommended

Multi-Voice Synthesizer - Part 7 - Adding Noise Back into the Mix.

genebren
genebren
11 Apr 2023

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:

image

First we have the normal chord sequence.

image

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:

image

image

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;

}


  • Sign in to reply
  • javagoza
    javagoza over 2 years ago

    Great progress! I have also started to generate and modulate noise. I'm a bit busy and I haven't been able to advance with the blogs but I continue to learn with blogs like yours. Thank you!
    This is one of my attempts simulating percussion instruments.

    image

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • More
    • Cancel
element14 Community

element14 is the first online community specifically for engineers. Connect with your peers and get expert answers to your questions.

  • Members
  • Learn
  • Technologies
  • Challenges & Projects
  • Products
  • Store
  • About Us
  • Feedback & Support
  • FAQs
  • Terms of Use
  • Privacy Policy
  • Legal and Copyright Notices
  • Sitemap
  • Cookies

An Avnet Company © 2025 Premier Farnell Limited. All Rights Reserved.

Premier Farnell Ltd, registered in England and Wales (no 00876412), registered office: Farnell House, Forge Lane, Leeds LS12 2NE.

ICP 备案号 10220084.

Follow element14

  • X
  • Facebook
  • linkedin
  • YouTube