Da dieses Thema sehr schlecht behandelt wird und Sebastiãos Ausschnitt mir geholfen hat, dieses Problem zu lösen, möchte ich hier eine vollständige Lösung zum Einrichten eines RaspberryPi hinzufügen (getestet auf einem RPi 3 und Zero W)!
Einen funktionierenden Slave einrichten:
Vorbereitungen
Stellen Sie sicher, dass Sie diese Zeile in Ihrer Datei /boot/config.txt auskommentiert haben :
dtparam=i2c_arm=on
Abhängigkeiten
Als nächstes installieren Sie g ++ und pigpio mit diesem Befehl:
sudo apt install g++ pigpio
Stifte
Wie in der Dokumentation angegeben, sind die Pins GPIO 18 (SDA) und 19 (SCL) .
Diese Seite hilft Ihnen beim Auffinden auf Ihrem RaspberryPi (das Layout sollte für RaspberryPi 2, 3, Zero und Zero W identisch sein).
Dieses Schema von der Website hilft:
Software
Erläuterung
Wie bereits erwähnt, basiert diese Lösung auf dem Code-Snippet von Sebastião . Ich habe es mit Hilfe von Joans Lösung modifiziert .
Ich habe auch versucht, den Code anhand der Dokumentation für die Funktion zu verstehen bscXfer
.
Im Quellcode werden die Daten in der bsc_xfer_t
Struktur zum Hinzufügen oder Empfangen von Nachrichten verwendet. Diese werden jedoch nur angewendet, wenn bscXfer
sie mit der Adresse der Struktur ausgeführt werden (wie Joan in seiner Lösung ausgeführt hat).
Die Ganzzahl bsc_xfer_t.control hat eine ganz besondere Rolle, die mehrere Dinge wie die Slave-I²C-Adresse und verschiedene andere Zustände angibt, die in der Dokumentation gut dokumentiert sind .
Um dies besser zu verstehen, habe ich die wichtigen Teile der Dokumentation in den Quellcode kopiert und einige Dinge geändert oder in separate Funktionen ausgelagert.
Quellcode
Die Adresse kann beliebig geändert werden (sofern sie nicht über 127 liegt (auch bekannt als 7F (16) oder 1111111 (2) ).
Da ich nicht gut in C ++ bin, müssen Sie auskommentieren, was Sie wollen, was Sie nicht tun wollen. Es wird empfohlen, die closeSlave
Funktion nach Abschluss des Tests auszuführen .
Hier die Datei slaTest.cpp :
#include <pigpio.h>
#include <iostream>
using namespace std;
void runSlave();
void closeSlave();
int getControlBits(int, bool);
const int slaveAddress = 0x03; // <-- Your address of choice
bsc_xfer_t xfer; // Struct to control data flow
int main(){
// Chose one of those two lines (comment the other out):
runSlave();
//closeSlave();
return 0;
}
void runSlave() {
gpioInitialise();
cout << "Initialized GPIOs\n";
// Close old device (if any)
xfer.control = getControlBits(slaveAddress, false); // To avoid conflicts when restarting
bscXfer(&xfer);
// Set I2C slave Address to 0x0A
xfer.control = getControlBits(slaveAddress, true);
int status = bscXfer(&xfer); // Should now be visible in I2C-Scanners
if (status >= 0)
{
cout << "Opened slave\n";
xfer.rxCnt = 0;
while(1){
bscXfer(&xfer);
if(xfer.rxCnt > 0) {
cout << "Received " << xfer.rxCnt << " bytes: ";
for(int i = 0; i < xfer.rxCnt; i++)
cout << xfer.rxBuf[i];
cout << "\n";
}
//if (xfer.rxCnt > 0){
// cout << xfer.rxBuf;
//}
}
}else
cout << "Failed to open slave!!!\n";
}
void closeSlave() {
gpioInitialise();
cout << "Initialized GPIOs\n";
xfer.control = getControlBits(slaveAddress, false);
bscXfer(&xfer);
cout << "Closed slave.\n";
gpioTerminate();
cout << "Terminated GPIOs.\n";
}
int getControlBits(int address /* max 127 */, bool open) {
/*
Excerpt from http://abyz.me.uk/rpi/pigpio/cif.html#bscXfer regarding the control bits:
22 21 20 19 18 17 16 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00
a a a a a a a - - IT HC TF IR RE TE BK EC ES PL PH I2 SP EN
Bits 0-13 are copied unchanged to the BSC CR register. See pages 163-165 of the Broadcom
peripherals document for full details.
aaaaaaa defines the I2C slave address (only relevant in I2C mode)
IT invert transmit status flags
HC enable host control
TF enable test FIFO
IR invert receive status flags
RE enable receive
TE enable transmit
BK abort operation and clear FIFOs
EC send control register as first I2C byte
ES send status register as first I2C byte
PL set SPI polarity high
PH set SPI phase high
I2 enable I2C mode
SP enable SPI mode
EN enable BSC peripheral
*/
// Flags like this: 0b/*IT:*/0/*HC:*/0/*TF:*/0/*IR:*/0/*RE:*/0/*TE:*/0/*BK:*/0/*EC:*/0/*ES:*/0/*PL:*/0/*PH:*/0/*I2:*/0/*SP:*/0/*EN:*/0;
int flags;
if(open)
flags = /*RE:*/ (1 << 9) | /*TE:*/ (1 << 8) | /*I2:*/ (1 << 2) | /*EN:*/ (1 << 0);
else // Close/Abort
flags = /*BK:*/ (1 << 7) | /*I2:*/ (0 << 2) | /*EN:*/ (0 << 0);
return (address << 16 /*= to the start of significant bits*/) | flags;
}
Beachten Sie, dass in einigen Fällen das erste Byte das Befehlsbyte und nicht Teil Ihrer allgemeinen Daten sein soll.
BEARBEITEN: Beachten Sie auch, dass dies zwar zu Testzwecken gut funktioniert, @crasic jedoch (erster Kommentar) darauf hinwies, dass es eine bessere (aber auch schlecht dokumentierte) Möglichkeit gibt, dies mit Ereignissen zu tun, anstatt eine Endlosschleife zu verwenden. Das sollte besser sein, wenn es mit mehreren Anwendungen verwendet wird.
Kompilieren und ausführen
Sie können dies mit kompilieren
g++ slaveTest.cpp -lpthread -lpigpio -o slaveTest
und ausführen mit
sudo ./slaveTest
Test mit einem Meister
Um es kurz als Master zu testen, ist die Verwendung von smbus eine beliebte Option, die viel einfacher ist und durch einfaches Suchen mit einer Suchmaschine Ihrer Wahl gefunden werden kann.
Meine gewählte Option in Kürze:
- Schlagen Sie die SDA- und SCL-Pins nach (sie unterscheiden sich als Master!)
- Lauf
sudo apt install python3 python3-smbus
- Kopieren Sie das folgende Snippet als zB masterI2C.py auf Ihr RPi
- Öffnen Sie mit diesem Snippet eine Python-Shell
python3 -i masterI2C.py
- Ausführen
sendData(0x03, 'Hello World of I2C!')
, um Daten zu senden
Master-Python-Snippet:
import smbus
bus = smbus.SMBus(1)
def sendData(slaveAddress, data):
intsOfData = list(map(ord, data))
bus.write_i2c_block_data(slaveAddress, intsOfData[0], intsOfData[1:])
Bild des Testens:
Ich hoffe, dass ich dieses Thema für andere Menschen klären konnte.
(Bei plötzlichen Problemen half mir normalerweise ein Neustart meines Sklaven-Himbeer-Pi.)