Wednesday, 1 October 2014

RaspberryPi inputs: to poll or to interrupt?

One of the best features of the RaspberryPi is the GPIO (General Purpose Inputs & Outputs).


Reading the input levels on the GPIO is pretty easy, especially using Gordon's wiringPi library.


But you need to think carefully about how best to do this in the software for your chosen project.


The two basic options are either to poll an input, or to use an interrupt.


polling an input


RaspberryPi inputs can be checked periodically to determine if the input level is high or low.

In a functional programming language like Python, we would probably create a program loop like this:-

while True:
    if GPIO.input(1)==True:
        ...input is HIGH, so do some stuff
    else:
        ...input is LOW, so do something else
    time.sleep(0.2)


This "while true" loop runs for as long as the program runs. The sleep time (200ms in this case) just ensures we don't paralyse the cpu by demanding its attention for 100% of the time.

In an event driven language like Gambas, we can use a timer (a periodic event) as the trigger to run an input checking routine;

inputTimer.delay=200 'a 200ms interval
inputTimer.start()

Public Sub inputTimer_Timer()

    if digitalRead(PIN_1)=0 Then
        ...input is HIGH, so do some stuff
    Else
        ...input is LOW, so do something else
    EndIf
End


This timer routine only runs when the 200ms interval has elapsed. We don't need to create any program loops because Gambas takes care of that for us.

In both examples, our program will check the current condition of an input, do something appropriate, and then free-up the cpu to do other system tasks. So the interval between input checking is approximately 200ms plus the time it takes to execute our "some stuff" or "something else" code.

attention to detail


We need to look carefully at our requirements to determine whether this approach is suitable for our application.

For example, in my bird box project I'm detecting birds as they enter or leave the bird box. Small garden birds can get through the 28mm hole really quickly. But considering the length of the bird, I reckon the sensor will typically be blocked for at least 500ms, and as my counter only needs to provide a rough approximation, I've set my Gambas timer interval to 500ms.

However, your requirements may be more demanding. What if we wanted a more accurate count of birds entering and leaving the nest box?

You may find it useful to draw a simple sketch, something like this:-


The second line shows that the input from the sensor may be high just long enough to be read by our software once, or the input may remain high long enough to be read high twice. The solution may be to count the first high input reading, but not count any further high readings until we have seen a low reading.

Now what happens if a bumble bee enters the nest and is briefly detected?


The short pulse at the input may or may not be read as a high, it just a matter of timing.

You can reduce the influence of small critters upsetting your count by reducing the sample interval (i.e. increasing the frequency).


However you must now modify your code so that a minimum number of consecutive reads, where the input is high, are required to count as a bird (e.g. 10 consecutive high readings = 500ms = bird). While you still need to read a low input level (or maybe 2 or more) before counting the next high sequence as a possible bird.

Another consequence of increasing the input read frequency may be higher cpu utilisation, possibly resulting in a less responsive system, more heat and higher power consumption.

what about using an interrupt?


The second approach is to use an interrupt which basically detects a change in input level in the background, allowing your program to get on with other tasks.

Now just let me ramble on a bit here about interrupts. Its probably because I'm an old fossil, but in another working-life, in another century, I developed hardware using chips like the Intel 8085 processor.

In that age, an interrupt was created by a level change to a special input to the processor, which immediately resulted in the cpu pushing its current program address onto a stack, and vectoring to a predetermined memory location, where you (the programmer) could determine what happened next.

Once the interrupt had been serviced, the cpu would pop the earlier address off the stack and continue where it had left off.
So I struggle a bit with the notion that the RaspberryPi has a true interrupt. However there are a few comments here that may well convince you that I am wrong.

But for me, an Interrupt must interrupt to be an Interrupt.

Once again, Gordon Henderson's wiringPi library is probably a good starting point for implementing an interrupt in your code. There are many Python examples out there that you can refer to.

For Gambas enthusiasts the outlook is not so bright. After reading the wiringPi documentation on wiringPiISR, you would expect it to work with Gambas, but unfortunately not. From what I understand, according to Mr. Gambas (Benoit) wiringPi would need to be modified to meet the needs of Gambas.

However, you can use Gordon's GPIO Utility using code like this (add a Label, Listbox, CommandButton and a timer):-

' Gambas class file

Public pInt As Process

Public Sub Form_Open()
  Timer1.Stop
  Timer1.Delay = 50
End

Public Sub btnSetInt_Click()
Dim strReturned As String

   pInt = Exec ["gpio", "-g", "wfi", "18", "falling"]
   Exec ["gpio", "exports"] To strReturned
   Label1.Text = "set: " & strReturned
   Timer1.Start

End

Public Sub Timer1_Timer()

  If pInt.State = 0 Then
    ListBox1.Add("triggered: " & Format(Now(), "hh:nn:ss") & " process: " & CStr(pInt.id), 0)
    pInt = Exec ["gpio", "-g", "wfi", "18", "falling"]
  Endif
End


This code works OK as long as you don't need to count input transitions that occur more than once in 50ms (or whatever the timer interval is, so long as its not unreasonably low).

Also note that this code is polling the interrupt status, so its not a true interrupt system.

any more options?


You could also consider creating a hardware interface between a RaspberryPi input and your sensor or detection circuit.

One example for a detection system that produces a narrow pulse (say 1ms) at a fairly low frequency (say 1 pulse per second) would be to interface via a 555 timer configured as a 500ms monostable. Each time the 555 is triggered, the output to the RaspberryPi input would be a 500ms pulse, which could be polled as previously discussed.

For projects with higher pulse repetition rates, maybe a binary counter could be used. Each input pulse would clock the counter, allowing the RaspberryPi to read the count periodically and reset the counter via a GPIO output.

No comments:

Post a comment