GPIO API general (Orange Pi ... H3)

I mean what is the output format for your LCD screen? Not character/text based right?
Is it a display device and could you play an animated beastie logo on it?
How do you use it? Can you play a mp4 to it?

I am asking because I want e-Paper display and I am wondering about workflow for display.
Is it treated like a miniture monitor?
 
Sorry man, I didn't understand you, at the time your message arrived I had a live consultation on my channel on how to edit a photo for the FreeBSD logo and I changed the photo every few minutes, I thought you were asking me about it.

That's right, not character/text-based.
I can play an animated beastie logo on it, but well need preparation and writing some code for that purpose.
No, I can't play an mp4 on it.
It is a cheap device with 65K colors and no fast data flow.
In general, e-Paper displays have a slow workflow ...

As I promised, I will soon publish basic work about this more as an experiment on how to use the mentioned commands.
 
Okay, I'm doing this as a token of gratitude to the people who have helped me reach my desired goal since the beginning of this topic.
This is my Orange Pi with a connected TFT display on a 40 pin connector, SPI0 channel.

dev.jpg


In addition to the three lines that are directly connected to the SPI0 channel: pins PC0, PC2 and PC3,
It uses 3 more GPIO pins to control the display: PA12 as Reset, PA13 as DC (data or command protocol) and PA14 as BL (screen on and off).

sch.jpg


I needed delays in milliseconds, so I made a simple msleep command without error handling. I call it from the script several times because direct handling of external devices requires delays ...

C:
/* msleep.c    FreeBSD */
/* Simple milisleep function */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int
main(int argc, char *argv[])
{
    long res;
    useconds_t u_sec;
    res = strtol(argv[1], NULL, 10);
    u_sec = (useconds_t)res;
    u_sec *= 1000;
    usleep(u_sec);
    exit(0);
}
:~ # cc -o msleep msleep.c

And finally the script as glue for all commands.

Bash:
#! /bin/tcsh -f
# $FreeBSD$
# Init and Draw background color

#set echo

alias reset        'gpioctl  RESET 0;./msleep 30;gpioctl  RESET 1'
alias DC_ctrl    'gpioctl  DC 0'
alias DC_data  'gpioctl  DC 1'
alias BL_off      'gpioctl  BL 0'
alias BL_on      'gpioctl  BL 1'
alias SPI_tx      'spi -d w -C'
alias SPI_ctrl    'DC_ctrl; SPI_tx'
alias SPI_data  'DC_data; SPI_tx'

# 16 bit colours
set BLACK  = (00 00)
set BLUE   = (00 1F)
set RED    = (F8 00)
set GREEN  = (05 E0)
set YELLOW = (FF 88)
set WHITE  = (FF FF)

# Set colour
set C = ($BLUE)

# For vertical lines loop
set Y = 160
set y = 0

# Init pins
gpioctl -n PA12 RESET
gpioctl -n PA13 DC
gpioctl -n PA14 BL
gpioctl -c RESET OUT
gpioctl -c DC OUT
gpioctl -c BL OUT

# Max SPI speed
# 33.333Mhz is max with good results
spi -s 33333333
reset
BL_off

#echo "init_cmds 1"
SPI_ctrl "01"; ./msleep 150                    # 1. cmd
SPI_ctrl "11"; ./msleep 255                    # 2. cmd
SPI_ctrl "B1"; SPI_data "01 2C 2D"       # 3. cmd
SPI_ctrl "B2"; SPI_data "01 2C 2D"            # 4. cmd
SPI_ctrl "B3"; SPI_data "01 2C 2D 01 2C 2D"    # 5. cmd
SPI_ctrl "B4"; SPI_data "07"                # 6. cmd
SPI_ctrl "C0"; SPI_data "A2 02 84"            # 7. cmd
SPI_ctrl "C1"; SPI_data "C5"                # 8. cmd
SPI_ctrl "C2"; SPI_data "0A 00"                # 9. cmd
SPI_ctrl "C3"; SPI_data "8A 2A"                # 10. cmd
SPI_ctrl "C4"; SPI_data "8A EE"                # 11. cmd
SPI_ctrl "C5"; SPI_data "0E"                # 12. cmd
SPI_ctrl "20"                                # 13. cmd
SPI_ctrl "36"; SPI_data "C0"                # 14. cmd
SPI_ctrl "3A"; SPI_data "05"                # 15. cmd

#echo "init_cmds 2"
SPI_ctrl "2A"; SPI_data "00 00 00 80"        # 1. cmd
SPI_ctrl "2B"; SPI_data "00 00 00 A0"        # 2. cmd

#echo "init_cmds 3"
SPI_ctrl "E0"; SPI_data "02 1c 07 12 37 32 29 2d 29 25 2B 39 00 01 03 10" # 1. cmd
SPI_ctrl "E1"; SPI_data "03 1d 07 06 2E 2C 29 2D 2E 2E 37 3F 00 00 02 10" # 2. cmd
SPI_ctrl "13"; ./msleep 10                           # 3. cmd
SPI_ctrl "29"; ./msleep 100                         # 4. cmd
BL_on

#Set_addr_win:
SPI_ctrl "2A"; SPI_data "00 02 00 81"        # 1. cmd
SPI_ctrl "2B"; SPI_data "00 00 00 A0"        # 2. cmd
SPI_ctrl "2C";

DC_data

# Draw background
while ($y <= $Y)
  @ y = $y + 1
  SPI_tx "$C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C  $C "
end
spi -i

And action ...


Thank you and sorry for the ugly video!

Source code for the ST7735 display driver:
 
Since we're talking about GPIO API here, I think this is relevant too.
Does anybody know about GPIO interrupts support? The wiki page still shows them as "to do".
I just wanted to announce that I am going a step further on this started topic. After the CLI experiments, I will start writing the right code with gpio (3)() - The C library ... For now, I know nothing about the availability of GPIO interrupts support. If I discover something during the work, I will report it.
 
Something I could spot at first glance:

:~ % gpioctl -l -v |grep PA11
pin 11: 1 PA11<>, caps:<IN,OUT,PU,PD,INTRLL,INTRLH,INTRER,INTREF,INTREB>
:~ % gpioctl -c PA11 IN INTRLL
Interrupt capability INTRLL cannot be set as configuration flag
:~ % gpioctl -l -v |grep PA11
pin 11: 1 PA11<IN>, caps:<IN,OUT,PU,PD,INTRLL,INTRLH,INTRER,INTREF,INTREB>


So it accepts that the pin is declared as INPUT but does not accept to be INTERRUPT ... :confused:
 
Since we're talking about GPIO API here, I think this is relevant too.
Does anybody know about GPIO interrupts support? The wiki page still shows them as "to do".
See a respective thread on the ARM mailing list:

This resulted into the implementation of "userland gpio interrupts“ in December 2020.

This is working perfectly with FreeBSD 13.0-RELEASE on a BeagleBone Black. I cannot tell anything about the various Fruty Pis, though.
 
does not accept to be INTERRUPT
gpioctl() doesn't list INTR* caps/flags, obviously it won't accept such configuration.
Interrupts are processed by kernel, it's not clear how to use them in user space.
Within a commercial project I'm involved in I wrote a Linux kernel module, which sends a signal to a userland program when an interrupt occurs. I'm not sure whether such approach is acceptable in general though.
 
gpioctl() doesn't list INTR* caps/flags, obviously it won't accept such configuration.
Interrupts are processed by kernel, it's not clear how to use them in user space.
Within a commercial project I'm involved in I wrote a Linux kernel module, which sends a signal to a userland program when an interrupt occurs. I'm not sure whether such approach is acceptable in general though.
Here comes a tiny C program which utilizes the user space interrupts facility for receiving events from a rotary encoder and some buttons which are attached to GPIO pins of the BBB. Interrupt processing is very fast, and the encoder and the buttons need to be debounced. Otherwise pressing a button or turning the encoder gives up to 10 interrupts per action.

Here is a post, on how I debounced the encoder and the buttons.


C:
//  encoder.c
//  Test tool for the Rotary Encoder and Buttons attached to the BBB's the GPIOs
//
//  Compilation: clang encoder.c -lpthread -lgpio -o encoder

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <math.h>
#include <termios.h>
#include <unistd.h>
#include <pthread.h>
#include <fcntl.h>
#include <err.h>
#include <libgpio.h>
#include <dev/iicbus/iic.h>
#include <sys/filio.h>
#include <sys/sysctl.h>
#include <sys/time.h>
#include <sys/types.h>


/*
   07   gpio2[ 2]     gpioctl -f /dev/gpioc2 -c  2 IN
   08   gpio2[ 3]     gpioctl -f /dev/gpioc2 -c  3 IN
   09   gpio2[ 5]     gpioctl -f /dev/gpioc2 -c  5 IN
   10   gpio2[ 4]     gpioctl -f /dev/gpioc2 -c  4 IN
   11   gpio1[13]     gpioctl -f /dev/gpioc1 -c 13 IN
   12   gpio1[12]     gpioctl -f /dev/gpioc1 -c 12 IN
   13   gpio0[23]     gpioctl -f /dev/gpioc0 -c 23 IN
   14   gpio0[26]     gpioctl -f /dev/gpioc0 -c 26 IN
   15   gpio1[15]     gpioctl -f /dev/gpioc1 -c 15 IN
   16   gpio1[14]     gpioctl -f /dev/gpioc1 -c 14 IN
   17   gpio0[27]     gpioctl -f /dev/gpioc0 -c 27 IN
   18   gpio2[ 1]     gpioctl -f /dev/gpioc2 -c  1 IN
   19   gpio0[22]     gpioctl -f /dev/gpioc0 -c 22 IN
*/

static inline double nanostamp(int64_t stamp)
{
   uint64_t ns = (1000000000*(stamp & 0xFFFFFFFFu) >> 32);
   return (int32_t)(stamp >> 32) + ns*1e-9;
}

#define CLK 2
#define DT  3
#define SW  4
#define BR  5

#define B1 12
#define B2 13
#define B3 14
#define B4 15


bool gRunning = true;

void *gpio1Events(void *gpio1_handle)
{
   gpio_handle_t gpio1 = *(gpio_handle_t *)gpio1_handle;
   ssize_t n, rc, rs = sizeof(struct gpio_event_detail);
   double  t;
   struct  gpio_event_detail buffer[64];

   while (gRunning)
   {
      if ((rc = read(gpio1, buffer, sizeof(buffer))) < 0)
         err(EXIT_FAILURE, "Cannot read from GPIO1");

      if (rc%rs != 0)
         err(EXIT_FAILURE, "%s: read() the odd count of %zd bytes from GPIO1\n", getprogname(), rc);

      else
      {
         n = rc/rs - 1;
         t = nanostamp(buffer[n].gp_time);
         printf("    - %12.9f\tGPIO1.%u \t%u\n", t, buffer[n].gp_pin, buffer[n].gp_pinstate);
      }
   }

   return NULL;
}


int main(int argc, char *const argv[])
{
   gpio_handle_t gpio1, gpio2;

   if ((gpio1 = gpio_open(1)) != GPIO_INVALID_HANDLE)
      if ((gpio2 = gpio_open(2)) != GPIO_INVALID_HANDLE)
      {
         gpio_config_t gcfg = {0, {}, 0, GPIO_PIN_INPUT|GPIO_INTR_EDGE_FALLING};
  
         // Button 1
         gcfg.g_pin = B1;
         gpio_pin_set_flags(gpio1, &gcfg);
  
         // Button 2
         gcfg.g_pin = B2;
         gpio_pin_set_flags(gpio1, &gcfg);
  
         // Button 3
         gcfg.g_pin = B3;
         gpio_pin_set_flags(gpio1, &gcfg);
  
         // Button 4
         gcfg.g_pin = B4;
         gpio_pin_set_flags(gpio1, &gcfg);
  
         // Encoder CLK
         gcfg.g_pin = CLK;
         gpio_pin_set_flags(gpio2, &gcfg);
  
         // Encoder SW
         gcfg.g_pin = SW;
         gpio_pin_set_flags(gpio2, &gcfg);
  
         // Reserve Button
         gcfg.g_pin = BR;
         gpio_pin_set_flags(gpio2, &gcfg);
  
         // Encoder DT pin
         gcfg.g_pin = DT;
         gcfg.g_flags = GPIO_PIN_INPUT|GPIO_INTR_NONE;
         gpio_pin_set_flags(gpio2, &gcfg);

         ssize_t   rc;
         pthread_t gpio1_events_thread;
         if ((rc = pthread_create(&gpio1_events_thread, NULL, gpio1Events, &gpio1)))
            err(EXIT_FAILURE, "Cannot create thread for permanently reading GPIO1 interrupts: %d.", rc);

         ssize_t n, rs = sizeof(struct gpio_event_detail);
         double  t;
         int     c = 0;
         struct  gpio_event_detail buffer[64];
  
         do
         {
            if ((rc = read(gpio2, buffer, sizeof(buffer))) < 0)
               err(EXIT_FAILURE, "Cannot read from GPIO2");
  
            if (rc%rs != 0)
               err(EXIT_FAILURE, "%s: read() the odd count of %zd bytes from GPIO2\n", getprogname(), rc);
  
            else
            {
               n = rc/rs - 1;
               t = nanostamp(buffer[n].gp_time);
  
               switch (buffer[n].gp_pin)
               {
                  case CLK:
                     c += (gpio_pin_get(gpio2, DT)) ? +1 : -1;
                     break;
  
                  case DT:
                     break;
  
                  case SW:
                  case BR:
                  default:
                     break;
               }
  
               printf("%5d %12.9f\tGPIO2.%u \t%u\t%zd\n", c, t, buffer[n].gp_pin, buffer[n].gp_pinstate, n);
            }
         } while (buffer[n].gp_pin != SW);
      }
  
      else
      {
         perror("GPIO2 Open");
         gpio_close(gpio1);
         exit(-1);
      }

   else
   {
      perror("GPIO1 Open");
      exit(-1);
   }
}
 
This resulted into the implementation of "userland gpio interrupts“ in December 2020.
These changes have already been accepted and implemented, I checked it in the code! 👍
There is also a test program gpioevenst:
:~ # cd /usr/src/tools/test/gpioevents/
:/usr/src/tools/test/gpioevents # make
:/usr/src/tools/test/gpioevents # make install

Code:
:~ % gpioevents
gpioevents: No pin number specified.
usage: gpioevents [-f ctldev] [-m method] [-s] [-n] [-S] [-u][-t timeout] [-d delay-usec] pin intr-config [pin intr-config ...]

  -d  delay before each call to read/poll/select/etc
  -n  Non-blocking IO
  -s  Single-shot (else loop continuously)
  -S  Report summary data (else report each event)
  -u  Show timestamps as UTC (else monotonic time)

Possible options for method:

  r    read (default)
  p    poll
  s    select
  k    kqueue
  a    aio_read (needs sysctl vfs.aio.enable_unsafe=1)
  i    signal-driven I/O

Possible options for intr-config:

  no     no interrupt
  er     edge rising
  ef     edge falling
  eb     edge both
:~ %
:~ % gpioevents -f /dev/gpioc0 -s 11 ef
("waiting")

For the reason of "ef" (edge falling) configuration and pin (11) state of 1 (high voltage) nothing happens ...
...
... but, after touching pin 11 with 0 voltage result is a quick response of interrupt:

time 314635.811642768 pin 11 state 0
:~ %


So it works great!
The next stage is to try to write a kernel module based on:
 
Back
Top