Communication avec le bus SPI
Le bus SPI
Le bus SPI est accessible par les broches du GPIO de la carte 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. |
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()
:
int CSpi::init()
{
char filename[20];
sprintf(filename, "/dev/spidev0.%c", m_noCe);
m_fileSpi=open(filename, O_RDWR);
if(m_fileSpi==-1) { // ouvre le fichier virtuel d'accès à SPI
QString mess="CSpi::init Erreur ouverture acces au bus SPI";
emit sigErreur(mess);
return -1;
} // if open
quint8 mode=(m_csHigh?SPI_CS_HIGH:0)|SPI_MODE_1;
if (ioctl(m_fileSpi, SPI_IOC_WR_MODE, &mode) != 0) {
QString mess="CSpi::init Erreur ouverture acces au bus SPI";
emit sigErreur(mess);
return -1;
} // if
if (ioctl(m_fileSpi, SPI_IOC_WR_MAX_SPEED_HZ, & m_speed) != 0) {
QString mess="CSpi::init Erreur ouverture acces au bus SPI";
emit sigErreur(mess);
return -1;
} // if
return m_fileSpi;
} // 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()
:
char buffer[50];
nb = write(mFileSpi, buffer, lg); // écriture
// ou
nb = read(mFileSpi, buffer, lg); lecture
Fermeture de la communication
Utilisez la fonction close()
afin de fermer le fichier de communication.
close(mFileSpi);
Conseil : Bibliothè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 :
class CSpi : public QObject
{
Q_OBJECT
public:
explicit CSpi(QObject *parent = 0, char noCs = '0', int speed=7000000, bool csHigh = false, int mode = SPI_MODE_0);
int lireNOctets(quint8 *buffer, int n);
int ecrireNOctets(quint8 *buffer, int n);
int lireEcrire(quint8 *em, int nbTotal); // cs reste haut
private:
int init();
//int m_addr; // Adresse du composant SPI
char m_noCe; // No du device CE/
int m_speed; // vitesse du bus SPI pour CE0
int m_fileSpi; // descripteur du fichier Spi
bool m_csHigh; // état de CS au repos
int m_mode; // mode de fonctionnement du Spi
signals:
void sigErreur(QString msg);
};
// CSPI_H
CSpi::CSpi(QObject *parent, char noCs, int speed, bool csHigh, int mode) :
QObject(parent)
{
m_mode = mode;
m_csHigh = csHigh;
m_noCe = noCs; // chip select
m_speed = speed;
qDebug() << "Démarrage de l'objet CSpi";
init();
} // constructeur
/////////////////////////////////////////////////////////////////
int CSpi::lireNOctets(quint8 *buffer, int n)
{
int nb = read(m_fileSpi, buffer, n);
if (nb != n)
emit sigErreur("CSpi::ecrireNOctets ERREUR lecture");
// qDebug() << buffer[0] << " " << buffer[1];
return nb;
} // lire
/////////////////////////////////////////////////////////////////
int CSpi::ecrireNOctets(quint8 *buffer, int n)
{
int nb = write(m_fileSpi, buffer, n);
if (nb !=n)
emit sigErreur("CSpi::ecrireNOctets ERREUR écriture");
return nb;
}
int CSpi::lireEcrire(quint8 *em, int nbTotal)
{
struct spi_ioc_transfer tr[5];
for(int i=0 ; i<nbTotal ; i++) {
memset(tr+i, 0, sizeof(tr[i]));
tr[i].tx_buf = (unsigned long) em+i;
tr[i].rx_buf = (unsigned long) em+i;
tr[i].len = 1;
tr[i].speed_hz = 0; // par défaut
tr[i].bits_per_word = 8; // nbre de bit par échange
tr[i].delay_usecs= 0; // par défaut
tr[i].cs_change = 0; // ne pas changer le CS, reste haut.
} // for
int ret = ioctl(m_fileSpi, SPI_IOC_MESSAGE(nbTotal), &tr);
// qDebug() << "CSpi::lireEcrire retour ioctl : " << ret;
return ret;
} // lireecrire
/////////////////////////////////////////////////////////////////
int CSpi::init()
{
char filename[20];
sprintf(filename, "/dev/spidev0.%c", m_noCe);
m_fileSpi=open(filename, O_RDWR);
if(m_fileSpi==-1) { // ouvre le fichier virtuel d'accès à SPI
QString mess="CSpi::init Erreur ouverture acces au bus SPI";
emit sigErreur(mess);
return -1;
} // if open
quint8 mode=(m_csHigh?SPI_CS_HIGH:0)|m_mode;
if (ioctl(m_fileSpi, SPI_IOC_WR_MODE, &mode) != 0) {
QString mess="CSpi::init Erreur ouverture acces au bus SPI";
emit sigErreur(mess);
return -1;
} // if
if (ioctl(m_fileSpi, SPI_IOC_WR_MAX_SPEED_HZ, & m_speed) != 0) {
QString mess="CSpi::init Erreur ouverture acces au bus SPI";
emit sigErreur(mess);
return -1;
} // if
return m_fileSpi;
} // init
Complément : Bus 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).