We are interested in reading the number of microseconds that a digital pin is set to HIGH. Wiring natively lacks a micros() function to tell us this so we need to write an accurate one.

This is the actual implementation of millis() in wiring.c for the Arduino.

unsigned long millis()
{
// timer 0 increments every 64 cycles, and overflows when it reaches
// 256. we would calculate the total number of clock cycles, then
// divide by the number of clock cycles per millisecond, but this
// overflows too often.
//return timer0_overflow_count * 64UL * 256UL / (F_CPU / 1000UL);

// instead find 1/128th the number of clock cycles and divide by
// 1/128th the number of clock cycles per millisecond
return timer0_overflow_count * 64UL * 2UL / (F_CPU / 128000UL);
}

Timer0 is configured to use the /64 prescaler. This means it increments every 64 CPU cycles. Notice that they use the actual CPU frequency (F_CPU) to do the calculation.

Note that the /64 prescaler will never give us a system that is more accurate than 512 steps. Most current radios support 1024 steps and some new 2.4Ghz and advanced FM systems have 2048 steps. Still 512 steps may be enough for our needs.

The Paparazzi project, back when they were using an AVR chip, set Timer3 to have a prescaler of /8 so they could gain higher accuracy. This adds load to the chip though because it is more frequently handling an interrupt request for the counter overflow.

Views: 3326

Reply to This

Replies to This Discussion

So just what is going on here?:
return timer0_overflow_count * 64UL * 2UL / (F_CPU / 128000UL);

Here is what I do understand:

The timer overflows once every 256 ticks. It ticks once ever 64 clock cycles. The CPU runs at 20 MHz and that value is stored in F_CPU. One overflow period takes 64 * 256 = 16384 clock cycles.

for what its worth, at 20Mhz, one overflow period is ~0.8192 milliseconds. We say approximately because the CPU frequency isn't exactly 20MHz. Thats why its important to use the value in F_CPU.

How many clock cycles does the overflow counter represent? Overflow count * clock ticks per overflow * prescaler value:
timer0_overflow_count * 256UL * 64UL

How many clock cycles make up a millisecond? CPU Frequency / milliseconds per second:
(F_CPU / 1000)

How do you get the number of milliseconds? clock ticks / clock ticks per millisecond
(timer0_overflow_count * 256UL * 64UL) / (F_CPU / 1000)

Thats yields the original implementation thats commented out in the source. It should yield accurate time. I think the current one is up to some tricks. Anyone want to try and explain?
Ok, this is turning into a series. I hope this isn't the wrong place to be posting this.

Now I'm going to do an improves version of Jordi's function to count milliseconds. I'm pretty sure he got it from this post on the Arduino Forum.
unsigned long hpticks (void) {  return (timer0_overflow_count  8) + TCNT0;}
Taking what we now know about the implementation of millis() we can improve this to get rid of the error. TCNT0 contains the number of ticks in Timer 0. Its some number between 0 and 255.

The ' 8' part is a bit shift left of 8. Bit shifting to the left by n bits is the same as multiplying by 2^n. bit shifting, by the way, is an optimization technique, its faster than multiplying. So the overflow counter is multiplying by 256. The poster claims, quite incorrectly, that the timer ticks 256 timer each millisecond. This would be true if the chip was clocked at 16Mhz but on the current Arduino boards its no longer true, its clocked at 20Mhz.

Here is a more accurate version:
unsigned long micros(void) {  return (((timer0_overflow_count * 256UL) + TCNT0) * 64UL ) / (F_CPU / 1000000UL) ;
}

So we multiply the overflow counter by ticks per overflow to get back total ticks. Then we add the remaining ticks in from counter 0. Then we multiply that by 64 to get CPU cycles. Then we divide by cpu cycles per microsecond.

This won't run for long before it overflows an unsigned long. You have about 3 minutes. Thats really bad, don't fly this code! There are two points here. First, this is still not that accurate as its constrained by the /64 prescaler on Timer0. Second, it wont run for a long time because of an overflow.

But we don't really care about absolute microseconds since the system started, we just want the number microseconds between two very closely spaced events, something that will never overflow. We want more accuracy too. that will lead us to using timer 3 and do even more creative math.
I'm still waiting on my Wiring board to show up from Sparkfun. They sent the wrong board so I'm still waiting.

The approach I'm going to use for reading PWM is based on interrupts. There are 8 pins on the Wiring board we can use for this. Unfortunately for us 2 are used by the Serial port and 2 by the Two wire interface. If you want to use either of these interfaces you will have to sacrifice 2 input channels. Still 4 channels in seems like quite enough for a minimum UAV.

Interrupts fire on the rise and fall of the PWM signal pulse. Separate interrupt code runs for each channel/pin. Capturing PWM with this method probably costs you 1% of the available CPU power. It will scale up for more channels without noticeable performance loss.

I'm going to use Timer 3 with the /8 prescaler to get sub usec times. It's a 16 bit timer so it wont overflow nearly as fast as the 8 bit timers. We'll be able to compare two reading on this timer to get the usec delta/pulse width.

The same timer can also trigger an interrupt when its counter matches some value you designate. This will run the output of PWM data to up to 10 pins. I don't see why on earth we will need more channels than 10 for a minimum UAV.

Basically the timer will be set up to divide each 50hz period into 10 frames, like a PWM pulse train. The interrupt handler will turn on a pin, set up the compare interrupt to fire when the pin needs to be turned off. Then it will be triggered by the interrupt, turn the pin off and figure out how long till the beginning of the next frame. Then it will set the compare register for that time. The cycle will go on endlessly.

So for 2% CPU you can have 4 channels in and 10 out and still have lots of room left over to do other stuff.

The library I'm writing will need to be configured for your hardware. Its going to support calibration data for each servo (extents and neutral point). A 'pass-through' flag for each servo. 2 and 3 position switch mode detection.

Individual pass through could be a big plus for testing. You could use a 3 position switch to designate a mode where altitude control is passed through and one where it isn't. So if the test isn't holding altitude you can disable that part of the autopilot output.
You might consider reaching in to your receiver for the raw transmit line - as its only one input, and contains all channels. Nicely enough, the signal bursts are serialized. Most of your timing code would stay the same.

I think you can find blogs with pictures and more info, but the overview is that receivers use a counter chip as the output, with each pin being set, in turn, for as long as the next radio signal burst. The counter chip can be inspected and referenced, then you will determine the CLK input and the RST input. Most likely the Reset is a longer than usual pulse on the main signal.

To demultiplex the channels, you only need to detect the Reset, and then time each pulse thereafter - assigning each pulse to a next channel in code.

Let us know if you write the Sketch.
(fair notice: I haven't done this myself with RC, I have done it with other systems - and I have read others doing this online.)

Ben
I'm just now getting to this function in my own code, and I'm confused by why you're not just using the PulseIn command. It does exactly what you want (read PWM), doesn't it?

It works in Wiring, too.
It waits for the pin to go high and then records the time it is on for. Its the waiting that kills performance. You could easily chew up more than 50% of your CPU cycles just waiting.

Waiting is bad but its used often in examples because it works and its easy to understand. Good high performance code doesn't do any waiting. It uses the CPU interrupts to run code when an event happens. A good timer is needed so you can compute time between interrupt events.

I have been way too busy with my other project to work on any code for this. Maybe later on this fall I'll get some time.
In Arduino 011 they added a timeout function, so if you judiciously use millis() you can avoid the performance hit.
You might want to check on that 20Mhz claim. As far as I know, Arduino is built with, ships with, and runs on a 16Mhz oscillator. The Diecimilia code is compiled assuming you're using a 16Mhz frequency. Millis() and as far as I can tell, Interrupts are based on this in the Arduino. If you've put a 20Mhz crystal or oscillator on your board, you would still have to take into consideration that the Arduino IDE is compiling your code on the assumption that there's you're using a 16Mhz crystal.
Not sure if this thread is OBE (Overcome By Events), but I agree with Gareth, this function should be called on an interrupt and not run periodically. For more info on how to do that with Arduino, look here.

http://www.arduino.cc/en/Reference/AttachInterrupt

for a lot more info look here,

http://www.uchobby.com/index.php/2007/11/24/arduino-interrupts/

As for timer (hardware based) PWM output on the Arduino, it looks like there are 3 sets of pins, see the link above

Reply to Discussion

RSS

© 2019   Created by Chris Anderson.   Powered by

Badges  |  Report an Issue  |  Terms of Service