target icon

Purpose

In this tutorial, we are going to manage the GPIO’s of the stm32 Pyboard.

list icon

Prerequisites

  • A complete installation of an Hyperpanel OS tools on a linux based PC including tools for flashing and debugging.
  • A visual editor to create or modify the C source.
  • A touch sensor.
  • A passive infra-red sensor.
  • A Pyboard kit.

list icon

Software release

hypv9wr61 and higher

align left icon

Description

This simple code is located into the Application Container, it is run by the VMK sub-operating system. On the other hand, the I/O container runs all the drivers that are VMIO Finite State machines.

The daugther board as a connector with 6 GPIO signals and three LED’s. Six jumpers may be installed on the GPIO connectors, when a jumper is plugged then the GPIO is HIGH and if it is removed then the GPIO electrical state is LOW. Two GPIO are associated to each LED, so we have 4 possible jumpers configuration for each LED. We have 4 differents LED blinking patterns and this application tracks dynamically the jumper configuration (fit/unfit) and updates the blinking pattern accordingly.

Signals GPIO1 and GPIO2 are selecting the blinking pattern for the RED LED, signals GPIO3 and GPIO4 are selecting the blinking pattern for the GREEN LED and GPIO5 and GPIO6 are used for the YELLOW LED.

The sample code dynamically tracks the jumper presence yes/no.

We connected to GPIO1 a touch sensor (cf. picture 05). Once we touch this connected device the GPIO1 signal goes up and then RED led blinks.

We connected to GPIO6 a passive infra-red sensor” (cf. picture 06). Once approach and near this connected device the GPIO6 signal goes up and then YELLOW LED blinks.

Also, a “Hello World” message is send every 4 seconds on serial port ASY0.

This program:

1.

Creates a new task.

2.

Gets three blinking LED (RED, GREEN, YELLOW) with a temporal pattern.

3.

Wait for one of the GPIO signals change . Start blinking leds on change state.

The Pyboard and two sensors connected thrue GPIOs.

Code

hypos-tuto-215-gpio.c

/*
**  hypos-tuto-215-gpio.c ================================================== **
**                                                                           **
**  This simple code is located into the Application Container, it is run    **
**  by the VMK sub-operating system. On the other hand, the I/O container    **
**  runs all the drivers that are VMIO Finite State machines.                **
**                                                                           **
**  The daugther bord as a connector with 6 GPIO signals and three LED's.    **
**  6 Jumpers may be installed on the GPIO connectors, when a jumper is      **
**  plugged then the GPIO is HIGH and if it is removed then the GPIO         **
**  electrical state is LOW. Two GPIO are associated to each LED, so we have **
**  4 possible jumpers configuration for each LED. We have 4 differents      **
**  LED blinking patterns and this application tracks dynamically the        **
**  jumper configuration (fit/unfit) and updates the blinking pattern        **
**  accordingly.                                                             **
**  ------------------------------------------------------------------------ **
**  Signals GPIO1 and GPIO2 are selecting the blinking pattern for the RED   **
**  LED, signals GPIO3 and GPIO4 are selecting the blinking pattern for the  **
**  GREEN LED and GPIO5 and GPIO6 are used for the YELLOW LED.               **
**  ------------------------------------------------------------------------ **
**  The sample code dynamically tracks the jumper presence/abscence. Also,   **
**  a "Hello World" message is send every 4 seconds on serial port ASY0      **
**  ======================================================================== **
*/

/* Includes files and external references ...................................*/

#include <hypos.h>                     // Hyperpanel OS basic interfaces
#include <drv_asy.h>                   // "asy_write" prototype

#include "./myio.h"                    // Interface of "myio.c"
#include "./mytsk.h"                   // Interface of "mytsk.c"


/* Internal defines of this module ------------------------------------------*/

#define  TICK               10000      // Code for tick event


/* Internal data types of the module ........................................*/

static void    open_gpio       (void)                                        ;
static void    close_gpio      (void)                                        ;
static int     read_gpio       (int )                                        ;
static void    open_led        (void)                                        ;
static void    close_led       (void)                                        ;
static void    set_led_pattern (int)                                         ;


/* Internal global variables of this module ---------------------------------*/

int            gpio_iod              ; // IOD of device GPIO\DEV0
int            gpio_iods[6]          ; // IOD of GPIO UNAME=GPIO1/2/3/4/5/6
char           gpio_val[6]           ; // Current GPIO states/vlaues (0/1)

int            led_iod               ; // IOD of device LED\DEV0
int            led_iods[3]           ; // IOD of LED LEDNAME=RED/GREEN/YELLOW

int            idto                  ; // Timer identifier


/* Static data of this module -----------------------------------------------*/

static const char *const gpio_taglist = "FUNC=GPIO\nOUTPUT=HI_Z\n"
                                        "WEAKPULL=DOWN\nEVENTS=REPORT"        ;

static const char *const led_tagpop  = "CMD=POPALL"                           ;
static const char *const led_blink[4] =
  {
    "PATTERN=OFF:0\nCMD=PUSH"                   ,
    "PATTERN=ON:2,OFF:16\nCMD=PUSH"             ,
    "PATTERN=ON:2,OFF:8\nCMD=PUSH"              ,
    "PATTERN=ON:1,OFF:1\nCMD=PUSH"              ,
  }                                                                           ;

/*  Procedure loop_app_tsk ----------------------------------------------------

    Purpose : This is our task main loop.
*/

int loop_app_tsk (void *param)
  {
    int             i                ; // Loop counter
    int             ev               ; // Our event
    char            str[80]          ; // Serial line message
    int             cpt = 0          ; // Messages counter

/*****************************************************************************
 * Step 1 : Here we initialize the needed drivers :                          *
 * ------   - With the GPIO driver we are using signals with user names      *
 *            GPIO1 to GPIO6. The board comes with a bloc of 3 x 6 pins that *
 *            is at the right of the LDO and each row "n" as 3 pins that are *
 *            from left to right GND 3V3 and GPIOn. The "open_gpîo"          *
 *            subroutine stores the 6 GPIO sub-channel IOD's in the static   *
 *            "gpio_ios[6]" array, "gpio_iods[0]" corresponds to GPIO1.      *
 *          - With the LED driver we use the 3 available LED's. The          *
 *            "open_led" subroutines stores in "led_iods[3]" the 3 sub-      *
 *            channels IODs, "led_iod[0]" is the RED LED, "led_iods[1]" is   *
 *            for the GREEN one and "led_iods[2]" for the YELLOW.            *
 *****************************************************************************/

    start                            : // Start Label

    open_gpio()                      ; // Open the GPIO driver -> gpio_iods[]
    open_led()                       ; // Open the LED driver -> led_iods[]


/*****************************************************************************
 * Step 2 : Now we read the 6 pins current states and we store those in      *
 * -------  the static "gpio_val[6]". Then we can call "set_led_pattern" for *
 *          each led (0->RED, 1->GREEN, 2 ->YELLOW) in order to set the      *
 *          blinking pattern the corresponds to the pair of GPIOs            *
 *****************************************************************************/

    for ( i = 0; i < 6; i++)           // We use "drv_get" to read the
      gpio_val[i] = read_gpio(i)     ; // state of GPIO i.

    set_led_pattern(0)               ; // Set patt for RED according GPIO1/2
    set_led_pattern(1)               ; // Set patt for GREEN according GPIO3/4
    set_led_pattern(2)               ; // Set patt for YELLOW according GPIO5/6


/*****************************************************************************
 * Step 3 : We start a timer that will send us an event every 4 second so    *
 * ------   that we can send repeatidly a message on serial line 0           *
 *****************************************************************************/

    set_tto(CLOCK      ,               // Timer mode: clock
            4000       ,               // Duration in milliseconds
            TICK       ,               // Event code
            0          ,               // Event reserve field
            &idto        )           ; // Timer identifier


/*****************************************************************************
 * Step 4 : Here is our main event loop. For each loop, we do as follows :   *
 * -------  - First we wait for an event. We are unscheduled until an event  *
 *            is received. When we are scheduled again, the received event   *
 *            is available from the "task_evt" global structure.             *
 *          - Then if the event code is TICK we print "Hello World"          *
 *          - If the event is IND_REPORT, the "reserve" field is the         *
 *            involved IOD. We compare it with the 6 GPIO sub-channels IOD's *
 *            that are in "gpio_ios[6]". When we have a match, then we have  *
 *            in "task_evt.longueur" the code for the enumerated INPUT tag   *
 *            value, so 0 if LOW and 1 if HIGH. We store the value in        *
 *            "gpio_val[i]"                                                  *
 *          - Led number "n" is associated with GPIO 2*n and 2*n+1. So       *
 *            whatever the "i" GPIO index value is (0 to 5), the involved    *
 *            led number (0 to 2) is i/2. So we call the "set_led_pattern"   *
 *            that sets a new blink pattern according to the values of       *
 *            gpio_val[2*(i/2)] and gpio_val[2*(i/2)+1]                      *
 *          ---------------------------------------------------------------- *
 *          We remind that the GPIO driver IND_REPORT events are as follows  *
 *                                                                           *
 *          code       IND_REPORT                                            *
 *          reserve    Subchannel IOD                                        *
 *          adresse    HNULL                                                 *
 *          longueur   LOW  (0) on falling edge or HIGH (1) on rising edge   *
 *          flags      0                                                     *
 *          res2       Number of the INPUT tag, so 16                        *
 *****************************************************************************/

    wait_ev                          : // Start of event loop label

    ev = wait_allevent()             ; // Unschedule until an event is
                                       // received

    if ( ev == TICK )                  // The event is the 4 seconds tick
      {                                //
        hsprintf(str,                  // We build the message, starting
              "%6d Hello World\r\n",   // with a 6 digits decimal counter
              cpt ++                ); // value
                                       //
        asy_write(0               ,    // Console/trace is serial 0
              (unsigned char*)str ,    // Message address
              strlen(str) )          ; // Count of bytes of the message
      }

    else if ( ev == IND_REPORT )       // The event is a IND_REPORT
      {                                //
        for ( i = 0; i < 6 ; i++)      // The "reserve" field is the involved
          if (task_evt.reserve ==      // IOD. We compare with the 6 
              gpio_iods[i]        )    // subchannels IODs for the 6 GPIO's
            {                          //
              gpio_val[i] =            // If we found the IOD, we copy the
                   task_evt.longueur ; // new INPUT value (0/1) to "gpio_val[i]
                                       //
              set_led_pattern( i/2 ) ; // The involved LED is "i/2" and we
                                       // set a new blinking pattern for it
            }                          //
      }

    goto wait_ev                     ; // Wait for the next event


/*****************************************************************************
 * Step 5 : Termination treatments                                           *
 * ------   - We stop the cyclic timer, using "clear_tto"                    *
 *          - We free the 6 GPIO sub-channel IOD's and close the driver      *
 *          - We free the 3 LED sub-channel IOD's and close the driver       *
 *          ---------------------------------------------------------------- *
 *          It has to be noted that we never execute those instructions      *
 *          because the loop at step 4 never exits. But here we have a       *
 *          didactic example and so we put the termination code.             *
 *****************************************************************************/

    clear_tto(idto)                  ; // Stop the timer
    close_gpio()                     ; // Close the GPIO driver
    close_led()                      ; // Close the LED driver

    goto start                       ; //

    return 0                         ;
  }


/*  Procedure open_gpio -------------------------------------------------------

    Purpose : Initialize the code of the GPIO driver module, then allocates
              the GPIO controller device, initializes its hardware and finally
              creates a user route in order to catch the IND_REPORT events.
              The input/output descriptor is kept in the global variable 
              "gpio_iod" for the GPIO controller and the sub-channels
              descriptors are gpio_iod1/2/3/4/5/6
*/

static void open_gpio(void)
  {

/*****************************************************************************
 * Step 1 : We call the "myio_open" that calls "drv_init_driver", then       *
 * ------   "add_uroute", then "drv_alloc_device" and "drv_open_device".     *
 *****************************************************************************/

    myio_open(DRVGPIO,DEV0,"",&gpio_iod); // Open GPIO\DEV0


/*****************************************************************************
 * Step 2 : The GPIO contoller is ready. We are going to allocate the pins   *
 * ------   subchannels, one for each button. We call the "drv_alloc_subchan"*
 *          procedure, this procedure sends a REQ_ALLOC_SUB event to the     *
 *          VMIO AUT_GPIO automaton. This automaton will then send us a      *
 *          RESP_ALLOC_SUB response event. Here we unschedule until this     *
 *          event is received. Buttons 1, 2, 3 have respectively connected   *
 *          to GPIO named  BUT0, BUT1 and BUT2.                              *
 *****************************************************************************/

    myio_alloc_sub(gpio_iod,"UNAME=GPIO1",&gpio_iods[0]); // Alloc GPIO 1
    myio_alloc_sub(gpio_iod,"UNAME=GPIO2",&gpio_iods[1]); // Alloc GPIO 2
    myio_alloc_sub(gpio_iod,"UNAME=GPIO3",&gpio_iods[2]); // Alloc GPIO 3
    myio_alloc_sub(gpio_iod,"UNAME=GPIO4",&gpio_iods[3]); // Alloc GPIO 4
    myio_alloc_sub(gpio_iod,"UNAME=GPIO5",&gpio_iods[4]); // Alloc GPIO 5
    myio_alloc_sub(gpio_iod,"UNAME=GPIO6",&gpio_iods[5]); // Alloc GPIO 6


/*****************************************************************************
 * Step  3 : Correctly configure the 6 GPIOs. We use the same taglist for    *
 * -------   all of them:                                                    *
 *                                                                           *
 *           "FUNC=GPIO\nOUTPUT=HI_Z\nWEAKPULL=DOWN\nEVENTS=REPORT"          *
 *                                                                           *
 *           Each row of the 6 GPIO connector is made of 3 pins, that are,   *
 *           from left to right, GND 3V3 and GPIO. The jumper is used to     *
 *           short-circuit 3V3 and the GPIO. So when the jumper is fitted,   *
 *           GPIO is 3V3. If we want to distinguish, the GPIO must be 0V     *
 *           when the jumper is not fitted and therfore we select a "down"   *
 *           weak resistor and so we have WEAKPULL=DOWN. If we use a jumper  *
 *           between GND and GPIO, we would have to select a weak pull-up,   *
 *           in such a case the taglist would be:                            *
 *                                                                           *
 *           "FUNC=GPIO\nOUTPUT=HI_Z\nWEAKPULL=UP\nEVENTS=REPORT"            *
 *                                                                           *
 *           The effect of OUTPUT=HI_Z is that the GPIO output logic is      *
 *           disabled, and so the GPIO is an input.                          *
 *****************************************************************************/

    myio_setval(gpio_iods[0],gpio_taglist) ; // Configure GPIO 1      
    myio_setval(gpio_iods[1],gpio_taglist) ; // Configure GPIO 2      
    myio_setval(gpio_iods[2],gpio_taglist) ; // Configure GPIO 3      
    myio_setval(gpio_iods[3],gpio_taglist) ; // Configure GPIO 4      
    myio_setval(gpio_iods[4],gpio_taglist) ; // Configure GPIO 5      
    myio_setval(gpio_iods[5],gpio_taglist) ; // Configure GPIO 6      
  }


/*  Procedure read_gpio -------------------------------------------------------

    Purpose : This procedure reads the state of one of the 6 GPIO's.
*/

static int  read_gpio(int n)
  {
    char            tagstr[80]       ; // Tag name then Tag+value
    int             val              ; // 0:LOW 1:HIGH

/*****************************************************************************
 * Step 1 : - we read the INPUT tag value. The "drv_getval" function, that   *
 *            is used by "myio_getval", take the name name on input, in our  *
 *            case "INPUT" and on output returns in the same string, here    *
 *            the "tagstr" local variable, the string "INPUT=HIGH" or        *
 *            "INPUT=LOW".                                                   *
 *          - We test the returned string value. If we have "INPUT=HIGH" we  *
 *            set "gpio_val[i]" to 1 else we clear it.                       *
 *****************************************************************************/

    strcpy(tagstr,"INPUT")           ; // Name of tag to be read

    myio_getval(gpio_iods[n] ,         // Sub-channel IOD
                tagstr       ,         // Input and output string
                sizeof(tagstr))      ; // String maximum size

    val = strcmp(tagstr,               // equal else a non 0 value
                 "INPUT=HIGH") ? 0     // tagstr <> "INPUT=HIGH" -> 0
                               : 1   ; // tagstr == "INPUT=HIGH" -> 1

    return val                   ;

  }


/*  Procedure close_gpio ------------------------------------------------------

    Purpose : This procedure closes the GPIO controller, then frees this
              device, terminates the GPIO module and deletes the route that
              had been created for the IND_REPORT events.
*/

static void close_gpio(void)
  {

/*****************************************************************************
 * Step 1 : Free all used subchannels. We call "myio_free_sub" that calls    *
 * ------   "drv_free_subchan" for each of the 3 buttons which IO descriptor *
 *          are "gpio_iod1/2/3". This procedure sends a REQ_FREE_SUB request *
 *          event to the VMIO AUT_GPIO automaton and here we unschedule      *
 *          until the corresponding RESP_FREE_SUB, sent by AUT_GPIO, is      *
 *          received.                                                        *
 *****************************************************************************/

    myio_free_sub(gpio_iods[0])      ; // Free GPIO 1
    myio_free_sub(gpio_iods[1])      ; // Free GPIO 2
    myio_free_sub(gpio_iods[2])      ; // Free GPIO 3
    myio_free_sub(gpio_iods[3])      ; // Free GPIO 4 
    myio_free_sub(gpio_iods[4])      ; // Free GPIO 5
    myio_free_sub(gpio_iods[5])      ; // Free GPIO 6


/*****************************************************************************
 * Step 2 : Close the GPIO device. We call the "myio_close" subroutine that  *
 * ------   calls "drv_close" device, the "drv_free" and then "drv_end".     *
 *          A REQ_CLOSE request event is sent to AUT_GPIO and we unschedule  *
 *          until RESP_CLOSE is received from AUT_GPIO. A REQ_FREE event is  *
 *          sent and RESP_FREE is awaited. And a REQ_END driver termination  *
 *          request is sent and we unschedule until RESP_END is received.    *
 *****************************************************************************/

    myio_close(gpio_iod)             ; // Closes the GPIO device and the driver
  }


/*  Procedure open_led --------------------------------------------------------

    Purpose : Initializes the LED driver, opens the LED controller, allocates
              the three LED (RED, GREEN, YELLOW) subchannels.
*/

static void open_led(void)
  {

/*****************************************************************************
 * Step 1 : We call the "myio_open" that calls "drv_init_driver", then       *
 * ------   "add_uroute", then "drv_alloc_device" and "drv_open_device".     *
 *          It has to be noted that the LED driver is a "software device",   *
 *          because there is no LED controller dedicated device. Most of the *
 *          time "hidden" GPIO (not visible by the application) are used.    *
 *          But other hardware may be used. For example a front panel will   *
 *          include a dedicated PCB and an I2C slave controller that drives  *
 *          and controls all the front-panel elements such as LEDS but also  *
 *          buttons, a 4x7 display, ... In such a case, the "drv_led" driver *
 *          will use low-level functions for I2C and not GPIO.               *
 *****************************************************************************/

    myio_open(DRVLED,DEV0,"",&led_iod)  ; // Open LED\DEV0


/*****************************************************************************
 * Step 2 : The contoller is ready. We are going to allocate the LED         *
 * ------   subchannels, one for each LED. We call the "myio_alloc_sub"      *
 *          subroutine that calls the "drv_alloc_subchan" procedure. This    *
 *          last procedure sends a REQ_ALLOC_SUB event to the VMIO AUT_LED   *
 *          automaton. This automaton will then send us a RESP_ALLOC_SUB     *
 *          response event. Here we unschedule until this event is received. *
 *****************************************************************************/

    myio_alloc_sub(led_iod,"LEDNAME=RED"    ,&led_iods[0]); // Allocates RED
    myio_alloc_sub(led_iod,"LEDNAME=GREEN"  ,&led_iods[1]); // Allocates GREEN
    myio_alloc_sub(led_iod,"LEDNAME=YELLOW" ,&led_iods[2]); // Allocates YELLOW
  }


/*  Procedure close_led -------------------------------------------------------

    Purpose : Closes the LED controller, then frees it and terminates the LED
              driver module.
*/

static void close_led(void)
  {

/*****************************************************************************
 * Step 1 : Free all used subchannels. We call "myio_free_sub" that calls    *
 * -------  "drv_free_subchan" for each of the 3 LEDs which IO descriptor    *
 *          are "led_iodr/g/y". This last procedure sends a REQ_FREE_SUB     *
 *          request event to the VMIO AUT_LED automaton and here we          *
 *          unschedule until the corresponding RESP_FREE_SUB, sent by        *
 *          AUT_LED, is received.                                            *
 *****************************************************************************/

    myio_free_sub(led_iods[0])       ; // Frees RED
    myio_free_sub(led_iods[1])       ; // Frees GREEN
    myio_free_sub(led_iods[2])       ; // Frees YELLOW


/*****************************************************************************
 * Step 2 : We call the "myio_close" procedure that calls "drv_close_device",*
 * ------   "drv_free", "drv_end" and "del_uroute".                          *
 *****************************************************************************/

    myio_close(led_iod)              ; // Closes LED device and driver
  }


/*  Procedure set_pattern_led -------------------------------------------------

    Purpose : Sert a new blinkink pattern for LED "n". The blinking pattern
              is selected using the gpio_val[2*n] + 2 * gpio_val[2*n+1]
              value.
*/

static void set_led_pattern(int n)
  {

    int             pat              ; // Pattern number

/*****************************************************************************
 * Step 1 : The LED driver has a stack of blinking patterns for each LED.    *
 * ------   Before pushing a new blinking pattern in the stack, we request   *
 *          to empty the blink pattern stack to the led driver. To do this,  *
 *          we send a REQ_SETVAL "CMD=POPALL" event to the LED driver, so we *
 *          call the "myio_setval" procedure that send the REQ_SETVAL event  *
 *          to the VMIO AUT_LED automaton. Then we ask to be unscheduled     *
 *          until the response event RESP_SETVAL is received.                *
 *****************************************************************************/

    myio_setval(led_iods[n],           // Always call "drv_setval" with
                led_tagpop)          ; // "CMD=POPALL"


/*****************************************************************************
 * Step 2 : Set the new blinking pattern. We have 4 different tag lists that *
 * ------   define 4 different blinking patterns:                            *
 *                                                                           *
 *          Pattern 0 is "PATTERN=OFF:0\nCMD=PUSH"                           *
 *          Pattern 1 is "PATTERN=ON:2,OFF:16\nCMD=PUSH"                     *
 *          Pattern 2 is "PATTERN=ON:2,OFF:8\nCMD=PUSH"                      *
 *          Pattern 3 is "PATTERN=ON:1,OFF:1\nCMD=PUSH"                      *
 *                                                                           *
 *          We use two GPIOs, so 4 possibilities, to select one of the 4     *
 *          patterns. If "n" is the led number (0 to 2) we use GPIOs 2*n and *
 *          2 * n + 1, we compute pat = gpio_val[2*n] + 2 * gpio_val[2*n+1]  *
 *          and then we use it as an index in the "led_blink[4]" table that  *
 *          holds the 4 string pattern addresses                             *
 *****************************************************************************/

    pat =     gpio_val[2*n]            // We use the current state og GPIOs
        + 2 * gpio_val[2*n+1]        ; // 2*n and 2*n+1 has a selector

    myio_setval(led_iods[n],           // Set pattern "led_blink[pat]"
                led_blink[pat] )     ; // for IOD "led_iods[n]"
  }