#include #include #include #include #include #include #include #include #include #include #include #include using namespace std; using namespace std::chrono_literals; B15F& drv = B15F::getInstance(); std::atomic clkRunning{false}; bool flanke_wechselt_nicht = false; // -------------------- Thread-sichere Wrapper -------------------- std::mutex drv_mtx; // Basis: atomar lesen/schreiben static inline void safeSet(volatile uint8_t* reg, uint8_t val) { std::lock_guard g(drv_mtx); drv.setRegister(reg, val); } static inline uint8_t safeGet(volatile uint8_t* reg) { std::lock_guard g(drv_mtx); return drv.getRegister(reg); } // WICHTIG: atomare Read-Modify-Write Operationen (sonst "lost updates" mit Clock-Thread!) static inline void safeSetBits(volatile uint8_t* reg, uint8_t bits) { std::lock_guard g(drv_mtx); uint8_t cur = drv.getRegister(reg); drv.setRegister(reg, (uint8_t)(cur | bits)); } static inline void safeClearBits(volatile uint8_t* reg, uint8_t bits) { std::lock_guard g(drv_mtx); uint8_t cur = drv.getRegister(reg); drv.setRegister(reg, (uint8_t)(cur & (uint8_t)~bits)); } static inline void safeWriteMasked(volatile uint8_t* reg, uint8_t mask, uint8_t value) { std::lock_guard g(drv_mtx); uint8_t cur = drv.getRegister(reg); cur = (uint8_t)((cur & (uint8_t)~mask) | (value & mask)); drv.setRegister(reg, cur); } // -------------------- Datenleitungen: Bits 0..6 (Bit7 = Clock bleibt unberührt) -------------------- static inline void setDataLinesOutput0_6_keep7() { // DDRA: Bits 0..6 = Output, Bit7 unverändert safeWriteMasked(&DDRA, 0x7F, 0x7F); } static inline void setDataLinesInput0_6_keep7(bool pullups_off = true) { // DDRA: Bits 0..6 = Input, Bit7 unverändert safeWriteMasked(&DDRA, 0x7F, 0x00); if (pullups_off) { // PORTA: Pullups auf 0..6 aus (Bit7 unverändert) safeWriteMasked(&PORTA, 0x7F, 0x00); } } // -------------------- Handshake (nach Nutzung inaktiv) -------------------- void HandshakeWarteschleife() { uint8_t Ueberpruefen; // oben Output, unten Input safeSet(&DDRA, 0b11110000); safeSet(&PORTA, 0b11110000); while (true) { this_thread::sleep_for(10ms); Ueberpruefen = safeGet(&PINA) & 0b00001111; // nur unteres Nibble if (Ueberpruefen == 0b00001110) { // ACK = 1110 cerr << "Anderer PC gefunden!" << endl; break; } } // Handshake deaktivieren: alles freigeben + Pullups aus safeSet(&DDRA, 0b00000000); safeSet(&PORTA, 0b00000000); } void Handshake(int& WelcherAnschluss) { uint8_t Ueberpruefen; // unten Output, oben Input safeSet(&DDRA, 0b00001111); Ueberpruefen = safeGet(&PINA); if ((Ueberpruefen & 0b11110000) > 0) { cerr << "Anderer PC gefunden: Anschluss 0 wird verwendet." << endl; WelcherAnschluss = 0; // ACK: unten 1110, oben wie gelesen beibehalten uint8_t out = (uint8_t)((Ueberpruefen & 0b11110000) | 0b00001110); safeSet(&PORTA, out); // kurz halten drv.delay_ms(20); // Handshake deaktivieren: alles freigeben + Pullups aus safeSet(&DDRA, 0b00000000); safeSet(&PORTA, 0b00000000); cerr << "Bestaetigung verschickt, Handshake beendet." << endl; } else { cerr << "Kein anderer PC gefunden; Wartesignal verschickt (1)" << endl; WelcherAnschluss = 1; HandshakeWarteschleife(); } } // -------------------- Input -------------------- string input() { cout << "Schreibe 1 (Datei) oder 0 (String): " << endl; int string_or_file; if (!(cin >> string_or_file)) { cerr << "Ungueltige Eingabe.\n"; return ""; } cin.ignore(numeric_limits::max(), '\n'); if (string_or_file == 1) { cout << "Gib den Dateipfad ein:" << endl; string path; getline(cin, path); ifstream file(path); if (!file.is_open()) { cerr << "Fehler: Datei konnte nicht geoeffnet werden: " << path << endl; return ""; } string content, line; while (getline(file, line)) { content += line + "\n"; } return content; } else if (string_or_file == 0) { cout << "Gib einen String ein:" << endl; string s; getline(cin, s); return s; } cerr << "Ungueltige Eingabe. Bitte 0 oder 1 eingeben.\n"; return ""; } // -------------------- Clock: nur Bit7 toggeln (atomar, ohne lost updates) -------------------- void clockSignal() { // Bit7 als Ausgang setzen, Rest nicht anfassen safeSetBits(&DDRA, 0x80); int delay_var = 100; int clk_period = 100; while (clkRunning.load()) { drv.delay_ms(delay_var); flanke_wechselt_nicht = false; drv.delay_ms(delay_var); // Bit7 HIGH safeSetBits(&PORTA, 0x80); drv.delay_ms(delay_var); flanke_wechselt_nicht = true; drv.delay_ms(delay_var); drv.delay_ms(clk_period); drv.delay_ms(delay_var); flanke_wechselt_nicht = false; drv.delay_ms(delay_var); // Bit7 LOW safeClearBits(&PORTA, 0x80); drv.delay_ms(delay_var); flanke_wechselt_nicht = true; drv.delay_ms(delay_var); drv.delay_ms(clk_period); } } // -------------------- Nibble-Logik senden -------------------- // FIX: wenn LSB==0 muss Bit4 auch wirklich geloescht werden (| 0 macht nichts) uint8_t control_bit_setzten(uint8_t byte){ if (byte & 0x01) { return (uint8_t)(byte | (1u << 4)); } else { return (uint8_t)(byte & ~(1u << 4)); } } vector fragmentiere(string data){ vector result; for (int i = 0; i < (int)data.size(); i++) { unsigned char letter = (unsigned char)data[i]; uint8_t nibble1 = (uint8_t)(letter & 0b00001111); nibble1 |= (uint8_t)(1u << 6); uint8_t nibble2 = (uint8_t)(letter >> 4); nibble1 = control_bit_setzten(nibble1); nibble2 = control_bit_setzten(nibble2); // Bit5 auf eins, um zu signalisieren, dass es sich um den 2. Nibble handelt nibble2 |= (uint8_t)(1u << 5); // letzte Sendung markieren: Bit6 auf 1 (in deinem Schema zusammen mit Bit5) if(i + 1 == (int)data.size()){ nibble2 |= (uint8_t)(1u << 6); } cout << "Character: " << (char)letter << endl; cout << "Unsigned 8-bit: " << bitset<8>(letter) << endl; cout << "Nibble 1: " << bitset<8>(nibble1) << endl; cout << "Nibble 2: " << bitset<8>(nibble2) << endl; cout << "--- Ueberpruefung ---" << endl; result.push_back(nibble1); result.push_back(nibble2); } return result; } // -------------------- Nibble-Logik empfangen -------------------- bool kontrolle_letzte_sendung(uint8_t byte){ return (byte & (1u << 6)) != 0; } bool controlbit_pruef(uint8_t byte) { return ((byte ^ (byte >> 4)) & 0x01) == 0; } bool vollständige_überprüfung(uint8_t nibble1, uint8_t nibble2){ if(controlbit_pruef(nibble1) && controlbit_pruef(nibble2)){ if((nibble1 & (1u << 5)) == 0 && ((nibble2 & (1u << 5)) != 0 )){ return true; } } return false; } bool abbruchbedingung(uint8_t byte){ // Bit6=1 und Bit5=1 => letztes Nibble (nach deinem Schema) if((byte & (1u << 6)) && ((byte & (1u << 5)) != 0)){ return true; } return false; } // -------------------- Schreiben: Bits 0..6 (Bit7 Clock bleibt) -------------------- void write7(uint8_t byte){ cout << "Schreibt wert: " << bitset<8>(byte) << ")\n"; // 0..6 Output, Bit7 unverändert setDataLinesOutput0_6_keep7(); // PORTA Bits 0..6 setzen, Bit7 unverändert lassen safeWriteMasked(&PORTA, 0x7F, (uint8_t)(byte & 0x7F)); } // -------------------- Master/Slave: Beide koennen senden & empfangen -------------------- // Protokoll: // CLK HIGH: Master sendet, Slave empfaengt // CLK LOW : Slave sendet, Master empfaengt void master(vector data_to_send) { cout << "Master gestartet\n"; uint8_t vorgaenger = 0; string original_data; // fuer symmetrische Sende-Logik bool wartetet_am_anfang = false; int pro_takt_eine_sendung = 1; // Default: wenn wir lesen, muessen unsere Datenleitungen Hi-Z sein. setDataLinesInput0_6_keep7(true); while (true) { // kleine Abtastpause (ähnlich wie beim Slave) drv.delay_ms(14); uint8_t pina = safeGet(&PINA); bool clk_high = (pina & 0x80) != 0; uint8_t empfangen_data = (uint8_t)(pina & 0x7F); if (clk_high) { // ========================= // CLK HIGH: MASTER SEND -> SLAVE READ // ========================= pro_takt_eine_sendung = 1; wartetet_am_anfang = true; if (!data_to_send.empty() && wartetet_am_anfang && (pro_takt_eine_sendung == 1 || pro_takt_eine_sendung == 2)) { uint8_t byte = data_to_send.front(); cout << "Char to send: " << (int)byte << endl; // Turnaround-Sicherheit: erst freigeben, dann treiben setDataLinesInput0_6_keep7(true); drv.delay_ms(1); write7(byte); // setzt 0..6 als Output + schreibt drv.delay_ms(100); data_to_send.erase(data_to_send.begin()); pro_takt_eine_sendung++; // nach dem Senden sofort wieder freigeben (Kollision vermeiden) setDataLinesInput0_6_keep7(true); } else { // nichts zu senden -> Bus freigeben setDataLinesInput0_6_keep7(true); } } else { // ========================= // CLK LOW: MASTER READ <- SLAVE SEND // ========================= setDataLinesInput0_6_keep7(true); if (vorgaenger != empfangen_data && empfangen_data != 0) { if (vollständige_überprüfung(vorgaenger, empfangen_data)) { uint8_t original_byte = (uint8_t)(((empfangen_data & 0x0F) << 4) | (vorgaenger & 0x0F)); original_data += static_cast(original_byte); cout << "Empfangen: " << original_data << endl; } } if (abbruchbedingung(empfangen_data)) { // optional: break; } vorgaenger = empfangen_data; drv.delay_ms(50); } } cout << endl << "Datenübertragung abgeschlossen." << endl; cout << "Empfangene Daten: " << original_data << endl; } void slave(vector data_to_send) { cout << "Slave gestartet\n"; bool wartetet_am_anfang = false; uint8_t vorgaenger = 0; string original_data; // Default: wenn wir lesen, muessen unsere Datenleitungen Hi-Z sein. setDataLinesInput0_6_keep7(true); int pro_takt_eine_sendung = 1; while (true) { drv.delay_ms(14); uint8_t pina = safeGet(&PINA); uint8_t empfangen_data = (uint8_t)(pina & 0x7F); bool clk_high = (pina & 0x80) != 0; if (clk_high) { // ========================= // CLK HIGH: SLAVE READ <- MASTER SEND // ========================= pro_takt_eine_sendung = 1; wartetet_am_anfang = true; // Wir lesen -> eigene Datenleitungen auf Input, um Kollision zu vermeiden setDataLinesInput0_6_keep7(true); if(vorgaenger != empfangen_data && empfangen_data != 0){ if(vollständige_überprüfung(vorgaenger, empfangen_data)){ uint8_t original_byte = (uint8_t)(((empfangen_data & 0x0F) << 4) | (vorgaenger & 0x0F)); original_data += static_cast(original_byte); cout << "Empfangen: " << original_data << endl; } } if(abbruchbedingung(empfangen_data)){ // optional: break; } vorgaenger = empfangen_data; drv.delay_ms(50); } else { // ========================= // CLK LOW: SLAVE SEND -> MASTER READ // ========================= if(!data_to_send.empty()){ if(pro_takt_eine_sendung == 1 || pro_takt_eine_sendung == 2){ if(wartetet_am_anfang){ uint8_t byte = data_to_send[0]; cout << "Char to send: " << (int)byte << endl; // Turnaround-Sicherheit setDataLinesInput0_6_keep7(true); drv.delay_ms(1); write7(byte); drv.delay_ms(100); data_to_send.erase(data_to_send.begin()); pro_takt_eine_sendung++; // WICHTIG: nach dem Senden wieder freigeben setDataLinesInput0_6_keep7(true); } } } else { // nach letzter Ausgabe: wieder freigeben, um sicher zu sein setDataLinesInput0_6_keep7(true); // optional: break; } } } } void resetter() { // alles frei + Clock low safeSet(&DDRA, 0b00000000); safeSet(&PORTA, 0b00000000); } // -------------------- Main -------------------- int main() { int Anschluss = 0; Handshake(Anschluss); string input_data = input(); cout << "Eingegebene Daten: " << input_data << endl; vector data_to_send = fragmentiere(input_data); std::thread t; // Anschluss==1 -> diese Seite erzeugt den Clock (Master) if (Anschluss == 1) { clkRunning = true; t = std::thread(clockSignal); } if (Anschluss == 1) { master(data_to_send); } else { slave(data_to_send); } if (t.joinable()) { clkRunning = false; t.join(); } return 0; }