Dernière mise à jour :
11/05/2014
Présentation
Nous allons aborder ici des méthodes d'accès à un registre à décalage
(shift register) de type 74595 (SN74595 ou 74HC595 par exemple). Ce
type de composant permet par exemple d'ajouter 8, 16 ou plus sorties
logiques à un PIC en ne consommant que trois broches. Dans
un premier temps, nous verrons comment disposer de 8 sorties
individuelles en n'utilisant que trois broches d'un PIC. Puis nous
verrons ensuite comment disposer de 16 sorties individuelles ou plus
(24, 32, etc), en utilisant toujours uniquement trois broches du
PIC. Avant de lire cette page, je vous conseille de
commencer avec la page
PIC -
Bases
- Configuration minimale, si ce n'est déjà fait.
Pour l'ajout de 16 entrées basé sur le même principe, voir page
Registre à décalage 74165.
Registre à décalage 74595
Une petite description de ce composant s'impose avant d'aller plus loin. Le
circuit intégré
74595 fait partie de la famille des circuits TTL (Transistor and
Transistor Logic) et fonctionne sous une tension d'alimentation de +5
V. Il dispose d'une entrée "série" sur laquelle on fait entrer à la
queue-leu-leu 8 bits de données, et de 8 sorties "parallèles" qui
restituent sous forme individuelle les 8 bits de données en question.
Dit autrement, le premier bit appliqué sur l'entrée série va se
retrouver sur une première sortie, le second bit appliqué sur l'entrée
série va se retrouver sur une seconde sortie, etc jusqu'au huitième bit
appliqué sur l'entrée série. Pour éviter que les sorties changent
d'état au fur et à mesure que les données arrivent sur l'entrée série,
on verrouille les sorties sur leur état actuel et on les fait
toutes changer d'état au même moment une fois que les 8 bits sont tous
réceptionnés. Nous avons besoin de trois fils pour faire travailler le
74595 :
- un fil de données série pour le transfert des bits à faire apparaître sur les sorties parallèles (Data)
- un fil d'horloge pour cadencer le transfert des données (Clock / CLK)
- un fil de validation / verrouillage des sorties parallèles (Strobe).
Si on désire plus de 8 sorties, on peut chaîner plusieurs 74595, nous verrons cela plus loin.
PIC 16F628A et 1 x 74595, pour 8 sorties
Passons
maintenant au côté pratique, avec un PIC de type 16F628A associé à un seul 74595. J'ai choisi
ce PIC car il est répandu et qu'il dispose d'un nombre de broches
suffisant pour facilement mettre en évidence le transfert des données
(8 bits). Mais bien sûr, n'importe quel autre PIC peut convenir, le
logiciel donné ici en exemple s'adapte sans problème à d'autres
références.
Dans
le cas présent, les données à transférer proviennent de l'état d'un
ensemble de huit interrupteurs (DSW1), il peut s'agir de toute
autre source (valeur numérisée en sortie de CAN, mémoire EEPROM, etc).
Les LED raccordées aux huit
sorties parallèles du registre à décalage 74595 reflètent l'état des
interrupteurs. Notons dès maintenant que les résistances de pullup du
port B du PIC sont activées et qu'on obtient un niveau logique haut sur
les entrées du port B quand les interrupteurs sont ouverts (position
OFF). Quand ils sont fermés (position ON, liaison effective vers la
masse), on obtient un niveau logique bas. Le code complet est visible
ci-après.
program electronique_tuto_pic_mp_shiftreg_x1_74595_16f628;
var
iValue: byte;
iLoop: byte;
Out_Clock: sbit at RA0_bit;
Out_Data: sbit at RA1_bit;
Out_Strobe: sbit at RA2_bit;
procedure CPU_Init;
begin
CMCON := 7;
TRISA := $00;
TRISB := $FF;
OPTION_REG := $00;
INTCON := $00;
Out_Data := 0;
Out_Clock := 0;
Out_Strobe := 0;
end;
procedure Data_Send(iVal: byte);
begin
for iLoop := 7 downTo 0 do
begin
Out_Data := iVal.iLoop;
Out_Clock := 1;
Out_Clock := 0;
end;
Out_Strobe := 1;
Out_Strobe := 0;
end;
begin
CPU_Init;
while true do
begin
iValue := PORTB;
Data_Send(iValue);
end;
end.
Vous seriez en droit de soulever deux remarques concernant ce code :
- Dans la procédure Data_Send, les bits de la variable iVal (iValue)
sont lues à l'envers.
On commence par le bit #7 et on termine avec le
bit #0. Celà s'explique par le fait que le premier bit qui arrive sur
l'entrée série du 74595 se retrouvera sur la dernière sortie (Q7) après
avoir transféré les huit bits. Si on ne transférait qu'un seul bit au
lieu de huit, la valeur de ce premier (et unique) bit reçu par le 74595
se reflèterait sur la première sortie (Q0). - On ne laisse aucun
temps mort entre les changements d'état de la ligne Clock pour
transmettre l'état de chaque bit de la variable iVal (iValue).
On la
met à 1 et on retourne aussitôt après à 0. On peut se le permettre ici
car le PIC fonctionne avec son horloge interne de 4 MHz et le transfert
des données ne peut donc excéder la vitesse de 1 Mbps (ou 1 MHz si vous
préférez). Le 74595 peut sans problème tourner à 5 MHz, et le temps qui
sépare deux instructions étant de 1 us, on ne risque pas d'avoir des
problèmes avec les temps de montée du circuit TTL.
Est-il possible de simplifier le code pour transférer les données ?
MikroPascal
dispose d'une librairie assez riche, laquelle comporte des
routines toutes faites pour les transferts de données série de type I2C
ou SPI. Ne serait-il pas possible d'utiliser ces dernières pour
simplifier un peu notre code ? Ma foi, on peut essayer, cela ne coûte
que quelques minutes.
program electronique_tuto_pic_mp_shiftreg_x1_74595_16f628;
var
SoftSpi_CLK : sbit at RA0_bit;
SoftSpi_SDO : sbit at RA1_bit;
SoftSpi_SDI : sbit at RA4_bit; // not used
SoftSpi_CLK_Direction : sbit at TRISA0_bit;
SoftSpi_SDO_Direction : sbit at TRISA1_bit;
SoftSpi_SDI_Direction : sbit at TRISA4_bit; // not used
iValue: byte;
iLoop: byte;
procedure CPU_Init;
begin
CMCON := 7;
TRISA := $00;
TRISB := $FF;
OPTION_REG := $00;
INTCON := $00;
Soft_SPI_Init;
end;
procedure Data_Send(iVal: byte);
begin
Soft_SPI_Write(iVal);
Out_Strobe := 1;
Out_Strobe := 0;
end;
begin
CPU_Init;
while true do
begin
iValue := PORTB;
Data_Send(iValue);
end;
end.
Tiens, ça fonctionne aussi ;-) On observe certes un léger
ralentissement du temps de transfert, mais il n'est guère critique dans
notre cas :
> 458 us entre chaque octet transmis avec la routine faite maison
> 713 us entre chaque octet transmis avec la routine Soft_SPI
Ce
décalage est principalement lié au temps d'activation de la sortie CLK,
qui est de 1 us avec la routine maison et de 29 us avec la routine SPI.
En toute franchise et sans vouloir vous influencer, essayez de peser le
pour et le contre.
PIC 16F628A et 2 x 74595, pour 16 sorties
Allez,
nous allons garder la routine maison... pour commencer. Cette fois, les
16 bits à transmettre font partie d'un mot (variable de type word),
mais on aurait pu
utiliser deux octets séparés. On garde les huit micro-interrupteurs
(DSW1) et pour différencier les deux paquets de huit bits, on inverse
l'état logique de tous les bits du second paquet.
Le code complet correspondant est le suivant.
program electronique_tuto_pic_mp_shiftreg_x2_74595_16f628;
var
iValue: word;
iLoop: byte;
Out_Clock: sbit at RA0_bit;
Out_Data: sbit at RA1_bit;
Out_Strobe: sbit at RA2_bit;
procedure CPU_Init;
begin
CMCON := 7;
TRISA := $00;
TRISB := $FF;
OPTION_REG := $00;
INTCON := $00;
Out_Data := 0;
Out_Clock := 0;
Out_Strobe := 0;
end;
procedure Data_Send(iVal: word);
begin
for iLoop := 15 downTo 0 do
begin
Out_Data := iVal.iLoop;
Out_Clock := 1;
Out_Clock := 0;
end;
Out_Strobe := 1;
Out_Strobe := 0;
end;
begin
CPU_Init;
while true do
begin
lo(iValue) := PORTB;
hi(iValue) := PORTB xor $FF; // inversion état bits
Data_Send(iValue);
end;
end.
Les seules différences par rapport au code du premier montage (avec
un seul registre 74595) concernent le nombre de bits à transmettre, 16
à la place de 8 (word à la place de byte, et boucle "for iLoop" de 15 à
0 au lieu de 7 à 0).