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 :

  1. testLCD.py qui contient quelques lignes qui utilisent l'afficheur

  2. i2c_lib.py trouvé sur internet

  3. 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 :

  1. GND

  2. VCC (à connecter sur le 5V

  3. SDA

  4. SCL

Et un joli dessin récupéré sur Internet pour le schéma de câblage.