My Digital Tachometer - putting it all together

Go To Last Post
192 posts / 0 new

Pages

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

bobgardner wrote:
I like those little TI switchers that have 3 terminals like a 7805. Wide input range 9-32 I think.
Yes, I think I have some... they can handle up to 2 amps. :wink: I've had these for many years, they're made by PowerTrends (I understand TI bought them out).

Cris

Perfection is achieved, not when there is nothing more to add, but when there is nothing left to take away. Antoine de Saint-Exupery (1900 - 1944)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

aeroHAWK,

Yes, you are correct, in general if the driver chip doesn't feel hot there is likely no danger of self-destruction. Still, let me make ask another question...

I have had experience with cockpit instrument design. One of the big issues is "sunlight readability". I am not a pilot, so I don't have direct experience with this issue. But I have had to deal with instrument lighting specs that require "sunlight readability".

Back in the old days of mechanical instruments apparently sunlight readability wasn't much of an issue. About the only sunlight readability issue was glare off of the instrument's cover glass. With today's LCD and LED instruments things are different.

LCD displays have one set of issues and LEDs another. To achieve sunlight readability with LEDs you need to drive the LEDs super-hard. We had a special theatrical spotlight we used to simulate the level of direct sunlight coming into a cockpit. LED drive levels which would seem "very bright" in a well lit office, were completely washed out when subjected to the stage light's simulated sunshine.

We frequently found that we were pushing the upper current limits of the LEDs (often 7 segment or British Flag types) and that the consequent total power requirements of the instrument presented design challenges of their own. At that time there was only a limited selection of LED displays that were actually suitable for cockpit use. I'm sure the choice is much wider today. Plus, we have much more efficeint LEDs.

So, you may want to keep this in mind and do a little sunlight testing before you settle on a specific LED drive level and the various codependent design parameters & decisions.

If your current consumption is only 300 to 500 mA at 5 volts, the power dissipation of your tachometer will be around 2 to 2.5 watts. Overall, you shouldn't have any overheating issues with an instrument of this volume, even in an enclosed cockpit on a hot summer afternoon.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
LCD displays have one set of issues

And then along comes a pilot wearing a pair of polarized sun glasses...

JC

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Chuck-Rowst wrote:
I have had to deal with instrument lighting specs that require "sunlight readability".
Chuck-Rowst wrote:
Back in the old days of mechanical instruments apparently sunlight readability wasn't much of an issue. About the only sunlight readability issue was glare off of the instrument's cover glass.
I have thought about this. I don't expect sunlight readability, at least not direct sunlight. My thinking is this; when 'in the old days' as you say, with glare off of the instrument's cover glass, you would shade the instrument with your hand to read it. I expect the same here. Also, when the sun is shining like that, it is usually because the weather is good. In good weather, the need for instruments is lower - just fly by looking out the window. :wink: A tachometer is far less critical than other instruments so a little inconvenience in reading doesn't seem like a major deal to me (and there are other cues to enable the pilot to set RPM). The radio in my car has sunlight readability issues and occasionally I need to shade it with my hand, so it is something I have dealt with.
Chuck-Rowst wrote:
you may want to keep this in mind and do a little sunlight testing
Thanks Chuck, I will.
Chuck-Rowst wrote:
Overall, you shouldn't have any overheating issues with an instrument of this volume, even in an enclosed cockpit on a hot summer afternoon.
Part of avionics installation involves providing adequate cooling air behind the panel. Many times there is a vent that directs a blast of outside air to the avionics. Also, when the MAX7219 was running so hot, I had designed a heat sink for the rear cover of the tach. I could still use it if needed. :wink:
DocJC wrote:
And then along comes a pilot wearing a pair of polarized sun glasses...
Yeah, that's a whole different issue. Fortunately, I don't need to deal with it here. :wink:

Cris

Perfection is achieved, not when there is nothing more to add, but when there is nothing left to take away. Antoine de Saint-Exupery (1900 - 1944)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

When you are flying indoor the fluorescent lights can be a pain...

Sometimes a good old fashioned glare shield is a wise approach.

JC

Attachment(s): 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

DocJC wrote:
When you are flying indoor the fluorescent lights can be a pain...
LOL. Well at least they aren't as bright as the sun... 8)

Perfection is achieved, not when there is nothing more to add, but when there is nothing left to take away. Antoine de Saint-Exupery (1900 - 1944)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Many of you may remember this PCB I started with in my AVR education.

I have three of them. One of them has just the ATtiny26 soldered in. I've used it as a pulse source to simulate my tachometer input, along with other stuff. I tack soldered leads to it and to the bar graph display board. This way I can test the bar graph circuit with a test program before I assemble the bar graph and 7 segment PC boards together.

After spending a few hours getting past some bonehead errors (I wasted a lot of time before I discovered I loaded the WRONG program into the AVR :oops: and I left the enable pins floating so I added a pull-down resistor)....
:D :lol: IT WORKS! :lol: :D

The LEDs are nice and bright so they washed out the whole photo. :shock:

Perfection is achieved, not when there is nothing more to add, but when there is nothing left to take away. Antoine de Saint-Exupery (1900 - 1944)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Very nice!

JC

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks Jay. :wink: It is always a big relief when I get a new PC board working for the first time. 8) Since I don't consider myself an electronics type, I worry that my ignorance may bite me now and then. So far I've been pretty lucky. :P

Cris

Perfection is achieved, not when there is nothing more to add, but when there is nothing left to take away. Antoine de Saint-Exupery (1900 - 1944)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

What will the led and rpm refresh rate be? Will you be able to post a video of the led tower and the rpm digits at the same time?

Imagecraft compiler user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

bobgardner wrote:
What will the led and rpm refresh rate be?
The LED tower will refresh at 80 Hz. The digital display is subject to the MAX7219 and I don't recall it's frequency. :?
bobgardner wrote:
Will you be able to post a video of the led tower and the rpm digits at the same time?
Unfortunately I do not have a way to get video. :cry: I don't use a smart phone and I have no other video camera.

Cris

Perfection is achieved, not when there is nothing more to add, but when there is nothing left to take away. Antoine de Saint-Exupery (1900 - 1944)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

aeroHAWK wrote:
The digital display is subject to the MAX7219 and I don't recall it's frequency.
6400/N where N is the number of digits you're multiplexing. You're using 4, so that should be 1600 Hz.

JJ

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

That's fast enough to not flicker on a video. Ask a buddy with a smartphone to record a sweep up sweep down with a pot. Yo Doc: I assume that was simulator, because there seemed to be more lcds than round gauges. Any idea who made it? As for full sunlight readability, I remember something like 1200 nits (candelas per sq meter). Wasn't that informative? Real Bright would have been about as good.

Imagecraft compiler user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

aeroHAWK,

Looks very nice!

Are you at a point in your code where you can load any "value" into the bar graph? Or, can you only load the complete bar?

With this type of display it's usually a good idea to write a simple test routine that "pumps" the display. That is, it sends successive loads to the shift registers that makes the display grow from one segment per load up to its full size, then back again. It's a pretty cool way to show off your handiwork, but also serves the purpose of uncovering various faults and deficiencies in the display. E.g. Shorted segments or length-dependent brightness issues.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Chuck-Rowst wrote:
Looks very nice!
Thanks Chuck!
Chuck-Rowst wrote:
Are you at a point in your code where you can load any "value" into the bar graph? Or, can you only load the complete bar?
Only the complete bar.

From here, I will be doing the programming in steps:

    1. Get both bar graph and 7segment to light all segments 2. Program bar graph to display any value (500-4000 rpm)
    3. See if I can combine bar graph and 7 segment routines
    4. Use Timer0 to time RPM counting
    5. Configure ADC to 'read' dimming signal level
    6. Set up Timer1 for bar graph dimming PWM
    7. Figure out bar graph top lit segment PWM
Numbers 1 & 2 are self explanatory.

For number 3, the MAX7219 and PCA9922s use the same clock. The MAX7219 gets two data bytes in each packet but the PCA9922s get just one. At first there will be separate routines for each, but then I want to combine them so the PCA9922s get their data byte at the same time as the MAX7219 gets its first byte.

Item 4 is basically changing from Timer1 to Timer0 to free up Timer1 for the bar graph dimming PWM. In the ATtiny26, Timer0 has no compare feature and Timer1 is the only PWM. My previous program uses Timer1 for the RPM timing, so I need to make sure I can figure out using Timer0.

The 5th step is to read the value of an external voltage level that will control the overall dimming for various light conditions.

Number 6 should be self explanatory and 7 is to adjust the brightness of the top lit bar graph segment proportional to the fraction of 100 RPM left from the other lit segments.

Cris

Perfection is achieved, not when there is nothing more to add, but when there is nothing left to take away. Antoine de Saint-Exupery (1900 - 1944)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I have now completed all the electronics assembly. :D :o :lol: :P

This is what it looks like all together:


I need to cut a hole for the connector in the metal can that covers the back:

Then all that remains is SOFTWARE. :shock:

Cris

Perfection is achieved, not when there is nothing more to add, but when there is nothing left to take away. Antoine de Saint-Exupery (1900 - 1944)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Sorry, briefly off topic:

Quote:
Yo Doc: I assume that was simulator

Nope, this was an American Eurocopter EC130, in an air medical configuration. The real deal, not a sim. This photo is the same aircraft, (I think. Its my photo, but I have 100's of copter photos...).

The National Flight Physician's conference is in two weeks. Guess where I'll be! (Actually it will all be classroom continuing education, no flight time, but there will be a number of aircraft on display).

A very nice looking aircraft, but pretty small for the job, by my thinking.

JC

Attachment(s): 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Wow! Very nice.

Actually incredibly nice. As much as I like photos, (one pic's worth a thousand words...), I won't show you any of my cases. Complete with hand written taped on labels, drill skidding across the case skuff marks, missing display benzels, etc.

JC

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

DocJC wrote:
... hand written taped on labels, drill skidding across the case skuff marks, missing display benzels, etc.
Sounds like most of my projects... ;)

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

DocJC wrote:
Wow! Very nice.
Thanks Jay. :wink:
DocJC wrote:
hand written taped on labels, drill skidding across the case skuff marks, missing display benzels, etc.
Yeah, I have some like that too... :shock: (but you'll notice I'm not posting pictures of them either). :wink:

I have finished item number 1 of my list (above):

I have to say, this image doesn't do it justice. :wink: When I first turned everything on, it was impressive! I am VERY happy with the results.... :P

Perfection is achieved, not when there is nothing more to add, but when there is nothing left to take away. Antoine de Saint-Exupery (1900 - 1944)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Gorgeous !!!!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Chuck-Rowst wrote:
Gorgeous !!!!
Thanks Chuck. :)

I'm now wrestling with what it takes to control the bar graph. The parallel data lines are making my head hurt since it is something I have never done before.... :? It's a lot of stuff to keep track of. :shock: At least for me. :oops: I'm also dealing with making sure the MAX7219 and PCA9922s don't confuse each other (since they have a common clock). My programming inexperience is rearing its head... I found some bonehead errors in my bit-banging code. :shock: :oops:

I AM making progress though. :wink: It's really neat to be past the construction phase and on to the programming phase.

Cris

Perfection is achieved, not when there is nothing more to add, but when there is nothing left to take away. Antoine de Saint-Exupery (1900 - 1944)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Above, I wrote:
I will be doing the programming in steps:
    1. Get both bar graph and 7segment to light all segments 2. Program bar graph to display any value (500-4000 rpm)
    3. See if I can combine bar graph and 7 segment routines
    4. Use Timer0 to time RPM counting
    5. Configure ADC to 'read' dimming signal level
    6. Set up Timer1 for bar graph dimming PWM
    7. Figure out bar graph top lit segment PWM
I have now completed Items 1, 2, and 3. :P

With separate routines for the MAX7219 and PCA9922s, for some reason (that I haven't determined), the MAX7219 routine would interfere with the PCA9922s. Although they all have a common clock, the latch lines are on separate ports :shock: and they are not supposed to be active at the same time. :? This is the pinout (as posted above):

//==============================================================================
//                                                                             |
//                          ATtiny 26 Microcontroller                          |
//                                                                             |
//                                 +----v----+                                 |
//        MAX7219 Load   MOSI/PB0 -| 1    20 |- PA0/ADC0  Analog Dimmer Input  |
//    MAX7219 Data Out   MISO/PB1 -| 2    19 |- PA1/ADC1  PCA9922 Load         |
//        Serial Clock    SCK/PB2 -| 3    18 |- PA2/ADC2  PCA9922 Data Out A   |
//    BarGraph Dim Out   OC1B/PB3 -| 4    17 |- PA3/AREF  Capacitor to ground  |
//                            VCC -| 5    16 |- GND                            |
//                            GND -| 6    15 |- AVCC                           |
//      16 mHz crystal  XTAL1/PB4 -| 7    14 |- PA4/ADC3  PCA9922 Data Out B   |
//      16 mHz crystal  XTAL2/PB5 -| 8    13 |- PA5/ADC4  PCA9922 Data Out C   |
//  Crank Sensor Input   INT0/PB6 -| 9    12 |- PA6/ADC5  PCA9922 Data Out D   |
//               Reset  RESET/PB7 -| 10   11 |- PA7/ADC6  PCA9922 Data Out E   |
//                                 +---------+                                 |
//                                                                             |
//==============================================================================

Instead of spending the time to find the problem, since I planed to integrate them together, I went straight to the integration. 8)

It had its challenges though. The MAX7219 needs a two byte data packet and the PCA9922s only one, so I had to manage that. I got lucky and it worked the first attempt. :wink:

So I have the bar graph incrementing 100 rpm every half second, as the digital display shows the actual number.

It is humorous that I can be so entertained by flashing colored lights.... :shock: :wink:

Cris

Perfection is achieved, not when there is nothing more to add, but when there is nothing left to take away. Antoine de Saint-Exupery (1900 - 1944)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Above, I wrote:
I need to cut a hole for the connector in the metal can that covers the back
I've done that now. :D Here is what the completed gauge looks like all assembled:


So all I need to do is finish the software. 8)

Cris

Perfection is achieved, not when there is nothing more to add, but when there is nothing left to take away. Antoine de Saint-Exupery (1900 - 1944)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

That's just gorgeous!

JJ

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

joeymorin wrote:
That's just gorgeous!
Thanks JJ! :wink:
JohanEkdahl wrote:
Close by was a photo of the actual workshop. By Golly! DocJC could perform open heart or brain surgery in there. That has got to be the neatest place I've ever seen!
Thanks Johan, but to be fair, the shop hadn't been used yet. I had just relocated to that space, the photo was taken just after the machines were set in place. I am in the process of remodeling the building to suit my specific needs (to see more, I have a web page that shows what I'm doing here - click on 'Shop' on the left side of the page). I hope to have it organized well enough to keep it clean. :wink:
JohanEkdahl wrote:
Since this thread is closing in on the finishing line (or at least the instrument is), let me tell you how much I have enjoyed this. Something being built from scratch and ideas and sketches to the finished hardware and soon software. And the end result hardware-wise is just as fine as the initial sketches hinted at.
I know that I enjoy seeing interesting projects that people do. I hoped that this would be something others would be interested in. Thanks for letting me know that you are. :wink: Also, I want all you "Freaks" to know that I couldn't have done this without you all! :!: This is my way of letting you all see the result of your contributions, and to express my thanks.
JohanEkdahl wrote:
If I had a hat on it would be off. Thank you very much!
Thanks again Johan! I also appreciate all your assistance!

Cris

Perfection is achieved, not when there is nothing more to add, but when there is nothing left to take away. Antoine de Saint-Exupery (1900 - 1944)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I am very jealous Cris. That is a magnificent workshop!

Ross McKenzie ValuSoft Melbourne Australia

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

valusoft wrote:
I am very jealous Cris. That is a magnificent workshop!
Thanks Ross! Comments like this help me keep from taking it for granted. I feel very fortunate to have such a nice place to do my work. :wink:

Cris

Perfection is achieved, not when there is nothing more to add, but when there is nothing left to take away. Antoine de Saint-Exupery (1900 - 1944)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thus far, this thread has been mostly about the mechanical hardware and the way it all comes together. Since that is all finished, the software is what's left. Since I'm not a software guy, I am running into some obstacles that some of you may help me get past. :wink:

I have some questions:

The top lit segment of the bar graph is PWMed with 8 levels of brightness. The update frequency of the bar graph is 80 Hz, with 8 levels of PWM. So the PWM frequency is (8 x 80) 640 Hz. Timer0 on the ATtiny26 has some limitations so the interrupt frequency is 1280 Hz.

Is there an easy way to divide the frequency in half? If it were hardware, triggering a flip-flop will halve the frequency, so I am wondering if there is a software equivalent?

Another question I have is more complicated to explain. I am looking for assistance in structuring the PWM routine....

The PWM duty cycle is a variable from 0 to 7. With a loop from 0 to 7, what is a simple way to determine which steps of the loop will turn on the LED an which will not?

Also... the bar graph shift registers are all loaded along with the shift register in the MAX7219. However, the MAX7219 gets two bytes while the bar graph gets only one. Plus, the MAX7219 needs two bytes for each of four digits and two more for intensity. So the PWM loop needs to keep track of the MAX7219 data as well as the bar graph data.

So that's 8 bytes to send to the bar graph (for the PWM) and 10 bytes to send to the MAX7219. Any suggestions???

Thanks!

Cris

Perfection is achieved, not when there is nothing more to add, but when there is nothing left to take away. Antoine de Saint-Exupery (1900 - 1944)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

aeroHAWK wrote:
The top lit segment of the bar graph is PWMed with 8 levels of brightness. The update frequency of the bar graph is 80 Hz, with 8 levels of PWM. So the PWM frequency is (8 x 80) 640 Hz. Timer0 on the ATtiny26 has some limitations so the interrupt frequency is 1280 Hz.

Is there an easy way to divide the frequency in half? If it were hardware, triggering a flip-flop will halve the frequency, so I am wondering if there is a software equivalent?

Use a flag at the top of the ISR:
ISR(TIMER0_OVF0_vect) {
  static uint8_t division_flag = 0;
  division_flag ^= 1;
  if (division_flag) {
    return;
  }
  // your code here
}

Quote:
Another question I have is more complicated to explain. I am looking for assistance in structuring the PWM routine....

The PWM duty cycle is a variable from 0 to 7. With a loop from 0 to 7, what is a simple way to determine which steps of the loop will turn on the LED an which will not?

Your user code sets a PWM level for the LED. Your PWM routine maintains a clock, then compares the level against the clock:
volatile uint8_t pwm_level;

// called only from ISR
void pwm_routine() {
  static uint8_t pwm_clock = 0;
  if (pwm_level && (pwm_level <= pwm_clock)) {
    // your code to light the LED
  }
  else {
    // your code to extinguish the LED
  }
  pwm_clock = (pwm_clock + 1) % 8;
  // any other code you need to handle PWM
}

This will give you 7 levels of different brightnesses, plus 'off'. If you want to forego the off state:

  if (pwm_level <= pwm_clock) {

Quote:
Also...
.
.
.
Any suggestions???
What have you got so far?

JJ

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

joeymorin wrote:
Use a flag at the top of the ISR
Thanks JJ. :wink: I thought of a flag, but it's the XOR that I hadn't....

Another thought I just had is to just use the faster frequency and use a 16 level PWM. :shock: I will still have plenty of time to execute the code.

joeymorin wrote:
Your user code sets a PWM level for the LED. Your PWM routine maintains a clock, then compares the level against the clock
YEP! Again, it's along the lines I was thinking but I just couldn't connect all the dots. :? Your code example is VERY HELPFUL!
joeymorin wrote:
What have you got so far?
Well, since I'm now thinking of using 16 levels of PWM (rather than eight), my general idea is to make a loop to send the first and second MAX7219 bytes of the 4 digits with the first 8 bar graph bytes, then repeat the first and second bytes of the intensity for the remaining bytes.
send bar graph PWM byte 0 and MAX7219 digit1 first byte
send bar graph PWM byte 1 and MAX7219 digit1 second byte
send bar graph PWM byte 2 and MAX7219 digit2 first byte
send bar graph PWM byte 3 and MAX7219 digit2 second byte
send bar graph PWM byte 4 and MAX7219 digit3 first byte
send bar graph PWM byte 5 and MAX7219 digit3 second byte
send bar graph PWM byte 6 and MAX7219 digit4 first byte
send bar graph PWM byte 7 and MAX7219 digit4 second byte
send bar graph PWM byte 8 and MAX7219 intensity first byte
send bar graph PWM byte 9 and MAX7219 intensity second byte
send bar graph PWM byte a and MAX7219 intensity first byte
send bar graph PWM byte b and MAX7219 intensity second byte
send bar graph PWM byte c and MAX7219 intensity first byte
send bar graph PWM byte d and MAX7219 intensity second byte
send bar graph PWM byte e and MAX7219 intensity first byte
send bar graph PWM byte f and MAX7219 intensity second byte

Repeating the intensity bytes may be redundant, but it makes it so I don't need a different subroutine. :wink:

So changing to 16 levels of PWM relieves the need to halve the frequency as well as it provides a simpler way to feed the data to all the shift registers (including the MAX7219's). So I think that's what I'll do! 8)

Cris

Perfection is achieved, not when there is nothing more to add, but when there is nothing left to take away. Antoine de Saint-Exupery (1900 - 1944)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

aeroHAWK wrote:
So I think that's what I'll do! 8)
Seems like you've got it under control... ;)

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

joeymorin wrote:
Seems like you've got it under control... ;)
It is interesting how a problem (or problems) can seem almost insurmountable, but then a simple suggestion can alter perception in a way that make things so much simpler! That's what you've done for me JJ!

I had made the problem so big in my head. Then you point me to a simple solution. THANKS! :wink:

Cris

Perfection is achieved, not when there is nothing more to add, but when there is nothing left to take away. Antoine de Saint-Exupery (1900 - 1944)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I had to put this project on hold for a few weeks to get some other work done. :(

But now I'm back to it for a little while. :wink: I've made a small change in my approach....

Earlier I wrote:
I want to switch from timer1 to timer0 for the RPM timer.
In response mnehpets wrote:
You might be able to use timer1 for both PWM dimming and RPM counting
As a reminder, joeymorin wrote:
Just remember that if you're mixing two PWM signals to drive one display element, you must be mindful of 'beating'.
After which, Chuck-Rowst wrote:
The solution would be to somehow co-ordinate the 9522 loading with the PWM dimming signal.
I have since gotten much more comfortable with my understanding of the timers. I discovered that it is really easy to use Timer1 for everything I need. This also synchronizes the PWMs so I won't need to deal with any potential 'beat' frequency issues. :wink:

But this is bringing up some concerns. :? I have found (using the simulator) that each interrupt takes something like 33 clock cycles to simply increment a count. :shock: With both the timer interrupt and tachometer interrupt, there seems to be a lot of unnecessary 'wheel spinning'. It looks like it can have an adverse affect on the accuracy and consistency of the RPM count.

This is from the .lst file from the compiler. I don't understand assembly well enough to know what it is doing.

(0134) //**********************************************************************
(0135) //***************************** TIMER1 ISR *****************************
(0136) //**********************************************************************
(0137) #pragma interrupt_handler timer1_ovf_isr:iv_TIM1_OVF
(0138) void timer1_ovf_isr(void)
(0139) {
(0140)   tick++;
    00109 9180 0061 LDS	R24,tick
    0010B 5F8F      SUBI	R24,0xFF
    0010C 9380 0061 STS	tick,R24
(0141)   start = 1;                      //set start flag
    0010E E081      LDI	R24,1
    0010F 9380 0060 STS	start,R24
    00111 9189      LD	R24,Y+
    00112 BF8F      OUT	0x3F,R24
    00113 9199      LD	R25,Y+
    00114 9189      LD	R24,Y+
    00115 9518      RETI
_int0_isr:
    00116 938A      ST	-Y,R24
    00117 939A      ST	-Y,R25
    00118 B78F      IN	R24,0x3F
    00119 938A      ST	-Y,R24
(0142) }
(0143) 
(0144) //**********************************************************************
(0145) //****************************** INT0 ISR ******************************
(0146) //**********************************************************************
(0147) #pragma interrupt_handler int0_isr:iv_INT0
(0148) void int0_isr(void)
(0149) {
(0150)   tachCount++;
    0011A 9180 0062 LDS	R24,tachCount
    0011C 9190 0063 LDS	R25,tachCount+1
    0011E 9601      ADIW	R24,1
    0011F 9390 0063 STS	tachCount+1,R25
    00121 9380 0062 STS	tachCount,R24
    00123 9189      LD	R24,Y+
    00124 BF8F      OUT	0x3F,R24
    00125 9199      LD	R25,Y+
    00126 9189      LD	R24,Y+
    00127 9518      RETI

Is there a way to streamline what the ISRs are doing?

Cris

Perfection is achieved, not when there is nothing more to add, but when there is nothing left to take away. Antoine de Saint-Exupery (1900 - 1944)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

aeroHAWK wrote:

(0137) #pragma interrupt_handler timer1_ovf_isr:iv_TIM1_OVF

Is there a way to streamline what the ISRs are doing?

Looks like Imagecraft, with which I have no experience.

Looking at int0_isr(void), I count 21 cycles. The .lst file doesn't seem to show the prologue/epilogue, so I'd guess there is at least another 14 cycles there:

push r24
push r25
in r25, SREG
push r25
.
.
.
pop r25
out SREG, r25
pop r25
pop r24

... but I'm only guessing what Imagecraft does for prologue/epilogue.

Add to that the 6 cycles of interrupt response (not including the interrupted instruction in mainline code) and that's a total of 21 + 14 + 6 = 41 cycles.

If it were GCC I'd suggest writing the ISR in asm (or inline asm). You could easily do the equivalent of:

tachCount++;

in 21-25 cycles in an ISR, +6 for the interrupt response time for a total of 27-31 cycles. If you have between 2 and 4 free I/O registers you could cut that down to about 21/23 cycles.

I/O registers you might consider using (if you don't need them for other things):

    TCNT0 OSCCAL (Since you're running with a crystal. This will affect EEPROM writes. Are you using EEPROM?)
    OCR1A/B/C (Are you using all of these?)
    USIDR (are you still using USI?)
    ADMUX (are you using the ADC?)
If you declare tachCount as a register variable, plus an extra uint8_t variable to save SREG, that could get you down to 14 cycles. Reserving registers for variables will have an impact on the rest of your code, as the compiler won't be able to use them elsewhere. It must be done carefully to ensure a) it doesn't affect performance elsewhere, and b) doesn't break anthing.

Since this is Imagecraft I don't know how to advise you, nor even if any of these approaches are possible. Uncle Bob will be around soon ;)

JJ

[EDIT]
Ah, I was hasty. Looks like the epilogue is there... I was counting it as part of the code (silly!)... and I missed the prologue above the comments.

Curious that Imagecraft uses Y as a stack pointer. I suppose it saves some IN/MOVW/OUT instructions in some cases, but it would seem that it also permanently eats an index register pair in the bargain.

In any case, the total cycle count for your ISR is 28 + 6 = 34 cycles, much closer to what you have observed.
[/EDIT]

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I didn't cover it in my last post, but you could see similar savings in void timer1_ovf_isr(void). Current total is 32 cycles. I expect with assembler, and 1 more (single-byte) register variable, and an I/O register below 0x20 (only 1 bit is needed), you could get this down to 15 cycles.

To get both these ISRs down to these low cycle counts requires reserving 4 of the general purpose registers. As I mentioned, this might not be a win in the end, depending on the reset of your code. Even without register variables, though, you could improve upon the existing code.

JJ

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

joeymorin wrote:
If it were GCC I'd suggest writing the ISR in asm (or inline asm).
Thanks JJ. I'm sure ImageCraft allows this also. I am interested in this. However I am clueless to what the ISR NEEDS to do.
joeymorin wrote:
If you declare tachCount as a register variable,
Can you give an example of how to do this?
joeymorin wrote:
plus an extra uint8_t variable to save SREG, that could get you down to 14 cycles.
This is less than half what is currently happening, so it definately piques my interest. :shock:

Could somebody explain what the ISR has to do so I can get a 'big picture'? And why is the ISR currently doing what it is doing?

Perfection is achieved, not when there is nothing more to add, but when there is nothing left to take away. Antoine de Saint-Exupery (1900 - 1944)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You reserve registers 20,21,22,23 with a tic box in the ide, it links with different libraries that don't allocate those regs. The syntax is in the help file. Also, you might have a GPIOR0 or 1 you could use.

Imagecraft compiler user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Additional sneakyness can save you an additional 2 cycles by placing the ISR code directly in the interrupt vector table, but this will preclude the use of interrupt sources the vectors for which are supplanted. The INT0 ISR can be as little as 4 words, but that would preclude the use of:

    pin change interrupts TIMER1_CMPA
    TIMER1_CMPB
Similarly, your TIMER1_OVF1 ISR could be as short as 5 words, but that would preclude the use of:
    USI_STRT USI_OVF
    EE_RDY
    ANA_COMP
I'm guessing those aren't the only TIMER1 interrupts you've got? ... so you likely won't be able to use this technique, but it only buys you 2 cycles anyway.

JJ

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

joeymorin wrote:
To get both these ISRs down to these low cycle counts requires reserving 4 of the general purpose registers.
I understand this is a kind of 'cheating', and requires care. I would not be surprised if I have registers available. I am using ADC, USI, and timer1, but not timer0.
bobgardner wrote:
You reserve registers 20,21,22,23 with a tic box in the ide, it links with different libraries that don't allocate those regs. The syntax is in the help file. Also, you might have a GPIOR0 or 1 you could use.
Thanks Bob. I'll take a look! :shock:
joeymorin wrote:
The INT0 ISR can be as little as 4 words
joeymorin wrote:
your TIMER1_OVF1 ISR could be as short as 5 words
I am still clueless as to what I would do with the registers. :? Could somebody give me an overview of what the ISR needs to do?
Cris

Perfection is achieved, not when there is nothing more to add, but when there is nothing left to take away. Antoine de Saint-Exupery (1900 - 1944)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

joeymorin wrote:
I'm guessing those aren't the only TIMER1 interrupts you've got?
This is Timer1:
//------------------------------------------------------------------------------
//                               SET UP TIMER1
//------------------------------------------------------------------------------
//TIMER1 initialize - prescale:32 & Compare 149 = 3333.333 Hz (300 uSec)
void timer1_init(void)
{
  OCR1B = 0x00;                   //output compare B set to 0
  OCR1C = 0x95;                   //output compare C set to 149 (300 uSec)
  TCCR1A = (1<<PWM1B);            //enable PWM output B (PB3, pin 4)
  TCCR1B = (1<<CTC1) |            //clear on compare
		     (1<<CS12) | (1<<CS11); //F_cpu / 32
}

And other Interrupts:

MCUCR = (1<<ISC00);               //external interrupt detects ANY change
TIMSK = (1<<TOIE0);               //enable Timer0 overflow interrupt
GIMSK = (1<<INT0);                //enable Interrupt0 (PB6 - pin 9)

Perfection is achieved, not when there is nothing more to add, but when there is nothing left to take away. Antoine de Saint-Exupery (1900 - 1944)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

aeroHAWK wrote:
However I am clueless to what the ISR NEEDS to do.
But... you wrote it.

Do you mean you don't know what an ISR needs to do in the prologue/epilogue? It needs to save/restore any GP registers it uses, save/restore SREG if it is modified, restore any stack pointers to their pre-interrupt state, and it must end with a RETI.

Quote:
Can you give an example of how to do this?
In GCC it would be:
register uint16_t tachCount asm ("r2");

Quote:
And why is the ISR currently doing what it is doing?
Here's the existing prologue:
    00116 938A      ST   -Y,R24
    00117 939A      ST   -Y,R25
    00118 B78F      IN   R24,0x3F
    00119 938A      ST   -Y,R24

This saves R24 on the stack (pointed to by Y, which is register pair R28/R29), then R25, then it copies SREG into R24 and saves it onto the stack.

The epilogue undoes this, and ends with a RETI which pops the return address off the hardware stack (different from the Imagecraft R28/R29 user stack), re-enables interrupts, and return to the interrupted code:

    00123 9189      LD   R24,Y+
    00124 BF8F      OUT   0x3F,R24
    00125 9199      LD   R25,Y+
    00126 9189      LD   R24,Y+
    00127 9518      RETI

It does this because the code that actually implements:

tachCount++;

... looks like this:

    0011A 9180 0062 LDS   R24,tachCount
    0011C 9190 0063 LDS   R25,tachCount+1
    0011E 9601      ADIW   R24,1
    0011F 9390 0063 STS   tachCount+1,R25
    00121 9380 0062 STS   tachCount,R24

So R24 and R25 are loaded with the 16-bit tachCount found in SRAM, incremented by 1 (as a 16-bit word), and stored back into SRAM. SREG is potentially altered by ADIW. So the prologue and epilogue must save and restore R24/R25/SREG.

The compiler does all of this automatically. It does a good job, too. However in some cases the programmer can do better.

bobgardner wrote:
Also, you might have a GPIOR0 or 1 you could use.
There are no GPIORn registers in an ATtiny26, but I've listed a few other I/O registers that might be available (depending on what else he's using).

JJ

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

aeroHAWK wrote:
joeymorin wrote:
general purpose registers.
I understand this is a kind of 'cheating', and requires care. I would not be surprised if I have registers available. I am using ADC, USI, and timer1, but not timer0.
Those are I/O registers. The sentence from my post you were quoting was in reference to the general purpose registers R0 through R31.

Indeed a solution can include the use of one, the other, or both.

aeroHAWK wrote:
This is Timer1:
So only the two ISRs: INT0 and TIMER1 overflow.

You do use OCR1B and OCR1C for PWM and timer resolution, but OCR1A is available for use in an ISR. So is TCNT0. That's only two. More would be better.

The general idea is that instead of preserving a GP register like R24 by way of:

ST   -Y,R24

... which takes 2 cycles, you use an I/O register with:

OUT   TCNT0,R24

... which takes 1 cycles. A similar savings is seen with the restore.

So for every I/O register you have available, you can save 2 cycles by using OUT/IN instead of ST/LD.

You currently need to save R24/R25/SREG. If you have three available I/O registers, you could save 6 cycles.

If you also used the I/O registers to store tachCount in the first place, you could save another 4 cycles by using IN/OUT instead of LD/ST on each of the two bytes making up the variable.

This would require 5 free I/O registers. You probably don't have that many.

The other alternative I mentioned was reserving GP registers for tachCount and/or the copy of SREG. Doing so means you don't need to load or save tachCount at all, and only need one pair of IN/OUT to preserve SREG (instead of two). So the ISR becomes merely:

IN R4, SREG
ADIW R24, 1
OUT SREG, R4
RETI

This assumes you've declared a uint8_t as a register variable in R4 to store a copy of SREG, and tachCount as a register variable in R24/R25.

[EDIT: corrected invalid register for ADIW]

JJ

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

Last Edited: Fri. Nov 15, 2013 - 06:44 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I checked to see if there were any new posts just before I hit the "Submit" button on a post I was working on. It turns out you answered the questions I was just about to ask.... :wink:

joeymorin wrote:
OCR1A is available for use in an ISR. So is TCNT0. That's only two. More would be better.
I believe I can use TCNT0, OCR1A, EEDR, and OSCCAL.
joeymorin wrote:
The other alternative I mentioned was reserving GP registers for tachCount and/or the copy of SREG. Doing so means you don't need to load or save tachCount at all
So is this because when using GP registers you know that they won't be changed, but when using I/O registers, you can't be sure?

Cris

Perfection is achieved, not when there is nothing more to add, but when there is nothing left to take away. Antoine de Saint-Exupery (1900 - 1944)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

aeroHAWK wrote:
I believe I can use TCNT0, OCR1A, EEDR, and OSCCAL.
I didn't think you could use EEDR because I thought it was like UDRn on devices with a USART i.e. read accesses a different physical register than does write. A simple test program proves me wrong (on the mega328, at least).

So that's 4. That will save you 8 cycles off the bat.

joeymorin wrote:
So is this because when using GP registers you know that they won't be changed, but when using I/O registers, you can't be sure?
Not exactly.

It is up to you to arrange matters such that whatever storage location you use (be it SRAM, I/O register, or GP register) is exclusive. You couldn't for example employ timer 0 for some purpose and also use TCNT0 to store a variable. The timer would continuously update that I/O register whenever the timer was running.

General purpose registers are normally the purview of the compiler. By reserving one or more general purpose registers for use by a single variable, you remove those registers from the pool of registers available to the compiler for implementing the rest of your code.

For simple programs, there may very well be a number of registers that remain unused by the compiler in any way. In such circumstances, you will see no negative impacts of reserving a general purpose register.

In other cases, you might find that the code generated by the compiler for the rest of your program suffers w.r.t. performance because it had fewer registers in which to shuffle data about. Optimising compilers are pretty good about making choices based on available resources. If a temporary copy of a variable is needed, it can be stored more quickly and efficiently in a register than it can in SRAM. If you reduce the pool of available registers, the compiler can't do this as much or as often.

By reserving a general purpose register for use by a variable, you're telling the compiler to never use it for another purpose, even temporarily. This is useful in the context of writing a super-fast ISR, but it may impact the rest of your code.

I/O registers won't change unless the underlying hardware of which they are a part directs that change. TCNT0 won't change, unless timer 0 is running. EEDR won't change unless you fetch a value from EEPROM. OSCCAL won't change ever (except on reset).

Writing in pure assembler gives you ultimate control over all resources on the mcu. But it's harder for most of us :)

JJ

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Not that I want to stifle your education on assembler, registers, and ISRs, but you may want to take a step back and analyse were the problem lies, exactly.

If you're seeing problems with accuracy from your rpm calculations, you may consider other approaches beyond hand-optimising ISR code.

If the tiny26 had input capture, this would all be a moot point. However, it doesn't :(

The inaccuracies you've observed are due to the fact that you must rely on an interrupt to count the time between pulses from your tach sensor (or pulses per fixed unit time, in your case). Those inaccuracies were always there in the form of interrupt response time and interrupt jitter. In short the timestamp you grab in your INT0 ISR does not reflect the precise moment the sensor triggered interrupt, but the sum of that moment and the interrupt latency.

In your earlier testing that offset was consistent (with jitter of at most 3 cpu cycles), so you probably never saw it.

Now you have a competing interrupt, the one that handles your PWM and display functions. This has the potential to amplify the error you are seeing by adding an unknown and essentially unpredictable additional latency to the 'grab' of the timestamp. If the PWM interrupt is running when the tach sensor triggers the INT0 interrupt, the processing of the INT0 ISR code will be deferred until the PWM interrupt has completed.

Additionally, any other mainline code that accesses shared variable atomically must temporarily disable interrupts to do so.

Instead of whittling away the cycle-count of your ISRs, you may simply want to increase your RPM averaging window. Should be a lot simpler to do.

JJ

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

joeymorin wrote:
The other alternative I mentioned was reserving GP registers for tachCount
joeymorin wrote:
The general idea is that instead of preserving a GP register like R24 by way of:
ST   -Y,R24

... which takes 2 cycles, you use an I/O register with:

OUT   TCNT0,R24

... which takes 1 cycles.

The ISR would still need to increment tachCount, so if an I/O register was used, a few more cycles could be saved....
joeymorin wrote:
By reserving one or more general purpose registers for use by a single variable, you remove those registers from the pool of registers available to the compiler for implementing the rest of your code.
If the saved variable was volatile, wouldn't this be less of an issue?
joeymorin wrote:
In your earlier testing that offset was consistent (with jitter of at most 3 cpu cycles), so you probably never saw it.
Yes, I suppose this spoiled me. :wink:
joeymorin wrote:
Instead of whittling away the cycle-count of your ISRs, you may simply want to increase your RPM averaging window.
Yes, I agree, there is a balance to be found. I know that I am inexperienced and ignorant, so for me to be able to strike that balance, I want to get more educated. Certainly, if speeding up the ISRs is a royal pain, I would forgo the process. So far, I haven't found that to be the case (thanks in large part to you). :wink: When I calculated the 'worst case', I was surprised to find the degree of the error, so I think it's worth exploring options.

I do have a circular buffer to average 10 readings that will smooth out the digital display (along with the possibility of some dead band and/or hysteresis). But the bar graph display will not utilize this as I want to keep it responsive (although it really isn't needed for my specific application - but hey... I'm anal :shock: ).

Thanks JJ, for keeping me from getting lost in the forest... er, :? or the trees.... Your explanation of interrupt response time and interrupt jitter is precisely why I asked these questions and as an engineer, I know it can be easy to get caught-up in the esoteric and loose sight of the real task. :wink:

Cris

Perfection is achieved, not when there is nothing more to add, but when there is nothing left to take away. Antoine de Saint-Exupery (1900 - 1944)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I've re-read my posts, and there's a point I didn't really make which you may have not concluded.

Ultimately, any arithmetic must be done on values in the general purpose registers. For example, the ADIW will add a constant value to a register pair. SUB will subtract the contents of one register from another, and store it in that register. LSL will shift all the bits in a register to the left by one bit, and place a zero in the least significatnt bit. In effect, any operation on a piece of data must be done in a general purpose register.

Yes there are exceptions, like SBI/CBI which acts directly on a single bit of an I/O register. There are also tests that don't involve a general purpose register, and other instructions. However, in general the GP registers are involved in any action.

This is why it is valuable to reserve a GP register for exclusive use by a variable. In so doing, you obviate the need to move it from elsewhere into a GP register, and the need to move it back from whence it came.

This is a characteristic of all AVR and some other devices, but not all. The Motorola 6800 and 6809 CPU, for example, allows some direct arithmetic on RAM without the need to use any of the (very limited) general purpose registers. But that's another story.

JJ

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

joeymorin wrote:
any arithmetic must be done on values in the general purpose registers.
Ah ha! That makes sense (and why you would not have suggested using an I/O register). This is an important piece of the puzzle. It helps me understand the reason for GP registers and all the moving of things around.

So I have some studying to do. I need to understand more assembly, and your explanations are a big help! I think I have a good start understanding things so it is a matter of working out some details.

[edit]
Can I assume that when I declare tachCount as a variable register, the compiler will work around it?
Are there rules of thumb as to which registers to use (or does it even matter)?
Oh, and JJ, the syntax you showed should also work in ImageCraft. :wink:
[/edit]

Cris

Perfection is achieved, not when there is nothing more to add, but when there is nothing left to take away. Antoine de Saint-Exupery (1900 - 1944)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

bobgardner wrote:
You reserve registers 20,21,22,23 with a tic box in the ide, it links with different libraries that don't allocate those regs. The syntax is in the help file.
Above, I wrote:
Can I assume that when I declare tachCount as a variable register, the compiler will work around it?
Are there rules of thumb as to which registers to use (or does it even matter)?
In the help file, I found this (it answers my questions):
Quote:
You can ask the compiler not to use the registers R20, R21, R22, and R23 by checking the Compiler->Options->Target->"Do Not Use R20..R23" option. You should not check this option in general since the compiler may generate larger program because it has less registers to use. You cannot reserve other registers besides this set.

You can access these registers in your C program by using the pragma:

#pragma global_register : :... 

Note that you must still declare the datatypes of the global register variables. They must be of char, short, or int types, and you are responsible to ensure that the register numbering is correct. A 2-byte global register will use the register number you specified and the next register number to hold its content. For example, timer_16 above is an unsigned int, and it will occupy register R20 and R21.

Since these registers are in the upper 16 set of the AVR registers, very efficient code will be generated for them when assigning constants, etc.

Thanks Bob! :wink:

Cris

Perfection is achieved, not when there is nothing more to add, but when there is nothing left to take away. Antoine de Saint-Exupery (1900 - 1944)

Pages