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 took the IR decoding out of the ISR which killed it. It must be inside the ISR to function. I will play with it some more.

UPDATE: I have taken your library and put the IR decode back in the ISR. I did extensive testing previously with the oscilloscope and found it was necessary. As the IR decoding routine is relatively small it has no noticable affect on the other timing functions.

I now have everything working but as you noticed, the variables in the ISR needed to be public to work. I will attach the new version tonight after I add the DC motor function and then hopefully one of the other members will help solve the issue.

Note: I did look at Ken Sherriff's IR decoder routine and it seems he put the variables in a structure. As I'm not confident yet on how to do that I am leaving it as is for now.

Ok, my fault. Didn't test the code before uploading it.

What I learn now. The ISR is a C-function, so you can't access private C++ variables, only public C++ variables or normal C-variables like static int will work.

The problem with your motor is timer2 interfere with PWM on pin 3,11. You can't change OCR in the ISR because this will change the PWM frequency. I see no solution for this problem. Better use 1PWM and 1 direction pin for each motor, not 2PWM pins for one motor.

I have updated the schematic accordingly an will get the PCB changed. As I mentioned in the blog, I will loose variable braking but gain an external interrupt pin.
I'll test it this morning. There may be a better way. Hopefully some of the other members will have suggestions.

OB-one, I bow before your skills and determination! I am kind of in your shoes when it comes to library writing knowledge. But I lack the needed time to actually do something about it, as robotics for me is a hobby, I do it in my spare time, when I am not too tired to be any good. I wish I had a job like yours, I could actually do more interesting stuff. But this is not about me, it's about your efforts. I feel like robotics would not advance if there weren't guys like you. Thank you and God bless you!

I am sorry I could not add anything helpful to what you're doing. I had a gut feeling that the flag was the right answer form the beginning but I could not offer a reason so I didn't mention it. I kind of not question much the rules, if they say keep the ISR as short as possible, they know why for sure. Now you found that by actually measuring stuff.

I imagine you know that you can use the Timer0 for motors PWM, if you don't change the frequency of the timer, it doesn't interfere with the millis and delays, exactly how you found out you can use Timer2 to do all that stuff at a fixed frequency.

Now I have a suggestion about your hardware. Have you heard about the ATmega32u4 chip? Makers are switching for that chip since it has built in USB functions and it is compatible with the Arduino IDE as "Leonardo". Adafruit has a new breakout board for it, based on the Teensy design. This seems to be the future, and with all the features of your board it will make the perfect micro for small robots. Take a look at it, will you?

Cheers!

You would be surprised how little time I actually have. Aside from the fact that I spend a lot of time writing manuals, providing tech support etc. My boss keeps hitting me with his projects. Some can take months to complete. my job can be very frustrating some times.

I have done all this work on the Micro magician library during my holiday which will finish soon. My boss thinks support software is a waste of time. I am trying to prove to him that good product support is necessary for good sales.

I had a look at the ATmega32u4 but right now I cannot see much benifit. Ok, yes it has built in USB interface which saves a small amount of space but other than that I see no real benifit unless it was drastically cheaper.

The USB interface I am using now with it's external crystal is about 4x the size of the CP2102 but it was about half the price and the USB drivers seemed to load easier. At this point I would rather go back to the CP2102 if I wanted to save some space.

As far as the future of Arduino goes, this is hard to judge. There are a few boards that, running their own software claim to have a high degree of compatibility with Arduino, all sporting much faster, more powerful chips such as ARM processors.

Arduino themselves are now experimenting with an Arduino Mega compatible board using an ARM processor.

Then again maybe Microchip will finally pull their finger out and produce something as user friendly as the Arduino system (both the boards and the software must be easy and intuitive).

 

 

 

Wow, you need help. Too bad I can't relocate... Had I been single, I might have went there. Being the only one that is able to write good docs in English and do the design and testing and support is not making your life easy for sure. Heck, building robots stops to be fun, it becames work, as CtC said in a hangout. But still, I feel it beats working in a factory or renovating apartments like I do. I'm at a point where I'm asking myself if running a store, even as small as mine is worth it. I guess I would rather have my products manufatured and sold by others. That would make things easier for everybody, the stores would get more money, I would get a little to be fine and not struggle with finances and shipping, buyers would get fair prices and a place where they could buy all the parts they need for a robot. I was at a point where I wanted to do what CtC does but I just couldn't, since I was the only provider for my family. Now that my wife works, we have other palns that will delay the robotics enterprise a few good years. We'll see where the market will stand at that point in time.

I see what you mean with the 32u4. I guess you're right, if having a usb chip on board reduces costs, then there is no real need to actually use that micro. Another thing that comes to mind, can you add a li-io/po charger like Max1555 on board? That would make the board a complete system. Users could charge from USB, a solar panel or a charging station. Sure, for a many motors/servos robot a single cell would run out fast, but for most robots that only use a pair of dc motors and a pan servo that would be enough. I was also thinking about using a couple of micro servos with the electronics removed as driving motors, that would make the robot work on 3.6V easily.

I am amazed at the length of time to do an adc read. There must be a lot of overhead in the arduino libraries to cause that sort of delay. But yeah, should never attempt to do too much in an isr. It sounds like you have come a long way with it. Seems you have thought of a lot of issues that could arise with your libraries. I wish I had something constructive to add but I've played around with pics and assembler too much and had very little to do with arduino or avr.

If I can add one thing though to encourage you on this journey. The arduino would not be the success today that it is if they had made up the hardware and then just put it out there. It is as much the software that makes it what it is. So your efforts to write libraries for this controller can make the difference between a good piece of hardware or a good system. Hope that helps you when it's late and your bleary eyed looking at the monitor wondering why the bloody hell this cryptic language called c is so popular.

Yes I think I am finally seeing the light at the end of the tunnel. I am fortunate to have access to a good quality dual trace oscilloscope. It would have been impossible to debug the timer issues without it.

The fact is I do like C language and for the most part I find it almost as easy as basic except that the bugs can be harder to exterminate.

I admit for now that I only understand about every second thing involved with the library creation, same goes for the timer interrupts as the MCU datasheets are badly written. I am sure there are professional programmers and engineers out there who wish they could have 5 minutes alone with the guy who wrote them (10 minutes if they want him to suffer needlessly).

C++ How to Program. HM Dietel and PJ Deitel. I read it in a weekend when I had nothing else to do. Well not entirely but mostly. I found that the way it was written, it seemed to answer questions that popped into my head, while I was reading it, in a timely manner. Anyway I used it to help understand the arduino and write some simple stuff. But it covers all the topics raised in your forum post.

The oscilloscope was a good techie way around the programming problem.  I don't know how to say this without sounding like I'm crowing but the microchip mplab IDE has a simulator which is helpful for a lot of reasons if not the timer feature where you can check how long your bit of code takes to run, and setup breakpoints to stop and start. Of course this information is of no use to arduino users. But one other thing I find frustrating when writing a sketch for arduino is the lack of assistance in pinning down errors. In mplab they give you the line numbers to help you find any errors.

Oh and I checked how long a bit of code takes I wrote for a pic to read an adc channel and it took 46 microseconds to get reading. Looking at the datasheet for the 168 on page 251 it says a conversion time of 13-260uS. Seems a big variation but I imagine it's probably in the middle on average. So there must be some inefficiencey in the arduino environment to drag a reading out to 3 times that.