Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 26 Jan 2023 04:04:51 +0000
From:      "Dr. Rolf Jansen" <freebsd-rj@cyclaero.com>
To:        freebsd-arm <freebsd-arm@freebsd.org>
Cc:        Karl Denninger <karl@denninger.net>
Subject:   Re: GPIO inputs on Pis?
Message-ID:  <01070185ec3ff177-a3ef6a92-17ae-4b94-819b-c86b90ebb681-000000@eu-central-1.amazonses.com>
In-Reply-To: <1cd552a7-b015-c935-06fc-9f12e1b37daa@denninger.net>
References:  <0b235f83-7cb3-1d14-7c64-aee7c1c0c23d@denninger.net> <01070185e46b1a6e-ee34b885-1215-45c7-ac18-83320c02cac2-000000@eu-central-1.amazonses.com> <a9b00748-1a11-3806-1103-ac2e6ad6bf43@denninger.net> <01070185e4732fd8-c0bede0b-d9df-4557-a174-cb237fa4bfaf-000000@eu-central-1.amazonses.com> <fec41ff8-b658-36c8-615d-e3ae63dd9b37@denninger.net> <01070185e534f221-14f362b5-2b96-43f0-b0b0-f496ad9994fe-000000@eu-central-1.amazonses.com> <1cd552a7-b015-c935-06fc-9f12e1b37daa@denninger.net>

next in thread | previous in thread | raw e-mail | index | archive | help
> Am 25.01.2023 um 13:45 schrieb Karl Denninger <karl@denninger.net>:
>=20
> On 1/24/2023 14:15, Dr. Rolf Jansen wrote:
>> Yes, and for this reason, this GPIO event code which was developed by =
Christian Kr=C3=A4mer in the course of the GSoC-2018 and has been =
submitted in 2020 by Ian Lepore to the freebsd tree is perfect.
>>=20
>> Ian tested it with a 10 MHz sqaure wave on an imx6 (ARMv7, 1GHz) and =
got an event every 10 =C2=B5s. That means the max. speed without event =
losses would be 100 kHz. I did not do exact measurements, however, my =
impression is that my RPi4B can do it a tad faster than my BeagleBone =
Black. With the RPi4, I needed to improve the debouncing of the encoder =
and the buttons, because it saw hundreds of bounces which the BBB =
didn=E2=80=99t. However, it may also be, that the internal GPIO circuits =
of the BBB have a different damping characteristic.
>>=20
>> Anyway for my applications, 100 kHz way faster than what I need.
>>=20
>> On my GitHub repository I placed another example on using the GPIO =
events for the RPi:
>>=20
>> https://github.com/cyclaero/shutdd
>>=20
>> See also the respective thread on this mailing list:
>>=20
>> https://lists.freebsd.org/archives/freebsd-arm/2022-July/001576.html
> So..... just to see  if I'm understanding this correctly (pretty sure =
I am having read through the test code).
>=20
> Presume I have "X" pins configured as inputs and "Y" configured as =
outputs.  I use the ioctl calls to set the outputs (which works just =
fine) and can read current input state (which also works.)
>=20
> If I set on the same descriptor (not on the specific pins; it applies =
to all input pins on that descriptor as it appears there's no =
pin-specific setting in the configuration flags to enable this on a =
pin-by-pin basis) the event report config type I want I can then =
select() on the descriptor with the usual timeouts (or zero for a poll) =
and get a "ready" (much as one would for any other sort of I/O) and, if =
I do get a "ready" return on the descriptor a read() on that descriptor =
will return zero or more structures of the type I said I configured, =
each of which describes either an individual state change on one of =
configured input pins that is set up for individual event reporting OR a =
structure of the summary of changes for a given pin.
>=20
My understanding of what you wrote above sounds correct. However, I =
don=E2=80=99t use select(), since I am very comfortable with calling a =
blocking read() from a loop in a pthread. The advantage with this is, =
that there is almost no time gap in the user land between reading of the =
events. Below comes an example

You open the respective GPIO bank (the RPI4 got only 1 while the BBB got =
4) for reading.

   gpio_handle_t gpio0;

   if ((gpio0 =3D gpio_open(0)) !=3D GPIO_INVALID_HANDLE)
   {

You configure the pins, here for a mechanical rotary encoder having a =
push button facility on the axis. The clock and the button changes shall =
cause interrupts, while the direction shall be polled.=20

      gpio_config_t gcfg =3D {0, {}, 0, =
GPIO_PIN_INPUT|GPIO_INTR_EDGE_FALLING};

      // Encoder CLOCK
      gcfg.g_pin =3D 20;
      gpio_pin_set_flags(gpio0, &gcfg);

      // Encoder BUTTON
      gcfg.g_pin =3D 26;
      gpio_pin_set_flags(gpio0, &gcfg);

      // Encoder DIRECTION
      gcfg.g_pin =3D 19;
      gcfg.g_flags =3D GPIO_PIN_INPUT|GPIO_INTR_NONE;
      gpio_pin_set_flags(gpio0, &gcfg);

Now the event loop:

      ssize_t n, rc, rs =3D sizeof(struct gpio_event_detail);
      double  t;
      int     c =3D 0;
      struct  gpio_event_detail buffer[64];

      do
      {
         if ((rc =3D read(gpio0, buffer, sizeof(buffer))) < 0)
            err(EXIT_FAILURE, "Cannot read from GPIO0");

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

         else
         {
            n =3D rc/rs - 1;
            t =3D nanostamp(buffer[n].gp_time);

            switch (buffer[n].gp_pin)
            {
               case 20:
                  c +=3D (gpio_pin_get(gpio0, 19)) ? +1 : -1;
                  break;

               case 19:
                  break;

               case 26:
               default:
                  break;
            }

            printf("%5d %12.9f\tGPIO0.%u \t%u\t%zd\n", c, t, =
buffer[n].gp_pin, buffer[n].gp_pinstate, n);
         }
      } while (buffer[n].gp_pin !=3D 26);
   }

Basically, that=E2=80=99s it.

=20
> I assume given the 16-bit "count" field on the summary return that =
counts are "since last returned" and not "since boot" or "since =
descriptor was opened and configuration set.=E2=80=9C
>=20
> This also presumes that there is some buffer depth on this that, at =
some point, may be exceeded so if I "go away" for too long I may miss =
things -- particularly if I need detail-level reporting.

Yes, and for this reason I like to call blocking reads from within a =
relatively tight event loop.

> Thus it looks like that its possible for most cases (assuming =
resolution of the time stamps is high enough and the driver timing is =
accurate enough, plus the code is sparse enough that actually does the =
reading) to not just read an optical encoder with this but also (if you =
use detail reporting) to read a bi-phase encoder so you can determine =
which direction it is moving.

The computational resolution is nanoseconds, but of course you won=E2=80=99=
t see interrupts in GHz speed - on a RPI4 expect some hundred kHz. I use =
the following function for conversion to a double value:

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

> On to check it out; if I missed something here a "heh idiot, no it =
actually works like this!" would be appreciated :-)

I do poll the direction pin when the clock pin of the encoder cause =
interrupts. At least for mechanical encoders this is less cumbersome =
because depending on the debouncing circuit you might see pulse trains =
for each tick and tack. With respect to the direction it is then hard to =
figure out whether it is actually 0 or 1.




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?01070185ec3ff177-a3ef6a92-17ae-4b94-819b-c86b90ebb681-000000>