Wednesday, April 24, 2013

Problem reading RC Channels - The RCArduino Loop Back Test

Many people experience problems adding additional channels to the RCArduino test sketches or porting them to new devices, its usually down to mistakes in the way the additional channels are coded

This post presents an example of managing 8 input/output channels, its a very simple sketch which outputs 8 RC Channels and reads them right back in again.

The sketch can be used in three steps -

1) Initially the sketch outputs fixed values for the servos and reads them back in as a loop back test.

2) Once you are comfortable with the code, you can replace one or more of the loop back connections to start reading RC Channels. 

3) Once you are confident that the sketch is reading your channels correctly you can connect your servos and ESCs to the outputs and your done, 8 channels in, 8 channels out in three easy steps.


Step 1 - Loop back testing
To use the loop back test in step 1, connect pins 2 to 9 (the channel inputs) to pins 10 to 13 and A0 to A3 (the channel outputs).

Each channel is set to a fixed output from 1100 to 1800, if the code works on your board, your should see these values being output in the serial monitor.

Step 2 - RC Channel reading

Once you have this up and running, you can start swapping the connections to an RC receiver one by one. You should now see the channel values for the receiver channels updating in your serial monitor. If the values are within a range of around 1000 to 2000, your ready to move to step 3.

Step 3 - Full Control
To start outputting the values that you have read in, remove the comments from the servoName.writeMicroseconds functions to have full control of upto 8 servos/escs using 8 RC Receiver channels.

If it does not work, let me know which board you have, the code is easily adapted to work on any of the 8-bit Arduino boards. If you have a 32 bit Arduino Due, there is a dedicated post here -

http://rcarduino.blogspot.ae/2013/04/reading-rc-channels-with-arduino-due.html

For more performance and smoother output on the 8-bit Arduinos an RCArduinoFastLib versions will be added in separate in the coming days.

// RCArduino MultiChannel Loop back and servo ESC control for upto 8 RC channels
//
// rcarduino.blogspot.com
//
// A simple approach for reading three RC Channels using pin change interrupts
//
// See related posts -
// http://rcarduino.blogspot.co.uk/2012/01/how-to-read-rc-receiver-with.html
// http://rcarduino.blogspot.co.uk/2012/03/need-more-interrupts-to-read-more.html
// http://rcarduino.blogspot.co.uk/2012/01/can-i-control-more-than-x-servos-with.html
//
// rcarduino.blogspot.com
//

// include the pinchangeint library - see the links in the related topics section above for details
#include <PinChangeInt.h>

#include <Servo.h>

// Assign your channel in pins
#define CHANNEL1_IN_PIN 2
#define CHANNEL2_IN_PIN 3
#define CHANNEL3_IN_PIN 4
#define CHANNEL4_IN_PIN 5
#define CHANNEL5_IN_PIN 6
#define CHANNEL6_IN_PIN 7
#define CHANNEL7_IN_PIN 8
#define CHANNEL8_IN_PIN 9

// Assign your channel out pins
#define CHANNEL1_OUT_PIN 10
#define CHANNEL2_OUT_PIN 11
#define CHANNEL3_OUT_PIN 12
#define CHANNEL4_OUT_PIN 13
#define CHANNEL5_OUT_PIN A0
#define CHANNEL6_OUT_PIN A1
#define CHANNEL7_OUT_PIN A2
#define CHANNEL8_OUT_PIN A3

// Servo objects generate the signals expected by Electronic Speed Controllers and Servos
// We will use the objects to output the signals we read in
// this example code provides a straight pass through of the signal with no custom processing
Servo servoChannel1;
Servo servoChannel2;
Servo servoChannel3;
Servo servoChannel4;
Servo servoChannel5;
Servo servoChannel6;
Servo servoChannel7;
Servo servoChannel8;

// These bit flags are set in bUpdateFlagsShared to indicate which
// channels have new signals
#define CHANNEL1_FLAG 1
#define CHANNEL2_FLAG 2
#define CHANNEL3_FLAG 4
#define CHANNEL4_FLAG 8
#define CHANNEL5_FLAG 16
#define CHANNEL6_FLAG 32
#define CHANNEL7_FLAG 64
#define CHANNEL8_FLAG 128

// holds the update flags defined above
volatile uint8_t bUpdateFlagsShared;

// shared variables are updated by the ISR and read by loop.
// In loop we immediatley take local copies so that the ISR can keep ownership of the
// shared ones. To access these in loop
// we first turn interrupts off with noInterrupts
// we take a copy to use in loop and the turn interrupts back on
// as quickly as possible, this ensures that we are always able to receive new signals
volatile uint16_t unChannel1InShared;
volatile uint16_t unChannel2InShared;
volatile uint16_t unChannel3InShared;
volatile uint16_t unChannel4InShared;
volatile uint16_t unChannel5InShared;
volatile uint16_t unChannel6InShared;
volatile uint16_t unChannel7InShared;
volatile uint16_t unChannel8InShared;

void setup()
{
  Serial.begin(115200);

  Serial.println("multiChannels");

  // attach servo objects, these will generate the correct
  // pulses for driving Electronic speed controllers, servos or other devices
  // designed to interface directly with RC Receivers
  servoChannel1.attach(CHANNEL1_OUT_PIN);
  servoChannel2.attach(CHANNEL2_OUT_PIN);
  servoChannel3.attach(CHANNEL3_OUT_PIN);
  servoChannel4.attach(CHANNEL4_OUT_PIN);
  servoChannel5.attach(CHANNEL5_OUT_PIN);
  servoChannel6.attach(CHANNEL6_OUT_PIN);
  servoChannel7.attach(CHANNEL7_OUT_PIN);
  servoChannel8.attach(CHANNEL8_OUT_PIN);

  // using the PinChangeInt library, attach the interrupts
  // used to read the channels
  PCintPort::attachInterrupt(CHANNEL1_IN_PIN, calcChannel1,CHANGE);
  PCintPort::attachInterrupt(CHANNEL2_IN_PIN, calcChannel2,CHANGE);
  PCintPort::attachInterrupt(CHANNEL3_IN_PIN, calcChannel3,CHANGE);
  PCintPort::attachInterrupt(CHANNEL4_IN_PIN, calcChannel4,CHANGE);
  PCintPort::attachInterrupt(CHANNEL5_IN_PIN, calcChannel5,CHANGE);
  PCintPort::attachInterrupt(CHANNEL6_IN_PIN, calcChannel6,CHANGE);
  PCintPort::attachInterrupt(CHANNEL7_IN_PIN, calcChannel7,CHANGE);
  PCintPort::attachInterrupt(CHANNEL8_IN_PIN, calcChannel8,CHANGE);

  // for loop back test only, lets set each channel to a known value
  servoChannel1.writeMicroseconds(1100);
  servoChannel2.writeMicroseconds(1200);
  servoChannel3.writeMicroseconds(1300);
  servoChannel4.writeMicroseconds(1400);
  servoChannel5.writeMicroseconds(1500);
  servoChannel6.writeMicroseconds(1600);
  servoChannel7.writeMicroseconds(1700);
  servoChannel8.writeMicroseconds(1800);
}

void loop()
{
  // create local variables to hold a local copies of the channel inputs
  // these are declared static so that thier values will be retained
  // between calls to loop.
  static uint16_t unChannel1In;
  static uint16_t unChannel2In;
  static uint16_t unChannel3In;
  static uint16_t unChannel4In;
  static uint16_t unChannel5In;
  static uint16_t unChannel6In;
  static uint16_t unChannel7In;
  static uint16_t unChannel8In;

  uint8_t bUpdateFlags = 0;
  // check shared update flags to see if any channels have a new signal
  // for nicely formatted serial output use this
  if(bUpdateFlagsShared == 0xFF)
  // for more responsive projects update any channels whenever a new signal is available using this
  // if(bUpdateFlagsShared)
  {
    noInterrupts(); // turn interrupts off quickly while we take local copies of the shared variables

    // take a local copy of which channels were updated in case we need to use this in the rest of loop
    bUpdateFlags = bUpdateFlagsShared;
  
    // in the current code, the shared values are always populated
    // so we could copy them without testing the flags
    // however in the future this could change, so lets
    // only copy when the flags tell us we can.
  

    if(bUpdateFlags & CHANNEL1_FLAG)
    {
      unChannel1In = unChannel1InShared;
    }
  
    if(bUpdateFlags & CHANNEL2_FLAG)
    {
      unChannel2In = unChannel2InShared;
    }
  
    if(bUpdateFlags & CHANNEL3_FLAG)
    {
      unChannel3In = unChannel3InShared;
    }

    if(bUpdateFlags & CHANNEL4_FLAG)
    {
      unChannel4In = unChannel4InShared;
    }
  
    if(bUpdateFlags & CHANNEL5_FLAG)
    {
      unChannel5In = unChannel5InShared;
    }
  
    if(bUpdateFlags & CHANNEL6_FLAG)
    {
      unChannel6In = unChannel6InShared;
    }
   
    if(bUpdateFlags & CHANNEL7_FLAG)
    {
      unChannel7In = unChannel7InShared;
    }
  
    if(bUpdateFlags & CHANNEL8_FLAG)
    {
      unChannel8In = unChannel8InShared;
    }
  
    // clear shared copy of updated flags as we have already taken the updates
    // we still have a local copy if we need to use it in bUpdateFlags
    bUpdateFlagsShared = 0;
  
    interrupts(); // we have local copies of the inputs, so now we can turn interrupts back on
    // as soon as interrupts are back on, we can no longer use the shared copies, the interrupt
    // service routines own these and could update them at any time. During the update, the
    // shared copies may contain junk. Luckily we have our local copies to work with :-)
  }
 
  // do any processing from here onwards
  // only use the local values unAuxIn, unThrottleIn and unSteeringIn, the shared
  // variables unAuxInShared, unThrottleInShared, unSteeringInShared are always owned by
  // the interrupt routines and should not be used in loop
  if(bUpdateFlags & CHANNEL1_FLAG)
  {
      // when you are ready to add a servo or esc, remove the // from the following line to use the channel input to control it
      // servoChannel1.writeMicroseconds(unChannel1In);
      Serial.println("");
      Serial.print(bUpdateFlags);
      Serial.print(",");
      Serial.print(unChannel1In);
      Serial.print(",");
  }
 
  if(bUpdateFlags & CHANNEL2_FLAG)
  {
      // when you are ready to add a servo or esc, remove the // from the following line to use the channel input to control it
      // servoChannel2.writeMicroseconds(unChannel2In);
      Serial.print(unChannel2In);
      Serial.print(",");
  }
 
  if(bUpdateFlags & CHANNEL3_FLAG)
  {
      // when you are ready to add a servo or esc, remove the // from the following line to use the channel input to control it
      // servoChannel3.writeMicroseconds(unChannel3In);
      Serial.print(unChannel3In);
      Serial.print(",");
  }
 
  if(bUpdateFlags & CHANNEL4_FLAG)
  {
      // when you are ready to add a servo or esc, remove the // from the following line to use the channel input to control it
      // servoChannel4.writeMicroseconds(unChannel4In);
      Serial.print(unChannel4In);
      Serial.print(",");
  }
 
  if(bUpdateFlags & CHANNEL5_FLAG)
  {
      // when you are ready to add a servo or esc, remove the // from the following line to use the channel input to control it
      // servoChannel5.writeMicroseconds(unChannel5In);
      Serial.print(unChannel5In);
      Serial.print(",");
  }
 
  if(bUpdateFlags & CHANNEL6_FLAG)
  {
      // when you are ready to add a servo or esc, remove the // from the following line to use the channel input to control it
      // servoChannel6.writeMicroseconds(unChannel6In);
      Serial.print(unChannel6In);
      Serial.print(",");
  }
 
  if(bUpdateFlags & CHANNEL7_FLAG)
  {
      // when you are ready to add a servo or esc, remove the // from the following line to use the channel input to control it
      // servoChannel7.writeMicroseconds(unChannel7In);
      Serial.print(unChannel7In);
      Serial.print(",");
  }
 
  if(bUpdateFlags & CHANNEL8_FLAG)
  {
      // when you are ready to add a servo or esc, remove the // from the following line to use the channel input to control it
      // servoChannel8.writeMicroseconds(unChannel8In);
      Serial.print(unChannel8In);
      Serial.print(",");
  }
}


void calcChannel1()
{
  static uint32_t ulStart;
 
  if(PCintPort::pinState) // this is equivalent to digitalRead(CHANNEL1_IN_PIN) but about 10 times faster
  {
    ulStart = micros();
  }
  else
  {
    unChannel1InShared = (uint16_t)(micros() - ulStart);
    bUpdateFlagsShared |= CHANNEL1_FLAG;
  }
}

void calcChannel2()
{
  static uint32_t ulStart;
 
  if(PCintPort::pinState)
  {
    ulStart = micros();
  }
  else
  {
    unChannel2InShared = (uint16_t)(micros() - ulStart);
    bUpdateFlagsShared |= CHANNEL2_FLAG;
  }
}

void calcChannel3()
{
  static uint32_t ulStart;
 
  if(PCintPort::pinState)
  {
    ulStart = micros();
  }
  else
  {
    unChannel3InShared = (uint16_t)(micros() - ulStart);
    bUpdateFlagsShared |= CHANNEL3_FLAG;
  }
}

void calcChannel4()
{
  static uint32_t ulStart;
 
  if(PCintPort::pinState)
  {
    ulStart = micros();
  }
  else
  {
    unChannel4InShared = (uint16_t)(micros() - ulStart);
    bUpdateFlagsShared |= CHANNEL4_FLAG;
  }
}

void calcChannel5()
{
  static uint32_t ulStart;
 
  if(PCintPort::pinState)
  {
    ulStart = micros();
  }
  else
  {
    unChannel5InShared = (uint16_t)(micros() - ulStart);
    bUpdateFlagsShared |= CHANNEL5_FLAG;
  }
}

void calcChannel6()
{
  static uint32_t ulStart;
 
  if(PCintPort::pinState)
  {
    ulStart = micros();
  }
  else
  {
    unChannel6InShared = (uint16_t)(micros() - ulStart);
    bUpdateFlagsShared |= CHANNEL6_FLAG;
  }
}

void calcChannel7()
{
  static uint32_t ulStart;
 
  if(PCintPort::pinState)
  {
    ulStart = micros();
  }
  else
  {
    unChannel7InShared = (uint16_t)(micros() - ulStart);
    bUpdateFlagsShared |= CHANNEL7_FLAG;
  }
}

void calcChannel8()
{
  static uint32_t ulStart;
 
  if(PCintPort::pinState)
  {
    ulStart = micros();
  }
  else
  {
    unChannel8InShared = (uint16_t)(micros() - ulStart);
    bUpdateFlagsShared |= CHANNEL8_FLAG;
  }
}
Duane B


Thursday, April 11, 2013

The Problem ( and Solutions ) With Arduino Interrupts

If you have ever tried to port an Arduino project that uses interrupts from one board type to another, you have probably experienced frustration, this includes the sample code throughout RCArduino.


The following post examines the differences in interrupts between the popular boards, the Arduino UNO, Micro, Mega, Mini and Leonardo. Based on this information we can modify sketches to run on the full range of 8-bit Arduinos.

The 8-bitArduino boards are based on one of three related chips -

ATMega328 - UNO and Mini
ATMega32u4 - Leonardo and Micro
ATMega2560 - Mega

Each of these chips supports two types of interrupts -

1) External Interrupts
These are flexible easy to use interrupts which can be triggered by rising, falling or changing signals. The disadvantage is that there are a limited number available on each chip type.

If we want to access more interrupts we need to look at the next type -

2) Pin Change Interrupts
The underlying chip in your Arduino supports a second type of interrupt however these interrupts are not directly supported by Arduino and need to be accessed through an additional library.

Differences In External Interrupts

The external interrupts are associated with specific digital pins on each chip type, the following table taken from the attachInterrupt reference page lists the available external interrupts and the associated Arduino pin on each chip -


Boardint.0int.1int.2int.3int.4int.5
Uno, Ethernet,
Mini
23
Mega25602321201918
Leonardo, Micro3201


The Arduino team have hidden some of the differences between the ATMega328 and ATM2560 so that attaching INT0 using the attachInterrupt function will attach an interrupt to digital pin 2 on both chips even though on the Mega digital pin2 is actually INT4.

The same logic has not been carried across to the ATMega32u4 Based leonardo. Notice how int0 and int1 are actually reversed on the Leonardo, this will be a major trap for people who are porting code from the UNO.


Does the Leonardo have four external interrupts ?
While the Leonardo appears to have 4 external interrupts, int2 and int3 are attached to digital pins 0 and 1 which are almost always reserved for serial input/output. So yes there are four interrupts, but two of them are only available by disabling serial functionality.

Differences In Pin Change Interrupts
On the Arduino UNO, pin change interrupts can be used to enable interrupts on any of the Arduino PINs to give access to a total of 19 interrupts (13 digital pins and 6 Analog pins).

I initially assumed that this was also possible on the Mega, Micro and Leonardo as well. It isn't.

Pin change interrupts are supported on the following Leonardo/Micro pins - 8,9,10 and 11.

Pin change interrupts are supported on Arduino Mega pins 10,11,12,13,14,15 and analog pins 6 to 15

Interrupts and RCArduino
These differences between the Arduino platforms will have been responsible for some of the difficulty that users have had in porting RCArduino code to Minis, Micros, Leonardos and Megas.

The good news is that now we have a full understanding of the inconsistencies between the different devices there should be no problem in modifying the sample sketches to run on them.

If your having trouble with a sample sketch, get in touch, in the meantime I will be updating some of the sketches to work across multiple boards.

Stay tuned

Duane B

Tuesday, February 12, 2013

Arduino Notes, Frequencies and Scales plus a Bonus Filter.

A quick post with a file I use a lot to experiment with Audio projects.

The file contains the frequencies used to generate the midi notes 0 to 127 in your Arduino projects. The file doesn't quite provide the frequency, instead it provides the phase increment required to generate the frequency. If you are familiar with Direct Digital Synthesis you will know that this is what we use in our Auduino projects to generate sine and other interesting waveforms at a given frequency.

The Illutron B Uses Direct Digital Synthesis, there is an introduction to the technique here -
http://rcarduino.blogspot.com/2012/08/the-must-build-arduino-project-illutron.html

Saving Memory
The note tables file uses PROGMEM to efficiently store the frequency table out of the way in program memory -

http://arduino.cc/en/Reference/PROGMEM

In addition to the 128 midi notes, two scales are provided, the pentatonic blues scale that will be familiar to anyone who has built and Auduino and the equally widely used C Minor blues scale. To preserve memory, these scales are also stored in progmem. You can add your own scales by following the same pattern.

Accessing The Notes

To access a note you can use -

uint16_t unPhaseIncrement = getMidiNotePhaseIncrement(sNote);

where sNote is between 0 and 127

To access a note from one of the scales based on an analogInput you can use -

uint16_t unPhaseIncrement = getCMBluesPhaseIncrement(map(aInput,0,1024,0,CMBLUES_NOTES));

In the sample code above, we are using the map function to map an analog input value held in aInput the range of notes available in the C Minor Blues scale.

A Simple Test Synthesiser
At the end of this post is a very simple synth which uses the note tables and is based on the popular Auduino with one bonus feature. I have included the low pass filter from the Mozzi Arduino audio libraries. The Mozzi filter is better than the simple hardware filters I have tried and is very easy to add to a project.

In the test sketch the filter can be driven in two ways based on the position of an analog input. At the bottom end of the input range, the filter is controlled directly by the input, at the top end of the range, the filter is controlled by an oscillator, the frequency of the oscillator is  adjustable within this range.

To keep things simple, the test sketch uses the same hardware as an Auduino so if you want to have a play around with the mozzi filter or creating your own scales - load up and play away.

Mozzi

The Mozzi filter can be downloaded from http://sensorium.github.com/Mozzi/

The Mozzi sound samples are very good quality, the project implements a range of synthesizer building blocks and is something to take a closer look at.

Arduino Note Tables -

// Arduino Note Tables rcarduino.blogspot.com

#ifndef _NOTETABLES_
#define _NOTETABLES_

#include "avr/pgmspace.h"

#define MIDI_NOTES 128
// used to convert midi note numbers into the increments required to generate the note in the ISR
PROGMEM unsigned int midiNoteToWavePhaseIncrement[MIDI_NOTES] =
{
 66 // 0,8.18,66.98,66                C
,70 // 1,8.66,70.96,70
,75 // 2,9.18,75.18,75
,79 // 3,9.72,79.65,79
,84 // 4,10.30,84.38,84
,89 // 5,10.91,89.40,89
,94 // 6,11.56,94.72,94
,100 // 7,12.25,100.35,100
,106 // 8,12.98,106.32,106
,112 // 9,13.75,112.64,112
,119 // 10,14.57,119.34,119
,126 // 11,15.43,126.43,126
,133 // 12,16.35,133.95,133            C
,141 // 13,17.32,141.92,141
,150 // 14,18.35,150.35,150
,159 // 15,19.45,159.29,159
,168 // 16,20.60,168.77,168
,178 // 17,21.83,178.80,178
,189 // 18,23.12,189.43,189
,200 // 19,24.50,200.70,200
,212 // 20,25.96,212.63,212
,225 // 21,27.50,225.28,225
,238 // 22,29.14,238.67,238
,252 // 23,30.87,252.86,252
,267 // 24,32.70,267.90,267           C - lowest note used on rcarduino ribbon synth
,283 // 25,34.65,283.83,283
,300 // 26,36.71,300.71,300
,318 // 27,38.89,318.59,318
,337 // 28,41.20,337.53,337
,357 // 29,43.65,357.60,357
,378 // 30,46.25,378.87,378
,401 // 31,49.00,401.40,401
,425 // 32,51.91,425.27,425
,450 // 33,55.00,450.55,450
,477 // 34,58.27,477.34,477
,505 // 35,61.74,505.73,505
,535 // 36,65.41,535.80,535           C
,567 // 37,69.30,567.66,567
,601 // 38,73.42,601.42,601
,637 // 39,77.78,637.18,637
,675 // 40,82.41,675.07,675
,715 // 41,87.31,715.21,715
,757 // 42,92.50,757.74,757
,802 // 43,98.00,802.79,802
,850 // 44,103.83,850.53,850
,901 // 45,110.00,901.11,901
,954 // 46,116.54,954.69,954
,1011 // 47,123.47,1011.46,1011       C
,1071 // 48,130.81,1071.60,1071
,1135 // 49,138.59,1135.32,1135
,1202 // 50,146.83,1202.83,1202
,1274 // 51,155.56,1274.36,1274
,1350 // 52,164.81,1350.13,1350
,1430 // 53,174.61,1430.42,1430
,1515 // 54,185.00,1515.47,1515
,1605 // 55,196.00,1605.59,1605
,1701 // 56,207.65,1701.06,1701
,1802 // 57,220.00,1802.21,1802
,1909 // 58,233.08,1909.38,1909
,2022 // 59,246.94,2022.92,2022
,2143 // 60,261.63,2143.20,2143       C
,2270 // 61,277.18,2270.64,2270
,2405 // 62,293.66,2405.66,2405
,2548 // 63,311.13,2548.71,2548
,2700 // 64,329.63,2700.27,2700
,2860 // 65,349.23,2860.83,2860
,3030 // 66,369.99,3030.95,3030
,3211 // 67,392.00,3211.18,3211
,3402 // 68,415.30,3402.12,3402
,3604 // 69,440.00,3604.42,3604
,3818 // 70,466.16,3818.75,3818
,4045 // 71,493.88,4045.83,4045
,4286 // 72,523.25,4286.41,4286      C
,4541 // 73,554.37,4541.29,4541
,4811 // 74,587.33,4811.33,4811
,5097 // 75,622.25,5097.42,5097
,5400 // 76,659.26,5400.53,5400
,5721 // 77,698.46,5721.67,5721
,6061 // 78,739.99,6061.89,6061
,6422 // 79,783.99,6422.36,6422
,6804 // 80,830.61,6804.25,6804
,7208 // 81,880.00,7208.85,7208
,7637 // 82,932.33,7637.51,7637
,8091 // 83,987.77,8091.66,8091
,8572 // 84,1046.50,8572.82,8572     C
,9082 // 85,1108.73,9082.58,9082
,9622 // 86,1174.66,9622.66,9622
,10194 // 87,1244.51,10194.85,10194
,10801 // 88,1318.51,10801.07,10801
,11443 // 89,1396.91,11443.33,11443
,12123 // 90,1479.98,12123.79,12123
,12844 // 91,1567.98,12844.71,12844
,13608 // 92,1661.22,13608.50,13608
,14417 // 93,1760.00,14417.70,14417
,15275 // 94,1864.65,15275.02,15275
,16183 // 95,1975.53,16183.31,16183
,17145 // 96,2093.00,17145.63,17145     C
,18165 // 97,2217.46,18165.16,18165
,19245 // 98,2349.32,19245.31,19245
,20389 // 99,2489.01,20389.70,20389
,21602 // 100,2637.02,21602.14,21602
,22886 // 101,2793.83,22886.67,22886
,24247 // 102,2959.95,24247.58,24247
,25689 // 103,3135.96,25689.42,25689
,27216 // 104,3322.44,27216.99,27216
,28835 // 105,3520.00,28835.39,28835
,30550 // 106,3729.31,30550.04,30550
,32366 // 107,3951.06,32366.63,32366
,34291 // 108,4186.01,34291.26,34291    C
,36330 // 109,4434.92,36330.32,36330
,38490 // 110,4698.64,38490.65,38490
,40779 // 111,4978.03,40779.41,40779
,43204 // 112,5274.04,43204.25,43204
,45773 // 113,5587.65,45773.32,45773
,48495 // 114,5919.91,48495.14,48495
,51378 // 115,6271.92,51378.79,51378
,54433 // 116,6644.87,54433.96,54433
,57670 // 117,7040.00,57670.76,57670
,61100 // 118,7458.62,61100.07,61100
,64733 // 119,7902.13,64733.26,64733
,3046 // 120,8372.02,68582.53,3046      C
,7124 // 121,8869.84,72660.64,7124
,11445 // 122,9397.27,76981.30,11445
,16022 // 123,9956.06,81558.77,16022
,20872 // 124,10548.07,86408.50,20872
,26010 // 125,11175.30,91546.65,26010
,31454 // 126,11839.81,96990.28,31454
,31454 // 127,11839.81,96990.28,31454 // this is wrong, need to calculate correct value, even though at 8Khz its wrapping around on every tick
};

// Pentatonic scale
// C D E G A C
// to map to midi note

#define PENTATONIC_NOTES 54

// provides an index of pentatonic notes in the midi note table

PROGMEM unsigned char sPentatonicNotes[PENTATONIC_NOTES] =
{
  0,   2,  4,  7,  9,
  12, 14, 16, 19, 21,
  24, 26, 28, 31, 33,
  36, 38, 40, 43, 45,
  48, 50, 52, 55, 57,
  60, 62, 64, 67, 69,
  72, 74, 76, 79, 81,
  84, 86, 88, 91, 93,
  96, 98,100,103,105,
 108,110,112,115,117,
 120,122,124,127
};

// C Minor Blues scale
// C D# F F# G A# C
// to map to midi note

#define CMBLUES_NOTES 65

// provides an index of pentatonic notes in the midi note table

PROGMEM unsigned char sCMBluesNotes[CMBLUES_NOTES] =
{
  0,   3,  5,  6,  7, 10,  // 6
  12, 15, 17, 18, 19, 22,  // 12
  24, 27, 29, 30, 31, 34,  // 18
  36, 39, 41, 42, 43, 46,  // 24
  48, 51, 53, 54, 55, 58,  // 30
  60, 63, 65, 66, 67, 70,  // 36  
  72, 75, 77, 78, 79, 82,  // 42
  84, 87, 89, 90, 91, 94,  // 48
  96, 99,101,102,103,106,  // 54
 108,111,113,114,115,118,  // 60
 120,123,125,126,127       // 65
};


unsigned int getMidiNotePhaseIncrement(unsigned char sNote)
{
 if(sNote >= MIDI_NOTES)
 {
   sNote = (MIDI_NOTES - 1);
 }

 return pgm_read_word(midiNoteToWavePhaseIncrement + (sNote));
}
unsigned int getPentatonicPhaseIncrement(unsigned char sPentatonicNote)
{
 if(sPentatonicNote >= PENTATONIC_NOTES)
  sPentatonicNote = (PENTATONIC_NOTES - 1);

 uint8_t sMidiIndex = pgm_read_byte(sPentatonicNotes + sPentatonicNote);

 return pgm_read_word(midiNoteToWavePhaseIncrement + sMidiIndex);
}

unsigned int getCMBluesPhaseIncrement(unsigned char sCMBluesNote)
{
 if(sCMBluesNote >= CMBLUES_NOTES)
  sCMBluesNote = (CMBLUES_NOTES - 1);
 
 uint8_t sMidiIndex = pgm_read_byte(sCMBluesNotes + sCMBluesNote);
 return pgm_read_word(midiNoteToWavePhaseIncrement + sMidiIndex);
}

#endif

The very simple, very lo-fi synth

// Very Lo Fi synth with Mozzi filter rcarduino.blogspot.com

#include "NoteTables.h"
#include "lowpass.h"

#define PWM_OUT_REG OCR2B

#define KEY_COUNT 20
#define KEY_WIDTH (1024/KEY_COUNT)

class CAudio
{
  public:
   CAudio(){}
   static void begin(uint8_t bStopTimer0Interrupts)
   {
     // Setup timer 1
     TCCR1A=0x0;          // set the timer prescaler to 8 = 16/8 = 2MHz
     TCCR1B=0x02;          // set the timer prescaler to 8 = 16/8 = 2MHz
     TIMSK1 |= (1<<OCIE1A);   // Enable output compare match interrupt on OCR1A
 
     TCCR2A=0B10110011;                                    //-8 bit audio PWM
     //TCCR0A=0x83;          // Set timer waveform generation mode to FAST PWM, clear OC0A On match, set at bottom - OC0A = digital pin 6.
     TCCR2B=0x01;          // Set to clock frequency, no prescaler

     pinMode(3,OUTPUT);
    
     if(bStopTimer0Interrupts)
     {
      // stops timer0 which triggers interrupts for the millis and micros functions
      // if we stop the timer, we get better audio, but loose millis and micros.
      TIMSK0 &= (~((1 << OCIE0A)| (1 << OCIE0B)));
     }
   }
};


#define SAW 0
#define SQUARE 1
#define RAMP 2


class COscilator
{
public:
  void setWaveform(uint8_t sWaveform)
  {
    m_sWaveform = sWaveform;
  }
  void setPhaseIncrement(uint16_t unPhaseIncrement)
  {
    m_unPhaseIncrement = unPhaseIncrement;
  }
  uint8_t hasTriggered()
  {
    return m_unPhaseAccumulator < m_unPhaseIncrement;
  }
  void trigger()
  {
    m_unPhaseAccumulator = 0;
  }
  void updatePhase()
  {
    m_unPhaseAccumulator += m_unPhaseIncrement;
  }
  uint8_t getSample()
  {
    uint8_t sSample = m_unPhaseAccumulator>>8;
    switch(m_sWaveform)
    {
      case SQUARE:
       (sSample >= 127) ? sSample = 255 : sSample = 0;
      break;
      case RAMP:
       sSample = ~sSample;
      break;
    }
    return sSample;
  }
protected:
  uint16_t m_unPhaseIncrement;
  uint16_t m_unPhaseAccumulator;
  uint8_t m_sWaveform;
};

COscilator Osc1,Osc2,LFO,LFOFilter;

uint16_t getPhaseIncrement(uint16_t aInput,uint8_t sOffset)
{
  uint8_t sKey = aInput/KEY_WIDTH;

  // find where on the ribbon the key starts 
  uint16_t unKeyStart = sKey * KEY_WIDTH;
 
  // find where on the ribbon the key ends
  uint16_t unKeyEnd = (sKey+1) * KEY_WIDTH;
 
  // map where the current input sits between the key start and key end to a frequency that sits at the same point between
  // the frequency of the key and the frequency of the next key
 
  return map(aInput,unKeyStart,unKeyEnd,getMidiNotePhaseIncrement(sKey+sOffset),getMidiNotePhaseIncrement(sKey+1+sOffset));
}

LowPassFilter lpFilter;
uint16_t gFilter;

void setup()
{
  Serial.begin(9600);
  CAudio::begin(true); 

  Osc1.setWaveform(RAMP);
  Osc2.setWaveform(RAMP);
  LFO.setWaveform(SAW);
  LFOFilter.setWaveform(RAMP);
}

void loop()
{
  uint16_t aInput = analogRead(1);
  if(aInput >= 1021)
  {
    Osc1.setPhaseIncrement(0);
    Osc2.setPhaseIncrement(0);
    LFO.setPhaseIncrement(0);
    LFOFilter.trigger();
  }
  else
  {
    uint8_t sOffset = 20;//((analogRead(1) >> 3)/12)*12;
    uint16_t unPhaseIncrement = getCMBluesPhaseIncrement(map(aInput,0,1024,0,CMBLUES_NOTES));
    Osc1.setPhaseIncrement(analogRead(2)<<2);
    Osc2.setPhaseIncrement(analogRead(3)<<5);

    //lpFilter.setCutoffFreq(analogRead(4)>>2);
    lpFilter.setResonance(analogRead(5)>>2);
   
    LFO.setPhaseIncrement(unPhaseIncrement);   
   
    uint16_t unFilter = analogRead(4);
    if(unFilter < 512)
    {
     LFOFilter.setPhaseIncrement(0);
     unFilter>>=1;
    
     uint8_t sreg = SREG;
     cli();
     gFilter = unFilter;
     SREG = sreg;
    }
    else
    {
     uint8_t sreg = SREG;
     cli();
     gFilter = unFilter;
     SREG = sreg;
    
     LFOFilter.setPhaseIncrement((gFilter-512));
    }
  }
}



#define DELAY_LENGTH 1024

// iterate the grains and LFO
SIGNAL (TIMER1_COMPA_vect)
{
  static uint8_t sDelayBuffer[DELAY_LENGTH];
  static uint16_t sDelayIndex;

  sDelayIndex++;
  if(sDelayIndex >= DELAY_LENGTH)
  {
    sDelayIndex = 0;
  }
 
  OCR1A += 125;

  Osc1.updatePhase();
  Osc2.updatePhase();
  LFO.updatePhase();
  LFOFilter.updatePhase();
 
  if(LFO.hasTriggered())
  {
    Osc1.trigger();
    Osc2.trigger();
  }
   
  if(gFilter < 512)
  {
    lpFilter.setCutoffFreq(gFilter>>1);
  }
  else
  {
   lpFilter.setCutoffFreq(LFOFilter.getSample());
  }
 
  uint16_t nOutput = lpFilter.next((Osc1.getSample()>>1)+(Osc2.getSample()>>1));
  if(nOutput > 255)
   nOutput = 255;
  PWM_OUT_REG = nOutput;

  // not being mixed in to output but easy to add, see rcarduino.blogspot.com
  sDelayBuffer[sDelayIndex] = PWM_OUT_REG;
}




And don't forget the Mozzi filter 'lowpass.h' from https://github.com/sensorium/Mozzi

Duane B

Tuesday, February 5, 2013

RCArduino Libraries FAQ

A central point for questions and help on using the RCArduino Libraries.

The RCArduino Libraries provides servo output, PPM input and individual RC Channel input.

Its been created to reduce servo jitter in projects that need to both read and output RC Channels. If you want smooth focus for camera work, animatronics that don't twitch around or fast smooth control of your RC Vehicle, read on.

1) Why a new servo and RC library ?

The standard Arduino Servo library is very good, its well documented and well supported, it also has some challenges when used in projects that read incoming RC Signals. The RCArduino Servo library addresses these challenges to provide smoother output. This is achieved through faster interrupt service routines which reduce clashes and glitches.

A major improvement is also acheived by not resetting timer1 as the standard servo library can. This allows us to use timer1 for timing incoming RC Signals as well as generating the servo outputs.



An detailed explanation can be found here -

http://rcarduino.blogspot.com/2012/11/how-to-read-rc-channels-rcarduinofastlib.html

2) Can I use the library to read PPM signals and output RC Channels ?

Yes, an example is provided here, but come back here for more documentation if you need it.

http://rcarduino.blogspot.com/2012/11/how-to-read-rc-receiver-ppm-stream.html

3) Can I use the library to read individual RC Channels and output RC Channels ? 

Yes, an example is provided here, but come back here for more documentation and if your planning to use a mega look out for an update with an example using the six Arduino Mega interrupts instead of the pinchangeint library which is used in the UNO version to provide more than the standard two interrupts.

http://rcarduino.blogspot.com/2012/11/how-to-read-rc-channels-rcarduinofastlib.html

Some background on the pinchangeint library if you need more than 2 interrupts on an UNO or more than 6 on a Mega -

http://rcarduino.blogspot.com/2012/03/need-more-interrupts-to-read-more.html
How do we use the library ?

The RCArduino library uses arrays internally to record the channel input and output values. This is slightly more efficient than the linked lists used by the servo library however its also a little less user friendly. Unlike the Servo library which allows us to attach servos at runtime, the RCArduino library requires that we set the number of servos at compile time. Similarly if you plan to use the PPM Input capability you will need to specify the number of input channels at compile time.

To make this as simple as possible, two #define's are used  in the RCArduinoFastLib.h file.

To set the number of input channels if your going to read a PPM Stream change the number here -
// Change to set the number of channels in PPM Input stream
#define RC_CHANNEL_IN_COUNT 3

To set the number of servos in your project change the number here -

// Change to set the number of servos/ESCs
#define RC_CHANNEL_OUT_COUNT 4

Set the RC_CHANNEL_OUT_COUNT to one more servo than you need, the library is able to generate higher refresh rate signals than the standard Servo library which forces a 50Hz refresh rate. To set the refresh rate we create an additional entry in the servo array. We use the 'setFrameSpace' function on this last entry to set the frame space which sets the refresh rate. While this is initially an awkward additional step, it also gives us a lot more flexibility, for example we can use the library to drive high refresh rates upto 500Hz.

To generate a 50Hz refresh rate which is equivalent to the standard servo library we can use the following calculation -

1/50 = 20,000 microseconds
frame space = 20,000 - (2,000 * number of servos)

An Example -
To drive 8 servos we would set RC_CHANNEL_OUT_COUNT to (8 + 1)= 9. Remember the additional entry is for our framespace.

#define RC_CHANNEL_OUT_COUNT 9

we can then calculate

frame space = 20,000 - (2,000 * (RC_CHANNEL_OUT_COUNT - 1) )

Note we take away 1 so that we do not include the framespace in the framespace calculation

frame space = 20,000 - (2,000 * 8)
frame space = 4,000

So to set the frame space to give a refresh rate of 50 Hz for 8 servos we call

CRCArduinoFastServos::setFrameSpaceA(SERVO_FRAME_SPACE,4000);

Why the maths ?
 
While this is more complex than the standard servo library it is also more flexible and provides a quick and easy way to drive the high frequency ESCs and Servos that are becoming more popular.

If your unsure about the frame space calculation or would like to try the RCArduino libraries with 500Hz ESCs and Servos use the comments or contact me Duane B through the Arduino forum.

I am also considering automating the calculation through a convenience function in a future release, stay tuned.

Now that we have that out of the way, its all very easy.

Attaching A Servo
To attach a servo we call the attach function supply the index of the servo we want to attach (they start at 0, remember they are held in an array) and the Arduino pin we want to attach it to.

CRCArduinoFastServos::attach(SERVO_THROTTLE/* Servo index */,THROTTLE_OUT_PIN /* the pin we want it attached to */);

The final thing we need to do inside out set up function is to call the begin function, this initialises timer1 for use by the library and attaches the interrupt routines.

Understanding The Library and Reading and Writing Channels

The key to understanding and using the library is to be aware of the RC_CHANNEL_IN_COUNT and RC_CHANNEL_OUT_COUNT settings and also to understand that the channel values are stored in arrays which are accessed by the index you supply when calling the read and write functions -

CRCArduinoFastServos::writeMicroseconds(SERVO_STEERING /* 0 based index into servo array */,unSteeringIn);
CRCArduinoPPMChannels::getChannel(SERVO_STEERING /* 0 based index into PPM input array */);

You can create your own indexes into the arrays by defining them in your sketch, the number of input channels does not need to match the number of output channels. You can also have a different index for an input channel that you use for the output channel, in fact you will sometimes find that channels within the PPM stream are in a different order than the printed channel numbers on your receiver.

To get started try the examples provided -

http://rcarduino.blogspot.com/2012/11/how-to-read-rc-channels-rcarduinofastlib.html

http://rcarduino.blogspot.com/2012/11/how-to-read-rc-receiver-ppm-stream.html

For any help, use the comments section below

Duane B

Sunday, January 13, 2013

New Arduino Board - Esplora

A new Arduino Board has recently been announced - the Arduino Esplora.

Its a very different proposition to previous Arduino boards in that it is hardwired with a large number of built in sensors -


The Esplora has the following on-board inputs and outputs :


  • Analog joystick with central push-button two axis (X and Y) and a center pushbutton.
  • 4 push-buttons laid out in a diamond pattern.
  • Linear potentiometer slider near the bottom of the board.
  • Microphone for getting the loudness (amplitude) of the surrounding environment.
  • Light sensor for getting the brightness.
  • Temperature sensor reads the ambient temperature
  • Three-axis accelerometer measures the board's relation to gravity on three axes (X, Y, and Z)
  • Buzzer can produce square-waves.
  • RGB led bright LED with Red Green and Blue elements for color mixing.
  • 2 TinkerKit Inputs to connect the TinkerKit sensor modules with the 3-pin connectors.
  • 2 TinkerKit Outputs to connect the TinkerKit actuator modules with the 3-pin connectors.
  • TFT display connector connector for an optional color LCD screen, SD card, or other devices that use the SPI protocol. 
Information reposted from the official Arduino site where full details of the Arduino Esplora can be found -

http://arduino.cc/en/Main/ArduinoBoardEsplora


The TFT Display connector will be interesting - adding a display to the array of build in inputs will make the Esplora and ideal platform for 8-bit Arcade clones - something that will feature on rcarduino as and when an Esplora arrives.

To get up and running quickly with the new inputs and outputs, the Esplora has its own set of easy to use libraries.

Esplora libraries -

readSlider() 
readLightSensor() 
readTemperature() 
readMicrophone() 
readJoystickSwitch() 
readJoystickButton() 
readAccelerometer() 
readButton() 
readJoystickX() 
readJoystickY() 
writeRGB() 
writeRed() 
writeGreen() 
writeBlue() 
readRed() 
readGreen() 
readBlue() 
tone() 
noTone() 

reposted from the official Arduino website - http://arduino.cc/en/Reference/EsploraLibrary
 

To get a flavour of what can be achieved out of the box with the new Arduino Esplora, check out the example sketches

EsploraBlink : Blink the Esplora's RGB LED
EsploraAccelerometer : Read the values from the accelerometer
EsploraJoystickMouse : Use the Esplora's joystick to control the cursor on your computer EsploraLedShow : Use the Joystick and slider to create a light show with the LED
EsploraLedShow2 : Use the Esplora's microphone, linear potentiometer, and light sensor to change the color of the onboard LED.
 EsploraLightCalibrator : Read the values from the accelerometer
 EsploraMusic : Make some music with the Esplora
 EsploraSoundSensor : Read the values from the Esplora's microphone
 EsploraTemperatureSensor : Read the temperature sensor and get the temperature in in Farhenheit or Celsius.

Experts
EsploraKart : Use the Esplora as a controller to play a kart racing game.
EsploraTable : Print the Esplora sensor information to a table format.
 EsploraRemote : Connect the Esplora to Processing and control the outputs.
 EsploraPong : Play Pong with the Esplora using Processing.

For the original list see the official Arduino website link - http://arduino.cc/en/Reference/EsploraLibrary
Stay Tuned Duane B

Saturday, December 29, 2012

New Year Projects

A quick teaser of some of the projects and components that will be featured on RC Arduino in the new year.

TFT Screen, 4 * Bi Color LED Matrix, Analogue Joystick, Microphone

Analogue Joystick, Touch Screen Overlay, Soft Pot.

A small collection of mini synths - Korg Monotron, Dubreq Stylophone, Bleep Labs Nebulophone.

RC Car Active Power Distribution
I am also planning to bring this monster back to life -
 Its two RC Cars joined together to make a twin motor 4wd car with independent throttle control to the front and rear axles. The result is a small car with way too much power, its very much in the spirit of a Group B rally car or the twin motored Suzuki Escudo rally car.

Stay Tuned

DUane B

Saturday, December 1, 2012

Arduino Due DDS - Part 1 - Sinewaves and Fixed Point Maths

This post provides a quick introduction to Direct Digital Synthesis (DDS) on the Arduino Due. The sample sketch outputs a sinewave at a 44.1Khz sample rate with 12 bit resolution using the built in Digital To Analog Converter (DAC).

A related sketch which provides more interesting audio output without the explanation can be found here -
http://rcarduino.blogspot.com/2012/11/quick-and-dirty-synth-for-arduino-due.html

All of the Audio projects on RCArduino are based on this same DDS technique described below, see the following links for DDS Projects -
http://rcarduino.blogspot.com/2012/10/five-dollar-synthesiser.html
http://rcarduino.blogspot.com/2012/08/the-must-build-arduino-project-illutron.html

http://rcarduino.blogspot.com/2012/11/auduino-with-delay.html
http://rcarduino.blogspot.com/2012/08/adding-audio-to-arduino-projects.html
http://rcarduino.blogspot.com/2012/10/arduino-modular-synthesizer-part-one.html

Direct Digital Synthesis - Introduction
DDS is a common approach to generating sinewave output from digital systems. The approach is based around outputting samples from a description of the required waveform held in memory. The description is an array of samples which can be traversed to plot the waveform onscreen or by using DDS can generate a sinewave output at a selected frequency.

The 600 sample 12 Bit sine wave table used in the sketch below plotted on screen using processing -

The 128 sample 8 Bit wave tables used in the Illutron B synth -


Direct Digital Synthesis - Sample Rate
To generate an output waveform we need a way to regularly update the output value, we do this by using a timer interrupt. The frequency of this interrupt is termed the sample rate, in our case we are using a 44.1Khz sample rate meaning that we have an interrupt triggering 44,100 times per second.

As this interrupt is triggering at such a high rate it is very important that we keep it efficient, this is the reason for using a pre computed wavetable rather than computing the sine samples on the fly.

Direct Digital Synthesis - Frequency Generation
We can generate different frequencies by changing the rate at which we traverse through the wave table. One pass through the wavetable provides one complete cycle of the selected waveform (sine in our case). If we pass through the table once in 44,100 interrupts we generate a frequency of 1Hz, if we pass through 100 times faster, we get a sinewave output at a frequency of 100Hz.

Direct Digital Synthesis - Implementation and Terminology
The following terms are commonly used in reference to DDS and variations are used throughout the RC Arduino audio projects.

1) Phase Accumulator - this is an array index which points to the current sample in the selected wavetable.
2) Phase Increment - this value is used to advance the Phase Accumulator each time the interrupt is called.

Example usage of the phase accumulator used inside the timer interrupt to select the next sample in the wave table array -

// Update the phase accumulator by adding the phase increment
ulPhaseAccumulator += ulPhaseIncrement;

// get the current sample from the sine table using the phase accumulator as the index  
uint32_t ulOutput = nSineTable[ulPhaseAccumulator>>20];

Direct Digital Synthesis - Fixed Point Maths
To generate a full range of frequencies we will often need to use fractional values for our phase accumulator.


The usual method of working with fractional values is to use the float data type however in DDS we need our interrupts to run as fast as possible. With very few exceptions microcontrollers are not able to process the float data type at high speed, the solution is to use fixed point integer maths. 

Fixed Points For Higher Integer Precision
There are many applications of fixed point arithmetic but in DDS the main use is to provide increased precision - equivalent to having many decimal points while still using the high speed integer (whole number) data type.

To get extra precision we use more bits than we need to represent our data range. For example the sample code below has a wave table size of 600 samples so the range of we need for our phase accumulator is 0 to 599.

We can represent this range with 10 bits but if we use 16 bits we have 6 additional bits of precession.


16 Bit Integer














Bit  15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
Value 32768 16384 8192 4096 2048 1024 512 256 128 64 32 16 8 4 2 1

















Fixed Point 10.6 using 16 Bit Integer












Bit  15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
Value 1024 512 256 128 64 32 16 8 4 2 1 0.5 0.25 0.125 0.0625 0.0313

We use this additional precision to perform fractional addition to the phase accumulator, the 10.6 format gives 1/32 precision as the least significant bit.

Fixed point addition - we know its fixed point, the compiler doesn't

// Update the phase accumulator by adding the phase increment
ulPhaseAccumulator += ulPhaseIncrement;  

This line of code is actually adding two fixed point numbers, part of the trick is that the compiler does not know this, it sees them simply as two integers which it processes using very fast hardware integer addition.

When we want to use the phase accumulator as the array index, we perform an arithmetic shift six places to the right, this drops off the six bits of additional precession and leaves us with a 10 bit integer value for the array index.

// drop the six bits of additional precision and access the remaining 10 bits as an integer array index in the range 0 - 599

uint16_t unOutputValue = sineTable[ulPhaseAccumulator>>6];

The only trick to fixed point maths is to figure out a scheme which will work for your project and stick to it. Remember the compiler does not know that you are treating part of the number as fractions so the responsibility is on you the programmer to keep consistency.

Fixed Point In The RCArduino DDS Example Sketch
The standard data type for the 32 bit Arduino Due is a 32 bit integer, this gives us scope for very high levels of precession in integer maths. The scheme used for the phase increment and phase accumulator variables in the sample sketch is 12.20 (12+20 = 32 Bits) this can be seen in the following line of code where the precision is shifted out to access the pure 12 bit integer value -

uint32_t ulOutput = nSineTable[ulPhaseAccumulator>>20];

Where ever you see code that makes extensive use of '>>' operations you might be looking at fixed point arithmetic, in a follow up post we will look at how we perform fast fixed point multiplication. This is another key to fast and flexible synthesizer engines.

Fixed Point Multiplication is used in Audio synthesis to apply envelopes and amplitude modulation.

Envelope Applied to a DDS sine wave using Fixed Point Maths in the Illutron B Project -

DDS Sinewave for Arduino Due - The Sketch
Use a potentiometer connected to analog pin 0 to control the pitch. To hear the output you can use an amplifier circuit such as this one used throughout RCArduino - 

http://rcarduino.blogspot.com/2012/08/adding-audio-to-arduino-projects.html

The sinewave is output through DAC0 - Note that the DAC does not provide a full 0-3.3volt swing, this is a hardware limitation of the SAM3X8E chip.


Caution : The SAM3X8E microcontroller at the heart of the Arduino Due is less able to sink and source current than the AVR family of chips used in the 8-Bit Arduinos. See the update below for the most recent recommendations - 

Update 09/06/2013 - Gaétan Ro has taken things much further in the latest build of his Groovuino project. The Project uses the technique described here combined with some sensors, a sequencer and filter to produce a really nice sounding groovebox. I plan to build one of these for myself and hope to be able to help Gaétan with its further development.

http://www.youtube.com/watch?v=EqI2hEVbMPI

Gaéta's blog -  http://groovuino.blogspot.com/

Update 28/03/2013 : There is little information on the SAM3X8E DAC in the datasheet however Arduino forum used 'stimmer' has found recommendations on a closely related chip which suggest that a 2K Ohm resistor should be placed as a series current limiting resistor between the DAC Output and the load. This assumes that the load has no resistance of its own which will be the case if you accidentally short something, if you know the resistance of your load, you can reduce this value, if not, its a reasonable starting point and will protect against accidents. For more information see the original thread here - http://arduino.cc/forum/index.php/topic,139733.15.html

Update 28/03/2013 : Gaétan Ro has been building on the techniques here to produce an Arduino Due based groove box he is calling the 'Groovuino'. You can hear a clip of the project in action here -

https://soundcloud.com/gaetino/groovuino

And follow the future development on the blog Gaétan has started here -

http://groovuino.blogspot.ae/

// RCArduino DDS Sinewave for Arduino Due
// RCArduino DDS Sinewave by RCArduino is licensed under a Creative Commons Attribution 3.0 Unported License.
// Based on a work at rcarduino.blogspot.com.

// For helpful background information on Arduino Due Timer Configuration, refer to the following link
// thanks to Sebastian Vik
// http://arduino.cc/forum/index.php?action=post;topic=130423.15;num_replies=20

// For background information on the DDS Technique see
// http://interface.khm.de/index.php/lab/experiments/arduino-dds-sinewave-generator/

// For audio sketches making extensive use of DDS Techniques, search the RCArduino Blog
// for the tags Audio or synth

// These are the clock frequencies available to the timers /2,/8,/32,/128
// 84Mhz/2 = 42.000 MHz
// 84Mhz/8 = 10.500 MHz
// 84Mhz/32 = 2.625 MHz
// 84Mhz/128 = 656.250 KHz
//
// 44.1Khz = CD Sample Rate
// Lets aim for as close to the CD Sample Rate as we can get -
//
// 42Mhz/44.1Khz = 952.38
// 10.5Mhz/44.1Khz = 238.09 // best fit divide by 8 = TIMER_CLOCK2 and 238 ticks per sample
// 2.625Hmz/44.1Khz = 59.5
// 656Khz/44.1Khz = 14.88

// 84Mhz/44.1Khz = 1904 instructions per tick

// the phase accumulator points to the current sample in our wavetable
uint32_t ulPhaseAccumulator = 0;
// the phase increment controls the rate at which we move through the wave table
// higher values = higher frequencies
volatile uint32_t ulPhaseIncrement = 0;   // 32 bit phase increment, see below

// full waveform = 0 to SAMPLES_PER_CYCLE
// Phase Increment for 1 Hz =(SAMPLES_PER_CYCLE_FIXEDPOINT/SAMPLE_RATE) = 1Hz
// Phase Increment for frequency F = (SAMPLES_PER_CYCLE/SAMPLE_RATE)*F
#define SAMPLE_RATE 44100.0
#define SAMPLES_PER_CYCLE 600
#define SAMPLES_PER_CYCLE_FIXEDPOINT (SAMPLES_PER_CYCLE<<20)
#define TICKS_PER_CYCLE (float)((float)SAMPLES_PER_CYCLE_FIXEDPOINT/(float)SAMPLE_RATE)

// to represent 600 we need 10 bits
// Our fixed point format will be 10P22 = 32 bits


// We have 521K flash and 96K ram to play with

// Create a table to hold the phase increments we need to generate midi note frequencies at our 44.1Khz sample rate
#define MIDI_NOTES 128
uint32_t nMidiPhaseIncrement[MIDI_NOTES];

// fill the note table with the phase increment values we require to generate the note
void createNoteTable(float fSampleRate)
{
  for(uint32_t unMidiNote = 0;unMidiNote < MIDI_NOTES;unMidiNote++)
  {
    // Correct calculation for frequency
    Serial.print(unMidiNote);
    Serial.print(" ");
    float fFrequency = ((pow(2.0,(unMidiNote-69.0)/12.0)) * 440.0);
    Serial.print(fFrequency);
    Serial.print(" ");
   
    nMidiPhaseIncrement[unMidiNote] = fFrequency*TICKS_PER_CYCLE;
    Serial.println(nMidiPhaseIncrement[unMidiNote]);
  }
}

// Create a table to hold pre computed sinewave, the table has a resolution of 600 samples
#define WAVE_SAMPLES 600
// default int is 32 bit, in most cases its best to use uint32_t but for large arrays its better to use smaller
// data types if possible, here we are storing 12 bit samples in 16 bit ints
uint16_t nSineTable[WAVE_SAMPLES];

// create the individual samples for our sinewave table
void createSineTable()
{
  for(uint32_t nIndex = 0;nIndex < WAVE_SAMPLES;nIndex++)
  {
    // normalised to 12 bit range 0-4095
    nSineTable[nIndex] = (uint16_t)  (((1+sin(((2.0*PI)/WAVE_SAMPLES)*nIndex))*4095.0)/2);
    Serial.println(nSineTable[nIndex]);
  }
}

void setup()
{
  Serial.begin(9600);

  createNoteTable(SAMPLE_RATE);
  createSineTable();
 
  /* turn on the timer clock in the power management controller */
  pmc_set_writeprotect(false);
  pmc_enable_periph_clk(ID_TC4);

  /* we want wavesel 01 with RC */
  TC_Configure(/* clock */TC1,/* channel */1, TC_CMR_WAVE | TC_CMR_WAVSEL_UP_RC | TC_CMR_TCCLKS_TIMER_CLOCK2);
  TC_SetRC(TC1, 1, 238); // sets <> 44.1 Khz interrupt rate
  TC_Start(TC1, 1);
 
  // enable timer interrupts on the timer
  TC1->TC_CHANNEL[1].TC_IER=TC_IER_CPCS;
  TC1->TC_CHANNEL[1].TC_IDR=~TC_IER_CPCS;
 
  /* Enable the interrupt in the nested vector interrupt controller */
  /* TC4_IRQn where 4 is the timer number * timer channels (3) + the channel number (=(1*3)+1) for timer1 channel1 */
  NVIC_EnableIRQ(TC4_IRQn);

  // this is a cheat - enable the DAC
  analogWrite(DAC0,0);
}

void loop()
{
  // read analog input 0 drop the range from 0-1024 to 0-127 with a right shift 3 places,
  // then look up the phaseIncrement required to generate the note in our nMidiPhaseIncrement table
  uint32_t ulInput = analogRead(0);
  ulPhaseIncrement = nMidiPhaseIncrement[ulInput>>3];
}

void TC4_Handler()
{
  // We need to get the status to clear it and allow the interrupt to fire again
  TC_GetStatus(TC1, 1);
 
  ulPhaseAccumulator += ulPhaseIncrement;   // 32 bit phase increment, see below

  // if the phase accumulator over flows - we have been through one cycle at the current pitch,
  // now we need to reset the grains ready for our next cycle
  if(ulPhaseAccumulator > SAMPLES_PER_CYCLE_FIXEDPOINT)
  {
   // DB 02/Jan/2012 - carry the remainder of the phase accumulator
   ulPhaseAccumulator -= SAMPLES_PER_CYCLE_FIXEDPOINT;
   }

  // get the current sample  
  uint32_t ulOutput = nSineTable[ulPhaseAccumulator>>20];
 
  // we cheated and user analogWrite to enable the dac, but here we want to be fast so
  // write directly 
  dacc_write_conversion_data(DACC_INTERFACE, ulOutput);
}