tag:blogger.com,1999:blog-72140976713104786812024-03-19T02:47:29.481-07:00RCArduinoArduino Projects, Libraries and TutorialsCan_I_Trade?http://www.blogger.com/profile/07867684474123805993noreply@blogger.comBlogger73125tag:blogger.com,1999:blog-7214097671310478681.post-46086946738905071752014-02-12T08:59:00.001-08:002014-03-05T08:21:44.936-08:00RCArduino Downloads<div dir="ltr" style="text-align: left;" trbidi="on">
Downloads available so far -<br />
<br />
Illutron B,<br />
Personal Lap Timer Part 1<br />
Latest Personal Lap Timer<br />
Serial Servos - 20 Servos from 4 Pins <br />
<br />
projects are now uploaded to google docs.<br />
<br />
To download zip files containing the projects, you will need to -<br />
<br />
1) Select the project link below<br />
<br />
2) In the window that opens look for the 'File' menu in the top left of the screen<br />
<br />
3) Open the file menu and select 'Download', this should open a popup window asking you to confirm the download, you should see the name of the project zip file in this window, if so accept the download.<br />
<br />
4) Unzip the folder inside the file and move it into your arduino projects folder. You should now be able to build the project.<br />
<br />
<br />
<b>IllutronBDemo0_2.zip</b> <br />
<a href="https://docs.google.com/file/d/0B8WjAvuO3LnIZUNLRkxwWTZKYzQ" target="_blank">https://docs.google.com/file/d/0B8WjAvuO3LnIZUNLRkxwWTZKYzQ</a><br />
<b>Original project Post</b><br />
<a href="http://rcarduino.blogspot.ae/2012/08/the-must-build-arduino-project-illutron.html" target="_blank">http://rcarduino.blogspot.ae/2012/08/the-must-build-arduino-project-illutron.html</a><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<iframe allowfullscreen='allowfullscreen' webkitallowfullscreen='webkitallowfullscreen' mozallowfullscreen='mozallowfullscreen' width='320' height='266' src='https://www.youtube.com/embed/fTmLKKLv5cE?feature=player_embedded' frameborder='0'></iframe></div>
<br />
<br />
<b>LapTimerBuildAlongPart1.zip</b><br />
<a href="https://docs.google.com/file/d/0B8WjAvuO3LnIS18wb3QzMm1LM3c" target="_blank">https://docs.google.com/file/d/0B8WjAvuO3LnIS18wb3QzMm1LM3c</a><br />
<b>Original Posts</b><br />
<a href="http://rcarduino.blogspot.com/2012/07/lap-timer-build-along-part-one.html" target="_blank">http://rcarduino.blogspot.com/2012/07/lap-timer-build-along-part-one.html</a><br />
<a href="http://rcarduino.blogspot.com/2012/11/lap-timer-part-5-buzzer-external-audio.html" target="_blank">http://rcarduino.blogspot.com/2012/11/lap-timer-part-5-buzzer-external-audio.html</a><b><br /></b><br />
<b><br /></b>
<b>LapTimerStandAloneComplexISRAudio.zip</b><br />
<b></b><a href="https://docs.google.com/file/d/0B8WjAvuO3LnId1lCVGV3ZTJ0RzQ" target="_blank">https://docs.google.com/file/d/0B8WjAvuO3LnId1lCVGV3ZTJ0RzQ</a><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<iframe allowfullscreen='allowfullscreen' webkitallowfullscreen='webkitallowfullscreen' mozallowfullscreen='mozallowfullscreen' width='320' height='266' src='https://www.youtube.com/embed/ryQFShhtzlQ?feature=player_embedded' frameborder='0'></iframe></div>
<br />
<b>Serial Servos</b><br />
<b><a href="http://rcarduino.blogspot.ae/2012/10/arduino-serial-servos-20-servos-4-pins.html">http://rcarduino.blogspot.ae/2012/10/arduino-serial-servos-20-servos-4-pins.html</a> </b><br />
<b>Download Here - </b><br />
<a href="https://drive.google.com/file/d/0B8WjAvuO3LnISmRzWWg1OHpEOGs/edit?usp=sharing">https://drive.google.com/file/d/0B8WjAvuO3LnISmRzWWg1OHpEOGs/edit?usp=sharing</a><br />
<br />
More to come as and when I get asked for them. <br />
<br />
As I am new to using google docs, please let me know if you successfully download the files.<br />
<br />
Enjoy<br />
<br />
Duane B.</div>
Can_I_Trade?http://www.blogger.com/profile/07867684474123805993noreply@blogger.com178tag:blogger.com,1999:blog-7214097671310478681.post-82521002257538327562013-07-24T11:57:00.000-07:002013-07-24T11:57:04.774-07:00PPM Output (Draft - Looking for testers)<div dir="ltr" style="text-align: left;" trbidi="on">
If we have code capable of generating servo signals its very simple to adapt this to generate a PPM output instead.<br />
<br />
In a previous post we looked at reading an incoming PPM signal and we saw how the PPM Signal describes the individual servo pulses -<br />
<a href="http://rcarduino.blogspot.ae/2012/11/how-to-read-rc-receiver-ppm-stream.html" target="_blank">http://rcarduino.blogspot.ae/2012/11/how-to-read-rc-receiver-ppm-stream.html</a><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiuJkCV9RYe5Wa2aTMVeAOEH0vnHbdX9ZhL5XNd76pQ0Xy2g4KgmtKx1qYzxVqg8e5aCT7kXES330dQuFrJShl1iNG5f9C3ShuRinup_mR9O8-yo0mnkQTgdOndG0jz9m3azki-hrC-3SeY/s1600/PPMlabel.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><br /></a></div>
To generate the PPM Stream from a set of servo commands all we need to do is change our code to toggle a single pin instead of individual pins for each servo.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiuJkCV9RYe5Wa2aTMVeAOEH0vnHbdX9ZhL5XNd76pQ0Xy2g4KgmtKx1qYzxVqg8e5aCT7kXES330dQuFrJShl1iNG5f9C3ShuRinup_mR9O8-yo0mnkQTgdOndG0jz9m3azki-hrC-3SeY/s1600/PPMlabel.PNG" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="132" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiuJkCV9RYe5Wa2aTMVeAOEH0vnHbdX9ZhL5XNd76pQ0Xy2g4KgmtKx1qYzxVqg8e5aCT7kXES330dQuFrJShl1iNG5f9C3ShuRinup_mR9O8-yo0mnkQTgdOndG0jz9m3azki-hrC-3SeY/s400/PPMlabel.PNG" width="400" /></a></div>
<br />
If we look at the point where the channel 1 pulse ends and the channel 2 pulse starts in the diagram above. The code needs to find which pin is associated with the current chanel, then set the pin low to end the pulse. It then finds the next channel, sets its pin high and then sets the timer compare register so that a interrupt routine will be called again when its time to end the new channels pulse and start the next channels pulse.<br />
<br />
To output PPM its actually a lot simpler. We are always dealing with the same pin so all we need to do is set the pin high, set the compare register so that the interrupt is called again to when we need start the next channel pulse and then we immediately set the PPM pin low again and exit. Systems which expect a PPM Input are only interested in the rising edge of the PPM Pulse so once we have set it high (giving the external system the rising pulse edge it is looking for) we can immediately set it low again and get out.<br />
<br />
<b>Timers, Compare Registers, Service Routines</b><br />
<br />
The following posts provide an indepth background to how the standard Arduino Servo library uses timers, compare registers and interrupt service routines to control upto 12 Servos. The RCArduinoFastLib uses the same approach with a number of optimizations including support for more servos, faster refresh rates, dual refresh rates and reading incoming PPM signals. The remainder of this post presents a small modification of the RCArduinoFastLib which will provide a PPM output instead of individual servo channels.<br />
<br />
<b>Background posts</b><br />
Standard Arduino servo library overview<br />
<a href="http://rcarduino.blogspot.ae/2012/01/can-i-control-more-than-x-servos-with.html" target="_blank">http://rcarduino.blogspot.ae/2012/01/can-i-control-more-than-x-servos-with.html</a><br />
<br />
RCArduinoFastLib<br />
<a href="http://rcarduino.blogspot.ae/2012/11/how-to-read-rc-channels-rcarduinofastlib.html" target="_blank">http://rcarduino.blogspot.ae/2012/11/how-to-read-rc-channels-rcarduinofastlib.html</a><br />
<a href="http://rcarduino.blogspot.ae/2012/11/how-to-read-rc-receiver-ppm-stream.html" target="_blank">http://rcarduino.blogspot.ae/2012/11/how-to-read-rc-receiver-ppm-stream.html</a><br />
<br />
<b>The Modified RCArduinoFastLib with PPM Output</b><br />
Looking for testers, PM me Duane B on the Arduino forum for a zip folder to build the library and test sketch.<br />
<br />
<br />
<br />
<br />
<br /></div>
Can_I_Trade?http://www.blogger.com/profile/07867684474123805993noreply@blogger.com48tag:blogger.com,1999:blog-7214097671310478681.post-78103407776206921512013-06-01T05:39:00.000-07:002013-06-09T22:53:03.108-07:00RC Arduino Traction Control - The Traction Alarm - Draft<div dir="ltr" style="text-align: left;" trbidi="on">
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<br />
<span id="goog_238353360"></span><span id="goog_238353361"></span>In order to control traction we first need to detect when traction has failed. This post presents a hardware and software solution for efficiently detecting when traction is lost.<br />
<br />
One of the challenges with detecting a loss of traction is that there is a lot happening. The RCArduino test vehicle generates two interrupts for each revolution of each wheel. With the four wheels rotating as much as 80 times per second, thats 2 * 4 * 80 = 640 interrupts per second. If we add two or three RC Channels we quickly approach 1000 interrupts per second.<br />
<br />
It might sound like a lot to process, 1000 interrupts in a second, but the 16Mhz processor in your Arduino is able to get through 16,000 operations in each one thousandth of a second.<br />
<br />
<br />
<br />
<table>
<tbody>
<tr>
<td>Bench Testing<br />
<br />
</td><td>Road Testing<br />
More lights = Less traction. You can see the car light up on acceleration and the lights go one by one as traction is recovered. </td>
</tr>
<tr>
<td><div class="separator" style="clear: both; text-align: center;">
<iframe allowfullscreen='allowfullscreen' webkitallowfullscreen='webkitallowfullscreen' mozallowfullscreen='mozallowfullscreen' width='320' height='266' src='https://www.youtube.com/embed/CFzVhjfR1ZA?feature=player_embedded' frameborder='0'></iframe></div>
</td>
<td><iframe allowfullscreen='allowfullscreen' webkitallowfullscreen='webkitallowfullscreen' mozallowfullscreen='mozallowfullscreen' width='320' height='266' src='https://www.youtube.com/embed/ww5RePXP12g?feature=player_embedded' frameborder='0'></iframe></td>
</tr>
</tbody></table>
<br />
<br />
<b>If the processor is so fast, why do we need to focus on efficiency ?</b><br />
<br />
The problem is that there will be times when all of our interrupts will occur at the same time. The Arduino is only able to process one interrupt at a time and so a queue will be formed. The interrupts within the queue are processed according to a fixed priority, not the time at which they occured.<br />
<br />
The interrupts we are interested in are listed below according to the priority with which they are processed<br />
<br />
INT0 - External Interrupt 0 - used to read RC Channels or wheel rotations<br />
INT1 - External Interrupt 1- used to read RC Channels or wheel rotations<br />
PCINT0 - Pin change interrupts - used to read RC Channels or wheel rotations<br />
PCINT1 - Pin change interrupts - used to read RC Channels or wheel rotations<br />
PCINT2 - Pin change interrupts - used to read RC Channels or wheel rotations<br />
TIMER1COMPA - Used by the servo library to generate output pulses<br />
<br />
If each type of interrupt takes 10 microseconds to process, we could have the signals we want to send to our ESC or steering servo (TIMER1COMPA in the table above) disrupted by upto 50 microseconds which can represent as much as a 10 percent error in the throttle and steering signals.<br />
<br />
<br />
<div style="background: #F0F0F0;">
<br />
<i>You can find a full list of the interrupt vectors and the order in which they are processed in the ATMega328 Datasheet under the second '11.4 Interrupt Vectors in ATmega328P' in the table 'Table 11-6. Reset and Interrupt Vectors in ATmega328'</i><br />
<br />
<i>You can find more background on clashing/queuing interrupts in this post introducing the RCArduinoFastLib which includes an explanation of the diagram below -</i><br />
<br />
<div style="text-align: center;">
Error introduced when interrupts collide</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi5xcuHvxpYiz33BvYuA4bGYwqb9GX9UTTVLdW9R52aPbroVanOD4SuM05cza789G_rWdcfZ5JCQ2QE1pkeiWklmsbPNugmjjvjS0cvmWsTrvw2DTeRCDvKQmxNkeucsoXq_0J6NQajy0yJ/s1600/servoclash.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="195" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi5xcuHvxpYiz33BvYuA4bGYwqb9GX9UTTVLdW9R52aPbroVanOD4SuM05cza789G_rWdcfZ5JCQ2QE1pkeiWklmsbPNugmjjvjS0cvmWsTrvw2DTeRCDvKQmxNkeucsoXq_0J6NQajy0yJ/s320/servoclash.PNG" width="320" /></a></div>
<br />
Reducing interrupt clashes in Arduino RC Projects -<br />
<a href="http://rcarduino.blogspot.ae/2012/11/how-to-read-rc-channels-rcarduinofastlib.html" target="_blank">http://rcarduino.blogspot.ae/2012/11/how-to-read-rc-channels-rcarduinofastlib.html</a><br />
<br /></div>
<br />
<b>Pin Change Interrupts Vs Port Change Interrupts </b><br />
<br />
The Arduino UNO, Leonardo, Mini and Micro have only two external interrupts, so how will we read four wheel sensors and two or more RC Channels ?<br />
<br />
In the past the RCArduinoBlog has featured the pinchangeinterrupt library, this uses a capability of the ATMega chip in your Arduino to attach an interrupt to any of the 19 pins. The pinchangeint library adds higher level code to this low level capability in order to provide very similar functionality to the two external interrupts.<br />
<br />
More here - <a href="http://rcarduino.blogspot.com/2012/03/need-more-interrupts-to-read-more.html" target="_blank">http://rcarduino.blogspot.com/2012/03/need-more-interrupts-to-read-more.html</a> <br />
<br />
This convenience comes at a high cost in terms of performance but fortunately there is a simple trick we can use to access more interrupts without the performance hit of the pinchangeint library.<br />
<br />
<b>Introducing Port Change Interrupts - A Simple Trick</b><br />
<br />
If we limit ourselves to using just one pin from each of the Arduino ports we can eliminate all of the processing overhead normally required to determine which pin has caused the interrupt. This approach provides us with a total of five highly efficient interrupts, enough for four wheel sensors and a PPM Stream containing our RC Channel information.<br />
<br />
<b>Ok, but whats a port ?</b><br />
<br />
<br />
The ATMega microprocessor in your Arduino has its pins arranged into ports, the following diagram shows how the Arduino software maps digital and analogue pins onto the ATMega 328 microprocessor ports. Similar diagrams are available for the ATMega 32u4 and ATMega 2560 used in the Leonardo and Mega. <br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://arduino.cc/en/uploads/Hacking/Atmega168PinMap2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="256" src="http://arduino.cc/en/uploads/Hacking/Atmega168PinMap2.png" width="400" /></a></div>
<br />
The diagrams all show the pin function assigned by Arduino in red accompanied by black text which gives the ATMega port, pin number and any additional functions supported by the pin.<br />
<br />
If we look at the Arduino pin labelled digital pin 2 we can see that nearest the pin, this is also labelled PD2, this tells us that the pin is pin number 2 on PORT D.<br />
<br />
The ATMega 328 has three ports -<br />
<br />
PORTB (digital pin 8 to digital pin 13)<br />
PORTC (analog pin 0 to analog pin 5)<br />
PORTD (digital pin 0 to digital pin 7)<br />
<br />
To create a port change interrupt we must select a single pin from a port. If we try to use more than one pin on a single port we will need additional code to determine which pin has changed, this will quickly use up our performance advantage.<br />
<br />
<i>As an example of the cost of having multiple pin change interrupts on a single port, the pinchangeint library is around 5 times less efficient than an external interrupt.</i><br />
<br />
If we limit ourselves to this one pin per port approach we can have three additional interrupts with no lose in performance.<br />
<br />
<b>A Quick Introduction To Direct Port Manipulation and Registers</b><br />
<br />
<br />
<div style="background: #E0E0E0;">
<br />
Have you ever seen code that looks like the following -<br />
<br />
PORTB ^= (1<<5);<br />
<br />
It
might look like low level assembly but it isn't, its accessing the chip registers directly and there are two reasons why we would want to do this -<br />
<br />
1) Performance - direct access is very fast, the code sample above could be 10 times faster than calling the equivalent digitalWrite(13,!digitalRead(13));<br />
<br />
<br />
2) Configuration - Registers set up and control each of the chips modules, sometimes we will need to access a module which is not set up by default.<br />
<br />
<br />
Registers can be thought of as a set of related switches which can be turned on and off to control some part of the chips behavior. Once you know the registers used by the modules your interested in, its as simple as programming the push button interface of a kitchen appliance. <br />
<br />
Lets walk through the set up we need for the pin change interrupts used in the traction control project.<br />
<br />
<b>Step One - Turning on pin change interrupts</b><br />
Pin change interrupts are turned on through the PCICR (Pin Change Interrupt Control Register). Keep in mind that this register is just a collection of on/off switches, in this register there are only three switches named PCIE2, PCIE1 and PCIE0. These three switches turn the pin change interrupt module on or off for PORTD, PORTC and PORTB respectively -<br />
<br />
PORTB (digital pin 8 to digital pin 13)<br />
PORTC (analog pin 0 to analog pin 5)<br />
PORTD (digital pin 0 to digital pin 7)<br />
<br />
<br />
As our project will require pin change interrupts to be enabled on all three ports, we will need to turn the three switches on by setting the corresponding bit in the register to 1, it looks like this -<br />
<br />
PCICR = (1<<PCIE2)|(1<<PCIE1)|(1<<PCIE0);<br />
<br />
Each useable bit in every register is assigned a name in the datasheet, when we use the bit name in our code, it evaluates to the bit number in the register, not the bit value. To convert the bit number to the bit value we use the following notation<br />
<br />
bit value = (1 << bit number) <br />
<br />
We can combine values using the bitwise or operator '|' and thats how we arrived at the code above (and below) -</div>
<div style="background: #E0E0E0;">
<br />
PCICR = (1<<PCIE2)|(1<<PCIE1)|(1<<PCIE0); // turn on PCIE2, PCIE1 and PCIE0 in the PCICR register<br />
<br />
<br />
<b>Step Two - Choosing our interrupt pins and linking them to a pin change interrupt</b> <br />
<br />
We have discussed that there is a major performance benefit in enabling just one pin change interrupt on each port and this is easily done through setting the corresponding but in a mask register however before we do this we should return to the pin mapping diagram and take a closer look at the secondary functions of each pin.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://arduino.cc/en/uploads/Hacking/Atmega168PinMap2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="206" src="http://arduino.cc/en/uploads/Hacking/Atmega168PinMap2.png" width="320" /></a></div>
The text between the brackets indicates the additional functions supported by each pin, for example analog input 0 has ADC0 and PCINT8 (analog to digital converter 0 and pin change interrupt 8). </div>
<div style="background: #E0E0E0;">
</div>
<div style="background: #E0E0E0;">
<br /></div>
<div style="background: #E0E0E0;">
We have two goals in selecting the pins -</div>
<div style="background: #E0E0E0;">
<br /></div>
<div style="background: #E0E0E0;">
1) Choose the pins so that we only enable one from each of the ports</div>
<div style="background: #E0E0E0;">
2) Choose pins where we do not expect to use the secondary function later in our project - I might want to use SPI for data logging for example so must avoid these pins.<br />
<br />
For now I have selected the following pins to enable our port change interrupts - </div>
<div style="background: #E0E0E0;">
<br /></div>
<div style="background: #E0E0E0;">
digital pin 4 (PCINT20,XCK,T0) // PD4, pin 4 on PORT D<br />
digital pin 8 (PCINT0,CLKO/ICP1) // PB0, pin 0 on PORT B<br />
analog input 0(ADC0,PCINT8) // PC0, pin 0 on PORT C</div>
<div style="background: #E0E0E0;">
</div>
<div style="background: #E0E0E0;">
Now that we have our pins, we can use the mask registers associated with each port to attach the interrupt.</div>
<div style="background: #E0E0E0;">
</div>
<div style="background: #E0E0E0;">
PCMSK0 = (1 << PCINT0 ); // enable pin change interrupts on PCINT0 contained in register PCMSK0</div>
<div style="background: #E0E0E0;">
PCMSK1 = (1 << PCINT8); // enabled pin change interrupts on PCINT8 contained in register PCMSK1</div>
<div style="background: #E0E0E0;">
PCMSK2 = (1 << PCINT20); // enabled pin change interrupts on PCINT20 contained in register PCMSK2</div>
<div style="background: #E0E0E0;">
<br /></div>
<div style="background: #E0E0E0;">
<br />
<br />
<i>All of this information, including the chip modules, the registers associated with each module and the individual switches within the registers can be found in the datasheet. In this case we are dealing with PCICR and the PCMSKn registers found in sections 13.2.5 to 13.2.8 of the ATMega328 Datasheet.</i><br />
<br />
<a href="http://www.arduino.cc/en/Reference/PortManipulation" target="_blank">http://www.arduino.cc/en/Reference/PortManipulation</a><br />
<br /></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
The code -<br />
<br />
<div style="background-color: #f0f0f0;">
<span style="font-family: "Courier New", Courier, monospace;">#define IDLE_PERIOD 200<br /><br />#define FLAG_FRONT_LEFT 1<br />#define FLAG_REAR_LEFT 2<br />#define FLAG_REAR_RIGHT 4 <br />#define FLAG_FRONT_RIGHT 8<br /><br /><br />volatile uint16_t unPeriodFR;<br />volatile uint16_t unPeriodFL;<br />volatile uint16_t unPeriodRR;<br />volatile uint16_t unPeriodRL;<br /><br />volatile uint8_t sFlags;<br /><br />#define GREEN_INDICATOR 9<br />#define YELLOW_INDICATOR 10<br />#define RED_INDICATOR 11<br /><br />void setup() { <br /> pinMode(3,INPUT);<br /> pinMode(4,INPUT);<br /> pinMode(8,INPUT);<br /> pinMode(A0,INPUT);<br /> pinMode(6,OUTPUT);<br /><br /> pinMode(GREEN_INDICATOR,OUTPUT);<br /> pinMode(YELLOW_INDICATOR,OUTPUT);<br /> pinMode(RED_INDICATOR,OUTPUT);<br /><br /> // enable the external interrupt<br /> EIMSK = 2; //1 - enable external interrupt 1<br /> EICRA = 12; //3 - enable interrupt on rising edge only<br /> <br /> // enable the pin change interrupts<br /> PCICR = _BV(PCIE0)|_BV(PCIE1)|_BV(PCIE2);<br /> PCMSK0 = 1;//PORT B<br /> PCMSK1 = 1;//PORT C<br /> PCMSK2 = 16;//PORT D<br /><br /> TCCR1A = 0;<br /> TCCR1B=0x05;//Prescaler= 1024 <br /> <br /> Serial.begin(9600); <br />}<br /><br />void loop() {<br /> static uint16_t unLocalPeriodFR; <br /> static uint16_t unLocalPeriodFL;<br /> static uint16_t unLocalPeriodRR;<br /> static uint16_t unLocalPeriodRL;<br /> <br /> static uint32_t ulLastUpdate;<br /> static uint8_t sLocalFlags; <br /> delay(100);<br /><br /> // Take a temporary copy of the periods <br /> uint8_t sreg = SREG;<br /> cli();<br /><br /> unLocalPeriodFR = unPeriodFR;<br /> unLocalPeriodFL = unPeriodFL;<br /> unLocalPeriodRR = unPeriodFR;<br /> unLocalPeriodRL = unPeriodFL;<br /> <br /> sLocalFlags = sFlags & (FLAG_FRONT_RIGHT | FLAG_FRONT_LEFT);<br /> sFlags = 0;<br /> <br /> SREG = sreg;<br /> <br /> if(sLocalFlags)<br /> {<br /> ulLastUpdate = millis();<br /> <br /> uint16_t unTop = unLocalPeriodFR;<br /> <br /> if(unLocalPeriodFL > unTop)<br /> {<br /> unTop = unLocalPeriodFL;<br /> }<br /> if(unLocalPeriodRR > unTop)<br /> {<br /> unTop = unLocalPeriodRR;<br /> }<br /> if(unLocalPeriodRL > unTop)<br /> {<br /> unTop = unLocalPeriodRL;<br /> }<br /> <br /> uint16_t unBottom = unLocalPeriodFR;<br /> if(unLocalPeriodFL < unBottom)<br /> {<br /> unBottom = unLocalPeriodFL;<br /> }<br /> if(unLocalPeriodRR < unBottom)<br /> {<br /> unBottom = unLocalPeriodRR;<br /> }<br /> if(unLocalPeriodRL < unBottom)<br /> {<br /> unBottom = unLocalPeriodRL;<br /> }<br /> <br /> Serial.print(unTop);<br /> Serial.print(" ");<br /> Serial.print(unBottom);<br /> Serial.print(" ");<br /> Serial.println(unTop>>3);<br /> <br /> // if the difference is greater than 1/8th of top<br /> uint16_t unDifference = (unTop - unBottom);<br /> if(unDifference > (unTop >> 3))<br /> { <br /> tone(6,69);<br /> }<br /> else<br /> {<br /> noTone(6);<br /> }<br /> <br /> digitalWrite(GREEN_INDICATOR,unDifference > (unTop >> 3)); // difference > 1/8th of top<br /> digitalWrite(YELLOW_INDICATOR,unDifference > (unTop >> 2)); // difference > 1/4 of top<br /> digitalWrite(RED_INDICATOR,unDifference > (unTop >> 1)); // difference > 1/2 of top<br /> }<br /> <br /> if((millis() - ulLastUpdate) > IDLE_PERIOD)<br /> {<br /> noTone(6);<br /> digitalWrite(GREEN_INDICATOR,LOW); // difference > 1/8th of top<br /> digitalWrite(YELLOW_INDICATOR,LOW); // difference > 1/4 of top<br /> digitalWrite(RED_INDICATOR,LOW); // difference > 1/2 of top<br /> }<br />}<br /><br />ISR(INT0_vect)<br />{<br /><br />}<br /><br />// WHEEL ATMEGA pin, ports and masks<br />// FL 5 PORTD3 8 (also INT1)<br />// FR 6 PORTD4 16<br />// RR 8 PORTB0 1<br />// RL A0 PORTC0 1<br /><br />// WHEEL ATMEGA pin, ports and masks<br />// FL 5 PORTD3 8 (also INT1)<br />ISR(INT1_vect)<br />{<br /> static uint16_t unLastTimer;<br /> <br /> if(PIND&8)<br /> {<br /> // debounce<br /> if((TCNT1-unLastTimer) > 100)<br /> {<br /> unPeriodFL = TCNT1-unLastTimer;<br /> // not strictly accurate, TCNT1 will have advanced between the previous line and this line,<br /> // however we are interested in the difference between our measurements which is not effected<br /> // by this constant error in each measurement unLastTimer = TCNT1;<br /> unLastTimer =TCNT1;<br /> sFlags |= FLAG_FRONT_LEFT;<br /> }<br /> } <br />}<br /><br />// WHEEL ATMEGA pin, ports and masks<br />// FR 6 PORTD4 16<br />ISR(PCINT2_vect)<br />{<br /> static uint16_t unLastTimer;<br /> if(PIND & 16)<br /> {<br /> // debounce<br /> if((TCNT1-unLastTimer) > 100)<br /> {<br /> unPeriodRL = TCNT1-unLastTimer;<br /> unLastTimer = TCNT1;<br /> sFlags |= FLAG_REAR_LEFT;<br /> }<br /> } <br />}<br /><br />// WHEEL ATMEGA pin, ports and masks<br />// RR 8 PORTB0 1<br />ISR(PCINT0_vect)<br />{<br /> static uint16_t unLastTimer;<br /> if(PINB & 1)<br /> {<br /> // debounce<br /> if((TCNT1-unLastTimer) > 100)<br /> {<br /> unPeriodRR = TCNT1-unLastTimer;<br /> // not strictly accurate, TCNT1 will have advanced between the previous line and this line,<br /> // however we are interested in the difference between our measurements which is not effected<br /> // by this constant error in each measurement.<br /> unLastTimer = TCNT1;<br /> sFlags |= FLAG_REAR_RIGHT; <br /> }<br /> }<br />}<br /><br />// WHEEL ATMEGA pin, ports and masks<br />// RL A0 PORTC0 1<br />ISR(PCINT1_vect)<br />{<br /> static uint16_t unLastTimer;<br /> if(PINC & 1)<br /> {<br /> // debounce<br /> if((TCNT1-unLastTimer) > 100)<br /> {<br /> <br /> unPeriodFR = TCNT1-unLastTimer;<br /> // not strictly accurate, TCNT1 will have advanced between the previous line and this line,<br /> // however we are interested in the difference between our measurements which is not effected<br /> // by this constant error in each measurement.<br /> unLastTimer = TCNT1; <br /> sFlags |= FLAG_FRONT_RIGHT;<br /> }<br /> }<br />}</span><br />
<span style="background-color: #f3f3f3;"></span></div>
<br />
Come back next time for track testing the control algorithms<br />
<br />
Duane B (RCArduino)<br />
<br /></div>
Can_I_Trade?http://www.blogger.com/profile/07867684474123805993noreply@blogger.com52tag:blogger.com,1999:blog-7214097671310478681.post-87481890054257562702013-04-25T00:42:00.001-07:002013-04-27T23:34:05.570-07:00Reading RC Channels with Arduino Due<div dir="ltr" style="text-align: left;" trbidi="on">
Update 27/04/2013<br />
<br />
The following code was originally presented as a test sketch to demonstrate a glitch in the Arduino Due micros function which is being resolved. See the following post for details of the glitch and resolution.<br />
<br />
<a href="http://arduino.cc/forum/index.php/topic,162787.0/topicseen.html" target="_blank">http://arduino.cc/forum/index.php/topic,162787.0/topicseen.html</a><br />
<br />
With this resolution in place the code presented below can be used to read 8 RC Channels and output them to a combination of upto 8 RC Servos or ESCs.<br />
<br />
The code can be operated in two configurations -<br />
<br />
1) Loop Back Test - Here 8 servo outputs are created and given fixed values from 1100 to 1800, these pulse values are output on pins 10 to 17 and can be read back in through the interrupts attached to pins 2 to 9. This is intended to give the user confidence that the code is able to read multiple RC Channels before moving to configuration 2 - Pass Through<br />
<br />
2) Pass Through - This is similar to 1) above however the input is now connected to the output such that a change in an incoming signal will cause a corresponding change in the servo output signal. To implement pass through on any channel, simply remove the comment form the start of the servo.writeMicrosecond command for that channel for example<br />
<br />
Change this -<br />
<br />
if(bUpdateFlags & CHANNEL1_FLAG)<br />
{<br />
// remove the // from the line below to implement pass through updates to the servo on this channel -<br />
//servoChannel1.writeMicroseconds(unChannel1In);<br />
Serial.print(unChannel1In);<br />
Serial.print(",");<br />
}<br />
to this -<br />
<br />
if(bUpdateFlags & CHANNEL1_FLAG)<br />
{<br />
// remove the // from the line below to implement pass through updates to the servo on this channel -<br />
servoChannel1.writeMicroseconds(unChannel1In);<br />
Serial.print(unChannel1In);<br />
Serial.print(",");<br />
}<br />
<br />
<br />
<br />
<br />
The code has been ported from an original project based on the Arduino UNO, follow the links in the comments for the background and detailed explanation.<br />
<br />
<span style="font-family: "Courier New", Courier, monospace;">// MultiChannels<br />//<br />// rcarduino.blogspot.com<br />//<br />// A simple approach for reading and writing eight RC Channels using Arduino Due interrupts<br />//<br />// See related posts -<br />// http://rcarduino.blogspot.co.uk/2012/01/how-to-read-rc-receiver-with.html<br />// http://rcarduino.blogspot.co.uk/2012/03/need-more-interrupts-to-read-more.html<br />// http://rcarduino.blogspot.co.uk/2012/01/can-i-control-more-than-x-servos-with.html<br />//<br />// rcarduino.blogspot.com<br />// <br /><br />#include "Servo.h"<br /><br />// Assign your channel in pins<br />#define CHANNEL1_IN_PIN 2<br />#define CHANNEL2_IN_PIN 3<br />#define CHANNEL3_IN_PIN 4<br />#define CHANNEL4_IN_PIN 5<br />#define CHANNEL5_IN_PIN 6<br />#define CHANNEL6_IN_PIN 7<br />#define CHANNEL7_IN_PIN 8<br />#define CHANNEL8_IN_PIN 9<br /><br />// Assign your channel out pins<br />#define CHANNEL1_OUT_PIN 10<br />#define CHANNEL2_OUT_PIN 11<br />#define CHANNEL3_OUT_PIN 12<br />#define CHANNEL4_OUT_PIN 13<br />#define CHANNEL5_OUT_PIN 14<br />#define CHANNEL6_OUT_PIN 15<br />#define CHANNEL7_OUT_PIN 16<br />#define CHANNEL8_OUT_PIN 17<br /><br />// Servo objects generate the signals expected by Electronic Speed Controllers and Servos<br />// We will use the objects to output the signals we read in<br />// this example code provides a straight pass through of the signal with no custom processing<br />Servo servoChannel1;<br />Servo servoChannel2;<br />Servo servoChannel3;<br />Servo servoChannel4;<br />Servo servoChannel5;<br />Servo servoChannel6;<br />Servo servoChannel7;<br />Servo servoChannel8;<br /><br />// These bit flags are set in bUpdateFlagsShared to indicate which<br />// channels have new signals<br />#define CHANNEL1_FLAG 1<br />#define CHANNEL2_FLAG 2<br />#define CHANNEL3_FLAG 4<br />#define CHANNEL4_FLAG 8<br />#define CHANNEL5_FLAG 16<br />#define CHANNEL6_FLAG 32<br />#define CHANNEL7_FLAG 64<br />#define CHANNEL8_FLAG 128<br /><br />// holds the update flags defined above<br />volatile uint32_t bUpdateFlagsShared;<br /><br />// shared variables are updated by the ISR and read by loop.<br />// In loop we immediatley take local copies so that the ISR can keep ownership of the<br />// shared ones. To access these in loop<br />// we first turn interrupts off with noInterrupts<br />// we take a copy to use in loop and the turn interrupts back on<br />// as quickly as possible, this ensures that we are always able to receive new signals<br />volatile uint32_t unChannel1InShared;<br />volatile uint32_t unChannel2InShared;<br />volatile uint32_t unChannel3InShared;<br />volatile uint32_t unChannel4InShared;<br />volatile uint32_t unChannel5InShared;<br />volatile uint32_t unChannel6InShared;<br />volatile uint32_t unChannel7InShared;<br />volatile uint32_t unChannel8InShared;<br /><br />void setup()<br />{<br /> Serial.begin(115200);<br /><br /> Serial.println("multiChannels");<br /><br /> // attach servo objects, these will generate the correct<br /> // pulses for driving Electronic speed controllers, servos or other devices<br /> // designed to interface directly with RC Receivers <br /> servoChannel1.attach(CHANNEL1_OUT_PIN);<br /> servoChannel2.attach(CHANNEL2_OUT_PIN);<br /> servoChannel3.attach(CHANNEL3_OUT_PIN);<br /> servoChannel4.attach(CHANNEL4_OUT_PIN);<br /> servoChannel5.attach(CHANNEL5_OUT_PIN);<br /> servoChannel6.attach(CHANNEL6_OUT_PIN);<br /> servoChannel7.attach(CHANNEL7_OUT_PIN);<br /> servoChannel8.attach(CHANNEL8_OUT_PIN);<br /><br /> // attach the interrupts used to read the channels<br /> attachInterrupt(CHANNEL1_IN_PIN, calcChannel1,CHANGE);<br /> attachInterrupt(CHANNEL2_IN_PIN, calcChannel2,CHANGE);<br /> attachInterrupt(CHANNEL3_IN_PIN, calcChannel3,CHANGE);<br /> attachInterrupt(CHANNEL4_IN_PIN, calcChannel4,CHANGE);<br /> attachInterrupt(CHANNEL5_IN_PIN, calcChannel5,CHANGE);<br /> attachInterrupt(CHANNEL6_IN_PIN, calcChannel6,CHANGE);<br /> attachInterrupt(CHANNEL7_IN_PIN, calcChannel7,CHANGE);<br /> attachInterrupt(CHANNEL8_IN_PIN, calcChannel8,CHANGE);<br /><br /> // for loop back test only, lets set each channel to a known value<br /> servoChannel1.writeMicroseconds(1100);<br /> servoChannel2.writeMicroseconds(1200);<br /> servoChannel3.writeMicroseconds(1300);<br /> servoChannel4.writeMicroseconds(1400);<br /> servoChannel5.writeMicroseconds(1500);<br /> servoChannel6.writeMicroseconds(1600);<br /> servoChannel7.writeMicroseconds(1700);<br /> servoChannel8.writeMicroseconds(1800);<br />}<br /><br />void loop()<br />{<br /> // create local variables to hold a local copies of the channel inputs<br /> // these are declared static so that thier values will be retained<br /> // between calls to loop.<br /> static uint32_t unChannel1In;<br /> static uint32_t unChannel2In;<br /> static uint32_t unChannel3In;<br /> static uint32_t unChannel4In;<br /> static uint32_t unChannel5In;<br /> static uint32_t unChannel6In;<br /> static uint32_t unChannel7In;<br /> static uint32_t unChannel8In;<br /> <br /> // local copy of update flags<br /> static uint32_t bUpdateFlags;<br /><br /> // check shared update flags to see if any channels have a new signal<br /> if(bUpdateFlagsShared)<br /> {<br /> noInterrupts(); // turn interrupts off quickly while we take local copies of the shared variables<br /><br /> // take a local copy of which channels were updated in case we need to use this in the rest of loop<br /> bUpdateFlags = bUpdateFlagsShared;<br /> <br /> // in the current code, the shared values are always populated<br /> // so we could copy them without testing the flags<br /> // however in the future this could change, so lets<br /> // only copy when the flags tell us we can.<br /> <br /> if(bUpdateFlags & CHANNEL1_FLAG)<br /> {<br /> unChannel1In = unChannel1InShared;<br /> }<br /> <br /> if(bUpdateFlags & CHANNEL2_FLAG)<br /> {<br /> unChannel2In = unChannel2InShared;<br /> }<br /> <br /> if(bUpdateFlags & CHANNEL3_FLAG)<br /> {<br /> unChannel3In = unChannel3InShared;<br /> }<br /> <br /> if(bUpdateFlags & CHANNEL4_FLAG)<br /> {<br /> unChannel4In = unChannel4InShared;<br /> }<br /> <br /> if(bUpdateFlags & CHANNEL5_FLAG)<br /> {<br /> unChannel5In = unChannel5InShared;<br /> }<br /> <br /> if(bUpdateFlags & CHANNEL6_FLAG)<br /> {<br /> unChannel6In = unChannel6InShared;<br /> }<br /> <br /> if(bUpdateFlags & CHANNEL7_FLAG)<br /> {<br /> unChannel7In = unChannel7InShared;<br /> }<br /> <br /> if(bUpdateFlags & CHANNEL8_FLAG)<br /> {<br /> unChannel8In = unChannel8InShared;<br /> }<br /> // clear shared copy of updated flags as we have already taken the updates<br /> // we still have a local copy if we need to use it in bUpdateFlags<br /> bUpdateFlagsShared = 0;<br /> <br /> interrupts(); // we have local copies of the inputs, so now we can turn interrupts back on<br /> // as soon as interrupts are back on, we can no longer use the shared copies, the interrupt<br /> // service routines own these and could update them at any time. During the update, the<br /> // shared copies may contain junk. Luckily we have our local copies to work with :-)<br /> }<br /> <br /> // do any processing from here onwards<br /> // only use the local values unChannel1, unChannel2, unChannel3, unChannel4, unChannel5, unChannel6, unChannel7, unChannel8<br /> // variables unChannel1InShared, unChannel2InShared, etc are always owned by the <br /> // the interrupt routines and should not be used in loop<br /> <br /> if(bUpdateFlags & CHANNEL1_FLAG)<br /> {<br /> // remove the // from the line below to implement pass through updates to the servo on this channel -<br /> //servoChannel1.writeMicroseconds(unChannel1In);<br /> Serial.println();<br /> Serial.print(unChannel1In);<br /> Serial.print(",");<br /> }<br /><br /> if(bUpdateFlags & CHANNEL2_FLAG)<br /> {<br /> // remove the // from the line below to implement pass through updates to the servo on this channel -<br /> //servoChannel2.writeMicroseconds(unChannel2In);<br /> Serial.print(unChannel2In);<br /> Serial.print(",");<br /> }<br /><br /> if(bUpdateFlags & CHANNEL3_FLAG)<br /> {<br /> // remove the // from the line below to implement pass through updates to the servo on this channel -<br /> //servoChannel3.writeMicroseconds(unChannel3In);<br /> Serial.print(unChannel3In);<br /> Serial.print(",");<br /> }<br /><br /> if(bUpdateFlags & CHANNEL4_FLAG)<br /> {<br /> // remove the // from the line below to implement pass through updates to the servo on this channel -<br /> // servoChannel4.writeMicroseconds(unChannel4In);<br /> Serial.print(unChannel4In);<br /> Serial.print(",");<br /> }<br /> <br /> if(bUpdateFlags & CHANNEL5_FLAG)<br /> {<br /> // remove the // from the line below to implement pass through updates to the servo on this channel -<br /> // servoChannel5.writeMicroseconds(unChannel5In);<br /> Serial.print(unChannel5In);<br /> Serial.print(",");<br /> }<br /> <br /> if(bUpdateFlags & CHANNEL6_FLAG)<br /> {<br /> // remove the // from the line below to implement pass through updates to the servo on this channel -<br /> // servoChannel6.writeMicroseconds(unChannel6In);<br /> Serial.print(unChannel6In);<br /> }<br /> <br /> if(bUpdateFlags & CHANNEL7_FLAG)<br /> {<br /> // remove the // from the line below to implement pass through updates to the servo on this channel -<br /> // servoChannel7.writeMicroseconds(unChannel7In);<br /> Serial.print(unChannel7In);<br /> Serial.print(",");<br /> }<br /> <br /> if(bUpdateFlags & CHANNEL8_FLAG)<br /> {<br /> // remove the // from the line below to implement pass through updates to the servo on this channel -<br /> // servoChannel8.writeMicroseconds(unChannel8In);<br /> Serial.print(unChannel8In);<br /> }<br /> <br /> bUpdateFlags = 0;<br />}<br /><br />void calcChannel1()<br />{<br /> static uint32_t ulStart;<br /> <br /> if(digitalRead(CHANNEL1_IN_PIN))<br /> {<br /> ulStart = micros();<br /> }<br /> else<br /> {<br /> unChannel1InShared = (uint32_t)(micros() - ulStart);<br /> bUpdateFlagsShared |= CHANNEL1_FLAG;<br /> }<br />}<br /><br />void calcChannel2()<br />{<br /> static uint32_t ulStart;<br /> <br /> if(digitalRead(CHANNEL2_IN_PIN))<br /> {<br /> ulStart = micros();<br /> }<br /> else<br /> {<br /> unChannel2InShared = (uint32_t)(micros() - ulStart);<br /> bUpdateFlagsShared |= CHANNEL2_FLAG;<br /> }<br />}<br /><br />void calcChannel3()<br />{<br /> static uint32_t ulStart;<br /> <br /> if(digitalRead(CHANNEL3_IN_PIN))<br /> {<br /> ulStart = micros();<br /> }<br /> else<br /> {<br /> unChannel3InShared = (uint32_t)(micros() - ulStart);<br /> bUpdateFlagsShared |= CHANNEL3_FLAG;<br /> }<br />}<br /><br />void calcChannel4()<br />{<br /> static uint32_t ulStart;<br /> <br /> if(digitalRead(CHANNEL4_IN_PIN))<br /> {<br /> ulStart = micros();<br /> }<br /> else<br /> {<br /> unChannel4InShared = (uint32_t)(micros() - ulStart);<br /> bUpdateFlagsShared |= CHANNEL4_FLAG;<br /> }<br />}<br /><br />void calcChannel5()<br />{<br /> static uint32_t ulStart;<br /> <br /> if(digitalRead(CHANNEL5_IN_PIN))<br /> {<br /> ulStart = micros();<br /> }<br /> else<br /> {<br /> unChannel5InShared = (uint32_t)(micros() - ulStart);<br /> bUpdateFlagsShared |= CHANNEL5_FLAG;<br /> }<br />}<br /><br />void calcChannel6()<br />{<br /> static uint32_t ulStart;<br /> <br /> if(digitalRead(CHANNEL6_IN_PIN))<br /> {<br /> ulStart = micros();<br /> }<br /> else<br /> {<br /> unChannel6InShared = (uint32_t)(micros() - ulStart);<br /> bUpdateFlagsShared |= CHANNEL6_FLAG;<br /> }<br />}<br /><br />void calcChannel7()<br />{<br /> static uint32_t ulStart;<br /> <br /> if(digitalRead(CHANNEL7_IN_PIN))<br /> {<br /> ulStart = micros();<br /> }<br /> else<br /> {<br /> unChannel7InShared = (uint32_t)(micros() - ulStart);<br /> bUpdateFlagsShared |= CHANNEL7_FLAG;<br /> }<br />}<br /><br />void calcChannel8()<br />{<br /> static uint32_t ulStart;<br /> <br /> if(digitalRead(CHANNEL8_IN_PIN))<br /> {<br /> ulStart = micros();<br /> }<br /> else<br /> {<br /> unChannel8InShared = (uint32_t)(micros() - ulStart);<br /> bUpdateFlagsShared |= CHANNEL8_FLAG;<br /> }<br />}</span><br />
<span style="font-family: "Courier New", Courier, monospace;"><br /></span></div>
Can_I_Trade?http://www.blogger.com/profile/07867684474123805993noreply@blogger.com30tag:blogger.com,1999:blog-7214097671310478681.post-25878160046137829482013-04-24T23:46:00.002-07:002013-04-27T23:35:41.822-07:00Problem reading RC Channels - The RCArduino Loop Back Test<div dir="ltr" style="text-align: left;" trbidi="on">
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<br />
<br />
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.<br />
<br />
The sketch can be used in three steps -<br />
<br />
1) Initially the sketch outputs fixed values for the servos and reads them back in as a loop back test.<br />
<br />
2) Once you are comfortable with the code, you can replace one or more of the loop back connections to start reading RC Channels. <br />
<br />
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.<br />
<br />
<br />
Step 1 - Loop back testing<br />
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).<br />
<br />
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.<br />
<br />
Step 2 - RC Channel reading<br />
<br />
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.<br />
<br />
Step 3 - Full Control <br />
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. <br />
<br />
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 -<br />
<br />
<a href="http://rcarduino.blogspot.ae/2013/04/reading-rc-channels-with-arduino-due.html" target="_blank">http://rcarduino.blogspot.ae/2013/04/reading-rc-channels-with-arduino-due.html</a><br />
<br />
For more performance and smoother output on the 8-bit Arduinos an RCArduinoFastLib versions will be added in separate in the coming days.<br />
<br />
<span style="font-family: "Courier New", Courier, monospace;"><span style="font-size: x-small;">// RCArduino MultiChannel Loop back and servo ESC control for upto 8 RC channels<br />//<br />// rcarduino.blogspot.com<br />//<br />// A simple approach for reading three RC Channels using pin change interrupts<br />//<br />// See related posts -<br />// http://rcarduino.blogspot.co.uk/2012/01/how-to-read-rc-receiver-with.html<br />// http://rcarduino.blogspot.co.uk/2012/03/need-more-interrupts-to-read-more.html<br />// http://rcarduino.blogspot.co.uk/2012/01/can-i-control-more-than-x-servos-with.html<br />//<br />// rcarduino.blogspot.com<br />// <br /><br />// include the pinchangeint library - see the links in the related topics section above for details<br />#include <PinChangeInt.h><br /><br />#include <Servo.h><br /><br />// Assign your channel in pins<br />#define CHANNEL1_IN_PIN 2<br />#define CHANNEL2_IN_PIN 3<br />#define CHANNEL3_IN_PIN 4<br />#define CHANNEL4_IN_PIN 5<br />#define CHANNEL5_IN_PIN 6<br />#define CHANNEL6_IN_PIN 7<br />#define CHANNEL7_IN_PIN 8<br />#define CHANNEL8_IN_PIN 9<br /><br />// Assign your channel out pins<br />#define CHANNEL1_OUT_PIN 10<br />#define CHANNEL2_OUT_PIN 11<br />#define CHANNEL3_OUT_PIN 12<br />#define CHANNEL4_OUT_PIN 13<br />#define CHANNEL5_OUT_PIN A0<br />#define CHANNEL6_OUT_PIN A1<br />#define CHANNEL7_OUT_PIN A2<br />#define CHANNEL8_OUT_PIN A3<br /><br />// Servo objects generate the signals expected by Electronic Speed Controllers and Servos<br />// We will use the objects to output the signals we read in<br />// this example code provides a straight pass through of the signal with no custom processing<br />Servo servoChannel1;<br />Servo servoChannel2;<br />Servo servoChannel3;<br />Servo servoChannel4;<br />Servo servoChannel5;<br />Servo servoChannel6;<br />Servo servoChannel7;<br />Servo servoChannel8;<br /><br />// These bit flags are set in bUpdateFlagsShared to indicate which<br />// channels have new signals<br />#define CHANNEL1_FLAG 1<br />#define CHANNEL2_FLAG 2<br />#define CHANNEL3_FLAG 4<br />#define CHANNEL4_FLAG 8<br />#define CHANNEL5_FLAG 16<br />#define CHANNEL6_FLAG 32<br />#define CHANNEL7_FLAG 64<br />#define CHANNEL8_FLAG 128<br /><br />// holds the update flags defined above<br />volatile uint8_t bUpdateFlagsShared;<br /><br />// shared variables are updated by the ISR and read by loop.<br />// In loop we immediatley take local copies so that the ISR can keep ownership of the<br />// shared ones. To access these in loop<br />// we first turn interrupts off with noInterrupts<br />// we take a copy to use in loop and the turn interrupts back on<br />// as quickly as possible, this ensures that we are always able to receive new signals<br />volatile uint16_t unChannel1InShared;<br />volatile uint16_t unChannel2InShared;<br />volatile uint16_t unChannel3InShared;<br />volatile uint16_t unChannel4InShared;<br />volatile uint16_t unChannel5InShared;<br />volatile uint16_t unChannel6InShared;<br />volatile uint16_t unChannel7InShared;<br />volatile uint16_t unChannel8InShared;<br /><br />void setup()<br />{<br /> Serial.begin(115200);<br /><br /> Serial.println("multiChannels");<br /><br /> // attach servo objects, these will generate the correct<br /> // pulses for driving Electronic speed controllers, servos or other devices<br /> // designed to interface directly with RC Receivers <br /> servoChannel1.attach(CHANNEL1_OUT_PIN);<br /> servoChannel2.attach(CHANNEL2_OUT_PIN);<br /> servoChannel3.attach(CHANNEL3_OUT_PIN);<br /> servoChannel4.attach(CHANNEL4_OUT_PIN);<br /> servoChannel5.attach(CHANNEL5_OUT_PIN);<br /> servoChannel6.attach(CHANNEL6_OUT_PIN);<br /> servoChannel7.attach(CHANNEL7_OUT_PIN);<br /> servoChannel8.attach(CHANNEL8_OUT_PIN);<br /><br /> // using the PinChangeInt library, attach the interrupts<br /> // used to read the channels<br /> PCintPort::attachInterrupt(CHANNEL1_IN_PIN, calcChannel1,CHANGE);<br /> PCintPort::attachInterrupt(CHANNEL2_IN_PIN, calcChannel2,CHANGE);<br /> PCintPort::attachInterrupt(CHANNEL3_IN_PIN, calcChannel3,CHANGE);<br /> PCintPort::attachInterrupt(CHANNEL4_IN_PIN, calcChannel4,CHANGE);<br /> PCintPort::attachInterrupt(CHANNEL5_IN_PIN, calcChannel5,CHANGE);<br /> PCintPort::attachInterrupt(CHANNEL6_IN_PIN, calcChannel6,CHANGE);<br /> PCintPort::attachInterrupt(CHANNEL7_IN_PIN, calcChannel7,CHANGE);<br /> PCintPort::attachInterrupt(CHANNEL8_IN_PIN, calcChannel8,CHANGE);<br /><br /> // for loop back test only, lets set each channel to a known value<br /> servoChannel1.writeMicroseconds(1100);<br /> servoChannel2.writeMicroseconds(1200);<br /> servoChannel3.writeMicroseconds(1300);<br /> servoChannel4.writeMicroseconds(1400);<br /> servoChannel5.writeMicroseconds(1500);<br /> servoChannel6.writeMicroseconds(1600);<br /> servoChannel7.writeMicroseconds(1700);<br /> servoChannel8.writeMicroseconds(1800);<br />}<br /><br />void loop()<br />{<br /> // create local variables to hold a local copies of the channel inputs<br /> // these are declared static so that thier values will be retained<br /> // between calls to loop.<br /> static uint16_t unChannel1In;<br /> static uint16_t unChannel2In;<br /> static uint16_t unChannel3In;<br /> static uint16_t unChannel4In;<br /> static uint16_t unChannel5In;<br /> static uint16_t unChannel6In;<br /> static uint16_t unChannel7In;<br /> static uint16_t unChannel8In;<br /><br /> uint8_t bUpdateFlags = 0;<br /> // check shared update flags to see if any channels have a new signal<br /> // for nicely formatted serial output use this<br /> if(bUpdateFlagsShared == 0xFF)<br /> // for more responsive projects update any channels whenever a new signal is available using this<br /> // if(bUpdateFlagsShared)<br /> {<br /> noInterrupts(); // turn interrupts off quickly while we take local copies of the shared variables<br /><br /> // take a local copy of which channels were updated in case we need to use this in the rest of loop<br /> bUpdateFlags = bUpdateFlagsShared;<br /> <br /> // in the current code, the shared values are always populated<br /> // so we could copy them without testing the flags<br /> // however in the future this could change, so lets<br /> // only copy when the flags tell us we can.<br /> <br /><br /> if(bUpdateFlags & CHANNEL1_FLAG)<br /> {<br /> unChannel1In = unChannel1InShared;<br /> }<br /> <br /> if(bUpdateFlags & CHANNEL2_FLAG)<br /> {<br /> unChannel2In = unChannel2InShared;<br /> }<br /> <br /> if(bUpdateFlags & CHANNEL3_FLAG)<br /> {<br /> unChannel3In = unChannel3InShared;<br /> }<br /><br /> if(bUpdateFlags & CHANNEL4_FLAG)<br /> {<br /> unChannel4In = unChannel4InShared;<br /> }<br /> <br /> if(bUpdateFlags & CHANNEL5_FLAG)<br /> {<br /> unChannel5In = unChannel5InShared;<br /> }<br /> <br /> if(bUpdateFlags & CHANNEL6_FLAG)<br /> {<br /> unChannel6In = unChannel6InShared;<br /> }<br /> <br /> if(bUpdateFlags & CHANNEL7_FLAG)<br /> {<br /> unChannel7In = unChannel7InShared;<br /> }<br /> <br /> if(bUpdateFlags & CHANNEL8_FLAG)<br /> {<br /> unChannel8In = unChannel8InShared;<br /> }<br /> <br /> // clear shared copy of updated flags as we have already taken the updates<br /> // we still have a local copy if we need to use it in bUpdateFlags<br /> bUpdateFlagsShared = 0;<br /> <br /> interrupts(); // we have local copies of the inputs, so now we can turn interrupts back on<br /> // as soon as interrupts are back on, we can no longer use the shared copies, the interrupt<br /> // service routines own these and could update them at any time. During the update, the<br /> // shared copies may contain junk. Luckily we have our local copies to work with :-)<br /> }<br /> <br /> // do any processing from here onwards<br /> // only use the local values unAuxIn, unThrottleIn and unSteeringIn, the shared<br /> // variables unAuxInShared, unThrottleInShared, unSteeringInShared are always owned by<br /> // the interrupt routines and should not be used in loop<br /> if(bUpdateFlags & CHANNEL1_FLAG)<br /> {<br /> // when you are ready to add a servo or esc, remove the // from the following line to use the channel input to control it<br /> // servoChannel1.writeMicroseconds(unChannel1In);<br /> Serial.println("");<br /> Serial.print(bUpdateFlags);<br /> Serial.print(",");<br /> Serial.print(unChannel1In);<br /> Serial.print(",");<br /> }<br /> <br /> if(bUpdateFlags & CHANNEL2_FLAG)<br /> {<br /> // when you are ready to add a servo or esc, remove the // from the following line to use the channel input to control it<br /> // servoChannel2.writeMicroseconds(unChannel2In);<br /> Serial.print(unChannel2In);<br /> Serial.print(",");<br /> }<br /> <br /> if(bUpdateFlags & CHANNEL3_FLAG)<br /> {<br /> // when you are ready to add a servo or esc, remove the // from the following line to use the channel input to control it<br /> // servoChannel3.writeMicroseconds(unChannel3In);<br /> Serial.print(unChannel3In);<br /> Serial.print(",");<br /> }<br /> <br /> if(bUpdateFlags & CHANNEL4_FLAG)<br /> {<br /> // when you are ready to add a servo or esc, remove the // from the following line to use the channel input to control it<br /> // servoChannel4.writeMicroseconds(unChannel4In);<br /> Serial.print(unChannel4In);<br /> Serial.print(",");<br /> }<br /> <br /> if(bUpdateFlags & CHANNEL5_FLAG)<br /> {<br /> // when you are ready to add a servo or esc, remove the // from the following line to use the channel input to control it<br /> // servoChannel5.writeMicroseconds(unChannel5In);<br /> Serial.print(unChannel5In);<br /> Serial.print(",");<br /> }<br /> <br /> if(bUpdateFlags & CHANNEL6_FLAG)<br /> {<br /> // when you are ready to add a servo or esc, remove the // from the following line to use the channel input to control it<br /> // servoChannel6.writeMicroseconds(unChannel6In);<br /> Serial.print(unChannel6In);<br /> Serial.print(",");<br /> }<br /> <br /> if(bUpdateFlags & CHANNEL7_FLAG)<br /> {<br /> // when you are ready to add a servo or esc, remove the // from the following line to use the channel input to control it<br /> // servoChannel7.writeMicroseconds(unChannel7In);<br /> Serial.print(unChannel7In);<br /> Serial.print(",");<br /> }<br /> <br /> if(bUpdateFlags & CHANNEL8_FLAG)<br /> {<br /> // when you are ready to add a servo or esc, remove the // from the following line to use the channel input to control it<br /> // servoChannel8.writeMicroseconds(unChannel8In);<br /> Serial.print(unChannel8In);<br /> Serial.print(",");<br /> }<br />}<br /><br /><br />void calcChannel1()<br />{<br /> static uint32_t ulStart;<br /> <br /> if(PCintPort::pinState) // this is equivalent to digitalRead(CHANNEL1_IN_PIN) but about 10 times faster<br /> {<br /> ulStart = micros();<br /> }<br /> else<br /> {<br /> unChannel1InShared = (uint16_t)(micros() - ulStart);<br /> bUpdateFlagsShared |= CHANNEL1_FLAG;<br /> }<br />}<br /><br />void calcChannel2()<br />{<br /> static uint32_t ulStart;<br /> <br /> if(PCintPort::pinState)<br /> {<br /> ulStart = micros();<br /> }<br /> else<br /> {<br /> unChannel2InShared = (uint16_t)(micros() - ulStart);<br /> bUpdateFlagsShared |= CHANNEL2_FLAG;<br /> }<br />}<br /><br />void calcChannel3()<br />{<br /> static uint32_t ulStart;<br /> <br /> if(PCintPort::pinState)<br /> {<br /> ulStart = micros();<br /> }<br /> else<br /> {<br /> unChannel3InShared = (uint16_t)(micros() - ulStart);<br /> bUpdateFlagsShared |= CHANNEL3_FLAG;<br /> }<br />}<br /><br />void calcChannel4()<br />{<br /> static uint32_t ulStart;<br /> <br /> if(PCintPort::pinState)<br /> {<br /> ulStart = micros();<br /> }<br /> else<br /> {<br /> unChannel4InShared = (uint16_t)(micros() - ulStart);<br /> bUpdateFlagsShared |= CHANNEL4_FLAG;<br /> }<br />}<br /><br />void calcChannel5()<br />{<br /> static uint32_t ulStart;<br /> <br /> if(PCintPort::pinState)<br /> {<br /> ulStart = micros();<br /> }<br /> else<br /> {<br /> unChannel5InShared = (uint16_t)(micros() - ulStart);<br /> bUpdateFlagsShared |= CHANNEL5_FLAG;<br /> }<br />}<br /><br />void calcChannel6()<br />{<br /> static uint32_t ulStart;<br /> <br /> if(PCintPort::pinState)<br /> {<br /> ulStart = micros();<br /> }<br /> else<br /> {<br /> unChannel6InShared = (uint16_t)(micros() - ulStart);<br /> bUpdateFlagsShared |= CHANNEL6_FLAG;<br /> }<br />}<br /><br />void calcChannel7()<br />{<br /> static uint32_t ulStart;<br /> <br /> if(PCintPort::pinState)<br /> {<br /> ulStart = micros();<br /> }<br /> else<br /> {<br /> unChannel7InShared = (uint16_t)(micros() - ulStart);<br /> bUpdateFlagsShared |= CHANNEL7_FLAG;<br /> }<br />}<br /><br />void calcChannel8()<br />{<br /> static uint32_t ulStart;<br /> <br /> if(PCintPort::pinState)<br /> {<br /> ulStart = micros();<br /> }<br /> else<br /> {<br /> unChannel8InShared = (uint16_t)(micros() - ulStart);<br /> bUpdateFlagsShared |= CHANNEL8_FLAG;<br /> }<br />}</span></span>Duane B <br />
<br />
<br /></div>
Can_I_Trade?http://www.blogger.com/profile/07867684474123805993noreply@blogger.com27tag:blogger.com,1999:blog-7214097671310478681.post-25362230762899522842013-04-11T12:06:00.000-07:002013-04-16T09:08:24.554-07:00The Problem ( and Solutions ) With Arduino Interrupts<div dir="ltr" style="text-align: left;" trbidi="on">
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.<br />
<br />
<br />
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. <br />
<br />
The 8-bitArduino boards are based on one of three related chips -<br />
<br />
ATMega328 - UNO and Mini<br />
ATMega32u4 - Leonardo and Micro<br />
ATMega2560 - Mega<br />
<br />
Each of these chips supports two types of interrupts -<br />
<br />
<b>1) External Interrupts</b><br />
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.<br />
<br />
If we want to access more interrupts we need to look at the next type -<br />
<br />
<b>2) Pin Change Interrupts</b><br />
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. <br />
<br />
<b>Differences In External Interrupts</b><br />
<br />
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 -<br />
<br />
<br />
<table border="0" cellpadding="5" style="width: 90%px;"><tbody>
<tr><td align="left"><b>Board</b></td><td align="center"><b>int.0</b></td><td align="center"><b>int.1</b></td><td align="center"><b>int.2</b></td><td align="center"><b>int.3</b></td><td align="center"><b>int.4</b></td><td align="center"><b>int.5</b></td></tr>
<tr><td align="left">Uno, Ethernet,<br />
Mini </td><td align="center">2</td><td align="center">3</td></tr>
<tr><td align="left">Mega2560</td><td align="center">2</td><td align="center">3</td><td align="center">21</td><td align="center">20</td><td align="center">19</td><td align="center">18</td></tr>
<tr><td align="left">Leonardo, Micro</td><td align="center">3</td><td align="center">2</td><td align="center">0</td><td align="center">1</td></tr>
</tbody></table>
<br />
<br />
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.<br />
<br />
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.<br />
<br />
<br />
<b>Does the Leonardo have four external interrupts ?</b><br />
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.<br />
<br />
<b>Differences In Pin Change Interrupts</b><br />
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).<br />
<br />
I initially assumed that this was also possible on the Mega, Micro and Leonardo as well. It isn't.<br />
<br />
Pin change interrupts are supported on the following Leonardo/Micro pins - 8,9,10 and 11.<br />
<br />
Pin change interrupts are supported on Arduino Mega pins 10,11,12,13,14,15 and analog pins 6 to 15<br />
<br />
<b>Interrupts and RCArduino</b><br />
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.<br />
<br />
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.<br />
<br />
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.<br />
<br />
Stay tuned<br />
<br />
Duane B</div>
Can_I_Trade?http://www.blogger.com/profile/07867684474123805993noreply@blogger.com28tag:blogger.com,1999:blog-7214097671310478681.post-77303265934987907212013-02-12T09:30:00.001-08:002013-02-12T09:31:48.861-08:00Arduino Notes, Frequencies and Scales plus a Bonus Filter.<div dir="ltr" style="text-align: left;" trbidi="on">
A quick post with a file I use a lot to experiment with Audio projects.<br />
<br />
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.<br />
<br />
The Illutron B Uses Direct Digital Synthesis, there is an introduction to the technique here -<br />
<a href="http://rcarduino.blogspot.com/2012/08/the-must-build-arduino-project-illutron.html" target="_blank">http://rcarduino.blogspot.com/2012/08/the-must-build-arduino-project-illutron.html</a><br />
<br />
<b>Saving Memory</b><br />
The note tables file uses PROGMEM to efficiently store the frequency table out of the way in program memory - <br />
<br />
<a href="http://arduino.cc/en/Reference/PROGMEM" target="_blank">http://arduino.cc/en/Reference/PROGMEM</a><br />
<br />
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.<br />
<br />
<b>Accessing The Notes</b><br />
<br />
To access a note you can use -<br />
<br />
<span style="background-color: #f3f3f3;">uint16_t unPhaseIncrement = getMidiNotePhaseIncrement(sNote);</span><br />
<br />
where sNote is between 0 and 127<br />
<br />
To access a note from one of the scales based on an analogInput you can use -<br />
<br />
<span style="background-color: #f3f3f3;">uint16_t unPhaseIncrement = getCMBluesPhaseIncrement(map(aInput,0,1024,0,CMBLUES_NOTES));</span><br />
<br />
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.<br />
<br />
<b>A Simple Test Synthesiser</b><br />
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.<br />
<br />
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.<br />
<br />
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.<br />
<br />
<b>Mozzi</b><br />
<br />
The Mozzi filter can be downloaded from <a href="http://sensorium.github.com/Mozzi/" target="_blank">http://sensorium.github.com/Mozzi/</a> <br />
<br />
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.<br />
<br />
<b>Arduino Note Tables -</b><br />
<br />
<div style="background: #F0F0F0;">
// Arduino Note Tables rcarduino.blogspot.com<br />
<br />
#ifndef _NOTETABLES_<br />
#define _NOTETABLES_<br />
<br />
#include "avr/pgmspace.h"<br />
<br />
#define MIDI_NOTES 128<br />
// used to convert midi note numbers into the increments required to generate the note in the ISR<br />
PROGMEM unsigned int midiNoteToWavePhaseIncrement[MIDI_NOTES] =<br />
{<br />
66 // 0,8.18,66.98,66 C<br />
,70 // 1,8.66,70.96,70<br />
,75 // 2,9.18,75.18,75<br />
,79 // 3,9.72,79.65,79<br />
,84 // 4,10.30,84.38,84<br />
,89 // 5,10.91,89.40,89<br />
,94 // 6,11.56,94.72,94<br />
,100 // 7,12.25,100.35,100<br />
,106 // 8,12.98,106.32,106<br />
,112 // 9,13.75,112.64,112<br />
,119 // 10,14.57,119.34,119<br />
,126 // 11,15.43,126.43,126<br />
,133 // 12,16.35,133.95,133 C<br />
,141 // 13,17.32,141.92,141<br />
,150 // 14,18.35,150.35,150<br />
,159 // 15,19.45,159.29,159<br />
,168 // 16,20.60,168.77,168<br />
,178 // 17,21.83,178.80,178<br />
,189 // 18,23.12,189.43,189<br />
,200 // 19,24.50,200.70,200<br />
,212 // 20,25.96,212.63,212<br />
,225 // 21,27.50,225.28,225<br />
,238 // 22,29.14,238.67,238<br />
,252 // 23,30.87,252.86,252<br />
,267 // 24,32.70,267.90,267 C - lowest note used on rcarduino ribbon synth<br />
,283 // 25,34.65,283.83,283<br />
,300 // 26,36.71,300.71,300<br />
,318 // 27,38.89,318.59,318 <br />
,337 // 28,41.20,337.53,337<br />
,357 // 29,43.65,357.60,357<br />
,378 // 30,46.25,378.87,378<br />
,401 // 31,49.00,401.40,401<br />
,425 // 32,51.91,425.27,425<br />
,450 // 33,55.00,450.55,450<br />
,477 // 34,58.27,477.34,477<br />
,505 // 35,61.74,505.73,505<br />
,535 // 36,65.41,535.80,535 C<br />
,567 // 37,69.30,567.66,567<br />
,601 // 38,73.42,601.42,601<br />
,637 // 39,77.78,637.18,637<br />
,675 // 40,82.41,675.07,675<br />
,715 // 41,87.31,715.21,715<br />
,757 // 42,92.50,757.74,757<br />
,802 // 43,98.00,802.79,802<br />
,850 // 44,103.83,850.53,850<br />
,901 // 45,110.00,901.11,901<br />
,954 // 46,116.54,954.69,954<br />
,1011 // 47,123.47,1011.46,1011 C<br />
,1071 // 48,130.81,1071.60,1071<br />
,1135 // 49,138.59,1135.32,1135<br />
,1202 // 50,146.83,1202.83,1202<br />
,1274 // 51,155.56,1274.36,1274<br />
,1350 // 52,164.81,1350.13,1350<br />
,1430 // 53,174.61,1430.42,1430<br />
,1515 // 54,185.00,1515.47,1515<br />
,1605 // 55,196.00,1605.59,1605<br />
,1701 // 56,207.65,1701.06,1701<br />
,1802 // 57,220.00,1802.21,1802<br />
,1909 // 58,233.08,1909.38,1909<br />
,2022 // 59,246.94,2022.92,2022<br />
,2143 // 60,261.63,2143.20,2143 C<br />
,2270 // 61,277.18,2270.64,2270<br />
,2405 // 62,293.66,2405.66,2405<br />
,2548 // 63,311.13,2548.71,2548<br />
,2700 // 64,329.63,2700.27,2700<br />
,2860 // 65,349.23,2860.83,2860<br />
,3030 // 66,369.99,3030.95,3030<br />
,3211 // 67,392.00,3211.18,3211<br />
,3402 // 68,415.30,3402.12,3402<br />
,3604 // 69,440.00,3604.42,3604<br />
,3818 // 70,466.16,3818.75,3818<br />
,4045 // 71,493.88,4045.83,4045<br />
,4286 // 72,523.25,4286.41,4286 C<br />
,4541 // 73,554.37,4541.29,4541<br />
,4811 // 74,587.33,4811.33,4811<br />
,5097 // 75,622.25,5097.42,5097<br />
,5400 // 76,659.26,5400.53,5400<br />
,5721 // 77,698.46,5721.67,5721<br />
,6061 // 78,739.99,6061.89,6061<br />
,6422 // 79,783.99,6422.36,6422<br />
,6804 // 80,830.61,6804.25,6804<br />
,7208 // 81,880.00,7208.85,7208<br />
,7637 // 82,932.33,7637.51,7637<br />
,8091 // 83,987.77,8091.66,8091<br />
,8572 // 84,1046.50,8572.82,8572 C<br />
,9082 // 85,1108.73,9082.58,9082<br />
,9622 // 86,1174.66,9622.66,9622<br />
,10194 // 87,1244.51,10194.85,10194<br />
,10801 // 88,1318.51,10801.07,10801<br />
,11443 // 89,1396.91,11443.33,11443<br />
,12123 // 90,1479.98,12123.79,12123<br />
,12844 // 91,1567.98,12844.71,12844<br />
,13608 // 92,1661.22,13608.50,13608<br />
,14417 // 93,1760.00,14417.70,14417<br />
,15275 // 94,1864.65,15275.02,15275<br />
,16183 // 95,1975.53,16183.31,16183<br />
,17145 // 96,2093.00,17145.63,17145 C<br />
,18165 // 97,2217.46,18165.16,18165<br />
,19245 // 98,2349.32,19245.31,19245<br />
,20389 // 99,2489.01,20389.70,20389<br />
,21602 // 100,2637.02,21602.14,21602<br />
,22886 // 101,2793.83,22886.67,22886<br />
,24247 // 102,2959.95,24247.58,24247<br />
,25689 // 103,3135.96,25689.42,25689<br />
,27216 // 104,3322.44,27216.99,27216<br />
,28835 // 105,3520.00,28835.39,28835<br />
,30550 // 106,3729.31,30550.04,30550<br />
,32366 // 107,3951.06,32366.63,32366<br />
,34291 // 108,4186.01,34291.26,34291 C<br />
,36330 // 109,4434.92,36330.32,36330<br />
,38490 // 110,4698.64,38490.65,38490<br />
,40779 // 111,4978.03,40779.41,40779<br />
,43204 // 112,5274.04,43204.25,43204<br />
,45773 // 113,5587.65,45773.32,45773<br />
,48495 // 114,5919.91,48495.14,48495<br />
,51378 // 115,6271.92,51378.79,51378<br />
,54433 // 116,6644.87,54433.96,54433<br />
,57670 // 117,7040.00,57670.76,57670<br />
,61100 // 118,7458.62,61100.07,61100<br />
,64733 // 119,7902.13,64733.26,64733<br />
,3046 // 120,8372.02,68582.53,3046 C<br />
,7124 // 121,8869.84,72660.64,7124<br />
,11445 // 122,9397.27,76981.30,11445<br />
,16022 // 123,9956.06,81558.77,16022<br />
,20872 // 124,10548.07,86408.50,20872<br />
,26010 // 125,11175.30,91546.65,26010<br />
,31454 // 126,11839.81,96990.28,31454<br />
,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<br />
};<br />
<br />
// Pentatonic scale<br />
// C D E G A C<br />
// to map to midi note<br />
<br />
#define PENTATONIC_NOTES 54<br />
<br />
// provides an index of pentatonic notes in the midi note table<br />
<br />
PROGMEM unsigned char sPentatonicNotes[PENTATONIC_NOTES] =<br />
{<br />
0, 2, 4, 7, 9, <br />
12, 14, 16, 19, 21, <br />
24, 26, 28, 31, 33,<br />
36, 38, 40, 43, 45,<br />
48, 50, 52, 55, 57,<br />
60, 62, 64, 67, 69,<br />
72, 74, 76, 79, 81,<br />
84, 86, 88, 91, 93,<br />
96, 98,100,103,105,<br />
108,110,112,115,117,<br />
120,122,124,127 <br />
};<br />
<br />
// C Minor Blues scale<br />
// C D# F F# G A# C<br />
// to map to midi note<br />
<br />
#define CMBLUES_NOTES 65<br />
<br />
// provides an index of pentatonic notes in the midi note table<br />
<br />
PROGMEM unsigned char sCMBluesNotes[CMBLUES_NOTES] =<br />
{<br />
0, 3, 5, 6, 7, 10, // 6<br />
12, 15, 17, 18, 19, 22, // 12<br />
24, 27, 29, 30, 31, 34, // 18<br />
36, 39, 41, 42, 43, 46, // 24<br />
48, 51, 53, 54, 55, 58, // 30<br />
60, 63, 65, 66, 67, 70, // 36 <br />
72, 75, 77, 78, 79, 82, // 42<br />
84, 87, 89, 90, 91, 94, // 48<br />
96, 99,101,102,103,106, // 54<br />
108,111,113,114,115,118, // 60<br />
120,123,125,126,127 // 65<br />
};<br />
<br />
<br />
unsigned int getMidiNotePhaseIncrement(unsigned char sNote)<br />
{<br />
if(sNote >= MIDI_NOTES)<br />
{<br />
sNote = (MIDI_NOTES - 1);<br />
}<br />
<br />
return pgm_read_word(midiNoteToWavePhaseIncrement + (sNote));<br />
}<br />
unsigned int getPentatonicPhaseIncrement(unsigned char sPentatonicNote)<br />
{<br />
if(sPentatonicNote >= PENTATONIC_NOTES)<br />
sPentatonicNote = (PENTATONIC_NOTES - 1);<br />
<br />
uint8_t sMidiIndex = pgm_read_byte(sPentatonicNotes + sPentatonicNote);<br />
<br />
return pgm_read_word(midiNoteToWavePhaseIncrement + sMidiIndex);<br />
}<br />
<br />
unsigned int getCMBluesPhaseIncrement(unsigned char sCMBluesNote)<br />
{<br />
if(sCMBluesNote >= CMBLUES_NOTES)<br />
sCMBluesNote = (CMBLUES_NOTES - 1);<br />
<br />
uint8_t sMidiIndex = pgm_read_byte(sCMBluesNotes + sCMBluesNote);<br />
return pgm_read_word(midiNoteToWavePhaseIncrement + sMidiIndex);<br />
}<br />
<br />
#endif
</div>
<br />
The very simple, very lo-fi synth<br />
<br />
<div style="background: #F0F0F0;">
// Very Lo Fi synth with Mozzi filter rcarduino.blogspot.com<br />
<br />
#include "NoteTables.h"<br />
#include "lowpass.h"<br />
<br />
#define PWM_OUT_REG OCR2B<br />
<br />
#define KEY_COUNT 20 <br />
#define KEY_WIDTH (1024/KEY_COUNT)<br />
<br />
class CAudio<br />
{<br />
public:<br />
CAudio(){}<br />
static void begin(uint8_t bStopTimer0Interrupts)<br />
{<br />
// Setup timer 1<br />
TCCR1A=0x0; // set the timer prescaler to 8 = 16/8 = 2MHz<br />
TCCR1B=0x02; // set the timer prescaler to 8 = 16/8 = 2MHz<br />
TIMSK1 |= (1<<OCIE1A); // Enable output compare match interrupt on OCR1A<br />
<br />
TCCR2A=0B10110011; //-8 bit audio PWM<br />
//TCCR0A=0x83; // Set timer waveform generation mode to FAST PWM, clear OC0A On match, set at bottom - OC0A = digital pin 6.<br />
TCCR2B=0x01; // Set to clock frequency, no prescaler<br />
<br />
pinMode(3,OUTPUT);<br />
<br />
if(bStopTimer0Interrupts)<br />
{<br />
// stops timer0 which triggers interrupts for the millis and micros functions<br />
// if we stop the timer, we get better audio, but loose millis and micros.<br />
TIMSK0 &= (~((1 << OCIE0A)| (1 << OCIE0B)));<br />
}<br />
}<br />
};<br />
<br />
<br />
#define SAW 0<br />
#define SQUARE 1<br />
#define RAMP 2<br />
<br />
<br />
class COscilator<br />
{<br />
public:<br />
void setWaveform(uint8_t sWaveform)<br />
{<br />
m_sWaveform = sWaveform;<br />
}<br />
void setPhaseIncrement(uint16_t unPhaseIncrement)<br />
{<br />
m_unPhaseIncrement = unPhaseIncrement;<br />
}<br />
uint8_t hasTriggered()<br />
{<br />
return m_unPhaseAccumulator < m_unPhaseIncrement;<br />
}<br />
void trigger()<br />
{<br />
m_unPhaseAccumulator = 0;<br />
}<br />
void updatePhase()<br />
{<br />
m_unPhaseAccumulator += m_unPhaseIncrement; <br />
}<br />
uint8_t getSample()<br />
{<br />
uint8_t sSample = m_unPhaseAccumulator>>8;<br />
switch(m_sWaveform)<br />
{<br />
case SQUARE:<br />
(sSample >= 127) ? sSample = 255 : sSample = 0;<br />
break;<br />
case RAMP:<br />
sSample = ~sSample;<br />
break;<br />
}<br />
return sSample;<br />
}<br />
protected:<br />
uint16_t m_unPhaseIncrement;<br />
uint16_t m_unPhaseAccumulator;<br />
uint8_t m_sWaveform;<br />
};<br />
<br />
COscilator Osc1,Osc2,LFO,LFOFilter;<br />
<br />
uint16_t getPhaseIncrement(uint16_t aInput,uint8_t sOffset)<br />
{<br />
uint8_t sKey = aInput/KEY_WIDTH;<br />
<br />
// find where on the ribbon the key starts <br />
uint16_t unKeyStart = sKey * KEY_WIDTH;<br />
<br />
// find where on the ribbon the key ends<br />
uint16_t unKeyEnd = (sKey+1) * KEY_WIDTH;<br />
<br />
// map where the current input sits between the key start and key end to a frequency that sits at the same point between<br />
// the frequency of the key and the frequency of the next key<br />
<br />
return map(aInput,unKeyStart,unKeyEnd,getMidiNotePhaseIncrement(sKey+sOffset),getMidiNotePhaseIncrement(sKey+1+sOffset));<br />
}<br />
<br />
LowPassFilter lpFilter;<br />
uint16_t gFilter;<br />
<br />
void setup()<br />
{<br />
Serial.begin(9600);<br />
CAudio::begin(true); <br />
<br />
Osc1.setWaveform(RAMP);<br />
Osc2.setWaveform(RAMP);<br />
LFO.setWaveform(SAW);<br />
LFOFilter.setWaveform(RAMP);<br />
}<br />
<br />
void loop()<br />
{<br />
uint16_t aInput = analogRead(1);<br />
if(aInput >= 1021)<br />
{<br />
Osc1.setPhaseIncrement(0);<br />
Osc2.setPhaseIncrement(0);<br />
LFO.setPhaseIncrement(0);<br />
LFOFilter.trigger();<br />
}<br />
else<br />
{<br />
uint8_t sOffset = 20;//((analogRead(1) >> 3)/12)*12;<br />
uint16_t unPhaseIncrement = getCMBluesPhaseIncrement(map(aInput,0,1024,0,CMBLUES_NOTES));<br />
Osc1.setPhaseIncrement(analogRead(2)<<2);<br />
Osc2.setPhaseIncrement(analogRead(3)<<5);<br />
<br />
//lpFilter.setCutoffFreq(analogRead(4)>>2);<br />
lpFilter.setResonance(analogRead(5)>>2);<br />
<br />
LFO.setPhaseIncrement(unPhaseIncrement); <br />
<br />
uint16_t unFilter = analogRead(4);<br />
if(unFilter < 512)<br />
{<br />
LFOFilter.setPhaseIncrement(0);<br />
unFilter>>=1;<br />
<br />
uint8_t sreg = SREG;<br />
cli();<br />
gFilter = unFilter;<br />
SREG = sreg;<br />
}<br />
else<br />
{<br />
uint8_t sreg = SREG;<br />
cli();<br />
gFilter = unFilter;<br />
SREG = sreg;<br />
<br />
LFOFilter.setPhaseIncrement((gFilter-512));<br />
}<br />
}<br />
}<br />
<br />
<br />
<br />
#define DELAY_LENGTH 1024<br />
<br />
// iterate the grains and LFO <br />
SIGNAL (TIMER1_COMPA_vect) <br />
{<br />
static uint8_t sDelayBuffer[DELAY_LENGTH];<br />
static uint16_t sDelayIndex;<br />
<br />
sDelayIndex++; <br />
if(sDelayIndex >= DELAY_LENGTH)<br />
{<br />
sDelayIndex = 0;<br />
}<br />
<br />
OCR1A += 125;<br />
<br />
Osc1.updatePhase();<br />
Osc2.updatePhase();<br />
LFO.updatePhase();<br />
LFOFilter.updatePhase();<br />
<br />
if(LFO.hasTriggered())<br />
{<br />
Osc1.trigger();<br />
Osc2.trigger();<br />
}<br />
<br />
if(gFilter < 512)<br />
{<br />
lpFilter.setCutoffFreq(gFilter>>1);<br />
}<br />
else<br />
{<br />
lpFilter.setCutoffFreq(LFOFilter.getSample());<br />
}<br />
<br />
uint16_t nOutput = lpFilter.next((Osc1.getSample()>>1)+(Osc2.getSample()>>1));<br />
if(nOutput > 255)<br />
nOutput = 255;<br />
PWM_OUT_REG = nOutput;<br />
<br />
// not being mixed in to output but easy to add, see rcarduino.blogspot.com <br />
sDelayBuffer[sDelayIndex] = PWM_OUT_REG;<br />
}<br />
<br />
<br />
<br /></div>
<br />
And don't forget the Mozzi filter 'lowpass.h' from https://github.com/sensorium/Mozzi<br />
<br />
Duane B<b><br /></b></div>
Can_I_Trade?http://www.blogger.com/profile/07867684474123805993noreply@blogger.com28tag:blogger.com,1999:blog-7214097671310478681.post-36536764299759648382013-02-05T09:40:00.002-08:002013-02-05T09:40:33.863-08:00RCArduino Libraries FAQ<div dir="ltr" style="text-align: left;" trbidi="on">
A central point for questions and help on using the RCArduino Libraries.<br />
<br />
The RCArduino Libraries provides servo output, PPM input and individual RC Channel input.<br />
<br />
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. <br />
<br />
<b>1) Why a new servo and RC library ?</b><br />
<br />
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.<br />
<br />
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.<br />
<br />
<br />
<br />
An detailed explanation can be found here -<br />
<br />
<a href="http://rcarduino.blogspot.com/2012/11/how-to-read-rc-channels-rcarduinofastlib.html" target="_blank">http://rcarduino.blogspot.com/2012/11/how-to-read-rc-channels-rcarduinofastlib.html</a><br />
<br />
<b>2) Can I use the library to read PPM signals and output RC Channels ?</b><br />
<br />
Yes, an example is provided here, but come back here for more documentation if you need it.<br />
<br />
<a href="http://rcarduino.blogspot.com/2012/11/how-to-read-rc-receiver-ppm-stream.html" target="_blank">http://rcarduino.blogspot.com/2012/11/how-to-read-rc-receiver-ppm-stream.html</a><br />
<br />
<b>3) Can I use the library to read individual RC Channels and output RC Channels ? </b><br />
<br />
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.<br />
<br />
<a href="http://rcarduino.blogspot.com/2012/11/how-to-read-rc-channels-rcarduinofastlib.html" target="_blank">http://rcarduino.blogspot.com/2012/11/how-to-read-rc-channels-rcarduinofastlib.html</a><br />
<br />
Some background on the pinchangeint library if you need more than 2 interrupts on an UNO or more than 6 on a Mega -<br />
<br />
<a href="http://rcarduino.blogspot.com/2012/03/need-more-interrupts-to-read-more.html" target="_blank">http://rcarduino.blogspot.com/2012/03/need-more-interrupts-to-read-more.html</a><br />
<b>How do we use the library ?</b><br />
<br />
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.<br />
<br />
To make this as simple as possible, two #define's are used in the RCArduinoFastLib.h file.<br />
<br />
To set the number of input channels if your going to read a PPM Stream change the number here -<br />
// Change to set the number of channels in PPM Input stream<br />
#define RC_CHANNEL_IN_COUNT 3<br />
<br />
To set the number of servos in your project change the number here -<br />
<br />
// Change to set the number of servos/ESCs<br />
#define RC_CHANNEL_OUT_COUNT 4<br />
<br />
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.<br />
<br />
To generate a 50Hz refresh rate which is equivalent to the standard servo library we can use the following calculation -<br />
<br />
1/50 = 20,000 microseconds<br />
frame space = 20,000 - (2,000 * number of servos)<br />
<br />
<b>An Example -</b><br />
To drive 8 servos we would set RC_CHANNEL_OUT_COUNT to (8 + 1)= 9. Remember the additional entry is for our framespace.<br />
<br />
#define RC_CHANNEL_OUT_COUNT 9<br />
<br />
we can then calculate<br />
<br />
frame space = 20,000 - (2,000 * (RC_CHANNEL_OUT_COUNT - 1) )<br />
<br />
Note we take away 1 so that we do not include the framespace in the framespace calculation<br />
<br />
frame space = 20,000 - (2,000 * 8)<br />
frame space = 4,000<br />
<br />
So to set the frame space to give a refresh rate of 50 Hz for 8 servos we call<br />
<br />
<span style="font-family: "Courier New", Courier, monospace; font-size: xx-small;">CRCArduinoFastServos::setFrameSpaceA(SERVO_FRAME_SPACE,4000);</span><br />
<br />
<b>Why the maths ?</b><br />
<b> </b><br />
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.<br />
<br />
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.<br />
<br />
I am also considering automating the calculation through a convenience function in a future release, stay tuned.<br />
<br />
Now that we have that out of the way, its all very easy.<br />
<br />
<b>Attaching A Servo </b><br />
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.<br />
<br />
<span style="font-family: "Courier New", Courier, monospace; font-size: xx-small;"> CRCArduinoFastServos::attach(SERVO_THROTTLE/* Servo index */,THROTTLE_OUT_PIN /* the pin we want it attached to */);</span><br />
<br />
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.<br />
<br />
<b>Understanding The Library and Reading and Writing Channels</b><br />
<br />
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 -<br />
<br />
<span style="font-family: "Courier New", Courier, monospace; font-size: xx-small;">CRCArduinoFastServos::writeMicroseconds(SERVO_STEERING /* 0 based index into servo array */,unSteeringIn);</span><br />
<span style="font-family: "Courier New", Courier, monospace; font-size: xx-small;">CRCArduinoPPMChannels::getChannel(SERVO_STEERING </span><span style="font-family: "Courier New", Courier, monospace; font-size: xx-small;"><span style="font-family: "Courier New", Courier, monospace; font-size: xx-small;"> /* 0 based index into PPM input array */</span>);</span><br />
<span style="font-family: "Courier New", Courier, monospace; font-size: xx-small;"><br /></span>
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.<br />
<br />
To get started try the examples provided - <br />
<br />
<a href="http://rcarduino.blogspot.com/2012/11/how-to-read-rc-channels-rcarduinofastlib.html" target="_blank">http://rcarduino.blogspot.com/2012/11/how-to-read-rc-channels-rcarduinofastlib.html</a><br />
<br /><a href="http://rcarduino.blogspot.com/2012/11/how-to-read-rc-receiver-ppm-stream.html" target="_blank">http://rcarduino.blogspot.com/2012/11/how-to-read-rc-receiver-ppm-stream.html</a><br />
<br />
For any help, use the comments section below<br />
<br />
Duane B </div>
Can_I_Trade?http://www.blogger.com/profile/07867684474123805993noreply@blogger.com36tag:blogger.com,1999:blog-7214097671310478681.post-24132691835348502442013-01-13T08:55:00.001-08:002013-01-13T08:56:25.066-08:00New Arduino Board - Esplora<div dir="ltr" style="text-align: left;" trbidi="on">
A new Arduino Board has recently been announced - the Arduino Esplora.<br />
<br />
Its a very different proposition to previous Arduino boards in that it is hardwired with a large number of built in sensors -<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://farm9.staticflickr.com/8044/8134573901_63952a4f17_b.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="143" src="http://farm9.staticflickr.com/8044/8134573901_63952a4f17_b.jpg" width="320" /></a></div>
<br />
<div style="background: #F0F0F0;">
The Esplora has the following on-board inputs and outputs :<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://farm9.staticflickr.com/8069/8209014766_1b5a58e3c2_c.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="182" src="http://farm9.staticflickr.com/8069/8209014766_1b5a58e3c2_c.jpg" width="400" /></a></div>
<br />
<ul>
<li><b>Analog joystick with central push-button</b> two axis (X and Y) and a center pushbutton.
</li>
<li><b>4 push-buttons</b> laid out in a diamond pattern.
</li>
<li><b>Linear potentiometer</b> slider near the bottom of the board.
</li>
<li><b>Microphone</b> for getting the loudness (amplitude) of the surrounding environment.
</li>
<li><b>Light sensor</b> for getting the brightness.
</li>
<li><b>Temperature sensor</b> reads the ambient temperature
</li>
<li><b>Three-axis accelerometer</b> measures the board's relation to gravity on three axes (X, Y, and Z)
</li>
<li><b>Buzzer</b> can produce square-waves.
</li>
<li><b>RGB led</b> bright LED with Red Green and Blue elements for color mixing.
</li>
<li><b>2 <span class="wikiword">TinkerKit</span> Inputs</b> to connect the <span class="wikiword">TinkerKit</span> sensor modules with the 3-pin connectors.
</li>
<li><b>2 <span class="wikiword">TinkerKit</span> Outputs</b> to connect the <span class="wikiword">TinkerKit</span> actuator modules with the 3-pin connectors.
</li>
<li><b>TFT display connector</b> connector for an optional color LCD screen, SD card, or other devices that use the SPI protocol. </li>
</ul>
Information reposted from the official Arduino site where full details of the Arduino Esplora can be found -<br />
<br />
<a href="http://arduino.cc/en/Main/ArduinoBoardEsplora" target="_blank">http://arduino.cc/en/Main/ArduinoBoardEsplora</a><br />
<br /></div>
<br />
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.<br />
<br />
To get up and running quickly with the new inputs and outputs, the Esplora has its own set of easy to use libraries. <br />
<br />
<div style="background: #F0F0F0;">
Esplora libraries -<br />
<br />
<b>readSlider() </b><br />
<b>readLightSensor() </b><br />
<b>readTemperature() </b><br />
<b>readMicrophone() </b><br />
<b>readJoystickSwitch() </b><br />
<b>readJoystickButton() </b><br />
<b>readAccelerometer() </b><br />
<b>readButton() </b><br />
<b>readJoystickX() </b><br />
<b>readJoystickY() </b><br />
<b>writeRGB() </b><br />
<b>writeRed() </b><br />
<b>writeGreen() </b><br />
<b>writeBlue() </b><br />
<b>readRed() </b><br />
<b>readGreen() </b><br />
<b>readBlue() </b><br />
<b>tone() </b><br />
<b>noTone() </b><br />
<br />
reposted from the official Arduino website - <a href="http://arduino.cc/en/Reference/EsploraLibrary">http://arduino.cc/en/Reference/EsploraLibrary</a><br />
</div>
<br />
To get a flavour of what can be achieved out of the box with the new Arduino Esplora, check out the example sketches<br />
<br />
<div style="background: #F0F0F0;">
<b>EsploraBlink</b> : Blink the Esplora's RGB LED<br />
<b>EsploraAccelerometer</b> : Read the values from the accelerometer<br />
<b>EsploraJoystickMouse</b> : Use the Esplora's joystick to control the cursor on your computer
<b>EsploraLedShow</b> : Use the Joystick and slider to create a light show with the LED<br />
<b>EsploraLedShow2</b> : Use the Esplora's microphone, linear potentiometer, and light sensor to change the color of the onboard LED.<br />
<b>EsploraLightCalibrator</b> : Read the values from the accelerometer<br />
<b>EsploraMusic</b> : Make some music with the Esplora<br />
<b>EsploraSoundSensor</b> : Read the values from the Esplora's microphone<br />
<b>EsploraTemperatureSensor</b> : Read the temperature sensor and get the temperature in in Farhenheit or Celsius.<br />
<br />
<b>Experts</b><br />
<b>EsploraKart</b> : Use the Esplora as a controller to play a kart racing game.<br />
<b>EsploraTable</b> : Print the Esplora sensor information to a table format.<br />
<b>EsploraRemote</b> : Connect the Esplora to Processing and control the outputs.<br />
<b>EsploraPong</b> : Play Pong with the Esplora using Processing.<br />
<br />
For the original list see the official Arduino website link - <a href="http://arduino.cc/en/Reference/EsploraLibrary" target="_blank">http://arduino.cc/en/Reference/EsploraLibrary</a>
</div>
</div>
Stay Tuned
Duane BCan_I_Trade?http://www.blogger.com/profile/07867684474123805993noreply@blogger.com4tag:blogger.com,1999:blog-7214097671310478681.post-9319283774474825342012-12-29T08:10:00.001-08:002013-01-05T08:16:48.530-08:00New Year Projects<div dir="ltr" style="text-align: left;" trbidi="on">
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
</div>
A quick teaser of some of the projects and components that will be featured on RC Arduino in the new year.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhbwp0QH0xgjqQC1b_bUM5-bOKtSD6cTW5LjF5xZFoNGG3COcrvB_ogR55_mGGpcR8yVCVnMzcv5bYePKqqA5Nlg4nknVapbVJ6wcJF6rV3M_yjt0u5xAlC9hO6G1QU59Nk4D7Kb0Zovrx3/s1600/screenstickmic.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="267" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhbwp0QH0xgjqQC1b_bUM5-bOKtSD6cTW5LjF5xZFoNGG3COcrvB_ogR55_mGGpcR8yVCVnMzcv5bYePKqqA5Nlg4nknVapbVJ6wcJF6rV3M_yjt0u5xAlC9hO6G1QU59Nk4D7Kb0Zovrx3/s640/screenstickmic.png" width="640" /></a></div>
TFT Screen, 4 * Bi Color LED Matrix, Analogue Joystick, Microphone <br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgLmhN81e43zd5hJXk1htQa1BbyLWLAi316H8jrtbEuCzdRcx5kPbx8Ekh-4soZ6aPTg1YtcgIpuYoxFjag0Qn1eauxJkK9MwpKVvVqfiZKv9ucIQfzi5RPOThldjDKMYb0AA-kG59n0l-8/s1600/screensticksoftpot.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="382" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgLmhN81e43zd5hJXk1htQa1BbyLWLAi316H8jrtbEuCzdRcx5kPbx8Ekh-4soZ6aPTg1YtcgIpuYoxFjag0Qn1eauxJkK9MwpKVvVqfiZKv9ucIQfzi5RPOThldjDKMYb0AA-kG59n0l-8/s640/screensticksoftpot.png" width="640" /></a></div>
Analogue Joystick, Touch Screen Overlay, Soft Pot.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEguOeTl962ELZopU8ovmehLX5bY1C7IJeRVC1c2wfD4quUWYDRlNaXa3oqcKShi6b-O3OYXTszfRKCalRZlnA7kNDpfGS4bSyXyq3qSlzTSev_tF5N1aPgH9VPr3vRvygUNDcMZ61jdXhyphenhyphenF/s1600/minisynths.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="420" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEguOeTl962ELZopU8ovmehLX5bY1C7IJeRVC1c2wfD4quUWYDRlNaXa3oqcKShi6b-O3OYXTszfRKCalRZlnA7kNDpfGS4bSyXyq3qSlzTSev_tF5N1aPgH9VPr3vRvygUNDcMZ61jdXhyphenhyphenF/s640/minisynths.png" width="640" /></a></div>
A small collection of mini synths - Korg Monotron, Dubreq Stylophone, Bleep Labs Nebulophone.<br />
<br />
<b>RC Car Active Power Distribution </b><br />
I am also planning to bring this monster back to life -<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjCKV548SlEjgi0R9f1JQzTtjP4XarkWAv4BmnXk9gdKbibQ2VRGViKTjncHzjwlfauEoaxtfwnVOhxpGDKqfU0p6ru9sqUXNgl6QVqg7hhX_XsTDK3fcXZwmHbMGTFB2OoPCKAMbgDjMv8/s1600/m05.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="188" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjCKV548SlEjgi0R9f1JQzTtjP4XarkWAv4BmnXk9gdKbibQ2VRGViKTjncHzjwlfauEoaxtfwnVOhxpGDKqfU0p6ru9sqUXNgl6QVqg7hhX_XsTDK3fcXZwmHbMGTFB2OoPCKAMbgDjMv8/s400/m05.PNG" width="400" /></a></div>
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.<br />
<br />
Stay Tuned<br />
<br />
DUane B</div>
Can_I_Trade?http://www.blogger.com/profile/07867684474123805993noreply@blogger.com5tag:blogger.com,1999:blog-7214097671310478681.post-47938199862002447972012-12-01T11:27:00.000-08:002013-06-09T08:35:39.491-07:00Arduino Due DDS - Part 1 - Sinewaves and Fixed Point Maths<div dir="ltr" style="text-align: left;" trbidi="on">
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). <br />
<br />
A related sketch which provides more interesting audio output without the explanation can be found here -<br />
<a href="http://rcarduino.blogspot.com/2012/11/quick-and-dirty-synth-for-arduino-due.html" target="_blank">http://rcarduino.blogspot.com/2012/11/quick-and-dirty-synth-for-arduino-due.html</a><br />
<br />
All of the Audio projects on RCArduino are based on this same DDS technique described below, see the following links for DDS Projects -<br />
<a href="http://rcarduino.blogspot.com/2012/10/five-dollar-synthesiser.html" target="_blank">http://rcarduino.blogspot.com/2012/10/five-dollar-synthesiser.html</a><br />
<a href="http://rcarduino.blogspot.com/2012/08/the-must-build-arduino-project-illutron.html" target="_blank">http://rcarduino.blogspot.com/2012/08/the-must-build-arduino-project-illutron.html</a><br />
<br />
<a href="http://rcarduino.blogspot.com/2012/11/auduino-with-delay.html" target="_blank">http://rcarduino.blogspot.com/2012/11/auduino-with-delay.html</a><br />
<a href="http://rcarduino.blogspot.com/2012/08/adding-audio-to-arduino-projects.html" target="_blank">http://rcarduino.blogspot.com/2012/08/adding-audio-to-arduino-projects.html</a><br />
<a href="http://rcarduino.blogspot.com/2012/10/arduino-modular-synthesizer-part-one.html" target="_blank">http://rcarduino.blogspot.com/2012/10/arduino-modular-synthesizer-part-one.html</a><br />
<br />
<b>Direct Digital Synthesis - Introduction</b><br />
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.<br />
<br />
<b>The 600 sample 12 Bit sine wave table used in the sketch below plotted on screen using processing -</b><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhCSZSDTjPUGnT_muEsSS6YyErIJkxdSjrIenTV_EzIGZ7QBlW1ga-0fxkdKs0d6Mm8uJoOhXHpnOZIA7IRBy_ZmZs1IJ34siT6zzOUntvxD6pTqrpTYXpJJygWgOCyT_gop369rljL-9Qg/s1600/sinewave.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="136" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhCSZSDTjPUGnT_muEsSS6YyErIJkxdSjrIenTV_EzIGZ7QBlW1ga-0fxkdKs0d6Mm8uJoOhXHpnOZIA7IRBy_ZmZs1IJ34siT6zzOUntvxD6pTqrpTYXpJJygWgOCyT_gop369rljL-9Qg/s320/sinewave.PNG" width="320" /></a></div>
<br />
<b>The 128 sample 8 Bit wave tables used in the Illutron B synth -</b><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjxjoW_ZmacAqMr24VcZXKwIQTb8Xoegpv9p17HYxg_-mmad_COMt5CRRj9_0ziG4c7GTNdabvVTPikFU1CQp59UggRaTnK6U7zymtYmy8sqtG1bQxxgiTiV5QUF-_VIbxDHgHbrId8ByuJ/s1600/wavetables.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjxjoW_ZmacAqMr24VcZXKwIQTb8Xoegpv9p17HYxg_-mmad_COMt5CRRj9_0ziG4c7GTNdabvVTPikFU1CQp59UggRaTnK6U7zymtYmy8sqtG1bQxxgiTiV5QUF-_VIbxDHgHbrId8ByuJ/s320/wavetables.PNG" width="320" /></a></div>
<br />
<br />
<b>Direct Digital Synthesis - Sample Rate</b><br />
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.<br />
<br />
<i>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.</i><br />
<br />
<b>Direct Digital Synthesis - Frequency Generation</b><br />
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.<br />
<br />
<b>Direct Digital Synthesis - Implementation and Terminology</b><br />
The following terms are commonly used in reference to DDS and variations are used throughout the RC Arduino audio projects.<br />
<br />
1) Phase Accumulator - this is an array index which points to the current sample in the selected wavetable.<br />
2) Phase Increment - this value is used to advance the Phase Accumulator each time the interrupt is called.<br />
<br />
<b>Example usage of the phase accumulator used inside the timer interrupt to select the next sample in the wave table array -</b><br />
<br />
// Update the phase accumulator by adding the phase increment <br />
ulPhaseAccumulator += ulPhaseIncrement;<br />
<br />
// get the current sample from the sine table using the phase accumulator as the index <br />
uint32_t ulOutput = nSineTable[ulPhaseAccumulator>>20];<br />
<br />
<b>Direct Digital Synthesis - Fixed Point Maths</b> <br />
To generate a full range of frequencies we will often need to use fractional values for our phase accumulator.<br />
<br />
<br />
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. <br />
<br />
<b>Fixed Points For Higher Integer Precision</b> <br />
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.<br />
<br />
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.<br />
<br />
We can represent this range with 10 bits but if we use 16 bits we have 6 additional bits of precession.<br />
<br />
<br />
<table border="0" cellpadding="0" cellspacing="0" style="width: 816px;"><colgroup><col style="width: 48pt;" width="64"></col>
<col span="16" style="mso-width-alt: 1718; mso-width-source: userset; width: 35pt;" width="47"></col>
</colgroup><tbody>
<tr height="20" style="height: 15.0pt;">
<td class="xl63" colspan="2" height="20" style="height: 15.0pt; mso-ignore: colspan; width: 83pt;" width="111"><b>16 Bit Integer</b></td>
<td style="width: 35pt;" width="47"><br /></td>
<td style="width: 35pt;" width="47"><br /></td>
<td style="width: 35pt;" width="47"><br /></td>
<td style="width: 35pt;" width="47"><br /></td>
<td style="width: 35pt;" width="47"><br /></td>
<td style="width: 35pt;" width="47"><br /></td>
<td style="width: 35pt;" width="47"><br /></td>
<td style="width: 35pt;" width="47"><br /></td>
<td style="width: 35pt;" width="47"><br /></td>
<td style="width: 35pt;" width="47"><br /></td>
<td style="width: 35pt;" width="47"><br /></td>
<td style="width: 35pt;" width="47"><br /></td>
<td style="width: 35pt;" width="47"><br /></td>
<td style="width: 35pt;" width="47"><br /></td>
<td style="width: 35pt;" width="47"><br /></td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td class="xl63" height="20" style="height: 15.0pt;">Bit<span style="mso-spacerun: yes;"> </span></td>
<td align="right">15</td>
<td align="right">14</td>
<td align="right">13</td>
<td align="right">12</td>
<td align="right">11</td>
<td align="right">10</td>
<td align="right">9</td>
<td align="right">8</td>
<td align="right">7</td>
<td align="right">6</td>
<td align="right">5</td>
<td align="right">4</td>
<td align="right">3</td>
<td align="right">2</td>
<td align="right">1</td>
<td align="right">0</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td class="xl63" height="20" style="height: 15.0pt;">Value</td>
<td align="right">32768</td>
<td align="right">16384</td>
<td align="right">8192</td>
<td align="right">4096</td>
<td align="right">2048</td>
<td align="right">1024</td>
<td align="right">512</td>
<td align="right">256</td>
<td align="right">128</td>
<td align="right">64</td>
<td align="right">32</td>
<td align="right">16</td>
<td align="right">8</td>
<td align="right">4</td>
<td align="right">2</td>
<td align="right">1</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td class="xl63" height="20" style="height: 15.0pt;"><br /></td>
<td><br /></td>
<td><br /></td>
<td><br /></td>
<td><br /></td>
<td><br /></td>
<td><br /></td>
<td><br /></td>
<td><br /></td>
<td><br /></td>
<td><br /></td>
<td><br /></td>
<td><br /></td>
<td><br /></td>
<td><br /></td>
<td><br /></td>
<td><br /></td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td class="xl63" colspan="4" height="20" style="height: 15.0pt; mso-ignore: colspan;"><b>Fixed
Point 10.6 using 16 Bit Integer</b></td>
<td><b><br /></b></td>
<td><b><br /></b></td>
<td><b><br /></b></td>
<td><b><br /></b></td>
<td><b><br /></b></td>
<td><b><br /></b></td>
<td><b><br /></b></td>
<td><b><br /></b></td>
<td><b><br /></b></td>
<td><b><br /></b></td>
<td><b><br /></b></td>
<td><b><br /></b></td>
<td><b><br /></b></td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td class="xl63" height="20" style="height: 15.0pt;">Bit<span style="mso-spacerun: yes;"> </span></td>
<td align="right">15</td>
<td align="right">14</td>
<td align="right">13</td>
<td align="right">12</td>
<td align="right">11</td>
<td align="right">10</td>
<td align="right">9</td>
<td align="right">8</td>
<td align="right">7</td>
<td align="right">6</td>
<td align="right">5</td>
<td align="right">4</td>
<td align="right">3</td>
<td align="right">2</td>
<td align="right">1</td>
<td align="right">0</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td class="xl63" height="20" style="height: 15.0pt;">Value</td>
<td align="right">1024</td>
<td align="right">512</td>
<td align="right">256</td>
<td align="right">128</td>
<td align="right">64</td>
<td align="right">32</td>
<td align="right">16</td>
<td align="right">8</td>
<td align="right">4</td>
<td align="right">2</td>
<td align="right">1</td>
<td align="right">0.5</td>
<td align="right">0.25</td>
<td align="right">0.125</td>
<td align="right">0.0625</td>
<td align="right">0.0313</td>
</tr>
</tbody></table>
<br />
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.<br />
<br />
<b>Fixed point addition - we know its fixed point, the compiler doesn't</b><br />
<br />
// Update the phase accumulator by adding the phase increment <br />
ulPhaseAccumulator += ulPhaseIncrement; <br />
<br />
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.<br />
<br />
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.<br />
<br />
// drop the six bits of additional precision and access the remaining 10 bits as an integer array index in the range 0 - 599<br />
<br />
uint16_t unOutputValue = sineTable[ulPhaseAccumulator>>6]; <br />
<br />
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.<br />
<br />
<b>Fixed Point In The RCArduino DDS Example Sketch</b> <br />
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 -<br />
<br />
uint32_t ulOutput = nSineTable[ulPhaseAccumulator>>20];<br />
<br />
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.<br />
<br />
Fixed Point Multiplication is used in Audio synthesis to apply envelopes and amplitude modulation.<br />
<br />
<b>Envelope Applied to a DDS sine wave using Fixed Point Maths in the Illutron B Project - </b><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjBW6B0BRAtBlml7euMRTqA0odDtdghOophFBuh79fmm1_unOyBWOu6WUHbfn4CH8wGl3ON6MpqJGhiWXaGld8FrIDB-z7_BnWgJq7ryPK2Dpc0aPq3tmIEkwp19-fugkmhEqT3kGsJe_lc/s1600/envelope.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="135" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjBW6B0BRAtBlml7euMRTqA0odDtdghOophFBuh79fmm1_unOyBWOu6WUHbfn4CH8wGl3ON6MpqJGhiWXaGld8FrIDB-z7_BnWgJq7ryPK2Dpc0aPq3tmIEkwp19-fugkmhEqT3kGsJe_lc/s320/envelope.PNG" width="320" /></a></div>
<br />
<b>DDS Sinewave for Arduino Due - The Sketch</b><br />
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 - <b> </b><br />
<b><br /></b>
<a href="http://rcarduino.blogspot.com/2012/08/adding-audio-to-arduino-projects.html" target="_blank">http://rcarduino.blogspot.com/2012/08/adding-audio-to-arduino-projects.html</a><b></b><br />
<b><br /></b>
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.<b></b><br />
<b><br /></b>
<br />
<b>Caution : </b>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 - <a href="http://arduino.cc/forum/index.php/topic,139733.0.html" target="_blank"></a><b> </b><br />
<br />
<b>Update 09/06/2013</b> - 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.<br />
<br />
<a href="http://www.youtube.com/watch?v=EqI2hEVbMPI" target="_blank">http://www.youtube.com/watch?v=EqI2hEVbMPI</a><br />
<br />
Gaéta's blog - <a href="http://groovuino.blogspot.ae/" target="_blank">http://groovuino.blogspot.com/</a><br />
<br />
<b>Update 28/03/2013</b> : 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 - <a href="http://arduino.cc/forum/index.php/topic,139733.15.html" target="_blank">http://arduino.cc/forum/index.php/topic,139733.15.html</a><br />
<br />
<b>Update 28/03/2013</b> : 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 -<br />
<br />
<a href="https://soundcloud.com/gaetino/groovuino" target="_blank">https://soundcloud.com/gaetino/groovuino</a><br />
<br />
And follow the future development on the blog Gaétan has started here -<br />
<br />
<a href="http://groovuino.blogspot.ae/" target="_blank">http://groovuino.blogspot.ae/</a><br />
<br />
<div style="background-color: #f0f0f0;">
// RCArduino DDS Sinewave for Arduino Due<br />
// RCArduino DDS Sinewave by RCArduino is licensed under a Creative Commons Attribution 3.0 Unported License.<br />
// Based on a work at rcarduino.blogspot.com.<br />
<br />
// For helpful background information on Arduino Due Timer Configuration, refer to the following link<br />
// thanks to Sebastian Vik<br />
// http://arduino.cc/forum/index.php?action=post;topic=130423.15;num_replies=20<br />
<br />
// For background information on the DDS Technique see <br />
// http://interface.khm.de/index.php/lab/experiments/arduino-dds-sinewave-generator/<br />
<br />
// For audio sketches making extensive use of DDS Techniques, search the RCArduino Blog<br />
// for the tags Audio or synth<br />
<br />
// These are the clock frequencies available to the timers /2,/8,/32,/128<br />
// 84Mhz/2 = 42.000 MHz<br />
// 84Mhz/8 = 10.500 MHz<br />
// 84Mhz/32 = 2.625 MHz<br />
// 84Mhz/128 = 656.250 KHz<br />
// <br />
// 44.1Khz = CD Sample Rate<br />
// Lets aim for as close to the CD Sample Rate as we can get -<br />
//<br />
// 42Mhz/44.1Khz = 952.38<br />
// 10.5Mhz/44.1Khz = 238.09 // best fit divide by 8 = TIMER_CLOCK2 and 238 ticks per sample <br />
// 2.625Hmz/44.1Khz = 59.5<br />
// 656Khz/44.1Khz = 14.88<br />
<br />
// 84Mhz/44.1Khz = 1904 instructions per tick<br />
<br />
// the phase accumulator points to the current sample in our wavetable<br />
uint32_t ulPhaseAccumulator = 0;<br />
// the phase increment controls the rate at which we move through the wave table<br />
// higher values = higher frequencies<br />
volatile uint32_t ulPhaseIncrement = 0; // 32 bit phase increment, see below<br />
<br />
// full waveform = 0 to SAMPLES_PER_CYCLE<br />
// Phase Increment for 1 Hz =(SAMPLES_PER_CYCLE_FIXEDPOINT/SAMPLE_RATE) = 1Hz<br />
// Phase Increment for frequency F = (SAMPLES_PER_CYCLE/SAMPLE_RATE)*F<br />
#define SAMPLE_RATE 44100.0<br />
#define SAMPLES_PER_CYCLE 600<br />
#define SAMPLES_PER_CYCLE_FIXEDPOINT (SAMPLES_PER_CYCLE<<20)<br />
#define TICKS_PER_CYCLE (float)((float)SAMPLES_PER_CYCLE_FIXEDPOINT/(float)SAMPLE_RATE)<br />
<br />
// to represent 600 we need 10 bits<br />
// Our fixed point format will be 10P22 = 32 bits<br />
<br />
<br />
// We have 521K flash and 96K ram to play with<br />
<br />
// Create a table to hold the phase increments we need to generate midi note frequencies at our 44.1Khz sample rate<br />
#define MIDI_NOTES 128<br />
uint32_t nMidiPhaseIncrement[MIDI_NOTES];<br />
<br />
// fill the note table with the phase increment values we require to generate the note<br />
void createNoteTable(float fSampleRate)<br />
{<br />
for(uint32_t unMidiNote = 0;unMidiNote < MIDI_NOTES;unMidiNote++)<br />
{<br />
// Correct calculation for frequency<br />
Serial.print(unMidiNote);<br />
Serial.print(" ");<br />
float fFrequency = ((pow(2.0,(unMidiNote-69.0)/12.0)) * 440.0);<br />
Serial.print(fFrequency);<br />
Serial.print(" ");<br />
<br />
nMidiPhaseIncrement[unMidiNote] = fFrequency*TICKS_PER_CYCLE;<br />
Serial.println(nMidiPhaseIncrement[unMidiNote]);<br />
}<br />
}<br />
<br />
// Create a table to hold pre computed sinewave, the table has a resolution of 600 samples<br />
#define WAVE_SAMPLES 600<br />
// default int is 32 bit, in most cases its best to use uint32_t but for large arrays its better to use smaller<br />
// data types if possible, here we are storing 12 bit samples in 16 bit ints<br />
uint16_t nSineTable[WAVE_SAMPLES];<br />
<br />
// create the individual samples for our sinewave table<br />
void createSineTable()<br />
{<br />
for(uint32_t nIndex = 0;nIndex < WAVE_SAMPLES;nIndex++)<br />
{<br />
// normalised to 12 bit range 0-4095<br />
nSineTable[nIndex] = (uint16_t) (((1+sin(((2.0*PI)/WAVE_SAMPLES)*nIndex))*4095.0)/2);<br />
Serial.println(nSineTable[nIndex]);<br />
}<br />
}<br />
<br />
void setup()<br />
{<br />
Serial.begin(9600);<br />
<br />
createNoteTable(SAMPLE_RATE);<br />
createSineTable();<br />
<br />
/* turn on the timer clock in the power management controller */<br />
pmc_set_writeprotect(false);<br />
pmc_enable_periph_clk(ID_TC4);<br />
<br />
/* we want wavesel 01 with RC */<br />
TC_Configure(/* clock */TC1,/* channel */1, TC_CMR_WAVE | TC_CMR_WAVSEL_UP_RC | TC_CMR_TCCLKS_TIMER_CLOCK2);<br />
TC_SetRC(TC1, 1, 238); // sets <> 44.1 Khz interrupt rate<br />
TC_Start(TC1, 1);<br />
<br />
// enable timer interrupts on the timer <br />
TC1->TC_CHANNEL[1].TC_IER=TC_IER_CPCS;<br />
TC1->TC_CHANNEL[1].TC_IDR=~TC_IER_CPCS;<br />
<br />
/* Enable the interrupt in the nested vector interrupt controller */<br />
/* TC4_IRQn where 4 is the timer number * timer channels (3) + the channel number (=(1*3)+1) for timer1 channel1 */<br />
NVIC_EnableIRQ(TC4_IRQn);<br />
<br />
// this is a cheat - enable the DAC<br />
analogWrite(DAC0,0);<br />
}<br />
<br />
void loop()<br />
{<br />
// read analog input 0 drop the range from 0-1024 to 0-127 with a right shift 3 places,<br />
// then look up the phaseIncrement required to generate the note in our nMidiPhaseIncrement table<br />
uint32_t ulInput = analogRead(0);<br />
ulPhaseIncrement = nMidiPhaseIncrement[ulInput>>3]; <br />
}<br />
<br />
void TC4_Handler()<br />
{<br />
// We need to get the status to clear it and allow the interrupt to fire again<br />
TC_GetStatus(TC1, 1);<br />
<br />
ulPhaseAccumulator += ulPhaseIncrement; // 32 bit phase increment, see below<br />
<br />
// if the phase accumulator over flows - we have been through one cycle at the current pitch,<br />
// now we need to reset the grains ready for our next cycle<br />
if(ulPhaseAccumulator > SAMPLES_PER_CYCLE_FIXEDPOINT)<br />
{<br />
// DB 02/Jan/2012 - carry the remainder of the phase accumulator<br />
ulPhaseAccumulator -= SAMPLES_PER_CYCLE_FIXEDPOINT;<br />
}<br />
<br />
// get the current sample <br />
uint32_t ulOutput = nSineTable[ulPhaseAccumulator>>20];<br />
<br />
// we cheated and user analogWrite to enable the dac, but here we want to be fast so<br />
// write directly <br />
dacc_write_conversion_data(DACC_INTERFACE, ulOutput);<br />
}</div>
</div>
Can_I_Trade?http://www.blogger.com/profile/07867684474123805993noreply@blogger.com68tag:blogger.com,1999:blog-7214097671310478681.post-14772898859203040662012-11-30T12:08:00.004-08:002013-01-13T08:25:57.419-08:00Quick And Dirty Synth For Arduino Due<div dir="ltr" style="text-align: left;" trbidi="on">
The first RCArduino project for the Arduino Due is the Quick And Dirty Synth. The synth is a simple showcase for audio output through the DAC running at the Audio CD sample rate of 44.1Khz.<br />
<br />
<br />
<a href="http://creativecommons.org/licenses/by/3.0/deed.en_US" rel="license"><img alt="Creative Commons License" src="http://i.creativecommons.org/l/by/3.0/88x31.png" style="border-width: 0;" /></a><br />
<span property="dct:title" xmlns:dct="http://purl.org/dc/terms/">RCArduino Quick And Dirty Synth</span> by <a href="http://www.blogger.com/rcarduino.blogspot.com" property="cc:attributionName" rel="cc:attributionURL" xmlns:cc="http://creativecommons.org/ns#">RCArduino</a> is licensed under a<br />
<a href="http://creativecommons.org/licenses/by/3.0/deed.en_US" rel="license">Creative Commons Attribution 3.0 Unported License</a>.<br />
Based on a work at <a href="http://www.blogger.com/rcarduino.blogspot.com" rel="dct:source" xmlns:dct="http://purl.org/dc/terms/">rcarduino.blogspot.com</a>.
<br />
<br />
<br />
The showcase is based on the simplest synth engine I could create - 3 counters counting up at a rate controlled by three analog inputs.<br />
<br />
It might sound simple but its a surprisingly rich sounding synth engine, here is how it works - <br />
<br />
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.<br />
<br />
<br />
<b>Example Ramp Output - </b><br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjSjPxQirhyphenhyphenxH-t3UowImqqSUPIjuaAgAhOEuLJhiwS31WAvGII95dp1XHJRpmN5kUUEX6E7UvU6JHEwuMJB6wU1wBy0oX1ZRKz3fgWU_67QztvPhaTX-65iJWmBC6sRx5rZOeGqxa_LcA1/s1600/TriangleWave.PNG" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="91" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjSjPxQirhyphenhyphenxH-t3UowImqqSUPIjuaAgAhOEuLJhiwS31WAvGII95dp1XHJRpmN5kUUEX6E7UvU6JHEwuMJB6wU1wBy0oX1ZRKz3fgWU_67QztvPhaTX-65iJWmBC6sRx5rZOeGqxa_LcA1/s320/TriangleWave.PNG" width="320" /></a><br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjSjPxQirhyphenhyphenxH-t3UowImqqSUPIjuaAgAhOEuLJhiwS31WAvGII95dp1XHJRpmN5kUUEX6E7UvU6JHEwuMJB6wU1wBy0oX1ZRKz3fgWU_67QztvPhaTX-65iJWmBC6sRx5rZOeGqxa_LcA1/s1600/TriangleWave.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"></a> <br />
<br />
<b>Example Ramp At A Higher Frequency -</b><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjJXnR_kwUZYe1uUvTV_YNZzh0fHrbfGokApswn5PQzS5jh0nLGd9yGti3L-Vl8pGHERK0xeAchKzwKzh6xlr6rz84jVW0g9SXWg42zamTmrTok3ZUXhID7g8JvnVUmc2hQzK0vTtfRpgJb/s1600/TriangleWave2.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="110" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjJXnR_kwUZYe1uUvTV_YNZzh0fHrbfGokApswn5PQzS5jh0nLGd9yGti3L-Vl8pGHERK0xeAchKzwKzh6xlr6rz84jVW0g9SXWg42zamTmrTok3ZUXhID7g8JvnVUmc2hQzK0vTtfRpgJb/s320/TriangleWave2.PNG" width="320" /></a></div>
<br />
<br />
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.<br />
<br />
Example - Two triangle at frequency F and 3F mixed together to create a new output waveform.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjZABFDHOMFnEBOO1omzZInhYt4x5rg-5zvrrsFrhy5iorBYjPRbTN73lJxi6qShvDyOFh0TPMujt3Ebo12hTnDR-qcGMQbv6Wi7Gtpw3HLf47GCryjLYj9EV3kQOiA2siqiXlc-IAw7TEe/s1600/mixedWaves.PNG" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" height="106" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjZABFDHOMFnEBOO1omzZInhYt4x5rg-5zvrrsFrhy5iorBYjPRbTN73lJxi6qShvDyOFh0TPMujt3Ebo12hTnDR-qcGMQbv6Wi7Gtpw3HLf47GCryjLYj9EV3kQOiA2siqiXlc-IAw7TEe/s400/mixedWaves.PNG" width="400" /></a></div>
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.<br />
<br />
<br />
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. <br />
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjwEJj5XdGxXii78WvN4XqJtix3YKmsLp7b6bu_r9o0wBLrb4i_M7HXr_QJWxrMy2EvjjP5J9106DRV-OA0y_gUFbKzoRonDDVBsI24t8Hz-fWqIqsTfxUKV-T4uspj1Z6v_J5qdYH9h24J/s1600/RepeatingGrains.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="77" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjwEJj5XdGxXii78WvN4XqJtix3YKmsLp7b6bu_r9o0wBLrb4i_M7HXr_QJWxrMy2EvjjP5J9106DRV-OA0y_gUFbKzoRonDDVBsI24t8Hz-fWqIqsTfxUKV-T4uspj1Z6v_J5qdYH9h24J/s400/RepeatingGrains.PNG" width="400" /></a></div>
<br />
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.<br />
<br />
All of the Audio projects on RCArduino use the following simple amplifier circuit -<br />
<a href="http://rcarduino.blogspot.com/2012/08/adding-audio-to-arduino-projects.html" target="_blank">http://rcarduino.blogspot.com/2012/08/adding-audio-to-arduino-projects.html</a><br />
<br />
To learn more about the synthesis technique used in the quick and dirty synthesizer see the following link -<br />
<br />
<a href="http://rcarduino.blogspot.com/2012/08/adding-audio-to-arduino-projects.html" target="_blank">http://rcarduino.blogspot.com/2012/08/adding-audio-to-arduino-projects.html</a><br />
<br />
<b>The code </b><br />
<br />
<b>Caution : </b>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 - <a href="http://arduino.cc/forum/index.php/topic,139733.0.html" target="_blank">http://arduino.cc/forum/index.php/topic,139733.0.html</a><b> </b><br />
<br />
<div style="background-color: #f0f0f0;">
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">// 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.</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">// Based on a work at rcarduino.blogspot.com.</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"><br /></span></span>
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">// For helpful background information on Arduino Due Timer Configuration, refer to the following link</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">// thanks to Sebastian Vik</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">// http://arduino.cc/forum/index.php?action=post;topic=130423.15;num_replies=20</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"><br /></span></span>
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">// The following folders within the arduino install provide access to source code and documentation</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">// its very low level, the documentation is next to useless, but it might help someone</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">// C:\arduino-1.5.1r2\hardware\arduino\sam\system\libsam\source</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">// C:/arduino-1.5.1r2/hardware/arduino/sam/system/CMSIS/Device/ATMEL/sam3xa/html/tc1.html</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">// C:/arduino-1.5.1r2/hardware/arduino/sam/system/CMSIS/Device/ATMEL/sam3xa/html/SAM3XA.html</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">// C:\arduino-1.5.1r2\hardware\arduino\sam\system\CMSIS\CMSIS\Documentation</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"><br /></span></span>
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">// These are the clock frequencies available to the timers /2,/8,/32,/128</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">// 84Mhz/2 = 42.000 MHz</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">// 84Mhz/8 = 10.500 MHz</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">// 84Mhz/32 = 2.625 MHz</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">// 84Mhz/128 = 656.250 KHz</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">// </span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">// 44.1Khz = CD Sample Rate</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">// Lets aim for as close to the CD Sample Rate as we can get -</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">//</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">// 42Mhz/44.1Khz = 952.38</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">// 10.5Mhz/44.1Khz = 238.09 // best fit divide by 8 = TIMER_CLOCK2 and 238 ticks per sample </span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">// 2.625Hmz/44.1Khz = 59.5</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">// 656Khz/44.1Khz = 14.88</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"><br /></span></span>
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">// 84Mhz/44.1Khz = 1904 instructions per tick</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"><br /></span></span>
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">// These variables represent our synth engine, its similar to a vastly simplified Auduino and has a similar sound.</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">// Phase Accumulator controls the main pitch, grain1 and grain two phase accumulator control the pitch for the two grains</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">// The grainPhaseAccumulators are essentially simple counters, when you count up and overflow back to 0, you generate a ramp waveform</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">// we generate two and mix them together.</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">uint32_t ulPhaseAccumulator = 0; // 32 bit phase accumulator, if we shift >> 20 bits we get a 12 bit value for our output ADC</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">volatile uint32_t ulPhaseIncrement = 0; // 32 bit phase increment, see below</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">uint32_t ulGrain1PhaseAccumulator = 0; // 32 bit phase accumulator, if we shift >> 20 bits we get a 12 bit value for our output ADC</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">volatile uint32_t ulGrain1PhaseIncrement = 0; // 32 bit phase increment, see below</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">uint32_t ulGrain2PhaseAccumulator = 0; // 32 bit phase accumulator, if we shift >> 20 bits we get a 12 bit value for our output ADC</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">volatile uint32_t ulGrain2PhaseIncrement = 0; // 32 bit phase increment, see below</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"><br /></span></span>
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">// full waveform = 0 to 4294967296</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">// Phase Increment for 1 Hz =(4294967296/44100) = 1Hz</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">// Phase Increment for frequency F = (4294967296/44100)*F</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">#define SAMPLE_RATE 44100.0</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">#define TICKS_PER_CYCLE (4294967296.0/SAMPLE_RATE)</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"><br /></span></span>
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">// Create a table to hold the phase increments we need to generate midi note frequencies at our 44.1Khz sample rate</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">#define MIDI_NOTES 128</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">uint32_t nMidiPhaseIncrement[MIDI_NOTES];</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"><br /></span></span>
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">void createNoteTable(float fSampleRate)</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">{</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> for(uint32_t unMidiNote = 0;unMidiNote < MIDI_NOTES;unMidiNote++)</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> {</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> // Correct calculation for frequency</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> Serial.print(unMidiNote);</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> Serial.print(" ");</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> float fFrequency = ((pow(2.0,(unMidiNote-69.0)/12.0)) * 440.0);</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> Serial.print(fFrequency);</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> Serial.print(" ");</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> nMidiPhaseIncrement[unMidiNote] = fFrequency*TICKS_PER_CYCLE;</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> Serial.println(nMidiPhaseIncrement[unMidiNote]);</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> }</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">}</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"><br /></span></span>
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">void setup()</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">{</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> Serial.begin(9600);</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"><br /></span></span>
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> createNoteTable(SAMPLE_RATE);</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> </span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> /* turn on the timer clock in the power management controller */</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> pmc_set_writeprotect(false);</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> pmc_enable_periph_clk(ID_TC4);</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"><br /></span></span>
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> /* we want wavesel 01 with RC */</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> TC_Configure(/* clock */TC1,/* channel */1, TC_CMR_WAVE | TC_CMR_WAVSEL_UP_RC | TC_CMR_TCCLKS_TIMER_CLOCK2);</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> TC_SetRC(TC1, 1, 238); // sets <> 44.1 Khz interrupt rate</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> TC_Start(TC1, 1);</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> </span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> // enable timer interrupts on the timer </span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> TC1->TC_CHANNEL[1].TC_IER=TC_IER_CPCS;</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> TC1->TC_CHANNEL[1].TC_IDR=~TC_IER_CPCS;</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> </span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> /* Enable the interrupt in the nested vector interrupt controller */</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> /* TC4_IRQn where 4 is the timer number * timer channels (3) + the channel number (=(1*3)+1) for timer1 channel1 */</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> NVIC_EnableIRQ(TC4_IRQn);</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"><br /></span></span>
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> // this is a cheat - enable the DAC</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> analogWrite(DAC0,0);</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">}</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"><br /></span></span>
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">void loop()</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">{</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> // read analog input 0 drop the range from 0-1024 to 0-127 with a right shift 3 places,</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> // then look up the phaseIncrement required to generate the note in our nMidiPhaseIncrement table</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> uint32_t ulInput = analogRead(0);</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> ulPhaseIncrement = nMidiPhaseIncrement[ulInput>>3]; </span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> </span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> // set the phase increment for grains 1 and 2, we do not want a precise frequency for these, </span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> // they set the character of the note rather than pitch</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> ulGrain1PhaseIncrement = analogRead(1)<<18;</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> ulGrain2PhaseIncrement = analogRead(2)<<18;</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">}</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"><br /></span></span>
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">void TC4_Handler()</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">{</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> // We need to get the status to clear it and allow the interrupt to fire again</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> TC_GetStatus(TC1, 1);</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> </span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> ulPhaseAccumulator += ulPhaseIncrement; // 32 bit phase increment, see below</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> // if the phase accumulator over flows - we have been through one cycle at the current pitch,</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> // now we need to reset the grains ready for our next cycle</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> if(ulPhaseAccumulator < ulPhaseIncrement)</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> {</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> ulGrain1PhaseAccumulator = 0;</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> ulGrain2PhaseAccumulator = 0;</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> }</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> else</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> {</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> // increment the grains</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> ulGrain1PhaseAccumulator += ulGrain1PhaseIncrement;</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> ulGrain2PhaseAccumulator += ulGrain2PhaseIncrement;</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> }</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> </span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> // mix the grains by adding them together - to the result of adding these two 32bit values into the 12 bit ADC Output</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> // we shift both values right 21 places to sum two 11 bit values giving a 12 bit result.</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> uint32_t ulOutput = (ulGrain1PhaseAccumulator>>21)+(ulGrain2PhaseAccumulator>>21);</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> </span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> // we cheated and user analogWrite to enable the dac, but here we want to be fast so</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> // write directly </span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> dacc_write_conversion_data(DACC_INTERFACE, ulOutput);</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">}</span></span></div>
<br /></div>
Can_I_Trade?http://www.blogger.com/profile/07867684474123805993noreply@blogger.com9tag:blogger.com,1999:blog-7214097671310478681.post-79991658484676225582012-11-24T08:59:00.001-08:002012-11-24T08:59:11.917-08:00Lap Timer Part 5 - Buzzer, External Audio and Some Builds<div dir="ltr" style="text-align: left;" trbidi="on">
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
</div>
The latest version of the lap timer includes two new features -<br />
<br />
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.<br />
<br />
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.<br />
<br />
For previous steps of the build along see the project index page -<br />
<a href="http://rcarduino.blogspot.com/p/project-index.html" target="_blank">http://rcarduino.blogspot.com/p/project-index.html</a><br />
<br />
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.<br />
<br />
<div style="text-align: center;">
<b>ALLY - Transponder mounted in touring car chassis</b> </div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh9aTIzOv-k_k7WZn8eBNnVaICkpEm3uoobqckQRi4kvUVV8WCifCN_wRqC6V1g91F9ktB93KRGTH5nwDEVduYcS8NG7gB9NjjA9_LotXgDa0rrmvZP1oWCaYaY2p7puu18MUyV6ViurelE/s1600/ALLYCar.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="210" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh9aTIzOv-k_k7WZn8eBNnVaICkpEm3uoobqckQRi4kvUVV8WCifCN_wRqC6V1g91F9ktB93KRGTH5nwDEVduYcS8NG7gB9NjjA9_LotXgDa0rrmvZP1oWCaYaY2p7puu18MUyV6ViurelE/s400/ALLYCar.PNG" width="400" /></a></div>
<br />
<br />
<table>
<tbody>
<tr>
<td> <b>Howie314 - Using an LCD Shield for a fast build</b>
</td>
</tr>
<tr>
<td><div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEht-Kx_nWOXAECh-ixY8u9nhOsjjYm1MxAbebYTX8-oFIZWIGVPF4OEj3aN-XYnWeYnCv45sQyKAhyphenhyphenCPGfgw3Pr2OHCGzfge6b8BCZtFFQKxNcrhciUD0W8vS9Vlzrg57Qa53biNqmSlC5u/s1600/howie314.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEht-Kx_nWOXAECh-ixY8u9nhOsjjYm1MxAbebYTX8-oFIZWIGVPF4OEj3aN-XYnWeYnCv45sQyKAhyphenhyphenCPGfgw3Pr2OHCGzfge6b8BCZtFFQKxNcrhciUD0W8vS9Vlzrg57Qa53biNqmSlC5u/s1600/howie314.PNG" /></a></div>
</td>
<td>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.<br />
<br />
If someone would design an LCD Shield with a rotary encoder as well as buttons we could have the perfect lap timer interface.
</td>
</tr>
<tr>
<td><div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhJUgiShwEVvC8rUsAp52TGw6IfvKOGmmaI8THncyTXNsE-WFI4RviCt9LUfIO6IvgetLb_JjRb6zCt8RhupqmKmmCf6mQABqlpEehfVIf5JhnPOsOwvX4Lz7Hy15TiYGNkTS-ryIGmLe4Q/s1600/Howie314ExternalAudio.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhJUgiShwEVvC8rUsAp52TGw6IfvKOGmmaI8THncyTXNsE-WFI4RviCt9LUfIO6IvgetLb_JjRb6zCt8RhupqmKmmCf6mQABqlpEehfVIf5JhnPOsOwvX4Lz7Hy15TiYGNkTS-ryIGmLe4Q/s1600/Howie314ExternalAudio.PNG" /></a></div>
</td>
<td><br />
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.<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br /></td></tr>
<tr>
<td><div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjf93YwU2DIplqNynfBOohF-K4t4DbHWRgV7JAKt5gQOXbHFtQJSw2rDH90HOngxFxtJVfZ3C_xl1-6hC1XF1dpyblIq5ABKcu9OpSouihUlqW5Gn92qoBv8Hu2DJfia6iELmpCtma8AA30/s1600/HowieUI.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjf93YwU2DIplqNynfBOohF-K4t4DbHWRgV7JAKt5gQOXbHFtQJSw2rDH90HOngxFxtJVfZ3C_xl1-6hC1XF1dpyblIq5ABKcu9OpSouihUlqW5Gn92qoBv8Hu2DJfia6iELmpCtma8AA30/s1600/HowieUI.PNG" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEht-Kx_nWOXAECh-ixY8u9nhOsjjYm1MxAbebYTX8-oFIZWIGVPF4OEj3aN-XYnWeYnCv45sQyKAhyphenhyphenCPGfgw3Pr2OHCGzfge6b8BCZtFFQKxNcrhciUD0W8vS9Vlzrg57Qa53biNqmSlC5u/s1600/howie314.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><br /></a></div>
</td>
<td>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.</td>
</tr>
</tbody></table>
<table><tbody>
<tr><td><br /></td><td><br /></td></tr>
<tr><td><b>ALLY's Build and Enclosure</b><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEixqn1p4xmm6qHy1zlDss5Hh75DelvUV3U0FsTL-5D_UdZvJOTiZznC2DZbLh7FNZ198KDIO816osVLKT6ZUekiwMFg4cbGbkbmvRIp8iT3cbo29c7L6Cs62fL7UPJSj80wLvsxmuUbqyWe/s1600/AllyEnclosure.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="209" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEixqn1p4xmm6qHy1zlDss5Hh75DelvUV3U0FsTL-5D_UdZvJOTiZznC2DZbLh7FNZ198KDIO816osVLKT6ZUekiwMFg4cbGbkbmvRIp8iT3cbo29c7L6Cs62fL7UPJSj80wLvsxmuUbqyWe/s320/AllyEnclosure.PNG" width="320" /></a></div>
<br /></td>
<td>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.</td>
</tr>
<tr>
<td><b>ALLY Transponder Mount</b><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhhVHFJp4pBFIZRwCncNFWO2kT6UZpfAEioFONHFYXV4bRHKaR-r07KfL-hLW_51hrpvdQf3aYL8IbT6-c-ERuE2Z5QUpF6L2hDIXOAxe_afIlj2Ss7yxaAdT1ZXTGpERFZuEVYqMkqhvoj/s1600/ALLYtransponder.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="195" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhhVHFJp4pBFIZRwCncNFWO2kT6UZpfAEioFONHFYXV4bRHKaR-r07KfL-hLW_51hrpvdQf3aYL8IbT6-c-ERuE2Z5QUpF6L2hDIXOAxe_afIlj2Ss7yxaAdT1ZXTGpERFZuEVYqMkqhvoj/s320/ALLYtransponder.PNG" width="320" /></a></div>
</td>
<td>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.<br />
<br />
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.
</td>
</tr>
<tr>
<td><b>ALLY's Carbon</b><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjV11Dshw8ZEFSKhR2pR7c0QPnevSi3KnbQ1WsVg_C-PZZVgCdfWjx8bBjyqcEctPEG-nNEbXm6n2thwOzH8Lw-DRRB3gxDn5qzMDJu4FgbW0gOLBXFGCY1I138fQTZv66xL3DRfluciBEX/s1600/ALLYCarbon.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="226" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjV11Dshw8ZEFSKhR2pR7c0QPnevSi3KnbQ1WsVg_C-PZZVgCdfWjx8bBjyqcEctPEG-nNEbXm6n2thwOzH8Lw-DRRB3gxDn5qzMDJu4FgbW0gOLBXFGCY1I138fQTZv66xL3DRfluciBEX/s320/ALLYCarbon.PNG" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEixqn1p4xmm6qHy1zlDss5Hh75DelvUV3U0FsTL-5D_UdZvJOTiZznC2DZbLh7FNZ198KDIO816osVLKT6ZUekiwMFg4cbGbkbmvRIp8iT3cbo29c7L6Cs62fL7UPJSj80wLvsxmuUbqyWe/s1600/AllyEnclosure.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><br /></a></div>
</td>
<td>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. </td>
</tr>
</tbody></table>
<b>Lap Timer Piezo Buzzer and External Audio</b><br />
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.<br />
<br />
The feedback is deliberately simple but totally effective - one beep to confirm a lap and two beeps if its a new best lap.<br />
<br />
To add the audio features we have two options<br />
<br />
1) A Piezo Buzzer<br />
2) An External Speaker<br />
<br />
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.<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhRqBCcc-JbgRbbYTtgwQghWPHpDoVhNgSau9IaepN2wiDK8Z48F1n7IkJRqI_yY2HQM8L70tH7hew16ssvv3ty1hiV97Qj7_hjtwLob-5wDC3A6aC19AyKiMMDfcQeNCgAOmmx6CyoSd0-/s1600/lapscheme.PNG" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" height="263" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhRqBCcc-JbgRbbYTtgwQghWPHpDoVhNgSau9IaepN2wiDK8Z48F1n7IkJRqI_yY2HQM8L70tH7hew16ssvv3ty1hiV97Qj7_hjtwLob-5wDC3A6aC19AyKiMMDfcQeNCgAOmmx6CyoSd0-/s320/lapscheme.PNG" width="320" /></a><b>Piezo With Transistor Driver</b><br />
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.<br />
<br />
<br />
<br />
<b>Components</b><br />
1 * 2.2K Resistor<br />
<span class="regularText">1 * P2N2222 Transistor</span><br />
<span class="regularText">1 * Piezo Buzzer </span> <br />
<br />
<b>External Audio Through A Speaker</b><br />
<i>The Peizo option can be used without adding the external audio option, but if you want more volume in your build read on - </i><br />
<br />
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 -<br />
<br />
<a href="http://rcarduino.blogspot.com/2012/08/adding-audio-to-arduino-projects.html" target="_blank">http://rcarduino.blogspot.com/2012/08/adding-audio-to-arduino-projects.html</a><br />
<br />
The LM386 Audio Amp can be build to a very compact form factor to fit your chosen enclosure -<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhVB-j6VHQbsEjx-azRyUMWJlfGv8yHOYD2MVqZGl3bPUizJ24fmisQjTG9svd-xPDJwfLJEMK1t6PBvnaoSLmIt_do0UpVk7jg5d-7FF7L-MJ95Wx4KeXyiZKiefbjYXT8PK29aPTGyLka/s1600/lm386laptimer.PNG" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" height="180" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhVB-j6VHQbsEjx-azRyUMWJlfGv8yHOYD2MVqZGl3bPUizJ24fmisQjTG9svd-xPDJwfLJEMK1t6PBvnaoSLmIt_do0UpVk7jg5d-7FF7L-MJ95Wx4KeXyiZKiefbjYXT8PK29aPTGyLka/s320/lm386laptimer.PNG" width="320" /></a></div>
<b>Parts List </b><br />
1 x LM386-N4<br />
2 x 100uf Electrolytic Capacitors<br />
1 x 0.1uf Capacitor<br />
1 x 100Ohm Resistor<br />
1 x 10K Potentiometer1 x Phono socket for speaker connection<br />
<br />
<br />
<br />
<b>Schematic</b> - Note the audio output is from analog pin 5, for simplicity only the audio components are shown, as there<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjqV_feD0kAxb-RsLl9kQWStuPkmBUBUC390jUu7PbdSazNPIXrD8SxoPQLCwLHh6EwM8n_9aEw9KI5AOW3EErPML21VFYjOsKRnVi4WYHD0yNQk3UGjAZzqb9otfCNcctUke465HSG6-gN/s1600/AudioAmp.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="190" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjqV_feD0kAxb-RsLl9kQWStuPkmBUBUC390jUu7PbdSazNPIXrD8SxoPQLCwLHh6EwM8n_9aEw9KI5AOW3EErPML21VFYjOsKRnVi4WYHD0yNQk3UGjAZzqb9otfCNcctUke465HSG6-gN/s320/AudioAmp.PNG" width="320" /></a></div>
For the latest code, contact me Duane B though the Arduino forum.<br />
<br />
<b>Future Developments</b><br />
Some common requests that I will be adding in the near future are -<br />
<br />
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.<br />
<br />
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.<br />
<br />
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.<br />
<br />
If you have a build you want to share, please submit some pictures,<br />
<br />
Stay Tuned<br />
<br />
Duane B<br />
<br />
</div>
Can_I_Trade?http://www.blogger.com/profile/07867684474123805993noreply@blogger.com18tag:blogger.com,1999:blog-7214097671310478681.post-35294662958125561892012-11-15T03:02:00.000-08:002013-01-29T07:24:49.222-08:00Auduino With Delay<div dir="ltr" style="text-align: left;" trbidi="on">
This is the first in a series of posts introducing simple audio effects that can be used in micro controller projects.<br />
<br />
<i>Next Week - Bit Crushing effects</i><br />
<br />
The Delay effect is one of the simplest and most effective enhancements we can add to our audio projects.<br />
<br />
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.<br />
<br />
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.<br />
<br />
<b>How do we create the delay effect</b><br />
Delay is very simple to add in a microcontroller, all we need is a block of memory to record the output in.<br />
<br />
The larger the block of memory, the longer the delay we can record and the deeper the effect.<br />
<br />
In this case we are using a 1K block of memory in the array named sDelayBuffer -<br />
<div style="background: #F0F0F0;">
<br />
// Duane B <br />
// rcarduino.blogspot.com<br />
// 15/11/2012<br />
// Very simple ring buffer delay<br />
// we record the output in this array<br />
// and then mix it back in with the output as the buffer wraps around<br />
// can be switched on and off by a button on DELAY_BUTTON<br />
#define MAX_DELAY 1024<br />
unsigned char sDelayBuffer[MAX_DELAY];<br />
unsigned int nDelayCounter = 0;<br />
unsigned char bDelay;</div>
<br />
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 -<br />
<br />
<div style="background: #F0F0F0;">
<br />
// Duane B <br />
// rcarduino.blogspot.com<br />
// 15/11/2012<br />
// add a button to set bDelay true or false to turn delay on and off<br />
if(bDelay)<br />
{<br />
// Output to PWM (this is faster than using analogWrite) <br />
// Here we add the delay buffer to the output value, this produces<br />
// an subtle echo effect, the delay buffer is effectivley replaying the sound from<br />
// 1/8th of a second ago.<br />
<br />
LED_PORT |= 1 << LED_BIT; // Faster than using digitalWrite<br />
PWM_VALUE = (output + (sDelayBuffer[nDelayCounter]))>>1;<br />
<br />
// add the new output to the buffer so we can use it when the buffer next wraps around<br />
sDelayBuffer[nDelayCounter] = PWM_VALUE;<br />
nDelayCounter++;<br />
if(nDelayCounter == MAX_DELAY)<br />
{<br />
nDelayCounter = 0;<br />
}<br />
}<br />
else<br />
{<br />
LED_PORT &= ~(1 << LED_BIT); // Faster than using digitalWrite<br />
<br />
PWM_VALUE = output;<br />
}</div>
<br />
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.<br />
<br />
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.<br />
<br />
<br />
<b>The RCArduino Five Dollar Synthesizer is another audio project enhanced with this delay effect -</b><br />
<br />
<b><a href="http://rcarduino.blogspot.com/2012/10/five-dollar-synthesiser.html" target="_blank">http://rcarduino.blogspot.com/2012/10/five-dollar-synthesiser.html</a> </b><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<iframe allowfullscreen='allowfullscreen' webkitallowfullscreen='webkitallowfullscreen' mozallowfullscreen='mozallowfullscreen' width='320' height='266' src='https://www.youtube.com/embed/yQsgIRSDGm0?feature=player_embedded' frameborder='0'></iframe></div>
<br />
<b>Further Development</b><br />
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. <br />
<br />
<b>Auduino Accreditation</b><br />
The Auduino is an original work by Peter Knight, the original project can be found here -<br />
<a href="http://code.google.com/p/tinkerit/wiki/Auduino" target="_blank">http://code.google.com/p/tinkerit/wiki/Auduino</a><br />
<br />
<b>Auduino with delay</b><br />
<br />
Auduino with delay is a very slight modification by Duane B (rcarduino) to the original work of Peter Knight.<br />
<br />
<b>Notes</b><br />
- This code also include the volatile fix which allows the Auduino to work correctly in Arduino 1.0 and later <br />
- 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.<br />
- LED 13, now indicates whether delay is on or off.<br />
<br />
<br />
<div style="background: #F0F0F0;">
// Auduino, the Lo-Fi granular synthesiser<br />
//<br />
// by Peter Knight, Tinker.it http://tinker.it<br />
//<br />
// Help: http://code.google.com/p/tinkerit/wiki/Auduino<br />
// More help: http://groups.google.com/group/auduino<br />
//<br />
// Analog in 0: Grain 1 pitch<br />
// Analog in 1: Grain 2 decay<br />
// Analog in 2: Grain 1 decay<br />
// Analog in 3: Grain 2 pitch<br />
// Analog in 4: Grain repetition frequency<br />
//<br />
// Digital 3: Audio out (Digital 11 on ATmega8)<br />
//<br />
// Changelog:<br />
// 19 Nov 2008: Added support for ATmega8 boards<br />
// 21 Mar 2009: Added support for ATmega328 boards<br />
// 7 Apr 2009: Fixed interrupt vector for ATmega328 boards<br />
// 8 Apr 2009: Added support for ATmega1280 boards (Arduino Mega)<br />
<br />
#include <avr/io.h><br />
#include <avr/interrupt.h><br />
<br />
uint16_t syncPhaseAcc;<br />
volatile uint16_t syncPhaseInc;<br />
uint16_t grainPhaseAcc;<br />
volatile uint16_t grainPhaseInc;<br />
uint16_t grainAmp;<br />
volatile uint8_t grainDecay;<br />
uint16_t grain2PhaseAcc;<br />
volatile uint16_t grain2PhaseInc;<br />
uint16_t grain2Amp;<br />
volatile uint8_t grain2Decay;<br />
<br />
// Map Analogue channels<br />
#define SYNC_CONTROL (4)<br />
#define GRAIN_FREQ_CONTROL (0)<br />
#define GRAIN_DECAY_CONTROL (2)<br />
#define GRAIN2_FREQ_CONTROL (3)<br />
#define GRAIN2_DECAY_CONTROL (1)<br />
<br />
// DB<br />
#define SMOOTH_PIN 8<br />
<br />
<br />
// Changing these will also requires rewriting audioOn()<br />
<br />
#if defined(__AVR_ATmega8__)<br />
//<br />
// On old ATmega8 boards.<br />
// Output is on pin 11<br />
//<br />
#define LED_PIN 13<br />
#define LED_PORT PORTB<br />
#define LED_BIT 5<br />
#define PWM_PIN 11<br />
#define PWM_VALUE OCR2<br />
#define PWM_INTERRUPT TIMER2_OVF_vect<br />
<br />
<br />
<br />
#elif defined(__AVR_ATmega1280__)<br />
//<br />
// On the Arduino Mega<br />
// Output is on pin 3<br />
//<br />
#define LED_PIN 13<br />
#define LED_PORT PORTB<br />
#define LED_BIT 7<br />
#define PWM_PIN 3<br />
#define PWM_VALUE OCR3C<br />
#define PWM_INTERRUPT TIMER3_OVF_vect<br />
#else<br />
//<br />
// For modern ATmega168 and ATmega328 boards<br />
// Output is on pin 3<br />
//<br />
#define PWM_PIN 3<br />
#define PWM_VALUE OCR2B<br />
#define LED_PIN 13<br />
#define LED_PORT PORTB<br />
#define LED_BIT 5<br />
#define PWM_INTERRUPT TIMER2_OVF_vect<br />
#endif<br />
<br />
// Duane B <br />
// rcarduino.blogspot.com<br />
// 15/11/2012<br />
// Very simple ring buffer delay<br />
// we record the output in this array<br />
// and then mix it back in with the output as the buffer wraps around<br />
// can be switched on and off by a button on DELAY_BUTTON<br />
#define MAX_DELAY 1024<br />
unsigned char sDelayBuffer[MAX_DELAY];<br />
unsigned int nDelayCounter = 0;<br />
unsigned char bDelay;<br />
<br />
#define DELAY_BUTTON 4<br />
<br />
<br />
// Smooth logarithmic mapping<br />
//<br />
uint16_t antilogTable[] = {<br />
64830,64132,63441,62757,62081,61413,60751,60097,59449,58809,58176,57549,56929,56316,55709,55109,<br />
54515,53928,53347,52773,52204,51642,51085,50535,49991,49452,48920,48393,47871,47356,46846,46341,<br />
45842,45348,44859,44376,43898,43425,42958,42495,42037,41584,41136,40693,40255,39821,39392,38968,<br />
38548,38133,37722,37316,36914,36516,36123,35734,35349,34968,34591,34219,33850,33486,33125,32768<br />
};<br />
uint16_t mapPhaseInc(uint16_t input) {<br />
return (antilogTable[input & 0x3f]) >> (input >> 6);<br />
}<br />
<br />
// Stepped chromatic mapping<br />
//<br />
uint16_t midiTable[] = {<br />
17,18,19,20,22,23,24,26,27,29,31,32,34,36,38,41,43,46,48,51,54,58,61,65,69,73,<br />
77,82,86,92,97,103,109,115,122,129,137,145,154,163,173,183,194,206,218,231,<br />
244,259,274,291,308,326,346,366,388,411,435,461,489,518,549,581,616,652,691,<br />
732,776,822,871,923,978,1036,1097,1163,1232,1305,1383,1465,1552,1644,1742,<br />
1845,1955,2071,2195,2325,2463,2610,2765,2930,3104,3288,3484,3691,3910,4143,<br />
4389,4650,4927,5220,5530,5859,6207,6577,6968,7382,7821,8286,8779,9301,9854,<br />
10440,11060,11718,12415,13153,13935,14764,15642,16572,17557,18601,19708,20879,<br />
22121,23436,24830,26306<br />
};<br />
uint16_t mapMidi(uint16_t input) {<br />
return (midiTable[(1023-input) >> 3]);<br />
}<br />
<br />
// Stepped Pentatonic mapping<br />
//<br />
uint16_t pentatonicTable[54] = {<br />
0,19,22,26,29,32,38,43,51,58,65,77,86,103,115,129,154,173,206,231,259,308,346,<br />
411,461,518,616,691,822,923,1036,1232,1383,1644,1845,2071,2463,2765,3288,<br />
3691,4143,4927,5530,6577,7382,8286,9854,11060,13153,14764,16572,19708,22121,26306<br />
};<br />
<br />
uint16_t mapPentatonic(uint16_t input) {<br />
uint8_t value = (1023-input) / (1024/53);<br />
return (pentatonicTable[value]);<br />
}<br />
<br />
<br />
void audioOn() {<br />
#if defined(__AVR_ATmega8__)<br />
// ATmega8 has different registers<br />
TCCR2 = _BV(WGM20) | _BV(COM21) | _BV(CS20);<br />
TIMSK = _BV(TOIE2);<br />
#elif defined(__AVR_ATmega1280__)<br />
TCCR3A = _BV(COM3C1) | _BV(WGM30);<br />
TCCR3B = _BV(CS30);<br />
TIMSK3 = _BV(TOIE3);<br />
#else<br />
// Set up PWM to 31.25kHz, phase accurate<br />
TCCR2A = _BV(COM2B1) | _BV(WGM20);<br />
TCCR2B = _BV(CS20);<br />
TIMSK2 = _BV(TOIE2);<br />
#endif<br />
}<br />
<br />
<br />
void setup() {<br />
pinMode(PWM_PIN,OUTPUT);<br />
audioOn();<br />
pinMode(LED_PIN,OUTPUT);<br />
<br />
pinMode(DELAY_BUTTON,INPUT);<br />
<br />
// set pin mode and turn on pull up so that default mode <br />
// is PENTATONIC, pull the pin low to switch to smooth<br />
pinMode(SMOOTH_PIN,INPUT);<br />
digitalWrite(SMOOTH_PIN,HIGH);<br />
}<br />
<br />
void loop() {<br />
// The loop is pretty simple - it just updates the parameters for the oscillators.<br />
//<br />
// Avoid using any functions that make extensive use of interrupts, or turn interrupts off.<br />
// They will cause clicks and poops in the audio.<br />
<br />
// defaults to pentatonic stepped tones, pull pin low for smooth frequency without distinct tones <br />
// syncPhaseInc = mapPhaseInc(analogRead(SYNC_CONTROL)) / 4;<br />
<br />
syncPhaseInc = mapPentatonic(analogRead(SYNC_CONTROL));<br />
<br />
// updated 29/01/2013<br />
// pull the DELAY_BUTTON pin high for delay, low for no delay<br />
// use either a pull up/pull down resistor<br />
// or a pull up resistor with a toggle switch between the pin and ground<br />
bDelay = digitalRead(DELAY_BUTTON);<br />
<br />
// Stepped mapping to MIDI notes: C, Db, D, Eb, E, F...<br />
//syncPhaseInc = mapMidi(analogRead(SYNC_CONTROL));<br />
<br />
// Stepped pentatonic mapping: D, E, G, A, B<br />
<br />
<br />
grainPhaseInc = mapPhaseInc(analogRead(GRAIN_FREQ_CONTROL)) / 2;<br />
grainDecay = analogRead(GRAIN_DECAY_CONTROL) / 8;<br />
grain2PhaseInc = mapPhaseInc(analogRead(GRAIN2_FREQ_CONTROL)) / 2;<br />
grain2Decay = analogRead(GRAIN2_DECAY_CONTROL) / 4;<br />
}<br />
<br />
<br />
SIGNAL(PWM_INTERRUPT)<br />
{<br />
uint8_t value;<br />
uint16_t output;<br />
<br />
syncPhaseAcc += syncPhaseInc;<br />
if (syncPhaseAcc < syncPhaseInc) {<br />
// Time to start the next grain<br />
grainPhaseAcc = 0;<br />
grainAmp = 0x7fff;<br />
grain2PhaseAcc = 0;<br />
grain2Amp = 0x7fff;<br />
// LED_PORT ^= 1 << LED_BIT; // Faster than using digitalWrite<br />
}<br />
<br />
// Increment the phase of the grain oscillators<br />
grainPhaseAcc += grainPhaseInc;<br />
grain2PhaseAcc += grain2PhaseInc;<br />
<br />
// Convert phase into a triangle wave<br />
value = (grainPhaseAcc >> 7) & 0xff;<br />
if (grainPhaseAcc & 0x8000) value = ~value;<br />
// Multiply by current grain amplitude to get sample<br />
output = value * (grainAmp >> 8);<br />
<br />
// Repeat for second grain<br />
value = (grain2PhaseAcc >> 7) & 0xff;<br />
if (grain2PhaseAcc & 0x8000) value = ~value;<br />
output += value * (grain2Amp >> 8);<br />
<br />
// Make the grain amplitudes decay by a factor every sample (exponential decay)<br />
grainAmp -= (grainAmp >> 8) * grainDecay;<br />
grain2Amp -= (grain2Amp >> 8) * grain2Decay;<br />
<br />
// Scale output to the available range, clipping if necessary<br />
output >>= 9;<br />
if (output > 255) output = 255;<br />
<br />
// Duane B <br />
// rcarduino.blogspot.com<br />
// 15/11/2012<br />
// add a button to set bDelay true or false to turn delay on and off<br />
if(bDelay)<br />
{<br />
// Output to PWM (this is faster than using analogWrite) <br />
// Here we add the delay buffer to the output value, this produces<br />
// an subtle echo effect, the delay buffer is effectivley replaying the sound from<br />
// 1/8th of a second ago.<br />
<br />
LED_PORT |= 1 << LED_BIT; // Faster than using digitalWrite<br />
PWM_VALUE = (output + (sDelayBuffer[nDelayCounter]))>>1;<br />
<br />
// add the new output to the buffer so we can use it when the buffer next wraps around<br />
sDelayBuffer[nDelayCounter] = PWM_VALUE;<br />
nDelayCounter++;<br />
if(nDelayCounter == MAX_DELAY)<br />
{<br />
nDelayCounter = 0;<br />
}<br />
}<br />
else<br />
{<br />
LED_PORT &= ~(1 << LED_BIT); // Faster than using digitalWrite<br />
<br />
PWM_VALUE = output;<br />
}<br />
}</div>
<br />
<br /></div>
Can_I_Trade?http://www.blogger.com/profile/07867684474123805993noreply@blogger.com23tag:blogger.com,1999:blog-7214097671310478681.post-68022608986027251332012-11-11T09:31:00.004-08:002013-02-04T07:40:33.295-08:00How To Read RC Receiver PPM Stream<div dir="ltr" style="text-align: left;" trbidi="on">
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. <br />
<br />
This stream can often be accessed by removing the receiver case and adding a single wire to an existing connection within the receiver.<br />
<br />
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.<br />
<br />
<i>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.</i><br />
<br />
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.<br />
<br />
Scroll down for a video of the library and an RC Receiver hack in action -<br />
<br />
<b>What does the PPM Stream Look Like ?</b><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiuJkCV9RYe5Wa2aTMVeAOEH0vnHbdX9ZhL5XNd76pQ0Xy2g4KgmtKx1qYzxVqg8e5aCT7kXES330dQuFrJShl1iNG5f9C3ShuRinup_mR9O8-yo0mnkQTgdOndG0jz9m3azki-hrC-3SeY/s1600/PPMlabel.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="132" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiuJkCV9RYe5Wa2aTMVeAOEH0vnHbdX9ZhL5XNd76pQ0Xy2g4KgmtKx1qYzxVqg8e5aCT7kXES330dQuFrJShl1iNG5f9C3ShuRinup_mR9O8-yo0mnkQTgdOndG0jz9m3azki-hrC-3SeY/s400/PPMlabel.PNG" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEipifmhku7Srg8rZK5_uwd41C5g_SthTNUINPurgBlpweelBTAaeVRtd5JVAN56UHEa4DjQDxibBdKEVP0dDc7nzHRAKeK1L1wgHiSWZyNk11uIWVjhccYqbTbMbnX7GYApNGw_nM2nV37W/s1600/PPM.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><br /></a></div>
<br />
<br />
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.<br />
<br />
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.<br />
<br />
<br />
<b>How do we find the PPM Stream ?</b><br />
If your equipment provides direct access to the PPM Stream, skip over this part, if it does not, read on.<b> </b><br />
<br />
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.<br />
<br />
<br />
Fortunately for us, the de-multiplexer is most often just a simple shift register clocked by the PPM Stream.<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhOJflrPZjZBa47OwlR53meidFC57kMxruYX-CiYaNMkxbtgBYYWyLZ7Ir0807PxuF6YNyeBAG9WIkdyR2-HPXY7Dd-XDfjnBBHN0EGw41GGpfLc5sc3nb1I6ph4DDypG22jokcPfJb5jN3/s1600/insidereceiver.PNG" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhOJflrPZjZBa47OwlR53meidFC57kMxruYX-CiYaNMkxbtgBYYWyLZ7Ir0807PxuF6YNyeBAG9WIkdyR2-HPXY7Dd-XDfjnBBHN0EGw41GGpfLc5sc3nb1I6ph4DDypG22jokcPfJb5jN3/s320/insidereceiver.PNG" width="285" /></a></div>
The shift register is clearly visible inside this Hitec HFS-03MM Receiver - its the IC in the center.<br />
<br />
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).<br />
<br />
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.<br />
<br />
The best bit - read on and theres a ready made library at the end of the post. <br />
<br />
Example Datasheet for the 4015 Shift Register used to demultiplex the PPM Signal in the pictured receiver<br />
<a href="http://docs-europe.electrocomponents.com/webdocs/05f9/0900766b805f9f8d.pdf" target="_blank">http://docs-europe.electrocomponents.com/webdocs/05f9/0900766b805f9f8d.pdf</a><br />
<br />
<b>Breaking out the PPM Signal</b><br />
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 - <br />
<b> </b><br />
To access the PPM Stream from Arduino we need to solder an additional wire to the clock pin of the 4015 shift register.<br />
<br />
<b>Example PPM Hack - 1</b> <br />
<br />
Hitec 27Mhz FM 3 Channel HFS03MM Receiver - tapping the 4015 shift register to access PPM signal. <br />
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEikouL06wNyA4kaBb-cQYwDVUWYQdO0N3XIcUVEJrFJ68MQhd4Ex2MyofqX-VG8CnDIBnKhzRWKtc01enyBvDSCuN-s7Yl5DBVXtHxMr8tPBSr_gdV-2qPrSEjLcTX-_itfiGNfJ59_D5yd/s1600/FMReceiverPPM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="220" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEikouL06wNyA4kaBb-cQYwDVUWYQdO0N3XIcUVEJrFJ68MQhd4Ex2MyofqX-VG8CnDIBnKhzRWKtc01enyBvDSCuN-s7Yl5DBVXtHxMr8tPBSr_gdV-2qPrSEjLcTX-_itfiGNfJ59_D5yd/s320/FMReceiverPPM.png" width="320" /></a></div>
<br />
<br />
<br />
<br />
<br />
<b>Example PPM Hack - 2 </b><br />
<br />
Different Receiver, Different Manufacturer, Different Technology - Futaba R152JE 27Mhz AM <br />
<br />
The Same 4015 Shift Register inside tapped for PPM Output using thin white wire soldered to the clock pin.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjWALIN_5w9Bn3bSv9YzM4BvRXhYRr8COMh6PupFZUOf2g7xCDHYsThDIhvGpHwRRHAj-J_SK1HUlGwxL2RvU1ZSp-XDNZulPBV7yB24_1VN6Bs98MOpOY7rb4KE00Tn4CGQh6MkyyuFhGc/s1600/AMBreakout.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="203" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjWALIN_5w9Bn3bSv9YzM4BvRXhYRr8COMh6PupFZUOf2g7xCDHYsThDIhvGpHwRRHAj-J_SK1HUlGwxL2RvU1ZSp-XDNZulPBV7yB24_1VN6Bs98MOpOY7rb4KE00Tn4CGQh6MkyyuFhGc/s320/AMBreakout.png" width="320" /></a></div>
<br />
A male jumper wire attached to the hacked Hitec receiver ready for connection to Arduino -<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi_YvVfj0eYLOHJHXWZs0_Um_4dFWnCu4DcCYpiiGuPV5WAUH-5Ohs9qgI_lE_3ZScoPO4oa5ZCEYshx36MUnyP94ErqSvjTGML-ErxatAhNNOAZPjWYTzM75AFmPEtdj6IQfj8hdtfPzYp/s1600/FMBrokeOut.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="278" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi_YvVfj0eYLOHJHXWZs0_Um_4dFWnCu4DcCYpiiGuPV5WAUH-5Ohs9qgI_lE_3ZScoPO4oa5ZCEYshx36MUnyP94ErqSvjTGML-ErxatAhNNOAZPjWYTzM75AFmPEtdj6IQfj8hdtfPzYp/s320/FMBrokeOut.png" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhYkvC9kircLeOcv4q2qcRu0_VH_flNyqOnvOPWdlJ1IcjhY5poE_zNw6YcbfCQLlPHnDPzlCk5oKWDil1yoff6DcvGvzLr1uK93_f6KXuHWxMtCfuS5QITJSQ4fFhpW0dj8MpR-1UEaSiA/s1600/FMBrokeOut.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><br /></a></div>
<div style="text-align: center;">
<b>VIDEO - The RC Arduino Library and Receiver Hack in action</b> </div>
<div style="text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<iframe allowfullscreen='allowfullscreen' webkitallowfullscreen='webkitallowfullscreen' mozallowfullscreen='mozallowfullscreen' width='320' height='266' src='https://www.youtube.com/embed/dqxlONWB5O8?feature=player_embedded' frameborder='0'></iframe></div>
<br />
<div style="text-align: center;">
<b>Schematic showing connections in the video above</b></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh7oigsyGIsyVWort0PaFlT6MTVIxm_XuITLbAzl-aiksAe_ywTDJZXbQL_Q2Gx-3NBmH8rBuP1jszIEf46w29CZSlBPdcDNaQOPv-QuN7cH_-OsUK97cBBA00H9hmCz4wGhxzi6KOIwm1r/s1600/RCConnections.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="212" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh7oigsyGIsyVWort0PaFlT6MTVIxm_XuITLbAzl-aiksAe_ywTDJZXbQL_Q2Gx-3NBmH8rBuP1jszIEf46w29CZSlBPdcDNaQOPv-QuN7cH_-OsUK97cBBA00H9hmCz4wGhxzi6KOIwm1r/s320/RCConnections.PNG" width="320" /></a></div>
<br />
<br />
<div style="background: #F0F0F0; clear: both;">
<b>Multiplexing Servos From Arduino Using PPM Style Signals</b><br />
<br />
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.<br />
<br />
<b>RC Arduino Serial Servos</b><br />
<br />
Introduction and 10 Servos from 2 Pins <br />
<a href="http://rcarduino.blogspot.com/2012/08/arduino-serial-servos.html" target="_blank">http://rcarduino.blogspot.com/2012/08/arduino-serial-servos.html</a><br />
<br />
20 Servos From 4 Pins <br />
<a href="http://rcarduino.blogspot.com/2012/10/arduino-serial-servos-20-servos-4-pins.html" target="_blank">http://rcarduino.blogspot.com/2012/10/arduino-serial-servos-20-servos-4-pins.html</a></div>
<br />
<b>How do we read this RC Receiver PPM Pulse stream with a micro controller ?</b><br />
<br />
Now that we have access to the PPM Stream, how do we read it with our Arduino ?<br />
<br />
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. <br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiuJkCV9RYe5Wa2aTMVeAOEH0vnHbdX9ZhL5XNd76pQ0Xy2g4KgmtKx1qYzxVqg8e5aCT7kXES330dQuFrJShl1iNG5f9C3ShuRinup_mR9O8-yo0mnkQTgdOndG0jz9m3azki-hrC-3SeY/s1600/PPMlabel.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="212" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiuJkCV9RYe5Wa2aTMVeAOEH0vnHbdX9ZhL5XNd76pQ0Xy2g4KgmtKx1qYzxVqg8e5aCT7kXES330dQuFrJShl1iNG5f9C3ShuRinup_mR9O8-yo0mnkQTgdOndG0jz9m3azki-hrC-3SeY/s640/PPMlabel.PNG" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEipifmhku7Srg8rZK5_uwd41C5g_SthTNUINPurgBlpweelBTAaeVRtd5JVAN56UHEa4DjQDxibBdKEVP0dDc7nzHRAKeK1L1wgHiSWZyNk11uIWVjhccYqbTbMbnX7GYApNGw_nM2nV37W/s1600/PPM.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><br /></a></div>
1) Once we have found this space, we can set our channel counter to 0 and record the current time.<br />
<br />
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<br />
<br />
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<br />
<br />
4) When we have received all of the channels we expect, we start again at 1.<br />
<br />
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.<br />
<br />
<b>Sample Arduino Code For Reading RC Receiver PPM Signal</b><br />
<br />
The following code is taken from the RCArduinoFastLib -<br />
<span style="font-size: small;"><span style="font-family: "Courier New",Courier,monospace;"><br /></span></span>
<span style="font-size: small;"><span style="font-family: "Courier New",Courier,monospace;">// we could save a few micros by writting this directly in the signal handler rather than using attach interrupt<br />
void CRCArduinoPPMChannels::INT0ISR()<br />
{<br />
// only ever called for rising edges, so no need to check the pin state<br />
<br />
// calculate the interval between this pulse and the last one we received which is recorded in m_unChannelRiseTime<br />
uint16_t ulInterval = TCNT1 - m_unChannelRiseTime;<br />
<br />
// if all of the channels have been received we should be expecting the frame space next, lets check it<br />
if(m_sCurrentInputChannel == RC_CHANNEL_IN_COUNT)<br />
{<br />
// we have received all the channels we wanted, this should be the frame space<br />
if(ulInterval < MINIMUM_FRAME_SPACE)<br />
{<br />
// it was not so we need to resynch<br />
forceResynch();<br />
}<br />
else<br />
{<br />
// it was the frame space, next interval will be channel 0<br />
m_sCurrentInputChannel = 0;<br />
}<br />
}<br />
else<br />
{<br />
// if we were expecting a channel, but found a space instead, we need to resynch<br />
if(ulInterval > MAXIMUM_PULSE_SPACE)<br />
{<br />
forceResynch();<br />
}<br />
else<br />
{<br />
// its a good signal, lets record it and move onto the next channel<br />
m_unChannelSignalIn[m_sCurrentInputChannel++] = ulInterval;<br />
}<br />
}<br />
// record the current time <br />
m_unChannelRiseTime = TCNT1; <br />
} </span></span><br />
<br />
<br />
<b>Reading the PPM stream with the RCArduinoFastLib </b><br />
<br />
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 ?<br />
<br />
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. <br />
<br />
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.<br />
<br />
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.<br />
<br />
You can find the RCArduinoFastServos library in this post -<br />
<a href="http://rcarduino.blogspot.com/2012/11/how-to-read-rc-channels-rcarduinofastlib.html" target="_blank">http://rcarduino.blogspot.com/2012/11/how-to-read-rc-channels-rcarduinofastlib.html</a><br />
<br />
<b>Sample Sketch - Read PPM Input and Output To Mulitple Servos</b><br />
<br />
A sample sketch you can use to read three channels or PPM is presented below. The sketch can easily be modified to read upto 10 channels.<br />
<br />
If you need help or want to ask a question - ask away.<br />
<br />
<div style="background: #F0F0F0;">
<span style="font-family: "Courier New", Courier, monospace; font-size: x-small;">#include <RCArduinoFastLib.h><br /><br /> // MultiChannels<br />//<br />// rcarduino.blogspot.com<br />//<br />// A simple approach for reading three RC Channels using pin change interrupts<br />//<br />// See related posts -<br />// http://rcarduino.blogspot.co.uk/2012/01/how-to-read-rc-receiver-with.html<br />// http://rcarduino.blogspot.co.uk/2012/03/need-more-interrupts-to-read-more.html<br />// http://rcarduino.blogspot.co.uk/2012/01/can-i-control-more-than-x-servos-with.html<br />//<br />// rcarduino.blogspot.com<br />// <br /><br />// Assign your channel out pins<br />#define THROTTLE_OUT_PIN 8<br />#define STEERING_OUT_PIN 9<br />#define AUX_OUT_PIN 10<br />#define OTHER_OUT_PIN 11<br /><br />// Assign servo indexes<br />#define SERVO_THROTTLE 0<br />#define SERVO_STEERING 1<br />#define SERVO_AUX 2<br />#define SERVO_OTHER 3<br />#define SERVO_FRAME_SPACE 4<br /><br />volatile uint32_t ulCounter = 0;<br /><br />void setup()<br />{<br /> Serial.begin(115200);<br /><br /> // attach servo objects, these will generate the correct<br /> // pulses for driving Electronic speed controllers, servos or other devices<br /> // designed to interface directly with RC Receivers <br /><br /> CRCArduinoFastServos::attach(SERVO_THROTTLE,THROTTLE_OUT_PIN);<br /> CRCArduinoFastServos::attach(SERVO_STEERING,STEERING_OUT_PIN);<br /> CRCArduinoFastServos::attach(SERVO_AUX,AUX_OUT_PIN);<br /> CRCArduinoFastServos::attach(SERVO_OTHER,OTHER_OUT_PIN);<br /> <br /> // lets set a standard rate of 50 Hz by setting a frame space of 10 * 2000 = 3 Servos + 7 times 2000<br /> CRCArduinoFastServos::setFrameSpaceA(SERVO_FRAME_SPACE,6*2000);<br /><br /> CRCArduinoFastServos::begin();<br /> CRCArduinoPPMChannels::begin();<br />}<br /><br />void loop()<br />{<br /> // Pass the signals str<span style="font-size: x-small;">aight through - </span></span><br />
<span style="font-family: "Courier New", Courier, monospace; font-size: x-small;"><span style="font-size: x-small;"> </span><br /> uint16_t unThrottleIn = CRCArduinoPPMChannels::getChannel(SERVO_THROTTLE);<br /> if(unThrottleIn)<br /> {<br /> CRCArduinoFastServos::writeMicroseconds(SERVO_THROTTLE,unThrottleIn);<br /> }<br /><br /> uint16_t unSteeringIn = CRCArduinoPPMChannels::getChannel(SERVO_STEERING);<br /> if(unSteeringIn)<br /> {<br /> CRCArduinoFastServos::writeMicroseconds(SERVO_STEERING,unSteeringIn);<br /> }<br /><br /> uint16_t unAuxIn = CRCArduinoPPMChannels::getChannel(SERVO_AUX);<br /> if(unAuxIn)<br /> {<br /> CRCArduinoFastServos::writeMicroseconds(SERVO_AUX,unAuxIn);<br /> }<br />}</span><br />
<span style="font-family: "Courier New", Courier, monospace; font-size: x-small;"><br /></span>
<span style="font-family: "Courier New", Courier, monospace; font-size: x-small;"></span><br />
<span style="font-family: "Courier New", Courier, monospace; font-size: x-small;">/* DB 04/02/2012 REMOVED The interrupt service rout<span style="font-size: x-small;">ine definition here, it <span style="font-size: x-small;">clashes with<span style="font-size: x-small;"> the attachInterrupt in the cpp file */</span></span></span></span><br />
<span style="font-family: "Courier New", Courier, monospace; font-size: x-small;"><span style="font-size: x-small;"><span style="font-size: x-small;"><span style="font-size: x-small;">/* REMOVE BEGIN </span></span></span> <br />ISR(INT0_vect) {<br /> CRCArduinoPPMChannels::INT0ISR();<br />}</span><br />
<span style="font-family: "Courier New", Courier, monospace; font-size: x-small;">REMOVE END<span style="font-size: x-small;"> */</span> </span></div>
<br />
Duane B</div>
Can_I_Trade?http://www.blogger.com/profile/07867684474123805993noreply@blogger.com359tag:blogger.com,1999:blog-7214097671310478681.post-91901504379655014702012-11-04T11:00:00.000-08:002013-02-05T08:15:42.606-08:00How to read RC Channels - The RCArduinoFastLib<div dir="ltr" style="text-align: left;" trbidi="on">
<b>Background - The problem we are solving.</b><br />
<br />
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 -<br />
<br />
<br />
1) The Servo Library uses an internal interrupt to generate the servo signals<br />
2) The Interrupts we use to read incoming RC Signals<br />
3) The Arduino interrupt that drives the timing functions millis() and micros()<br />
<br />
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.<br />
<br />
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. <br />
<br />
<div style="text-align: center;">
<b>Timing clash between an interrupt and the servo library interrupt</b> </div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi5xcuHvxpYiz33BvYuA4bGYwqb9GX9UTTVLdW9R52aPbroVanOD4SuM05cza789G_rWdcfZ5JCQ2QE1pkeiWklmsbPNugmjjvjS0cvmWsTrvw2DTeRCDvKQmxNkeucsoXq_0J6NQajy0yJ/s1600/servoclash.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="195" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi5xcuHvxpYiz33BvYuA4bGYwqb9GX9UTTVLdW9R52aPbroVanOD4SuM05cza789G_rWdcfZ5JCQ2QE1pkeiWklmsbPNugmjjvjS0cvmWsTrvw2DTeRCDvKQmxNkeucsoXq_0J6NQajy0yJ/s320/servoclash.PNG" width="320" /></a></div>
<br />
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.<br />
<br />
<b>RCArduinoFastLib</b> <br />
<br />
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.<br />
<br />
The new library has the following features -<br />
<br />
1) Upto 18 Servos available on an Arduino UNO - 50% more than the standard Servo library.<br />
<br />
2) Does not reset Timer1 allowing for fast and precise timing in RC Projects using a minimal input interrupt routine<br />
<br />
3) Support for higher refresh rates of anywhere from 50 to 500Hz depending on the number of servos<br />
<br />
4) Uses a more direct method than digitalWrite for faster ISR Execution<br />
<br />
5) Reduces servo glitch frequency by 75% and glitch size by a factor of 2 when used with the RCArduino channel reading code.<br />
<br />
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. <br />
<br />
<br />
<br />
<b>How is the new library able to reduce glitches ?</b><br />
<br />
As described in the introduction typical RC Projects include at least three sets of interrupts -<br />
<br />
1) The internal interrupt used by the Arduino Servo library<br />
2) To read incoming RC Signals<br />
3) The internal interrupts used by the Arduino millis and micros functions<br />
<br />
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.<br />
<br />
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.<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjTPNbOVWBijIOHtJgS9Pl-F4LChITFXr9Uti1y-wuqTjXlS4bFbGi_zcsCDnyFd6ccxe44hubF7OEur0Y-46ls0rGfPk_Vm59mFM83hELfArkbU8r4AM2O_wAVhaJsM2IUsZjGroKIn44g/s1600/RCErrorFastServo.bmp" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="171" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjTPNbOVWBijIOHtJgS9Pl-F4LChITFXr9Uti1y-wuqTjXlS4bFbGi_zcsCDnyFd6ccxe44hubF7OEur0Y-46ls0rGfPk_Vm59mFM83hELfArkbU8r4AM2O_wAVhaJsM2IUsZjGroKIn44g/s400/RCErrorFastServo.bmp" width="400" /></a></div>
<br />
The blue line represents the sketch previously published here on RCArduino -<br />
<a href="http://rcarduino.blogspot.com/2012/04/how-to-read-multiple-rc-channels-draft.html" target="_blank">http://rcarduino.blogspot.com/2012/04/how-to-read-multiple-rc-channels-draft.html</a><br />
<br />
The red line represents the current version using RCArduinoFastLib and outlined in this post.<br />
<br />
<b>An Optimised example sketch</b><br />
<br />
Using the RCArduinoFastLib combined with the following optimisations will provide you with smoother glitch free RC Projects.<br />
<br />
<br />
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 - <a href="http://code.google.com/p/arduino-pinchangeint/" target="_blank">http://code.google.com/p/arduino-pinchangeint/</a><br />
<br />
<div style="background: #F0F0F0;">
<b>Original Interrupt Service Routing For Reading An RC Channel Pulse</b><br />
<br />
<span style="font-family: "Courier New",Courier,monospace;">void calcThrottle()</span><br />
<span style="font-family: "Courier New",Courier,monospace;">{</span><br />
<span style="font-family: "Courier New",Courier,monospace;"> if(digitalRead(THROTTLE_IN_PIN))</span><br />
<span style="font-family: "Courier New",Courier,monospace;"> {</span><br />
<span style="font-family: "Courier New",Courier,monospace;"> ulThrottleStart = micros();</span><br />
<span style="font-family: "Courier New",Courier,monospace;"> }</span><br />
<span style="font-family: "Courier New",Courier,monospace;"> else</span><br />
<span style="font-family: "Courier New",Courier,monospace;"> {</span><br />
<span style="font-family: "Courier New",Courier,monospace;"> unThrottleInShared = (uint16_t)(micros() - ulThrottleStart);</span><br />
<span style="font-family: "Courier New",Courier,monospace;"> bUpdateFlagsShared |= THROTTLE_FLAG;</span><br />
<span style="font-family: "Courier New",Courier,monospace;"> }</span><br />
<span style="font-family: "Courier New",Courier,monospace;">}</span></div>
<br />
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.<br />
<br />
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.<br />
<br />
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.<br />
<br />
<div style="background: #F0F0F0;">
<b>Updated ISR</b><br />
<br />
<br />
<span style="font-family: "Courier New",Courier,monospace; font-size: small;">void calcThrottlePulse()</span><br />
<span style="font-family: "Courier New",Courier,monospace; font-size: small;">{</span><br />
<span style="font-family: "Courier New",Courier,monospace; font-size: small;"> if(PCintPort::pinState) </span><br />
<span style="font-family: "Courier New",Courier,monospace; font-size: small;"> {</span><br />
<span style="font-family: "Courier New",Courier,monospace; font-size: small;"> unThrottleInStart = TCNT1;</span><br />
<span style="font-family: "Courier New",Courier,monospace; font-size: small;"> }</span><br />
<span style="font-family: "Courier New",Courier,monospace; font-size: small;"> else</span><br />
<span style="font-family: "Courier New",Courier,monospace; font-size: small;"> {</span><br />
<span style="font-family: "Courier New",Courier,monospace; font-size: small;"> unThrottleInShared = <b>(</b>TCNT1 - unThrottleInStart)>>1;</span><br />
<span style="font-family: "Courier New",Courier,monospace; font-size: small;"> bUpdateFlagsShared |= THROTTLE_FLAG;</span><br />
<span style="font-family: "Courier New",Courier,monospace; font-size: small;"> }</span><br />
<span style="font-family: "Courier New",Courier,monospace; font-size: small;">}</span><br />
<br /></div>
<br />
<br />
<div style="background: #F0F0F0;">
<b>Update 04/11/2012</b>- 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.<br />
<br />
<b>Forum Topic </b><br />
<a href="http://arduino.cc/forum/index.php/topic,87195.15.html" target="_blank">http://arduino.cc/forum/index.php/topic,87195.15.html</a><br />
<br />
<b>Resulting improvement in RC signal quality - </b><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjM8ALGjoMM-f_VqKVXaLsafn5PKDn_dXvUSpcDX-WDokTM75abOebUZLPkIQUgYKq5Llyb3OIt4PhOfYxgSJFBNDtVfmpUOBJuIwOc8vaN1o9NhkqsnVVlwZYn0-Ib_ASeXyufuECC5SJ3/s1600/optopti.PNG" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="192" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjM8ALGjoMM-f_VqKVXaLsafn5PKDn_dXvUSpcDX-WDokTM75abOebUZLPkIQUgYKq5Llyb3OIt4PhOfYxgSJFBNDtVfmpUOBJuIwOc8vaN1o9NhkqsnVVlwZYn0-Ib_ASeXyufuECC5SJ3/s400/optopti.PNG" width="400" /></a></div>
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%.</div>
<br />
<br />
<b>The RCArduinoFastLib</b><br />
<br />
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.<br />
<br />
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.<br />
<br />
Stay tuned ...<br />
Duane B<br />
<br />
<b>The test sketch -</b><br />
<br />
<div style="background: #F0F0F0;">
#include <RCArduinoFastLib.h><br />
<br />
// MultiChannels<br />
//<br />
// rcarduino.blogspot.com<br />
//<br />
// A simple approach for reading three RC Channels using pin change interrupts<br />
//<br />
// See related posts -<br />
// http://rcarduino.blogspot.co.uk/2012/01/how-to-read-rc-receiver-with.html<br />
// http://rcarduino.blogspot.co.uk/2012/03/need-more-interrupts-to-read-more.html<br />
// http://rcarduino.blogspot.co.uk/2012/01/can-i-control-more-than-x-servos-with.html<br />
//<br />
// rcarduino.blogspot.com<br />
// <br />
<br />
// include the pinchangeint library - see the links in the related topics section above for details<br />
#include <PinChangeInt.h><br />
<br />
// Assign your channel in pins<br />
#define THROTTLE_IN_PIN 5<br />
#define STEERING_IN_PIN 6<br />
#define AUX_IN_PIN 7<br />
<br />
// Assign your channel out pins<br />
#define THROTTLE_OUT_PIN 8<br />
#define STEERING_OUT_PIN 9<br />
#define AUX_OUT_PIN 10<br />
<br />
// Assign servo indexes<br />
#define SERVO_THROTTLE 0<br />
#define SERVO_STEERING 1<br />
#define SERVO_AUX 2<br />
#define SERVO_FRAME_SPACE 3<br />
<br />
// These bit flags are set in bUpdateFlagsShared to indicate which<br />
// channels have new signals<br />
#define THROTTLE_FLAG 1<br />
#define STEERING_FLAG 2<br />
#define AUX_FLAG 4<br />
<br />
// holds the update flags defined above<br />
volatile uint8_t bUpdateFlagsShared;<br />
<br />
// shared variables are updated by the ISR and read by loop.<br />
// In loop we immediatley take local copies so that the ISR can keep ownership of the<br />
// shared ones. To access these in loop<br />
// we first turn interrupts off with noInterrupts<br />
// we take a copy to use in loop and the turn interrupts back on<br />
// as quickly as possible, this ensures that we are always able to receive new signals<br />
volatile uint16_t unThrottleInShared;<br />
volatile uint16_t unSteeringInShared;<br />
volatile uint16_t unAuxInShared;<br />
<br />
// These are used to record the rising edge of a pulse in the calcInput functions<br />
// They do not need to be volatile as they are only used in the ISR. If we wanted<br />
// to refer to these in loop and the ISR then they would need to be declared volatile<br />
uint16_t unThrottleInStart;<br />
uint16_t unSteeringInStart;<br />
uint16_t unAuxInStart;<br />
<br />
uint16_t unLastAuxIn = 0;<br />
uint32_t ulVariance = 0;<br />
uint32_t ulGetNextSampleMillis = 0;<br />
uint16_t unMaxDifference = 0;<br />
<br />
void setup()<br />
{<br />
Serial.begin(115200);<br />
<br />
Serial.println("multiChannels");<br />
<br />
// attach servo objects, these will generate the correct<br />
// pulses for driving Electronic speed controllers, servos or other devices<br />
// designed to interface directly with RC Receivers <br />
CRCArduinoFastServos::attach(SERVO_THROTTLE,THROTTLE_OUT_PIN);<br />
CRCArduinoFastServos::attach(SERVO_STEERING,STEERING_OUT_PIN);<br />
CRCArduinoFastServos::attach(SERVO_AUX,AUX_OUT_PIN);<br />
<br />
// lets set a standard rate of 50 Hz by setting a frame space of 10 * 2000 = 3 Servos + 7 times 2000<br />
CRCArduinoFastServos::setFrameSpaceA(SERVO_FRAME_SPACE,7*2000);<br />
<br />
CRCArduinoFastServos::begin();<br />
<br />
// using the PinChangeInt library, attach the interrupts<br />
// used to read the channels<br />
PCintPort::attachInterrupt(THROTTLE_IN_PIN, calcThrottle,CHANGE);<br />
PCintPort::attachInterrupt(STEERING_IN_PIN, calcSteering,CHANGE);<br />
PCintPort::attachInterrupt(AUX_IN_PIN, calcAux,CHANGE);<br />
}<br />
<br />
void loop()<br />
{<br />
// create local variables to hold a local copies of the channel inputs<br />
// these are declared static so that thier values will be retained<br />
// between calls to loop.<br />
static uint16_t unThrottleIn;<br />
static uint16_t unSteeringIn;<br />
static uint16_t unAuxIn;<br />
// local copy of update flags<br />
static uint8_t bUpdateFlags;<br />
<br />
// check shared update flags to see if any channels have a new signal<br />
if(bUpdateFlagsShared)<br />
{<br />
noInterrupts(); // turn interrupts off quickly while we take local copies of the shared variables<br />
<br />
// take a local copy of which channels were updated in case we need to use this in the rest of loop<br />
bUpdateFlags = bUpdateFlagsShared;<br />
<br />
// in the current code, the shared values are always populated<br />
// so we could copy them without testing the flags<br />
// however in the future this could change, so lets<br />
// only copy when the flags tell us we can.<br />
<br />
if(bUpdateFlags & THROTTLE_FLAG)<br />
{<br />
unThrottleIn = unThrottleInShared;<br />
}<br />
<br />
if(bUpdateFlags & STEERING_FLAG)<br />
{<br />
unSteeringIn = unSteeringInShared;<br />
}<br />
<br />
if(bUpdateFlags & AUX_FLAG)<br />
{<br />
unAuxIn = unAuxInShared;<br />
}<br />
<br />
// clear shared copy of updated flags as we have already taken the updates<br />
// we still have a local copy if we need to use it in bUpdateFlags<br />
bUpdateFlagsShared = 0;<br />
<br />
interrupts(); // we have local copies of the inputs, so now we can turn interrupts back on<br />
// as soon as interrupts are back on, we can no longer use the shared copies, the interrupt<br />
// service routines own these and could update them at any time. During the update, the<br />
// shared copies may contain junk. Luckily we have our local copies to work with :-)<br />
}<br />
<br />
// do any processing from here onwards<br />
// only use the local values unAuxIn, unThrottleIn and unSteeringIn, the shared<br />
// variables unAuxInShared, unThrottleInShared, unSteeringInShared are always owned by<br />
// the interrupt routines and should not be used in loop<br />
<br />
// the following code provides simple pass through<br />
// this is a good initial test, the Arduino will pass through<br />
// receiver input as if the Arduino is not there.<br />
// This should be used to confirm the circuit and power<br />
// before attempting any custom processing in a project.<br />
<br />
// we are checking to see if the channel value has changed, this is indicated <br />
// by the flags. For the simple pass through we don't really need this check,<br />
// but for a more complex project where a new signal requires significant processing<br />
// this allows us to only calculate new values when we have new inputs, rather than<br />
// on every cycle.<br />
if(bUpdateFlags & THROTTLE_FLAG)<br />
{<br />
CRCArduinoFastServos::writeMicroseconds(SERVO_THROTTLE,unThrottleIn);<br />
}<br />
<br />
if(bUpdateFlags & STEERING_FLAG)<br />
{<br />
CRCArduinoFastServos::writeMicroseconds(SERVO_STEERING,unSteeringIn);<br />
}<br />
<br />
if(bUpdateFlags & AUX_FLAG)<br />
{<br />
CRCArduinoFastServos::writeMicroseconds(SERVO_AUX,unAuxIn);<br />
}<br />
<br />
bUpdateFlags = 0;<br />
}<br />
<br />
<br />
// simple interrupt service routine<br />
void calcThrottle()<br />
{<br />
if(PCintPort::pinState)<br />
{<br />
unThrottleInStart = TCNT1;<br />
}<br />
else<br />
{<br />
unThrottleInShared = (TCNT1 - unThrottleInStart)>>1;<br />
bUpdateFlagsShared |= THROTTLE_FLAG;<br />
}<br />
}<br />
<br />
void calcSteering()<br />
{<br />
if(PCintPort::pinState)<br />
{<br />
unSteeringInStart = TCNT1;<br />
}<br />
else<br />
{<br />
unSteeringInShared = (TCNT1 - unSteeringInStart)>>1;<br />
<br />
bUpdateFlagsShared |= STEERING_FLAG;<br />
}<br />
}<br />
<br />
void calcAux()<br />
{<br />
if(PCintPort::pinState)<br />
{<br />
unAuxInStart = TCNT1;<br />
}<br />
else<br />
{<br />
unAuxInShared = (TCNT1 - unAuxInStart)>>1;<br />
bUpdateFlagsShared |= AUX_FLAG; }<br />
}<br />
<br /></div>
<br />
<b>The RCArduinoFastLib.h file</b><br />
<br />
<div style="background: #F0F0F0;">
/*****************************************************************************************************************************<br />
// RCArduinoFastLib by DuaneB is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.<br />
//<br />
// http://rcarduino.blogspot.com<br />
//<br />
*****************************************************************************************************************************/<br />
<br />
#include "Arduino.h"<br />
<br />
// COMMENT OR UNCOMMENT THIS LINE TO ENABLE THE SECOND BANK OF SERVOS<br />
//#define MORE_SERVOS_PLEASE 1<br />
<br />
// the first bank of servos uses OC1A - this will disable PWM on digital pin 9 - a small price for 10 fast and smooth servos<br />
// 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<br />
<br />
// The library blindly pulses all ten servos one and after another<br />
// If you change the RC_CHANNEL_OUT_COUNT to 4 servos, the library will pulse them more frequently than<br />
// it can ten - <br />
// 10 servos at 1500us = 15ms = 66Hz<br />
// 4 Servos at 1500us = 6ms = 166Hz<br />
// if you wanted to go even higher, run two servos on each timer<br />
// 2 Servos at 1500us = 3ms = 333Hz<br />
// <br />
// You might not want a high refresh rate though, so the setFrameSpace function is provided for you to <br />
// add a pause before the library begins its next run through the servos<br />
// for 50 hz, the pause should be to (20,000 - (RC_CHANNEL_OUT_COUNT * 2000))<br />
<br />
// Change to set the number of servos/ESCs <br />
#define RC_CHANNEL_OUT_COUNT 4<br />
<br />
#if defined (MORE_SERVOS_PLEASE)<br />
#define RCARDUINO_MAX_SERVOS (RC_CHANNEL_OUT_COUNT*2)<br />
#else<br />
#define RCARDUINO_MAX_SERVOS (RC_CHANNEL_OUT_COUNT)<br />
#endif<br />
<br />
// Minimum and Maximum servo pulse widths, you could change these, <br />
// Check the servo library and use that range if you prefer<br />
#define RCARDUINO_SERIAL_SERVO_MIN 1000<br />
#define RCARDUINO_SERIAL_SERVO_MAX 2000<br />
#define RCARDUINO_SERIAL_SERVO_DEFAULT 1500<br />
<br />
#define RC_CHANNELS_NOPORT 0<br />
#define RC_CHANNELS_PORTB 1<br />
#define RC_CHANNELS_PORTC 2<br />
#define RC_CHANNELS_PORTD 3<br />
#define RC_CHANNELS_NOPIN 255<br />
<br />
//////////////////////////////////////////////////////////////////////////////////////////////////////////<br />
//<br />
// CRCArduinoFastServos<br />
//<br />
// A class for generating signals in combination with a 4017 Counter<br />
// <br />
// Output upto 10 Servo channels using just digital pins 9 and 12<br />
// 9 generates the clock signal and must be connected to the clock pin of the 4017<br />
// 12 generates the reset pulse and must be connected to the master reset pin of the 4017<br />
//<br />
// The class uses Timer1, as this prevents use with the servo library<br />
// The class uses pins 9 and 12<br />
// The class does not adjust the servo frame to account for variations in pulse width,<br />
// on the basis that many RC transmitters and receivers designed specifically to operate with servos<br />
// output signals between 50 and 100hz, this is the same range as the library<br />
//<br />
// Use of an additional pin would provide for error detection, however using pin 12 to pulse master reset<br />
// at the end of every frame means that the system is essentially self correcting<br />
//<br />
// Note<br />
// This is a simplified derivative of the Arduino Servo Library created by Michael Margolis<br />
// The simplification has been possible by moving some of the flexibility provided by the Servo library<br />
// from software to hardware.<br />
//<br />
////////////////////////////////////////////////////////////////////////////////////////////////////////////<br />
<br />
<br />
class CRCArduinoFastServos<br />
{<br />
public:<br />
static void setup();<br />
<br />
// configures timer1<br />
static void begin();<br />
<br />
// called by the timer interrupt service routine, see the cpp file for details.<br />
static void OCR1A_ISR();<br />
<br />
#if defined(MORE_SERVOS_PLEASE)<br />
static void OCR1B_ISR();<br />
#endif<br />
<br />
// called to set the pulse width for a specific channel, pulse widths are in microseconds - degrees are for wimps !<br />
static void attach(uint8_t nChannel,uint8_t nPin);<br />
static void writeMicroseconds(uint8_t nChannel,uint16_t nMicroseconds);<br />
static void setFrameSpaceA(uint8_t sChannel,uint16_t unMicroseconds);<br />
static void setFrameSpaceB(uint8_t sChannel,uint16_t unMicroseconds);<br />
<br />
protected:<br />
class CPortPin<br />
{<br />
public:<br />
//uint8_t m_sPort;<br />
volatile unsigned char *m_pPort;<br />
uint8_t m_sPinMask;<br />
uint16_t m_unPulseWidth;<br />
};<br />
<br />
// this sets the value of the timer1 output compare register to a point in the future<br />
// based on the required pulse with for the current servo<br />
static void setOutputTimerForPulseDurationA() __attribute__((always_inline));<br />
<br />
<br />
static void setChannelPinLowA(uint8_t sChannel) __attribute__((always_inline));<br />
static void setCurrentChannelPinHighA();<br />
<br />
// Easy to optimise this, but lets keep it readable instead, its short enough.<br />
static volatile uint8_t* getPortFromPin(uint8_t sPin) __attribute__((always_inline));<br />
static uint8_t getPortPinMaskFromPin(uint8_t sPin) __attribute__((always_inline));<br />
<br />
// Records the current output channel values in timer ticks<br />
// Manually set by calling writeChannel, the function adjusts from<br />
// user supplied micro seconds to timer ticks <br />
volatile static CPortPin m_ChannelOutA[RC_CHANNEL_OUT_COUNT]; <br />
// current output channel, used by the timer ISR to track which channel is being generated<br />
static uint8_t m_sCurrentOutputChannelA;<br />
<br />
#if defined(MORE_SERVOS_PLEASE)<br />
// Optional channel B for servo number 10 to 19<br />
volatile static CPortPin m_ChannelOutB[RC_CHANNEL_OUT_COUNT]; <br />
static uint8_t m_sCurrentOutputChannelB;<br />
static void setOutputTimerForPulseDurationB();<br />
<br />
static void setChannelPinLowB(uint8_t sChannel) __attribute__((always_inline));<br />
static void setCurrentChannelPinHighB() __attribute__((always_inline));<br />
#endif<br />
<br />
// two helper functions to convert between timer values and microseconds<br />
static uint16_t ticksToMicroseconds(uint16_t unTicks) __attribute__((always_inline));<br />
static uint16_t microsecondsToTicks(uint16_t unMicroseconds) __attribute__((always_inline));<br />
};<br />
<br />
// Change to set the number of channels in PPM Input stream<br />
#define RC_CHANNEL_IN_COUNT 3<br />
// two ticks per us, 3000 us * 2 ticks = 6000 minimum frame space<br />
#define MINIMUM_FRAME_SPACE 6000<br />
#define MAXIMUM_PULSE_SPACE 5000<br />
<br />
class CRCArduinoPPMChannels<br />
{<br />
public:<br />
static void begin();<br />
static void INT0ISR();<br />
static uint16_t getChannel(uint8_t nChannel);<br />
static uint8_t getSynchErrorCounter();<br />
<br />
protected:<br />
static void forceResynch();<br />
<br />
static volatile uint16_t m_unChannelSignalIn[RC_CHANNEL_IN_COUNT];<br />
static uint8_t m_sCurrentInputChannel;<br />
<br />
static uint16_t m_unChannelRiseTime;<br />
static volatile uint8_t m_sOutOfSynchErrorCounter;<br />
};</div>
<br />
<b>The RCArduinoFastLib.cpp file</b><br />
<br />
<div style="background: #F0F0F0;">
<br />
/*****************************************************************************************************************************<br />
// RCArduinoFastLib by DuaneB is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.<br />
//<br />
// http://rcarduino.blogspot.com<br />
//<br />
*****************************************************************************************************************************/<br />
<br />
#include "arduino.h"<br />
#include "RCArduinoFastLib.h"<br />
<br />
/*----------------------------------------------------------------------------------------<br />
<br />
This is essentially a derivative of the Arduino Servo Library created by Michael Margolis<br />
<br />
As the technique is very similar to the Servo class, it can be useful to study in order<br />
to understand the servo class.<br />
<br />
What does the library do ? It uses a very inexpensive and common 4017 Counter IC<br />
To generate pulses to independently drive up to 10 servos from two Arduino Pins<br />
<br />
As previously mentioned, the library is based on the techniques used in the Arduino Servo<br />
library created by Michael Margolis. This means that the library uses Timer1 and Timer1 output<br />
compare register A.<br />
<br />
OCR1A is linked to digital pin 9 and so we use digital pin 9 to generate the clock signal<br />
for the 4017 counter.<br />
<br />
Pin 12 is used as the reset pin.<br />
<br />
*/<br />
<br />
void CRCArduinoFastServos::setup()<br />
{<br />
m_sCurrentOutputChannelA = 0;<br />
while(m_sCurrentOutputChannelA < RC_CHANNEL_OUT_COUNT)<br />
{<br />
m_ChannelOutA[m_sCurrentOutputChannelA].m_unPulseWidth = microsecondsToTicks(RCARDUINO_SERIAL_SERVO_MAX); <br />
<br />
#if defined (MORE_SERVOS_PLEASE)<br />
m_ChannelOutB[m_sCurrentOutputChannelA].m_unPulseWidth = microsecondsToTicks(RCARDUINO_SERIAL_SERVO_MAX); <br />
#endif<br />
<br />
m_sCurrentOutputChannelA++;<br />
}<br />
}<br />
<br />
<br />
// Timer1 Output Compare A interrupt service routine<br />
// call out class member function OCR1A_ISR so that we can<br />
// access out member variables<br />
ISR(TIMER1_COMPA_vect)<br />
{<br />
CRCArduinoFastServos::OCR1A_ISR();<br />
}<br />
<br />
void CRCArduinoFastServos::OCR1A_ISR()<br />
{<br />
// If the channel number is >= 10, we need to reset the counter<br />
// and start again from zero.<br />
// to do this we pulse the reset pin of the counter<br />
// this sets output 0 of the counter high, effectivley<br />
// starting the first pulse of our first channel<br />
if(m_sCurrentOutputChannelA >= RC_CHANNEL_OUT_COUNT)<br />
{<br />
// reset our current servo/output channel to 0<br />
m_sCurrentOutputChannelA = 0;<br />
<br />
CRCArduinoFastServos::setChannelPinLowA(RC_CHANNEL_OUT_COUNT-1);<br />
}<br />
else<br />
{ <br />
CRCArduinoFastServos::setChannelPinLowA(m_sCurrentOutputChannelA-1);<br />
}<br />
<br />
CRCArduinoFastServos::setCurrentChannelPinHighA();<br />
<br />
// set the duration of the output pulse<br />
CRCArduinoFastServos::setOutputTimerForPulseDurationA();<br />
<br />
// done with this channel so move on.<br />
m_sCurrentOutputChannelA++;<br />
}<br />
<br />
void CRCArduinoFastServos::setChannelPinLowA(uint8_t sChannel)<br />
{<br />
volatile CPortPin *pPortPin = m_ChannelOutA + sChannel;<br />
<br />
if(pPortPin->m_sPinMask)<br />
*pPortPin->m_pPort ^= pPortPin->m_sPinMask;<br />
}<br />
<br />
void CRCArduinoFastServos::setCurrentChannelPinHighA()<br />
{<br />
volatile CPortPin *pPortPin = m_ChannelOutA + m_sCurrentOutputChannelA;<br />
<br />
if(pPortPin->m_sPinMask)<br />
*pPortPin->m_pPort |= pPortPin->m_sPinMask;<br />
}<br />
<br />
// After we set an output pin high, we need to set the timer to comeback for the end of the pulse <br />
void CRCArduinoFastServos::setOutputTimerForPulseDurationA()<br />
{<br />
OCR1A = TCNT1 + m_ChannelOutA[m_sCurrentOutputChannelA].m_unPulseWidth; <br />
}<br />
<br />
#if defined(MORE_SERVOS_PLEASE)<br />
// Timer1 Output Compare B interrupt service routine<br />
// call out class member function OCR1B_ISR so that we can<br />
// access out member variables<br />
ISR(TIMER1_COMPB_vect)<br />
{<br />
CRCArduinoFastServos::OCR1B_ISR();<br />
}<br />
<br />
void CRCArduinoFastServos::OCR1B_ISR()<br />
{<br />
if(m_sCurrentOutputChannelB >= RC_CHANNEL_OUT_COUNT)<br />
{<br />
// reset our current servo/output channel to 0<br />
m_sCurrentOutputChannelB = 0;<br />
CRCArduinoFastServos::setChannelPinLowB(RC_CHANNEL_OUT_COUNT-1);<br />
}<br />
else<br />
{ <br />
CRCArduinoFastServos::setChannelPinLowB(m_sCurrentOutputChannelB-1);<br />
}<br />
<br />
CRCArduinoFastServos::setCurrentChannelPinHighB();<br />
<br />
// set the duration of the output pulse<br />
CRCArduinoFastServos::setOutputTimerForPulseDurationB();<br />
<br />
// done with this channel so move on.<br />
m_sCurrentOutputChannelB++;<br />
}<br />
<br />
void CRCArduinoFastServos::setChannelPinLowB(uint8_t sChannel)<br />
{<br />
volatile CPortPin *pPortPin = m_ChannelOutB + sChannel;<br />
<br />
if(pPortPin->m_sPinMask)<br />
*pPortPin->m_pPort ^= pPortPin->m_sPinMask;<br />
}<br />
<br />
void CRCArduinoFastServos::setCurrentChannelPinHighB()<br />
{<br />
volatile CPortPin *pPortPin = m_ChannelOutB + m_sCurrentOutputChannelB;<br />
<br />
if(pPortPin->m_sPinMask)<br />
*pPortPin->m_pPort |= pPortPin->m_sPinMask;<br />
}<br />
<br />
<br />
<br />
// After we set an output pin high, we need to set the timer to comeback for the end of the pulse <br />
void CRCArduinoFastServos::setOutputTimerForPulseDurationB()<br />
{<br />
OCR1B = TCNT1 + m_ChannelOutB[m_sCurrentOutputChannelB].m_unPulseWidth; <br />
}<br />
#endif<br />
<br />
// updates a channel to a new value, the class will continue to pulse the channel<br />
// with this value for the lifetime of the sketch or until writeChannel is called<br />
// again to update the value<br />
void CRCArduinoFastServos::writeMicroseconds(uint8_t nChannel,uint16_t unMicroseconds)<br />
{<br />
// dont allow a write to a non existent channel<br />
if(nChannel > RCARDUINO_MAX_SERVOS)<br />
return;<br />
<br />
// constraint the value just in case<br />
unMicroseconds = constrain(unMicroseconds,RCARDUINO_SERIAL_SERVO_MIN,RCARDUINO_SERIAL_SERVO_MAX);<br />
<br />
#if defined(MORE_SERVOS_PLEASE)<br />
if(nChannel >= RC_CHANNEL_OUT_COUNT)<br />
{<br />
unMicroseconds = microsecondsToTicks(unMicroseconds);<br />
unsigned char sChannel = nChannel-RC_CHANNEL_OUT_COUNT;<br />
// disable interrupts while we update the multi byte value output value<br />
uint8_t sreg = SREG;<br />
cli();<br />
<br />
m_ChannelOutB[sChannel].m_unPulseWidth = unMicroseconds; <br />
<br />
// enable interrupts<br />
SREG = sreg;<br />
return;<br />
}<br />
#endif<br />
<br />
unMicroseconds = microsecondsToTicks(unMicroseconds);<br />
<br />
// disable interrupts while we update the multi byte value output value<br />
uint8_t sreg = SREG;<br />
cli();<br />
<br />
m_ChannelOutA[nChannel].m_unPulseWidth = unMicroseconds; <br />
<br />
// enable interrupts<br />
SREG = sreg;<br />
}<br />
<br />
uint16_t CRCArduinoFastServos::ticksToMicroseconds(uint16_t unTicks)<br />
{<br />
return unTicks / 2;<br />
}<br />
<br />
uint16_t CRCArduinoFastServos::microsecondsToTicks(uint16_t unMicroseconds)<br />
{<br />
return unMicroseconds * 2;<br />
}<br />
<br />
void CRCArduinoFastServos::attach(uint8_t sChannel,uint8_t sPin)<br />
{<br />
if(sChannel >= RCARDUINO_MAX_SERVOS)<br />
return;<br />
<br />
#if defined(MORE_SERVOS_PLEASE)<br />
if(sChannel >= RC_CHANNEL_OUT_COUNT)<br />
{<br />
// disable interrupts while we update the multi byte value output value<br />
uint8_t sreg = SREG;<br />
cli();<br />
<br />
m_ChannelOutB[sChannel-RC_CHANNEL_OUT_COUNT].m_unPulseWidth = microsecondsToTicks(RCARDUINO_SERIAL_SERVO_DEFAULT);<br />
m_ChannelOutB[sChannel-RC_CHANNEL_OUT_COUNT].m_pPort = getPortFromPin(sPin);<br />
m_ChannelOutB[sChannel-RC_CHANNEL_OUT_COUNT].m_sPinMask = getPortPinMaskFromPin(sPin);<br />
// enable interrupts<br />
SREG = sreg;<br />
pinMode(sPin,OUTPUT);<br />
return;<br />
}<br />
#endif<br />
<br />
// disable interrupts while we update the multi byte value output value<br />
uint8_t sreg = SREG;<br />
cli();<br />
<br />
m_ChannelOutA[sChannel].m_unPulseWidth = microsecondsToTicks(RCARDUINO_SERIAL_SERVO_DEFAULT);<br />
m_ChannelOutA[sChannel].m_pPort = getPortFromPin(sPin);<br />
m_ChannelOutA[sChannel].m_sPinMask = getPortPinMaskFromPin(sPin);<br />
<br />
// enable interrupts<br />
SREG = sreg;<br />
<br />
pinMode(sPin,OUTPUT);<br />
}<br />
<br />
// this allows us to run different refresh frequencies on channel A and B<br />
// for example servos at a 70Hz rate on A and ESCs at 250Hz on B<br />
void CRCArduinoFastServos::setFrameSpaceA(uint8_t sChannel,uint16_t unMicroseconds)<br />
{<br />
// disable interrupts while we update the multi byte value output value<br />
uint8_t sreg = SREG;<br />
cli();<br />
<br />
m_ChannelOutA[sChannel].m_unPulseWidth = microsecondsToTicks(unMicroseconds);<br />
m_ChannelOutA[sChannel].m_pPort = 0;<br />
m_ChannelOutA[sChannel].m_sPinMask = 0;<br />
<br />
// enable interrupts<br />
SREG = sreg;<br />
}<br />
<br />
#if defined (MORE_SERVOS_PLEASE)<br />
void CRCArduinoFastServos::setFrameSpaceB(uint8_t sChannel,uint16_t unMicroseconds)<br />
{<br />
// disable interrupts while we update the multi byte value output value<br />
uint8_t sreg = SREG;<br />
cli();<br />
<br />
m_ChannelOutB[sChannel].m_unPulseWidth = microsecondsToTicks(unMicroseconds);<br />
m_ChannelOutB[sChannel].m_pPort = 0;<br />
m_ChannelOutB[sChannel].m_sPinMask = 0;<br />
<br />
// enable interrupts<br />
SREG = sreg;<br />
}<br />
#endif<br />
// Easy to optimise this, but lets keep it readable instead, its short enough.<br />
volatile uint8_t* CRCArduinoFastServos::getPortFromPin(uint8_t sPin)<br />
{<br />
volatile uint8_t* pPort = RC_CHANNELS_NOPORT;<br />
<br />
if(sPin <= 7)<br />
{<br />
pPort = &PORTD;<br />
}<br />
else if(sPin <= 13)<br />
{<br />
pPort = &PORTB;<br />
}<br />
else if(sPin <= A5) // analog input pin 5<br />
{<br />
pPort = &PORTC;<br />
}<br />
<br />
return pPort;<br />
}<br />
<br />
// Easy to optimise this, but lets keep it readable instead, its short enough.<br />
uint8_t CRCArduinoFastServos::getPortPinMaskFromPin(uint8_t sPin)<br />
{<br />
uint8_t sPortPinMask = RC_CHANNELS_NOPIN;<br />
<br />
if(sPin <= A5)<br />
{<br />
if(sPin <= 7)<br />
{<br />
sPortPinMask = (1 << sPin);<br />
}<br />
else if(sPin <= 13)<br />
{<br />
sPin -= 8;<br />
sPortPinMask = (1 << sPin);<br />
}<br />
else if(sPin <= A5)<br />
{<br />
sPin -= A0;<br />
sPortPinMask = (1 << sPin);<br />
}<br />
}<br />
<br />
return sPortPinMask;<br />
} <br />
<br />
void CRCArduinoFastServos::begin()<br />
{<br />
TCNT1 = 0; // clear the timer count <br />
<br />
// Initilialise Timer1<br />
TCCR1A = 0; // normal counting mode <br />
TCCR1B = 2; // set prescaler of 64 = 1 tick = 4us <br />
<br />
// ENABLE TIMER1 OCR1A INTERRUPT to enabled the first bank (A) of ten servos<br />
TIFR1 |= _BV(OCF1A); // clear any pending interrupts; <br />
TIMSK1 |= _BV(OCIE1A) ; // enable the output compare interrupt <br />
<br />
#if defined(MORE_SERVOS_PLEASE)<br />
<br />
// ENABLE TIMER1 OCR1B INTERRUPT to enable the second bank (B) of 10 servos <br />
TIFR1 |= _BV(OCF1B); // clear any pending interrupts; <br />
TIMSK1 |= _BV(OCIE1B) ; // enable the output compare interrupt <br />
<br />
#endif<br />
<br />
OCR1A = TCNT1 + 4000; // Start in two milli seconds<br />
<br />
for(uint8_t sServo = 0;sServo<RC_CHANNEL_OUT_COUNT;sServo++)<br />
{<br />
Serial.println(m_ChannelOutA[sServo].m_unPulseWidth);<br />
<br />
#if defined(MORE_SERVOS_PLEASE)<br />
Serial.println(m_ChannelOutB[sServo].m_unPulseWidth);<br />
#endif<br />
}<br />
}<br />
<br />
volatile CRCArduinoFastServos::CPortPin CRCArduinoFastServos::m_ChannelOutA[RC_CHANNEL_OUT_COUNT]; <br />
uint8_t CRCArduinoFastServos::m_sCurrentOutputChannelA;<br />
<br />
#if defined(MORE_SERVOS_PLEASE) <br />
volatile CRCArduinoFastServos::CPortPin CRCArduinoFastServos::m_ChannelOutB[RC_CHANNEL_OUT_COUNT]; <br />
uint8_t CRCArduinoFastServos::m_sCurrentOutputChannelB;<br />
#endif<br />
<br />
volatile uint16_t CRCArduinoPPMChannels::m_unChannelSignalIn[RC_CHANNEL_IN_COUNT];<br />
uint8_t CRCArduinoPPMChannels::m_sCurrentInputChannel = 0;<br />
<br />
uint16_t CRCArduinoPPMChannels::m_unChannelRiseTime = 0;<br />
uint8_t volatile CRCArduinoPPMChannels::m_sOutOfSynchErrorCounter = 0;<br />
<br />
void CRCArduinoPPMChannels::begin()<br />
{<br />
m_sOutOfSynchErrorCounter = 0;<br />
attachInterrupt(0,CRCArduinoPPMChannels::INT0ISR,RISING);<br />
}<br />
<br />
// we could save a few micros by writting this directly in the signal handler rather than using attach interrupt<br />
void CRCArduinoPPMChannels::INT0ISR()<br />
{<br />
// only ever called for rising edges, so no need to check the pin state<br />
<br />
// calculate the interval between this pulse and the last one we received which is recorded in m_unChannelRiseTime<br />
uint16_t ulInterval = TCNT1 - m_unChannelRiseTime;<br />
<br />
// if all of the channels have been received we should be expecting the frame space next, lets check it<br />
if(m_sCurrentInputChannel == RC_CHANNEL_IN_COUNT)<br />
{<br />
// we have received all the channels we wanted, this should be the frame space<br />
if(ulInterval < MINIMUM_FRAME_SPACE)<br />
{<br />
// it was not so we need to resynch<br />
forceResynch();<br />
}<br />
else<br />
{<br />
// it was the frame space, next interval will be channel 0<br />
m_sCurrentInputChannel = 0;<br />
}<br />
}<br />
else<br />
{<br />
// if we were expecting a channel, but found a space instead, we need to resynch<br />
if(ulInterval > MAXIMUM_PULSE_SPACE)<br />
{<br />
forceResynch();<br />
}<br />
else<br />
{<br />
// its a good signal, lets record it and move onto the next channel<br />
m_unChannelSignalIn[m_sCurrentInputChannel++] = ulInterval;<br />
}<br />
}<br />
// record the current time <br />
m_unChannelRiseTime = TCNT1; <br />
} <br />
<br />
// if we force a resynch we set the channel<br />
void CRCArduinoPPMChannels::forceResynch() <br />
{ <br />
m_sCurrentInputChannel = RC_CHANNEL_IN_COUNT;<br />
<br />
if(m_sOutOfSynchErrorCounter<255)<br />
m_sOutOfSynchErrorCounter++; <br />
}<br />
<br />
uint8_t CRCArduinoPPMChannels::getSynchErrorCounter() <br />
{ <br />
uint8_t sErrors = m_sOutOfSynchErrorCounter;<br />
<br />
m_sOutOfSynchErrorCounter = 0;<br />
<br />
return sErrors;<br />
}<br />
<br />
uint16_t CRCArduinoPPMChannels::getChannel(uint8_t sChannel)<br />
{<br />
uint16_t ulPulse;<br />
unsigned char sreg = SREG;<br />
<br />
cli();<br />
<br />
ulPulse = m_unChannelSignalIn[sChannel];<br />
m_unChannelSignalIn[sChannel] = 0;<br />
<br />
SREG = sreg;<br />
<br />
return ulPulse>>1;<br />
}<br />
<br /></div>
<br /></div>
Can_I_Trade?http://www.blogger.com/profile/07867684474123805993noreply@blogger.com200tag:blogger.com,1999:blog-7214097671310478681.post-69833286410911564922012-11-04T06:43:00.000-08:002012-11-04T07:35:29.015-08:00Robot Band<div dir="ltr" style="text-align: left;" trbidi="on">
Its not RC and its probably not Arduino, but this little robot band should give you lots of inspiration for Arduino based music visualisers.<br />
<br />
The little singing robot in the center is great when he gets going, the lazer turrets are impressive as well - 1:30 for the impatient -<br />
<br />
<br />
<br />
<iframe allowfullscreen="allowfullscreen" frameborder="0" height="300" mozallowfullscreen="mozallowfullscreen" src="http://player.vimeo.com/video/35512208" webkitallowfullscreen="webkitallowfullscreen" width="400"></iframe>
<br />
<br />
<br />
Originally featured in the following article<br />
<br />
Fast Company<br />
<a href="http://www.fastcodesign.com/1671110/for-a-japanese-music-video-a-tiny-robot-band-inspired-by-dieter-rams#1" target="_blank">http://www.fastcodesign.com/1671110/for-a-japanese-music-video-a-tiny-robot-band-inspired-by-dieter-rams#1</a> </div>
Can_I_Trade?http://www.blogger.com/profile/07867684474123805993noreply@blogger.com4tag:blogger.com,1999:blog-7214097671310478681.post-34175100838941045172012-10-28T12:15:00.003-07:002012-10-28T12:41:12.386-07:00Arduino Serial Servos - 20 Servos, 4 Pins, High Refresh Rates<div dir="ltr" style="text-align: left;" trbidi="on">
In a recent post we looked at a very simple technique for controlling 10 servos with two Arduino pins using only one 30 cent chip.<br />
<br />
I have since extended the library for two new functions -<br />
<br />
1) Control upto 20 servos using only Four pins and two 30 cent chips<br />
2) Variable refresh rate to maximise stability and holding torque<br />
<br />
<b>10 More Servos</b><br />
<br />
The first addition - An additional 10 more servos has been achieved by duplicating the original libraries interaction with Timer1, OCR1A and the 4017 Chip onto OCR1B and a second 4017 chip. To put it another way each of the timer1 output compare registers is driving its own bank of ten servos through its own 4017 decade counter.<br />
<br />
Refer to the original post here - <a href="http://rcarduino.blogspot.com/2012/08/arduino-serial-servos.html" target="_blank">http://rcarduino.blogspot.com/2012/08/arduino-serial-servos.html</a><br />
For a description of the basic scheme.<br />
<br />
<br />
<b>Variable Refresh Rate</b><br />
This second capability has always been a function of the library but is now more accessible.<br />
<br />
<br />
The library blindly cycles through each bank of ten servos generating the required pulse. When the library gets to the last servo it points itself back to the first servo and starts again.<br />
<br />
<br />
<div style="background-color: #f3f3f3;">
<br /></div>
<div style="background-color: #f3f3f3;">
Remember, this is interrupt driven and is happening in the background, your main code is not effected.</div>
<div style="background-color: #f3f3f3;">
<br /></div>
<div style="background-color: #f3f3f3;">
A rough calculation suggests that driving 20 servos with this library will take a fraction more than half of one percent of Arduino processing power leaving you free to fill the other 99.5%</div>
<div style="background-color: #f3f3f3;">
<br /></div>
<br />
As the library continually cycles through the two banks of servos, a reduction in the number of servos will reduce the time taken for a cycle and therefore increase the servo refresh rate.<br />
<br />
If we assume an average servo pulse of 1500ms, the following average refresh rates apply -<br />
<br />
<div style="background-color: #f3f3f3;">
</div>
<table border="0" cellpadding="0" cellspacing="0" style="background-color: #f3f3f3; width: 733px;"><colgroup><col style="mso-width-alt: 5668; mso-width-source: userset; width: 116pt;" width="155"></col>
<col style="mso-width-alt: 6217; mso-width-source: userset; width: 128pt;" width="170"></col>
<col style="mso-width-alt: 7204; mso-width-source: userset; width: 148pt;" width="197"></col>
<col style="mso-width-alt: 7716; mso-width-source: userset; width: 158pt;" width="211"></col>
</colgroup><tbody>
<tr height="20" style="height: 15.0pt;">
<td class="xl63" height="20" style="height: 15pt; text-align: right; width: 116pt;" width="155"><b>Servos
In Each Bank</b></td>
<td class="xl63" style="text-align: right; width: 128pt;" width="170"><b>Average Servo Position</b></td>
<td class="xl63" style="text-align: right; width: 148pt;" width="197"><b>Time To Cycle Through Servos</b></td>
<td class="xl63" style="text-align: right; width: 158pt;" width="211"><b>Average Refresh Rate</b></td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">1</td>
<td align="right">1500</td>
<td align="right">1500</td>
<td align="right">666.6666667</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">2</td>
<td align="right">1500</td>
<td align="right">3000</td>
<td align="right">333.3333333</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" class="xl64" height="20" style="height: 15.0pt;"><b>3</b></td>
<td align="right" class="xl64"><b>1500</b></td>
<td align="right" class="xl64"><b>4500</b></td>
<td align="right" class="xl64"><b>222.2222222</b></td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" class="xl64" height="20" style="height: 15.0pt;"><b>4</b></td>
<td align="right" class="xl64"><b>1500</b></td>
<td align="right" class="xl64"><b>6000</b></td>
<td align="right" class="xl64"><b>166.6666667</b></td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">5</td>
<td align="right">1500</td>
<td align="right">7500</td>
<td align="right">133.3333333</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">6</td>
<td align="right">1500</td>
<td align="right">9000</td>
<td align="right">111.1111111</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">7</td>
<td align="right">1500</td>
<td align="right">10500</td>
<td align="right">95.23809524</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">8</td>
<td align="right">1500</td>
<td align="right">12000</td>
<td align="right">83.33333333</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">9</td>
<td align="right">1500</td>
<td align="right">13500</td>
<td align="right">74.07407407</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">10</td>
<td align="right">1500</td>
<td align="right">15000</td>
<td align="right">66.66666667</td>
</tr>
</tbody></table>
<div style="background-color: #f3f3f3;">
<br /></div>
<br />
I have highlighted the values for 3 and 4 servos as 250Hz seems to be a common request for the refresh rates of ESCs used in quadcopters.<br />
<br />
What advantage do we get by increasing the servo refresh rate ? in the case of quadcopters faster refresh can improve stability.<br />
<br />
In the case of robotics, higher refresh rates can increase holding torque and again improve stability.<br />
<br />
To drive 8 servos with a high refresh rate a fast and simple option is to use both banks of servos and drive only four servos on each bank.<br />
<br />
<div style="background-color: #f3f3f3;">
<br /></div>
<div style="background-color: #f3f3f3;">
You may already be using a variety of refresh rates without knowing, some of my RC Car transmitters operate at 50Hz, others at 60Hz and one at 91Hz - I have been mixing and matching equipment for years and only discovered the different refresh rates by plugging my receivers into an Arduino. </div>
<div style="background-color: #f3f3f3;">
<br /></div>
<br />
The refresh rate is determined by the total pulse width for each bank of servos - four servos set to 1 microsecond will be cycled through 250 (1/(4*0.001) times a second, if the same four servos are set to 2 microseconds we slow down to 125 (1/(4*.002) times a second.<br />
<br />
<b>Shouldn't I add code to fix a constant refresh rate ? </b><br />
<br />
My thinking is that if we can refresh faster and take advantage of the increased stability and hold, why would we introduce code and complexity to prevent this ? If you would prefer a fixed refresh rate, keep a few more servos than you need and use them to adjust the total.<br />
<br />
<b>The Code - </b><br />
<b> </b> <br />
Here is the new library and test sketch for you to try. The test sketch sets up the servos with increasing pulse widths, an interrupt service routine is provided which allows you to test the pulse width on any of the servo outputs.<br />
<br />
To add or remove the second bank of servos driven by OCR1B, refer to the following line in the RCArduinoSerialServos.h file<br />
<br />
<div style="background-color: #f3f3f3;">
<br /></div>
<div style="background-color: #f3f3f3;">
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">// COMMENT OR UNCOMMENT THIS LINE TO ENABLE THE SECOND BANK OF SERVOS</span><br style="font-family: "Courier New",Courier,monospace;" /><span style="font-family: "Courier New",Courier,monospace;">#define MORE_SERVOS_PLEASE 1</span></span> </div>
<br />
To change the number of servos driven by the library in order to increase the refresh rate refer to the following line in the RCArduinSerialServos.h file -<br />
<br />
<div style="background-color: #f3f3f3; font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;">// THIS IS FOR ADVANCED USERS ONLY -<br />//<br />// Change the channel out count to adjust the frequency,<br />//<br />// BEFORE DOING THIS VERIFY THAT YOUR SERVOS AND ESCS ARE COMPATIBLE<br />// WITH HIGHER AND VARIABLE REFRESH RATES<br />//<br />// The library blindly pulses all ten servos one and after another<br />// If you change the RC_CHANNEL_OUT_COUNT to 4 servos, the library will pulse them more frequently than<br />// it can ten - <br />// 10 servos at 1500us = 15ms = 66Hz<br />// 4 Servos at 1500us = 6ms = 166Hz<br />// if you wanted to go even higher, run two servos on each 4017<br />// 2 Servos at 1500us = 3ms = 333Hz<br />#define RC_CHANNEL_OUT_COUNT 4</span></div>
<br />
<b>RCArduinoSerialServos.h</b><div style="background-color: #f3f3f3; font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"><br /></span></div>
<div style="background-color: #f3f3f3; font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;">**************************************************************************************<br />// RCArduinoChannels by DuaneB is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.<br />//<br />// http://rcarduino.blogspot.com<br />//<br />*****************************************************************************************************************************/<br /><br /><br />#include "Arduino.h"<br /><br />// COMMENT OR UNCOMMENT THIS LINE TO ENABLE THE SECOND BANK OF SERVOS<br />#define MORE_SERVOS_PLEASE 1<br />// the second bank of servos uses pin 10 (clock) and 13 (reset) to drive an additional ten servos.<br />// the first bank uses pin 9 (clock) and 12 (reset) <br /><br />// THIS IS FOR ADVANCED USERS ONLY -<br />//<br />// Change the channel out count to adjust the frequency,<br />//<br />// BEFORE DOING THIS VERIFY THAT YOUR SERVOS AND ESCS ARE COMPATIBLE<br />// WITH HIGHER AND VARIABLE REFRESH RATES<br />//<br />// The library blindly pulses all ten servos one and after another<br />// If you change the RC_CHANNEL_OUT_COUNT to 4 servos, the library will pulse them more frequently than<br />// it can ten - <br />// 10 servos at 1500us = 15ms = 66Hz<br />// 4 Servos at 1500us = 6ms = 166Hz<br />// if you wanted to go even higher, run two servos on each 4017<br />// 2 Servos at 1500us = 3ms = 333Hz<br />#define RC_CHANNEL_OUT_COUNT 4<br /><br />#if defined (MORE_SERVOS_PLEASE)<br />#define RCARDUINO_MAX_SERVOS (RC_CHANNEL_OUT_COUNT*2)<br />#else<br />#define RCARDUINO_MAX_SERVOS (RC_CHANNEL_OUT_COUNT)<br />#endif<br /><br />// Minimum and Maximum servo pulse widths, you could change these, <br />// Check the servo library and use that range if you prefer<br />#define RCARDUINO_SERIAL_SERVO_MIN 1000<br />#define RCARDUINO_SERIAL_SERVO_MAX 2000<br /><br />//////////////////////////////////////////////////////////////////////////////////////////////////////////<br />//<br />// CRCArduinoSerialServos<br />//<br />// A class for generating signals in combination with a 4017 Counter<br />// <br />// Output upto 10 Servo channels using just digital pins 9 and 12<br />// 9 generates the clock signal and must be connected to the clock pin of the 4017<br />// 12 generates the reset pulse and must be connected to the master reset pin of the 4017<br />//<br />// The class uses Timer1, as this prevents use with the servo library<br />// The class uses pins 9 and 12<br />// The class does not adjust the servo frame to account for variations in pulse width,<br />// on the basis that many RC transmitters and receivers designed specifically to operate with servos<br />// output signals between 50 and 100hz, this is the same range as the library<br />//<br />// Use of an additional pin would provide for error detection, however using pin 12 to pulse master reset<br />// at the end of every frame means that the system is essentially self correcting<br />//<br />// Note<br />// This is a simplified derivative of the Arduino Servo Library created by Michael Margolis<br />// The simplification has been possible by moving some of the flexibility provided by the Servo library<br />// from software to hardware.<br />//<br />////////////////////////////////////////////////////////////////////////////////////////////////////////////<br /><br />class CRCArduinoSerialServos<br />{<br />public:<br /> CRCArduinoSerialServos();<br /><br /> // configures timer1<br /> static void begin();<br /><br /> // called by the timer interrupt service routine, see the cpp file for details.<br /> static void OCR1A_ISR();<br /> <br />#if defined(MORE_SERVOS_PLEASE)<br /> static void OCR1B_ISR();<br />#endif<br /><br /> // called to set the pulse width for a specific channel, pulse widths are in microseconds - degrees are for wimps !<br /> static void writeMicroseconds(uint8_t nChannel,uint16_t nMicroseconds);<br /><br />protected:<br /> // this sets the value of the timer1 output compare register to a point in the future<br /> // based on the required pulse with for the current servo<br /> static void setOutputTimerForPulseDurationA();<br /> <br /> // Records the current output channel values in timer ticks<br /> // Manually set by calling writeChannel, the function adjusts from<br /> // user supplied micro seconds to timer ticks <br /> volatile static uint16_t m_unChannelOutA[RC_CHANNEL_OUT_COUNT]; <br /> // current output channel, used by the timer ISR to track which channel is being generated<br /> static uint8_t m_sCurrentOutputChannelA;<br /> <br />#if defined(MORE_SERVOS_PLEASE)<br /> // Optional channel B for servo number 10 to 19<br /> volatile static uint16_t m_unChannelOutB[RC_CHANNEL_OUT_COUNT]; <br /> static uint8_t m_sCurrentOutputChannelB;<br /> static void setOutputTimerForPulseDurationB();<br />#endif<br /><br /> // two helper functions to convert between timer values and microseconds<br /> static uint16_t ticksToMicroseconds(uint16_t unTicks);<br /> static uint16_t microsecondsToTicks(uint16_t unMicroseconds);<br />};</span></div>
<br />
<b>RCArduinoSerialServos.cpp</b><br />
<div style="background-color: #f3f3f3; font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;">#include "RCArduinoSerialServos.h"<br /><br />/*----------------------------------------------------------------------------------------<br /><br />This is essentially a derivative of the Arduino Servo Library created by Michael Margolis<br /><br />As the technique is very similar to the Servo class, it can be useful to study in order<br />to understand the servo class.<br /><br />What does the library do ? It uses a very inexpensive and common 4017 Counter IC<br />To generate pulses to independently drive up to 10 servos from two Arduino Pins<br /><br />As previously mentioned, the library is based on the techniques used in the Arduino Servo<br />library created by Michael Margolis. This means that the library uses Timer1 and Timer1 output<br />compare register A.<br /><br />OCR1A is linked to digital pin 9 and so we use digital pin 9 to generate the clock signal<br />for the 4017 counter.<br /><br />Pin 12 is used as the reset pin.<br /><br />*/<br /><br />// Timer1 Output Compare A interrupt service routine<br />// call out class member function OCR1A_ISR so that we can<br />// access out member variables<br />ISR(TIMER1_COMPA_vect)<br />{<br /> CRCArduinoSerialServos::OCR1A_ISR();<br />}<br /><br />void CRCArduinoSerialServos::OCR1A_ISR()<br />{<br /> // If the channel number is >= 10, we need to reset the counter<br /> // and start again from zero.<br /> // to do this we pulse the reset pin of the counter<br /> // this sets output 0 of the counter high, effectivley<br /> // starting the first pulse of our first channel<br /> if(m_sCurrentOutputChannelA >= RC_CHANNEL_OUT_COUNT)<br /> {<br /> // reset our current servo/output channel to 0<br /> m_sCurrentOutputChannelA = 0;<br /><br /> // pulse reset on the counter - we set it high here<br /> PORTB|=16;<br /><br /> // set the duration of the output pulse<br /> CRCArduinoSerialServos::setOutputTimerForPulseDurationA();<br /><br /> // finish the reset pulse - we set it low here<br /> PORTB^=16;<br /> }<br /> else<br /> {<br /> // pulse the clock pin high<br /> PORTB|=2;<br /><br /> // set the duration of the output pulse<br /> CRCArduinoSerialServos::setOutputTimerForPulseDurationA();<br /><br /> // finish the clock pulse - set it back to low<br /> PORTB^=2;<br /> }<br /> // done with this channel so move on.<br /> m_sCurrentOutputChannelA++;<br />}<br />// After we set an output pin high, we need to set the timer to comeback for the end of the pulse <br />void CRCArduinoSerialServos::setOutputTimerForPulseDurationA()<br />{<br /> OCR1A = TCNT1 + m_unChannelOutA[m_sCurrentOutputChannelA]; <br />}<br /><br />#if defined(MORE_SERVOS_PLEASE)<br />// Timer1 Output Compare B interrupt service routine<br />// call out class member function OCR1B_ISR so that we can<br />// access out member variables<br />ISR(TIMER1_COMPB_vect)<br />{<br /> CRCArduinoSerialServos::OCR1B_ISR();<br />}<br /><br />void CRCArduinoSerialServos::OCR1B_ISR()<br />{<br /> // If the channel number is >= 10, we need to reset the counter<br /> // and start again from zero.<br /> // to do this we pulse the reset pin of the counter<br /> // this sets output 0 of the counter high, effectivley<br /> // starting the first pulse of our first channel<br /> if(m_sCurrentOutputChannelB >= RC_CHANNEL_OUT_COUNT)<br /> {<br /> // reset our current servo/output channel to 10 -<br /> // Note that 10 is the first servo on output compare B<br /> m_sCurrentOutputChannelB = 0;<br /><br /> // pulse reset on the counter - we set it high here<br /> PORTB|=32;<br /><br /> // set the duration of the output pulse<br /> CRCArduinoSerialServos::setOutputTimerForPulseDurationB();<br /><br /> // finish the reset pulse - we set it low here<br /> PORTB^=32;<br /> }<br /> else<br /> {<br /> // pulse the clock pin high<br /> PORTB|=4;<br /><br /> // set the duration of the output pulse<br /> CRCArduinoSerialServos::setOutputTimerForPulseDurationB();<br /><br /> // finish the clock pulse - set it back to low<br /> PORTB^=4;<br /> }<br /> // done with this channel so move on.<br /> m_sCurrentOutputChannelB++;<br />}<br /><br />// After we set an output pin high, we need to set the timer to comeback for the end of the pulse <br />void CRCArduinoSerialServos::setOutputTimerForPulseDurationB()<br />{<br /> OCR1B = TCNT1 + m_unChannelOutB[m_sCurrentOutputChannelB]; <br />}<br />#endif<br /><br />// updates a channel to a new value, the class will continue to pulse the channel<br />// with this value for the lifetime of the sketch or until writeChannel is called<br />// again to update the value<br />void CRCArduinoSerialServos::writeMicroseconds(uint8_t nChannel,uint16_t unMicroseconds)<br />{<br /> // dont allow a write to a non existent channel<br /> if(nChannel > RCARDUINO_MAX_SERVOS)<br /> return;<br /><br /> // constraint the value just in case<br /> unMicroseconds = constrain(unMicroseconds,RCARDUINO_SERIAL_SERVO_MIN,RCARDUINO_SERIAL_SERVO_MAX);<br /><br />#if defined(MORE_SERVOS_PLEASE)<br /> if(nChannel >= RC_CHANNEL_OUT_COUNT)<br /> {<br /> // disable interrupts while we update the multi byte value output value<br /> uint8_t sreg = SREG;<br /> cli();<br /> <br /> m_unChannelOutB[nChannel-RC_CHANNEL_OUT_COUNT] = microsecondsToTicks(unMicroseconds); <br /><br /> // enable interrupts<br /> SREG = sreg;<br /> return;<br /> }<br />#endif<br /> <br /> // disable interrupts while we update the multi byte value output value<br /> uint8_t sreg = SREG;<br /> cli();<br /> <br /> m_unChannelOutA[nChannel] = microsecondsToTicks(unMicroseconds); <br /><br /> // enable interrupts<br /> SREG = sreg;<br />}<br /><br />uint16_t CRCArduinoSerialServos::ticksToMicroseconds(uint16_t unTicks)<br />{<br /> return unTicks / 2;<br />}<br /><br />uint16_t CRCArduinoSerialServos::microsecondsToTicks(uint16_t unMicroseconds)<br />{<br /> return unMicroseconds * 2;<br />}<br /><br />void CRCArduinoSerialServos::begin()<br />{<br /> // set the pins we will use for channel A (OCR1A) as outputs<br /> pinMode(9,OUTPUT); // clock uses OCR1A and should not be changed<br /> pinMode(12,OUTPUT); // reset, if you really needed to, you could change this, but remember to change it in the ISR as well<br /><br /> // pulse reset<br /> digitalWrite(12,HIGH);<br /> digitalWrite(12,LOW);<br /><br />#if defined (MORE_SERVOS_PLEASE)<br /><br /> // set the pins we will use for channel B (OCR1B) as outputs<br /> pinMode(10,OUTPUT); // clock uses OCR1B and should not be changed<br /> pinMode(13,OUTPUT); // reset, if you really needed to, you could change this, but remember to change it in the ISR as well<br /><br /> // pulse reset<br /> digitalWrite(12,HIGH);<br /> digitalWrite(12,LOW);<br /><br />#endif<br /><br /> TCNT1 = 0; // clear the timer count <br /><br /> // Initilialise Timer1<br /> TCCR1A = 0; // normal counting mode <br /> TCCR1B = 2; // set prescaler of 64 = 1 tick = 4us <br /><br /> // ENABLE TIMER1 OCR1A INTERRUPT to enabled the first bank (A) of ten servos<br /> TIFR1 |= _BV(OCF1A); // clear any pending interrupts; <br /> TIMSK1 |= _BV(OCIE1A) ; // enable the output compare interrupt <br /><br />#if defined(MORE_SERVOS_PLEASE)<br /><br /> // ENABLE TIMER1 OCR1B INTERRUPT to enable the second bank (B) of 10 servos <br /> TIFR1 |= _BV(OCF1B); // clear any pending interrupts; <br /> TIMSK1 |= _BV(OCIE1B) ; // enable the output compare interrupt <br /><br />#endif<br /><br /> OCR1A = TCNT1 + 4000; // Start in two milli seconds<br />}<br /><br />// See the .h file<br />volatile uint16_t CRCArduinoSerialServos::m_unChannelOutA[RC_CHANNEL_OUT_COUNT]; <br />uint8_t CRCArduinoSerialServos::m_sCurrentOutputChannelA;<br /><br />#if defined(MORE_SERVOS_PLEASE)<br /><br />volatile uint16_t CRCArduinoSerialServos::m_unChannelOutB[RC_CHANNEL_OUT_COUNT]; <br />uint8_t CRCArduinoSerialServos::m_sCurrentOutputChannelB;<br /><br />#endif</span></div>
<div style="background-color: #f3f3f3; font-family: "Courier New",Courier,monospace;">
<br /></div>
<br />
<b> RCArduinoSerialServos sketch</b><br />
<br />
<div style="background-color: #f3f3f3; font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;"><br /></span></div>
<div style="background-color: #f3f3f3; font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;">// RCArduinoSerialServos by DuaneB is licensed under a Creative Commons Attribution-NonCommercial 3.0 Unported License.<br />// Based on a work at rcarduino.blogspot.com.<br />#include <RCArduinoSerialServos.h><br /><br />volatile uint32_t ulRiseTime;<br />volatile uint32_t ulPulseWidth;<br /><br />void setup()<br />{<br /> Serial.begin(9600);<br /> Serial.println("RCArduinoSerialServos");<br /> <br /> // set the channels<br /> for(uint16_t nChannel = 0;nChannel < RCARDUINO_MAX_SERVOS;nChannel++)<br /> {<br /> CRCArduinoSerialServos::writeMicroseconds(nChannel,1000+(nChannel*50));<br /> }<br /><br /> CRCArduinoSerialServos::begin();<br /> <br /> attachInterrupt(0,calcPulse,CHANGE);<br />}<br /><br />void loop()<br />{<br /> delay(10);<br /><br /> if(ulPulseWidth != 0)<br /> {<br /> // disable interrupts so that our pulse value does not get overwritten while we try and read it<br /> uint8_t sReg = SREG;<br /> cli();<br /> <br /> // take a local copy of the pulse witdth so that we can reenable interrupts as soon as possible<br /> uint32_t ulLocalPulseWidth = ulPulseWidth;<br /> <br /> // clear the pulse width so that we will only pick up new values written by calcPulse rather <br /> // than keep printing old values.<br /> ulPulseWidth = 0;<br /> <br /> // turn interrupts back on<br /> SREG = sReg;<br /> <br /> // print the pulse width<br /> Serial.println(ulLocalPulseWidth);<br /> }<br /> <br />}<br /><br />// Read pulse width applied to digital pin 2 (interrupt 0)<br />void calcPulse()<br />{<br /> if(digitalRead(2))<br /> {<br /> ulRiseTime = micros();<br /> }<br /> else<br /> {<br /> ulPulseWidth = micros()- ulRiseTime;<br /> }<br />}</span></div>
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br /></div>
Can_I_Trade?http://www.blogger.com/profile/07867684474123805993noreply@blogger.com16tag:blogger.com,1999:blog-7214097671310478681.post-6204949579437380052012-10-23T12:26:00.001-07:002012-11-25T23:45:04.194-08:00Five Dollar Synthesiser (post in progress)<div dir="ltr" style="text-align: left;" trbidi="on">
What do you get out of interfacing Arduino to a low cost keyboard -<br />
<br />
<b>UPDATE - New synthesis mode added - Auduino style grain synthesis</b><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<iframe allowfullscreen='allowfullscreen' webkitallowfullscreen='webkitallowfullscreen' mozallowfullscreen='mozallowfullscreen' width='320' height='266' src='https://www.youtube.com/embed/ZuKoG15oObk?feature=player_embedded' frameborder='0'></iframe></div>
<br />
<br />
<b>UPDATE - Listen to the five dollar synthesiser here - </b><br />
<div class="separator" style="clear: both; text-align: center;">
<iframe allowfullscreen='allowfullscreen' webkitallowfullscreen='webkitallowfullscreen' mozallowfullscreen='mozallowfullscreen' width='320' height='266' src='https://www.youtube.com/embed/yQsgIRSDGm0?feature=player_embedded' frameborder='0'></iframe></div>
<br />
<br />
<b>Why hack a cheap childs keyboad - </b><br />
<b>1)</b> Three octaves or more of ready made piano style keys<br />
<b>2)</b> A large number of general purpose buttons for configuring you synth engine, adding effects or controlling a sequencer <br />
<br />
This post concludes with a sketch which will provide your keyboard with some interesting capabilities to experiment with -<br />
<br />
<b>3) Four different waveforms</b> - sine, square, ramp and dirty triangle<br />
<b>4) An adjustable speed arpeggio mode</b>, low speed = arpeggios, high speed = chord like effects, very high speed = spacey distortion<br />
<b>5) Arpeggio record and play back</b><br />
<b>6) Transpose function</b>, this shifts the three octave keyboard up and down by upto 7 octaves to give full access to the 128 midi note range - its also used in the video to creating the sweeping Arpeggios.<br />
<br />
<b>7) Delay !</b> - the code now features a delay function which you can here in action in the video.<br />
<br />
Quick demonstration of using transpose with arpeggios - <br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<iframe allowfullscreen='allowfullscreen' webkitallowfullscreen='webkitallowfullscreen' mozallowfullscreen='mozallowfullscreen' width='320' height='266' src='https://www.youtube.com/embed/lbTUv96BTIk?feature=player_embedded' frameborder='0'></iframe></div>
<br />
<b>Interfacing a low cost keyboard to Arduino -</b><br />
<br />
The keyboard I am using is available for around 5 to 10 dollars, any similar keyboard in this price bracket is likely to work in the same way so pick one up and prepare to plug in.<br />
<br />
<b>So whats inside your typical five dollar keyboard ?</b><br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgUkBm6OMVeiAijlEboVh6-eE6eoDEEZAeE_5w7Klic6ckOm53UYFZ_hoBZ-Hdy3N79m7m3AyRXyX8etJK6bt_fAooDKIB6dxycXCyuU9-7qvDenEs0ebH0BhDWgra5Aa0eVRotYB79msBp/s1600/keyboardinside.PNG" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" height="183" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgUkBm6OMVeiAijlEboVh6-eE6eoDEEZAeE_5w7Klic6ckOm53UYFZ_hoBZ-Hdy3N79m7m3AyRXyX8etJK6bt_fAooDKIB6dxycXCyuU9-7qvDenEs0ebH0BhDWgra5Aa0eVRotYB79msBp/s320/keyboardinside.PNG" width="320" /></a>The main features are two PCBs which connect the many buttons and keys in a matrix.<br />
<br />
The top PCB has been turned over to show the buttons and connections.<br />
<br />
The entire matrix of buttons and keys is accessible from the17 solder pads visible in the center of the top PCB. Through these 17 pads we can read the state of any of the 36 keys and 33 buttons on our example keyboard.<br />
<br />
Your keyboard may have a different number of keys and buttons, but the concept will be the same - all of the buttons are connected in a matrix pattern and this matrix is used to read the state of the individual buttons.<br />
<br />
<div style="background: #f0f0f0;">
In the top right of the picture you might be able to make out a small amplifier. This amplifier uses a LM386 Chip which has previously been featured on RC Arduino in the post 'Adding Audio To Arduino Projects', this drives a small speaker fitted in the keyboard.<br />
<br />
<a href="http://rcarduino.blogspot.com/2012/08/adding-audio-to-arduino-projects.html" target="_blank">http://rcarduino.blogspot.com/2012/08/adding-audio-to-arduino-projects.html</a></div>
<br />
<br />
<b>Zooming in for a closer look</b><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj4ZfEYfIDLUnPbYVtr_UD1UcniluKhtady3Xx54GjX9fpiuf_8BaxKSOddbE_P0lhbY46DBGU8VQwyiq_8J8eE2CbVTdveGPuDeSgYuWlh9H4LaI7goTk8KKFlMYhr6sJAoqIrST0KX_cs/s1600/keyboardmatrix.PNG" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" height="167" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj4ZfEYfIDLUnPbYVtr_UD1UcniluKhtady3Xx54GjX9fpiuf_8BaxKSOddbE_P0lhbY46DBGU8VQwyiq_8J8eE2CbVTdveGPuDeSgYuWlh9H4LaI7goTk8KKFlMYhr6sJAoqIrST0KX_cs/s320/keyboardmatrix.PNG" width="320" /></a></div>
While it might not be obvious from the picture the 17 solder connections represent 9 rows of 8 columns.<br />
<br />
When a button is pressed it makes a unique connection between a row and a column.<br />
<br />
By reading the state of these unique connections we know which keys are currently pressed. <br />
<br />
<br />
The great thing about using a ready made keyboard is that someone has already built the matrix for us. This many buttons and keys to plug into our Arduino is an excellent deal - its even better when you consider that we can read these buttons using only 10 or less Arduino pins.<br />
<br />
<b>How can we read the buttons using only 10 pins ?</b><br />
<br />
The solution is multiplexing. To do this we switch all of the rows off and then switch each one on in turn. We record which row is currently on and then read the column to see if any keys are pressed.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjUjKGTzsCx-tNTU69j5XtNX-92RCBsW6Q-_m58hCgrb2mbBrCAz1Q2BakMGWZQj2D9BTmG4t9_4lMZgTwjIFgZYkhWotHGs7FtikygY8cTN6EdEro9Soq2nyoIDu_moPqmOSt4TwLdhMX6/s1600/keyboardrow0.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="152" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjUjKGTzsCx-tNTU69j5XtNX-92RCBsW6Q-_m58hCgrb2mbBrCAz1Q2BakMGWZQj2D9BTmG4t9_4lMZgTwjIFgZYkhWotHGs7FtikygY8cTN6EdEro9Soq2nyoIDu_moPqmOSt4TwLdhMX6/s320/keyboardrow0.PNG" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjgmOWE5G4rS4R45aNQpcSC3Mp0Y7xFqLW1X0t24vRtnZ4gqvX6_Qw1bZab7ZzJoff5tkVA20nC6-doKfPaTmpI-9pUjJJ43oTYMeXsSIk5iOrFExrBud7hdBv-Wobpbx2gDnt0i1O9_ynG/s1600/keyboardrow1.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="152" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjgmOWE5G4rS4R45aNQpcSC3Mp0Y7xFqLW1X0t24vRtnZ4gqvX6_Qw1bZab7ZzJoff5tkVA20nC6-doKfPaTmpI-9pUjJJ43oTYMeXsSIk5iOrFExrBud7hdBv-Wobpbx2gDnt0i1O9_ynG/s320/keyboardrow1.PNG" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh2hix9HIRO_VbHMmrbW7SN_we80T9tCxjAj4-PbhGtcDSOyKQ3DMfwjRmu-tcAEPzEmM0Jf36BLKWbtUAlIqpt_hlPD5Ln9uOg9oHxXOX5rE04YE7wZ9G2bWYA1z-YhNipsNfcUTdUMV2G/s1600/keyboardrow2.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="152" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh2hix9HIRO_VbHMmrbW7SN_we80T9tCxjAj4-PbhGtcDSOyKQ3DMfwjRmu-tcAEPzEmM0Jf36BLKWbtUAlIqpt_hlPD5Ln9uOg9oHxXOX5rE04YE7wZ9G2bWYA1z-YhNipsNfcUTdUMV2G/s320/keyboardrow2.PNG" width="320" /></a></div>
If we press the center key of the keyboard, our column pins will not read anything until row 3 is the active (on) row. Then we can then do some simple maths to get the key number -<br />
<br />
key number = (row * number of pins in a column) + column;<br />
<br />
20 = (2*8) + 4<br />
<br />
<b>How do we do this with Arduino ?</b><br />
<br />
One approach to this would be to dedicate a digital output pin for each row and a digital input pin for each column. This can be done but its a huge waste of resources, we would also need to write code to manage the switching on and off (multiplexing) of all of these pins.<br />
<br />
A 4017 Decade Counter is a low cost widely available chip that will do this work for us using only 2 pins from the Arduino. <br />
<br />
<br />
<div style="background: #F0F0F0;">
This chip has previously been featured on RC Arduino as a means to drive upto 10 Servos from just two digital output pins. It is a chip with all sorts of uses - LED Chasers, input and output multiplexing and more. If you don't have any, order a few, they are about 30 cents each and I can guarantee you will find a use for them.<br />
<br />
<a href="http://rcarduino.blogspot.com/2012/08/arduino-serial-servos.html" target="_blank">http://rcarduino.blogspot.com/2012/08/arduino-serial-servos.html</a></div>
<br />
The 4017 Decade counter does exactly what we want -<br />
<br />
1) Has ten outputs<br />
2) It only activates one output at a time<br />
3) It automatically figures out which output to turn off and which one to turn on in a repeatable sequence<br />
4) It does this switching across the 10 outputs and all we have to do is simply set an output HIGH then LOW to tell it when to switch to the next row.<br />
<br />
<b>Upgrading your keyboard</b><br />
Your keyboard almost certainly has an existing micro controller inside it. In the example keyboard the micro controller is encased in a blob of white plastic so there is no chance to identify it or reprogram it.<br />
<br />
To upgrade the keyboard we want to access the keyboard matrix using our Arduino, in the case of the example keyboard the microcontroller is on a separate board which can easily be removed by desoldering. <br />
<br />
Before - The microcontroller PCB is fitted sideways into the main key matrix PCB.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjHrFPn9UVp6cqLQV7eS53LpzgGUAIyFFckVMM7WtMXlPGFj7nGf2oPIeE6yDiFioUAO0dm36OXhyphenhyphen3jOtLZH00fod9feQQNpAMpTMqQQZNbeqD-gv47Sr5ZObpBsCrzbhc06N0uPFF2aXUG/s1600/keyboardbefore.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="204" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjHrFPn9UVp6cqLQV7eS53LpzgGUAIyFFckVMM7WtMXlPGFj7nGf2oPIeE6yDiFioUAO0dm36OXhyphenhyphen3jOtLZH00fod9feQQNpAMpTMqQQZNbeqD-gv47Sr5ZObpBsCrzbhc06N0uPFF2aXUG/s320/keyboardbefore.PNG" width="320" /></a></div>
<br />
After - The original micro controller replaced with a set of wires that we can use to access the key matrix from Arduino. <br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjuWQ4j3C2VRaPJMBaiSa1sYuZMwhdaajT5OJWKNrk0mZmKKUA2T1oo7Y7dUsVLAt0dwWLivaYwJw8NBliq1R_1owLpE13GFMguFbCHP_z25gC7LwD40-PkrhkVsy872wAV7EbiP5QPhlAB/s1600/keyboardafter.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="209" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjuWQ4j3C2VRaPJMBaiSa1sYuZMwhdaajT5OJWKNrk0mZmKKUA2T1oo7Y7dUsVLAt0dwWLivaYwJw8NBliq1R_1owLpE13GFMguFbCHP_z25gC7LwD40-PkrhkVsy872wAV7EbiP5QPhlAB/s320/keyboardafter.PNG" width="320" /></a></div>
<br />
Once the original micro controller is disconnected from the key matrix we can solder in a set of wires in its place, these wires will be used to connect the Arduino and 4017.<br />
<br />
<b>The Schematic</b><br />
<br />
<b>Parts List - </b><br />
8 * Current limiting resistors ( whatever you have between 500Ohms and 1K Ohms)<br />
8 * 10K Pull down resistors <br />
10 Diodes<br />
1 * 4017 Decade Counter<br />
Connecting wire<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhDQ4rvMwUjZghd-Fv9IdDNuws9_CdqbXhZmK303Pp6Rzr4r6VfGQz2M5zVL5e89buvfzbe6FFM71Zm_whoMjX3mUBqh0zeW7mGUjg8rWHvoI8-AEeMJxVc_E66YotaZ_dwb8ZgCmX7DrUz/s1600/4017KeyScanD.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="247" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhDQ4rvMwUjZghd-Fv9IdDNuws9_CdqbXhZmK303Pp6Rzr4r6VfGQz2M5zVL5e89buvfzbe6FFM71Zm_whoMjX3mUBqh0zeW7mGUjg8rWHvoI8-AEeMJxVc_E66YotaZ_dwb8ZgCmX7DrUz/s320/4017KeyScanD.PNG" width="320" /></a></div>
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
The arrangement of resistors shown for pin 4 should be duplicated for the 8 columns (pins 4 to 11). The 10K resistor pulls the input down to ground when the switch is open, without this, the switch would be floating leading to inconsistent readings. The 680 Resistors is in series with the pin and is there to limit the current entering the pin when a key is pressed.<br />
<br />
<b>In pictures</b><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh293n5ozahN0eQYuzP6stqwqaVB4bwj8sV6i-h7dQgBq4QD8bjXoVf2jXY-KsaUFevN7-ucRnCHSlXdeFZGUt56Z5dfjPLXr1NHHewaMVKBWr9TbhNkTsXxnd5EAD0yIlQ2wZAWyrKiMi7/s1600/4017Columns2.PNG" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh293n5ozahN0eQYuzP6stqwqaVB4bwj8sV6i-h7dQgBq4QD8bjXoVf2jXY-KsaUFevN7-ucRnCHSlXdeFZGUt56Z5dfjPLXr1NHHewaMVKBWr9TbhNkTsXxnd5EAD0yIlQ2wZAWyrKiMi7/s320/4017Columns2.PNG" width="206" /></a></div>
<b>Columns</b><br />
Starting at the bottom of the picture each column is connect to ground through a 10K pull down resistor.<br />
<br />
The keyboard columns are connected through the thin white wires in the center of the picture.<br />
<br />
These are then connected to the white jumper wires at the top of the screen through 680 Ohm current limiting resistors.<br />
<br />
Thats it for the columns<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhIpIHGUxyJuWKpkVjRagEwx3jttADRoeDxO0o8NOBkm7zVaOy9N6QUykCOVewOSgqpU0KV6rrjxkKmPclSkhcuGs6vhxEFxG7Glm0xLLlJBX6WUjvxBNTlYl-fM3nDI92Zn5bL9OCysM6r/s1600/4017Columns.PNG" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" height="275" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhIpIHGUxyJuWKpkVjRagEwx3jttADRoeDxO0o8NOBkm7zVaOy9N6QUykCOVewOSgqpU0KV6rrjxkKmPclSkhcuGs6vhxEFxG7Glm0xLLlJBX6WUjvxBNTlYl-fM3nDI92Zn5bL9OCysM6r/s320/4017Columns.PNG" width="320" /></a></div>
<b>Rows</b><br />
Starting from the bottom we have the keyboard rows connected through the thin white wires.<br />
<br />
Each of these is connected through a diode to an output of the 4017 Decade counter.<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<b>The full circuit - 72 Keys from 10 Arduino Pins using just one IC, 16 Resistors and 9 Diodes - </b><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhHD0L4W3vdVVJz4-Nup5Yjx9Wby-_M6n9Lf402JD8LBa2pvGNvj4fmsXAmVaHBFYJYCvPv-FlxbZFiet8YmpxzEdUBIxNf1QF-HW_rOwqCyD0sUYG_gPaWkz34_kv_qAe1FTr1iuCMJlJS/s1600/4017allin.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="245" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhHD0L4W3vdVVJz4-Nup5Yjx9Wby-_M6n9Lf402JD8LBa2pvGNvj4fmsXAmVaHBFYJYCvPv-FlxbZFiet8YmpxzEdUBIxNf1QF-HW_rOwqCyD0sUYG_gPaWkz34_kv_qAe1FTr1iuCMJlJS/s400/4017allin.PNG" width="400" /></a></div>
Later in the series we can introduce a shift register to bring the number of Arduino pins down to 5 from the current 10. <br />
<br />
<b>Key Scanning Code</b><br />
<br />
<div style="clear: both;">
</div>
<div style="background: #f0f0f0;">
<br />
In order to ensure the code is portable between the Arduino Mega, Uno, Leonardo, Teensy and Due, the code makes use of the DigitalIO Library. This is not as fast as using direct port manipulation or the DigitalPin library but it is around four times faster than standard Arduino digitalRead and digitalWrite functions - a useful saving.<br />
<br />
The DigitalIO and DigitalPin libraries are the work of Arduino forum user FatLib16 whose name comes from fast SD Card libraries that FatLib16 has also created.<br />
<br />
The DigitalIO and DigitalPin Libraries can be downloaded from the following link -<br />
<br />
<a href="http://code.google.com/p/beta-lib/downloads/detail?name=DigitalPinBeta20120804.zip&can=2&q=" target="_blank">http://code.google.com/p/beta-lib/downloads/detail?name=DigitalPinBeta20120804.zip&can=2&q=</a><br />
<br />
For a complete list of FatLib16's libraries see here -<br />
<a href="http://code.google.com/p/beta-lib/downloads/list" target="_blank">http://code.google.com/p/beta-lib/downloads/list</a><br />
<br />
Some of these will be interesting to explore in the future as a way to add a sample and playback capability to the 5 dollar keyboard.</div>
<br />
<b>Key Scan using DigitalIO Library</b><br />
<br />
<div style="background: #F0F0F0;">
<br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> for(unsigned char sRow = 0;sRow <= (KEYS/COL_PINS);sRow++)<br /> {<br /> for(unsigned char sCol = 0;sCol < COL_PINS;sCol++)<br /> {</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> // read each column pin using an array of DigitalIO objects, its faster than digital read</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> // and portable to UNO, Mega, Leonardo, Teensy, Teensy 3 and Due in the future</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> // Direct port manipulation is faster, but not portable.<br /> if(columnPins[sCol].read())<br /> {<br /> unsigned char sKey = (sRow<<3) + sCol;</span></span> <br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> // Do something with this key</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> }</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> }</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> clockKeyScanCounter(); // clock the 4017 to scan the next <span style="font-size: x-small;">keypad ro<span style="font-size: x-small;">w</span></span> </span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> }</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"></span></span>
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"><br /></span></span>
</div>
<br />
<b>The Next Step</b><br />
<br />
<br />
The synth engine is provided by a single oscillator, using the library referenced in this post -<br />
<br />
<br />
<a href="http://rcarduino.blogspot.com/2012/10/arduino-modular-synthesizer-part-one.html" target="_blank">http://rcarduino.blogspot.com/2012/10/arduino-modular-synthesizer-part-one.html</a><br />
<br />
The next version
will include a simulated patch panel to configure the oscillator paths for a wider range of sounds and effects. <br />
<br />
For a zip file including the complete sketch and modular synth library, message me Duane B on the Arduino Forum.<br />
<br />
Duane B</div>
Can_I_Trade?http://www.blogger.com/profile/07867684474123805993noreply@blogger.com30tag:blogger.com,1999:blog-7214097671310478681.post-73446966065373416802012-10-19T01:48:00.000-07:002012-10-19T01:48:05.880-07:00Lap Timer Build Along Part 4 - Adding the IR Detector<div dir="ltr" style="text-align: left;" trbidi="on">
The final part of the lap timer build along is also the easiest part, involving only the IR Detector and an optional LED with current limiting resistors.<br />
<br />
The previous steps can be found in the project index section of RC Arduino -<br />
<a href="http://rcarduino.blogspot.com/p/project-index.html" target="_blank">http://rcarduino.blogspot.com/p/project-index.html</a><br />
<br />
A good introduction to IR Detectors is provided on the Ada Fruit website here -<br />
<br />
<a href="http://learn.adafruit.com/ir-sensor" target="_blank">http://learn.adafruit.com/ir-sensor</a><br />
<br />
To start our build we need a small section of strip board and a set of three wires for power, ground and IR Out. <br />
<br />
These can be soldered to the strip board so each leg of the IR Detector is connected through the copper strips of the strip board to one strand of the cable. In the picture I have used a section of ribbon cable, the cable should be long enough to suite your application, for example long enough to mount the detector on the steering column support of your Kart and allow you to attach the main unit to the steering wheel - don't make the cable longer than necessary it will only get in the way and may pick up interference.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgfT5F12pFnGIccCv-z9OzxvW-ADp3CU7cuSB6d1S6bjHfgifBfmbJIjK92VmkSeAv-HKL7x455Q33FaIYRc8h5Rdm5oW_WdPB5KY_s9bWDoneOoDHOD8IksC3BLYjkhUnhSIBnZcgG2O9L/s1600/IRDetector.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="142" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgfT5F12pFnGIccCv-z9OzxvW-ADp3CU7cuSB6d1S6bjHfgifBfmbJIjK92VmkSeAv-HKL7x455Q33FaIYRc8h5Rdm5oW_WdPB5KY_s9bWDoneOoDHOD8IksC3BLYjkhUnhSIBnZcgG2O9L/s320/IRDetector.PNG" width="320" /></a></div>
Notice that the IR Detector is facing away from the connecting wires and that there is room for some additional components between the detector and the connecting wires, this is to allow us to add an indicator ID.<br />
<br />
<div style="text-align: center;">
Reverse Side View</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiW9jFJpsRO5xPJgdata2SgnBPCA88m2em8uv5oxHTPdDG-Juz3maZW0-glvnvejLQt9XHX94xqFCQjz-7m8PuyTk9Vb_FK3t-FyJajQvzdKuI6nC3wocNlMaaTJckzq_7hFlSkHfQOmJDv/s1600/IRDetectorReverse.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="133" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiW9jFJpsRO5xPJgdata2SgnBPCA88m2em8uv5oxHTPdDG-Juz3maZW0-glvnvejLQt9XHX94xqFCQjz-7m8PuyTk9Vb_FK3t-FyJajQvzdKuI6nC3wocNlMaaTJckzq_7hFlSkHfQOmJDv/s320/IRDetectorReverse.PNG" width="320" /></a></div>
<br />
Next we add a 10K current limiting resistor between the output of the IR Detector and the wire we will be connecting to our Arduino interrupt pin.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj5NNZkSp2hC_CloknzBnPJe0nl5_6UMikAOmpQWzgRT0ibKDYq8V5YOlv0DwWO-59gbI5fsVGuSQZZDv79I9Pl1QEPRGotTJXqWVNOGD55c7XAR62395BAOZb9ftWZO8kZR3NUM4bEpMyq/s1600/IRDetector10K.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="134" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj5NNZkSp2hC_CloknzBnPJe0nl5_6UMikAOmpQWzgRT0ibKDYq8V5YOlv0DwWO-59gbI5fsVGuSQZZDv79I9Pl1QEPRGotTJXqWVNOGD55c7XAR62395BAOZb9ftWZO8kZR3NUM4bEpMyq/s320/IRDetector10K.PNG" width="320" /></a></div>
For this resistor to have any effect we need to cut the copper track underneath the resistor so that the current has to pass through the resistor, a 3mm or 3.5mm drill bit will do this nicely.<br />
<br />
<div style="text-align: center;">
Reverse view showing the cut in the copper track beneath the 10K resistor </div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEincFHjqgnAkMu5sCgCLTVpwNsLkbWIQvpaTDWRpeDR2lGkmy5mqOjiQPUNqJZgAML3GRR6zx3tNtWPBSVgBSJlkQBnPcTn-afnMExqmaK-NHGv8nGgmo4GiqE-ql0kmTq-EsBh3OAUitMm/s1600/IRDetector10KReverse.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="121" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEincFHjqgnAkMu5sCgCLTVpwNsLkbWIQvpaTDWRpeDR2lGkmy5mqOjiQPUNqJZgAML3GRR6zx3tNtWPBSVgBSJlkQBnPcTn-afnMExqmaK-NHGv8nGgmo4GiqE-ql0kmTq-EsBh3OAUitMm/s320/IRDetector10KReverse.PNG" width="320" /></a></div>
<br />
Next we need to add the current limiting resistor for our indicator LED, I am using 560, but anything from 500 to 800 Ohms should be fine. This resistor connects from the row with the VCC Pin of the detector to the row below the Vout pin. From here we can also add two short lengths of connecting wire for our indicator LED, on wire should come from the VOut track and another from the track below VOut where we have just added the resistor.<br />
<br />
This is to allow use to add an indicator LED which will light whenever the unit receives an IR Signal, this is useful as it will let you know if there is environmental interference such as reflected sunlight or fluorescent lighting.<br />
<br />
At this point you should have the following circuit -<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj7PE-ShF4GLfEQMxYtrNv9ypwtxaIdxS3IzCqM-I8ydhUYnShVwRVRY03rNR0bMRXQRGXf9Y3MvnvdHPyBwF2zgCjqucMRvRFuDXWwLyMT1idZuEAUavpHtR0M_S8G7CCukvMgOB_OvUgp/s1600/IRDetectorIndicator.PNG" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" height="282" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj7PE-ShF4GLfEQMxYtrNv9ypwtxaIdxS3IzCqM-I8ydhUYnShVwRVRY03rNR0bMRXQRGXf9Y3MvnvdHPyBwF2zgCjqucMRvRFuDXWwLyMT1idZuEAUavpHtR0M_S8G7CCukvMgOB_OvUgp/s320/IRDetectorIndicator.PNG" width="320" /></a></div>
You can now add an indicator LED of whatever colour will be most visible in your application. To connect the LED, the long leg should be connected to the length of wire which is soldered to the same row as the 560 Ohm resistor, the shorted leg should be connected to same the same row as the Vout pin of the IR Detector.<br />
<br />
You can test this set up immediately by connecting 4 to 6 volts to the circuit, the positive voltage should be applied to the top row, the ground should be connected to the middle row. If you use a TV Remote or the Transponder from part 3 of the build along, you should see the LED Light, if not, try swapping the LED around incase it was soldered in reverse.<br />
<br />
This is essentially the same circuit as shown in the Lady Ada tutorial -<br />
<br />
<a href="http://learn.adafruit.com/ir-sensor/testing-an-ir-sensor" target="_blank">http://learn.adafruit.com/ir-sensor/testing-an-ir-sensor</a><br />
<br />
Assuming that you have tested your detector correctly, we can now connect it to the Lap Timer.<br />
<br />
To do this we need to connect the Vcc wire (top pin/wire in the picture assuming the detector is facing away from the connecting wires) to the 5V supply of the Arduino. Next we need to connect the center wire to the ground of the Arduino. Finally connect the bottom wire to digital pin 2 of your Arduino.<br />
<br />
Congratulations, you have now finished the electronics however to be able to use the lap timer you need to build a small enclosure for the detector, without this sunlight and many type of indoor lighting will saturate your detector so that it is unable to detect signals. As I am based in Dubai where the sun is always fierce I have gone as far as spraying the inside of my enclosure with ultra matt camouflage paint. You can see the enclosures I have used in the following clips of the timer in action, not that the indicator LED is on the outside of the enclosure where we can see it - you knew that already right ?<br />
<br />
<div style="text-align: center;">
Build Along Lap Timer in action complete with IR Detector as built in this post </div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<iframe allowfullscreen='allowfullscreen' webkitallowfullscreen='webkitallowfullscreen' mozallowfullscreen='mozallowfullscreen' width='320' height='266' src='https://www.youtube.com/embed/ryQFShhtzlQ?feature=player_embedded' frameborder='0'></iframe></div>
<br />
I have recently added a few extensions to the project including a count down mode and support for external audio, you can see the new menu options and see them in action at the track in the following two clips -<br />
<br />
<br />
<table>
<tbody>
<tr>
<td><div style="text-align: center;">
New Menu Options</div>
<div class="separator" style="clear: both; text-align: center;">
<iframe allowfullscreen='allowfullscreen' webkitallowfullscreen='webkitallowfullscreen' mozallowfullscreen='mozallowfullscreen' width='320' height='266' src='https://www.youtube.com/embed/ua4z5bb5bKU?feature=player_embedded' frameborder='0'></iframe></div>
</td>
<td><div style="text-align: center;">
At the track with external audio enabled</div>
<div class="separator" style="clear: both; text-align: center;">
<object class="BLOGGER-youtube-video" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0" data-thumbnail-src="http://2.gvt0.com/vi/0_IP14gFKcI/0.jpg" height="266" width="320"><param name="movie" value="http://www.youtube.com/v/0_IP14gFKcI&fs=1&source=uds" /><param name="bgcolor" value="#FFFFFF" /><param name="allowFullScreen" value="true" /><embed width="320" height="266" src="http://www.youtube.com/v/0_IP14gFKcI&fs=1&source=uds" type="application/x-shockwave-flash" allowfullscreen="true"></embed></object></div>
</td>
</tr>
<tr>
<td><br /></td>
<td><br /></td>
</tr>
</tbody></table>
The external audio option uses an LM386 based amplifier to drive
external speakers. You can use this IC to add big sound to any Arduino
project, here is a link to the circuit as used in the Lap Timer -<br />
<br />
<a href="http://rcarduino.blogspot.com/2012/08/adding-audio-to-arduino-projects.html" target="_blank">http://rcarduino.blogspot.com/2012/08/adding-audio-to-arduino-projects.html</a><br />
<br />
If you would like the latest code, contact me through the arduino forum for a zip file containing the full project.<br />
<br />
Future Developments - I am considering adding support for three additional transponder types -<br />
<br />
1) Magnetic - I am told that many Kart Tracks use a magnetic strip under the track which lapping Karts detect using a window sensor such as you would use for home security.<br />
2) Commercial Beacons - The commercial beacons used at many auto racing tracks use a well know pattern of pulses, it would make sense to support this pattern allowing users to 'arrive and drive' without having to place your own transponder around the track.<br />
3) User selected pulse length - Allow the user to choose a pulse length, this would allow two or three systems to be used alongside each other. All of the calculations needed to build your own unique transponder are linked in previous parts of build along. <br />
<br />
Stay tuned.<br />
<br />
Duane B<br />
<br /></div>
Can_I_Trade?http://www.blogger.com/profile/07867684474123805993noreply@blogger.com20tag:blogger.com,1999:blog-7214097671310478681.post-57834727236688315112012-10-13T04:05:00.003-07:002012-10-13T04:07:26.816-07:00Algorithmic Music Visualizer<div dir="ltr" style="text-align: left;" trbidi="on">
This is a short follow up to a previous post 'Algorithmic Music On Arduino'<br />
<br />
<a href="http://rcarduino.blogspot.com/2012/09/algorithmic-music-on-arduino.html" target="_blank">http://rcarduino.blogspot.com/2012/09/algorithmic-music-on-arduino.html</a><br />
<br />
The music generation algorithms take advantage of the repeating structure within binary numbers, an obvious development is a visualizer that would reveal some of the structure within the music.<br />
<br />
This quick project uses the value of the algorithm output to drive the sound and visuals directly. The visualizer is simply a scrolling buffer of the 8 most recent output values.<br />
<br />
<div style="background: #f0f0f0;">
<br />
While it would be possible to use FFT to visualize the music, in the case of algorithmic music it is more interesting to use the visualizer as a means to explore what is happening within the algorithm
rather than in the frequency spectrum.<br />
<br />
</div>
<br />
<br />
The short video shows that even with no additional processing, the visuals are very different for each of the algorithms and reveal some of the structure within the music.<br />
<br />
The first algorithm running from 0:11 to 0:38 produces a cascade effect reminiscent of the matrix - unfortunately this effect is subtle and needs development for the best effect. The second section of the video provides a more obvious demonstration of how the sound is being developed within the algorithm.<br />
<br />
Some other ideas to explore in the future -<br />
<br />
1) Bit rate - for each bit in the 8 bit output, increment a counter if it has changed from the last sample. Use the values for the 8-bits to drive the display<br />
<br />
2) Divide by 2 - Use the current output for row 0, update row 1 every second output, row 2 every fourth output, row 3 every 8 etc upto 128<br />
<br />
For now, here is a scrolling buffer of the 8 most recent output values (and a little reaction from the family)<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<iframe allowfullscreen='allowfullscreen' webkitallowfullscreen='webkitallowfullscreen' mozallowfullscreen='mozallowfullscreen' width='320' height='266' src='https://www.youtube.com/embed/p6A9XpogZV4?feature=player_embedded' frameborder='0'></iframe></div>
<br />
Duane B<br />
<br /></div>
Can_I_Trade?http://www.blogger.com/profile/07867684474123805993noreply@blogger.com1tag:blogger.com,1999:blog-7214097671310478681.post-15340910644045092032012-10-10T22:36:00.001-07:002012-10-12T00:30:53.292-07:00New Arduino Products and Video Tutorials<div dir="ltr" style="text-align: left;" trbidi="on">
UK Based electronics supplier RS-Online have added a new Arduino section to the home page of thier site.<br />
<br />
Clicking on the links will take you to this section with an overview of the new starter kit, some video demonstrations and datasheets.<br />
<br />
These are the 15 projects included in the new official Arduino starter kit -<br />
<br />
01 GET TO KNOW YOUR OWN TOOL an introduction to the concepts you'll need to use this kit<br />
02 SPACESHIP INTERFACE design to control panel for your starship<br />
03 LOVE-O-METER measure how hot-blooded you are<br />
04 COLOR MIXING LAMP produce any color with a lamp that uses light as an input<br />
05 MOOD CUE clue people in to how you're doing<br />
06 LIGHT THERIM create a musical instrument you play by waving your hands<br />
07 KEYBOARD INSTRUMENT play music and make some noise with this keyboard<br />
08 DIGITAL HOURGLASS a light-up hourglass that can stop you from working too much<br />
09 MOTORIZED PINWHEEL a color wheel that will have your head spinning<br />
10 ZOETROPE create a mechanical animation you can play forward or reverse<br />
11 CRYSTAL BALL a mystical tour to answer all your tough question<br />
12 KNOCK LOCK tap out the secret code to open the door<br />
13 TOUCHY-FEEL LAMP a lamp that responds to your touch<br />
14 TWEAK THE ARDUINO LOGO control your personal computer from your Arduino<br />
15 HACKING BUTTONS create a master control for all your devices!<br />
<br />
More information including the video tutorials can be found here -<br />
<br />
<a href="http://uk.rs-online.com/web/generalDisplay.html?id=arduino" target="_blank">http://uk.rs-online.com/web/generalDisplay.html?id=arduino</a><br />
<br />
<b>Arduino Due</b> <br />
<br />
While the site does not mention the upcoming Arduino Due, there is a 'stay tuned for Arduino' email notification which you can subscribe to in the link above. If RS have any marketting sense I am sure they will use this mailing list to announce the availability of Due or pre orders.<br />
<br />
So if you are UK based Stay Tuned.<br />
<br />
UPDATE: The Arduino team have started a new thread on the Arduino forum for the newly released started kit - <a href="http://arduino.cc/forum/index.php/topic,125444.0/topicseen.html" target="_blank">http://arduino.cc/forum/index.php/topic,125444.0/topicseen.html</a><br />
<br />
<br />
Duane B </div>
Can_I_Trade?http://www.blogger.com/profile/07867684474123805993noreply@blogger.com2tag:blogger.com,1999:blog-7214097671310478681.post-76775144527465128572012-10-07T06:04:00.001-07:002012-10-07T11:11:45.446-07:00Arduino Modular Synthesizer - Part One<div dir="ltr" style="text-align: left;" trbidi="on">
The RC Arduino site has featured a number of Audio projects recently, and while they all produce very different sounds they all use the same basic technique to generate their sound.<br />
<br />
With this in mind an obvious development of these projects would be a modular synthesizer which allows the user to easily combine the techniques used by individual projects to produce new instruments.<br />
<br />
<b>Modular Synthesizer</b><br />
A modular synthesizer is traditionally a large analogue device often including a mix and match of vintage components. Early models were often enormous DIY machines that could easily take up a whole wall of a room.<br />
<br />
Current models are still large in comparison to digital synthesizers this is partly due to the physical user interface which is also what gives these machines their character. <br />
<br />
<b>A typical modular synthesizer user interface </b>- Analog inputs to control the module oscillators and a patchwork of wires to direct the signals between different modules. <br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="http://upload.wikimedia.org/wikipedia/commons/thumb/6/66/Doepfer_A-100.jpg/789px-Doepfer_A-100.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="242" src="http://upload.wikimedia.org/wikipedia/commons/thumb/6/66/Doepfer_A-100.jpg/789px-Doepfer_A-100.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Original photo by Nina Richards</td></tr>
</tbody></table>
<br />
<br />
<b>Arduino as a Digital Modular Synth ?</b><br />
<br />
While the Arduino is a digital device, the Auduino and Illutron B projects both demonstrate the appeal of using analog inputs to control an Arduino based digital synthesizer.<br />
<br />
<table>
<tbody>
<tr>
<td><b>Auduino</b>
</td>
<td><b>Illutron B</b></td></tr>
<tr><td><div class="separator" style="clear: both; text-align: center;">
<iframe allowfullscreen='allowfullscreen' webkitallowfullscreen='webkitallowfullscreen' mozallowfullscreen='mozallowfullscreen' width='320' height='266' src='https://www.youtube.com/embed/BGkMBjvw1a8?feature=player_embedded' frameborder='0'></iframe></div>
<br />
<b>Auduino Features - </b><br />
A grain synthesizer which uses two
triangle waveforms with independently controlled frequency and decay to
create a more complex waveform. A fifth control is then used to set the
pitch by controlling the repetition rate of the complex waveform.<br />
<br />
The
tone of the waveform can be dramatically altered by adjusting the
frequencies of the two component waveforms. </td>
<td><div class="separator" style="clear: both; text-align: center;">
</div>
<iframe allowfullscreen='allowfullscreen' webkitallowfullscreen='webkitallowfullscreen' mozallowfullscreen='mozallowfullscreen' width='320' height='266' src='https://www.youtube.com/embed/fTmLKKLv5cE?feature=player_embedded' frameborder='0'></iframe><b> </b><br />
<br />
<b>Illutron B Features</b> - <br />
A wavetable synthesizer which uses
pre calculated waveforms stored within the program memory. These
waveforms are played back at different speeds to produce sound at the
required pitch. To further increase the musical quality of the
waveforms, they are combined with an envelope, this describes how the
sound developes over time and can provide an approximation of many
musical instruments.<br />
<br />
<br /></td>
</tr>
</tbody></table>
<br />
<br />
More information and examples of both of these projects can be found here -<br />
<br />
<a href="http://rcarduino.blogspot.com/2012/08/the-must-build-arduino-project-illutron.html" target="_blank">http://rcarduino.blogspot.com/2012/08/the-must-build-arduino-project-illutron.html</a><br />
<a href="http://rcarduino.blogspot.com/2012/08/adding-audio-to-arduino-projects.html" target="_blank">http://rcarduino.blogspot.com/2012/08/adding-audio-to-arduino-projects.html</a><br />
<br />
<b>Taking it further</b><br />
<br />
The goal of this series of posts is to extend these ideas by -<br />
<br />
1) Building a set of software components that will provide digital versions of the most common modules found in a modular synthesizer. <br />
<br />
<br />
2) Building a simple, low cost simulated patch panel to
direct how the digital sound will be built and processed by the software
running on the Arduino.<br />
<br />
<b>Progress so far</b> <br />
<br />
Below are two examples of vastly different Arduino synthesisers built for this project with the same four oscillators connected in different ways - in effect this is a demonstration of the concept of an Arduino modular synthesiser.<br />
<br />
<table>
<tbody>
<tr>
<td><div style="text-align: center;">
XLT2 - eXtremely Limited Techno Toy</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgShmcCPtoLpmzu_NJHlnELWLlPqRmsmVUGMplEfwPev5QxoK6GK311mba-7rtyRdRpRlmszDhEoIJQhxvVekkud9pmdyNoHht5-S2oGVSehTmU7ejaxRcBt7eHgawNvcAt7vgjm7Hg7I9R/s1600/XLT2.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgShmcCPtoLpmzu_NJHlnELWLlPqRmsmVUGMplEfwPev5QxoK6GK311mba-7rtyRdRpRlmszDhEoIJQhxvVekkud9pmdyNoHht5-S2oGVSehTmU7ejaxRcBt7eHgawNvcAt7vgjm7Hg7I9R/s320/XLT2.PNG" width="255" /></a></div>
</td>
<td><div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgk7AnDF7LV4YHBsclrFeR8yQINmsVFMWTjRN3zHLBB5hGETDYYvTT-qMioy2V7HlafGMV4BCHCUKOlNWKTFztDyC1zGAxwxx_gPwXhpGoOfzpZhNcTEpK2hxvyXoDeXbbba1shPNwxboxz/s1600/AuduinoModulated.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;">Gated Grain Synthesiser<img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgk7AnDF7LV4YHBsclrFeR8yQINmsVFMWTjRN3zHLBB5hGETDYYvTT-qMioy2V7HlafGMV4BCHCUKOlNWKTFztDyC1zGAxwxx_gPwXhpGoOfzpZhNcTEpK2hxvyXoDeXbbba1shPNwxboxz/s320/AuduinoModulated.PNG" width="241" /></a></div>
</td>
</tr>
<tr>
<td><div class="separator" style="clear: both; text-align: center;">
<iframe allowfullscreen='allowfullscreen' webkitallowfullscreen='webkitallowfullscreen' mozallowfullscreen='mozallowfullscreen' width='320' height='266' src='https://www.youtube.com/embed/pv3Srnp8G5Q?feature=player_embedded' frameborder='0'></iframe></div>
<br /></td>
<td><div class="separator" style="clear: both; text-align: center;">
<object width="320" height="266" class="BLOGGER-youtube-video" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0" data-thumbnail-src="http://2.gvt0.com/vi/vzPqAE_B5gY/0.jpg"><param name="movie" value="http://www.youtube.com/v/vzPqAE_B5gY&fs=1&source=uds" /><param name="bgcolor" value="#FFFFFF" /><param name="allowFullScreen" value="true" /><embed width="320" height="266" src="http://www.youtube.com/v/vzPqAE_B5gY&fs=1&source=uds" type="application/x-shockwave-flash" allowfullscreen="true"></embed></object></div>
<br /></td>
</tr>
</tbody></table>
<br />
<b>The eXtremeley Limited Techno Toy or XLT2</b><br />
<br />
As an initial experiment I wanted to build something that could be built from a few common components and that would be able to generate a reasonable range of new sounds.<br />
<br />
The first result is the XLT2 - eXtemeley Limited Techno Toy.<br />
<br />
An added bonus is that this synth and the Gated Grain Synth below are both directly compatible with the Auduino synth circuit, so if you have never built an Auduino before, now you have two more reasons to build one.<br />
<br />
The XLT2 features two audio oscillators that are able to generate square, ramp or sine waveforms at audio frequencies.<br />
<br />
These audio waveforms are then combined with individual low frequency oscillators ( LFOs ) which again generate square, ramp or sine waves but at much lower frequencies. These low frequency waveforms may not be audible on their own, but when we direct them to control the amplitude (volume) of an audio signal they can add a rythm, pulse or vibrato effect.<br />
<br />
Each LFO also has an associated LED which reflects the LFO output both in frequency and profile - i.e. a squarewave will switch the LED hard on, then hard off whereas a sinewave will fade on and fade off reflecting the effect the LFO has on the audio signal.<br />
<br />
A final control is provided to mix the two channels together according to a user selected ratio.<br />
<br />
<b>The Gated Grain Synthesizer</b><br />
<br />
Once the XLT2 was up and running I wanted to try creating a similar sound to the Auduino using the same four oscillators as the XLT2, the grain synthesizer is the result.<br />
<br />
This synth can produce a sound similar to the Auduino or even a passable violin if a sinewave is used as the output gate.<br />
<br />
The main feature of the Gated Grain synth is that it sounds very different from the XLT2 but is built from the same four oscillators driving different aspects of the audio signal. <br />
<br />
<b>Proof of concept</b> <br />
<br />
The difference in sound between these two devices demonstrates the potential for an Arduino based synthesiser which allows the end user to configure the sound generation path using a simulated patch pannel. A follow up post will explore some of the options for implementing the patch pannel and how the relevant routing can be implemented at run time in software.<br />
<br />
<div style="background: #F0F0F0;">
<b>Getting Serious and moving to the Arduino Due</b><br />
<br />
While this project has lots of potential for development the limited processing power of the 8-bit 16Mhz Arduino UNO will only get us so far.<br />
<br />
The soon to be release Arduino Due platform however will allow the idea to be taken much further.<br />
<br />
The Due has a built in analog to digital converter giving a true analog output rather than the UNOs PWM output, this together with higher resolution should provide far better sound quality.<br />
<br />
The higher processing speeds of the Arduino Due will also allow for more oscillators and more importantly - complex relationships between the oscillators. It should also be possible to implement some software filters.<br />
<br />
Perhaps the most exciting possibility is the high sampling rate offered by the Due, it may be possible to capture samples directly with the due and use them there and then as part of the synthesizer.<br />
<br />
<br /></div>
<br />
<br />
As the project stands, the user must select the waveforms at compile time however an obvious development would be to add a button for each oscillator allowing the user to cycle through the available waveforms. As my intention is to provide this function and others through a simulated patch panel, I will leave it to the user to add these buttons.<br />
<br />
<br />
Note : The Audio output in the video is through a PC Speaker driven by the simple amplifier outlined in this post -<br />
<br />
<a href="http://rcarduino.blogspot.com/2012/08/adding-audio-to-arduino-projects.html" target="_blank">http://rcarduino.blogspot.com/2012/08/adding-audio-to-arduino-projects.html</a><br />
<br />
<br />
<br />
<div style="background: #F0F0F0;">
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">#define SAMPLE_MAX (65535.0)</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">#define SAMPLE_FREQUENCY (8000.0)</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">#define TIMER1_FREQUENCY 2000000</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">#define UPDATE_RATE 8000</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"><br /></span></span>
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">#define MIDI_NOTE_MAX 127</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"><br /></span></span>
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">#define MIX_MIDDLE 127</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"><br /></span></span>
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">#define GRAIN1_FREQUENCY_INPUT A0</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">#define GRAIN1_AMPLITUDE_INPUT A1</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"><br /></span></span>
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">#define GRAIN2_FREQUENCY_INPUT A2</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">#define GRAIN2_AMPLITUDE_INPUT A3</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"><br /></span></span>
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">#define LFO_FREQUENCY_INPUT A4</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">#define LFO_AMPLITUDE_INPUT A5</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"><br /></span></span>
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">#define GRAIN1_SHAPE_INPUT 4</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">#define GRAIN2_SHAPE_INPUT 7</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">#define LFO_SHAPE_INPUT 8</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"><br /></span></span>
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">#define PWM_OUT OCR0A</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"><br /></span></span>
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">// Will put this in progmem - eventually</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">/*PROGMEM*/ </span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">unsigned char sineWave[256] =</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">{</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">127 , //0</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">130 , //1</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">133 , //2</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">136 , //3</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">139 , //4</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">142 , //5</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">145 , //6</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">148 , //7</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">151 , //8</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">154 , //9</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">157 , //10</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">160 , //11</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">163 , //12</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">166 , //13</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">169 , //14</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">172 , //15</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">175 , //16</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">178 , //17</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">181 , //18</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">184 , //19</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">186 , //20</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">189 , //21</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">192 , //22</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">194 , //23</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">197 , //24</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">200 , //25</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">202 , //26</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">205 , //27</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">207 , //28</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">209 , //29</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">212 , //30</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">214 , //31</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">216 , //32</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">218 , //33</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">221 , //34</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">223 , //35</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">225 , //36</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">227 , //37</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">229 , //38</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">230 , //39</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">232 , //40</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">234 , //41</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">235 , //42</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">237 , //43</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">239 , //44</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">240 , //45</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">241 , //46</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">243 , //47</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">244 , //48</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">245 , //49</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">246 , //50</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">247 , //51</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">248 , //52</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">249 , //53</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">250 , //54</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">250 , //55</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">251 , //56</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">252 , //57</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">252 , //58</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">253 , //59</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">253 , //60</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">253 , //61</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">253 , //62</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">253 , //63</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">254 , //64</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">253 , //65</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">253 , //66</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">253 , //67</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">253 , //68</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">253 , //69</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">252 , //70</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">252 , //71</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">251 , //72</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">250 , //73</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">250 , //74</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">249 , //75</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">248 , //76</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">247 , //77</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">246 , //78</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">245 , //79</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">244 , //80</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">243 , //81</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">241 , //82</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">240 , //83</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">239 , //84</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">237 , //85</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">235 , //86</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">234 , //87</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">232 , //88</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">230 , //89</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">229 , //90</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">227 , //91</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">225 , //92</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">223 , //93</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">221 , //94</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">218 , //95</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">216 , //96</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">214 , //97</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">212 , //98</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">209 , //99</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">207 , //100</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">205 , //101</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">202 , //102</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">200 , //103</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">197 , //104</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">194 , //105</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">192 , //106</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">189 , //107</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">186 , //108</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">184 , //109</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">181 , //110</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">178 , //111</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">175 , //112</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">172 , //113</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">169 , //114</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">166 , //115</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">163 , //116</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">160 , //117</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">157 , //118</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">154 , //119</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">151 , //120</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">148 , //121</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">145 , //122</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">142 , //123</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">139 , //124</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">136 , //125</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">133 , //126</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">130 , //127</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">127 , //128</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">124 , //129</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">121 , //130</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">118 , //131</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">115 , //132</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">112 , //133</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">109 , //134</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">106 , //135</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">103 , //136</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">100 , //137</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">97 , //138</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">94 , //139</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">91 , //140</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">88 , //141</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">85 , //142</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">82 , //143</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">79 , //144</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">76 , //145</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">73 , //146</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">70 , //147</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">68 , //148</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">65 , //149</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">62 , //150</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">60 , //151</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">57 , //152</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">54 , //153</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">52 , //154</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">49 , //155</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">47 , //156</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">45 , //157</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">42 , //158</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">40 , //159</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">38 , //160</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">36 , //161</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">33 , //162</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">31 , //163</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">29 , //164</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">27 , //165</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">25 , //166</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">24 , //167</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">22 , //168</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">20 , //169</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">19 , //170</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">17 , //171</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">15 , //172</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">14 , //173</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">13 , //174</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">11 , //175</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">10 , //176</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">9 , //177</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">8 , //178</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">7 , //179</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">6 , //180</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">5 , //181</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">4 , //182</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">4 , //183</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">3 , //184</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">2 , //185</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">2 , //186</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">1 , //187</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">1 , //188</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">1 , //189</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">1 , //190</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">1 , //191</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">0 , //192</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">1 , //193</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">1 , //194</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">1 , //195</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">1 , //196</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">1 , //197</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">2 , //198</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">2 , //199</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">3 , //200</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">4 , //201</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">4 , //202</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">5 , //203</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">6 , //204</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">7 , //205</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">8 , //206</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">9 , //207</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">10 , //208</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">11 , //209</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">13 , //210</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">14 , //211</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">15 , //212</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">17 , //213</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">19 , //214</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">20 , //215</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">22 , //216</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">24 , //217</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">25 , //218</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">27 , //219</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">29 , //220</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">31 , //221</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">33 , //222</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">36 , //223</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">38 , //224</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">40 , //225</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">42 , //226</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">45 , //227</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">47 , //228</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">49 , //229</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">52 , //230</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">54 , //231</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">57 , //232</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">60 , //233</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">62 , //234</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">65 , //235</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">68 , //236</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">70 , //237</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">73 , //238</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">76 , //239</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">79 , //240</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">82 , //241</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">85 , //242</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">88 , //243</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">91 , //244</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">94 , //245</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">97 , //246</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">100 , //247</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">103 , //248</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">106 , //249</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">109 , //250</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">112 , //251</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">115 , //252</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">118 , //253</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">121 , //254</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">123 //255</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">};</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"><br /></span></span>
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">// should pre calculate these and store them in progmem to save memory</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">// for prototyping, lets calculate them and use ram</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">unsigned char rampWave[256];</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">unsigned char squareWave[256];</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"><br /></span></span>
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">// used to convert midi note numbers into the increments required to generate the note in the ISR</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">PROGMEM unsigned int midiNoteToWavePhaseIncrement[128] =</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">{</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> 66 // 0,8.18,66.98,66</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,70 // 1,8.66,70.96,70</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,75 // 2,9.18,75.18,75</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,79 // 3,9.72,79.65,79</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,84 // 4,10.30,84.38,84</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,89 // 5,10.91,89.40,89</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,94 // 6,11.56,94.72,94</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,100 // 7,12.25,100.35,100</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,106 // 8,12.98,106.32,106</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,112 // 9,13.75,112.64,112</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,119 // 10,14.57,119.34,119</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,126 // 11,15.43,126.43,126</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,133 // 12,16.35,133.95,133</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,141 // 13,17.32,141.92,141</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,150 // 14,18.35,150.35,150</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,159 // 15,19.45,159.29,159</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,168 // 16,20.60,168.77,168</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,178 // 17,21.83,178.80,178</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,189 // 18,23.12,189.43,189</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,200 // 19,24.50,200.70,200</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,212 // 20,25.96,212.63,212</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,225 // 21,27.50,225.28,225</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,238 // 22,29.14,238.67,238</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,252 // 23,30.87,252.86,252</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,267 // 24,32.70,267.90,267</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,283 // 25,34.65,283.83,283</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,300 // 26,36.71,300.71,300</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,318 // 27,38.89,318.59,318</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,337 // 28,41.20,337.53,337</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,357 // 29,43.65,357.60,357</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,378 // 30,46.25,378.87,378</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,401 // 31,49.00,401.40,401</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,425 // 32,51.91,425.27,425</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,450 // 33,55.00,450.55,450</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,477 // 34,58.27,477.34,477</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,505 // 35,61.74,505.73,505</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,535 // 36,65.41,535.80,535</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,567 // 37,69.30,567.66,567</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,601 // 38,73.42,601.42,601</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,637 // 39,77.78,637.18,637</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,675 // 40,82.41,675.07,675</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,715 // 41,87.31,715.21,715</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,757 // 42,92.50,757.74,757</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,802 // 43,98.00,802.79,802</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,850 // 44,103.83,850.53,850</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,901 // 45,110.00,901.11,901</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,954 // 46,116.54,954.69,954</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,1011 // 47,123.47,1011.46,1011</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,1071 // 48,130.81,1071.60,1071</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,1135 // 49,138.59,1135.32,1135</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,1202 // 50,146.83,1202.83,1202</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,1274 // 51,155.56,1274.36,1274</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,1350 // 52,164.81,1350.13,1350</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,1430 // 53,174.61,1430.42,1430</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,1515 // 54,185.00,1515.47,1515</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,1605 // 55,196.00,1605.59,1605</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,1701 // 56,207.65,1701.06,1701</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,1802 // 57,220.00,1802.21,1802</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,1909 // 58,233.08,1909.38,1909</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,2022 // 59,246.94,2022.92,2022</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,2143 // 60,261.63,2143.20,2143</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,2270 // 61,277.18,2270.64,2270</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,2405 // 62,293.66,2405.66,2405</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,2548 // 63,311.13,2548.71,2548</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,2700 // 64,329.63,2700.27,2700</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,2860 // 65,349.23,2860.83,2860</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,3030 // 66,369.99,3030.95,3030</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,3211 // 67,392.00,3211.18,3211</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,3402 // 68,415.30,3402.12,3402</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,3604 // 69,440.00,3604.42,3604</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,3818 // 70,466.16,3818.75,3818</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,4045 // 71,493.88,4045.83,4045</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,4286 // 72,523.25,4286.41,4286</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,4541 // 73,554.37,4541.29,4541</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,4811 // 74,587.33,4811.33,4811</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,5097 // 75,622.25,5097.42,5097</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,5400 // 76,659.26,5400.53,5400</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,5721 // 77,698.46,5721.67,5721</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,6061 // 78,739.99,6061.89,6061</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,6422 // 79,783.99,6422.36,6422</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,6804 // 80,830.61,6804.25,6804</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,7208 // 81,880.00,7208.85,7208</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,7637 // 82,932.33,7637.51,7637</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,8091 // 83,987.77,8091.66,8091</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,8572 // 84,1046.50,8572.82,8572</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,9082 // 85,1108.73,9082.58,9082</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,9622 // 86,1174.66,9622.66,9622</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,10194 // 87,1244.51,10194.85,10194</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,10801 // 88,1318.51,10801.07,10801</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,11443 // 89,1396.91,11443.33,11443</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,12123 // 90,1479.98,12123.79,12123</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,12844 // 91,1567.98,12844.71,12844</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,13608 // 92,1661.22,13608.50,13608</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,14417 // 93,1760.00,14417.70,14417</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,15275 // 94,1864.65,15275.02,15275</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,16183 // 95,1975.53,16183.31,16183</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,17145 // 96,2093.00,17145.63,17145</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,18165 // 97,2217.46,18165.16,18165</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,19245 // 98,2349.32,19245.31,19245</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,20389 // 99,2489.01,20389.70,20389</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,21602 // 100,2637.02,21602.14,21602</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,22886 // 101,2793.83,22886.67,22886</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,24247 // 102,2959.95,24247.58,24247</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,25689 // 103,3135.96,25689.42,25689</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,27216 // 104,3322.44,27216.99,27216</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,28835 // 105,3520.00,28835.39,28835</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,30550 // 106,3729.31,30550.04,30550</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,32366 // 107,3951.06,32366.63,32366</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,34291 // 108,4186.01,34291.26,34291</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,36330 // 109,4434.92,36330.32,36330</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,38490 // 110,4698.64,38490.65,38490</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,40779 // 111,4978.03,40779.41,40779</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,43204 // 112,5274.04,43204.25,43204</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,45773 // 113,5587.65,45773.32,45773</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,48495 // 114,5919.91,48495.14,48495</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,51378 // 115,6271.92,51378.79,51378</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,54433 // 116,6644.87,54433.96,54433</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,57670 // 117,7040.00,57670.76,57670</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,61100 // 118,7458.62,61100.07,61100</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,64733 // 119,7902.13,64733.26,64733</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,3046 // 120,8372.02,68582.53,3046</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,7124 // 121,8869.84,72660.64,7124</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,11445 // 122,9397.27,76981.30,11445</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,16022 // 123,9956.06,81558.77,16022</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,20872 // 124,10548.07,86408.50,20872</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,26010 // 125,11175.30,91546.65,26010</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">,31454 // 126,11839.81,96990.28,31454</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">};</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"><br /></span></span>
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">class CSynth</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">{</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">public:</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> volatile static unsigned char* m_pWaveForm1;</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> volatile static unsigned char* m_pWaveForm2;</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> volatile static unsigned char* m_pLFOShape1;</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> volatile static unsigned char* m_pLFOShape2;</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> volatile static unsigned int m_nWaveForm1Accumulator;</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> volatile static unsigned int m_nWaveForm1Increment;</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> volatile static unsigned int m_nWaveForm2Accumulator;</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> volatile static unsigned int m_nWaveForm2Increment;</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> volatile static unsigned int m_nLFOAccumulator1;</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> volatile static unsigned int m_nLFOIncrement1;</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> volatile static unsigned int m_nLFOAccumulator2;</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> volatile static unsigned int m_nLFOIncrement2;</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> volatile static unsigned char m_sLFOType;</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> volatile static unsigned char m_sMix;</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> static void startAudio()</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> {</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> TCCR1A=0x0; // set the timer prescaler to 8 = 16/8 = 2MHz</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> TCCR1B=0x02; // set the timer prescaler to 8 = 16/8 = 2MHz</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> TIMSK1 |= (1<<OCIE1A); // Enable output compare match interrupt on OCR1A</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> </span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> TCCR0A=0B10110011; //-8 bit audio PWM</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> //TCCR0A=0x83; // Set timer waveform generation mode to FAST PWM, clear OC0A On match, set at bottom - OC0A = digital pin 6.</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> TCCR0B=0x01; // Set to clock frequency, no prescaler</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> OCR0A=127; // set in the middle - do we need this ? probably not.</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> DDRD|=1<<6; // Set digital pin 6 to output - channels 2 and 3</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> }</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> static void triggerMidiNote(unsigned char sNote)</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> {</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> if(sNote < MIDI_NOTE_MAX)</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> {</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> m_nWaveForm1Increment = (pgm_read_word(midiNoteToWavePhaseIncrement + (sNote)));</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> }</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> }</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">};</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"><br /></span></span>
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">volatile unsigned char* CSynth::m_pWaveForm1;</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">volatile unsigned char* CSynth::m_pWaveForm2;</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">volatile unsigned char* CSynth::m_pLFOShape1;</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">volatile unsigned char* CSynth::m_pLFOShape2;</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">volatile unsigned int CSynth::m_nWaveForm1Accumulator;</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">volatile unsigned int CSynth::m_nWaveForm1Increment;</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">volatile unsigned int CSynth::m_nWaveForm2Accumulator;</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">volatile unsigned int CSynth::m_nWaveForm2Increment;</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">volatile unsigned int CSynth::m_nLFOAccumulator1;</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">volatile unsigned int CSynth::m_nLFOIncrement1;</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">volatile unsigned int CSynth::m_nLFOAccumulator2;</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">volatile unsigned int CSynth::m_nLFOIncrement2;</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">volatile unsigned char CSynth::m_sLFOType;</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">volatile unsigned char CSynth::m_sMix;</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> </span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">CSynth synth;</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"><br /></span></span>
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">// iterate the grains and LFO </span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">SIGNAL (TIMER1_COMPA_vect) </span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">{</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> OCR1A += (TIMER1_FREQUENCY/UPDATE_RATE);</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"><br /></span></span> <span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> // cross fade between the two channels</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> unsigned char sAmplitude1 = 0;</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> unsigned char sAmplitude2 = 0;</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> </span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> if(synth.m_sMix < 127)</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> {</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> sAmplitude1 = synth.m_sMix << 1;</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> sAmplitude2 = 255;</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> }</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> else if(synth.m_sMix > 127)</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> {</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> sAmplitude2 = (255-synth.m_sMix) << 1;</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> sAmplitude1 = 255;</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> }</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> </span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> OCR0B = OCR0A = </span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> (((((</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> (*(synth.m_pWaveForm1 + ((synth.m_nWaveForm1Accumulator+=synth.m_nWaveForm1Increment)>>8))) *</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> (*(synth.m_pLFOShape1 + ((synth.m_nLFOAccumulator1+=synth.m_nLFOIncrement1) >> 8)))) >> 8) *</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> sAmplitude1) >> 8) +</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> ((((</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> (*(synth.m_pWaveForm2 + ((synth.m_nWaveForm2Accumulator+=synth.m_nWaveForm2Increment)>>8))) *</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> (*(synth.m_pLFOShape2 + ((synth.m_nLFOAccumulator2+=synth.m_nLFOIncrement2) >> 8)))) >> 8) *</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> sAmplitude2) >> 8)) >> 1; </span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"><br /></span></span>
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">}</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"><br /></span></span>
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">void setup()</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">{</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> pinMode(GRAIN1_SHAPE_INPUT,INPUT);</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> pinMode(GRAIN2_SHAPE_INPUT,INPUT);</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> pinMode(LFO_SHAPE_INPUT,INPUT);</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"><br /></span></span>
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> digitalWrite(GRAIN1_SHAPE_INPUT,HIGH);</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> digitalWrite(GRAIN2_SHAPE_INPUT,HIGH);</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> digitalWrite(LFO_SHAPE_INPUT,HIGH);</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"><br /></span></span>
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> Serial.begin(9600);</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"><br /></span></span>
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">for(int nIndex = 0;nIndex < 256;nIndex ++)</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> {</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> rampWave[nIndex] = 255 - nIndex;</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> squareWave[nIndex] = (nIndex < 127) ? 0 : 255;</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> }</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"><br /></span></span>
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> synth.m_pWaveForm1 = sineWave;</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> synth.m_pWaveForm2 = sineWave;</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> synth.m_pLFOShape1 = rampWave;</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> synth.m_pLFOShape2 = squareWave;</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> synth.m_sMix = MIX_MIDDLE;</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> synth.startAudio();</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">}</span></span><span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"><br /></span></span>
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">void loop()</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">{</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> synth.triggerMidiNote(analogRead(1)>>3);</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> synth.m_nWaveForm2Increment = analogRead(2)<<5;</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"><br /></span></span>
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> // the LFO Oscilators control the volume of the two waveforms, a low value</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> synth.m_nLFOIncrement1 = analogRead(3) >> 2;</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> synth.m_nLFOIncrement2 = analogRead(4) >> 2;</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"><br /></span></span>
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> // this mixes the two channels a low value will shut off one channel, a high value will shut off the other</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> // anywhere in between will generate a proportional mix of the two channels</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> synth.m_sMix = analogRead(5) >> 2;</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">}</span></span><br />
<br /></div>
</div>
Can_I_Trade?http://www.blogger.com/profile/07867684474123805993noreply@blogger.com7tag:blogger.com,1999:blog-7214097671310478681.post-67183475042836830452012-10-04T03:57:00.000-07:002012-10-04T04:00:14.979-07:00Arduino Due More Info and Teensy 3.0 on the way<div dir="ltr" style="text-align: left;" trbidi="on">
A few more bits of information on the Arduino Due -<br />
<br />
Youtube video of the launch presentation -<br />
<br />
<a href="http://www.youtube.com/watch?v=4TZQt9lTAOU&feature=player_embedded" target="_blank">http://www.youtube.com/watch?v=4TZQt9lTAOU&feature=player_embedded</a><br />
<br />
Post on hackaday with a link to the PDF that was handed out on the day -<br />
<br />
<a href="http://hackaday.com/2012/10/03/finally-an-arm-powered-arduino/" target="_blank">http://hackaday.com/2012/10/03/finally-an-arm-powered-arduino/</a><br />
<br />
Now that the details are out, discussion has turned to compatibility. Some key points are that the DUE operatates at 3.3 volts and offers less current per pin and so while its form is compatible with Arduino Mega sheilds, it may not be directly usable with them or with many of the Arduino example projects that have been published the current generation Arduino's.<br />
<br />
Also many approaches to connecting external components will need to be revisited, for example it is common to connect an Arduino directly to a 5 volt RC Reveiver or a Servo, this will not be advisable with the 3.3 volt Arduino Due.<br />
<br />
For now, the Arduino UNO will remain the recommended starting point for beginners, there are an enormous number of projects that can be completed using the entry level UNO, I have yet to build anything more than the UNO can handle.<br />
<br />
<br />
Having said all that, the Arduino Due allows us all to dream much bigger and right now I am thinking about a really big Arduino Due based modular synthesiser.<br />
<br />
<b>An Arduino Due Alternative ? The Teensy 3.0</b><br />
<br />
Slightly ahead of the Arduino Due launch the Arduino compatible Teensy 3.0 project is now shipping, this offers an intermediate point between the UNO and Due in terms of price and power. I look forward to finding out how capable and compatible the Teensy is when mine arrives.<br />
<br />
The Teensy 3.0, is particularly attractive for its small size and low cost, it is also ARMbased like the Due.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://s3.amazonaws.com/ksr/projects/294049/photo-full.jpg?1346192366" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="http://s3.amazonaws.com/ksr/projects/294049/photo-full.jpg?1346192366" width="320" /></a></div>
<br />
<br />
<a href="http://www.pjrc.com/store/teensy3.html" target="_blank">http://www.pjrc.com/store/teensy3.html</a><br />
<br />
The original Teensy 3.0 kick starter page -<br />
<br />
<a href="http://www.kickstarter.com/projects/paulstoffregen/teensy-30-32-bit-arm-cortex-m4-usable-in-arduino-a" target="_blank">http://www.kickstarter.com/projects/paulstoffregen/teensy-30-32-bit-arm-cortex-m4-usable-in-arduino-a</a><br />
<br />
Duane B</div>
Can_I_Trade?http://www.blogger.com/profile/07867684474123805993noreply@blogger.com4tag:blogger.com,1999:blog-7214097671310478681.post-76154503208142688182012-09-30T09:52:00.000-07:002012-10-28T07:32:51.576-07:00Arduino Due<div dir="ltr" style="text-align: left;" trbidi="on">
Where to buy, download the software and find the specs -<br />
<br />
Update 7: The RC Arduino projects will be compiled and tested on a wider range of boards including Mega (now that I have one), Teensy and Due when I receive it. Expect to see a new 'Tested On' Header appearing on posts shortly and a list of tested projects here.<br />
<br />
Update 6: If you have not seen it yet, here is the picture of the Due as posted on the arduino.cc homepage. That chip is an impressive size. There is also a dedicated Due section of the Arduino forum with the latest updates on hardware, software, shield compatibility and libraries.<br />
<br />
Official Arduino Due Forum - <br />
<a href="http://arduino.cc/forum/index.php/board,87.0.html" target="_blank">http://arduino.cc/forum/index.php/board,87.0.html</a><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://arduino.cc/en/uploads/Main/arduino_due_in_hand.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="http://arduino.cc/en/uploads/Main/arduino_due_in_hand.jpg" width="320" /></a></div>
<br />
<br />
Update 5: If your interested in what your Due or Teensy is doing under the hood I can confirm that the approach to viewing the assembly code outlined here works for both of the new boards, I will update the screen shots at some point, but the process remains the same -<br />
<a href="http://rcarduino.blogspot.com/2012/09/how-to-view-arduino-assembly.html" target="_blank">http://rcarduino.blogspot.com/2012/09/how-to-view-arduino-assembly.html</a><br />
<br />
And here is my Teensy3.0 with headers attached -<br />
<br />
<table>
<tbody>
<tr>
<td>Additional pin pads from the reverse brought around to female headers glued to the main headers. I used Gorilla Glue to attached the outside set of headers, I did this before putting them anywhere near the Teensy. The glue has some very aggressive solvents which I did not want to risk damaging the board with.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiy0fMbythNwEoWeEerD5qeT3tVDzVDcJ2c_Y-FM8cmtmUl8gVxaUnCbWUZqP-OSBbFp-g4Jlt0c7M1t8fCX_q0clCyeoAYCJD2U2UEdadlkpc_nciGq982ZPvVHqW6uKdO64PdY0j4MhvA/s1600/teensyheaders.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="265" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiy0fMbythNwEoWeEerD5qeT3tVDzVDcJ2c_Y-FM8cmtmUl8gVxaUnCbWUZqP-OSBbFp-g4Jlt0c7M1t8fCX_q0clCyeoAYCJD2U2UEdadlkpc_nciGq982ZPvVHqW6uKdO64PdY0j4MhvA/s320/teensyheaders.png" width="320" /></a></div>
<br /></td><td>Underside view showing the pads and single strand hook up wire used to hook them up to the headers.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiJgi6CdPN1EMKELlzXXb8BwA-IPe_x4ohxcE4yp5ZGIrfwL-vBKSk30fMlOksDlJn4pUnH9Z8a5mtZELODkFCJa_H8_apM7-WbRIw5IUze8yKAyJC8xjHvJj-PrkT0KR43-ZIciIzXy8fj/s1600/teensyheadersreverse.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="211" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiJgi6CdPN1EMKELlzXXb8BwA-IPe_x4ohxcE4yp5ZGIrfwL-vBKSk30fMlOksDlJn4pUnH9Z8a5mtZELODkFCJa_H8_apM7-WbRIw5IUze8yKAyJC8xjHvJj-PrkT0KR43-ZIciIzXy8fj/s320/teensyheadersreverse.png" width="320" /></a></div>
</td>
</tr>
</tbody></table>
<br />
<br />
<br />
Update 4: I am happy to be one of the lucky ones to have ordered a Due, the Arduino Store is now showing the Due as out of stock however a number of online retailers are now showing that they have or are expecting stock so there are still units out there if your looking.<br />
<br />
Update 3: Official hardware information, and much more here -<br />
<br />
<a href="http://arduino.cc/en/Main/ArduinoBoardDue" target="_blank">http://arduino.cc/en/Main/ArduinoBoardDue</a><br />
Pin Mapping <br />
<a href="http://arduino.cc/en/Hacking/PinMappingSAM3X" target="_blank">http://arduino.cc/en/Hacking/PinMappingSAM3X</a><br />
Due Only Libraries<br />
<a href="http://arduino.cc/en/Reference/Audio" target="_blank">http://arduino.cc/en/Reference/Audio</a><br />
<a href="http://arduino.cc/en/Reference/Scheduler" target="_blank">http://arduino.cc/en/Reference/Scheduler</a><br />
<a href="http://arduino.cc/en/Reference/USBHost" target="_blank">http://arduino.cc/en/Reference/USBHost</a><br />
<br />
<br />
The Audio library in particular should be interesting as a reference for using DMA (Direct Memory Access) for high speed access to external storage. <br />
<br />
Update 2: Software available for download here -<br />
<a href="http://arduino.cc/en/Main/SoftwareDue" target="_blank">http://arduino.cc/en/Main/SoftwareDue</a><br />
<br />
Update1: Arduino Due is out and available for purchase.<br />
Disclaimer: The following links advertise the Arduino Due for sale they are provided for information purposes only, this is not an endorsement of any product or outlet nor is it in anyway a guarantee that these stores have devices available for shipment.<br />
<br />
<a href="http://store.arduino.cc/ww/index.php?main_page=product_info&cPath=11_12&products_id=243" target="_blank">http://store.arduino.cc/ww/index.php?main_page=product_info&cPath=11_12&products_id=243</a><br />
<a href="http://www.homotix.it/index.cfm?Page=Catalogo&IdCatProdotto=139&IdSchedaProdotto=2245" target="_blank">http://www.homotix.it/index.cfm?Page=Catalogo&IdCatProdotto=139&IdSchedaProdotto=2245</a><br />
<br />
It looks as if detailed information on the Arduino Due is being held back until the boards are available for retail on October 22nd. I continue to monitor online retailers for pre orders but have not seen any yet.<br />
<br />
The following links contain a selection of the information released so far - <br />
<br />
<div style="background-color: #f0f0f0;">
<b>Quick Comparison with current Arduinos and Teensy 3.0</b><br />
<br />
<br />
<table border="0" cellpadding="0" cellspacing="0" style="width: 485px;"><colgroup><col style="width: 48pt;" width="64"></col>
<col style="mso-width-alt: 4022; mso-width-source: userset; width: 83pt;" width="110"></col>
<col style="mso-width-alt: 3474; mso-width-source: userset; width: 71pt;" width="95"></col>
<col style="mso-width-alt: 5558; mso-width-source: userset; width: 114pt;" width="152"></col>
<col style="width: 48pt;" width="64"></col>
</colgroup><tbody>
<tr height="20" style="height: 15.0pt;">
<td height="20" style="height: 15.0pt; width: 48pt;" width="64"><br /></td>
<td class="xl63" style="text-align: center; width: 83pt;" width="110"><b>Arduino UNO</b></td>
<td class="xl63" style="text-align: center; width: 71pt;" width="95"><b>Arduino Mega</b></td>
<td class="xl63" style="text-align: center; width: 114pt;" width="152"><b>Arduino Due</b></td>
<td class="xl63" style="text-align: center; width: 48pt;" width="64"><b>Teensy 3.0</b></td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td class="xl63" height="20" style="height: 15.0pt;"><b>MCU</b></td>
<td>ATMega 328</td>
<td>ATMega 2560</td>
<td>AT91SAM3X8E</td>
<td>MK20DX128VLH5</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td class="xl63" height="20" style="height: 15.0pt;"><b>Architecture</b></td>
<td>8 bit AVR</td>
<td>8 bit AVR</td>
<td>32 bit ARM Cortex M3</td>
<td class="xl63">32 bit ARM Cortex M4 * </td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td class="xl63" height="20" style="height: 15.0pt;"><b>CPU Speed</b></td>
<td style="text-align: right;">16Mhz</td>
<td style="text-align: right;">16Mhz</td>
<td class="xl63" style="text-align: right;">84 Mhz</td>
<td style="text-align: right;">48 Mhz*</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td class="xl63" height="20" style="height: 15.0pt;"><b>Flash Memory</b></td>
<td style="text-align: right;">32K</td>
<td style="text-align: right;">256K</td>
<td class="xl63" style="text-align: right;">512K</td>
<td style="text-align: right;">128K</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td class="xl63" height="20" style="height: 15.0pt;"><b>RAM</b></td>
<td style="text-align: right;">2K</td>
<td style="text-align: right;">8K</td>
<td class="xl63" style="text-align: right;">96K</td>
<td style="text-align: right;">16K</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td class="xl63" height="20" style="height: 15.0pt;"><b>EEPROM</b></td>
<td style="text-align: right;">1K</td>
<td style="text-align: right;">4K</td>
<td class="xl63" style="text-align: right;">250K</td>
<td style="text-align: right;">2K</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td class="xl63" height="20" style="height: 15.0pt;"><b>Digital Pins</b></td>
<td align="right">14</td>
<td align="right" class="xl63">54</td>
<td align="right" class="xl63">54</td>
<td align="right">34</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td class="xl63" height="20" style="height: 15.0pt;"><b>PWM Pins</b></td>
<td align="right">6</td>
<td align="right">15</td>
<td align="right" class="xl63">16</td>
<td align="right">10</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td class="xl63" height="20" style="height: 15.0pt;"><b>Analog</b> <b>Inputs</b></td>
<td align="right">6</td>
<td align="right" class="xl63">16</td>
<td align="right">12</td>
<td align="right">14</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td class="xl63" height="20" style="height: 15.0pt;"><b>DAC</b></td>
<td style="text-align: right;">0</td>
<td style="text-align: right;">0</td>
<td class="xl63" style="text-align: right;">2</td>
<td style="text-align: right;"> 0</td>
</tr>
</tbody></table>
<br /></div>
<br />
* Teensy 3.0 supports overclocking to 94Mhz<br />
* Teensy 3.0 is based on ARM M4 architecture which is based on the same M3 Architecture as the Due, but with added digital signal processing instructions.<br />
<br />
<br />
None of the projects on the RCArduino blog use the full power of an Arduino UNO, in fact the lap timer project is the largest and still only uses 10K of the 32K available on an UNO. If your a beginner, get an UNO. All of the online examples will work with an UNO, it can also be directly connected to a wide range of external devices with no additional circuitry. This does not apply to the Due.<br />
<br />
The Due is a 3.3 volt device where as the current Arduino's are 5volts, this is not much of a problem, but a bigger issue is that the Due is very limited in the amount of current it can accept (sink) or generate (drive/source). A large number of projects that you can find online today will need additional circuitry to be run on an Arduino Due. If you are a beginner don't give yourself the additional headache of having to converting online examples to run on the Due, but don't take my word for it, here is a quote from one of the originators of the Arduino project - <br />
<br />
<br />
<blockquote class="tr_bq">
<i><b>starting from zero on the Due is not recommended.</b></i><br />
<i><b>Get the starter kit and learn on the Uno then when you're an expert consider the Due.</b></i><br />
<i><b><br /></b></i>
<i><b>m </b></i></blockquote>
Quote taken from - <a href="http://arduino.cc/forum/index.php/topic,125444.msg960996.html#msg960996" target="_blank">http://arduino.cc/forum/index.php/topic,125444.msg960996.html#msg960996</a><br />
<br />
It has also been confirmed by Massimo that the Arduino Due will not be compatible with the vast number of extension shields that have been developed for the older 5V Arduino Mega - this could be a great opportunity to pick up old stock shields at a lower price, they can always be converted to run on Due once you are familiar with the platform.<br />
<br />
None of this is a negative, the Due is a very advanced high performance processor which has been added to the Arduino line up. However like anything high performance it needs to be treated a little more carefully. The Due will open up huge new range of applications that are simply not possible with the current generation Arduinos and one of its greatest strengths will be that you can learn the basics on a tough little Arduino UNO and then transfer exactly the same skill set to the super performance Arduino Due.<br />
<br />
<br />
While details of the Arduino Due are being held back until the launch, the datasheet for the Atmel SAM3X8E microcontroller at the heart of the new Due is widely available.<br />
<br />
One of the best references I have found is available in the link below however be aware that a major goal or the Arduino project is to sheild you from this level of complexity so for the vast majority of Arduino users the following document is irrelevant.<br />
<br />
Rest assured that we will all still be using pinMode, digitalRead, digitalWrite and all of the major libraries on the new Arduino Due platform, however for anyone wanting to write performance orientated libraries for the new Arduino Due the following will be useful reading, for everyone else, please skip this link.<br />
<br />
<a href="http://www.atmel.com/Images/doc11057.pdf" target="_blank">http://www.atmel.com/Images/doc11057.pdf</a><br />
<br />
<br />
Some more accessible information here, I also have a Teensy 3.0 on its way to Dubai which gets a mention in this post -<br />
<br />
<a href="http://rcarduino.blogspot.com/2012/10/arduino-due-more-info-and-teensy-30-on.html" target="_blank">http://rcarduino.blogspot.com/2012/10/arduino-due-more-info-and-teensy-30-on.html</a><br />
<a href="http://rcarduino.blogspot.com/2012/10/new-arduino-products-and-video-tutorials.html" target="_blank">http://rcarduino.blogspot.com/2012/10/new-arduino-products-and-video-tutorials.html</a><br />
<a href="http://arduino.cc/forum/index.php/topic,88029.msg940049.html#new" target="_blank">http://arduino.cc/forum/index.php/topic,88029.msg940049.html#new</a><br />
<br />
Massimo Banzi of the Arduino team has been answering some questions relating to the Due on this Arduino forum thread -<br />
<br />
<a href="http://arduino.cc/forum/index.php/topic,113460.60.html" target="_blank">http://arduino.cc/forum/index.php/topic,113460.60.html</a><br />
<br />
<br />
Update: with only four days until the Arduino Due is launched, my Teensy 3.0 has arrived in Dubai.<br />
Like the Due, the Teensy is based on an ARM Processor core the Teensy actually uses a Cortex M4 core which has advantages for digital signal processing applications over the Cortex M3 core offered by the Arduino Due.<br />
<br />
<div style="text-align: center;">
Teensy 3.0 ARM Cortex M4 Based Arduino Compatible.</div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjIIWGQpKQTirC5C09nsquQD1ytcr6MESJROhb-bIjfgm1qY3D4VJy-c0l3M_vQd8GDzVUd-PRGaCekm5R6aoR3ojIWlOxf0k0g4I_FIyH0z_B2MsvPDgmlIWZLvXckOydzEeoBYuQvazS7/s1600/tDubai.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="201" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjIIWGQpKQTirC5C09nsquQD1ytcr6MESJROhb-bIjfgm1qY3D4VJy-c0l3M_vQd8GDzVUd-PRGaCekm5R6aoR3ojIWlOxf0k0g4I_FIyH0z_B2MsvPDgmlIWZLvXckOydzEeoBYuQvazS7/s320/tDubai.png" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
<br />
<br />
Teensy - It really is very very small, here it is hiding in the top right of an Arduino UNO, its about the same size as the bare ATMega328 Chip on an Arduino UNO.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgCpJ1W4T0r7zFHKPDe1hfmKsyOdDgWWfTaCfH0gyFDikzTRQDIKenDZrsSICd8XJo9hcokiTNw611rOhXNEJdpk1K71ysqAeHM1lyc7T5jOT0HEyiaW-JhzRKIJN3wdT8mbcs-zB1Qw1dA/s1600/uno_teensy.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="204" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgCpJ1W4T0r7zFHKPDe1hfmKsyOdDgWWfTaCfH0gyFDikzTRQDIKenDZrsSICd8XJo9hcokiTNw611rOhXNEJdpk1K71ysqAeHM1lyc7T5jOT0HEyiaW-JhzRKIJN3wdT8mbcs-zB1Qw1dA/s320/uno_teensy.png" width="320" /></a></div>
<br />
<br />
One thing about these new boards that will have immediate impact is the very low input and output currents compared to the current generation of boards. There are not many projects I can think of where a Teensy will be a slot in replacement for an Uno or a Mega without some additional circuitry to switch the 20ma current typically of an Arduino pin. <br />
<br />
Over the coming days I will look at converting some of the existing RC Arduino projects to use the Teensy, it will be interesting to see how easily the software and especially the hardware conversion is.<br />
<br />
Stay tuned <br />
<br />
Duane B <br />
<br /></div>
Can_I_Trade?http://www.blogger.com/profile/07867684474123805993noreply@blogger.com2