The showcase is based on the simplest synth engine I could create - 3 counters counting up at a rate controlled by three analog inputs.

It might sound simple but its a surprisingly rich sounding synth engine, here is how it works -

Two of the counters generate ramp waveforms - think about it, they are counting up from 0 to 4095, when they overflow, they go back to 0 and start the count again.

Example Ramp Output -

Example Ramp At A Higher Frequency -

These ramp waveforms are summed together at the output to generate a more complex waveform - two ramp waves of independent frequency superimposed on each other.

Example - Two triangle at frequency F and 3F mixed together to create a new output waveform.

The third ramp waveform is used to control the pitch. It is not mixed with the output waveforms instead it achieves pitch control by resetting the first two waveforms.

Output waveform reset at frequency determined by third counter - notice that the counter is not directly present in the output but controls the repetition/synchronization of the output which in turn creates the pitch.

To hear the technique in action connect three potentiometers to your Arduino Due on analog inputs 0,1,2 and an audio amplifier to DAC0.

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. I would suggest using a series resistor of around 500 Ohms when connecting the Arduino Due DAC to an external device. See this thread on the Arduino forum for more information - http://arduino.cc/forum/index.php/topic,139733.0.html

// RCArduino Quick And Dirty Synth for Arduino Due// RCArduino Quick And Dirty Synth 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// The following folders within the arduino install provide access to source code and documentation// its very low level, the documentation is next to useless, but it might help someone// C:\arduino-1.5.1r2\hardware\arduino\sam\system\libsam\source// C:/arduino-1.5.1r2/hardware/arduino/sam/system/CMSIS/Device/ATMEL/sam3xa/html/tc1.html// C:/arduino-1.5.1r2/hardware/arduino/sam/system/CMSIS/Device/ATMEL/sam3xa/html/SAM3XA.html// C:\arduino-1.5.1r2\hardware\arduino\sam\system\CMSIS\CMSIS\Documentation// 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// These variables represent our synth engine, its similar to a vastly simplified Auduino and has a similar sound.// Phase Accumulator controls the main pitch, grain1 and grain two phase accumulator control the pitch for the two grains// The grainPhaseAccumulators are essentially simple counters, when you count up and overflow back to 0, you generate a ramp waveform// we generate two and mix them together.uint32_t ulPhaseAccumulator = 0; // 32 bit phase accumulator, if we shift >> 20 bits we get a 12 bit value for our output ADCvolatile uint32_t ulPhaseIncrement = 0; // 32 bit phase increment, see belowuint32_t ulGrain1PhaseAccumulator = 0; // 32 bit phase accumulator, if we shift >> 20 bits we get a 12 bit value for our output ADCvolatile uint32_t ulGrain1PhaseIncrement = 0; // 32 bit phase increment, see belowuint32_t ulGrain2PhaseAccumulator = 0; // 32 bit phase accumulator, if we shift >> 20 bits we get a 12 bit value for our output ADCvolatile uint32_t ulGrain2PhaseIncrement = 0; // 32 bit phase increment, see below// full waveform = 0 to 4294967296// Phase Increment for 1 Hz =(4294967296/44100) = 1Hz// Phase Increment for frequency F = (4294967296/44100)*F#define SAMPLE_RATE 44100.0#define TICKS_PER_CYCLE (4294967296.0/SAMPLE_RATE)// Create a table to hold the phase increments we need to generate midi note frequencies at our 44.1Khz sample rate#define MIDI_NOTES 128uint32_t nMidiPhaseIncrement[MIDI_NOTES];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]); }}void setup(){ Serial.begin(9600); createNoteTable(SAMPLE_RATE); /* 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]; // set the phase increment for grains 1 and 2, we do not want a precise frequency for these, // they set the character of the note rather than pitch ulGrain1PhaseIncrement = analogRead(1)<<18; ulGrain2PhaseIncrement = analogRead(2)<<18;}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 < ulPhaseIncrement) { ulGrain1PhaseAccumulator = 0; ulGrain2PhaseAccumulator = 0; } else { // increment the grains ulGrain1PhaseAccumulator += ulGrain1PhaseIncrement; ulGrain2PhaseAccumulator += ulGrain2PhaseIncrement; } // mix the grains by adding them together - to the result of adding these two 32bit values into the 12 bit ADC Output // we shift both values right 21 places to sum two 11 bit values giving a 12 bit result. uint32_t ulOutput = (ulGrain1PhaseAccumulator>>21)+(ulGrain2PhaseAccumulator>>21); // 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);}

Saturday, November 24, 2012

1) External Audio - There is now an option to use a very simple amplifier circuit which is small enough to fit inside most project boxes. This will give you a lot more volume when you are using the system outdoors.

2) Countdown - With countdown switched on, the lap timer will beep to count down the last few seconds of the current lap time - it adds a little extra pressure - to beat the lap you have to beat the beeps.

Before we get to the build, here are some user pictures of current builds, if you have pictures of your own build I am happy to share them.

ALLY - Transponder mounted in touring car chassis

Howie314 - Using an LCD Shield for a fast build

This build by Arduino Forum User Howie314 uses a LCD Keypad Shield, it keeps the size down and means there is very little soldering required to build the project. Many LCD Shields are available and all work in a similar manner meaning that you can get a lap timer up and running quickly with almost no soldering required.

If someone would design an LCD Shield with a rotary encoder as well as buttons we could have the perfect lap timer interface.

Howie314 is one of the first using the most recent version of the code with the new options of external audio and countdown. Additional options that I am considering in the near future are 1) Window magnet transponders and 2) AIM Transponders. See the end of post for more details.

Howie also did some work on the user interface as the LCD Shield uses a single analog pin to read multiple buttons. It makes sense to offer a version of the build along based on these readily available shields so I will be taking a look at Howies modifications with a plan to offer a single version of the project which can be run on standalone builds or LCD Shields - props to Howie.

ALLY's Build and Enclosure

Arduino forum user ALLY has used a standalone LCD and soldered his own buttons. This gives a bit more flexibility in the type of display and enclosures you can use, but also requires more work to build.

ALLY Transponder Mount

Here is a shot of ALLY's transponder mounted in his RC Touring car. Notice that the IR Emitter is mounted on a small post, this is a nice solution as the LED is well away from any areas that would impact in a crash.

If you want to try something similar make sure to keep the wires to the LED short. I tried wires of about 15 cm which unfortunately acted as an antenna causing radio interference.

ALLY's Carbon

ALLY has used a large carbon fibre effect enclosure, there is lots of room to add indicator LEDs, or one of my favorite features - a small amplifier for optional external speakers.

Lap Timer Piezo Buzzer and External Audio
One of the most useful features of the lap timer is instant audio feedback, this is particularly important with RC Car racing and Kart racing where corners are fractions of a second apart and checking a display is not an option.

The feedback is deliberately simple but totally effective - one beep to confirm a lap and two beeps if its a new best lap.

To add the audio features we have two options

1) A Piezo Buzzer
2) An External Speaker

I have found it useful to have both options in my build, using the quieter peizo for testing near my home and the external audio to overcome the additional noise at the track, the latest code includes a menu to switch between these two output options.

Piezo With Transistor Driver
To get as much volume as possible from the buzzer we can drive it using the battery voltage rather than the regulated 5 volts from the Arduino. To switch the higher battery voltage from an Arduino pin we use a NPN transistor connected between the piezo (the load) and ground. This arrangement allows us to switch the higher voltages and currents through the Peizo and can also be applied to other loads such as high powered LEDs, relays and other components you may want to use in your future projects.

Components
1 * 2.2K Resistor1 * P2N2222 Transistor1 * Piezo Buzzer

External Audio Through A SpeakerThe Peizo option can be used without adding the external audio option, but if you want more volume in your build read on -

In order to power a speaker we need a few more components to drive the extra current, one easy to use option is the LM386 Audio Amp chip. This is used extensively throughout the RCArduino blog in a variety of audio projects, full details and videos of the chip in action cab be found here -

Schematic - Note the audio output is from analog pin 5, for simplicity only the audio components are shown, as there

For the latest code, contact me Duane B though the Arduino forum.

Future Developments
Some common requests that I will be adding in the near future are -

1) Support for individual transponders - this will take some experimenting, but it should be possible to create a simple transponder scheme which will allow several lap timers to be used simultaneously with each car paired to a specific lap timer.

2) Support for magnetic transponders - I am told that many kart tracks use a magnetic strip under the track to activate transponders which are similar to magnetic window sensors, I hope to work with an Arduino forum user to add support for this option, it should be simple, but I have nowhere to test the system locally.

Inspired by Howie314's use of an off the shelf LCD Shield I will also add support for a build along based on this quick start option.

Thursday, November 15, 2012

This is the first in a series of posts introducing simple audio effects that can be used in micro controller projects.

Next Week - Bit Crushing effects

The Delay effect is one of the simplest and most effective enhancements we can add to our audio projects.

The delay effect works by recording the output as it is being generated and then mixing this sound back in with itself - after a delay. The result should be familiar to anyone who has every played an electric guitar through an amp with reverb.

In the case of the Auduino synthesizer the result is a mild echo effect and slightly smoother, more metallic sound - the effect can be turned on or off through a push button in the code provided below.

How do we create the delay effect
Delay is very simple to add in a microcontroller, all we need is a block of memory to record the output in.

The larger the block of memory, the longer the delay we can record and the deeper the effect.

In this case we are using a 1K block of memory in the array named sDelayBuffer -

// Duane B
// rcarduino.blogspot.com
// 15/11/2012
// Very simple ring buffer delay
// we record the output in this array
// and then mix it back in with the output as the buffer wraps around
// can be switched on and off by a button on DELAY_BUTTON
#define MAX_DELAY 1024
unsigned char sDelayBuffer[MAX_DELAY];
unsigned int nDelayCounter = 0;
unsigned char bDelay;

The other modification is inside the interrupt service routine which generates the Audiuno output, essentially what we are doing is adding the sound we recorded 1/8th of a second ago on top of the current output value -

// Duane B
// rcarduino.blogspot.com
// 15/11/2012
// add a button to set bDelay true or false to turn delay on and off
if(bDelay)
{
// Output to PWM (this is faster than using analogWrite)
// Here we add the delay buffer to the output value, this produces
// an subtle echo effect, the delay buffer is effectivley replaying the sound from
// 1/8th of a second ago.

// add the new output to the buffer so we can use it when the buffer next wraps around
sDelayBuffer[nDelayCounter] = PWM_VALUE;
nDelayCounter++;
if(nDelayCounter == MAX_DELAY)
{
nDelayCounter = 0;
}
}
else
{
LED_PORT &= ~(1 << LED_BIT); // Faster than using digitalWrite

PWM_VALUE = output;
}

We test whether delay is enabled, if it is we calculate the output value by adding the initial output to the earlier recorded output from our delay buffer. After outputting this combined value we record it in the delay buffer replacing the value we just used. Over time, the code cycles through the delay buffer over and over again, mixing the current output with a sample from 1/8th of a second back - a bit like playing your instrument in a large hall where the distinct sound is the result of the current sound being constantly mixed with its echo.

Thats all there is to generating delay in a micro controller synth engine - exactly the same code is used to create the delay effect in the RCArduino Five Dollar Synthesizer.

The RCArduino Five Dollar Synthesizer is another audio project enhanced with this delay effect -

Further Development
The amount of delay we can provide is determined to the memory we use to record the samples. In the Auduino we are using 1K which at an 8K play back rate gives us 125ms of delay. This can be increased by bit crushing the samples - using 4 bits per sample we get 250ms, 2 bits gets us half a second, with 1 bit we can get a whole second. Unfortunately initial experiments suggest that the effect is largely lost when applying these techniques, its a bit like shouting into a cave and getting a different echo back - your ears just don't buy it.

Auduino with delay is a very slight modification by Duane B (rcarduino) to the original work of Peter Knight.

Notes
- This code also include the volatile fix which allows the Auduino to work correctly in Arduino 1.0 and later
- Remember to use a pull up or pull down resistor if you are not using a push button or switch for the delay button or if your more comfortable modifying the code, replace the button code with true or false.
- LED 13, now indicates whether delay is on or off.

// Duane B
// rcarduino.blogspot.com
// 15/11/2012
// Very simple ring buffer delay
// we record the output in this array
// and then mix it back in with the output as the buffer wraps around
// can be switched on and off by a button on DELAY_BUTTON
#define MAX_DELAY 1024
unsigned char sDelayBuffer[MAX_DELAY];
unsigned int nDelayCounter = 0;
unsigned char bDelay;

// set pin mode and turn on pull up so that default mode
// is PENTATONIC, pull the pin low to switch to smooth
pinMode(SMOOTH_PIN,INPUT);
digitalWrite(SMOOTH_PIN,HIGH);
}

void loop() {
// The loop is pretty simple - it just updates the parameters for the oscillators.
//
// Avoid using any functions that make extensive use of interrupts, or turn interrupts off.
// They will cause clicks and poops in the audio.

// updated 29/01/2013
// pull the DELAY_BUTTON pin high for delay, low for no delay
// use either a pull up/pull down resistor
// or a pull up resistor with a toggle switch between the pin and ground
bDelay = digitalRead(DELAY_BUTTON);

// Duane B
// rcarduino.blogspot.com
// 15/11/2012
// add a button to set bDelay true or false to turn delay on and off
if(bDelay)
{
// Output to PWM (this is faster than using analogWrite)
// Here we add the delay buffer to the output value, this produces
// an subtle echo effect, the delay buffer is effectivley replaying the sound from
// 1/8th of a second ago.

// add the new output to the buffer so we can use it when the buffer next wraps around
sDelayBuffer[nDelayCounter] = PWM_VALUE;
nDelayCounter++;
if(nDelayCounter == MAX_DELAY)
{
nDelayCounter = 0;
}
}
else
{
LED_PORT &= ~(1 << LED_BIT); // Faster than using digitalWrite

Sunday, November 11, 2012

Many RC Transmitters and Receivers provide access to the PPM Stream, this is a single stream of pulses which includes the information for all of the receiver channels in a single connection.

This stream can often be accessed by removing the receiver case and adding a single wire to an existing connection within the receiver.

If we can gain access to this stream we can write smaller, faster code and only need a single interrupt pin to read all of the receiver channels.

Using a single built in interrupt to read multiple channels is much faster than using pin change interrupts which leads to a visibly smoother output signal for your servos and ESCs.

This post concludes with a library which can be used to read a PPM stream using a single interrupt. The same library is also able to output upto 18 servo signals from a single Arduino UNO with no additional components. This is an increase of 6 servos over the standard library - its also faster leading to fewer glitches.

Scroll down for a video of the library and an RC Receiver hack in action -

What does the PPM Stream Look Like ?

The stream is made up of a series of short pulses, the first pulse is the start marker. The time between the start marker and the start of the next pulse defines the pulse length for channel one. The time to the next pulse defines the pulse length for channel 2 and so on. The end of the pulse stream is marked by a gap know as the frame space. This gap indicates that there are no more channels to receive and the next pulse will be the start of a new frame. Each frame contains the pulse widths for all of your receiver channels.

Note - unlike servo signals, in a PPM Stream it is the gaps between pulses that defines the pulse width for a channel, not the duration of the pulse itself which can be very short.

How do we find the PPM Stream ?
If your equipment provides direct access to the PPM Stream, skip over this part, if it does not, read on.

The PPM Stream is transmitted between your transmitter and receiver as a single data stream. Inside the receiver this signal is de-multiplexed to produce the individual channels signals as seen in the diagram.

Fortunately for us, the de-multiplexer is most often just a simple shift register clocked by the PPM Stream.

The shift register is clearly visible inside this Hitec HFS-03MM Receiver - its the IC in the center.

The PPM Stream is routed to the clock pin (clock A) of the shift register, the PWM Streams for the individual channels are taken from the shift register outputs (Q1a,Q2a,Q3a).

Usually we would try to read these outputs using three separate interrupt pins, however as they are directly derived from the clock signal we can access the same information by reading the (PPM) clock signal directly. This saves us two interrupt pins and a lot of code and memory.

The best bit - read on and theres a ready made library at the end of the post.

Breaking out the PPM Signal
As above, if your receiver already provides access to the PPM Stream, skip ahead, if not, here are two receivers I have hacked. Its as simple as follows -
To access the PPM Stream from Arduino we need to solder an additional wire to the clock pin of the 4015 shift register.

The Same 4015 Shift Register inside tapped for PPM Output using thin white wire soldered to the clock pin.

A male jumper wire attached to the hacked Hitec receiver ready for connection to Arduino -

VIDEO - The RC Arduino Library and Receiver Hack in action

Schematic showing connections in the video above

Multiplexing Servos From Arduino Using PPM Style Signals

In a recent RCArduino Post we showed how a 4017 Decade counter IC could be used to control 10 servos from a single Adruino pin. Each time the counter is clocked by the Arduino it sets one servo output low and sets the next one high. We control the clock pulses to control the pulse widths for the individual servo outputs. Demulitplexing a PPM signal is essentially the same process, the RC Receiver applies a clock pulse to a shift register which shifts the pulse from one output to the next, the longer between clock pulses, the longer the servo pulse.

How do we read this RC Receiver PPM Pulse stream with a micro controller ?

Now that we have access to the PPM Stream, how do we read it with our Arduino ?

First of all we need to synchronise with the pulse stream, we do this by waiting for the long pause (the frame space) which indicates the end of one frame and the start of the next.

1) Once we have found this space, we can set our channel counter to 0 and record the current time.

2 ) The next pulse that arrives will indicate the end of the channel 1 pulse. We calculate the channel one pulse width by subtract the time recorded in 1) above from the current time. We also store the current time as the starting point for the channel 2 pulse width

3) We repeat the above process - subtract the last pulse time from the current time to get the pulse width for each of the remaining channels

4) When we have received all of the channels we expect, we start again at 1.

At each stage of the process 1-4 we know whether we are expecting a channel signal or a frame space and so we can use this information to confirm synchronization with the PPM Stream.

Sample Arduino Code For Reading RC Receiver PPM Signal

The following code is taken from the RCArduinoFastLib -// we could save a few micros by writting this directly in the signal handler rather than using attach interrupt
void CRCArduinoPPMChannels::INT0ISR()
{
// only ever called for rising edges, so no need to check the pin state

// calculate the interval between this pulse and the last one we received which is recorded in m_unChannelRiseTime
uint16_t ulInterval = TCNT1 - m_unChannelRiseTime;

// if all of the channels have been received we should be expecting the frame space next, lets check it
if(m_sCurrentInputChannel == RC_CHANNEL_IN_COUNT)
{
// we have received all the channels we wanted, this should be the frame space
if(ulInterval < MINIMUM_FRAME_SPACE)
{
// it was not so we need to resynch
forceResynch();
}
else
{
// it was the frame space, next interval will be channel 0
m_sCurrentInputChannel = 0;
}
}
else
{
// if we were expecting a channel, but found a space instead, we need to resynch
if(ulInterval > MAXIMUM_PULSE_SPACE)
{
forceResynch();
}
else
{
// its a good signal, lets record it and move onto the next channel
m_unChannelSignalIn[m_sCurrentInputChannel++] = ulInterval;
}
}
// record the current time
m_unChannelRiseTime = TCNT1;
}

Reading the PPM stream with the RCArduinoFastLib

One of the main features of the RCArduinoFastLib is a servo library, why do we need another servo library when the existing servo library is well know, widely used and reliable ?

The standard Arduino Servo library has one major flaw - it resets timer1 at the start of every frame. This means that timer1 cannot be used for timing as easily as you might want. A common approach to overcoming this is to use the micros() function for timing, but this is many times slower than accessing TCNT1 directly.

As our channel input timing and servo output timing is interrupt driven, we really care about speed, every little bit of inefficiency leads to more and bigger interrupt clashes which introduce the ticks and jitter often seen in Arduino based RC projects.

By using the RCArduinoFastServos library we avoid resetting timer1, gain an additional six servos and also the added benefit of being able to user timer1 for very fast timing of our input signals. These lead to a measurable increase in output signal quality with fewer and smaller clashes in short - glitch free projects.

Sunday, November 4, 2012

Your Arduino can only do one thing at a time, when one interrupt occurs no others can run until the current one finishes. This can cause problems in RC Projects which use interrupts for three key functions -

1) The Servo Library uses an internal interrupt to generate the servo signals
2) The Interrupts we use to read incoming RC Signals
3) The Arduino interrupt that drives the timing functions millis() and micros()

When two of our interrupts are triggered at the same time, one will be held waiting until the first one finishes. This introduces errors into our input readings and our output servo pulses.

In the example below an interrupt occurs which blocks the servo library from being able to generate the ideal pulses for our Servos and ESCs, instead we end up with an error - the length of the error is directly determined by the length of the interrupt function.

Timing clash between an interrupt and the servo library interrupt

To reduce glitches, ticks and measurement errors in our RC Projects we have to reduce the time we spend in the input and output interrupts.

RCArduinoFastLib

This post provides an improved approach to reading multiple RC Channels and introduces a new servo library which can be used for smoother, faster RC Projects.

The new library has the following features -

1) Upto 18 Servos available on an Arduino UNO - 50% more than the standard Servo library.

2) Does not reset Timer1 allowing for fast and precise timing in RC Projects using a minimal input interrupt routine

3) Support for higher refresh rates of anywhere from 50 to 500Hz depending on the number of servos

4) Uses a more direct method than digitalWrite for faster ISR Execution

5) Reduces servo glitch frequency by 75% and glitch size by a factor of 2 when used with the RCArduino channel reading code.

6) Provides two banks of upto 10 servos with independent refresh rates for each bank, allowing servos and ESCs to be refreshed at different rates in the same project.

How is the new library able to reduce glitches ?

As described in the introduction typical RC Projects include at least three sets of interrupts -

1) The internal interrupt used by the Arduino Servo library
2) To read incoming RC Signals
3) The internal interrupts used by the Arduino millis and micros functions

Interrupt clashes effect the measurement of incoming RC Signals and the generation of the output signal. The worst case is an input signal being used to generate an output where both input and output have been delayed - an error on an error.

The following plot compares an original sketch published on RCArduino with an optimised version using RCArduinoFastLib. The optimised version (red) reduces interrupt clashes by about 75% and reduces glitches result from interrupt clashes to half their original size. The result is a smoother, more stable Project.

The red line represents the current version using RCArduinoFastLib and outlined in this post.

An Optimised example sketch

Using the RCArduinoFastLib combined with the following optimisations will provide you with smoother glitch free RC Projects.

1) PinChangeInt Version 2.01 - The latest version of the PinChangeInt library includes an optimization which saves 2us over previous versions of the library. Download and install PinChangeInt version 2.01 or later from the library home page here - http://code.google.com/p/arduino-pinchangeint/

2) PCintPort::pinState - The Arduino digitalRead function takes around 1.4 us to complete. The pin change int library gives us a member variable PCintPort::pinState which we use to replace digitalRead inside our ISR and save over 1us of processing time.

3) Timer1 - Steps 1) and 2) each gave us a saving of over 1us, and so will step 3. By accessing timer1 directly to measure the incoming pulse width, we can save a few more micros. In order to do this we need to use the RCArduinoFastServo library in place of the standard servo library, swapping the servo libraries also gets us a speed boost in the servo interrupts.

4) By using timer1 directly we can use a two byte value to store our intermediate times instead of a 4 byte long. This halves the number of read,update,store operations in the ISR leading to another speed boost. It also provides a small boost to the loop function.

Update 04/11/2012- In a forum topic I mentioned to Arduino
forum users robtillaart and greygnome that the pin change int library
could be improved if a certain part of the code was made optional. The
guys took this onboard and as a result the ISR is much now faster while retaining the original functionality.
Expect pinchangeint 2.02 to be released shortly.

The
green plot shows the results of repeating the previous test with the
modified pinchangeint library combined with RCArduinoFastLib. The graph shows the maximum glitch within
a ten second period, the green line hovers between 1 and 2% this means
that in many cases the maximum error encountered in a ten second period
is only 1%, very few errors over 2% occur. Compare this to the
original blue line showing an average error of 3.5% with occasional
errors of 4 and 5%.

The RCArduinoFastLib

Over the next few weeks, I will provide some examples of the different ways in which the RCArduinoFastLib can be used. For now, here is a test sketch for reading and outputting three channels.

The sharp eyed will also notice that RCArduinoFastLib includes a PPM Reading class, next week we will look at using this to access the PPM stream inside standard RC receivers for some ultra smooth RC Projects.

// 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 unThrottleInShared;
volatile uint16_t unSteeringInShared;
volatile uint16_t unAuxInShared;

// These are used to record the rising edge of a pulse in the calcInput functions
// They do not need to be volatile as they are only used in the ISR. If we wanted
// to refer to these in loop and the ISR then they would need to be declared volatile
uint16_t unThrottleInStart;
uint16_t unSteeringInStart;
uint16_t unAuxInStart;

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 unThrottleIn;
static uint16_t unSteeringIn;
static uint16_t unAuxIn;
// local copy of update flags
static uint8_t bUpdateFlags;

// check shared update flags to see if any channels have a new signal
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.

// 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

// the following code provides simple pass through
// this is a good initial test, the Arduino will pass through
// receiver input as if the Arduino is not there.
// This should be used to confirm the circuit and power
// before attempting any custom processing in a project.

// we are checking to see if the channel value has changed, this is indicated
// by the flags. For the simple pass through we don't really need this check,
// but for a more complex project where a new signal requires significant processing
// this allows us to only calculate new values when we have new inputs, rather than
// on every cycle.
if(bUpdateFlags & THROTTLE_FLAG)
{
CRCArduinoFastServos::writeMicroseconds(SERVO_THROTTLE,unThrottleIn);
}

// COMMENT OR UNCOMMENT THIS LINE TO ENABLE THE SECOND BANK OF SERVOS
//#define MORE_SERVOS_PLEASE 1

// the first bank of servos uses OC1A - this will disable PWM on digital pin 9 - a small price for 10 fast and smooth servos
// the second bank of servos uses OC1B - this will disable PWM on digital pin 10 - a small price for 10 more fast and smooth servos

// The library blindly pulses all ten servos one and after another
// If you change the RC_CHANNEL_OUT_COUNT to 4 servos, the library will pulse them more frequently than
// it can ten -
// 10 servos at 1500us = 15ms = 66Hz
// 4 Servos at 1500us = 6ms = 166Hz
// if you wanted to go even higher, run two servos on each timer
// 2 Servos at 1500us = 3ms = 333Hz
//
// You might not want a high refresh rate though, so the setFrameSpace function is provided for you to
// add a pause before the library begins its next run through the servos
// for 50 hz, the pause should be to (20,000 - (RC_CHANNEL_OUT_COUNT * 2000))

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

//////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// CRCArduinoFastServos
//
// A class for generating signals in combination with a 4017 Counter
//
// Output upto 10 Servo channels using just digital pins 9 and 12
// 9 generates the clock signal and must be connected to the clock pin of the 4017
// 12 generates the reset pulse and must be connected to the master reset pin of the 4017
//
// The class uses Timer1, as this prevents use with the servo library
// The class uses pins 9 and 12
// The class does not adjust the servo frame to account for variations in pulse width,
// on the basis that many RC transmitters and receivers designed specifically to operate with servos
// output signals between 50 and 100hz, this is the same range as the library
//
// Use of an additional pin would provide for error detection, however using pin 12 to pulse master reset
// at the end of every frame means that the system is essentially self correcting
//
// Note
// This is a simplified derivative of the Arduino Servo Library created by Michael Margolis
// The simplification has been possible by moving some of the flexibility provided by the Servo library
// from software to hardware.
//
////////////////////////////////////////////////////////////////////////////////////////////////////////////

class CRCArduinoFastServos
{
public:
static void setup();

// configures timer1
static void begin();

// called by the timer interrupt service routine, see the cpp file for details.
static void OCR1A_ISR();

// this sets the value of the timer1 output compare register to a point in the future
// based on the required pulse with for the current servo
static void setOutputTimerForPulseDurationA() __attribute__((always_inline));

This is essentially a derivative of the Arduino Servo Library created by Michael Margolis

As the technique is very similar to the Servo class, it can be useful to study in order
to understand the servo class.

What does the library do ? It uses a very inexpensive and common 4017 Counter IC
To generate pulses to independently drive up to 10 servos from two Arduino Pins

As previously mentioned, the library is based on the techniques used in the Arduino Servo
library created by Michael Margolis. This means that the library uses Timer1 and Timer1 output
compare register A.

OCR1A is linked to digital pin 9 and so we use digital pin 9 to generate the clock signal
for the 4017 counter.

// Timer1 Output Compare A interrupt service routine
// call out class member function OCR1A_ISR so that we can
// access out member variables
ISR(TIMER1_COMPA_vect)
{
CRCArduinoFastServos::OCR1A_ISR();
}

void CRCArduinoFastServos::OCR1A_ISR()
{
// If the channel number is >= 10, we need to reset the counter
// and start again from zero.
// to do this we pulse the reset pin of the counter
// this sets output 0 of the counter high, effectivley
// starting the first pulse of our first channel
if(m_sCurrentOutputChannelA >= RC_CHANNEL_OUT_COUNT)
{
// reset our current servo/output channel to 0
m_sCurrentOutputChannelA = 0;

// After we set an output pin high, we need to set the timer to comeback for the end of the pulse
void CRCArduinoFastServos::setOutputTimerForPulseDurationA()
{
OCR1A = TCNT1 + m_ChannelOutA[m_sCurrentOutputChannelA].m_unPulseWidth;
}

#if defined(MORE_SERVOS_PLEASE)
// Timer1 Output Compare B interrupt service routine
// call out class member function OCR1B_ISR so that we can
// access out member variables
ISR(TIMER1_COMPB_vect)
{
CRCArduinoFastServos::OCR1B_ISR();
}

// After we set an output pin high, we need to set the timer to comeback for the end of the pulse
void CRCArduinoFastServos::setOutputTimerForPulseDurationB()
{
OCR1B = TCNT1 + m_ChannelOutB[m_sCurrentOutputChannelB].m_unPulseWidth;
}
#endif

// updates a channel to a new value, the class will continue to pulse the channel
// with this value for the lifetime of the sketch or until writeChannel is called
// again to update the value
void CRCArduinoFastServos::writeMicroseconds(uint8_t nChannel,uint16_t unMicroseconds)
{
// dont allow a write to a non existent channel
if(nChannel > RCARDUINO_MAX_SERVOS)
return;

// constraint the value just in case
unMicroseconds = constrain(unMicroseconds,RCARDUINO_SERIAL_SERVO_MIN,RCARDUINO_SERIAL_SERVO_MAX);

// this allows us to run different refresh frequencies on channel A and B
// for example servos at a 70Hz rate on A and ESCs at 250Hz on B
void CRCArduinoFastServos::setFrameSpaceA(uint8_t sChannel,uint16_t unMicroseconds)
{
// disable interrupts while we update the multi byte value output value
uint8_t sreg = SREG;
cli();

// we could save a few micros by writting this directly in the signal handler rather than using attach interrupt
void CRCArduinoPPMChannels::INT0ISR()
{
// only ever called for rising edges, so no need to check the pin state

// calculate the interval between this pulse and the last one we received which is recorded in m_unChannelRiseTime
uint16_t ulInterval = TCNT1 - m_unChannelRiseTime;

// if all of the channels have been received we should be expecting the frame space next, lets check it
if(m_sCurrentInputChannel == RC_CHANNEL_IN_COUNT)
{
// we have received all the channels we wanted, this should be the frame space
if(ulInterval < MINIMUM_FRAME_SPACE)
{
// it was not so we need to resynch
forceResynch();
}
else
{
// it was the frame space, next interval will be channel 0
m_sCurrentInputChannel = 0;
}
}
else
{
// if we were expecting a channel, but found a space instead, we need to resynch
if(ulInterval > MAXIMUM_PULSE_SPACE)
{
forceResynch();
}
else
{
// its a good signal, lets record it and move onto the next channel
m_unChannelSignalIn[m_sCurrentInputChannel++] = ulInterval;
}
}
// record the current time
m_unChannelRiseTime = TCNT1;
}

// if we force a resynch we set the channel
void CRCArduinoPPMChannels::forceResynch()
{
m_sCurrentInputChannel = RC_CHANNEL_IN_COUNT;