Let's Make Robots!

Building a new controller - fun and games

Friday 27th January, 2012
This blog is a an ongoing list of my thoughts, as I attempts to create a library for the Micro Magician. It is boring and should not be read by anyone :p

 

As some of you may already know, I am designing a new Arduino compatible controller for small robots. One problem with small robots is limited space. This makes sheilds, motor controllers and wires difficult to fit. For this reason I tried to incorporate a few useful devices onto a controller while keeping the cost low.

Hardware:
The end result is a controller 30mm x 60mm with built in dual 1A FET H bridge, 3-axis accelerometer and IR receiver. Up to 7 servos can be plugged directly into the PCB and powered directly from the battery.

The H bridge was a no brainer, most people want to add at least 2 DC motors to their robot for locomotion. The A3906 chip I've used has several advantages. It uses FETs instead of transistors so that more of your battery power goes to the motors. It can work on voltages from 2.5V - 9V. It has electronic braking and it has built in current limiting. I've set this to 900mA so that even if a motor stalls for an extended period of time no damage will be done to the controller.

I chose to include a 3-axis accelerometer because it can eliminate the need for bump switches. The magnitude and direction of an impact or collision can be easily determined and used to guide the robot away from any obstructions. The accelerometer can also warn the robot if it is going to fall over and be used as part of a self righting system.

I included the IR receiver because it provides a cheap and easy method of remotely controlling a robot using a common TV remote. This can also be used to locate a navigation beacon / docking station.

As small robots often run on small batteries this controller can run on voltages as low as 3.6V with a maximum input of 9V. The ability to run on 3.6V has required that the processor run at 3.3V so unfortunately the clock speed had to be reduced to 8MHz.

5V devices can be used with minimal interfacing. 5V digital I/O sensors should have a 4K7 resistor in series to limit the current in the MCU's clamping diodes. 5V analog sensors need a 2 resistor voltage divider (eg. 5K & 7.5K) to bring a 5V signal down to 3V.

 

Software:
Now, with the help of my fellow LMR members I am trying to write a library to suit the controller. This is where it gets difficult for me as programming is not my area of expertise. My first problem was that I have not written a library before and the tutorials I found tend to assume you are an experienced C language programmer. As I have programmed in basic most of my life and do not know a class from a data struct I needed help.

With the help of cr0c0, Maus and mogul (alphabetical order so as not to offend) and support from Grog, Rogue and RobotFreak I began creating my library.

Technical difficulties:
There are a number of technical difficulties to overcome to make this controller work well. Firstly in the initial hardware design, pins had to be chosen carefully to allow maximum functionality. As PWM on pins D9 and D10 are disabled by the Servo library they could not be used for the H bridge. I left D2 free as it can be used for an external interrupt but was forced to use D3 as part of the H bridge control in order to use electronic braking.

Now I am running into timer restrictions.
Timer0 is used for time functions in the Arduino IDE such as delay(), micros() and millis() so that was strictly off-limits.
Timer1 is used by the Servo library. As servos are commonly used in small robots I wanted to leave Timer1 alone.
Timer2 is the only timer left.

I want to use Timer 2 for impact detection which requires regular monitoring of the accelerometer but it is also used for the tone() command and Ken Sherriff's IR receiver library.

As I would like my little robots to detect impacts, make noise and receive IR commands all at once I am now trying to write a timer interrupt based library fuction that does all 3 from timer 2 - Impossible? Out comes the digital oscilloscope.

Impact function goes on a diet:
My biggest problem is that reading a single analog input takes about 260uS and thus my Impact detection routine that must read 3 inputs takes almost a full mS.I have modified the code so that it alternates between reading the X, Y and Z axises. Because only one axis is read each time the function is called this reduces the function time to about 350uS.

As the robot vibrates upon impact which can lead to false triggering after an initial impact I have written the code so it will stop taking readings for about 500mS after an impact. This also helps a bit.

After doing some test I found that I can set the timer interrupt to call a function  every 400uS and that the impact readings can be reduced to 1 in 5 (effectively taking a reading every 2mS) and still give reliable readings. I may be able to improve this later by changing the hardware filtering of the accelerometer outputs.

Minimum requirements for IR receiver decoding:
After studying the signals produced from a few different remotes I think I can decode them using the 400uS timer interrupt. The digital 0's had a width from about 600uS (Sony) to 450uS(Phillips) The protocols I looked at all had a large start bit 2-4mS in width. By checking the IR receiver pin for a low state every 400uS this can be easily detected and if necessary the impact detection can be temporarily disabled to allow a signal to be decoded.

Music?
With an interrupt frequency of only 400uS (2.5KHz) the robot can make beeps and general robot noise but music would not be possible. The simplest solution is to just use the tone() command (during which time impact detection and IR commands are temporarily disabled) and then re-configure Timer2 afterward.

 

After some thought, a flexible timer configuration:
Since my original attempt at writting this library I have now changed the timer mode. Originally the timer was configured for CTC mode. I have now changed it to overflow mode. This requires the ISR to reload the timer with a value and therefore my software can change the time interval between interrupts.

The reason I want a flexible time interval is that when no IR signal is being detected and only impact detection is running then the ISR only needs to be called about once every mS. This is enough to monitor the IR receiver when no signal is being decoded. The impact detection function can be configured to only take a reading every second function call.

When an IR signal is detected then the function can reduce the time interval between interrupts and delay ipact detection until after the IR signal has been decoded.

Timer troubles:
Before I went any further I decided to test what affects my timer interrupt had on servos. The answer: not good :(
I cannot read a single analog input without my servo doing a deranged dance on the table. Suddenly the interrupt flag from my earliest version of the software is looking much better.

Problem 1 soved:
Flags are the answer! For those new to programming, a flag is a bit, or in my case a byte that is set or reset when an event occurs. The reason flags have solved my problem is that during a interrupt service routine, other functions are suspended. If your ISR is very long as mine was it then interfers noticeably with other timed events.

Now my ISR which is normally triggered once every millisecond simply increments a byte which is used to determine how often the impact detection occurs and raises a flag (sets another byte to 1) if the IR receiver output is low.

ISR(TIMER2_OVF_vect)                                 // timer compare interrupt service routine
{
  TCNT2 = t2;                                        // reload timer 
  tiflag++;                                          // increment tiflag
  if(digitalRead(4)==0) irflag=1;                    // set IR flag if signal detected
}

This takes very few clock cycles to complete. Even if does happen just before the servo timer interrupt was about to occur it would only delay the pulse by a few microseconds.

With the digital oscilloscope monitoring a servo output on D8 and my timer 2 interrupt which is pulsing D7 I am now getting smooth jitter free results while at the same time my impact detection function is reporting results via the serial monitor. Currently my impact detection routine is now called when my timer interrupt flag (tiflag) =2 after which it resets the flag back to 0. This causes the impact detection function to be called once every 2mS.

A bit more experimentation showed that I did not need a flexible timer system. I can now set my timer intervals to 100uS and simply change the value at which my tiflag (which simply counts the timer interrupts) calls my impact function.

With my timer interrupts at 100uS, decoding IR signals is much easier and music is possible. Admittedly anyone who tunes or plays musical instruments for a living might disagree, but the note frequencies should be close enough at lower octaves.

IR decoding:
I have started with my IR decoding function. I thought I would try writing code that would measure pulse widths which is how the Sony IRC works. So far it is about 50% there. Lots of bugs to sort out but it is now 10:30pm  and I need a break.

 


 

Saturday 28th of January 2012

After going through my IR decoder code which uses a similar method to some of my previous robots I decided to scrap it and rewrite it. With the digital oscilloscope doing a lot of debugging for me I finally worked out my problems.

I decided to stick with the Sony protocol (same as picaxe uses) because it is based on pulse widths which are easy to measure using the timer interrupt. The entire IR decode function ended up as part of my timer ISR.

I checked the timing functions of a servo which was sweeping using a millis() command to control the speed (testing timer0 and timer1) and everything is running smoothly!

This is how my ISR looks now:

ISR(TIMER2_OVF_vect)                                         // timer overflow service routine
{                                                    
  TCNT2 = t2;                                                // reload timer 
  tiflag++;                                                  // increment tiflag used to call Impact function



  //----------------------------------------------------------- Sony IRC Decoder ------------------------------------------------------
  
  volatile static byte irbitvalue,irbitcount,irdata,newbit;
  if(digitalRead(irpin)==0)                                  // check state of IR receiver output 
  {
    irflag++;                                                // increment irflag used to measure ir pulse widths
    newbit=1;                                                // recognizes a new bit being measured
  }
  else                                                       // IR receiver output high - end of bit
  {
    if(irflag==1)
    {
      if(irflag>4)                                           // bitwidth>4 = logic "1"
      {
        irdata+=irbitvalue;                                  // increment data by bit value
      }
      irbitvalue*=2;                                         // bitvalue updated for next bit (1,2,4,8,16,32,64,128)
      irbitcount++;                                          // count the bits

      if(irbitcount>6)                                       // only want first 7 bits
      {
        irbitvalue=0;                                        // additional bits have a value of 0
        ircommand=irdata;                                    // make data available to user
      }

      if(irflag>8)                                           // start bit resets decoder values
      {
        irbitvalue=1;
        irbitcount=0;
        irdata=0;
      }

      newbit=0;                                              // addional interrupts not new bit
    }
    irflag=0;                                                // reset irflag when IR pulse ends  
  }
}

Note: I have changed the timer settings to interrupt every 200uS (5KHz).

 

Round 2:

Currently I have focussed on functions that are dependant on a timer interrupt as these are the most difficult to implement.

For now I will forget about music. I'm fairly certain it is just easier to use the existing tone() library and then call microM.Setup() afterwards to re-initialize timer 2. In that case the IR and Impact functions would be briefly disabled while the tone() library is in use.

Testing showed that while the Tone() library did disable the IR and Impact commands the timer was easily re-initialized after a tune was played and then all functions were back to normal. I don't think this will be an issue in most cases.

Later I will add some simple functions for the "H" bridge and that should be about it.

I have made a large number of changes to my original program. The 2 biggest changes are:

  1. Changing the structure to use flags in the ISR. This prevents the Impact() function from affecting other timer based events.
  2. Addition of a SIRC decoder to read Sony IR controllers.

The attached folder "Impact_IR.zip" contains the working code that needs to be made into a library.
The attached folder "microM.zip" contains my attempt to convert the file into a library.

Judging from the errors I got when it tried to compile I think that the local variables for the ISR have not been implemented correctly in the microM.h file. As the ISR was not mentioned in the original sample I'm not sure how to fix it.

The original library was named Micro.M but the code used micro.M. I decided that the micro.M was easier on the eyes and changed all instances of Micro.M to micro.M. Perhaps this has also caused a problem?

I will attempt to work out where I went wrong but in the meantime I am hoping someone with more experience can have a look at where I went wrong.

Once it is all working again I need to find out is there a more user friendly way of utilizing the library? Currently the inputs and outputs are all global variables. Originally I was trying to avoid global variables but I'm not sure there is a better way that would be as flexible.

 


 

Sunday 29th January 2012

With the help of RobotFreak I got the new libray going. It wasn't perfect as we had to use global variables to get the ISR working but all functions were working. I then went to write a simple function for the "H" bridge.

AWW CRAP! - I forgot that timer 2 affects the PWM on pins 3 & 11 which are used for the left motor control. I think I am going to have to change my ISR again!

I suppose I should explain here that because the A3906 has electronic braking I wanted to make good use of it. Rather than just having the brakes slammed on hard I have connected it so that PWM can control the braking as well as the speed. This requires 2 PWM pins for each motor.

Pins 9 and 10 are not used because PWM on those pins are disabled by the servo library. Pins 5 and 6 are driven by timer 0 and are used to control the right motor which works perfectly.

 

The Verdict
As so many libraries use timer 2, I've decided to modify the PCB design so that only PWM pins 5&6 (Timer0) are used for motor control. This is not a big deal as I want to make some small modifications to the PCB anyway.

Electronic braking will still be available but it will be either full brake or no brake. As a bonus, D3 will now be made available as an external interrupt pin. 

The fact is, it is almost impossible to get a product perfect first go. It usually takes about 3 revisions to perfect. At least when the Micro Magician does hit the shops you'll know that it has been put through it's paces and designed for maximum flexibility.

By the time the new PCB is ready for testing I will have the library finished and possibly some improved features if we can squeeze them in!

 

I have attached my latest version of the library. It all works fine except for the left motor. Can some of the experts look at it and tell me how I can eliminate the need for global variables in my ISR.

 

 

 

AttachmentSize
microM.zip4.84 KB

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.

You can read the analog ports by using ADC interrupt and process the result after reading A2.

Also, you can make the timer2 to call a function that calls other stuff at your choice of time interval: start analog conversions (if you do not want continuous mode), process analog readings, ...

You can still use timer2 if you want to use pins 3 and 11 as regular digital IO (no PWM). However you still need two independent PWM for motors so maybe use timer2 for motors and use timer1 for timing function - or reverse :)

For IR you can use interrupt on change for the pin IR receiver is connected to and a counter incremented by the timing function.

Currently I am not worried about the analog inputs anymore. I've resolved the problem partly in code and partly in hardware.

I am already using timer 2 as you suggest, for eaxample even though the interrupt triggers every 200uS for the IR receiver decoding it only calls the Impact detection routine every 3mS.

I did originally want 4 PWM pins for the H bridge as this allows you to vary the intensity of the electronic brake. I have now redesigned the PCB to only use 2 PWM pins. This only allows full electronic braking but has the advantage of freeing up D3 to be used as an external interrupt.

My library is written, it just needs tidying up and a function added for controlling a stepper motor. Thank you very much for your initial work on the library. I really needed that extra help.

Wow, takes 3 versions to be perfect, I thought it happens only to me... I always find an error on the first version, then want to add more stuff in the second version and mean while I get some better idea and do version 3. Seems this is pretty standard...

Glad that you freed pin 3 for encoders, although with all the interrupts you got there I think it will be hard to cram them in too, perhaps you can try to add this function to your library? Just use single channel per encoder and determine incrementation/decrementation from your motors direction.

Yeah, you finaly got to the same conclusion I got some time ago about the timer pins, only 5 and 6 are useful for motor control. At least it is good that we can use them and not lose the millis and delays. 

Since you're redesigning, can you add a charger for Li-Ion/Po batteries? even if you need to make the board a bit longer, having this feature will be very important. I have integrated a MAX1555 in my uBipediono board and here are my findings. I am using a 10440 (AAA) size Li-Ion battery, 600mAh rating, 3.6V. It is protected internally for both overcharge at 4.2V and overdischarge at 2.75V so it is safe for kids to play with. It cost me $5 for a pair from DealExtreme. I am using 4 HXT900 servos, move them 2 at a time and I can run the robot for about 2 hours on a full charge to a full discharge, then it takes 4 hours to recharge from the USB port. I added a xBee that was sending to the computer the battery voltage once per main loop and the robot run for one hour. I am designing a cheap rolling robot that would work perfectly with your board just to test how long it will run on such a battery.

A friend of mine who is in charge of a factory that makes powertools told me that even though they have been making power tools for many years it still takes about 3 prototypes to get all the bugs worked out.

To be honest I think encoders are too bulky for small robots. I've been playing with optical mouse sensors. They are cheaper and easier to use. They only require 2 digital pins for the equivalent of 2 quadrature encoders. The down side is your robot ends up with very little ground clearance. Perfect for desk bots.

I don't want to build a battery charger into the board as it then limits the type of batteries you can use. I was trying to increase the range of power sources this could work from, not limit them.

because this board has the female headers for power as well as I/O pins it would be easy to make a sheild for it that had a battery charger on it.

I may be new to electronics and robotics, but I've been programming for a long time in a variety of environments and languages.  I will gladly use that knowledge to help you or any other LMR user.

If you could post a zip of the current library code, I'd love to see the progress so far.

Maus

I have my IR decoder working now so I attempted to create a new library. Unfortunately the changes were substantial and I cannot get the new library to compile. All the errors seemed to be related to the local variables in the ISR. I suspect my biggest problem is that I have not specified them correctly in microM.h but I'm not sure how to fix it.

Any help would be appreciated.

Me and the family have caught colds.  Once the initial shock of illness has died down, then I'll start looking at the forums/blogs again to help.

Sorry,

Maus

Sorry to hear that, my bad (sickest) day was Thursday. Friday I felt better, but still could not get outside, so package shipment got delayed. Now I'm almost good and I guess tomorrow I'll go to work.

So, hope you'll feel better soon, all of you!

I Know how it is. I can't concentrate when I get a head cold. Take care. Any advice or corrections will be welcome.

I have done some modifications. Now the library will compile without errors. But don't ask me why the variables must be public.

Here is the download link:

microM.zip