Quantcast
Channel: Raspberry Pi Forums
Viewing all articles
Browse latest Browse all 4909

C/C++ • Example of GPIO use with Linux only (no third party libraries)

$
0
0
Background: it started from having to support many Linux versions, many Raspberry Pi versions and some other Pi flavours. I wanted to find "a library to end all libraries" and of course, failed. But I found that "sysfs" is deprecated and Linux has new ways to access GPIO.

Since those took a while to study and are harder to use than "pigpio", I decided to write a sample program to educate myself, measure performance and maybe help someone else. Caution: the program likely has errors, it might burn your Pi down or do other things. It's a sample / prototype, not a usable tool.

So here comes "gpiotest.cpp".

It should compile with GCC, but for compatibility reasons with my other work, I have used G++. Compile with:

Code:

g++ -std=c++17 -fpermissive -g gpiotest.cpp -pthread -o gpiotest
I advise to run it as root, because it tries to change Linux scheduling (more important) and elevate its priority (less important) for its two processes - main thread and reader thread. Without FIFO scheduling, I achieved around 10 KHz of frequency without massive sampling loss, with FIFO scheduling - nearly 50 KHz.

Source code:

Code:

// GPIO sample program: sends data on GPIO 17, receives on GPIO 27.// Public domain.#include <linux/gpio.h> // everything about GPIOs #include <sys/ioctl.h> // ioctl()#include <fcntl.h> // open(), O_RDONLY#include <unistd.h> // close()#include <stdio.h> // printf()#include <poll.h> // poll()#include <pthread.h> // threading// Chip 0 on older Pi models, chip 4 on Pi 5.#define CHIP "/dev/gpiochip4" // GPIO line numbers.#define WRITE_GPIO 17#define READ_GPIO 27// Termination flag.// This is not an example of good coding style.volatile int TERM = 0;// Data structures from Linux GPIO libs.// These here are global because a thread needs them.// This is not an example of good coding style.struct gpioevent_request event_request;struct pollfd poll_file_descriptor;void *reader(void *arg) {      // Elevate reader thread priority and use a FIFO scheduler.      pthread_t thread = pthread_self();   const struct sched_param sparam = { .sched_priority = 99, };   int res = pthread_setschedparam(thread, SCHED_FIFO, &sparam);   if (res != 0) printf("Failed to elevate reader thread priority!\n");         struct gpioevent_data event_data;      poll_file_descriptor.fd = event_request.fd;   poll_file_descriptor.events = POLLIN;      // Some variables for evaluating throughput      __u64 first_event_timestamp = 0;   __u64 last_event_timestamp = 0;   __u64 count_rising = 0;   __u64 count_falling = 0;      // Receive loop      while (TERM == 0) {      int poll_result = poll(&poll_file_descriptor, 1, 1); // time out after 1 milliseconds            if (poll_result == 0) {         // printf("Poll timeout.\n");         continue;      }            if (poll_result < 0) {         // printf("Poll error.\n");         continue;      }            if (poll_result > 1) {         // printf("Multiple events per poll.\n");      }      // The "revents" field counts returned events.      // The "POLLIN" constant seems to be a bitmask.            if (poll_file_descriptor.revents & POLLIN) {               int read_result = read(poll_file_descriptor.fd, &event_data, sizeof(event_data));                  if (read_result == -1) {            // printf("Read error.\n");            continue;         }         if (event_data.id == GPIOEVENT_EVENT_RISING_EDGE) {            count_rising++;            // printf("Rising edge at %llu.\n", event_data.timestamp);         } else if (event_data.id == GPIOEVENT_EVENT_FALLING_EDGE) {            count_falling++;            // printf("Falling edge at %llu.\n",event_data.timestamp);         } else {            // printf("Some other event?\n");         }                  if (first_event_timestamp == 0) {            first_event_timestamp = event_data.timestamp;         } else {            last_event_timestamp = event_data.timestamp;         }      }   }   printf("Received %llu rising and %llu falling edges.\n",count_rising,count_falling);   __u64 duration = last_event_timestamp - first_event_timestamp;   double seconds = ((double) duration / (double) 1000000000);      printf("Total duration %llu ns (%f s).\n",duration,seconds);      __u64 nanos_per_edge = duration / (count_rising + count_falling);      printf("Average %llu ns (%llu microseconds) per edge.\n",nanos_per_edge,(nanos_per_edge/1000));      __u64 per_second = count_rising / seconds;      printf("Rising edge frequency %llu Hz.\n",per_second);      close(poll_file_descriptor.fd);      return 0;}int main(int argc, char * const *argv) {      int res; // various call results      // Elevate main thread priority and use a FIFO scheduler.      const struct sched_param sparam = { .sched_priority = 99, };   res = sched_setscheduler(0, SCHED_FIFO, &sparam);   if (res != 0) printf("Failed to elevate main thread priority!\n");      // Data structures from Linux GPIO libs.   struct gpiochip_info chip_info;   struct gpiohandle_request handle_request;      int file_descriptor = open(CHIP, O_RDONLY);               if (file_descriptor < 0) {      printf("Failed opening GPIO chip.\n");      return 1;   }   res = ioctl(file_descriptor, GPIO_GET_CHIPINFO_IOCTL, &chip_info);      if (res < 0) {      printf("Failed getting chip information.\n");      close(file_descriptor);      return 1;   }         printf("GPIO chip information:\n");   printf("name: %s\n",chip_info.name);   printf("label: %s\n",chip_info.label);   printf("lines: %i\n", chip_info.lines);      for (int i = 0; i < chip_info.lines; i++) {            struct gpioline_info line_info;            line_info.line_offset = i;            if (ioctl(file_descriptor, GPIO_GET_LINEINFO_IOCTL, &line_info) < 0) {         printf("Failed getting line %i info.\n", i);      } else {         printf("%d %s\n",i,line_info.name);      }   }         // Request events on the reading line.   event_request.lineoffset = READ_GPIO;   event_request.eventflags = GPIOEVENT_REQUEST_BOTH_EDGES;   event_request.handleflags = GPIOHANDLE_REQUEST_INPUT | GPIOHANDLE_REQUEST_BIAS_PULL_DOWN;      res = ioctl(file_descriptor, GPIO_GET_LINEEVENT_IOCTL, &event_request);      if (res < 0) {      printf("Failed requesting events.\n");      close(file_descriptor);      return 1;   }      // Request handle on writing line. Many can be requested instead of one.      handle_request.lineoffsets[0] = WRITE_GPIO;   handle_request.flags = GPIOHANDLE_REQUEST_OUTPUT | GPIOHANDLE_REQUEST_BIAS_PULL_DOWN;   handle_request.lines = 1;   res = ioctl(file_descriptor, GPIO_GET_LINEHANDLE_IOCTL, &handle_request);      if (res < 0) {      printf("Failed requesting write handle.\n");      close(file_descriptor);      return 1;   }   // Start a reader thread   pthread_t reader_thread;   pthread_create(&reader_thread, NULL, &reader, NULL);         // Data handle for writing   struct gpiohandle_data hdata;      // Time variables for sleeping a short interval.   struct timespec ts, tr;   ts.tv_sec = 0;      printf("------------ now writing and reading -------------------\n");      // Write something out ad hope the reader reads it   for (int i = 0; i < 1000; i++) {            hdata.values[0] = 1;      res = ioctl(handle_request.fd, GPIOHANDLE_SET_LINE_VALUES_IOCTL, &hdata);      if (res == -1) printf("Failed setting line value.\n");            // Try to sleep for 5000 nanoseconds.      // In reality, due to call latencies, you end up sleeping longer.            ts.tv_nsec = 5000;      nanosleep(&ts, &tr);         hdata.values[0] = 0;      res = ioctl(handle_request.fd, GPIOHANDLE_SET_LINE_VALUES_IOCTL, &hdata);      if (res == -1) printf("Failed setting line value.\n");      ts.tv_nsec = 5000;      nanosleep(&ts, &tr);   }      close(handle_request.fd);      // Tell the thread to finish   TERM = 1;      // Give it time   sleep(1);      // Close resources   close(file_descriptor);      // Rejoin main thread with finished thread   pthread_join(reader_thread,NULL);      return 0;}
Sample output on Raspberry Pi 5, with GPIO 17 and 27 bridged together:

Code:

GPIO chip information:name: gpiochip4label: pinctrl-rp1lines: 540 ID_SDA1 ID_SCL2 GPIO23 GPIO34 GPIO45 GPIO56 GPIO67 GPIO78 GPIO89 GPIO910 GPIO1011 GPIO1112 GPIO1213 GPIO1314 GPIO1415 GPIO1516 GPIO1617 GPIO1718 GPIO1819 GPIO1920 GPIO2021 GPIO2122 GPIO2223 GPIO2324 GPIO2425 GPIO2526 GPIO2627 GPIO2728 PCIE_RP1_WAKE29 FAN_TACH30 HOST_SDA31 HOST_SCL32 ETH_RST_N33 -34 CD0_IO0_MICCLK35 CD0_IO0_MICDAT036 RP1_PCIE_CLKREQ_N37 -38 CD0_SDA39 CD0_SCL40 CD1_SDA41 CD1_SCL42 USB_VBUS_EN43 USB_OC_N44 RP1_STAT_LED45 FAN_PWM46 CD1_IO0_MICCLK47 2712_WAKE48 CD1_IO1_MICDAT149 EN_MAX_USB_CUR50 -51 -52 -53 ------------- now writing and reading -------------------Received 1000 rising and 1000 falling edges.Total duration 16653847 ns (0.016654 s).Average 8326 ns (8 microseconds) per edge.Rising edge frequency 60046 Hz.

Statistics: Posted by diastrikos — Sat Apr 13, 2024 2:49 pm



Viewing all articles
Browse latest Browse all 4909

Trending Articles