Go Down

Topic: The Catweazle (Read 6 times) previous topic - next topic

janost

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?

fungus


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

No, I don't answer questions sent in private messages (but I do accept thank-you notes...)

PaulRB


Like this?


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

janost

#33
Mar 18, 2014, 07:35 pm Last Edit: Mar 18, 2014, 07:37 pm by janost Reason: 1
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.

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

janost


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.

janost

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.

mrburnette

Quote
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?

Quote
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

janost

#38
Mar 20, 2014, 01:23 pm Last Edit: Mar 20, 2014, 01:35 pm by janost Reason: 1

Quote
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?

Quote
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 :)

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.

mrburnette

Quote
Otherwise it looks like any other Arduino program written in C


I spewed coffee reading that  :smiley-eek-blue:

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


janost


Quote
Otherwise it looks like any other Arduino program written in C


I spewed coffee reading that  :smiley-eek-blue:

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






:)

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

Only then, you are forced to become clever.

janost

#41
Mar 20, 2014, 10:16 pm Last Edit: Mar 20, 2014, 10:44 pm by janost Reason: 1
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.

Code: [Select]

//**************************************************************************/
//    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)
         );

     
      }
}



janost

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 :)

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

fungus

#43
Mar 21, 2014, 08:13 pm Last Edit: Mar 21, 2014, 08:22 pm by fungus Reason: 1

Bet you cant do that on the Tiny85 :)


Hmmmm.....

<rubs chin>

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.   :(

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.

No, I don't answer questions sent in private messages (but I do accept thank-you notes...)

janost

#44
Mar 22, 2014, 02:01 am Last Edit: Mar 22, 2014, 02:43 am by janost Reason: 1


Bet you cant do that on the Tiny85 :)


Hmmmm.....

<rubs chin>

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.   :(

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.   :(
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?



Go Up