Hallo,
Ich steh mal wieder etwas auf dem Schlauch und ich hoffe, dass mir einer von euch den "Schubs" in die richtige Richtung geben kann.
Erstmal die Voraussetzungen:
Ich habe an einem PC/PI mehrere I2C-GPIO- Porterweiterungen (MCP23017 und MCP23008) hängen. Die haben 16 bzw. 8 GPIO's und hängen mit verschiedenen Adressen am I2C.
Nun möchte ich diese Bausteine mit C++ über I2C mit entsprechenden Bibliotheken (MCP23017 und MCP23008) direkt ansteuern. Die jeweilige Bezeichnung, Busadresse usw. lese ich aus einer text/csv-DAtei ein und rufe diese Daten aus der Vectorvariable (mcp_elements mit eigener struct) ab. Das funktioniert soweit auch, z.B.:
-------code-------
MCP23017 mcpout1.(atoi(mcp_elements[i_e].busnr.c_str()), atoi(mcp_elements[i_e].busaddr.c_str()));
mcpout1.openI2C();
for ( int i17 = 0; i17 < 16; i17++) {
mcpout1.pinMode(i17, OUTPUT); mcpout1.pinMode(i17, LOW);
}
-------/code-------
Nun muss ich eben für jeden Baustein das Device mit dem Handle/ostream verbinden und benötige eben z.B. "mcpout1", "mcpout2", "mcpout3" usw. Da ich aber nicht sicher weiss, ob immer die gleiche Anzahl von I2C- Bausteinen an den Bus angeschlossen sind, ist diese "Hardcodierung" ungünstig. Um nicht jedes mal das Programm anpassen und neu kompilieren zu müssen, möchte ich die Art (MCP23008/MCP23017), Busadresse, Art der Ports (In-/Outputs) Nummerierung der Ports usw. aus der txt/csv-Datei lesen und das etwas dynamisch behandeln. Das Lesen der Infos klappt schon, aber dann hänge ich....
Nun die Frage: Kann ich eine Variable (event. einen Vector) erstellen in den ich dann in einer Zeile "Nr", "Streamref", "Portnummerstart", "Portnummerende" speichern kann und dann eben nur diese eine Variable in die anderen Unterprogramme übergeben kann? D.h., kann ich in dieser Variable, und wenn ja wie, die Streamreferenz speichern und übergeben kann. In den Unterprogrammen möchte ich die Variable nach dem Schema "liegt die anzusteuernde Portnummer zwischen "Start" und "Ende", dann ist der Stream/ Referenz zu verwenden benutzen.
Wie müsste die Variable aussehen? Vector mit welcher struct?
Wäre toll, wenn mich jemand erleuchten könnte.....oder wenigstens den Weg weisen könnte.
Hi,
On 02/04/2024 21:54, Sebastian Reinhardt wrote:
Ich steh mal wieder etwas auf dem Schlauch und ich hoffe, dass mir einer von euch den "Schubs" in die richtige Richtung geben kann.
Willkommen auf dem Schlauch. Beim Abstieg bitte Vorsicht, es sind Knoten an unerwarteten Stellen...
Erstmal die Voraussetzungen:
Ich habe an einem PC/PI mehrere I2C-GPIO- Porterweiterungen (MCP23017 und MCP23008) hängen. Die haben 16 bzw. 8 GPIO's und hängen mit verschiedenen Adressen am I2C.
Nun möchte ich diese Bausteine mit C++ über I2C mit entsprechenden Bibliotheken (MCP23017 und MCP23008) direkt ansteuern. Die jeweilige Bezeichnung, Busadresse usw. lese ich aus einer text/csv-DAtei ein und rufe diese Daten aus der Vectorvariable (mcp_elements mit eigener struct) ab. Das funktioniert soweit auch, z.B.:
-------code-------
MCP23017 mcpout1.(atoi(mcp_elements[i_e].busnr.c_str()), atoi(mcp_elements[i_e].busaddr.c_str()));
mcpout1.openI2C();
for ( int i17 = 0; i17 < 16; i17++) {
mcpout1.pinMode(i17, OUTPUT); mcpout1.pinMode(i17, LOW);
}
-------/code-------
Nun muss ich eben für jeden Baustein das Device mit dem Handle/ostream verbinden und benötige eben z.B. "mcpout1", "mcpout2", "mcpout3" usw. Da ich aber nicht sicher weiss, ob immer die gleiche Anzahl von I2C- Bausteinen an den Bus angeschlossen sind, ist diese "Hardcodierung" ungünstig. Um nicht jedes mal das Programm anpassen und neu kompilieren zu müssen, möchte ich die Art (MCP23008/MCP23017), Busadresse, Art der Ports (In-/Outputs) Nummerierung der Ports usw. aus der txt/csv-Datei lesen und das etwas dynamisch behandeln. Das Lesen der Infos klappt schon, aber dann hänge ich....
Ohne zu wissen wie das alles unter der Haube funktioniert ist der Rest dieser Mail eher spekulativ.
Nun die Frage: Kann ich eine Variable (event. einen Vector) erstellen in den ich dann in einer Zeile "Nr", "Streamref", "Portnummerstart", "Portnummerende" speichern kann und dann eben nur diese eine Variable in die anderen Unterprogramme übergeben kann? D.h., kann ich in dieser Variable, und wenn ja wie, die Streamreferenz speichern und übergeben kann. In den Unterprogrammen möchte ich die Variable nach dem Schema "liegt die anzusteuernde Portnummer zwischen "Start" und "Ende", dann ist der Stream/ Referenz zu verwenden benutzen.
Natürlich kannst Du komplexe Strukturen/Klassen mit Referenzen bauen und übergeben. Wir reden hier über C++, da geht sowas.
Profi-Tip: übergib diese Variable als "const MyClass&" an Unterfunktionen - das geht schneller/effizienter (nur ein Pointer auf dem Stack statt einer Kopie der Struktur). Im Vektor bitte als volle Kopie, sonst gibt es schnell Probleme.
Wie müsste die Variable aussehen? Vector mit welcher struct?
Vector ist gut. Struct ist reichlich veraltet - benutz' doch einfach C++! ;-)
Bei sowas baue ich mir gerne kleine Helferklassen. Zum Beispiel:
class McpOutRef { MCP23017 &ref; int start=0,end=0; public: McpOutRef(MCP23017&aref,int astart,int aend):ref(aref),start(astart),end(aend){} McpOutRef(const McpOutRef&)=default; McpOutRef(McpOutRef&&)=default;
McpOutRef& operator=(const McpOutRef&)=default; McpOutRef& operator=(McpOutRef&&)=default;
bool containsPort(int port)const{return astart<=port && aend>=port;}
MCP23017& mcp()const{return ref;}
};
Über containsPort könntest Du abfragen ob der gesuchte Port zu dieser Instanz gehört und über mcp() bekommst Du die Referenz.
Wenn man intern Pointer und extern Referenz benutzt, dann kann man noch etwas mehr "Magie" machen (z.B. saubere "Null-Refs"), aber das führt hier zu weit.
Du kannst Dir auch den Vektor erweitern:
class McpRefVector: public std::vector<McpOutRef> {
bool hasPort(int port){ for ( const auto&elem : *this) if(elem.containsPort(port)) return true; return false; }
McpOutRef& byPort(int port) { .... }
}
Es fehlt noch etwas "Boilerplate" für Constructor, etc. ...
Falls Dein Compiler sich über && oder =default beschwert: -std=c++17 - wir sind ja nicht mehr im finsteren Mittelalter! ;-)
Wäre toll, wenn mich jemand erleuchten könnte.....oder wenigstens den Weg weisen könnte.
Hell genug?
Konrad
Hallo Konrad,
Danke schon mal für die Antwort. Leider konnte ich mich die letzten Tage noch nicht damit beschäftigen. Nun bin ich mir nicht sicher oder ich sehe es einfach nicht. Und ja, ich habe wahrscheinlich nicht genau genug beschrieben, worum es mir geht. Den Teil, den Du bzgl. des späteren findens der Referenz anhand der Portnummer vorgeschlagen hast, ist nicht das eigentliche Problem. Ich hätte das zwar nicht so schön gelöst, aber so weit bin ich noch nicht...
Um mal noch etwas mehr Hintergrund zu liefern:
Ich habe einen RPi. Dort hängen an 3 verschiedenen I2C-Bus-Leitungen (der Hardware-I2C-Bus und 2 mit I2C-GPIO emulierte, deshalb auch die Busnummer im Code). Ich habe Ein- und Ausgabe-Bausteine auf diese I2C-"Busse" aufgeteilt, da auch noch ein I2C-Drucksensor mit dranhängt, der nur 100kHz- Baudrate vertragen. Dennoch reagieren die MCP's rasend schnell. Nun weiss ich nicht (ich weiss es schon, aber will nicht alles "hardcodieren"), wieviel der MCP23008 oder MCP23017 am jeweiligen Bus hängen.
Der Codeteil, um den es mir hier geht, liegt in der Subroutine "init_hw". Dort lese ich aus den csv-Dateien die Zeilen für die MCP's ein.
Wir nehmen jetzt mal 2 dieser MCP23017 an und in der csv steht dann folgendes:
-----------------
#################### # MCP-I2C-Portexpander # mcpxxxx;bus;adresse;input|output;start_pinnr;end_pinnr;Int_A;Int_B # mcp23017;3;0x22;input;108;131;22;24 mcp23017;4;0x24;output;208;231;;
-----------------
Normalerweise würde man ja das ganze "hardcodiert" so schreiben:
-------------------
int mcp_inc = 0;
if ( file_names[0].length() > 0) { READCSV read_csv; vector<mcp_csv_values> mcp_elements; read_csv.read_mcp_config_files(file_names[0], mcp_elements); for(int i_e=0; i_e < mcp_elements.size(); i_e++) {
if ( strstr(mcp_elements[i_e].mcptype.c_str(),"mcp23017") ) {
if ( mcp_inc == 0) {
MCP23017 mcpout1(atoi(mcp_elements[i_e].busnr.c_str()),(int)strtol(mcp_elements[i_e].busaddr.c_str(), NULL, 0));
for ( int i17 = 0; i17 < 16; i17++) { if( mcp_elements[i_e].inout_function == "input" ) { // set pin to intput and off/ low mcpout1.pinMode(i17, INPUT); mcpout1.pinMode(i17, LOW); } else if ( mcp_elements[i_e].inout_function == "output" ) { // set pin to output and off/ low mcpout1.pinMode(i17, OUTPUT); mcpout1.pinMode(i17, LOW); } else { cout << "error seting MCP direction!! exit" << endl; exit; } }
else if ( mcp_inc == 1) {
MCP23017 mcpout2(atoi(mcp_elements[i_e].busnr.c_str()),(int)strtol(mcp_elements[i_e].busaddr.c_str(), NULL, 0));
for ( int i17 = 0; i17 < 16; i17++) { if( mcp_elements[i_e].inout_function == "input" ) { // set pin to intput and off/ low mcpout2.pinMode(i17, INPUT); mcpout2.pinMode(i17, LOW); } else if ( mcp_elements[i_e].inout_function == "output" ) { // set pin to output and off/ low mcpout2.pinMode(i17, OUTPUT); mcpout2.pinMode(i17, LOW); } else { cout << "error seting MCP direction!! exit" << endl; exit; } }
mcp_inc++;
}
}
-------------------
Das gefällt mir nat. nicht. Was, wenn ich 3 MCP's am RPi hängen habe? Dann müsste ich das Programm umschreiben und neu kompilieren. Das möchte ich nat. nicht!
Nun mein Problem:
Ich möchte das in Kurzform so regeln:
-------------------------
int mcp_inc = 0;
if ( file_names[0].length() > 0) { READCSV read_csv; vector<mcp_csv_values> mcp_elements; read_csv.read_mcp_config_files(file_names[0], mcp_elements); for(int i_e=0; i_e < mcp_elements.size(); i_e++) {
if ( strstr(mcp_elements[i_e].mcptype.c_str(),"mcp23017") ) {
MCP23017 mcp_inc(atoi(mcp_elements[i_e].busnr.c_str()),(int)strtol(mcp_elements[i_e].busaddr.c_str(), NULL, 0));
for ( int i17 = 0; i17 < 16; i17++) { if( mcp_elements[i_e].inout_function == "input" ) { // set pin to intput and off/ low mcp_inc.pinMode(i17, INPUT); mcp_inc.pinMode(i17, LOW); } else if ( mcp_elements[i_e].inout_function == "output" ) { // set pin to output and off/ low mcp_inc.pinMode(i17, OUTPUT); mcp_inc.pinMode(i17, LOW); } else { cout << "error seting MCP direction!! exit" << endl; exit; } }
mcp_inc++;
}
-------------------------
Das Problem ist nur, dass meine Variable ja integer ist und nicht als Referenz taugt. D.h., dass obiger Code nat. nicht funktioniert! Der dient nur zur Verdeutlichung, was ich machen möchte.
Ich müsste also aus der Integer-Variable eine Referenz für den ostream machen und dann möchte ich die eben in einem Vector speichern, den ich in die anderen Unterprogramme (z.B. output-thread) übergebe. Dort kommen wir dann zum "Raussuchen" anhand der Portnummer. Aber darum geht es mir hier noch nicht!
Ich bin mir nicht sicher, ob das überhaupt funktioniert und wenn ja, wie?
Kleine Anmerkung noch für ggf. andere, die das hier mal später lesen:
Die Zeile in meiner früheren Mail:
-----------------
MCP23017 mcpout1.(atoi(mcp_elements[i_e].busnr.c_str()), atoi(mcp_elements[i_e].busaddr.c_str()));
-----------------
ist nat. Quatsch. Wenn man die Busadresse als String aus einer csv-Datei einliest und diese z.B. "0x20" lautet, dann macht "atoi()" daraus "0". Deshalb muss das eben so lauten:
-----------------
MCP23017 mcpout1(atoi(mcp_elements[i_e].busnr.c_str()),(int)strtol(mcp_elements[i_e].busaddr.c_str(), NULL, 0));
-----------------
Aber das nur am Rande. Da habe ich zu viel herumprobiert und mir einen Fehler eingebaut.
lug-dd@mailman.schlittermann.de