Logiciels > Programmation PIC > Bases > MP > Registre à décalage 74595

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.

pic_tuto_base_shift_reg_74595_x1_001a

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 :

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.

pic_tuto_base_shift_reg_74595_x2_001a

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).