The Catweazle

Yes, I'm selling it premounted with the USB/Serial programming cable.

But you know this site? This will probably get removed.

PM me?

The 2MB flash is not really required.

It uses GPIO0_17 as chipselect and 2 more GPIO for SIO and CLK.
It's fast and can read sustained data at 80MHz serial Clock.

And great DSP applications arent that great without a good chunk of memory?

PaulRB:
I guess that's just a standard 16 pin SMD breakout board. But that's all you need, really. There's not much else I would like to put on a small board. As separate serial comms header, like janost's board. A 3.3V regulator to allow flexibility for external power, and to provide regulated power for other components.

Like this?

http://oshpark.com/shared_projects/rQra0bCX

fungus:
Like this?

Yes, but I don't think I would be able to solder it up....

He can't solder TSSOP but he might be able to solder SOIC.
My PCB is SOIC with a TO-92 3.3v regulator and ordinary resistors and caps.

I plan to sell it with at least the LPC812 premounted.

But the LPC810 is an ordinary DIP8 and even simpler to put together.
Still the same performance but only 4K flash, 1K SRAM and 6GPIO.

It has the same onboard perpherials.

Yes, its a shame they are not making the 812 in a DIP16 or DIP20.

Janost, the most obvious rival in the market for Catweazle would been the Teensy 3.1, would you agree? Its faster and has more resources, at first glance. What would you see as the advantages of Catweazle vs. Teensy?

Paul

PaulRB:
Yes, its a shame they are not making the 812 in a DIP16 or DIP20.

Janost, the most obvious rival in the market for Catweazle would been the Teensy 3.1, would you agree? Its faster and has more resources, at first glance. What would you see as the advantages of Catweazle vs. Teensy?

Paul

The simplicity in the DIY breadboard /soldering and the price.
Looking at it like that you have the DUE already integrated into the Arduino IDE.

It has speed, memory and lots of GPIO.
It also has a nasty pricetag.

If you want it in DIP28 there is the LPC1114 but it cost's 8 times more than the LPC812.

I wrote an Analog Modeling Synthesizer on the LPC810 to show of its capabilities.
It has MIDI in, 3 detuneable oscillators, 24db LP-filter with resonance, envelope and LFO for modulation and a DAC at 16bits 44.1Khz

It still has 2K flash, 4GPIO and 1000bytes SRAM free.

That shows how effective the code usage is.

That shows how effective the code usage is.

Yes, but newbies should not expect to be able to write professional, efficient code day-one. Are you building up a set of libraries that would perform the common tasks for new to intermediate users?

MIDI in, 3 detuneable oscillators, 24db LP-filter with resonance, envelope and LFO for modulation and a DAC at 16bits 44.1Khz

... and I am impressed... but I still attribute this to programming skill as much, or more, than the uC capability.

Ray

mrburnette:

That shows how effective the code usage is.

Yes, but newbies should not expect to be able to write professional, efficient code day-one. Are you building up a set of libraries that would perform the common tasks for new to intermediate users?

MIDI in, 3 detuneable oscillators, 24db LP-filter with resonance, envelope and LFO for modulation and a DAC at 16bits 44.1Khz

... and I am impressed... but I still attribute this to programming skill as much, or more, than the uC capability.

Ray

Out of those 2024bytes of flash used, 512bytes is a 256step int16 lookup array for a sinewave :slight_smile:

The only librarycode I wrote is the wiring.c to be able to use pinmode, digitalwrite and digitalread instead of the LPCs confusing portmanipulation registers.

The only inline arm assembly code is the PDM DAC loop because it needs a high oversampling rate to produce 16bit resolution at 44.1KHz.

Otherwise it looks like any other Arduino program written in C

But I get your point, Ray.
The 4pole resonant filter algorithm written in floatingpoint consumes 2K alone.
And the lpc810 doesn't have a floatingpoint coprocessor so It was rewritten using 32bit integer code.

Otherwise it looks like any other Arduino program written in C

I spewed coffee reading that :fearful:

Yea, those "otherwise" statements are what make for bald newbies! Some professional programmers are even know to pull their hair when faced with a new architecture. A well-seasoned programmer is one that shaves his hair before every new assignment so that frustration is now visible from loose hair in the cubicle.

Ray

mrburnette:

Otherwise it looks like any other Arduino program written in C

I spewed coffee reading that :fearful:

Yea, those "otherwise" statements are what make for bald newbies! Some professional programmers are even know to pull their hair when faced with a new architecture. A well-seasoned programmer is one that shaves his hair before every new assignment so that frustration is now visible from loose hair in the cubicle.

Ray

:slight_smile:

You may waste all your resources until you run out of them.

Only then, you are forced to become clever.

This is the code for my LPC810 analog modeling synthesizer found here:

http://www.hackster.io/janost/micro-virtual-analog-synthesizer

It compiles to 1956bytes and would certainly compile in the Arduino IDE by just changing the LPC hardware references to ATmega equals without problems.

//**************************************************************************/
//    LPC810 Analog Modeling Synth main.c 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)
    	    		  );

    	  
       }
}

I guess I have to make this an Arduino shield to keep writing here?

The synth project is turning into a full fledged Analog synthesizer in a 8-pin DIP chip.
Just 5 external components to build a complete MIDI synthesizer.

Bet you cant do that on the Tiny85 :slight_smile:

(Or you could but it wont be hifi or 8 voice polyphony)

janost:
Bet you cant do that on the Tiny85 :slight_smile:

Hmmmm.....

It can do the DAC in hardware so there's no time wasted in interrupts: http://www.atmel.com/Images/doc2542.pdf

There's no hardware multiply instruction like in the Megas. That would be a heavy cost in a synthesizer. :frowning:

Still, let's do the math:

At 44.1kHz we have 362.8 clock cycles per sample (assuming 16MHz clock).

Three wavetable lookups - no big deal, call it 32 clock cycles.
ADSR - One 8bit x 16bit multiply - call it 64 clock cycles.
A bit of logic to update pointers, etc.
About 250 clock cycles left over for the filter?

All the numbers are multiplied by the same factors so the branching logic in the loop can work for all of them at once (do them in parallel).

I wouldn't say it was impossible...

Edit: Do we really need 16-bit accuracy? Surely 12-bit math would be enough...and that would make a BIG difference to the clock cycles.

fungus:

janost:
Bet you cant do that on the Tiny85 :slight_smile:

Hmmmm.....

It can do the DAC in hardware so there's no time wasted in interrupts: http://www.atmel.com/Images/doc2542.pdf

There's no hardware multiply instruction like in the Megas. That would be a heavy cost in a synthesizer. :frowning:

Still, let's do the math:

At 44.1kHz we have 362.8 clock cycles per sample (assuming 16MHz clock).

Three wavetable lookups - no big deal, call it 32 clock cycles.
ADSR - One 8bit x 16bit multiply - call it 64 clock cycles.
A bit of logic to update pointers, etc.
About 250 clock cycles left over for the filter?

All the numbers are multiplied by the same factors so the branching logic in the loop can work for all of them at once (do them in parallel).

I wouldn't say it was impossible...

Edit: Do we really need 16-bit accuracy? Surely 12-bit math would be enough...and that would make a BIG difference to the clock cycles.

Three wavetable lookups - no big deal, call it 32 clock cycles.
No sweat, any MCU can do that.

There's no hardware multiply instruction like in the Megas. That would be a heavy cost in a synthesizer. :frowning:
The ARM Cortex M0+ has a 32x32bit multiply in a single cycle.

About 250 clock cycles left over for the filter?
You have seen the filter, that is 32 at the least 16bit multiplies making a good sound.

Yes, you need 32-bit accuracy.
At the least in audio applications.

How long more are you going to count cycles?

My code will get expanded to 8 voices.
Is that going to work on the ATmega?

In 8-bits?

Your lost, Fungus, lost in your 8bit fantasy.
We cant spend time in cycle counting.

There are other MCUs that gives one hell of speed increase.
Let the 8bit platform go.

Edit: The code has no problem running on the Arduino Due?

janost:
My code will get expanded to 8 voices.
Is that going to work on the ATmega?

That wasn't the bet...the bet was to reproduce your current version. Moving the goalposts makes me think you're worried about your money. :roll_eyes:

(Will yours still work with 8 voices? Won't a huge 8-channel interrupt disrupt your "DAC"?)

PS: If the Tiny85 had a hardware multiply then it could do quite a few channels, no problem. Basically your argument is that the Tiny85 hasn't got a hardware multiplier.

I could always modify my synth code to use hardware PWM.
But it wont be 16bits anymore.

And it already runs 8 voices (24 oscillators) but in paraphony.
24 DCO, 1 ENV and 1 DCF.

Yes, I will use the ATmega to what its good for, scanning knobs and buttons.
The LPC doesn't have any ADC.

janost:
Yes, I will use the ATmega to what its good for, scanning knobs and buttons.
The LPC doesn't have any ADC.

My Space Invaders has 4 channel sound mixed at 44.1kHz (as well as running the game, obviously). It uses about 50% CPU on a 16MHz Mega328 (Pro Mini).