Communication avec le bus SPI

Le bus SPI

Le bus SPI est accessible par les broches du GPIO de la carte Raspberry.

Le bus SPI de la Raspberry

No de broche

Nom du signal

Rôle

19

MOSI

Master Output Slave Input. Informe du sens de communication.

21

MISO

Master Input Slave Output. Informe du sens de communication.

23

CLK

Horloge de synchronisation. La vitesse est réglable logiciellement.

24

CS0

Chip select. Permet d'activer le premier composant communiquant par ce bus.

26

CS1

Chip select. Permet d'activer le second composant communiquant par ce bus.

GPIO pins diagram
GPIO pins diagram

Ouverture de la liaison de communication

L'accès se fait par le système de fichiers, comme pour les autres bus.

2 fichiers existent, /dev/spidev0.0 et /dev/spidev0.1, respectivement pour adresser le composant validé par la broche CS0 et CS1.

Après avoir ouvert le fichier, il faut configurer les paramètres de transmission :

  • Vitesse (la vitesse max dépend du composant).

  • Synchro de transmission du bit de donnée sur front montant ou descendant du signal CLK.

  • Niveau actif du CSx.

Le driver système utilise plusieurs modes de fonctionnement permettant le réglage des paramètres ci-dessus.

Exemple d'initialisation du mode 1 avec choix du niveau actif de CSx ci-dessous avec la méthode init() :

1
int CSpi::init()
2
{
3
    char filename[20];
4
5
    sprintf(filename, "/dev/spidev0.%c", m_noCe);
6
    m_fileSpi=open(filename, O_RDWR);
7
    if(m_fileSpi==-1) {  // ouvre le fichier virtuel d'accès à SPI
8
        QString mess="CSpi::init Erreur ouverture acces au bus SPI";
9
        emit sigErreur(mess);
10
        return -1;
11
    } // if open
12
    quint8 mode=(m_csHigh?SPI_CS_HIGH:0)|SPI_MODE_1;
13
    if (ioctl(m_fileSpi, SPI_IOC_WR_MODE, &mode) != 0) {
14
        QString mess="CSpi::init Erreur ouverture acces au bus SPI";
15
         emit sigErreur(mess);
16
         return -1;
17
    } // if
18
    if (ioctl(m_fileSpi, SPI_IOC_WR_MAX_SPEED_HZ, & m_speed) != 0) {
19
        QString mess="CSpi::init Erreur ouverture acces au bus SPI";
20
         emit sigErreur(mess);
21
         return -1;
22
    } // if
23
    return m_fileSpi;
24
} // init

Notez l'utilisation de la fonction ioctl() permettant d'agir sur le périphérique afin de régler les différents paramètres. La vitesse de transmission choisie doit s'accorder sur celle du composant SPI le moins rapide.

Les paramètres utilisés dans la fonction ioctl() sont spécifiques à la nature du périphérique sous-jacent. Il faut donc se référer à la documentation système pour le bus SPI.

Lecture / écriture sur le bus

Les lectures/écritures se font par les fonctions standard read() et write() :

1
char buffer[50];
2
nb = write(mFileSpi, buffer, lg); // écriture
3
// ou
4
nb = read(mFileSpi, buffer, lg); lecture

Fermeture de la communication

Utilisez la fonction close() afin de fermer le fichier de communication.

1
close(mFileSpi);

ConseilBibliothèque

Il est fortement conseillé de créer une classe CSpi permettant la gestion de la communication avec le bus SPI.

Les prochaines applications seront plus rapides à développer. En voici un exemple :

1
#ifndef CSPI_H
2
#define CSPI_H
3
4
#include <QObject>
5
#include <QDebug>
6
#include <stdint.h>
7
#include <unistd.h>
8
#include <stdio.h>
9
#include <stdlib.h>
10
#include <getopt.h>
11
#include <fcntl.h>
12
#include <sys/ioctl.h>
13
#include <linux/types.h>
14
#include <linux/spi/spidev.h>
15
16
class CSpi : public QObject
17
{
18
    Q_OBJECT
19
20
public:
21
    explicit CSpi(QObject *parent = 0, char noCs = '0', int speed=7000000, bool csHigh = false, int mode = SPI_MODE_0);
22
    int lireNOctets(quint8 *buffer, int n);
23
    int ecrireNOctets(quint8 *buffer, int n);
24
    int lireEcrire(quint8 *em, int nbTotal); // cs reste haut
25
26
private:
27
    int init();
28
    //int m_addr;   // Adresse du composant SPI
29
    char m_noCe;   // No du device CE/
30
    int m_speed;   // vitesse du bus SPI pour CE0
31
    int m_fileSpi;  // descripteur du fichier Spi
32
    bool m_csHigh;   // état de CS au repos
33
    int m_mode;  // mode de fonctionnement du Spi
34
35
signals:
36
    void sigErreur(QString msg);
37
};
38
39
#endif // CSPI_H
40
1
#include "cspi.h"
2
3
CSpi::CSpi(QObject *parent, char noCs, int speed, bool csHigh, int mode) :
4
    QObject(parent)
5
{
6
    m_mode = mode;
7
    m_csHigh = csHigh;
8
    m_noCe = noCs;   // chip select
9
    m_speed = speed;
10
    qDebug() << "Démarrage de l'objet CSpi";
11
    init();
12
} // constructeur
13
14
/////////////////////////////////////////////////////////////////
15
int CSpi::lireNOctets(quint8 *buffer, int n)
16
{
17
    int nb = read(m_fileSpi, buffer, n);
18
    if (nb != n)
19
        emit sigErreur("CSpi::ecrireNOctets ERREUR lecture");
20
//    qDebug() << buffer[0] << "  " << buffer[1];
21
    return nb;
22
} // lire
23
24
/////////////////////////////////////////////////////////////////
25
int CSpi::ecrireNOctets(quint8 *buffer, int n)
26
{
27
    int nb = write(m_fileSpi, buffer, n);
28
    if (nb !=n)
29
        emit sigErreur("CSpi::ecrireNOctets ERREUR écriture");
30
    return nb;
31
}
32
33
int CSpi::lireEcrire(quint8 *em, int nbTotal)
34
{
35
    struct spi_ioc_transfer tr[5];
36
37
    for(int i=0 ; i<nbTotal ; i++) {
38
        memset(tr+i, 0, sizeof(tr[i]));
39
        tr[i].tx_buf = (unsigned long) em+i;
40
        tr[i].rx_buf = (unsigned long) em+i;
41
        tr[i].len = 1;
42
        tr[i].speed_hz = 0;  // par défaut
43
        tr[i].bits_per_word = 8; // nbre de bit par échange
44
        tr[i].delay_usecs= 0;  // par défaut
45
        tr[i].cs_change = 0;  // ne pas changer le CS, reste haut.
46
    } // for
47
    int ret = ioctl(m_fileSpi, SPI_IOC_MESSAGE(nbTotal), &tr);
48
//    qDebug() << "CSpi::lireEcrire retour ioctl : " << ret;
49
    return ret;
50
} // lireecrire
51
52
/////////////////////////////////////////////////////////////////
53
int CSpi::init()
54
{
55
    char filename[20];
56
57
    sprintf(filename, "/dev/spidev0.%c", m_noCe);
58
    m_fileSpi=open(filename, O_RDWR);
59
    if(m_fileSpi==-1) {  // ouvre le fichier virtuel d'accès à SPI
60
        QString mess="CSpi::init Erreur ouverture acces au bus SPI";
61
        emit sigErreur(mess);
62
        return -1;
63
    } // if open
64
    quint8 mode=(m_csHigh?SPI_CS_HIGH:0)|m_mode;
65
    if (ioctl(m_fileSpi, SPI_IOC_WR_MODE, &mode) != 0) {
66
        QString mess="CSpi::init Erreur ouverture acces au bus SPI";
67
         emit sigErreur(mess);
68
         return -1;
69
    } // if
70
    if (ioctl(m_fileSpi, SPI_IOC_WR_MAX_SPEED_HZ, & m_speed) != 0) {
71
        QString mess="CSpi::init Erreur ouverture acces au bus SPI";
72
         emit sigErreur(mess);
73
         return -1;
74
    } // if
75
    return m_fileSpi;
76
} // init
77

ComplémentBus SPI

Il existe plusieurs variétés de composant SPI.

  • Ceux qui nécessitent une activation du composant par la broche CE à l'état haut (état bas par défaut au repos). Pour ceux là, il n'est pas possible de les placer en parallèle afin de former un bus, à moins de disposer d'une interface électronique de bus. Un composant de la sorte se trouvera seul sur la ligne CSx du GPIO (cas du capteur de température TC72).

  • Ceux qui nécessitent une activation du composant par la broche CE à l'état bas. Il est alors possible de les placer un parallèle pour former un bus. Dans ce cas, il sera nécessaire que la classe CSpi devienne une classe singleton pour protéger le fichier d'accès au SPI contre un accès concurrent de plusieurs threads représentant les capteurs (voir chapitre concernant le bus i2c).