Electronic – Interrupt within an interrupt on a PIC18

c18interruptsmicrocontroller

I am working on code for an external interrupt from RB0 on a PIC18F4550. When an interrupt is detected on RB0, timer0 is activated.

I then need to watch for timer0 overflowing. If it does, then the code can return to main. If it does not, this means another rising edge has been detected on RB0. Once I have a certain number of concurrent RB0 interrupts before the timer overflows, I move on to the next procedure.

I am new to interrupts and am not sure if 'dealing with' the interrupt from timer0 within my RB0 ISR is OK.

Do I need a new ISR for timer0 to be called from within the RB0 ISR?

My RB0 ISR

Or is it OK to do as I am in my code?

Best Answer

Generally speaking, you really don't want to be wasting time in an ISR waiting for something to happen. Each ISR should do what it needs to do and exit quickly. Shared variables are used to keep track of state information and to communicate between ISRs.

In your case, you need to keep track of whether or not the timer has been started, and the number of RB0 events. You also need to know when to send a message, and what to put in the message. These become global variables:

bool timer_started;
int rb0_events;
bool send_message;
int message_data;

You initialize those variable in your mainline code, before any interrupts have been enabled. Then you enable the interrupts and go into a loop that simply monitors whether a message needs to be sent:

timer_started = 0;
rb0_events = 0;
send_message = 0;
message_data = 0;
/* Any other initialization goes here */
enable_interrupts();
while (1) {
  if (send_message) {
    send_message = 0;
    /* create and send the SMS message, using the value in message_data as needed */
  }
  /* Do other stuff while waiting for interrupts. */
}

In the RB0 ISR you check the timer_started flag and perform the desired action:

++rb0_events;
if (!timer_started) {
  timer_started = 1;
  start_timer();
}

And in the timer ISR, you perform the actions required there:

timer_started = 0;
stop_timer();
if (rb0_events >= THRESHOLD) {
  message_data = rb0_events;
  send_message = 1;
}
rb0_events = 0;

Based on your comments, it seems that what you are implementing is basically a frequency counter. In that case, there's no need to start and stop the timer; just let it run continuously, generating interrupts at a fixed interval. Use the RB0 interrupts to increment a counter, and use the timer interrupt to capture the value of that counter. The captured value represents the frequency of the events on RB0.

int rb0_events;
int rb0_frequency;
bool updated;

main () {
  rb0_events = 0;
  rb0_frequency = 0;
  updated = 0;

  /* configure and start timer */
  enable_interrupts();

  while (1) {
    if (updated) {
      if (rb0_frequency > THRESHOLD) {
        /* send SMS message */
      }
      updated = 0;
    }
    /* do other stuff while waiting for interrupts */
  }
}

rb0_isr () {
  ++rb0_events;
}

timer_isr () {
  rb0_frequency = rb0_events;
  rb0_events = 0;
  updated = 1;
}

Third try. As I understand it, you want to send a message if you've received at least 182 RB0 events in a row that never had more than 25 ms between them. Any gap longer than that resets the event counter.

int rb0_events;
bool send_message;

main () {
  rb0_events = 0;
  send_message = 0;
  /* Configure the RB0 and Timer interrupts, but don't start the timer. */
  enable_interrupts();
  while (1) {
    if (send_message) {
      send_message = 0;
      /* Send the SMS message. */
    }
    /* Do other stuff while waiting for interrupts. */
  }
}

rb0_isr () {
  /* Count the events, but keep the counter from "wrapping around" and triggering
   * a second message.
   */
  if (rb0_events < THRESHOLD + 1) ++rb0_events;
  /* Send one message when the counter reaches the threshold value.
   * No other message will be sent until the timer expires and resets the counter.
   */
  if (rb0_events == THRESHOLD) send_message = 1;
  /* The following call starts the 25 ms timer, or restarts it if it is already running. */
  start_timer();
}

timer_isr () {
  rb0_events = 0;
}