- 3D-Druck Einstieg und Tipps         
Ergebnis 1 bis 10 von 25

Thema: Wiederauferstehung des RP6? -- Erste Erfahrungen und Aufruf zum Austausch!

Hybrid-Darstellung

Vorheriger Beitrag Vorheriger Beitrag   Nächster Beitrag Nächster Beitrag
  1. #1
    Erfahrener Benutzer Roboter Experte
    Registriert seit
    24.01.2008
    Ort
    Zürich
    Beiträge
    604
    Hallo Gerhard,

    Das klingt doch super! Ich hoffe auch, dass mein Interesse für die Bastelei noch viele Jahre bestehen bleibt und ich mir mit zunehmender Unsportlichkeit vielleicht ja auch immer mehr Zeit dafür nehmen kann...
    Lass uns wissen wenn du dich wieder dran setzt, ich fand das immer ganz toll hier täglich Updates zu den verschiedenen Projekten der Leute zu lesen und aktiv mitzudiskutieren.

    Grüße nach Unterschleissheim (hab auch mal in Garching gewohnt & studiert),
    Roland

  2. #2
    Erfahrener Benutzer Roboter Experte
    Registriert seit
    24.01.2008
    Ort
    Zürich
    Beiträge
    604
    Also ich hab mich nun dafür entschieden, eine eher simple Hardware Abstraktion auf Basis des Crates 'avrd' selbst zu bauen. Die anderen sind zwar vielleicht genereller, aber haben so komplizierte Build Chains dass ich die Einfachheit des RP6 vermisse. Ich möchte das gerne "schrittweise begreifbar" haben, also erst mal verwenden und wenn man dann doch mal in die Funktionen schaut, sollen die auch nicht gerade über 10 Ecken laufen.

    Prinzipiell, gibt es für das grundlegendste einfach die Funktionen:
    Code:
    port::c4::set_output();
    port::c4::set_high();
    port::c4::set_low();
    Gibt's erste Meinungen hierzu? Ich glaube eine ähnliche Abstraktion mal beim Arduino-C gesehen zu haben... oder?

    Ein Beispiel wie man mit dieser API dann die 'set_leds()' umsetzen könnte gibt's hier:
    https://github.com/Pr0gm4n/rust-rp6l...1a5134edc189fe

    Nun verwende ich also für Register und Pins als Basis mal die Traits 'Pin' und 'Register' vom 'ruduino' Projekt (vermutlich daher die Nähe zur Arduino Abstraktion...), auch wenn ich hier und dort noch etwas daran verändern möchte:
    - Es würde eigentlich Sinn machen, wenn der Rust Compiler genutzt wird um zu überprüfen, dass man nur Pins auf high/low setzt, die in dem Moment auch als Output konfiguriert sind. Sollte eigentlich machbar sein.
    - Ich werde wohl auch über eine Art 'Pingroup' nachdenken, die in der gleichen Port-Gruppe sind (also z.B. PB7, PB1 und PB0 für die LEDs 4-6). Mit dieser Pin-Abstraktion müsste man die sonst alle nacheinander setzen, muss ja eigentlich nicht sein. Und ich würde hoffen dass man dann solchen Code schreiben könnte:

    Code:
    use crate::ports::*;
    
    pub fn set_leds(value: u8) {
        // set LEDs 1-3 connected on PORTC
        let pin_group = PinGroup([c6, c5, c4]);
        pin_group.set_outputs();
        pin_group.set(value);
    
        // set LEDs 4-6 connected on PORTB
        let pin_group = PinGroup([b0, b1, b7]);
        pin_group.set_outputs();
        pin_group.set(value >> 3);
    }
    Dabei müsste die 'set(...)' Funktion halt automatisch anhand der Anzahl vorhandener Pins maskieren, bzw. einfach die unteren x Bits verwenden.


    Liebe Grüße,
    Roland
    Geändert von Pr0gm4n (11.01.2023 um 17:29 Uhr)

  3. #3
    Erfahrener Benutzer Begeisterter Techniker
    Registriert seit
    20.08.2004
    Ort
    Unterschleissheim
    Beiträge
    332
    Hallo
    bei deinem Code-Beispiel fühl ich mich erst mal als Oldi, da ich ausser Assembler nur in C programmiere (n kann ) Also das ist Rust?
    Roboter Programmieren ist für mich auch nur sowas wie Bitschubserei, deine Funktionen sehen aber toll aus, mal sehen, ob ich sowas auch in c zustande bringe...

    Gruß
    gerhard

  4. #4
    Erfahrener Benutzer Roboter Experte
    Registriert seit
    24.01.2008
    Ort
    Zürich
    Beiträge
    604
    Hallo Gerhard,

    also jetzt wo du es sagst – der 'rustonian way' das zu implementieren wäre eigentlich eine API in dieser Art:
    Code:
    use crate::ports::*;
    
    pub fn set_leds(value: u8) {
        // set LEDs 1-3 connected on PORTC
        PinGroup([c6, c5, c4])
            .ensure_outputs()
            .set(value);
    
        // set LEDs 4-6 connected on PORTB
        PinGroup([b0, b1, b7])
            .ensure_outputs()
            .set(value >> 3);
    }
    Oder gar das 'ensure_output()' in der '.set(...)' Funktion handhaben... aber da bin ich mir dann nicht sicher ob das nicht "unnötig Zyklen verschwendet"? Was meint Ihr dazu? Sollte so eine API lieber priorisieren dass der Pin auf jeden Fall als Output gesetzt ist und dazu das DDR Register jedes mal überschreiben? (So hat das jedenfalls die RP6Lib implementiert bei den LEDs.)

    Und wenn man dann schon dabei ist, könnte man die LEDs die an verschiedenen PORTs angehängt sind ja auch gleich noch als 'MixedPinGroup' Typ implementieren..., dann bekäme man direkt eine Funktion '.set(...)' die dann die 6 verschiedenen Bits richtig ansteuert. Also (ein etwas vollständigeres Beispiel):

    Code:
    use crate::ports::*;
    
    pub struct RobotBase {
        LEDS: MixedPinGroup;
    }
    
    impl RobotBase {
        const LEDS = MixedPinGroup([b0, b1, b7, c6, c5, c4]);
    }
    Und in der main dann:
    Code:
    use rp6::*;
    
    pub extern "C" fn main() {
        RobotBase::init();
    
        loop {
            RobotBase::LEDS.set(0b111111);
            delay_ms(1000);
            RobotBase::LEDS.set(0b000000);
            delay_ms(1000);
        }
    }
    Möglichkeiten über Möglichkeiten... was meint Ihr?

    Grüße,
    Roland
    Geändert von Pr0gm4n (11.01.2023 um 17:31 Uhr)

  5. #5
    Erfahrener Benutzer Roboter Experte
    Registriert seit
    24.01.2008
    Ort
    Zürich
    Beiträge
    604
    Hallo zusammen!

    Nach einigem Rumprobieren und Ausloten was denn nun möglich ist, und was nicht, habe ich nun also ein solches Makro hinbekommen:

    Code:
        /// Set the LEDs on the `RobotBase` to the least significant 6 bits of the provided value
        pub fn set_leds(value: u8) {
            // set LEDs SL1-SL3
            set_pins!([c6, c5, c4], value);
            // set LEDs SL4-SL6
            set_pins!([b0, b1, b7], value >> 3);
        }
    https://github.com/Pr0gm4n/rust-rp6l...t_base.rs#L132

    Das mit den LEDs über mehrere Register hinweg hab ich mir dann nochmal überlegt und befunden, dass mir dazu gar nicht so viele Anwendungsfälle einfallen. Naja, das schöne an der Lösung mit dem Makro ist jedenfalls, dass das alles bereits zur Compile-Zeit übersetzt und entsprechend vom Compiler optimiert werden kann. Ich denke damit bin ich jetzt einfach mal zufrieden.

    Den Rest vom heutigen Nachmittag wollte ich gern damit verbringen, mehr von der RP6 Library zu portieren um so weitere Features "freizuschalten".

    Grüße aus dem verregneten Zürich,
    Roland

  6. #6
    Erfahrener Benutzer Roboter Experte
    Registriert seit
    24.01.2008
    Ort
    Zürich
    Beiträge
    604
    Hallo zusammen,

    mal wieder ein kleines Update von mir! Ich lerne bei dem Projekt doch auch einiges über die low-level Features von Rust – ganz anders als in der Arbeit wo es eher um parallele Datenstrukturen und Skalierbarkeit geht. Wirklich spannend, aber ich möchte mir auch die Zeit nehmen die Dinge richtig zu implementieren!

    Ich habe nun schon mal die UART Schnittstelle angebunden, zumindest schreibend. Ein paar Dinge habe ich dabei zum Beispiel gelernt:
    - Es wäre ja zu schade das Rust-Typensystem nicht vernünftig zu nutzen. Ich musste etwas rumprobieren, aber nun gibt es eine vernünftige API die etwa so benutzt werden kann:
    Code:
    Serial::write("Hier kann man einfach irgendetwas übergeben.");
    Serial::write('E');
    Serial::write('i');
    Serial::write('n');
    Serial::write('e');
    Serial::write("Zahl: ");
    Serial::write(1337);
    Serial::write(" oder was auch immer das den Trait `SerialWritable` implementiert.");
    Serial::new_line();
    
    // Alternativ gibt es auch ein Macro für Convenience. Bei Zeiten möchte ich mir auch
    // mal proc_macros ansehen und damit ein schöneres `format!` Macro bauen.
    println!("Counter: ", counter, " (DEC) | ", counter => hex, "(HEX)");
    - Rust's eingebaute Formatter sind wahre Monster (zumindest für den Embedded-Einsatz): https://jamesmunns.com/blog/fmt-unreasonably-expensive/
    - Ich habe also auf `ufmt` umgestellt – das kann leider nur Formatierung als Dezimal- und Hexadezimal Werte, da bin ich also mit einem Pull Request dran das hoffentlich mittelfristig zu ändern: https://github.com/japaric/ufmt/pull/54

    Demnächst steht dann an, sich damit auseinanderzusetzen wie ich die Hardware-Interrupts in Rust verarbeiten kann, um auch vernünftige Receive-Funktionen bauen zu können. Mal sehen wann ich wieder Zeit finde


    LG Roland

    - - - Aktualisiert - - -

    Ach ja, ich denke ich hatte noch gar nichts über die Fortschritte bei der sonstigen Library geredet. Es gibt jedenfalls nun eine Hardware-Abstraktion für alle Pins, basierend auf dem `avrd` crate (auch wenn der Bitmasken leider seltsam definiert...: https://github.com/avr-rust/avrd/issues/22)
    https://github.com/Pr0gm4n/rust-rp6l...t_base/port.rs

    Und damit sieht die Implementierung von Basis-Funktionen meiner Meinung nach schon sehr lesbar aus:
    https://github.com/Pr0gm4n/rust-rp6l...ase/mod.rs#L86

    Was meint Ihr dazu? Ein paar Namen habe ich nach wie vor einfach aus der RP6Lib übernommen, da ich mich noch nicht mit den zugehörigen Pins beschäftigt habe.


    LG und Gute Nacht,
    Roland

  7. #7
    Erfahrener Benutzer Roboter Experte
    Registriert seit
    06.11.2010
    Beiträge
    773
    Wow, gleich zwei Leute, die hier über den RP6 quatschen, da muss ich doch gleich mal mitschnuppern!
    Schön, dass noch mehr Menschen den RP6 nicht weglegen, sondern wiederauferstehen lassen wollen. Danke euch! Ich würde noch ein zweites Thema aufmachen, RP6 und ESP32, da ich leider rust nicht kann, aber ich beteilige mich hier gern nach meinen Möglichkeiten.

    Schöne Grüße in die (kleine) Runde!
    - fabqu

  8. #8
    Erfahrener Benutzer Robotik Einstein Avatar von inka
    Registriert seit
    29.10.2006
    Ort
    nahe Dresden
    Alter
    77
    Beiträge
    2.180
    hallo in die runde!

    habe bis jetzt nur mitgelesen, mein RP6 ruht seit jahren (2012?) unter einer "glasglocke", habe mich seitdem "nur" mit arduino und speziell mit ESP8266 und ESP32 beschäftigt. Finde es aber super, dass man hier versucht den RP6 wiederzubeleben....
    Wieviel ähnlichkeit hat rust mit arduino c/c++ ?

    so viel erstmal aus dem warmen und sonnigen süden, melde mich wieder wenn ich nicht nur mein smartphone zur verfügung hab
    gruß inka

  9. #9
    Erfahrener Benutzer Roboter Experte
    Registriert seit
    24.01.2008
    Ort
    Zürich
    Beiträge
    604
    Hallo Inka,

    also generell hat Rust von der Syntax schon Ähnlichkeit mit C, aber durchaus auch seine Eigenheiten. Es gibt jedenfalls ebenfalls keine Objekte/Klassen, sondern man packt Daten eben in Structs und Enums. Dazu gibt es dann noch Traits und generische Parameter, die im Wesentlichen zur Abstraktion und Modularisierung dienen.

    Meiner bisherigen Erfahrung nach spielt das bei der Interaktion mit einem Mikrocontroller allerdings eine eher nachrangige Rolle. Da geht es ja letztlich, wie Gerhard schon sagte, hauptsächlich ums "Bitschubsen". Ich nutze als Basis der Hardware-Abstraktion bei der Übersetzung der RP6Lib ein Github-Repo das für den Arduino designt war. Vermutlich kommt dadurch schnell das Gefühl auf, die Syntax wäre ähnlich zu Arduino C. Soll mir auch Recht sein, warum nicht. Letztlich möchte herausfinden wie man die RP6Lib so in Rust umsetzen kann, dass einem der Rust Compiler mit seinem starken Typensystem möglichst gut helfen kann, funktionierende Programme zu entwickeln.

    Als Beispiel: In C wird meines Wissens nicht groß zwischen einem char, uint8_t oder einem Struct mit 8 single-bit unsigned unterschieden. Man macht im Zweifel einfach einen Cast, fertig. Wenn ich in Rust zwei Datentypen mit der gleichen Größe / Datenlayout anlege, dann muss man den Compiler schon explizit zwingen das einfach zu casten. Das gilt also auch, wenn man z.B. TXEN = 3 vom Typ "Index in Register UCSRB" hat, anders als eine "Bitmaske für Register UCSRB" (also TXEN = 1 << 3; oder TXEN = 0x8; ). Ausserdem könnte man direkt den Compiler vergleichen lassen, ob man die Bitmaske nun wirklich auf das richtige Register anwendet. Bevor man dort also (z.B. Copy&Paste-)Fehler fabriziert, würde man noch einige (durchaus sinnvolle!) Compiler-Rückmeldungen bekommen, die einem dann bewusst machen, dass man da gerade vermutlich was macht, was so gar nicht gedacht ist. Macht das Sinn?

    Ob das am Ende wirklich jemandem nützt ausser mir, weil ich beim umschreiben der Library einiges lerne... das sehen wir dann!


    LG Roland

    - - - Aktualisiert - - -

    Ein weiteres Beispiel sieht man vielleicht oben mit dem Makro:
    Code:
    set_pins!([Led3, Led2, Led1], value);
    Ausgeschrieben generiert das (etwas bereinigt für Lesbarkeit) den Code:
    Code:
    let pin_mask = Led3::MASK | Led2::MASK | Led1::MASK;
    Led3::DDR::set_mask_raw(pin_mask);
    Led3::PORT::write(
        (Led3::PORT::read() & !pin_mask)
        | (((value >> 0) & 1) << Led1::OFFSET)
        | (((value >> 1) & 1) << Led2::OFFSET)
        | (((value >> 2) & 1) << Led3::OFFSET)
    );
    Also eine kleine Optimierung über eine Funktion, die jeden Pin einzeln setzt (z.B. Arduino-C meines Wissens), da sowohl DDR als auch PORT register nur einmal geschrieben werden. Der passende Code wird zur Compilezeit generiert, also ohne "schlecht wartbare" kopierte Aufrufe. Ich finde es ausserdem deutlich lesbarer als Funktion, als die originale Funktion in der RP6Lib, die die gleiche Optimierung macht. Zum Vergleich:

    Code:
    void updateStatusLEDs(void)
    {
    	DDRB &= ~0x83;
    	PORTB &= ~0x83;
    	if(statusLEDs.LED4){ DDRB |= SL4; PORTB |= SL4; }
    	if(statusLEDs.LED5){ DDRB |= SL5; PORTB |= SL5; }
    	if(statusLEDs.LED6){ DDRB |= SL6; PORTB |= SL6; }
    	DDRC &= ~0x70;
    	PORTC &= ~0x70;
    	DDRC |= ((statusLEDs.byte << 4) & 0x70);
    	PORTC |= ((statusLEDs.byte << 4) & 0x70);
    }
    
    void setLEDs(uint8_t leds)
    {
    	statusLEDs.byte = leds;
    	updateStatusLEDs();
    }
    Die Umsetzung dieses Makros hat seine Tücken, z.B., dass nur Pins in der gleichen Portgruppe damit angesteuert werden können. Dazu passiert noch etwas mehr als ich oben geschrieben habe. Ich habe einen Typencheck eingebaut, um sicherzustellen dass Led3:: DDR und Led3::PORT auch wirklich die richtigen Register für alle Pins beinhaltet, aber das wird dann durch Code-Eliminierung vom Compiler gar nicht in das fertige Binary übersetzt. Die ganzen Checks finden also zur Compilezeit (!) statt und wirken sich nicht negativ auf das Laufzeitverhalten aus. Ein möglicher Fehler für den Nutzer sähe z.B. so aus:
    Code:
    error[E0308]: mismatched types
       --> src/avr/device/pin.rs:145:24
        |
    144 |         let mut _typecheck = <$base_pin as Pin>::DDR::default();
        |                              ---------------------------------- expected due to this value
    145 |         $(_typecheck = <$pin as Pin>::DDR::default();)*
        |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `DDRB`, found struct `DDRC`
        |
       ::: src/rp6/robot_base/mod.rs:129:9
        |
    129 |         set_pins!([Led6, Led5, Led4, Led3, Led2, Led1], value);
        |         ------------------------------------------------------ in this macro invocation
        |
        = note: this error originates in the macro `set_pins` (in Nightly builds, run with -Z macro-backtrace for more info)
    Das ganze wäre in Realität dann farbig unterlegt und, wenn man die Fehlermeldungen vom Rust Compiler etwas kennt sieht man schnell, dass er einem direkt markiert dass der Fehler die nicht übereinstimmenden DDR Register sind.


    LG Roland
    Geändert von Pr0gm4n (01.02.2023 um 21:47 Uhr)

Ähnliche Themen

  1. Erste Erfahrungen mit Robomow RL 500 / Umbau
    Von robokalle im Forum Staubsaugerroboter / Reinigungs- und Rasenmähroboter
    Antworten: 76
    Letzter Beitrag: 07.07.2019, 14:30
  2. Antworten: 17
    Letzter Beitrag: 01.09.2016, 19:20
  3. erste erfahrungen mit CSA-1V ... berührungslose strommessung
    Von kolisson im Forum Sensoren / Sensorik
    Antworten: 9
    Letzter Beitrag: 14.10.2010, 10:21
  4. Erste Erfahrungen mit dem AmTel - Cocktailmaschine
    Von alex007 im Forum Vorstellungen+Bilder von fertigen Projekten/Bots
    Antworten: 14
    Letzter Beitrag: 04.03.2009, 21:41
  5. [ERLEDIGT] Erste Erfahrungen mit dem Conrad Roboter
    Von im Forum Robby CCRP5
    Antworten: 8
    Letzter Beitrag: 22.05.2007, 13:41

Berechtigungen

  • Neue Themen erstellen: Nein
  • Themen beantworten: Nein
  • Anhänge hochladen: Nein
  • Beiträge bearbeiten: Nein
  •  

Solar Speicher und Akkus Tests