The tiny musical LPC810
I did a breakout board for the NXP LPC810 ARM Cortex M0+ 8-DIP little monster and was thinking of what it could be good for?
Because of the raw 32-bit processing power hosted in this little thing I figured it could make a nice little synth.
But it doesn't have a digital to analog converter and the low 6GPIOs won't do for an R2R resistor ladder DAC I had to figure out something else.Most CD-players today use a 1-bit Sigma-Delta DAC.
Perfect, just 1 GPIO.
So I connected a lowpass filter (resistor and capacitor) to P0_3 on the beast and 2 hours later I had a PDM DAC with great specs.
How about a DAC with a resolution of 44.1KHz and 16-bits?
The bitstream runs at 3.6MHz and it can easily do stereo on 2 GPIOs.
Now it's time for the different virtual analog synthesizer blocks to be coded.
Output a 256 16-bit sinetable at 44.1KHz
Output a 256 16-bit sinetable at 44.1KHz
Warning: Embedding code files within the project story has been deprecated. To edit this file or add more files, go to the "Software" tab. To remove this file from the story, click on it to trigger the context menu, then click the trash can button (this won't delete it from the "Software" tab).
//**************************************************************************/
// Synth main.c with Arduino style definitions
//**************************************************************************/
#include "wiring.h"
#include "LPC8xx.h"
/* Control register bit definition. */
#define MRT_INT_ENA (0x1<<0)
#define MRT_REPEATED_MODE (0x00<<1)
#define MRT_ONE_SHOT_INT (0x01<<1)
#define MRT_ONE_SHOT_STALL (0x02<<1)
/* Status register bit definition */
#define MRT_STAT_IRQ_FLAG (0x1<<0)
#define MRT_STAT_RUN (0x1<<1)
int32_t err=0;
volatile int32_t DAC=0;
uint8_t phase=0;
int16_t sine[256] = {
0x0000, 0x0324, 0x0647, 0x096a, 0x0c8b, 0x0fab, 0x12c8, 0x15e2,
0x18f8, 0x1c0b, 0x1f19, 0x2223, 0x2528, 0x2826, 0x2b1f, 0x2e11,
0x30fb, 0x33de, 0x36ba, 0x398c, 0x3c56, 0x3f17, 0x41ce, 0x447a,
0x471c, 0x49b4, 0x4c3f, 0x4ebf, 0x5133, 0x539b, 0x55f5, 0x5842,
0x5a82, 0x5cb4, 0x5ed7, 0x60ec, 0x62f2, 0x64e8, 0x66cf, 0x68a6,
0x6a6d, 0x6c24, 0x6dca, 0x6f5f, 0x70e2, 0x7255, 0x73b5, 0x7504,
0x7641, 0x776c, 0x7884, 0x798a, 0x7a7d, 0x7b5d, 0x7c29, 0x7ce3,
0x7d8a, 0x7e1d, 0x7e9d, 0x7f09, 0x7f62, 0x7fa7, 0x7fd8, 0x7ff6,
0x7fff, 0x7ff6, 0x7fd8, 0x7fa7, 0x7f62, 0x7f09, 0x7e9d, 0x7e1d,
0x7d8a, 0x7ce3, 0x7c29, 0x7b5d, 0x7a7d, 0x798a, 0x7884, 0x776c,
0x7641, 0x7504, 0x73b5, 0x7255, 0x70e2, 0x6f5f, 0x6dca, 0x6c24,
0x6a6d, 0x68a6, 0x66cf, 0x64e8, 0x62f2, 0x60ec, 0x5ed7, 0x5cb4,
0x5a82, 0x5842, 0x55f5, 0x539b, 0x5133, 0x4ebf, 0x4c3f, 0x49b4,
0x471c, 0x447a, 0x41ce, 0x3f17, 0x3c56, 0x398c, 0x36ba, 0x33de,
0x30fb, 0x2e11, 0x2b1f, 0x2826, 0x2528, 0x2223, 0x1f19, 0x1c0b,
0x18f8, 0x15e2, 0x12c8, 0x0fab, 0x0c8b, 0x096a, 0x0647, 0x0324,
0x0000, 0xfcdc, 0xf9b9, 0xf696, 0xf375, 0xf055, 0xed38, 0xea1e,
0xe708, 0xe3f5, 0xe0e7, 0xdddd, 0xdad8, 0xd7da, 0xd4e1, 0xd1ef,
0xcf05, 0xcc22, 0xc946, 0xc674, 0xc3aa, 0xc0e9, 0xbe32, 0xbb86,
0xb8e4, 0xb64c, 0xb3c1, 0xb141, 0xaecd, 0xac65, 0xaa0b, 0xa7be,
0xa57e, 0xa34c, 0xa129, 0x9f14, 0x9d0e, 0x9b18, 0x9931, 0x975a,
0x9593, 0x93dc, 0x9236, 0x90a1, 0x8f1e, 0x8dab, 0x8c4b, 0x8afc,
0x89bf, 0x8894, 0x877c, 0x8676, 0x8583, 0x84a3, 0x83d7, 0x831d,
0x8276, 0x81e3, 0x8163, 0x80f7, 0x809e, 0x8059, 0x8028, 0x800a,
0x8000, 0x800a, 0x8028, 0x8059, 0x809e, 0x80f7, 0x8163, 0x81e3,
0x8276, 0x831d, 0x83d7, 0x84a3, 0x8583, 0x8676, 0x877c, 0x8894,
0x89bf, 0x8afc, 0x8c4b, 0x8dab, 0x8f1e, 0x90a1, 0x9236, 0x93dc,
0x9593, 0x975a, 0x9931, 0x9b18, 0x9d0e, 0x9f14, 0xa129, 0xa34c,
0xa57e, 0xa7be, 0xaa0b, 0xac65, 0xaecd, 0xb141, 0xb3c1, 0xb64c,
0xb8e4, 0xbb86, 0xbe32, 0xc0e9, 0xc3aa, 0xc674, 0xc946, 0xcc22,
0xcf05, 0xd1ef, 0xd4e1, 0xd7da, 0xdad8, 0xdddd, 0xe0e7, 0xe3f5,
0xe708, 0xea1e, 0xed38, 0xf055, 0xf375, 0xf696, 0xf9b9, 0xfcdc
};
void MRT_IRQHandler(void) {
pinMode(3,INPUT);
if ( LPC_MRT->Channel[0].STAT & MRT_STAT_IRQ_FLAG ) {
LPC_MRT->Channel[0].STAT = MRT_STAT_IRQ_FLAG; /* clear interrupt flag */
DAC=0x8000+sine[phase++];
}
pinMode(3,OUTPUT);
return;
}
void mrtInit(uint32_t delay)
{
/* Enable clock to MRT and reset the MRT peripheral */
LPC_SYSCON->SYSAHBCLKCTRL |= (0x1<<10);
LPC_SYSCON->PRESETCTRL &= ~(0x1<<7);
LPC_SYSCON->PRESETCTRL |= (0x1<<7);
LPC_MRT->Channel[0].INTVAL = delay;
LPC_MRT->Channel[0].INTVAL |= 0x1UL<<31;
LPC_MRT->Channel[0].CTRL = MRT_REPEATED_MODE|MRT_INT_ENA;
/* Enable the MRT Interrupt */
#if NMI_ENABLED
NVIC_DisableIRQ( MRT_IRQn );
NMI_Init( MRT_IRQn );
#else
NVIC_EnableIRQ(MRT_IRQn);
#endif
return;
}
int main(void)
{
/* Initialise the GPIO block */
/* Enable AHB clock to the GPIO domain. */
LPC_SYSCON->SYSAHBCLKCTRL |= (1 << 6);
LPC_SYSCON->PRESETCTRL &= ~(1 << 10);
LPC_SYSCON->PRESETCTRL |= (1 << 10);
LPC_SWM->PINENABLE0 = 0xffffffffUL; //All 6 GPIO enabled
/* Configure the multi-rate timer for 44K ticks */
mrtInit(__SYSTEM_CLOCK/44100);
pinMode(3,OUTPUT);
while(1) {
if(DAC >= err) {
digitalWrite(3,HIGH);
err += 0xFFFF-DAC;
}
else
{
digitalWrite(3,LOW);
err -= DAC;
}
}
}
The DCF
The DCF
Warning: Embedding code files within the project story has been deprecated. To edit this file or add more files, go to the "Software" tab. To remove this file from the story, click on it to trigger the context menu, then click the trash can button (this won't delete it from the "Software" tab).
//---------------- DCF block ---------------------------------------
//C implementation for a 4pole lowpass DCF with resonance:
vi=DCO;
vo = ((vo * (65536 - f)) + (t1 * f))/65536; //+3db
t1 = ((t1 * (65536 - f)) + (t2 * f))/65536; //+6db
t2 = ((t2 * (65536 - f)) + (t3 * f))/65536; //+9db
t3 = ((t3 * (65536 - f)) + (t4 * f))/65536; //+12db
t4 = ((t4 * (65536 - f)) + (t5 * f))/65536; //+15db
t5 = ((t5 * (65536 - f)) + (t6 * f))/65536; //+18db
t6 = ((t6 * (65536 - f)) + (t7 * f))/65536; //+21db
t7 = ((t7 * (65536 - f)) + (t8 * f))/65536; //+24db
t8 = vi-((vo*q)/65536); //resonance feedback
if (vo>32767) vo=32767;
if (vo<-32767) vo=-32767;
DCF=vo;
//-----------------------------------------------------------------
The DCO block
The DCO block
Warning: Embedding code files within the project story has been deprecated. To edit this file or add more files, go to the "Software" tab. To remove this file from the story, click on it to trigger the context menu, then click the trash can button (this won't delete it from the "Software" tab).
//-------------------- 3 DCO block ------------------------------------------
DCO1 += FREQ1; //increment phase accumulator1 with phase1
DCO2 += FREQ2; //increment phase accumulator2 with phase2
DCO3 += FREQ3; //increment phase accumulator3 with phase3
DCO=((((DCO1 & 0x7F0000)+(DCO2 & 0x7F0000)+(DCO3 & 0x7F0000))/65536)-192)*171;
//---------------------------------------------------------------------------
The Envelope
No synth Is complete without an envelope module.
The simple module controls volume over time by defining the attack time, decay time, sustain level and release time.
Really simple but needs a trigger to know when there is a note on or note off event.
Since our synth doesn't have a keyboard or MIDI input yet we can only run through the attack and release states, landing at the sustain level.
Testing the synth
The testcode plays a C4 with a nice Roland SawString sound (3 detuned saw oscillators) at a 20sec attack time and the filter fully open.
It's still monophonic but when implementing the MIDI handler I'll try to see how many blocks I can run indexed thus testing it for polyphony.
Enjoy.
The quest for SuperSaw sounds
Today lead synthesizers are measured by how good they are at generating the SuperSaw sound.
The sound is a famous trance lead sound that comes from the Roland JP-8000 analog modeling synth.
The sound is created using an odd number of detuned saw oscillators.
The first graph shows the output frequency spectrum of the oscillators.
Detuning the oscillators
The table shows the fractional detune of each oscillator against the center pitch.
The JP-8000 uses 7 oscillators but at least 3 oscillators are needed to imitate the SuperSaw sound.
The hostboard for scanning synth parameter pots
Populated
The basic DAC schematics
The synthesizer
Now I need to design the DCO, LFO, ENV and DCF blocks.
All of them need to go into the 44.1K ISR to calculate a 16-bit sample every timer tick.
Then it will be easy to run a UART as MIDI and modify the parameters in each block.
I may have to rewrite the PDM loop in ARM assembly to reach 64x or 128x oversampling in the bitstream.
At the moment, written in C, it's just 32x oversampling.
Another thing is that the ISR have to change the DAC GPIOs to input during the ISR execution to not leave the pin high or low.
This would create distortion at the sampling frequency.
Analog modeling filter code
The filter code is my personal version of a 4pole, 24db analog filter with resonance written in C.
Its fast and sound very round and pleasing with a great resonance sound.
This is the first time I put it out for everyone to use and it was originally written in x86 assembly code.
Runs great on the NXP.
Oscillators
The synth has three 32-bit saw oscillators tuned at middle F with 44701Hz sampling frequency.
This gives a 16bit fractional resolution and 0x00010000 as the frequency count for F4.
One oscillator is used for the center frequency of the key and the other two may be detuned above or below with a resolution of 1/65536 steps.
Perfect for generating analog synth sounds or SuperSaw sounds.
ENV block
ENV block
Warning: Embedding code files within the project story has been deprecated. To edit this file or add more files, go to the "Software" tab. To remove this file from the story, click on it to trigger the context menu, then click the trash can button (this won't delete it from the "Software" tab).
//--------------------- ENV block ---------------------------------
if (!envstat--) {
envstat=1024;
if (TRIG>0) {
if (state==0) {
volume += ATTACK;
if (volume>0xFFFF) {
volume=0xFFFF;
state++;
}
}
if (state==1) {
volume -= DECAY;
if (volume<SUSTAIN) {
volume=SUSTAIN;
state++;
}
}
else {
volume -= RELEASE;
if (volume<0) {
volume=0;
}
}
}
}
ENV=(volume*DCF)>>16;
//-----------------------------------------------------------------
Roland SawString demo on the LPC810 micro synth
Roland SawString demo on the LPC810 micro synth
Warning: Embedding code files within the project story has been deprecated. To edit this file or add more files, go to the "Software" tab. To remove this file from the story, click on it to trigger the context menu, then click the trash can button (this won't delete it from the "Software" tab).
//**************************************************************************/
// Analog Modeling Synth main code
//**************************************************************************/
#include "wiring.h"
#include "LPC8xx.h"
/* Control register bit definition. */
#define MRT_INT_ENA (0x1<<0)
#define MRT_REPEATED_MODE (0x00<<1)
#define MRT_ONE_SHOT_INT (0x01<<1)
#define MRT_ONE_SHOT_STALL (0x02<<1)
/* Status register bit definition */
#define MRT_STAT_IRQ_FLAG (0x1<<0)
#define MRT_STAT_RUN (0x1<<1)
#define SAW 0
#define SQUARE 1
#define SINE 2
//-------- Synth parameters --------------
uint8_t WAVEFORM=SAW; //SAW/SQUARE/SINE
uint32_t FREQ1 = 0x10000; //0x10000=C4
uint32_t FREQ2 = 0x10100; //0x10000=C4
uint32_t FREQ3 = 0x0FF00; //0x10000=C4
int32_t CUTOFF=65535; //freq 0-65535
int32_t RESONANCE=0; //reso=0-65535
uint8_t TRIG=1; //MIDItrig 1=note ON
uint32_t ATTACK=98; //Attackrate in 30mS steps
uint32_t DECAY=98; //Decayrate in 30mS steps
int32_t SUSTAIN=0xFFFF; //Sustainlevel 0-65535
uint32_t RELEASE=1; //Releaserate in 30mS steps
//-----------------------------------------
volatile uint16_t DAC=0;
uint32_t DCO1;
uint32_t DCO2;
uint32_t DCO3;
uint8_t state=0; //needs to be reset on a new trig
uint16_t envstat=1; //needs to be reset on a new trig
int32_t volume=0;
int32_t t1;
int32_t t2;
int32_t t3;
int32_t t4;
int32_t t5;
int32_t t6;
int32_t t7;
int32_t t8;
int32_t vi;
int32_t vo;
int16_t DCO;
int16_t DCF;
int32_t ENV;
int16_t LFO;
uint16_t lfocounter=1;
uint8_t phase;
const int16_t sine[256] = {
0x0000, 0x0324, 0x0647, 0x096a, 0x0c8b, 0x0fab, 0x12c8, 0x15e2,
0x18f8, 0x1c0b, 0x1f19, 0x2223, 0x2528, 0x2826, 0x2b1f, 0x2e11,
0x30fb, 0x33de, 0x36ba, 0x398c, 0x3c56, 0x3f17, 0x41ce, 0x447a,
0x471c, 0x49b4, 0x4c3f, 0x4ebf, 0x5133, 0x539b, 0x55f5, 0x5842,
0x5a82, 0x5cb4, 0x5ed7, 0x60ec, 0x62f2, 0x64e8, 0x66cf, 0x68a6,
0x6a6d, 0x6c24, 0x6dca, 0x6f5f, 0x70e2, 0x7255, 0x73b5, 0x7504,
0x7641, 0x776c, 0x7884, 0x798a, 0x7a7d, 0x7b5d, 0x7c29, 0x7ce3,
0x7d8a, 0x7e1d, 0x7e9d, 0x7f09, 0x7f62, 0x7fa7, 0x7fd8, 0x7ff6,
0x7fff, 0x7ff6, 0x7fd8, 0x7fa7, 0x7f62, 0x7f09, 0x7e9d, 0x7e1d,
0x7d8a, 0x7ce3, 0x7c29, 0x7b5d, 0x7a7d, 0x798a, 0x7884, 0x776c,
0x7641, 0x7504, 0x73b5, 0x7255, 0x70e2, 0x6f5f, 0x6dca, 0x6c24,
0x6a6d, 0x68a6, 0x66cf, 0x64e8, 0x62f2, 0x60ec, 0x5ed7, 0x5cb4,
0x5a82, 0x5842, 0x55f5, 0x539b, 0x5133, 0x4ebf, 0x4c3f, 0x49b4,
0x471c, 0x447a, 0x41ce, 0x3f17, 0x3c56, 0x398c, 0x36ba, 0x33de,
0x30fb, 0x2e11, 0x2b1f, 0x2826, 0x2528, 0x2223, 0x1f19, 0x1c0b,
0x18f8, 0x15e2, 0x12c8, 0x0fab, 0x0c8b, 0x096a, 0x0647, 0x0324,
0x0000, 0xfcdc, 0xf9b9, 0xf696, 0xf375, 0xf055, 0xed38, 0xea1e,
0xe708, 0xe3f5, 0xe0e7, 0xdddd, 0xdad8, 0xd7da, 0xd4e1, 0xd1ef,
0xcf05, 0xcc22, 0xc946, 0xc674, 0xc3aa, 0xc0e9, 0xbe32, 0xbb86,
0xb8e4, 0xb64c, 0xb3c1, 0xb141, 0xaecd, 0xac65, 0xaa0b, 0xa7be,
0xa57e, 0xa34c, 0xa129, 0x9f14, 0x9d0e, 0x9b18, 0x9931, 0x975a,
0x9593, 0x93dc, 0x9236, 0x90a1, 0x8f1e, 0x8dab, 0x8c4b, 0x8afc,
0x89bf, 0x8894, 0x877c, 0x8676, 0x8583, 0x84a3, 0x83d7, 0x831d,
0x8276, 0x81e3, 0x8163, 0x80f7, 0x809e, 0x8059, 0x8028, 0x800a,
0x8000, 0x800a, 0x8028, 0x8059, 0x809e, 0x80f7, 0x8163, 0x81e3,
0x8276, 0x831d, 0x83d7, 0x84a3, 0x8583, 0x8676, 0x877c, 0x8894,
0x89bf, 0x8afc, 0x8c4b, 0x8dab, 0x8f1e, 0x90a1, 0x9236, 0x93dc,
0x9593, 0x975a, 0x9931, 0x9b18, 0x9d0e, 0x9f14, 0xa129, 0xa34c,
0xa57e, 0xa7be, 0xaa0b, 0xac65, 0xaecd, 0xb141, 0xb3c1, 0xb64c,
0xb8e4, 0xbb86, 0xbe32, 0xc0e9, 0xc3aa, 0xc674, 0xc946, 0xcc22,
0xcf05, 0xd1ef, 0xd4e1, 0xd7da, 0xdad8, 0xdddd, 0xe0e7, 0xe3f5,
0xe708, 0xea1e, 0xed38, 0xf055, 0xf375, 0xf696, 0xf9b9, 0xfcdc
};
void MRT_IRQHandler(void) {
pinMode(3,INPUT);
if ( LPC_MRT->Channel[0].STAT & MRT_STAT_IRQ_FLAG ) {
LPC_MRT->Channel[0].STAT = MRT_STAT_IRQ_FLAG; /* clear interrupt flag */
//-------------------- 3 DCO block ------------------------------------------
DCO1 += FREQ1; //increment phase accumulator1 with phase1
DCO2 += FREQ2; //increment phase accumulator2 with phase2
DCO3 += FREQ3; //increment phase accumulator3 with phase3
switch (WAVEFORM) {
case SAW:
DCO=((((DCO1 & 0x7FFFFF)+(DCO2 & 0x7FFFFF)+(DCO3 & 0x7FFFFF))/65536)-192)*171;
break;
case SQUARE:
DCO=((((DCO1 & 0x400000)+(DCO2 & 0x400000)+(DCO3 & 0x400000))/65536)-96)*341;
break;
case SINE:
DCO=(sine[(DCO1>>15)&255]/4)+(sine[(DCO2>>15)&255]/4)+(sine[(DCO3>>15)&255]/4);
break;
}
//---------------------------------------------------------------------------
//---------------- DCF block ---------------------------------------
//C implementation for a 4pole lowpass DCF with resonance:
vi=DCO;
vo = ((vo * (65536 - CUTOFF)) + (t1 * CUTOFF))/65536; //+3db
t1 = ((t1 * (65536 - CUTOFF)) + (t2 * CUTOFF))/65536; //+6db
t2 = ((t2 * (65536 - CUTOFF)) + (t3 * CUTOFF))/65536; //+9db
t3 = ((t3 * (65536 - CUTOFF)) + (t4 * CUTOFF))/65536; //+12db
t4 = ((t4 * (65536 - CUTOFF)) + (t5 * CUTOFF))/65536; //+15db
t5 = ((t5 * (65536 - CUTOFF)) + (t6 * CUTOFF))/65536; //+18db
t6 = ((t6 * (65536 - CUTOFF)) + (t7 * CUTOFF))/65536; //+21db
t7 = ((t7 * (65536 - CUTOFF)) + (t8 * CUTOFF))/65536; //+24db
t8 = vi-((vo*RESONANCE)/65536); //resonance feedback
DCF=vo;
//-----------------------------------------------------------------
//--------------------- ENV block ---------------------------------
if (!envstat--) {
envstat=1024;
if (TRIG>0) {
if (state==0) {
volume += ATTACK;
if (volume>0xFFFF) {
volume=0xFFFF;
state++;
}
}
if (state==1) {
volume -= DECAY;
if (volume<SUSTAIN) {
volume=SUSTAIN;
state++;
}
}
else {
volume -= RELEASE;
if (volume<0) {
volume=0;
}
}
}
}
ENV=(volume*DCF)>>16;
//-----------------------------------------------------------------
DAC=0x8000+ENV;
}
pinMode(3,OUTPUT);
return;
}
void mrtInit(uint32_t delay)
{
/* Enable clock to MRT and reset the MRT peripheral */
LPC_SYSCON->SYSAHBCLKCTRL |= (0x1<<10);
LPC_SYSCON->PRESETCTRL &= ~(0x1<<7);
LPC_SYSCON->PRESETCTRL |= (0x1<<7);
LPC_MRT->Channel[0].INTVAL = delay;
LPC_MRT->Channel[0].INTVAL |= 0x1UL<<31;
LPC_MRT->Channel[0].CTRL = MRT_REPEATED_MODE|MRT_INT_ENA;
/* Enable the MRT Interrupt */
#if NMI_ENABLED
NVIC_DisableIRQ( MRT_IRQn );
NMI_Init( MRT_IRQn );
#else
NVIC_EnableIRQ(MRT_IRQn);
#endif
return;
}
int main(void)
{
/* Initialise the GPIO block */
/* Enable AHB clock to the GPIO domain. */
LPC_SYSCON->SYSAHBCLKCTRL |= (1 << 6);
LPC_SYSCON->PRESETCTRL &= ~(1 << 10);
LPC_SYSCON->PRESETCTRL |= (1 << 10);
LPC_SWM->PINENABLE0 = 0xffffffffUL; //All 6 GPIO enabled
/* Configure the multi-rate timer for 44K ticks */
mrtInit(__SYSTEM_CLOCK/33488);
pinMode(3,OUTPUT);
while(1) {
asm volatile(
" mov r0,r3 \n"
" mov r1,#0xA0 \n"
" lsl r1,#24 \n" //r1=0xA000
" mov r2,#0x10 \n"
" lsl r2,#8 \n"
" add r2,#0x0C \n" //r2=0x100C
" add r1,r2 \n" //r1=0xA000100C LPC_GPIO_PORT->W0[3]
" mov r6,#0xFF \n"
" lsl r6,#8 \n"
" add r6,#0xFF \n" //r6=0xFFFF
" mov r5,#0x01 \n"
" lsl r5,#16 \n" //r5=0x00010000
"loop%=: ldrh r7,[r0,#0] \n"
" add r2,r7 \n" //r2=phacc
" mov r4,r2 \n"
" and r4, r5 \n"
" str r4, [r1] \n" //LPC_GPIO_PORT->W0[3]
" and r2, r6 \n"
" mov r4,#0 \n"
" str r4, [r1] \n" //LPC_GPIO_PORT->W0[3]
" b loop%= \n"
: [dac] "=m" (DAC)
);
}
}
FFT plot of the Roland SuperSaw
The fractional detune
The dsp-G1
//**************************************************************************/
// Synth main.c with Arduino style definitions
//**************************************************************************/
#include "wiring.h"
#include "LPC8xx.h"
/* Control register bit definition. */
#define MRT_INT_ENA (0x1<<0)
#define MRT_REPEATED_MODE (0x00<<1)
#define MRT_ONE_SHOT_INT (0x01<<1)
#define MRT_ONE_SHOT_STALL (0x02<<1)
/* Status register bit definition */
#define MRT_STAT_IRQ_FLAG (0x1<<0)
#define MRT_STAT_RUN (0x1<<1)
int32_t err=0;
volatile int32_t DAC=0;
uint8_t phase=0;
int16_t sine[256] = {
0x0000, 0x0324, 0x0647, 0x096a, 0x0c8b, 0x0fab, 0x12c8, 0x15e2,
0x18f8, 0x1c0b, 0x1f19, 0x2223, 0x2528, 0x2826, 0x2b1f, 0x2e11,
0x30fb, 0x33de, 0x36ba, 0x398c, 0x3c56, 0x3f17, 0x41ce, 0x447a,
0x471c, 0x49b4, 0x4c3f, 0x4ebf, 0x5133, 0x539b, 0x55f5, 0x5842,
0x5a82, 0x5cb4, 0x5ed7, 0x60ec, 0x62f2, 0x64e8, 0x66cf, 0x68a6,
0x6a6d, 0x6c24, 0x6dca, 0x6f5f, 0x70e2, 0x7255, 0x73b5, 0x7504,
0x7641, 0x776c, 0x7884, 0x798a, 0x7a7d, 0x7b5d, 0x7c29, 0x7ce3,
0x7d8a, 0x7e1d, 0x7e9d, 0x7f09, 0x7f62, 0x7fa7, 0x7fd8, 0x7ff6,
0x7fff, 0x7ff6, 0x7fd8, 0x7fa7, 0x7f62, 0x7f09, 0x7e9d, 0x7e1d,
0x7d8a, 0x7ce3, 0x7c29, 0x7b5d, 0x7a7d, 0x798a, 0x7884, 0x776c,
0x7641, 0x7504, 0x73b5, 0x7255, 0x70e2, 0x6f5f, 0x6dca, 0x6c24,
0x6a6d, 0x68a6, 0x66cf, 0x64e8, 0x62f2, 0x60ec, 0x5ed7, 0x5cb4,
0x5a82, 0x5842, 0x55f5, 0x539b, 0x5133, 0x4ebf, 0x4c3f, 0x49b4,
0x471c, 0x447a, 0x41ce, 0x3f17, 0x3c56, 0x398c, 0x36ba, 0x33de,
0x30fb, 0x2e11, 0x2b1f, 0x2826, 0x2528, 0x2223, 0x1f19, 0x1c0b,
0x18f8, 0x15e2, 0x12c8, 0x0fab, 0x0c8b, 0x096a, 0x0647, 0x0324,
0x0000, 0xfcdc, 0xf9b9, 0xf696, 0xf375, 0xf055, 0xed38, 0xea1e,
0xe708, 0xe3f5, 0xe0e7, 0xdddd, 0xdad8, 0xd7da, 0xd4e1, 0xd1ef,
0xcf05, 0xcc22, 0xc946, 0xc674, 0xc3aa, 0xc0e9, 0xbe32, 0xbb86,
0xb8e4, 0xb64c, 0xb3c1, 0xb141, 0xaecd, 0xac65, 0xaa0b, 0xa7be,
0xa57e, 0xa34c, 0xa129, 0x9f14, 0x9d0e, 0x9b18, 0x9931, 0x975a,
0x9593, 0x93dc, 0x9236, 0x90a1, 0x8f1e, 0x8dab, 0x8c4b, 0x8afc,
0x89bf, 0x8894, 0x877c, 0x8676, 0x8583, 0x84a3, 0x83d7, 0x831d,
0x8276, 0x81e3, 0x8163, 0x80f7, 0x809e, 0x8059, 0x8028, 0x800a,
0x8000, 0x800a, 0x8028, 0x8059, 0x809e, 0x80f7, 0x8163, 0x81e3,
0x8276, 0x831d, 0x83d7, 0x84a3, 0x8583, 0x8676, 0x877c, 0x8894,
0x89bf, 0x8afc, 0x8c4b, 0x8dab, 0x8f1e, 0x90a1, 0x9236, 0x93dc,
0x9593, 0x975a, 0x9931, 0x9b18, 0x9d0e, 0x9f14, 0xa129, 0xa34c,
0xa57e, 0xa7be, 0xaa0b, 0xac65, 0xaecd, 0xb141, 0xb3c1, 0xb64c,
0xb8e4, 0xbb86, 0xbe32, 0xc0e9, 0xc3aa, 0xc674, 0xc946, 0xcc22,
0xcf05, 0xd1ef, 0xd4e1, 0xd7da, 0xdad8, 0xdddd, 0xe0e7, 0xe3f5,
0xe708, 0xea1e, 0xed38, 0xf055, 0xf375, 0xf696, 0xf9b9, 0xfcdc
};
void MRT_IRQHandler(void) {
pinMode(3,INPUT);
if ( LPC_MRT->Channel[0].STAT & MRT_STAT_IRQ_FLAG ) {
LPC_MRT->Channel[0].STAT = MRT_STAT_IRQ_FLAG; /* clear interrupt flag */
DAC=0x8000+sine[phase++];
}
pinMode(3,OUTPUT);
return;
}
void mrtInit(uint32_t delay)
{
/* Enable clock to MRT and reset the MRT peripheral */
LPC_SYSCON->SYSAHBCLKCTRL |= (0x1<<10);
LPC_SYSCON->PRESETCTRL &= ~(0x1<<7);
LPC_SYSCON->PRESETCTRL |= (0x1<<7);
LPC_MRT->Channel[0].INTVAL = delay;
LPC_MRT->Channel[0].INTVAL |= 0x1UL<<31;
LPC_MRT->Channel[0].CTRL = MRT_REPEATED_MODE|MRT_INT_ENA;
/* Enable the MRT Interrupt */
#if NMI_ENABLED
NVIC_DisableIRQ( MRT_IRQn );
NMI_Init( MRT_IRQn );
#else
NVIC_EnableIRQ(MRT_IRQn);
#endif
return;
}
int main(void)
{
/* Initialise the GPIO block */
/* Enable AHB clock to the GPIO domain. */
LPC_SYSCON->SYSAHBCLKCTRL |= (1 << 6);
LPC_SYSCON->PRESETCTRL &= ~(1 << 10);
LPC_SYSCON->PRESETCTRL |= (1 << 10);
LPC_SWM->PINENABLE0 = 0xffffffffUL; //All 6 GPIO enabled
/* Configure the multi-rate timer for 44K ticks */
mrtInit(__SYSTEM_CLOCK/44100);
pinMode(3,OUTPUT);
while(1) {
if(DAC >= err) {
digitalWrite(3,HIGH);
err += 0xFFFF-DAC;
}
else
{
digitalWrite(3,LOW);
err -= DAC;
}
}
}
//---------------- DCF block ---------------------------------------
//C implementation for a 4pole lowpass DCF with resonance:
vi=DCO;
vo = ((vo * (65536 - f)) + (t1 * f))/65536; //+3db
t1 = ((t1 * (65536 - f)) + (t2 * f))/65536; //+6db
t2 = ((t2 * (65536 - f)) + (t3 * f))/65536; //+9db
t3 = ((t3 * (65536 - f)) + (t4 * f))/65536; //+12db
t4 = ((t4 * (65536 - f)) + (t5 * f))/65536; //+15db
t5 = ((t5 * (65536 - f)) + (t6 * f))/65536; //+18db
t6 = ((t6 * (65536 - f)) + (t7 * f))/65536; //+21db
t7 = ((t7 * (65536 - f)) + (t8 * f))/65536; //+24db
t8 = vi-((vo*q)/65536); //resonance feedback
if (vo>32767) vo=32767;
if (vo<-32767) vo=-32767;
DCF=vo;
//-----------------------------------------------------------------
//-------------------- 3 DCO block ------------------------------------------
DCO1 += FREQ1; //increment phase accumulator1 with phase1
DCO2 += FREQ2; //increment phase accumulator2 with phase2
DCO3 += FREQ3; //increment phase accumulator3 with phase3
DCO=((((DCO1 & 0x7F0000)+(DCO2 & 0x7F0000)+(DCO3 & 0x7F0000))/65536)-192)*171;
//---------------------------------------------------------------------------
//--------------------- ENV block ---------------------------------
if (!envstat--) {
envstat=1024;
if (TRIG>0) {
if (state==0) {
volume += ATTACK;
if (volume>0xFFFF) {
volume=0xFFFF;
state++;
}
}
if (state==1) {
volume -= DECAY;
if (volume<SUSTAIN) {
volume=SUSTAIN;
state++;
}
}
else {
volume -= RELEASE;
if (volume<0) {
volume=0;
}
}
}
}
ENV=(volume*DCF)>>16;
//-----------------------------------------------------------------
roland_sawstring_demo_on_the_lpc810_micro_synth.c
C/C++//**************************************************************************/
// Analog Modeling Synth main code
//**************************************************************************/
#include "wiring.h"
#include "LPC8xx.h"
/* Control register bit definition. */
#define MRT_INT_ENA (0x1<<0)
#define MRT_REPEATED_MODE (0x00<<1)
#define MRT_ONE_SHOT_INT (0x01<<1)
#define MRT_ONE_SHOT_STALL (0x02<<1)
/* Status register bit definition */
#define MRT_STAT_IRQ_FLAG (0x1<<0)
#define MRT_STAT_RUN (0x1<<1)
#define SAW 0
#define SQUARE 1
#define SINE 2
//-------- Synth parameters --------------
uint8_t WAVEFORM=SAW; //SAW/SQUARE/SINE
uint32_t FREQ1 = 0x10000; //0x10000=C4
uint32_t FREQ2 = 0x10100; //0x10000=C4
uint32_t FREQ3 = 0x0FF00; //0x10000=C4
int32_t CUTOFF=65535; //freq 0-65535
int32_t RESONANCE=0; //reso=0-65535
uint8_t TRIG=1; //MIDItrig 1=note ON
uint32_t ATTACK=98; //Attackrate in 30mS steps
uint32_t DECAY=98; //Decayrate in 30mS steps
int32_t SUSTAIN=0xFFFF; //Sustainlevel 0-65535
uint32_t RELEASE=1; //Releaserate in 30mS steps
//-----------------------------------------
volatile uint16_t DAC=0;
uint32_t DCO1;
uint32_t DCO2;
uint32_t DCO3;
uint8_t state=0; //needs to be reset on a new trig
uint16_t envstat=1; //needs to be reset on a new trig
int32_t volume=0;
int32_t t1;
int32_t t2;
int32_t t3;
int32_t t4;
int32_t t5;
int32_t t6;
int32_t t7;
int32_t t8;
int32_t vi;
int32_t vo;
int16_t DCO;
int16_t DCF;
int32_t ENV;
int16_t LFO;
uint16_t lfocounter=1;
uint8_t phase;
const int16_t sine[256] = {
0x0000, 0x0324, 0x0647, 0x096a, 0x0c8b, 0x0fab, 0x12c8, 0x15e2,
0x18f8, 0x1c0b, 0x1f19, 0x2223, 0x2528, 0x2826, 0x2b1f, 0x2e11,
0x30fb, 0x33de, 0x36ba, 0x398c, 0x3c56, 0x3f17, 0x41ce, 0x447a,
0x471c, 0x49b4, 0x4c3f, 0x4ebf, 0x5133, 0x539b, 0x55f5, 0x5842,
0x5a82, 0x5cb4, 0x5ed7, 0x60ec, 0x62f2, 0x64e8, 0x66cf, 0x68a6,
0x6a6d, 0x6c24, 0x6dca, 0x6f5f, 0x70e2, 0x7255, 0x73b5, 0x7504,
0x7641, 0x776c, 0x7884, 0x798a, 0x7a7d, 0x7b5d, 0x7c29, 0x7ce3,
0x7d8a, 0x7e1d, 0x7e9d, 0x7f09, 0x7f62, 0x7fa7, 0x7fd8, 0x7ff6,
0x7fff, 0x7ff6, 0x7fd8, 0x7fa7, 0x7f62, 0x7f09, 0x7e9d, 0x7e1d,
0x7d8a, 0x7ce3, 0x7c29, 0x7b5d, 0x7a7d, 0x798a, 0x7884, 0x776c,
0x7641, 0x7504, 0x73b5, 0x7255, 0x70e2, 0x6f5f, 0x6dca, 0x6c24,
0x6a6d, 0x68a6, 0x66cf, 0x64e8, 0x62f2, 0x60ec, 0x5ed7, 0x5cb4,
0x5a82, 0x5842, 0x55f5, 0x539b, 0x5133, 0x4ebf, 0x4c3f, 0x49b4,
0x471c, 0x447a, 0x41ce, 0x3f17, 0x3c56, 0x398c, 0x36ba, 0x33de,
0x30fb, 0x2e11, 0x2b1f, 0x2826, 0x2528, 0x2223, 0x1f19, 0x1c0b,
0x18f8, 0x15e2, 0x12c8, 0x0fab, 0x0c8b, 0x096a, 0x0647, 0x0324,
0x0000, 0xfcdc, 0xf9b9, 0xf696, 0xf375, 0xf055, 0xed38, 0xea1e,
0xe708, 0xe3f5, 0xe0e7, 0xdddd, 0xdad8, 0xd7da, 0xd4e1, 0xd1ef,
0xcf05, 0xcc22, 0xc946, 0xc674, 0xc3aa, 0xc0e9, 0xbe32, 0xbb86,
0xb8e4, 0xb64c, 0xb3c1, 0xb141, 0xaecd, 0xac65, 0xaa0b, 0xa7be,
0xa57e, 0xa34c, 0xa129, 0x9f14, 0x9d0e, 0x9b18, 0x9931, 0x975a,
0x9593, 0x93dc, 0x9236, 0x90a1, 0x8f1e, 0x8dab, 0x8c4b, 0x8afc,
0x89bf, 0x8894, 0x877c, 0x8676, 0x8583, 0x84a3, 0x83d7, 0x831d,
0x8276, 0x81e3, 0x8163, 0x80f7, 0x809e, 0x8059, 0x8028, 0x800a,
0x8000, 0x800a, 0x8028, 0x8059, 0x809e, 0x80f7, 0x8163, 0x81e3,
0x8276, 0x831d, 0x83d7, 0x84a3, 0x8583, 0x8676, 0x877c, 0x8894,
0x89bf, 0x8afc, 0x8c4b, 0x8dab, 0x8f1e, 0x90a1, 0x9236, 0x93dc,
0x9593, 0x975a, 0x9931, 0x9b18, 0x9d0e, 0x9f14, 0xa129, 0xa34c,
0xa57e, 0xa7be, 0xaa0b, 0xac65, 0xaecd, 0xb141, 0xb3c1, 0xb64c,
0xb8e4, 0xbb86, 0xbe32, 0xc0e9, 0xc3aa, 0xc674, 0xc946, 0xcc22,
0xcf05, 0xd1ef, 0xd4e1, 0xd7da, 0xdad8, 0xdddd, 0xe0e7, 0xe3f5,
0xe708, 0xea1e, 0xed38, 0xf055, 0xf375, 0xf696, 0xf9b9, 0xfcdc
};
void MRT_IRQHandler(void) {
pinMode(3,INPUT);
if ( LPC_MRT->Channel[0].STAT & MRT_STAT_IRQ_FLAG ) {
LPC_MRT->Channel[0].STAT = MRT_STAT_IRQ_FLAG; /* clear interrupt flag */
//-------------------- 3 DCO block ------------------------------------------
DCO1 += FREQ1; //increment phase accumulator1 with phase1
DCO2 += FREQ2; //increment phase accumulator2 with phase2
DCO3 += FREQ3; //increment phase accumulator3 with phase3
switch (WAVEFORM) {
case SAW:
DCO=((((DCO1 & 0x7FFFFF)+(DCO2 & 0x7FFFFF)+(DCO3 & 0x7FFFFF))/65536)-192)*171;
break;
case SQUARE:
DCO=((((DCO1 & 0x400000)+(DCO2 & 0x400000)+(DCO3 & 0x400000))/65536)-96)*341;
break;
case SINE:
DCO=(sine[(DCO1>>15)&255]/4)+(sine[(DCO2>>15)&255]/4)+(sine[(DCO3>>15)&255]/4);
break;
}
//---------------------------------------------------------------------------
//---------------- DCF block ---------------------------------------
//C implementation for a 4pole lowpass DCF with resonance:
vi=DCO;
vo = ((vo * (65536 - CUTOFF)) + (t1 * CUTOFF))/65536; //+3db
t1 = ((t1 * (65536 - CUTOFF)) + (t2 * CUTOFF))/65536; //+6db
t2 = ((t2 * (65536 - CUTOFF)) + (t3 * CUTOFF))/65536; //+9db
t3 = ((t3 * (65536 - CUTOFF)) + (t4 * CUTOFF))/65536; //+12db
t4 = ((t4 * (65536 - CUTOFF)) + (t5 * CUTOFF))/65536; //+15db
t5 = ((t5 * (65536 - CUTOFF)) + (t6 * CUTOFF))/65536; //+18db
t6 = ((t6 * (65536 - CUTOFF)) + (t7 * CUTOFF))/65536; //+21db
t7 = ((t7 * (65536 - CUTOFF)) + (t8 * CUTOFF))/65536; //+24db
t8 = vi-((vo*RESONANCE)/65536); //resonance feedback
DCF=vo;
//-----------------------------------------------------------------
//--------------------- ENV block ---------------------------------
if (!envstat--) {
envstat=1024;
if (TRIG>0) {
if (state==0) {
volume += ATTACK;
if (volume>0xFFFF) {
volume=0xFFFF;
state++;
}
}
if (state==1) {
volume -= DECAY;
if (volume<SUSTAIN) {
volume=SUSTAIN;
state++;
}
}
else {
volume -= RELEASE;
if (volume<0) {
volume=0;
}
}
}
}
ENV=(volume*DCF)>>16;
//-----------------------------------------------------------------
DAC=0x8000+ENV;
}
pinMode(3,OUTPUT);
return;
}
void mrtInit(uint32_t delay)
{
/* Enable clock to MRT and reset the MRT peripheral */
LPC_SYSCON->SYSAHBCLKCTRL |= (0x1<<10);
LPC_SYSCON->PRESETCTRL &= ~(0x1<<7);
LPC_SYSCON->PRESETCTRL |= (0x1<<7);
LPC_MRT->Channel[0].INTVAL = delay;
LPC_MRT->Channel[0].INTVAL |= 0x1UL<<31;
LPC_MRT->Channel[0].CTRL = MRT_REPEATED_MODE|MRT_INT_ENA;
/* Enable the MRT Interrupt */
#if NMI_ENABLED
NVIC_DisableIRQ( MRT_IRQn );
NMI_Init( MRT_IRQn );
#else
NVIC_EnableIRQ(MRT_IRQn);
#endif
return;
}
int main(void)
{
/* Initialise the GPIO block */
/* Enable AHB clock to the GPIO domain. */
LPC_SYSCON->SYSAHBCLKCTRL |= (1 << 6);
LPC_SYSCON->PRESETCTRL &= ~(1 << 10);
LPC_SYSCON->PRESETCTRL |= (1 << 10);
LPC_SWM->PINENABLE0 = 0xffffffffUL; //All 6 GPIO enabled
/* Configure the multi-rate timer for 44K ticks */
mrtInit(__SYSTEM_CLOCK/33488);
pinMode(3,OUTPUT);
while(1) {
asm volatile(
" mov r0,r3 \n"
" mov r1,#0xA0 \n"
" lsl r1,#24 \n" //r1=0xA000
" mov r2,#0x10 \n"
" lsl r2,#8 \n"
" add r2,#0x0C \n" //r2=0x100C
" add r1,r2 \n" //r1=0xA000100C LPC_GPIO_PORT->W0[3]
" mov r6,#0xFF \n"
" lsl r6,#8 \n"
" add r6,#0xFF \n" //r6=0xFFFF
" mov r5,#0x01 \n"
" lsl r5,#16 \n" //r5=0x00010000
"loop%=: ldrh r7,[r0,#0] \n"
" add r2,r7 \n" //r2=phacc
" mov r4,r2 \n"
" and r4, r5 \n"
" str r4, [r1] \n" //LPC_GPIO_PORT->W0[3]
" and r2, r6 \n"
" mov r4,#0 \n"
" str r4, [r1] \n" //LPC_GPIO_PORT->W0[3]
" b loop%= \n"
: [dac] "=m" (DAC)
);
}
}
Comments
Please log in or sign up to comment.