Port-arm archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

Re: Writing to GPIO registers on NetBSD/RaspberryPi



domelevo%gmail.com@localhost (Jean-Baka Domelevo Entfellner) writes:

>Following discussions last months on this mailing list about how bad it is
>to use /dev/mem and mapping it with mmap to access the hardware memory (and
>I totally agree on this!), I am trying to rewrite for NetBSD a piece of
>hack written for GNU/Linux distros on the RaspberryPi.

>The goal of the project is to turn the RaspberryPi into an FM transmitter (
>http://www.icrobotics.co.uk/wiki/index.php/Turning_the_Raspberry_Pi_Into_an_FM_Transmitter).


I played a bit with the RaspberryPi GPIO last weekend.

NetBSD doesn't support access to the peripherals through /dev/mem
because /dev/mem (on most platforms) only maps memory. It's usually
special device drivers that can map their hardware into userspace.
For example the registers of a graphics controller used by the
X server.

Maybe we need something like this, but it should still be restricted
to INSECURE kernels, because accessing the hardware can easily
corrupt the system (maybe even damage?).


In my case, I didn't try to make a FM transmitter, but I wanted
to control a set of RGB-Leds through the PWM controller. This requires
the same kind of access to the hardware. The Hardware is built
with WS2811 chips (http://www.adafruit.com/datasheets/WS2811.pdf)
in a 8x8 pixel array (https://shop.pimoroni.com/products/unicorn-hat).

Obviously the provided Linux software just maps the peripherals
and controls them from userspace.


To get this a bit safer, I did three things:


1. Extend the GPIO driver to allow 'alternate function' modes in
   addition to just input or output. This lets me configure GPIO18
   to use ALT5 which is the output of the PWM controller channel 0.

# gpioctl gpio0 18 set
pin 18: caps: in out pp tri pu pd alt0 alt1 alt3 alt4 alt5, flags: alt5

The BCM2835 GPIO driver provides a set of available alternate functions
for each pin.


2. Write small drivers for PWM controller and the Clock Manager.
   The drivers don't do anything special, they just provide slightly
   abstracted accessor functions that can be used in the kernel.

For example:

range = 3;
div = 19200000 / (range * 800000);
bcm_cm_set(BCM_CM_PWM, CM_CTL_SRC_OSCILLATOR, div << 12 | 0);

sets the PWM clock to use the "oscillator" (19.2MHz) with
a fractional divider of 8 to generate a 2.4MHz PWM clock.

bcm_pwm_control(pwm, PWM_CTL_USEF | PWM_CTL_MSEN, range);
bcm_pwm_control(pwm, PWM_CTL_USEF | PWM_CTL_MSEN | PWM_CTL_PWEN, range);

configures the PWM channel to use the FIFO, the M/S algorithm,
it also sets the period to range=3 clocks. The second call then
enables the channel.

bcm_pwm_write(pwm, unicornhatpwm, NULL, 8*8*24);

writes one buffer (for channel0) to the FIFO of the PWM controller
(no DMA yet).

The buffer contains a sequence of (32bit) numbers for each PWM
encoded bit that gets transmitted. The number is the clock count
for the high period of the PWM signal, either 1 or 2 for 1:3 and
2:3 PWM ratios which the WS2811 interprets as 0 and 1 bits.


3. Add a sysctl to the RPI kernel (in rpi_machdep.c) that allows me
   to pass in a hex string for 8*8*24bit. When the string is set
   it is translated into a PWM encoded sequence and transmitted
   to the WS2811 chips.

Using a sysctl is just a quick hack to pass data into the kernel.
Here are some ideas for a better solution.

One could create a driver that is exposed to userland (i.e. /dev/pwm0
or even /dev/unicornhat). Then you could just copy an RGB buffer to
the device. You could also create a "RPI" driver that implements
such functions as individual units.

Since PWM is some kind of streaming, it might also be possible to
attach the driver to audio(4), but the details of the PWM controller
(different serializer modes, repeats, ...) might break the abstraction
of the audio driver.

Another idea is provide an attachment to gpio(4), but since the
hardware cannot be arbitrarily attached, it would be rather
limited. Also gpio(4) itself doesn't provide userland access
beyond gpioctl. You either need to attach a driver that provides
userland access (similar to gpioiic -> /dev/iic*) or pass data
through a new set of ioctl calls.


http://ftp.netbsd.org/pub/NetBSD/misc/mlelstv/unicornhat.jpg


Greetings,
-- 
-- 
                                Michael van Elst
Internet: mlelstv%serpens.de@localhost
                                "A potential Snark may lurk in every tree."


Home | Main Index | Thread Index | Old Index