Outils pour utilisateurs

Outils du site


tutoriel:oliv:arm-langage-c-iot

Langage C & IoT

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

Cet article est la reproduction d'un article précédemment écrit : Ici

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é portait uniquement sur la couche service proposée par les infrastructures d'un SI (Active Directory, SQL, messagerie, storage, backup…).

Idée du projet :

  • Interagir 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, même 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 / RockPro64 (de pine 64) [gpio non opérationnel]
  • armv7 : BeagleBone Black (rev C)

2. Système d'exploitation

  • OpenBSD

n.b. OpenBSD mon choix. J'utilise ce système personnellement 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) :

  • Écrire et Lire sur des pins digitaux
  • Interagir 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… LOL

3. Moyens techniques :

  • Les bibliothèques 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)

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

Interagir 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 interagir 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

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

Éditer : /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 descripteur de fichiers
  2. Comprendre comment écrire / lire sur un périphérique pris en compte par le système (Rappel : sur Unix, tout est fichier)
  3. Écrire 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 push 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(at)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és ultérieurement)

/* k2k.c, v1.0 2018/12/28 16:31:19 */
/* 
 * Copyright(c) 2018 Olivier Burelli <olivier(at)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é très utile. À 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 système ''ioctl'' nous permet de le faire !

  1. Initiliser une variable devfd
  2. Lui affecter la chaine complete du device du pin souhaité
  3. Affecter l'id du retour de la commande open du controleur gpio : /dev/gpio2 via un pointeur
  4. 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ème 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 bibliothèqe de deux fonctions, LIRE et ECRIRE sur un pin gpio, qui vont nous permettre d'interagir avec le monde extérieur.

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

Objectif : envoyer des mots de 4 ou 8 bits.

Prérequis :

1. Appréhension du datasheet HD44780

OK

2. Acquisition des principes présentés acquis

  • Mode commande
  • Mode écriture
  • Valeurs des commandes à passer (qui à dit flou ?)

3. À mettre en pratique en réutilisant la librairie de fonctions ci-dessus

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

Code envisagé :

#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'envoi 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 0xFFF a pour corrrespondance décimale 15 (soit la valeur d'un quartet de 0 à 15)
  • Un quartet se représente pour une correspondance décimale / hexadécimale ainsi : 2^3 + 2^2 + 2^1 + 2^0 ; 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 code ci-dessus, via un espace, sépare le quartet de poids fort de celui de poids faible à droite :

0x41  >>  0100 0001 

0x03  >>  0000 0011 

 137  >>  1000 1001 

  41  >>  0010 1001 

 0x3  >>  0000 0011

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ée… lors du changement de mode…

  • Mode 8 bit OK
  • Mode 4 bit OK

Interagir 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

Prochaines étapes :

  • Fonction positionnement cursor
  • Fonction défilement (optionnel)
  • Fonction créer nouveau charactère
  • Fonction insérer nouveau 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 8×01 16×02 20×04…

  • 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(), scrollLeft() ou scrollDown() peut être interessant à étudier.

STATUS :

  • Aucune donnée à afficher :)
  • En possession de matériel : capteur BOSH BME280, DS1621 & DS1631, eeprom 24LC256 (non supporté !)
  • Appréhension, impossible actuellement, pour moi de jouer avec le bus I2C. 8-o

Choux Blanc pour le moment FIXME


Contribut(rice|eur)s :

oliv pengouinpdt
tutoriel/oliv/arm-langage-c-iot.txt · Dernière modification: 2020/04/12 14:35 de pengouinpdt