A la découverte de son OS via l'embarquée : BBB & arm64

Ou comment mettre en pratique le cours Langage C

a marqué ce sujet comme résolu.

A la découverte de son OS : intéragir avec le monde externe

où comment via un PI (embarqué) mettre en pratique le Langage C

Présentation

Olivier, j’ai travaillé pendant très longtemps sur les couches infrastructures de l’informatique. Débutant en Langage C, mon domaine d’activitée portait uniquement sur la couche service proposée par les infrastructures d’un SI (Active Directory, SQL, messagerie, storage, backup…).

Idée du projet :
  • Intéragir avec le matériel Faire communiquer les matériels entre eux (et en second temps avec des arduinos…) Partager éventuellement cette expérience via un tutoriel ultérieurement.

Je ne suis pas développeur, ne l’ai jamais été, je fais du "Delivery Management" la qualité du code produit peut ne pas correspondre à l’état de l’art, meme si je souhaite d’ores et déjà produire un code propre. Vos conseils et remarques en ce sens seront les bienvenus. Merci.

Postulat de départ :
1. Plateforme Matérielle
  • arm64 : Raspberry PI3 / Rock64 (de pine 64)
  • armv7 : BeagleBone Black (rev C)
2. Opérating System
  • OpenBSD

n.b. OpenBSD mon choix. J’utilise ce système personellement depuis les années 2000. Il est simple et fonctionnel. Je sais qu’il existe des distributions linux dédiées pour piloter ces plateformes. Pas de polémique sur ce sujet s.v.p.

Sinon comment découvrir les entrailles de mon OS ? :D

Ma démarche sera donc empirique (non priorisée) :

  • Ecrire et Lire sur des pins digitaux
  • Intéragir avec un écran (7 segments / graphique)
  • Utiliser le BUS I2C
  • Utiliser le BUS IPC
  • Lire & écrire sur des pins analogiques

(…) Voilà assez de choses pour commencer… :diable:

3. Moyens techniques :
  • Les librairies en place sur l’OS : ioctl, gpio, syscall…
  • Les datasheets des matériels usités
  • Le cours de Lukas-84, Paraze, Taurre et Informaticienzero Langage C
  • Le K & R (le livre des auteurs du langage C)
  • Le cours de Olyte et Eskimon arduino-premiers-pas-en-informatique-embarquee

n.b. A savoir qu’à ce jour, je ne sais pas comment utiliser les pin ana / pwm.

+3 -0

Intéragir avec les "digital pins"

AXIOME : Sous Unix, tout est fichier.

Une fois l’OS installé (couverture non prévue à ce jour), j’ai découvert qu’il n’y avait pas de "fichiers" pour intéragir avec un PIN.

suis-je donc Marron ?

Pas de bras, pas de chocolat… On persévère !!!

Découverte de la commande système : gpioctl qui permet d’intéragir avec les pins.

Step: 1 activer les pins lors du boot au mode securelevel 0 (se référencer à la datasheet)

Editer : /etc/rc.securelevel

# Define the GPIO pin
# how to use it (direction)

##############################
#       Digital outputs      #
##############################

gpioctl gpio1 13 set out H8_11
gpioctl gpio1 12 set out H8_12
# Do not use H8_13
gpioctl gpio0 26 set out H8_14
gpioctl gpio1 15 set out H8_15
gpioctl gpio1 14 set out H8_16
gpioctl gpio0 27 set out H8_17
gpioctl gpio2 1  set out H8_18
gpioctl gpio1 29 set out H8_26

# LCD
gpioctl gpio2 23 set out H8_29 # to rs
gpioctl gpio2 25 set out H8_30 # to enable
gpioctl gpio2 12 set out H8_39 # data6
gpioctl gpio2 13 set out H8_40 # data7
gpioctl gpio2 10 set out H8_41 # data4
gpioctl gpio2 11 set out H8_42 # data5
gpioctl gpio2 8  set out H8_43 # data2
gpioctl gpio2 9  set out H8_44 # data3
gpioctl gpio2 6  set out H8_45 # data0
gpioctl gpio2 7  set out H8_46 # data1

##############################
#       Digital inputs       #
##############################

gpioctl gpio1 16 set in H9_15
gpioctl gpio1 17 set in H9_23
gpioctl gpio3 19 set in H9_27`

Step: 2 se baser sur le gpioctl.c fournit dans les sources pour :

  1. Comprendre comment accéder au filedescriptor
  2. Comprendre comment Ecrire / Lire sur un périphérique pris en compte par le système (Rappel : sur unix tout est fichier)
  3. Ecrire depuis ce fichier source "gpioctl.c" fournit par l’équipe OpenBSD, les fonctions à utiliser dans nos programmes ultérieures:
  • gpiofunctions.h
  • gpiofunctions.c
  • Un petit programme de test : k2k.c (to switch flash LED with a pushh pull button)

gpiofunctions.h

/* gpioFunctions.h, v1.0 2018/12/28 16:31:19    */
/* 
* Copyright(c) 2018 Olivier Burelli <olivier(at)burelli(dot)fr>
* 
* Based & FROM:
*   $OpenBSD: gpioctl.c,v 1.17 2015/12/26 20:52:03 mmcc Exp $
*
* Copyright (c) 2008 Marc Balmer <mbalmer(at)openbsd(dot)org>
* Copyright (c) 2004 Alexander Yurchenko <grange(at)openbsd(dot)org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

/*
* Functions to control GPIO devices.
*/

#ifndef GPIOFUNCTIONS_H
#define GPIOFUNCTIONS_H 

extern void digitalWrite(char *, char *, int);
extern int  digitalRead(char *, char *);

#endif

gpiofunctions.c - Source Updated, ajout de la function : openGpioDevice()

/* gpioFunctions.c, v1.1 2018/12/28 16:31:19 */
/*
* Copyright(c) 2018 Olivier Burelli <olivier@burelli.fr>
*
* Based & FROM:
*   $OpenBSD: gpioctl.c,v 1.17 2015/12/26 20:52:03 mmcc Exp $
*
* Copyright (c) 2008 Marc Balmer <mbalmer@openbsd.org>
* Copyright (c) 2004 Alexander Yurchenko <grange@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

/*
* Functions to control GPIO devices.
*/

#include <sys/types.h>
#include <sys/gpio.h>
#include <sys/ioctl.h>
#include <sys/limits.h>

#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <paths.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include "gpiofunctions.h"


void
digitalWrite(char *gpioId, char *gp_name, int action) {
    int devfd = -1;
    const char *errstr;
    struct gpio_pin_op op;

    /* mount & open the device dev : /dev/gpiox */
    openGpioDevice(gpioId, &devfd);

    /* Write the action */
    if (action < 0 || action > 2)
        errx(1, "%d: invalid action", action);
    memset(&op, 0, sizeof(op));

    op.gp_pin = strtonum(gp_name, 0, INT_MAX, &errstr);
    if (errstr) {
        if (gp_name != NULL)
        strlcpy(op.gp_name, gp_name, sizeof(op.gp_name));
        op.gp_value = (action == 0 ? GPIO_PIN_LOW : GPIO_PIN_HIGH);
    }

    if (action < 2) {
        ioctl(devfd, GPIOPINWRITE, &op);
    } else {
        ioctl(devfd, GPIOPINTOGGLE, &op);
    }

    /* To close file descriptor fd */
    close(devfd);

}

int
digitalRead(char *gpioId, char *gp_name) {
    int devfd = -1;
    struct gpio_pin_op op;

    /* mount & open the device dev : /dev/gpiox */
    openGpioDevice(gpioId, &devfd);

    /* Read the pin */
    memset(&op, 0, sizeof(op));
    if (gp_name != NULL)
        strlcpy(op.gp_name, gp_name, sizeof(op.gp_name));

    ioctl(devfd, GPIOPINREAD, &op);

    /* To close file descriptor */
    close(devfd);

    return op.gp_value;
}

int
openGpioDevice(char *gpioId, int *pdevfd) {
    char *dev;
    char devn[11];
    dev = gpioId;

    if (strncmp(_PATH_DEV, dev, sizeof(_PATH_DEV) - 1)) {
        (void)snprintf(devn, sizeof(devn), "%s%s", _PATH_DEV, dev);
        dev = devn;
    }
    *pdevfd = open(dev, O_RDWR);

    return 0;
}

k2k.c : Pour vérifier le tout… (schémas dessiné ultérieurement)

/* k2k.c, v1.0 2018/12/28 16:31:19 */
/* 
* Copyright(c) 2018 Olivier Burelli <olivier@burelli.fr>
* 
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

/*
* Flash LEDs
*/

#include <stdio.h>
#include <unistd.h>
#include "gpiofunctions.h"

int
main (void)
{
       int val = digitalRead("gpio1", "H9_15");
       
       while(val != 2)
       {
               val = digitalRead("gpio1", "H9_15");
       
               if(val == 1)
               {
                       digitalWrite("gpio1", "H8_11", 1);
                       usleep(200000);
                       digitalWrite("gpio1", "H8_11", 0);
                       digitalWrite("gpio1", "H8_12", 1);
                       usleep(200000);
                       digitalWrite("gpio1", "H8_12", 0);
                       digitalWrite("gpio0", "H8_14", 1);
                       usleep(200000);
                       digitalWrite("gpio0", "H8_14", 0);
                       digitalWrite("gpio1", "H8_12", 1);
                       usleep(200000);
                       digitalWrite("gpio1", "H8_12", 0);
               }
               else if(val == 0)
               {
                       digitalWrite("gpio1", "H8_15", 1);
                       usleep(150000);
                       digitalWrite("gpio1", "H8_15", 0);
                       digitalWrite("gpio1", "H8_16", 1);
                       usleep(150000);
                       digitalWrite("gpio1", "H8_16", 0);
                       digitalWrite("gpio0", "H8_17", 1);
                       usleep(150000);
                       digitalWrite("gpio0", "H8_17", 0);
                       digitalWrite("gpio1", "H8_16", 1);
                       usleep(150000);
                       digitalWrite("gpio1", "H8_16", 0);
               }
       }
       
       return 0;
} 

It works !

J’ai bien mérité un chocolat chaud :)

Explications / Difficultées rencontrées

Ici la lecture du source a été tres utile. A première lecture, il était difficile de comprends le source. Il fallait aussi comprendre la structure dans le fichier gpio.h qui fournit les indications pour affecter / lire un état électrique (haut ou bas).

Au final, la difficulté initiale était de comprendre le processus pour accéder à un périphérique.

L’appel system : ioctl nous permet de le faire :

  • Initiliser une variable devfd
  • Y affecter la chaine complete du device du pin souhaité
  • Affecter l’id du retour de la comande open du controleur gpio : /dev/gpio2 via un pointeur
  • Utiliser la déclaration op de la structure gpio_pin_op pour lire ou écrire une valeur (état 0 ou 1) via l’appel systèm IOCTL(device, action, structure). Au préalable, on a initialisé la structure "op" avec l’ID (H8-xx) déclaré dans /etc/rc.securelevel.
int
openGpioDevice(char *gpioId, int *pdevfd) {
    char *dev;
    char devn[11];
    dev = gpioId;

    if (strncmp(_PATH_DEV, dev, sizeof(_PATH_DEV) - 1)) {
        (void)snprintf(devn, sizeof(devn), "%s%s", _PATH_DEV, dev);
        dev = devn;
    }
    *pdevfd = open(dev, O_RDWR);

    return 0;
}
Que venons-nous de faire ?

Nous avons écrit une petite librairie de deux fonctions, LIRE et ECRIRE sur un pin gpio, qui vont nous permettre d’intéragir avec le monde extérieur.

+0 -0

Intéragir avec un LCD de type Hitachi HD44780 #1

HD44780 Datasheet OK

  1. Principe semble être acquis
  • Mode commande
  • Mode écriture
  • valeurs des commandes à passer demeurent flous
  1. A mettre en pratique en réutilisant la librairie de fonction précédente et ci-dessus.

Test effectué pour le 8 bit OK. Test KO avec le 4 bits.

Algorithme utilisé :

#include <stdio.h>

static void writeBits(u_int8_t bits);
static void write4bits(u_int8_t bits);

int main(void) {
       writeBits(0x41);
       writeBits(0x03);
       writeBits(137);
       writeBits(41);
       writeBits(0x3);

       return 0;
}

static void
writeBits(u_int8_t bits) {
       write4bits(bits >> 4);
       write4bits(bits & 0x0F);
       printf("\n");
}

static void write4bits(u_int8_t bits) {
       printf("%d", ((bits & 8)? 1 : 0));
       printf("%d", ((bits & 4)? 1 : 0));
       printf("%d", ((bits & 2)? 1 : 0));
       printf("%d ", ((bits & 1)? 1 : 0));
}
Explications / Difficultées rencontrées
  • Fournir depuis un nombre, une représentation binaire pour paralléliser l’envoie des données à l’afficheur. Soit en mode 8 fils, soit en mode 4 fils.
  • Comprendre et implémenter les instructions à envoyer à l’afficheur pour initialiser l’afficheur LCD en mode 4 ou 8 bits (fils)

La solution technique implémentée dans le code ci-dessus utilise l’opérateur de décalage '»' et l’opérateur bit à bit '&’.

En francais cela donne quoi ?

  • Un octet est égal à 8 bits de deux quartet (4 bits) qui forment un nombre héxadécimal 0xFF où F a pour corrrespondance décimale 15 (soit la valeur d’un quartet de 0 à 15)
  • Un quartet se represente pour une correspondance décimale / hexadécimale ainsi : 23 + 22 + 21 + 20 ; nos 4 bits !
  • L’idée ici est de faire une correspondance bit à bit hexadécimale de la valeur d’un quartet et de lui affecter un état haut ou bas (0 ou 1)
  • l’opérateur '&' effectue la comparaison de la translation d’un nombre au bit de poid fort : Est-ce que le nombre 14 est un composé mathématique de 2^3 (8) oui donc le bit de poids fort est à 1 il reste 6…
  • le décalage, lui permet de passer au bit suivant : Est-ce que le reste 6 de la comparaison est un composé de 2^2 (4) oui. etc etc

La sortie du programme ci-dessus, via un espace, sépare le quartet de poids fort de celui de poids faible à droite :

0100 0001

0000 0011

1000 1001

0010 1001

0000 0011

+0 -0

Intéragir avec un LCD de type Hitachi HD44780 #2

lcdfunctions.h Updated & OK

/* lcdfunctions.h, v1.2 2019/01/05 01:36:38 */
/* 
* Copyright(c) 2019 Olivier Burelli <olivier(at)burelli(dot)fr>
* 
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/

#include <sys/types.h>

#ifndef LCDFUNCTIONS_H
#define LCDFUNCTIONS_H

#define LENGTH 16+1         /* model used : DSN1602A (16x02) */
#define BIT_MODE    4
#define GPIOCTL "gpio2"
#define EN      "H8_30"
#define RS      "H8_29"
#define D0      "H8_45"     /* not used in 4bits mode */
#define D1      "H8_46"     /* not used in 4bits mode */
#define D2      "H8_43"     /* not used in 4bits mode */
#define D3      "H8_44"     /* not used in 4bits mode */
#define D4      "H8_41"
#define D5      "H8_42"
#define D6      "H8_39"
#define D7      "H8_40"

/* Instructions*/
#define CLEARDISPLAY    0x01
#define RETURNHOME      0x02
#define DISPLAYON       0x0C    /* low bit : 1, D = 1, Cursor, Blink */
#define ENTRYMODESET    0x06    /* low bit : 0, 1, I/D = 1, S = 0    */
#define DISPLAYOFF      0x08    /* low bit : 1, D = 0, Cursor, Blink */
/* FUNCTIONSET
* high bit 0001;
* low bit : DL = BIT_MODE, N=1 (2 lines, F = 0 (font 5x08), xx
*/
#define FUNCTIONSET     0x30
#define SETCGRAMADDR    0x40
#define SETDDRAMADDR    0x80

extern void setPinsDown();
extern void lcdDisplayClear();
extern void sendEnable();
extern void lcdDisplayOff();
extern void lcdDisplayOn();
extern void lcdDisplayClear();
extern void entryModeSet();
extern void initLCD();
extern void sendInst(u_int8_t);
extern void sendData(u_int8_t);

#endif

lcdfunctions.c Updated & OK

/* lcdfunctions.c, v1.2 2019/01/05 01:36:38 */
/* 
* Copyright(c) 2019 Olivier Burelli <olivier(at)burelli(dot)fr>
* 
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
#include <unistd.h>
#include "lcdfunctions.h"
#include "gpiofunctions.h"

static void modeData(u_int8_t m);
static void write4Bits(u_int8_t);
static void write8Bits(u_int8_t);
static void sendData4(u_int8_t);
static void sendData8(u_int8_t);
static void initLCD_4();
static void initLCD_8();

void
entryModeSet() {
    sendInst(ENTRYMODESET);
    usleep(40);
}

void
lcdDisplayClear() {
    sendInst(CLEARDISPLAY);
    usleep(40);
}

void
lcdDisplayOn() {
    sendInst(DISPLAYON);
    usleep(40);
}

void
lcdDisplayOff() {
    sendInst(DISPLAYOFF);
    usleep(40);
}

void
setPinsDown() {
    /* Set down Pins */
    digitalWrite(GPIOCTL, D0, 0);
    digitalWrite(GPIOCTL, D1, 0);
    digitalWrite(GPIOCTL, D2, 0);
    digitalWrite(GPIOCTL, D3, 0);
    digitalWrite(GPIOCTL, D4, 0);
    digitalWrite(GPIOCTL, D5, 0);
    digitalWrite(GPIOCTL, D6, 0);
    digitalWrite(GPIOCTL, D7, 0);
    digitalWrite(GPIOCTL, RS, 0);
}

void
sendEnable() {
    /* Send impulse to EN */
    digitalWrite(GPIOCTL, EN, 1);
    digitalWrite(GPIOCTL, EN, 0);
}

void
sendInst(u_int8_t instr) {
    modeData(0);
    if(BIT_MODE == 4)
        sendData4(instr);
    else if(BIT_MODE == 8)
        sendData8(instr);
}

void
sendData(u_int8_t data) {
    modeData(1);
    if(BIT_MODE == 4)
        sendData4(data);
    else if(BIT_MODE == 8)
        sendData8(data);
}

void
initLCD() {
    if(BIT_MODE == 4)
        initLCD_4();
    else if(BIT_MODE == 8)
        initLCD_8();
}

static void
sendData4(u_int8_t data) {
    /* High nibble */
    write4Bits(data >> 4);
    /* Low nibble */
    write4Bits(data & 0x0F);
}

static void
sendData8(u_int8_t data) {
    write8Bits(data);
}

static void
write4Bits(u_int8_t bits) {
    digitalWrite(GPIOCTL, D7, ((bits & 8) ? 1 : 0));
    digitalWrite(GPIOCTL, D6, ((bits & 4) ? 1 : 0));
    digitalWrite(GPIOCTL, D5, ((bits & 2) ? 1 : 0));
    digitalWrite(GPIOCTL, D4, ((bits & 1) ? 1 : 0));
    sendEnable();
}

static void
write8Bits(u_int8_t bits) {
    digitalWrite(GPIOCTL, D7, ((bits & 128) ? 1 : 0));
    digitalWrite(GPIOCTL, D6, ((bits &  64) ? 1 : 0));
    digitalWrite(GPIOCTL, D5, ((bits &  32) ? 1 : 0));
    digitalWrite(GPIOCTL, D4, ((bits &  16) ? 1 : 0));
    digitalWrite(GPIOCTL, D3, ((bits &   8) ? 1 : 0));
    digitalWrite(GPIOCTL, D2, ((bits &   4) ? 1 : 0));
    digitalWrite(GPIOCTL, D1, ((bits &   2) ? 1 : 0));
    digitalWrite(GPIOCTL, D0, ((bits &   1) ? 1 : 0));
    sendEnable();
}

static void
modeData(u_int8_t m) {
    digitalWrite(GPIOCTL, RS, m);
}

static void
initLCD_4() {
    /* Init process, step 1 : Wait 15 milliseconds & RS = 0*/
    usleep(15000);
    modeData(0);

    /* Init process, step 2 :
     * LCD is 8 bits - send function set #1
     */
    write4Bits(0x03);

    /* Wait 4.1 milliseconds */
    usleep(4100);

    /* init process, step 3 :
     * LCD is 8 bits - send function set #2
     */
    write4Bits(0x03);

    /* Wait 110 microseconds */
    usleep(100);

    /* Init process, step 4 :
     * LCD is 8bits - send function set #3
     */
    write4Bits(0x03);

    /* Init process, step 5
     * LCD is 8 bits - set mode to 4 bits
     */
    write4Bits(0x02);
    usleep(40);

    /* Init process, step 6 : Define Display Line & fonts
     * 001010xx : N = 1, 2 lines & F = 0, 5x8 font
     * LCD is 4 bits
     */
    sendInst(0x28);
    usleep(40);

    /* PROCESS, step 7 : Display On
     * LCD is 4 bits
     */
    lcdDisplayOn();

    /* PROCESS, step 8 : Display clear
     * LCD is 4 bits
     */
   lcdDisplayClear();

   /* PROCESS, step 9 : Entry mode set
    * LCD is 4 bits
    */
   entryModeSet();

   setPinsDown();
}

static void
initLCD_8() {
    /* PROCESS, step 1 : Wait 15 milliseconds */
    usleep(15000);

    /* PROCESS, step 2 :
     * Init mode 8bits - send function set #1
     */
    sendInst(FUNCTIONSET);

    /* Wait 4.1 milliseconds */
    usleep(4100);

    /* PROCESS, step 3 :
     * Init mode 8bits - send function set #2
     */
    sendInst(FUNCTIONSET);

    /* Wait 110 microseconds */
    usleep(100);
    /* PROCESS, step 3 :
     * Init mode 8bits - send function set #3
     */
    sendInst(FUNCTIONSET);

    /* PROCESS, step 4 :
     * Send function set + set lines & fonts
     * 1xxxYY ; DL = 1 : 8 bits & N = 1 : 2 lines  & F = 0 : fonts 8*5
     */
    sendInst(0x38);
    usleep(40);

    /* PROCESS, step 5 : send instruction display on
     * 11xx ; C = 0 : no cursor & B = 0 : no blinking cursor
     */
    lcdDisplayOn();

    /* PROCESS, step 6 : send instruction display clear
     * 11xx ; C = 0 : no cursor & B = 0 : no blinking cursor
     */
    lcdDisplayClear();

    /* PROCESS, step 7 : send instruction Entry Mode set */
    entryModeSet();
    
    setPinsDown();
}

Check : Lcd.c

#include "lcdfunctions.h"
#include "gpiofunctions.h"

int
main(void)
{
    char chaine[LENGTH] = "Hi OpenBSD @BBB!";
    
    initLCD();
    
    /* Write my Hello */
    for(int i=0; i < LENGTH-1 ; i++) {
        sendData(chaine[i]);
    }
     
    return 0;
}

Ggrrrrr, à force d’avoir la tête dans le guidon…

Il n’y avait pas d’erreur sur le mode 4 bit. La macro BIT_MODE doit aussi être changé… lors du changement de mode…

  • Mode 8 bit OK
  • Mode 4 bit OK
+0 -0

Intéragir avec un LCD de type Hitachi HD44780 #3

Etat des lieux :

  • Initialisation du LCD sur les Modes 4 bit & 8 bit OK
  • Fonction instruction et affichage OK

Next Steps :

  • Fonction positionnement cursor
  • Fonction défilement (optionnel)
  • Fonction créer new charactère
  • fonction insérer new charactère (8 possibles)

Objectifs :

Afficher la température d’un DS1631 (prochaine étape avec le bus iic), les informations systèmes…

Positionner le curseur

il existe des écrans lcd de type hitachi hd44780 8x01 16x02 20x04…

  • 8 charactères, 1 ligne
  • 16 charactères, 2 lignes
  • 20 charactères, 4 lignes

LENGHT est définie dans lcdfunctions.h

En parallèle la datasheet fournit les adresses de la ddram du LCD pour afficher un char, à une position donnée.

Difficultées : jongler avec les deux cas ci-dessous pour proposer une fonction quel que soit le type d’écran :

DataSheet - page 10, 11, 12

**Case 1:** When the number of display characters is less than 40 
×
 2 lines, the two lines are displayed
from the head. Note that the first line end address and the second line start address are not
consecutive. For example, when just the HD44780 is used, 8 characters 
×
 2 lines are displayed. See
Figure 5.

**Case 2:** For a 16-character 
×
 2-line display, the HD44780 can be extended using one 40-output
extension driver. See Figure 6.
When display shift operation is performed, the DDRAM address shifts. See Figure 6.

l’implémentation d’une fonction setCursor() ou scrollLeft() ou scrollDown() peut être interresant à implémenter.

Ces points vont passer en hold, jusqu’à ce que je sache quoi afficher :)

J’ai envie de tester la lecture de capteur BOSH BME280 ou DS1631 de dallas. J’ai commandé 4 petites mémoires eeprom 24LC256 pour essayer d’y stocker des informations.

J’ai surtout envie de jouer avec le bus I2C. :lol:

Choux Blanc pour le moment.

+0 -0

Status

Bonsoir tout le monde :ange:

Suite à quelques conseils, rendre le post plus lisible et interressant à lire, voici une refonte du post.

  • Imbrications des codes sources. dans la balise "secret".
  • Ajout de quelques explications sur les difficultées rencontrées

En parallèle, je viens de up une forge via gitea où sera déposé le projet. Un wiki détaillera les fonctions de chaque librairies.

Ce post me servira pour partager mes pérégrinations. Les codes sources seront publiés en second temps ici aussi… La balise "secret" étant un poil fastidieuse !

En espérant cette refonte soit plus plaisante à lire, si le sujet lui ne l’est pas :)

Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte