1
\$\begingroup\$

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?

\$\endgroup\$
3
\$\begingroup\$

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;
}
\$\endgroup\$
  • \$\begingroup\$ This seems a very slick compared to my method. I am having some trouble finding my way through this though. \$\endgroup\$ – Leon Ellis Mar 29 '15 at 14:25
  • \$\begingroup\$ I'm working on it now. \$\endgroup\$ – Leon Ellis Mar 29 '15 at 14:32
  • \$\begingroup\$ If the timer overflows, then the frequency has dropped below a level that I am interested in and so we can return to main. If the timer doesn't overflow for rb0_events = threshold amount of times, then a message will be sent. If it does overflow, i.e. I receive a flag, then I return to main. I'm struggling to work out where to go with this. \$\endgroup\$ – Leon Ellis Mar 29 '15 at 14:51
  • \$\begingroup\$ See edit above. \$\endgroup\$ – Dave Tweed Mar 29 '15 at 15:14
  • \$\begingroup\$ I see what you're saying. Let me give more detail. The rising edge is a square wave. It has been filtered prior to reaching the pic so that only 40-80Hz signals can reach the threshold of RB0. My timer overflows at 26ms so that peaks between any frequency able to reach the pic would be concurrent if they were active. If the signal stops briefly, the count starts again until 182 rising edges have been counted without longer than a 25ms gap between them. \$\endgroup\$ – Leon Ellis Mar 29 '15 at 15:57

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.