target icon

Purpose

This tutorial gives an example of using a I2C screen device, with LCD display and RGB color background.

list icon

Prerequisites

  • The Pyboard and the training daughter board from HyperPanel Lab.
  • A installation of an HyperpanelOS release already installed on a Linux PC, including tools for flashing and debugging. You need at least two USB ports on the computer (one for flashing and running, one for terminal).
  • The Olimex ARM-USB-OCD interface & cables.
  • A Grove-LCD RGB Backlight V4.0 device.

list icon

Software release

hypv9wr63 or higher.

align left icon

Description

  • The app gives an “how to use” example of an I2C device in “write” mode. The I2C driver available with HyperPanel OS support several controller/buses in parallel. In the case of the Pyboard, ther is only one bus.
  • The Grve-LCD RGB Backlight component include 2 different I2C devices: One is the LCD display, the other one is the RGB colored background.
  • The I2C driver support IO lists, including waits, conditionnal jumps and intricated loops. We use here some “waits” command for initialization sequence, according information of datasheets.
  • The driver provides two software interfaces in order to allow for both ease of use and versatility: Blocking and non blocking interfaces. We use here the blocking interface (i2c_write()).

Screen animation.

Code

hypos-tuto-217.c

/*
**  lcd_rgb.c - Sample code for HyperpanelOS ==============================  **
**                                                                           **
**  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 goal of this small app is to use a LCD-RGB I2C screen device.        **
**                                                                           **
**  =======================================================================  **
*/


/* Documentation for I2C devices --------------------------------------------

   (1) HD44780.pdf Hitachi Dot Matric Liquit Crystal Display Controller/Driver
   (2) PCA9633.pdf NXP 4-bit Fm+I2C-Bus LED driver - Product data sheet

*/
/* Include files and external reference -------------------------------------*/

#include <hypos.h>                     // Hyperpanel OS basic interfaces.    */
#include <drv_asy.h>                   // Prototype of "asy_write()".        */
#include <drv_i2c.h>                   // Prototype of "i2c_*()".            */


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

#define  TICK                 1000     // Code for tick event.

#define DUMMY_AD              0xFF     // I2C dev address - Dummy for wait.
#define LCD_AD                0x7c     // I2C dev address - LCD.
#define RGB_AD                0xc4     // I2C dev address - RGB.

#define RED                   0x04     // I2C register - RGB background color.
#define GREEN                 0x03     // I2C register - RGB background color.
#define BLUE                  0x02     // I2C register - RGB background color.

#define SCREEN_INIT              0     // I2C command - Screen initialisation
#define SET_LINE1                1     // I2C command - Cursor on line 1
#define SET_LINE2                2     // I2C command - Cursor on line 2


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

    static int      idto             ; // Timer identifier.


/* SCREEN_INIT command ......................................................*/

    static const char screen_init[]  = // I2C command - Screen initialisation
      {

/*    Length-1, Address, Control byte, Data byte                             */

      1, DUMMY_AD ,   50             , // I2C temporisation 50 ms

      2, LCD_AD   , 0x80 , 0x3C      , // LCD dev  : FUNCTION_SET try 1
                                       // Ctrl byte: 10000000 RS=0
                                       // Data byte: 00111100 2-line, on

      1, DUMMY_AD ,   40             , // I2C temporisation 40 ms

      2, LCD_AD   , 0x80 , 0x3C      , // LCD dev  : FUNCTION_SET try 2
                                       // Ctrl byte: 10000000 RS=0
                                       // Data byte: 00111100 2-line, on

      1, DUMMY_AD ,   40             , // I2C temporisation 40 ms

      2, LCD_AD   , 0x80 , 0x3C      , // LCD dev  : FUNCTION_SET try 3
                                       // Ctrl byte: 10000000 RS=0
                                       // Data byte: 00111100 2-line, on

      2, LCD_AD   , 0x80 , 0x0C      , // LCD dev  : DISPLAY_SWITCH
                                       // Ctrl byte: 10000000 RS=0
                                       // Data byte: 00001100 on,off,off

      1, DUMMY_AD ,    40            , // I2C temporisation more than 39us

      2, LCD_AD   , 0x80 , 0x01      , // LCD dev  : SCREEN_CLEAR
                                       // Ctrl byte: 10000000 RS=0
                                       // Data byte: 00000001 clear

      1, DUMMY_AD ,    2             , // I2C temporisation more than 1.53ms

      2, RGB_AD   , 0x00 , 0x00      , // RGB dev  : REG_MODE1, init
                                       // Register : 0x00
                                       // Data     : 0x00

      2, RGB_AD   , 0x08 , 0xFF      , // RGB dev  : REG_OUTPUT
                                       // Register : 0x08
                                       // Data     : 0xFF PWM & GRPPWM

      0, 0        , 0    , 0         , // End of command set
      }                              ; //

/* SET_LINE1 command ........................................................*/

    static const char set_line1[]    = // I2C command - Screen initialisation
      {

/*    Length-1, Address, Control byte, Data byte                             */

      2, LCD_AD   , 0x80 , 0x80      , // LCD dev  : CURSOR_RETURN
                                       // Ctrl byte: 10000000 RS=0
                                       // Data byte: 10000000 line 1, on

      0, 0        ,    0 ,    0      , // End of commands
      }                              ; //

/* SET_LINE2 command ........................................................*/

    static const char set_line2[]    = // I2C command - Screen initialisation
      {

/*    Length-1, Address, Control byte, Data byte                             */

      2, LCD_AD   , 0x80 , 0xC0      , // LCD dev  : CURSOR_RETURN
                                       // Ctrl byte: 10000000 RS=0
                                       // Data byte: 11000000 line 2, on

      0, 0        ,    0 ,    0      , // End of commands
      }                              ; //

    static char   *command[] =         // I2C commands table
      {                                //
        (char*)&screen_init[0]       , // I2C command - SCREEN_INIT
        (char*)&set_line1[0]         , // I2C command - SET_LINE1
        (char*)&set_line2[0]         , // I2C command - SET_LINE2
        (char*)0
      }                              ; //

    static const char *text[]        = // Messages on scree
      {
        "Hyperpanel   Red"           , // Message for "red" animation
        "Hyperpanel Green"           , // Message for "green" animation
        "Hyperpanel  Blue"           , // Message for "blue" animation
        "Hyperpanel White"           , // Message for "white" animation
      }                              ;


/*  Prototypes --------------------------------------------------------------*/

    static int  loop_app_task(void*) ; // Prototype
    static int  wait_evt(void)       ; // Prototype
    static void set_command(int)     ; // Prototype
    static void write_string(char*)  ; // Prototype
    static void set_color(int,int)   ; // Prototype


/*  Beginning of the code ---------------------------------------------------

    loop_app_tsk         Application entry point
    wait_evt             Wait for applicative events
    set_command          Write a command on the screen
    write_string         Write a character string on the screen
    set_color            Set a color level on the screen background
*/

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

    Purpose : This is our task main loop.
*/

int loop_app_tsk (void *param)
  {
    int             ev = TICK        ; // Our event
    int             curtime = 0      ; // Current time          
    char            mess[16]         ; // Message to be sent on ASY0
    char            col = -1         ; // Color
    short           lev = -4         ; // Level

/* Step 1 - Start a timer that will send an event every second ..............*/

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

/* Step 1 - LCD & RGB screen initialisation .................................*/

    set_command(SCREEN_INIT)         ; // LCD/RGB initialization sequence

/* Step 3 - Main loop .......................................................*/

    wait_ev :                          // Beginning of loop label
 
    if ( ev == TICK )                  // If the event is the tick event
      {                                //

        curtime ++                   ; // Time incrementation

/* Step 3.1 - Display timer on serial port and on screen ....................*/

        hsprintf(mess                , // Format de timer message.
          "   %02d:%02d:%02d:%01d"   , //             
          ((curtime/10)/3600)%24     , // Hour.
          ((curtime/10)/  60)%60     , // Minutes.
           (curtime/10)      %60     , // Seconds.
            curtime%10              ); // Thenths.

        if (!(curtime%10))             // Each second, send a trace.
          {                            //
            asy_write(0              , // Write on ASY0
              (unsigned char*)mess   , // the "mess" message
              strlen(mess)          ); // Count of bytes to be sent
            asy_write(0              , // Write on ASY0
              (unsigned char*)"\r\n" , // the "mess" message
              2                     ); // Count of bytes to be sent
          }

        set_command(SET_LINE2)       ; // Cursor on line 2.
        write_string(mess)           ; // Write current time on screen.
        set_command(SET_LINE1)       ; // Cursor on line 1.

/* Step 3.2 - Background color animation ....................................*/

        lev=(lev+4)%128              ; // Brightness level
        if (!(lev)) col=(col+1)%4    ; // Red/Green/Blue/White selection

        write_string(                  // Display title text on the screen.
               (char*)text[(int)col]);

        switch (col)
          {
            case 0 :
              set_color(RED  , lev)  ; // Set RED level
              set_color(GREEN,   0)  ; // Set GREEN level
              set_color(BLUE ,   0)  ; // Set BLUE level
              break                  ;

            case 1 :
              set_color(RED  ,   0)  ; // Set RED level
              set_color(GREEN, lev)  ; // Set GREEN level
              set_color(BLUE ,   0)  ; // Set BLUE level
              break                  ;

            case 2 :
              set_color(RED  ,   0)  ; // Set RED level
              set_color(GREEN,   0)  ; // Set GREEN level
              set_color(BLUE , lev)  ; // Set BLUE level
              break                  ;

            case 3 :
              set_color(RED  , lev)  ; // Set RED level
              set_color(GREEN, lev)  ; // Set GREEN level
              set_color(BLUE , lev)  ; // Set BLUE level
              break                  ;
          }
      }

    ev = wait_evt()                  ; // Unschedule until an event is received
    goto wait_ev                     ; // Wait for the next event

    return  0                        ; // Return code of the procedure 
  }


/*  Procedure wait_evt ------------------------------------------------------*/
/*
    Purpose : Unschedule until the next event is received, whatever it is.
*/

static int wait_evt (void)
  {
    int             waitlist[1][3]   ; // Parameter of "waitevt_task"
    int             ret              ; // Return code for "waiyevt_task"

/*****************************************************************************
 * Step 1 : Build a list with one WAIT_CODEINT entry that will accept all    *
 * ------   the event codes ranging from 0 to 20000. Then call               *
 *          "waitevt_task", we will be unscheduled until the next event will *
 *          be received                                                      *
 *****************************************************************************/

    waitlist[0][0]  = WAIT_CODEINT   ; // All events with
    waitlist[0][1]  = 0              ; // a code between 0
    waitlist[0][2]  = 20000          ; // and 20000

    waitevt_task(waitlist ,            // Address of waiting list
                 1        ,            // Size of "waitlist[]"
                 0        ,            // maximum waiting time = no
                 0        ,            // Do not purge previous events
                 &ret       )        ; // Return code

/*****************************************************************************
 * Step 2 : Here we are scheduled again. The VMK has written into its        *
 * ------   global variable "task_evt" a copy of the event that has          *
 *          scheduled us again.                                              *
 *****************************************************************************/

    return task_evt.code             ; // Return event code
  }


/*  Procedure set_command ---------------------------------------------------*/
/*
    Purpose : Send a set of commands to I2C devices.
*/

static void set_command(int cmd)
  {
    i2c_write(
      0                           ,    // Controller number
     -1                           ,    // Target I2C write address
      (unsigned char*)command[cmd],    // Command
      0                          );    // List of option flags
  }


/*  Procedure write_string --------------------------------------------------*/
/*
    Purpose : write a character string on the screen.
*/

static void write_string(char *s)
  {
    int             of = 0           ; // Offset for I2C frame building
    unsigned char   buf[32]          ; // Message to be sent on I2C

    memset(buf  ,strlen(s)+1,1);of++ ; // Lenght -1 ot the message
    memset(buf+of, LCD_AD   ,1);of++ ; // write address 01111100
    memset(buf+of, 0x40     ,1);of++ ; // Control Byte 01000000 (RS=1)
    memcpy(buf+of,s,strlen(s) )      ; //
    of=of+strlen(s)                  ; //
    memset(buf+of,0,1)               ; // End of frame

    i2c_write(0,-1,buf,0)            ; // Send I2C commands.
  }


/*  Procedure set_color -----------------------------------------------------*/
/*
    Purpose : Set a color level on RGB background
*/

static void set_color(int color, int level)
  {
    int             of = 0           ; // Offset for I2C frame building
    unsigned char   buf[8]           ; // Message to be sent on I2C

    memset(buf   ,    2  , 1); of++  ; // Lenght -1 ot the message
    memset(buf+of, RGB_AD, 1); of++  ; // write address
    memset(buf+of, color , 1); of++  ; // REG_RED  
    memset(buf+of, level , 1); of++  ; // 
    memset(buf+of, 0,      1)        ; // End of frame

    i2c_write(0,-1,buf,0)            ; // Send I2C commands.
  }