Pi400 Clock
Afficheur LCD Pi400
Avec l'arrivée de la Pi400, un ordinateur contenu dans le clavier, j'ai fait des infidélités au système d'exploitation de la fondation pour installer Ubuntu et profiter d'un OS 64 bits. Mais avec Ubuntu l'utilisation d'un afficheur LCD est un peu différente de ce que j'avais pu expérimenter précédemment sur la Pi.
En attendant de plus gros projets, voici comment faire une horloge avec un afficheur LCD à deux balles pour la Raspberry Pi400.
J'avoue avoir été un peu flemmard et m'être inspiré pour l'installation du SMBUS de cette page.
En avant
C'est un article un peu technique, donc il n'y aura pas beaucoup d'explications, je suppose que celui qui expérimente cela avec sa Raspberry Pi dispose déjà d'une petite expérience sous Linux.
Allez hop, on installe le SMBUS...
sudo apt install -y i2c-tools python3-pip
sudo pip3 install smbus2
sudo i2cdetect -y 1
Permet de connaître l'adresse de l'afficheur connecté sur le port I2C. On en aura besoin tout à l'heure.
Codium sudo ?
J'utilise Codium sous Ubuntu pour écrire le code python. Le code a besoin d'avoir les supers pouvoirs pour utiliser l'I2C. Comment déboguer en sudo ?
Pour exécuter Codium en mode sudo, ajouter dans le launch.json le paramètre suivant :
"sudo":true
Le code du projet
Le code du projet est composé de trois fichiers :
testLCD.py qui contient quelques lignes qui utilisent l'afficheur
i2c_lib.py trouvé sur internet
lcddriver.py trouvé aussi sur internet
J'ai un peu modifié le code du lcddriver mais c'est sans importance. Il ne faut pas oublier, dans ce fichier, d'indiquer l'adresse de l'afficheur (voir ci-dessus).
Le code n'a aucune prétention si ce n'est de servir de début à un autre projet qui aboutira ou pas, nous verrons bien.
testLCD.py
"""
A test for the I2C LCD
RC 2020-01-15
"""
import lcddriver
from datetime import datetime
from time import sleep
lcd = lcddriver.lcd()
lcd.lcd_clear()
while True:
today = datetime.today()
lcd.lcd_display_string(today.strftime("%d/%m/%Y"), 1)
lcd.lcd_display_string(today.strftime("%H:%M:%S"), 2)
sleep(.8)
i2c_lib.py
import smbus2
from time import *
class i2c_device:
def __init__(self, addr, port=1):
self.addr = addr
self.bus = smbus2.SMBus(port)
# Write a single command
def write_cmd(self, cmd):
self.bus.write_byte(self.addr, cmd)
sleep(0.0001)
# Write a command and argument
def write_cmd_arg(self, cmd, data):
self.bus.write_byte_data(self.addr, cmd, data)
sleep(0.0001)
# Write a block of data
def write_block_data(self, cmd, data):
self.bus.write_block_data(self.addr, cmd, data)
sleep(0.0001)
# Read a single byte
def read(self):
return self.bus.read_byte(self.addr)
# Read
def read_data(self, cmd):
return self.bus.read_byte_data(self.addr, cmd)
# Read a block of data
def read_block_data(self, cmd):
return self.bus.read_block_data(self.addr, cmd)
lcddriver.py
"""
LCD driver.
- Do not forget to instantiate with the address value using sudo i2cdetect -y 1
"""
import i2c_lib
from time import sleep
# LCD Address
# ADDRESS = 0x3f
# commands
LCD_CLEARDISPLAY = 0x01
LCD_RETURNHOME = 0x02
LCD_ENTRYMODESET = 0x04
LCD_DISPLAYCONTROL = 0x08
LCD_CURSORSHIFT = 0x10
LCD_FUNCTIONSET = 0x20
LCD_SETCGRAMADDR = 0x40
LCD_SETDDRAMADDR = 0x80
# flags for display entry mode
LCD_ENTRYRIGHT = 0x00
LCD_ENTRYLEFT = 0x02
LCD_ENTRYSHIFTINCREMENT = 0x01
LCD_ENTRYSHIFTDECREMENT = 0x00
# flags for display on/off control
LCD_DISPLAYON = 0x04
LCD_DISPLAYOFF = 0x00
LCD_CURSORON = 0x02
LCD_CURSOROFF = 0x00
LCD_BLINKON = 0x01
LCD_BLINKOFF = 0x00
# flags for display/cursor shift
LCD_DISPLAYMOVE = 0x08
LCD_CURSORMOVE = 0x00
LCD_MOVERIGHT = 0x04
LCD_MOVELEFT = 0x00
# flags for function set
LCD_8BITMODE = 0x10
LCD_4BITMODE = 0x00
LCD_2LINE = 0x08
LCD_1LINE = 0x00
LCD_5x10DOTS = 0x04
LCD_5x8DOTS = 0x00
# flags for backlight control
LCD_BACKLIGHT = 0x08
LCD_NOBACKLIGHT = 0x00
En = 0b00000100 # Enable bit
Rw = 0b00000010 # Read/Write bit
Rs = 0b00000001 # Register select bit
class lcd:
#initializes objects and lcd
def __init__(self, address:int = 0x3f) -> None:
"""
Input
- address: int, the LCD address in hex format, grabbed by sudo i2cdetect -y 1, ie 0x3f or 0x27...
Ouput
- None
"""
self.lcd_device = i2c_lib.i2c_device(address)
self.lcd_write(0x03)
self.lcd_write(0x03)
self.lcd_write(0x03)
self.lcd_write(0x02)
self.lcd_write(LCD_FUNCTIONSET | LCD_2LINE | LCD_5x8DOTS | LCD_4BITMODE)
self.lcd_write(LCD_DISPLAYCONTROL | LCD_DISPLAYON)
self.lcd_write(LCD_CLEARDISPLAY)
self.lcd_write(LCD_ENTRYMODESET | LCD_ENTRYLEFT)
sleep(0.2)
# clocks EN to latch command
def lcd_strobe(self, data):
self.lcd_device.write_cmd(data | En | LCD_BACKLIGHT)
sleep(.0005)
self.lcd_device.write_cmd(((data & ~En) | LCD_BACKLIGHT))
sleep(.0001)
def lcd_write_four_bits(self, data):
self.lcd_device.write_cmd(data | LCD_BACKLIGHT)
self.lcd_strobe(data)
# write a command to lcd
def lcd_write(self, cmd, mode=0):
self.lcd_write_four_bits(mode | (cmd & 0xF0))
self.lcd_write_four_bits(mode | ((cmd << 4) & 0xF0))
# put string function
def lcd_display_string(self, string, line):
if line == 1:
self.lcd_write(0x80)
if line == 2:
self.lcd_write(0xC0)
if line == 3:
self.lcd_write(0x94)
if line == 4:
self.lcd_write(0xD4)
for char in string:
self.lcd_write(ord(char), Rs)
# clear lcd and set to home
def lcd_clear(self):
self.lcd_write(LCD_CLEARDISPLAY)
self.lcd_write(LCD_RETURNHOME)
Un exemple d'afficheur 2x16 (deux lignes de 16 caratères) avec au dos son module I2C. Il existe aussi des afficheurs 4x20.
Attention, l'afficheur existe sans son module I2C et il est pénible à utiliser parce qu'il faut souder 80 fils (j'exagère à peine) sur les 200 broches GPIO de la Rasp pour le faire fonctionner.
L'afficheur dispose de ' broches :
GND
VCC (à connecter sur le 5V
SDA
SCL
Et un joli dessin récupéré sur Internet pour le schéma de câblage.