Timers, interrupts and timing?

Hi all, I'm working on a project at the moment that requires quite a lot of stuff to happen as quickly as possible.

Basically, I am working on a lightweight electric vehicle/controller management system, and as such, the main uC has to handle quite a bit of stuff, such as reading cell voltages from a LiPo battery, measuring current draw, current speed, reading a throttle input and outputting a PWM control signal etc etc. I have all the hardware design done using a 328.

My idea thus far is something like this: Keep timer0 intact for millis() and such, timer1 needs to stay untouched for the PWM to work, leaving me timer2. I have my brake connected to Pin 2 on an interrupt.

I was considering setting up timer2 to overflow at say, 50ms, and run the throttle routine, so that the throttle stays responsive all the time.

However, I also want to be able to measure current ground speed using a reed switch on the back wheel. Pretty much all I have left at this stage is the hardware interrupt on pin 3, however then there is the possibility it may override and thus miss the brake interrupt, which is a no-go. Pulsein is a blocking function and will slow down my loop, so it's not really an option.

So, I'm wondering, is there a way to say, give 1 interrupt priority over another in some way?

Both interrupts would only be required to set a variable or 2, so they would take very little time, but the possibility is still there.

Does anyone have any better ways/ideas I could 1. Timer interrupt the throttle code to run every 50ms or so, 2. Have a brake interrupt that can override everything (Or at least, can trigger straight after the speed interrupt), and of course, count pulses from a reed switch?

Can timer2 be used as a counter as well?

On 2nd thoughts I probably should have gone with a more capable uC, but I should be able to get this to work well enough I hope.

Thanks

I'm unclear why the brake needs an interrupt - I'd expect that you could get a fast enough response by polling. If the other stuff in loop is time consuming, just ensure that you only do one of the other tasks on each iteration of loop.

The brake on an interrupt is more a safety thing if anything, for example if something in the loop decides to take up a crazy amount of time for whatever reason, the brake isn't going to stop responding because of it. Same with the throttle.

In the time it has when it's not controlling the throttle or braking, it'll need to read in voltages from a 6S LiPo through a LTC6803 - which according to the datasheet, will take about 13ms. It will also need to read from an external ADC, do a few calculations, then spit a bunch of data out over the serial port.

Indeed in reality I could probably get away with just polling it, but I dunno, just seems like something that could really do with an interrupt if anything else.

I'm really just trying to avoid errors in my code trying to kill me through disabling the brake or locking up the throttle. I'd feel better if I knew my brake was on an interrupt, so that no matter what, it'll kill the speed controller.

Things:
So, I'm wondering, is there a way to say, give 1 interrupt priority over another in some way?

the interrupts have already a priority order, and pin2 (INT0) is the 1st after RESET, next is pin3 (INT1) ....
if your brake request is on pin2 , it's the 1st in priority order

As a beginner I can not competently answer the questions, but when I asked about the timers, I got relevant answers here:
http://forum.arduino.cc/index.php?topic=178466.0

alnath:

Things:
So, I'm wondering, is there a way to say, give 1 interrupt priority over another in some way?

the interrupts have already a priority order, and pin2 (INT0) is the 1st after RESET, next is pin3 (INT1) ....
if your brake request is on pin2 , it's the 1st in priority order

That's good to know, however it was my understanding that when an ISR is running, all other interrupts are disabled. So if the brake happened to trigger as the speed ISR is running (which would only be for as long as it takes to set 2 variables), it'd miss the brake interrupt?

afaik, if the interrupt occurs while another is running, it is memorized, and it will be handled as soon as the "running" one is over.

Edit : I knew this page was in my favorites :wink: : Gammon Forum : Electronics : Microprocessors : Interrupts
a "must read" :slight_smile:

if the interrupt occurs while another is running, it is memorized, and it will be handled as soon as the "running" one is over.

Interrupt routines need to be kept as quick as possible.
Polling can service things in a more deterministic way and since it is not having to save and restore context can be faster than interrupts.

So, I’m wondering, is there a way to say, give 1 interrupt priority over another in some way?

Absolutely! As Alnath pointed out, the interrupts are prioritized. All you have to do is use INT1 (or one of the PCx interrupts) for the reed switch interrupt, and INT0 for the brake. And one more thing. In order for one interrupt to interrupt another interrupt, you have to arrange for the interrupts to re-entrant. When an interrupt fires, the interrupt enable flag is cleared automatically, which disables further interrupts. If you want your interrupt service routine to be interruptable, you have to enable interrupts inside your interrupt service routine. Then a higher priority interrupt could fire and interrupt this one.

However, I expect you would find that in this case that’s not going to be necessary.

However, I also want to be able to measure current ground speed using a reed switch on the back wheel. Pretty much all I have left at this stage is the hardware interrupt on pin 3, however then there is the possibility it may override and thus miss the brake interrupt, which is a no-go. Pulsein is a blocking function and will slow down my loop, so it’s not really an option.

You won’t miss the brake interrupt. If the brake interrupt occurs while the interrupts are disabled because you’re servicing the reed switch interrupt, it will not be lost. It will be delayed until one clock cycle after your interrupt service routine returns. But it will still fire. And if your interrupt service routine only sets a variable or two, the delay will be very small.

If you really want high accuracy for your reed switch speed indicator, you could use the Timer1 Capture event. That way, you get accurate time stamps even if you’re servicing another interrupt when the event fires and the interrupt is delayed.

radman:
Interrupt routines need to be kept as quick as possible.

agreed :slight_smile:

radman:
Polling can service things in a more deterministic way and since it is not having to save and restore context can be faster than interrupts.

yep :slight_smile: it’s up to the programmer/designer to decide which is best for his project. Knowing what the interrupts are for, how they work and the possible side-effects, is important to take the right decision

  1. Don't use a reed switch for the speed sensor, because it will bounce and generate multiple interrupts. Use a Hall sensor instead.

  2. The ISR for the speed sensor only needs to take a few microseconds, because all it needs to do is store the time (as returned by a call to micros()) that the interrupt occurred, and the interval since the last interrupt. So if the throttle interrupt is happening every 50ms, then the speed sensor ISR isn't going to delay it significantly. Likewise, delaying the brake interrupt by a few microseconds is hardly likely to matter.

  3. Use the watchdog timer to reset the system if it isn't kicked often enough, and design the hardware so that on a reset, the throttle will be made safe (i.e. throttled all the way back). Kick the watchdog at the end of the throttle routine (whether you call the throttle routine in an ISR or by polling). Then if you stop responding to the throttle, the watchdog will cut in and kill the throttle.

  4. Consider using a cooperative scheduler (time triggered architecture). This is a common approach in safety-critical software, because it makes the behaviour much more predictable than having interrupts going off all over the place.

  5. Finally, don't build this system unless the speed is going to be very low and you will not be driving anywhere where you may be a danger to anyone other than yourself. Embedded software is not easy to get right with the degree of certainty you need. The Arduino core and IDE break some of the most basic rules of safe software design, so it would not be possible to certify an Arduino-based system to any of the commercial safety standards.

Thanks for the tips, I tried out a reed switch last night and indeed bounce will be an issue, so I'll use a hall sensor instead.

No need to worry though, this isn't for a commercial product, just for my own use, so if it kills me, it won't worry me too much :wink:

Hey guys, just wondered if someone could check my code and make sure I'm not doing anything too extreme in the ISR that could be slowing down my code?

In my final application the ISR shouldn't be called more than about 19 times/sec, so hopefully I still have enough time to do everything else?

volatile unsigned long currenttime;
volatile unsigned long previoustime;
volatile int rpms;
volatile boolean rpmsset = false;
float RPS;
float diameter = 100.00;
float circumference;
float groundspeed;
float RPM;

void setup(){
  Serial.begin(9600);
  attachInterrupt(0,getRPM,FALLING);
  circumference = diameter * 3.14;
}

void loop(){
  if (rpmsset == true){
    RPS = 1000.00/rpms;
    RPM = RPS * 60;
    groundspeed = (RPM * circumference)/16666.66;
    Serial.print("RPS: ");
    Serial.print(RPS);
    Serial.print(" | RPM: ");
    Serial.print(RPM);
    Serial.print(" | Speed: ");
    Serial.print(groundspeed);
    Serial.println(" km/h");
    rpmsset = false;
  }
  delay(100);
}

void getRPM(){
  currenttime = millis();
  rpms = currenttime - previoustime;
  rpmsset = true;
  previoustime = currenttime;  
}

I know there is a fair bit of floating point math involved, but the accuracy is pretty horrid without it. Since this is part of a 2 uC system, I might just have this one pass off the RPS data to the 2nd uC and have it handle all the floating point math - as it's timing isn't as critical as this one.

I don't see any floating point in the interrupt routine itself, so, looks OK to me.

The ISR is short and sweet.

You could do some critical section code in the loop() routine around the area where the volatile variable is used.

You might want to implement some sort of averaging system so your speedometer value doesn't jump around.

My idea is basically to collect a bunch of data as quickly as possible, then when it has a spare chance, say every second or so, dump it out all over the serial port, which will be connected to another 328.

So basically it’ll have to:

  • Constantly poll for the brake, and disable the ESC if activated
  • Quickly read an analog input for the throttle, convert to a PWM value and output to the ESC
  • Occasionally read from a LTC6803, maybe every second or so since it takes a relatively long amount of time (Probably ~15ms or so)
  • Read from an external I2C ADC fairly quickly
  • Read the hall sensor, and spit out some time values that I can convert to RPM/ground speed.

Once it’s done this, and collected all the data (Speed value, battery cell voltages, current readings etc), it needs to spit them all out over serial as quickly as possible to a second “display unit”, which will read and display the data on a LCD. As such, it can also handle some of the slow floating point math that doesn’t need to be done on the main uC. I’m just a bit worried about how long it’s going to take to send all the data over serial and read it reliably on the other end.

The reason it all needs to happen quickly is so that the throttle can remain as responsive as possible - I’m aiming for at least 50ms, probably 100ms absolute max. Thus why I was considering giving the throttle a timer interrupt, as it could be dangerous if you push the throttle and it doesn’t respond quick enough. Thoughts?

50ms should be no problem.

At 16MHz, the Arduino will execute approximately one machine code instruction per .0625 microseconds. By my math that's 800,000 instructions every 50ms.

Now, one typical 'C' statement in the Arduino IDE may require anywhere from one to several dozen machine instructions to execute, but still, we should be well within the realm of possibility.

Your challenge, as a programmer, is scheduling all that stuff to happen at the right time and in the right order.

The good news is, using the hardware UART for serial transfers should be non-blocking. That is, other code can execute as the serial data trickles out. Looks like the serial buffer is 127 bytes, so as long as you don't fill the buffer you should be ok. You can go ahead and push to your display unit fairly frequently, if the data set is small enough.

To help on the serial side, crank up the baud rate to 76,800 (WormFood's AVR Baud Rate Calculator Ver. 2.1.1). If you find serial can't keep up with the amount of data you need to push and you're not already using the hardware SPI pins for something else (or can move those functions to other pins), consider using SPI between the two Arduinos. SPI is really really fast compared to serial. :slight_smile:

One potential challenge is analogRead(): it's rather slow at 100 microseconds. If this is unacceptable, you can use advanced techniques to ask the ADC to begin a conversion and then you're free to go off and execute other important code while the ADC bakes its cake. Then you can come back later and grab the ADC result when it's finally ready.

Arduino's I2C is also supposed to be non-blocking, but there is a bug report here that may be relevant: I2C/Wire library: Make Wire library non-blocking · Issue #1476 · arduino/Arduino · GitHub

Hmm, sounds good to me.

Just off the top of my head, I'd probably be pushing at least 40 "values" (digits, characters etc)

Unfortunately SPI will be busy, so I'll have to rely on the UART.

The real challenge will be arranging the data in such a way I can read it back into variables on the 2nd uC, while not causing a bunch of delay sending it from the main uC.

I'll need to come up with some sort of command structure for sending the data, maybe using letters for commands followed by the data, for example to send the 6 battery cell voltages, I could send: a439b439c439d439e439f439 , then on the 2nd uC just split the data back into separate variables based on the command character (As I don't expect I'll have to send any characters, I can just use control numbers if I need to).

But I'll probably also need to set up a handshake system so that the data isn't missed/corrupted, which will probably involve disabling the interrupt briefly, not really an issue. So the main uC will send some data to tell the 2nd uC to get ready, smash it with the data as quickly as possible, and get back into the main loop, then the 2nd uC can sit there and crunch the data all it likes and display it.

That's my thoughts, anyway.

Seems to me, from working on similar issues with (ridiculously bloating) datalogging projects, that dedicating one processor to just throttle/esc/braking control gives you plenty of responsiveness. Then offload almost all the other work (display, cell monitoring, etc) to another processor that won't be
life- threatening if it crashes. This lets you leave your mission critical code short and sweet, and then you can play with lots of other ideas with (relative) impugnity. One of the best things about this platform is that the hardware is so inexpensive, 3 or 4 processors aren't much of an investment compared to a propulsion battery...

Of course, you'll have a panic handle that unhooks the main battery, so anything short of a battery fire will simply be a quick grab of the handle and a longer coast to the side of the road.... Death by EV is usually a pretty slow, predictable process, so if you have a redundant hardware disconnect, you'll probably be able to avert it.

Just another way to do it,

t

Things:
Hey guys, just wondered if someone could check my code and make sure I'm not doing anything too extreme in the ISR that could be slowing down my code?

No, you are not. However, I suggest a couple of improvements:

  1. Declare currentTime locally in the ISR since it doesn't need to be preserved between interrupts. This will make the ISR faster.

  2. Use micros() not millis() to do the timing. This will give you better resolution and consistency. millis() sometimes increments by 2.

So your ISR should look like this:

void getRPM(){
  unsigned long currenttime = micros();
  rpms = currenttime - previoustime;
  rpmsset = true;
  previoustime = currenttime;  
}