Laboratoire 3
Les notions abordées dans ce laboratoire sont :
Mathématiques :
l’algèbre booléenne,
les tables de vérité,
le système binaire de représentation des nombres.
Physique :
l’électronique numérique,
le décodage,
le multiplexage.
Programmation Python :
l’utilisation de la librairie numpy,
la manipulation de matrices,
l’utilisation des « pygame.EVENT ».
Programme 4: Afficheur 7 segments simple
Cette première partie a pour but de vous familiariser avec la notion de table de vérité. Nous allons apprendre à manipuler des signaux électriques binaires (0 ou 1) à travers des circuits combinatoires. Le but est de représenter des nombres binaires sur un afficheur 7 segments. Nous apprendrons aussi comment traiter des signaux reçus en entrée par l’intermédiaire d’un bouton-poussoir.
Les tables de vérité
Tout circuit combinatoire (simple combinaison de signaux d’entrée => pas de mémoire dans le circuit), aussi complexe qu’il soit, peut être représenté par une table de vérité. Elle donne les valeurs des signaux de sortie en fonction des signaux d’entrée. Cela permet de traiter le circuit comme une « boite noire » sans devoir se préocuper de sa réelle composition.
Prenons par exemple les tables de vérité des portes logiques ET et OU.
Chacune des portes logiques prend en entrée deux signaux et renvoie un seul signal. Ces portes se comportent exactement comme les fonctions « and » et « or » que vous connaissez déjà en python. Cependant il s’agit bien ici de réels composants élétroniques.
Comme vous pouvez le constater les deux colonnes de gauche représentent toutes
les combinaisons d’entrées possibles. On peut aussi considérer qu’il
s’agit d’une numérotation des entrées en nombres binaires ([0 0]
correspond
à l’entrée 0
, [0 1] -> 1
, [1 0] -> 2
, [1 1] -> 3
).
Vous pouvez consulter ici un rappel sur les nombres binaires.
Programme de base
Voici un programme qui vous permettra d’afficher les différents compasants élétroniques que nous allons étudier: un Arduino, un décodeur CD4511, un afficheur 7 segments et un bouton-poussoir.
# ------------------------------------------------------------------------
# Laboratoires de programmation mathématique et physique 2
# ------------------------------------------------------------------------
#
# Programme : 7 segments.
#
# ------------------------------------------------------------------------
import math
import pygame
import sys
import numpy as np
### Constante(s)
NOIR = (0, 0, 0)
GRIS = (200, 200, 200)
ROUGE = (255, 0, 0)
### Variables Globales
def dessiner_arduino(sortie_arduino, sortie_CD4511, sortie_bouton):
fenetre.blit(image_arduino, pos_arduino)
fenetre.blit(image_CD4511, pos_CD4511)
fenetre.blit(image_bouton, pos_bouton)
off_ard = 194
off_cd = 15
for i in range(0, 4):
if sortie_arduino[i] == 0:
couleur = NOIR
else:
couleur = ROUGE
pygame.draw.line(fenetre, couleur, (pos_arduino[0] + 280, pos_arduino[1] + off_ard),
(pos_CD4511[0] + 7, pos_CD4511[1] + off_cd), 5)
off_ard = off_ard + 14
off_cd = off_cd + 19
off_cd = 15
off_aff = 27
for i in range(0, 7):
if sortie_CD4511[i] == 0:
couleur = NOIR
else:
couleur = ROUGE
pygame.draw.line(fenetre, couleur, (pos_afficheur[0], pos_afficheur[1] + off_aff),
(pos_CD4511[0] + 102, pos_CD4511[1] + off_cd), 5)
off_aff = off_aff + 19
off_cd = off_cd + 19
connexion_bouton(sortie_bouton)
def dessiner_afficheur(sortie_CD4511):
positions_barres = [[32, 14], [89, 20], [87, 88], [28, 150],
[17, 88], [19, 20], [30, 82]]
fenetre.blit(image_afficheur, pos_afficheur)
i = 0
for barre in positions_barres:
if sortie_CD4511[i] == 0:
i = i + 1
continue
x_b = pos_afficheur[0] + int(round(barre[0]*(image_afficheur.get_width()/133)))
y_b = pos_afficheur[1] + int(round(barre[1]*(image_afficheur.get_height()/192)))
if i == 0 or i == 3 or i == 6:
fenetre.blit(barre_horizontale, (x_b, y_b))
else:
fenetre.blit(barre_verticale, (x_b, y_b))
i = i + 1
return
def composant_CD4511(entree):
return np.array([0, 0, 0, 0, 0, 0, 0])
def sortie_memorisee():
return np.array([0, 0, 0, 0])
def gerer_click():
return 0
def connexion_bouton(sortie_bouton):
return
### Paramètre(s)
dimensions_fenetre = (1100, 600) # en pixels
images_par_seconde = 25
pos_arduino = (65, 84)
pos_CD4511 = (537, 263)
pos_afficheur = (818, 251)
pos_bouton = (537, 486)
pos_centre_bouton = (589, 521)
rayon_bouton = 18
pin_arduino = (pos_arduino[0] + 279, pos_arduino[1] + 353)
pin_bouton = (pos_bouton[0] + 13, pos_bouton[1] + 13)
### Programme
# Initialisation
pygame.init()
fenetre = pygame.display.set_mode(dimensions_fenetre)
pygame.display.set_caption("Programme 7 segments")
horloge = pygame.time.Clock()
image_afficheur_s = pygame.image.load('images/7_seg_s.png').convert_alpha(fenetre)
barre_verticale_s = pygame.image.load('images/vertical_s.png').convert_alpha(fenetre)
barre_horizontale_s = pygame.image.load('images/horizontal_s.png').convert_alpha(fenetre)
image_afficheur = pygame.image.load('images/7_seg.png').convert_alpha(fenetre)
barre_verticale = pygame.image.load('images/vertical.png').convert_alpha(fenetre)
barre_horizontale = pygame.image.load('images/horizontal.png').convert_alpha(fenetre)
image_arduino = pygame.image.load('images/arduino.png').convert_alpha(fenetre)
image_CD4511 = pygame.image.load('images/CD4511.png').convert_alpha(fenetre)
image_CD4028 = pygame.image.load('images/CD4028.png').convert_alpha(fenetre)
image_bouton = pygame.image.load('images/bouton.png').convert_alpha(fenetre)
couleur_fond = GRIS
# Boucle principale
while True:
temps_maintenant = pygame.time.get_ticks()
for evenement in pygame.event.get():
if evenement.type == pygame.QUIT:
pygame.quit()
sys.exit()
sortie_bouton = 0
fenetre.fill(couleur_fond)
sortie_CD4511 = composant_CD4511(sortie_memorisee())
dessiner_afficheur(sortie_CD4511)
dessiner_arduino(sortie_memorisee(), sortie_CD4511, sortie_bouton)
pygame.display.flip()
horloge.tick(images_par_seconde)
Recopiez ce programme dans un fichier appelé prog-4.py
, et
vérifiez qu’il fonctionne correctement.
L’Arduino
L’Arduino est une plaquette programmable permettant de mémoriser des valeurs et d’éffectuer des opérations. Dans le cadre de cette simulation, nous allons l’utiliser pour générer des entrées binaires dans notre circuit.
Créez une variable globale valeur_memorisee
qui sera la valeur à afficher sur
le 7 segments. Initialisez cette valeur à un nombre arbitraire entre 0 et 9.
Ensuite, transformez cette variable décimale en une liste de 4 valeurs binaires,
utilisez le formalisme gros boutiste
(les bits avec le plus de poids sont aux petites adresses, exemple: 7 devient [0 1 1 1]
).
Indice: utiliser la division entière et le reste de la division par 2.
Procédure à suivre:
Créer et initialiser
valeur_memorisee
.Compléter la fonction
sortie_memorisee()
afin qu’elle retourne le nombre binaire correspondant àvaleur_memorisee
.Note: utiliser les
numpy.array
comme dans le programme de base.Vérifier votre fonction en testant différentes valeurs entre 0 et 9. Par exemple, pour 7 vous devriez obtenir ceci:
Le décodeur CD4511
Le décodeur est un circuit combinatoire que l’on connecte directement à la sortie de l’aduino. Son rôle est de transformer les signaux représentant des nombres binaires en signaux iterprétables par l’afficheur 7 segments. Il a donc 4 entrées et 7 sorties correspondant à chaque led de l’afficheur. Ces sorties sont étiquetées de « a » à « g » et correspondent au schéma suivant :
Pour afficher le nombre 7 il faut donc allumer les led « a », « b » et « c ». La sortie du
décodeur sera le vecteur [1 1 1 0 0 0 0] -> [a b c d e f g]
.
Determinez la table de vérité de ce composant: combien de lignes contient elle ? Sont elles
toutes utiles pour notre application ?
Procédure à suivre:
Compléter la fonction
composant_CD4511(entree)
. Créer une variabletdv
et y placer une matrice numpy représentant la table de vérité du composant. Dans cette matrice, placer les lignes de sortie de la table, les valeurs d’entrées correspondent à l’indice de la ligne. Par exemple pour récupérer la sortie correspondant à l’entrée[0 1 1 1]
on retourneratdv[7]
.Note: exemple de matrice numpy:
np.array([[1, 2], [3, 4]])
Transformer l’entrée (vecteur représentant un nombre binaire) en un nombre décimal afin de l’utiliser comme indice de la matrice.
Retourner la bonne ligne de la table de vérité et vérifier, pour plusieurs
valeur_memorisee
, que l’afficheur 7 segments affiche la bonne valeur.
Le bouton-poussoir
Le principe du bouton-poussoir est simple : si l’on appuie sur le bouton un signal
1 est envoyé à l’arduino sinon c’est un signal 0. Le but de cette partie du programme
est de modifier la valeur_mermorisee
en fonction du signal du bouton.
Procédure à suivre :
Connecter le bouton à l’arduino en complétant la fonction
connexion_bouton(sortie_bouton)
. Tracer simplement une ligne noire entrepin_arduino
etpin_bouton
, si lasortie_bouton
vaut 1 alors la ligne doit devenir rouge.Dans la boucle principale vérifier si l’utilisateur est en train de cliquer. Utiliser
pygame.mouse.get_pressed()
.Vérifier si le clic se situe bien sur le bouton. Utiliser
pos_centre_bouton
etrayon_bouton
. Si c’est le cas, mettre à joursortie_bouton
.Lancer le programme et observer que votre signal correspond à la descrition d’un bouton-poussoir.
Repérer un clic, si le signal du bouton passe de 0 à 1, incrémenter une fois la
valeur_mermorisee
. La valeur ne doit pas être continuellement incrémentée lorsqu’on laisse le bouton enfoncé, mais juste au moment du clic.Vérifier votre implémentation.
Le signal d’horloge
En électronique digitale, on utilise très souvent des signaux périodiques appelés signaux d’horloge
afin de synchroniser différents évenements. Il s’agit d’un signal oscillant entre 0 et 1
à une fréquence fixée. Créez un signal d’horloge qui incrémente valeur_mermorisee
toutes les
secondes.
Procédure à suivre:
Dans l’initialisation du programme, créer un
pygame.USEREVENT
en utilisantpygame.time.set_timer(pygame.USEREVENT, temps)
où
temps
est le délai entre deux évenements. Pour repérer l’événement utilisé, dans votre boucle principaleevenement.type == pygame.USEREVENT
Créer une variable
sig_horloge
qui varie entre 0 et 1 toutes les0.5
secondes.Quand
sig_horloge
passe de 0 à 1 (flanc montant de l’horloge) incrémentervaleur_mermorisee
.Afficher un cercle au niveau de l’afficheur 7 segments (utiliser
pos_afficheur
). Ce cercle sera rouge sisig_horloge
vaut 1, noir sinon.
Programme 5: Multiplexage des 7 segments
Nous allons maintenant afficher 6 valeurs différentes sur un ensemble d’afficheurs. Au premier abord, nous pourrions tenter d’utiliser 6 fois 4 sorties de l’arduino avec 6 décodeurs CD4511. Cependant, cette solution n’est pas acceptable, le nombre de sortie de l’arduino est limité à 13 et nous devrions utiliser un nombre excessif de composants. La solution: le multiplexage. Au lieu d’allumer tous les afficheurs en même temps, nous allons en allumer un à la fois. Il suffit ensuite de passer d’un afficheur à l’autre suffisamment rapidement pour créer l’illusion qu’ils sont tous allumés en même temps (persitance rétinienne).
Suite du programme
Remplacer les deux fonctions suivantes :
def dessiner_arduino(sortie_arduino, sortie_CD4511, sortie_CD4028, sortie_bouton): fenetre.blit(image_arduino, pos_arduino) fenetre.blit(image_CD4511, pos_CD4511) fenetre.blit(image_bouton, pos_bouton) fenetre.blit(image_CD4028, pos_CD4028) for j in range(0, 2): if j == 0: off_ard = 285 off_cd = 15 pos_carte = pos_CD4511 r = range(0, 4) if j == 1: off_ard = 194 off_cd = 91 pos_carte = pos_CD4028 r = range(4, 8) for i in r: if sortie_arduino[i] == 0: couleur = NOIR else: couleur = ROUGE pygame.draw.line(fenetre, couleur, (pos_arduino[0] + 280, pos_arduino[1] + off_ard), (pos_carte[0] + 7, pos_carte[1] + off_cd), 5) off_ard = off_ard + 14 off_cd = off_cd + 19 off_cd = 15 off_aff = 5 i = 0 for i in range(0, 7): if sortie_CD4511[i] == 0: couleur = NOIR else: couleur = ROUGE pygame.draw.line(fenetre, couleur, (pos_afficheur[0] + 591, pos_afficheur[1] + off_aff), (pos_CD4511[0] + 102, pos_CD4511[1] + off_cd), 5) off_aff = off_aff + 19 off_cd = off_cd + 19 if sortie_bouton == 0: couleur = NOIR else: couleur = ROUGE pygame.draw.line(fenetre, couleur, (pos_arduino[0] + 279, pos_arduino[1] + 353), (pos_bouton[0] + 13, pos_bouton[1] + 13), 5) i = 0 off_cd = (102, 111) off_aff = 44 for i in range(0, 6): if sortie_CD4028[i] == 0: couleur = NOIR else: couleur = ROUGE pygame.draw.line(fenetre, couleur, (pos_CD4028[0] + off_cd[0], pos_CD4028[1] + off_cd[1]), (pos_afficheur[0] + off_aff, pos_CD4028[1] + off_cd[1]), 5) pygame.draw.line(fenetre, couleur, (pos_afficheur[0] + off_aff, pos_afficheur[1]), (pos_afficheur[0] + off_aff, pos_CD4028[1] + off_cd[1] - 2), 5) off_cd = (off_cd[0], off_cd[1] - 20) off_aff = off_aff + 101 def dessiner_afficheur(sortie_CD4511, sortie_CD4028): positions_barres = [[32, 14], [89, 20], [87, 88], [28, 150], [17, 88], [19, 20], [30, 82]] for j in range(0, 6): fenetre.blit(image_afficheur_s, (pos_afficheur[0] + j*101, pos_afficheur[1])) if sortie_CD4028[j] == 1: i = 0 for barre in positions_barres: if sortie_CD4511[i] == 0: i = i + 1 continue x_b = j*101 + pos_afficheur[0] + int(round(barre[0]*(image_afficheur_s.get_width()/133))) y_b = pos_afficheur[1] + int(round(barre[1]*(image_afficheur_s.get_height()/192))) if i == 0 or i == 3 or i == 6: fenetre.blit(barre_horizontale_s, (x_b, y_b)) else: fenetre.blit(barre_verticale_s, (x_b, y_b)) i = i + 1 return
Remplacer la section des paramètres :
### Paramètre(s) dimensions_fenetre = (1100, 600) # en pixels images_par_seconde = 25 pos_arduino = (0, 70) pos_CD4511 = (333, 340) pos_CD4028 = (333, 128) pos_afficheur = (500, 350) pos_bouton = (333, 524) pos_centre_bouton = (pos_bouton[0] + 51, pos_bouton[1] + 34) rayon_bouton = 18 pin_arduino = (pos_arduino[0] + 279, pos_arduino[1] + 353) pin_bouton = (pos_bouton[0] + 13, pos_bouton[1] + 13)
Utiliser ces deux lignes dans la boucle principale pour tester l’affichage :
dessiner_arduino(np.zeros(8, dtype=int), np.zeros(7, dtype=int), np.zeros(6, dtype=int), 0) dessiner_afficheur(np.zeros(7, dtype=int), np.zeros(6, dtype=int))
Sélection d’un afficheur
Commençons par sélectionner un seul des 7 segments pour y afficher la valeur_memorisee
.
Procédure à suivre:
Modifier la fonction
sortie_memorisee()
afin qu’elle retourne aussi le numéro de l’afficheur à allumer (num_afficheur
). Ce nombre doit être en binaire comme il s’agit d’une sortie digitale. Il sera placé sur les 4 bits de poids faible de la sortie : par exemple, si l’on veut afficher la valeur 7 ([0 1 1 1]
) sur le troisième afficheur ([0 0 1 1]
), on aura comme sortie[0 1 1 1 0 0 1 1]
.Remplacer les arguments
sortie_arduino
,sortie_CD4511
etsortie_bouton
dans la fonctiondessiner_arduino()
de la boucle principale par les valeurs adéquates. Testez votre implémentation, vous devriez obtenir ceci pour l’exemple ci-dessus:Créer une fonction
composant_CD4028
, celle-ci prend en argument un vecteur de 4 signaux et retourne un vecteur de 7 signaux indiquant l’afficheur à allumer. Par exemple, pour allumer l’afficheur numéro 3, retourner[0 0 0 1 0 0 0]
.Indice: une fonction particulière de numpy peut être utilisée pour représenter la table de vérité de ce composant.
Remplacer la valeur
sortie_CD4028
par votre sortie de fonction et tester votre programme, vous devriez obtenir ceci:Créer un nouveau signal d’horloge avec une période de 40 ms. Sur le flanc montant de l’horloge, incrémenter la valeur de
num_afficheur
. Ceci devrait créer un balayage rapide des afficheurs.Note: utiliser
pygame.USEREVENT + 1
pour créer un nouvelUSEREVENT
Que constatez-vous ? L’affichage est-il lisible et continu ?
Latence des afficheurs
Afin de régler ce problème d’affichage, nous allons inclure dans notre code la latence des afficheurs. Un vrai afficheur 7-segment ne peut s’éteindre instantanement, cela prend quelque millisecondes, ce qui crée une latence.
Procédure à suivre:
Créer une variable globale
latence_mat
une matrice de taille(6 7)
dans laquelle chaque ligne comprend les 7 signaux d’un afficheur. Vous pouvez maintenant retenir l’entrée de chaque afficheur.Observez la fonction
dessiner_afficheur
, vous pouvez constater que cette fonction éxecute sa boucle principale uniquement si lasortie_CD4028
vaut 1. Une fois dans la boucle principale, chaque LED est allumée ou non en fonction de lasortie_CD4511
. Vérifier que vous comprenez bien cette fonction en effectuant des tests sur l’affichage (exemple: allumer trois afficheurs avec la même valeur).Modifier cette fonction pour faire en sorte que tous les 7 segments affichent leur valeur de latence. Si l’afficheur est sélectionné par la
sortie_CD4028
, mettre à jour sa valeur danslatence_mat
en utilisant lasortie_CD4511
courante. Ensuite, allumer tous les afficheurs avec les valeurs précédentes desortie_CD4511
comprise danslatence_mat
.Tester votre implémentation, augmenter la période de l’horloge de balayage des afficheurs afin d’observer la mise à jour des valeurs de latence. Quand la
valeur_memorisee
est modifiée, les valeurs afichées devraient changer graduellement, comme dans l’exemple ci-dessous:
Heures, minutes, secondes
Nous allons maintenant utiliser ces 6 afficheurs pour créer une horloge heures, minutes, secondes.
Procédure à suivre:
Créer un vecteur et y placer l’ensemble des valeurs à mémoriser : tous les digits de l’horloge.
Note: pour obtenir l’heure, par exemple, utiliser la librairie datetime:
import datetime as dt dt.datetime.now().hour
Balayer l’ensemble des valeurs mémorisées afin d’afficher l’horloge complète. Synchroniser le changement de digit avec le changement d’afficheur (placer les sur le même flanc montant). Si la sychronisation est correcte vous devriez avoir une valeur fixe dans chaque afficheur.
Toutes les secondes (utiliser la première horloge), mettre à jour vos valeurs mémorisées avec l’heure courante.
Tester votre implémentation, vous devriez obtenir une horloge fonctionnelle (ici pour 13h 56min 11sec):
Note: 40 ms fonctionne bien comme fréquence de balayage.
Hello World
Finalement, affichons « Hello World » sur les afficheurs. Le message « Hello World » est cependant trop long pour être entièrement affiché. Il va donc falloir le faire défiler en continu de gauche à droite.
Procédure à suivre:
Modifier la table de vérité de la fonction
composant_CD4511
, remplacer les nombres mémorisées par les lettres de l’expression « Hello World ». Chaque lettre doit prendre une ligne de la table, inutile de stocker deux fois la même lettre. Combien de lettres pouvez-vous stocker en gardant 4 bits en entrée ? Est-ce suffisant ?Chaque lettre correspond maintenant à une entrée de la table, vous pouvez donc créer un vecteur d’entiers représentant le message. Ce vecteur devient l’ensemble des valeurs mémorisées.
Afficher le début du message et toutes les secondes décaler le message vers la gauche pour réveler la suite. Boucler pour que le message s’affiche en continu.
Faites en sorte que cliquer sur le bouton décale le message d’une lettre vers la gauche. Cela permettra à l’utilisateur d’accélérer le display.
Vous devriez obtenir ceci: