/* 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
#! /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
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.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".
:~ % 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>
See a respective thread on the ARM mailing list: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".
gpioctl() doesn't list INTR* caps/flags, obviously it won't accept such configuration.does not accept to be INTERRUPT
Thanks, obsigna !See a respective thread on the ARM mailing list:
Porting FreeBSD to ARM processors: User Space GPIO Interrupt programming - GSoC-2018
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.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.
// 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);
}
}
Wow man, thanks a lot! You make my day!Here comes a tiny C program ...
These changes have already been accepted and implemented, I checked it in the code! ?This resulted into the implementation of "userland gpio interrupts“ in December 2020.
rS368585
reviews.freebsd.org
:~ # cd /usr/src/tools/test/gpioevents/
:/usr/src/tools/test/gpioevents # make
:/usr/src/tools/test/gpioevents # make install
:~ % 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")
time 314635.811642768 pin 11 state 0
:~ %