Bogusław Kempny

GPIO pulses counter

Autor adres Polski
About HC-SR04 LCD Camera fork() Short Message Service strfry() GPIO pulses Keypad Gate GPIO PWM SG90 RFID Graphologist Time and attendance Shutdown Temperature ....

The change of the signal state on the GPIO pin may, after the pin is appropriately programmed, generate an interrupt. This interrupt can be created on a rising edge (0 -> 1), a falling edge (1 -> 0), or both.

This has a lot of use cases, for example, counting amount of people passing by, measuring the number of revolutions (calculating the rotational speed) of something spinning, triggering an alert (light, sound, sending a text message ...) after opening a door, closing blinds on the windows after dusk ...

Just connect the sensor, like microswitch, optocoupler, or electromagnetic sensor, with the pin and you can start.

Of course, the sensor must provide a digital signal in the CMOS standard, 3.3V. If it produces a signal in the TTL standard, 5V, we have to convert it, for example using a two resistor voltage divider.

It is convenient if the signal is an 'open collector' or a mechanical contact. The GPIO pins are equipped with resistors that can be used to connect the pins to the ground or to the supply voltage simply by programming the pin without using extra wires.

The pin must, of course, be programmed to INPUT.

We connect an ordinary mechanical switch between the pin and the ground. We close - it is a logical zero, we open - a logical one.

In this example, we do not use an external source of pulses. The principle of the discussed operation can be explained using pulses generated by Raspberry Pi.

So, let's start.

If you do not have the wiringPi library yet, install it following the instructions given in the HC-SR04 sensor section.

We will discuss the imp_e.c program. You can compile it manually or use a ready-to-use script.

The program generates a given number of pulses on the GPIO pin 29 and counts them on pin 28.

We close pin 29 with pin 28 as shown in the photo.

To avoid closing two pins programmed on OUTPUT, first, set them as INPUT using the following commands:

gpio mode 28 input
gpio mode 29 input

Compile and run the program:

root@mobile3:/home/rcp# cc imp_e.c -o imp -lwiringPi
root@mobile3:/home/rcp# ./imp 755

An example output of the program looks as follows:

The current state of the counter: 750

Counted 755 pulses

The program generated 755 pulses on the GPIO pin 29. They were sent through the jumper to pin 28. There, they were counted, the program displayed their total number, and, during counting, every 10 pulses displayed an intermediate result.

Pulses were generated at a speed of 50 per second. However, this is not the limit of the Raspberry Pi's capabilities. I tested this program with pulses generated a thousand times faster. At this speed, it begins to lose about 1% of the individual pulses.

So, we can assume that the Raspberry Pi can handle tasks like this. I have applied this solution in practice to measure the amount of dispensed fuel in our system for operating a factory petrol station.

In the system, the Raspberry Pi counts the pulses from the PIUSI K600B/3 flowmeter, works with the MySQL database on a remote server, verifies drivers and cars permissions to refuel, saves the data about the amount of a distributed fuel, and measures the fuel level in a tank with the HC-SR04 sensor.

This solution has been in operation for a year now. Thanks to Raspberry Pi, already 80,000 liters of fuel have been distributed during almost 600 refuelings.

So how is it done?

The most important is to properly program the GPIO pin responsible for counting the pulses.

Using the wiringPi library function, we program the selected pin as input:

   pinMode(28, INPUT);

Now, we program interrupts. When the signal state on the pin changes, an interrupt is being generated and handled by an indicated procedure, in this case, int_impuls.

The interrupt can be generated on a falling edge of the signal, as in this example, on a rising edge (INT_EDGE_RISING) or on a rising and falling edge (INT_EDGE_BOTH).

    wiringPiISR (28, INT_EDGE_FALLING, &int_impuls);

If the source of the pulses is a mechanical switch or a sensor with an open collector output, then we turn on the resistor connecting the pin with the supply voltage. In our example, this line has a comment, because the source of pulses will be another GPIO pin:.

//    pullUpDnControl (28, PUD_UP) ;

The procedure which handles an interrupt is simple. After each interrupt, it increments by one the value of the global variable pulses, and every ten pulses, displays its current state:

int pulses;

void int_impuls(void)
   if(pulses%10 == 0)
     printf ("The current state of the counter : %d\r", pulses);

If we use an external source of pulses, this would be pretty much it.

The screen shows how many pulses are received and we can continue to work on the program development. For example, to activate a cuckoo clock mechanism in every 3600 pulses, if the pulses come at a rate of one per second.

Here is some practical advice. The described method of counting pulses is very fast. If we use a mechanical switch, it is worth filtering out, even with a simple capacitor, very short pulses that appear as a result of contacts' vibration at the moment of switching it on and off.

Otherwise, instead of one, we can observe several pulses.

As our GPIO pins 28 and 29 are already connected, let's do the generator.

We program pin 29 as output and we set a logical 0 on it:

   pinMode(29, OUTPUT);

Now, in a loop, iterated as many times as specified in the parameter of the program, we set a logical one on the pin 29 for 10 milliseconds, and then, a logical 0 for the next 10 milliseconds.

This gives us 50 rectangular pulses per second:

   int n;
      Delay(10000);   // 10 ms pulse
      Delay(10000);   // 10 ms space


During the loop execution, at each falling edge of the signal on the pin 29 (digitalWrite(29,LOW);) an interrupt is being generated followed by the execution of the int_impuls() procedure, which increases by 1 the value of the global variable pulses.

When the loop ends, the final pulses count is displayed:

   printf ("\n\nCounted %d pulses\n\n", pulses);

Before we finish here, since our program uses interrupts, there is one important thing to mention. The sleep and nanosleep functions do not resume after an interruption occurs.

That is why we use the Delay() procedure here, as it continues timing after an interrupt occurs.

Please pay attention to the procedure's name. It starts with a capital D.

A function with a similar name, but starting with a letter d in lowercase, exists in the wiringPi library, however, it cannot handle interrupts. It measures time in milliseconds, not microseconds. Using it can cause strange and difficult to diagnose behavior!

void Delay(int microsec)

 struct timespec tim3;
 tim3.tv_sec = 0;
 tim3.tv_nsec = microsec * 1000;