Un bus SPI (Serial Peripheral Interface) est un bus série synchrone souvent utilisé pour échanger des données entre un microcontrôleur et un ou plusieurs périphériques. Dans le cadre de ce projet, il permettra à notre application de dialoguer avec des modules accéléromètre, joystick, ou écran OLED par exemple.
Certains périphériques, comme l’écran OLED que nous proposons, possèdent des broches supplémentaires qui ne font pas partie du bus SPI.
Historiquement, les électroniciens utilisent les termes « maître » et « esclave » (ou « master » et « slave ») pour désigner les rôles joués par les composants d’un système lorsqu’ils communiquent ensemble. Il s’agit d’une analogie qui vise à faciliter la compréhension : dans un ordinateur, on dira que le processeur se comporte comme un maître, et ses périphériques comme des esclaves.
Il ne s’agit pas de faire la promotion de l’esclavage, ni d’en minimiser la gravité.
Cependant, depuis plusieurs années, cette terminologie est remise en question. La communauté des informaticiens et électroniciens remplace progressivement les mots « maître » et « esclave » par des termes plus neutres. À notre connaissance, il n’existe pas encore de consensus sur le choix de ces nouveaux termes. Pour cette raison, cette page continue à utiliser les mots « maître » et « esclave » qui restent très présents dans les documentations techniques des constructeurs et dans les spécifications de protocoles.
Dans une communication SPI, le maître est le composant qui a l’initiative des communications. L’esclave répond aux demandes du maître. Le maître produit le signal d’horloge qui servira à cadencer l’émission et la réception des bits de données. Les données sont échangées sur deux lignes souvent nommées MISO (Master In Slave Out) et MOSI (Master Out Slave In). On trouve également les dénominations SDI (Serial Data In) et SDO (Serial Data Out).
La figure ci-dessus illustre le fonctionnement typique de deux composants SPI. Le maître et l’esclave possèdent chacun un registre à décalage qui sert à la fois à l’émission et à la réception. Ces deux registres sont reliés de manière à former un anneau. Si on se place du point de vue du maître, la transmission d’une valeur se passe de la manière suivante. À chaque période de l’horloge série :
Dans une transmission SPI, l’envoi et la réception des données se font en parallèle. Dans l’exemple représenté ci-dessus, au bout de 8 périodes d’horloge, le maître et l’esclave ont simplement échangé les contenus de leur registres respectifs.
La synchronisation des données sur l’horloge série est définie par deux paramètres.
La figure suivante représente une trame SPI pour un échange de 8 bits :
Ces deux paramètres autorisent quatre variantes du protocole SPI. Pour que la communication ait lieu sans erreur, le maître et l’esclave doivent avoir les mêmes réglages de polarité et de phase.
Si ce n’est pas déjà fait, ouvrez votre projet Computer
dans Vivado :
cd $HOME/CoCiNum
./scripts/vivado vivado/Computer/Computer.xpr
Dans le panneau, Flow Navigator, exécutez l’action Add Sources, choisissez Add or create design sources et pressez le bouton Next.
Ajoutez les fichiers source VHDL suivants à votre projet.
Tous ces fichiers sont situés dans des sous-dossiers de CoCiNum/src/vhdl
.
Sous-dossier | Fichier | Rôle |
---|---|---|
SPI |
SPIMaster-precompiled.vhd |
Un périphérique contrôleur de bus SPI. |
Nous ne fournissons pas le code source du contrôleur SPI.
Le fichier SPIMaster-precompiled.vhd
contient du code VHDL qui a fait l’objet
d’une première étape de synthèse logique.
Il n’est pas destiné à être lu par un être humain.
Choisissez sur quel connecteur d’extension Pmod de la carte Basys3 vous allez
brancher votre périphérique SPI.
Sur la sérigraphie de la carte, ils portent les noms JA
(en haut à gauche),
JB
(en haut à droite), JC
(en bas à droite), et JXADC
(en bas à gauche).
Dans le panneau, Flow Navigator, exécutez à nouveau l’action Add Sources, choisissez Add or create constraints et pressez le bouton Next.
Ajoutez au projet Vivado le fichier de contraintes correspondant à votre choix :
Sous-dossier | Fichier | Rôle |
---|---|---|
Basys3 |
Basys3_PmodA.xdc |
Fichier de contraintes pour Vivado, brochage du connecteur JA. |
Basys3 |
Basys3_PmodB.xdc |
Fichier de contraintes pour Vivado, brochage du connecteur JB. |
Basys3 |
Basys3_PmodC.xdc |
Fichier de contraintes pour Vivado, brochage du connecteur JC. |
Basys3 |
Basys3_PmodXADC.xdc |
Fichier de contraintes pour Vivado, brochage du connecteur JD. |
L’entité SPIMaster
est conçue comme un périphérique pour le processeur Virgule
avec les ports suivants :
Port | Direction | Type | Rôle |
---|---|---|---|
clk_i |
Entrée | Logique | Le signal d’horloge global |
reset_i |
Entrée | Logique | La commande de réinitialisation |
valid_i |
Entrée | Logique | Demande de transfert de donnée |
ready_o |
Sortie | Logique | Indicateur de fin d’une lecture ou d’une écriture |
write_i |
Entrée | Logique | La commande d’écriture |
address_i |
Entrée | Vecteur de 2 bits | Le bus d’adresses |
wdata_i |
Entrée | Vecteur de 8 bits | Le bus de données en écriture |
rdata_o |
Sortie | Vecteur de 8 bits | Le bus de données en lecture |
evt_o |
Sortie | Logique | Indique la fin d’un échange de données |
miso_i |
Entrée | Logique | Les données série en provenance de l’esclave |
mosi_o |
Sortie | Logique | Les données série à destination de l’esclave |
sclk_o |
Sortie | Logique | L’horloge de communication série |
cs_n_o |
Sortie | Logique | Commande de sélection de l’esclave (Chip Select) |
L’entrée address_i
, sur deux bits, permet d’accéder à trois registres de 8 bits.
À l’adresse "01"
, seuls trois bits sont effectivement utilisés :
address_i |
Bits | Registre | Rôle |
---|---|---|---|
"00" |
7 à 0 | data_reg |
La donnée envoyée ou reçue. |
"01" |
0 | cs_reg |
La commande de sélection de l’esclave |
"01" |
1 | phase_reg |
La phase de l’horloge SPI |
"01" |
2 | polarity_reg |
La polarité de l’horloge SPI |
"10" |
7 à 0 | timer_max_reg |
La limite du compteur pour régler la vitesse de communication. |
Pour échanger une séquence d’octets sur le bus SPI, il faut :
polarity_reg
et phase_reg
selon les caractéristiques de l’esclave. Mettre cs_reg
à '1'
.data_reg
.evt_o
passe à '1'
.data_reg
.cs_reg
à '0'
.On utilisera un timer pour mesurer le temps dans les étapes 2, 7 et 9.
La sortie evt_o
sera typiquement reliée au contrôleur d’interruptions.
Computer_pkg
Dans le fichier Computer_pkg.vhd
, ajoutez des constantes pour définir les
caractéristiques des nouveaux périphériques du système :
Constante | Type | Valeur | Rôle |
---|---|---|---|
SPI_TIMER_ADDRESS |
Octet | 84hex | Les bits 31 à 24 de l’adresse pour accéder au timer de gestion des communications SPI. |
SPI_MASTER_ADDRESS |
Octet | 85hex | Les bits 31 à 24 de l’adresse pour accéder au contrôleur SPI. |
INTC_EVENTS_SPI_TIMER |
Entier | 3 | Pour le contrôleur d’interruptions, le numéro de l’événement indiquant la fin d’un cycle de comptage du timer SPI. |
INTC_EVENTS_SPI_MASTER |
Entier | 4 | Pour le contrôleur d’interruptions, le numéro de l’événement indiquant la fin d’une communication SPI. |
Les informations contenues dans la colonne Valeur sont des exemples. Vous pouvez les modifier à condition que chaque périphérique de votre architecture ait une adresse et un numéro d’événement différents des autres périphériques.
Computer
Dans le fichier Computer.vhd
, complétez l’entité Computer
en déclarant
les ports du connecteur d’extension que vous avez choisi.
Le tableau ci-dessous donne la liste des ports disponibles et leur rôle.
Ils sont tous de type std_logic
.
Rôle | JA |
JB |
JC |
JXADC |
Mode |
---|---|---|---|---|---|
Chip Select | pmod_a1 |
pmod_b1 |
pmod_c1 |
pmod_xadc1 |
out |
Master Out Slave In | pmod_a2 |
pmod_b2 |
pmod_c2 |
pmod_xadc2 |
out |
Master In Slave Out | pmod_a3 |
pmod_b3 |
pmod_c3 |
pmod_xadc3 |
in |
Serial Clock | pmod_a4 |
pmod_b4 |
pmod_c4 |
pmod_xadc4 |
out |
Structural
Dans le fichier Computer.vhd
, complétez l’architecture pour intégrer un
contrôleur SPI et un second timer dans le système.
Utilisez les constantes que vous avez ajoutées au paquetage Computer_pkg
.
Par précaution, le port d’entrée correspondant à la ligne Master In Slave Out
devra être resynchronisé.
Ajoutez une entrée et une sortie à l’instance sync_inst
de la manière suivante :
sync_inst : entity work.InputSynchronizer
generic map(
WIDTH => 19
)
port map(
...
data_i(18) => ..., -- Votre port d'entrée Master In Slave Out
data_o(18) => sync_spi_miso -- Pensez à déclarer ce signal
);
Pensez à déclarer le signal sync_spi_miso
et à le relier à l’entrée
miso_i
de votre contrôleur SPI.
Inspirez-vous des instances déjà présentes dans l’architecture.
Représentez sous la forme d’un schéma la nouvelle structure du système.
Si votre périphérique possède des broches supplémentaires, ajoutez la gestion de ces broches avant de passer à la suite. Voir en particulier la page Interfaçage d’un écran OLED.
Dans Vivado, générez le fichier binaire à charger dans le FPGA : Flow Navigator → Program and Debug → Generate Bitstream.
Si ce n’est pas déjà fait, reliez le connecteur micro-USB de la carte à un port USB de votre PC et mettez la carte sous tension.
Connectez Vivado à votre carte Basys3 : Flow Navigator → Program and Debug → Open Hardware Manager → Open Target → Auto-connect.
Configurez le FPGA : Flow Navigator → Program and Debug → Open Hardware Manager → Program Device.
Si vous n’avez pas d’autre périphérique à ajouter, vous pouvez passer à la section Développement logiciel embarqué.