Cet article est la reproduction d'un article précédemment écrit : Ici
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…).
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.
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) :
(…) Voilà assez de choses pour commencer…
ioctl
, gpio
, syscall
…n.b. A savoir qu'à ce jour, je ne sais pas comment utiliser les pin ana / pwm.
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 :
gpioctl.c
fournit par l'équipe OpenBSD, les fonctions à utiliser dans nos programmes ultérieures: gpiofunctions.h
gpiofunctions.c
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 :)
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 !
devfd
/dev/gpio2
via un pointeurop
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; }
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.
Objectif : envoyer des mots de 4 ou 8 bits.
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)); }
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 ?
0xFF
où F
a pour corrrespondance décimale 15 (soit la valeur d'un quartet de 0 à 15)2^3 + 2^2 + 2^1 + 2^0
; nos 4 bits !&
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…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
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…
Il existe des écrans lcd de type hitachi hd44780 8×01 16×02 20×04…
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 :
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.
Choux Blanc pour le moment
Contribut(rice|eur)s :