Showing posts with label port. Show all posts
Showing posts with label port. Show all posts

July 18, 2019

GPIO port for Raspberry Pi

As part of the R&D work on port! datatype in port-type branch, we have implemented a GPIO driver for Raspberry Pi boards, as a gpio:// scheme (no third-party library needed). This work helped define the low-level API for ports written in purely Red/System or a mix of Red and Red/System.

The Raspberry Pi is a very popular board with millions of units sold, so this is a market where Red could be potentially helpful to developers. We could run Red on such boards for years but did not have proper GPIO support, so this is now fixed!

The current features supported by the GPIO port are:
  • auto-detecting the Raspberry Pi board type
  • uses /dev/mem or/dev/gpiomem for direct and fast access.
  • reading a GPIO pin state.
  • writing to a GPIO pin state.
  • hardware PWM output support (on capable pins).
  • a simple DSL for sending commands.

Planned (but not scheduled) future features include:
  • generating events when a pin state changes.
  • software PWM on all GPIO pins.
  • a higher-level reactive object layer for a API-less interface.
  • various drivers for common peripherals.

The source code for the gpio:// scheme can be found there.

In the short video below, you can find a little pet project meant for testing some of the features. It implements a simple joypad with 4 directions support, a red LED indicating when the pad is active and a green one for notifying when a level is completed. The game is the Red port contributed by Huang Yongzhao of Rebox!, my old clone of BoxWorld written in Rebol. In the video below, it is running on a Raspberry Pi 3 using our work-in-progress red/GTK backend for Red (contributed by Rcqls), locally merged with the red/port-type development branch (EDIT: that branch has been merged into master now).




You can find the breadboard layout below made using Fritzing. Sorry for the messy wiring, it is my first try with such kind of tool. If you have the skills to improve it, here is the sketch file. The buttons rely on the internal pull-down resistors. Note that in the video, the board is reversed.



The source code of Redbox has been modified to add GPIO support, you can find the modified code here. The GPIO-related code is enclosed in a context:

    joypad: context [
        mapping: [
            16 down  20 up
            17 left  21 right
        ]

        row: [(id) state: #[false] direction: (direction)]
        table: collect [
            foreach [id direction] mapping [keep compose row]
        ]

        pins: extract table length? row
        port: none

        acquire: has [pin][
            port: open gpio://
            foreach pin pins [
                insert port [
                    set-mode  pin in
                    pull-down pin
                ]
            ]

            insert port [
                set-mode 18 out
                set-mode  4 out
                set 4 on
            ]
        ]

        pressed?: function [][
            foreach pin pins [
                entry: find table pin
                old: entry/state

                insert port [get pin]
                entry/state: make logic! port/data

                ;-- detect 0-to-1 transitions only, to avoid auto-firing
                if all [not old entry/state][return entry/direction]
            ]
            none
        ]

        show-win: does [insert port [set 18 on]]

        release: does [
            insert port [
                set  4 off
                set 18 off
            ]
            close port
        ]
    ]


The GPIO port has a simple API:

  • opening: port: open gpio://
  • sending commands: insert port [...commands...]
  • closing: close port

The sent commands form a small DSL:
Set the working mode for a given pin:
 
    set-mode <pin> <mode>

    <pin> : pin number (integer!)
    <mode>: in, out, pwm

Write a value on a pin: 
 
    set <pin> <value>
  
    <pin>  : pin number (integer!)
    <value>: true, false, on, off, yes, no, 0, 1

Read a value from a pin (the returned value is in port/data):

    get <pin>

    <pin>: pin number (integer!)    

Manage pull-up/down resistors:

   pull-off <pin>    ;-- disable any pull previously set
   pull-down <pin>   ;-- activate pull-down on the given pin
   pull-up <pin>     ;-- activate pull-up on the given pin   

Write a PWM value on a pin:

   set-pwm <pin> <value>
 
   <pin>  : pin number (integer!)
   <value>: an integer between 0 and 1024, or a percentage for duty cycle.

Fade in/out values on a PWM pin:

   fade <pin> from <start> to <end> <delay>   

   <pin>  : pin number (integer!)
   <start>: starting value (0-1024)
   <end>  : ending value (0-1024)
   <delay>: duration of the whole fading (time!)

Wait for a given duration:

   pause <delay>

   <delay>: integer => pause in miliseconds, float => pause in seconds.

Important notes
  • Only BCM numbering for GPIO pins is supported.
  • A word, or path or paren expression containing regular Red code can be used in place of any numeric value.
  • A single command block can contain an arbitrary number of commands.
  • The get command can be used multiple times, a block of corresponding results will then be returned in port/data.
  • Use sudo when running your GPIO code if it involves PWM!
  • An example of using fade and other commands is available here.
  • The PWM range is currently preset to 1024, though, the DSL can be trivially extended to allow a user-provided range value.

Happy hardware hacking! ;-)

July 17, 2011

Mac OS X port released!

Red/System Mac OS X port is now completed and on par with Windows and Linux ports. The new Mach-O file emitter supports all current Red/System's features including #syscall and #import directives.

Now that all major 3 OS are supported, I can say that the Mac OS X file ABI and function call ABI are really the most complicated one to support from scratch, the Windows one (PE/COFF) being the simplest (with its own set of oddities, but simpler) and Linux's ELF somewhere in between. Among the issues we had to identify and deal with:
  • Stack alignment on 16 bytes constraint at the point of every library call (meet with the infamous "gate keeper" code causing segfaults and bus errors on misaligned calls)
  • Working around dynamically linked libraries info extraction that need to be copied from each library header to the generated executable image. This could have prevented cross-compilation.
  • The need for direct CALL opcode in the generated IA-32 code instead of indirect ones used for PE and ELF library function callings.

After a lot of efforts spent mostly in debugging, here is the result:





As you can see, it is fully functional and all unit tests are passing!

A couple of notes about usage:
  • The library you declare in #import definition needs to be located in /usr/lib/ or else, you need to provide a full path to the library.
  • The file emitter should support up to 255 imported libraries (Mach-O limit), but it has been tested only with one so far (libc.dylib), so if you have anything odd happening with more than one imported library, please fill a bug report in github's tracker, or post a message to the mailing-list.
Enjoy!
Fork me on GitHub