-         

Seite 1 von 2 12 LetzteLetzte
Ergebnis 1 bis 10 von 15

Thema: Geschickte Beschleunigung von kritischem Code?

  1. #1
    Erfahrener Benutzer Roboter Genie
    Registriert seit
    08.07.2004
    Ort
    Südhessen
    Beiträge
    1.312

    Geschickte Beschleunigung von kritischem Code?

    Anzeige

    Hallo Leute,

    wir haben gestern unsere (PAL-)Kamera zum Laufen gebracht und schaffen es nun tatsächlich, ganze 80 Pixel pro Zeile zu lesen (bei aktuell 16MHz). Das ist mehr als doppelt so viel, wie anfangs ging, und das nur mit Code-Optimierungen.

    Nun Frage ich mich, aber bin leider mit meinem Latein bzw. C am Ende, wie man das Einlesen noch schneller ablaufen lassen könnte. Inline-Assembler wäre auch eine gute Möglichkeit, aber ich habe einfach zu wenig Ahnung von Assembler in Kombination mit C.

    Das Problem ist, dass die Pixel in ein Array/einen Puffer müssen, damit ich alle Pixel nach dem Einlesen auch verarbeiten kann.

    Der aktuelle, stark optimierte Code zum Einlesen einer Zeile sieht nun so aus:
    (Falls sich jemand fragt, wie wir das Zeilenende erkennen: Wie messen mit dem Oszi nach, wie lang PD0 high ist, und legen das getriggert über das Signal der Kamera, dann sehen wir, ob PD0 länger high ist, als die Zeile dauert. Dann wird der Wert MAX_X angepasst, bis es knapp vor dem Sync endet.)

    Code:
    #define MAX_X 80
    volatile uint8_t global_cam_pic[MAX_X+1];
    
    static inline void read_cam_line()
    {
      register uint8_t cnt asm("r3");
    
      PORTD |= _BV(PD0);
    
      cnt = MAX_X;
      do
      {
        global_cam_pic[cnt]=ADCH;
      }while (cnt--);
    
      PORTD &= ~_BV(PD0);
    
    }
    Ich hatte auch schon ein paar Alternativen probiert, z.B. mit Pointern, aber die sind ja gleich alle 16bit und deshalb dauert es dann wesentlich länger.

    Eine andere Idee war es, das Array irgendwie an den Anfang des RAMs legen zu können, um dann einfach von MAX_X auf Null runter zu schreiben, damit dann die while-Schleife wieder auf Null prüfen kann. (was mit einem ASM-Befehl geht, anstatt zweien)

    Hat noch jemand Optimierungsideen?

    Weitere Frage: Da ich den internen ADC benutze: Weiß jemand, wie man ihn gescheit übertakten kann, so dass man noch 8bit hat, und trotzdem >1.6MHz lesen kann?

  2. #2
    Erfahrener Benutzer Roboter Genie
    Registriert seit
    08.07.2004
    Ort
    Südhessen
    Beiträge
    1.312
    Komisch ist, dass dieser Thread garnicht in der Liste der letzten Threads auftaucht. Bestimmt ein INSERT verloren gegangen. Mal sehen, obs dieser Push-Post ändern kann...

  3. #3
    Erfahrener Benutzer Roboter Experte
    Registriert seit
    25.03.2006
    Ort
    Darmstadt
    Alter
    26
    Beiträge
    522
    Hallo thewulf00!

    Code:
    register uint8_t cnt asm("r3");
    Ich bin mir da zwar nicht 100% sicher, aber ich glaube dass das voll nach hinten losgeht, weil cnt dann wirklich als Variable vorhanden sein muss. 'register' sollte man generell nicht benutzen, es sei denn man weiss genau was man tut.

    Code:
    do
      {
        global_cam_pic[cnt]=ADCH;
      }while (cnt--);
    Hier wird 81 mal eingelesen und nicht 80.

    Ich hatte auch schon ein paar Alternativen probiert, z.B. mit Pointern, aber die sind ja gleich alle 16bit und deshalb dauert es dann wesentlich länger.
    Um den Wert aus dem Array zu laden, werden intern sowieso Pointer verwendet, insoweit wundert es micht, dass es länger dauert. Kannst Du bitte den genauen Code posten?

    Mein eigener Vorschlag wäre sowas hier:
    Code:
    define MAX_X 80
    
    volatile uint8_t global_cam_pic[MAX_X]; 
    
    static inline void read_cam_line()
    {
      PORTD |= _BV(PD0);
    
      uint8_t *p = global_cam_pic + MAX_X;
    
      do
      {
        *--p = ADCH;
      }while (p > global_cam_pic);
    
      PORTD &= ~_BV(PD0);
    
    }
    Wenn es vom Compiler so übersetzt wird wie man es als Mensch machen würde, würde man pro Pixel nur 7 Takte benötigen.

    MfG Mark

  4. #4
    Erfahrener Benutzer Robotik Visionär
    Registriert seit
    26.11.2005
    Ort
    bei Uelzen (Niedersachsen)
    Beiträge
    7.942
    Das einfachste und bste wäre hier wohl wirklich inline ASM. SOnt müßte man mal sehen was denn GCC bis jetzt aus dem Code macht. Also einfachmal den entsprechenden abschnitt aus dem .lst File zeigen.
    Man muß aber auch noch sehen wie man das mit dem AD wandler syncronisiert. Sonst hat man nur jeden AD wert 10 mal im Array, was auch nicht wirklich weiter hilft.

  5. #5
    Moderator Robotik Visionär Avatar von radbruch
    Registriert seit
    27.12.2006
    Ort
    Stuttgart
    Alter
    54
    Beiträge
    5.782
    Blog-Einträge
    8
    Zum Thema höhere Sampleraten des ADCs:
    http://www.roboternetz.de/phpBB2/viewtopic.php?t=33070

    Gruß

    mic

    Atmel’s products are not intended, authorized, or warranted for use
    as components in applications intended to support or sustain life!

  6. #6
    Erfahrener Benutzer Robotik Einstein Avatar von SprinterSB
    Registriert seit
    09.06.2005
    Ort
    An der Saar
    Beiträge
    2.801
    Was soll denn überhaupt die Zeile mit dem ADCH? Das liest doch den ADC viel zu schnell aus.
    Disclaimer: none. Sue me.

  7. #7
    Moderator Robotik Visionär Avatar von radbruch
    Registriert seit
    27.12.2006
    Ort
    Stuttgart
    Alter
    54
    Beiträge
    5.782
    Blog-Einträge
    8
    Das verstehen die "Kollegen" vom mikrocontroller.net auch nicht:

    http://www.roboternetz.de/phpBB2/zei...=337162#337162

    Ich weiß nicht ganz genau wie, aber es funktioniert.

    Gruß

    mic

    [Edit]
    Wenn der ADC fertig ist wartet er bis ADCH gelesen wurde und startet dann erneut. Ab jetzt läuft die Zeit. Bei Prescaler /2 bleiben ca. 26 Takte bis ein neuer Wert gemessen wurde und der alte Wert gespeichert sein muss. Ich bin jetzt zu faul zum Zählen, aber rein gefühlsmässig braucht meine do *pixelzeiger-Funktion wohl auch so um die30 Takte. Das würde dem ADC reichen ein neues Ergebniss bereitzustellen. Nur so ein Gedanke...

    Atmel’s products are not intended, authorized, or warranted for use
    as components in applications intended to support or sustain life!

  8. #8
    Erfahrener Benutzer Roboter Genie
    Registriert seit
    08.07.2004
    Ort
    Südhessen
    Beiträge
    1.312
    Danke für die vielen Antworten:
    @p_mork: Mit dem Register kann ich schon umgehen, keine Angst. Ich habe mir genug Quellen dazu durchgelesen. Und diese Zeile bedeutet nichts anderes, als dass die Variable nicht im RAM zu finden ist, sondern in r3, d.h. in einem Register, so dass er bei einem Zugriff nicht aus dem RAM lesen oder hineinschreiben muss. Diese Zeile hat den Ablauf stark beschleunigt.
    In den Quelle, die ich benutzt habe, ist angegeben, dass r3 vom GCC unbenutzt bleibt. Und selbst wenn es benutzt würde, außerhalb der Schleife ists mir egal.

    Ja natürlich wird 81 mal gelesen. Durch meine Experimente hatte ich für diesen Post den Originalzustand wiederhergestellt und vergessen, dass das '--' vor dem Cnt stehen muss.

    Ich hatte den selben Code benutzt wie Du, nur dass ich per Array-Zugriff die Adresse berechnet hatte, das macht aber keinen Unterschied:
    Code:
    uint8_t *p = &global_cam_pic[MAX_X];
    Das komische ist, dass dieser Ablauf länger dauert, als der ohne Pointer. Deshalb bin ich ja so enttäuscht.


    @Besserwessi:
    Ich glaube auch, dass man mit Inline-Assembler noch was kitzeln kann.
    Man könnte ja z.B. den Beginn des Arrays nehmen, und dann drauf los schreiben, ohne jedes Mal das High-Byte des RAMs mit setzen zu müssen. Die Startadresse des Puffers ist nämlich glücklicherweise auf 512. (Warum auch immer) Dann könnte man sogar über 200 Pixel schreiben, ohne das High-Byte des Pointers neu setzen zu müssen.

    Fakt ist, dass ich tatsächlich 80 unterschiedliche Pixel bekomme. Ich habe das ja schon auf den PC übertragen und mir angeschaut. Genau, wie Radbruch sagt, keiner versteht so genau, warum, aber der ADC läuft schneller als im DB angegeben.


    @sprinterSB: Siehe meinen Abschnitt an Besserwessi und Radbruchs Antwort: Ich lese de facto 80 unterschiedliche Bits in 50µs.


    @radbruch: Danke, den Link habe ich gesucht.
    Ja, das kann schon sein, aber das Problem ist, dass ich nicht denke, dass ich 26 Takte brauche. Aber wie gehabt, wird wahrscheinlich einfach unten abgeschnitten.

  9. #9
    Erfahrener Benutzer Robotik Visionär
    Registriert seit
    26.11.2005
    Ort
    bei Uelzen (Niedersachsen)
    Beiträge
    7.942
    Mit inline ASM hätte man vor allem den Vorteil, dass mangenau abzählen kann wie viele Takte die Schleife braicht und dann alle 26 Takte (Das wäre die maximale Geschwindigkeit den ADC) einen Wert ausließt. Das Limit von 26 Takten könnte eventuell sogar der Compiler erreichen. In ASM kann man da auch noch gemütlich was anderes nebenbei machen oder auf das Ready Flag warten.

    Ob der ADC bei maximaler Geschwingdigkeit 8( MHz ADC Clock satt der maximal vorgesehenen 1 MHz) noch was sinnvolles raus giebt ist aber einen andere Frage. Da der ADC mindestens 13 Takte braicht, sollte man also wenigstens rund 1,6 µs ja Wandlung brauchen, es sei denn da wäre ein Fehler im Datenblatt und eine Teiler von 1 wäre möglich.

  10. #10
    Moderator Robotik Visionär Avatar von radbruch
    Registriert seit
    27.12.2006
    Ort
    Stuttgart
    Alter
    54
    Beiträge
    5.782
    Blog-Einträge
    8
    Hallo

    Auf die 26 Prozessortakte komme ich ja über die 13 ADC-Takte bei prescaler /2. Das schafft man auch in C locker, die Frage ist eben, was man einliest wenn der ADC noch wandelt.

    Erhält man die gültigen Bits die bisher schon gewandelt wurden, angefangen vom MSB? Und nach dem Lesen von ADCH wird die Wandlung neu gestartet? Dann würde eine Wandlung weniger als 13 Zyklen dauern und das wäre die Möglichkeit weniger als 10 Ergebnissbits zu wandeln. Das dies möglich ist wird im Datenblatt erwähnt aber nicht näher beschrieben (Startbeitrag in obrigem Sampleratenthread). Aber genau das ist es was den Unterschied zur Standartanwendung des ADCs mit Prüfung des Flags ausmacht. So könnte man das Tempo vielleicht sogar noch steigern. Es gibt noch viel zu erforschen :)

    Weil meine Einleseschleife gleichzeitig noch auf Zeilenende prüft (Wert < Schwarz) dauert sie etwas länger als eure reinen Einleseschleifen. Aber vermutlich nicht wesentlich viel länger weil ihr ja auch auf eine Abbruchbedingung für die Schleife prüfen müßt.

    Wie lange die Wandlung absolut benötigt hängt ja vom Systemtakt ab. Mein 8MHz-Mega32 erreicht mit Prescaler /2 ausreichend genaue Messwerte, möglicherweise schafft der ADC das bei 16MHz-Takt nicht mehr.

    Gruß

    mic

    Atmel’s products are not intended, authorized, or warranted for use
    as components in applications intended to support or sustain life!

Seite 1 von 2 12 LetzteLetzte

Berechtigungen

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