- 12V Akku mit 280 Ah bauen         
Ergebnis 1 bis 5 von 5

Thema: Bitte um Hilfe bei C-Code aus unbekannter Quelle

  1. #1
    Erfahrener Benutzer Robotik Visionär Avatar von oberallgeier
    Registriert seit
    01.09.2007
    Ort
    Oberallgäu
    Beiträge
    8.692

    Bitte um Hilfe bei C-Code aus unbekannter Quelle

    Anzeige

    Powerstation Test
    Hallo Programmierkundige - in C.

    Vor Jahren war ich über eine Waitroutine gestolpert die ich nicht selten in meinen Quellen benutze besondes dann, wenn Warten angesagt ist ohne dass Timer, insbes. Interrupts erwünscht/zugelassen sind. Das übliche "delay" aus den Codesammlungen hatte ich eigentlich nie benutzt, weil ich der Hoffnung war, irgendwann in die Geheimnisse der erwähnten Routine einzdringen. Is nicht! Leider. Statt C klingt die mir immer noch wie Kantonesisch, Mandarin, Spanisch oder so - mit zusätzlichen Defekten in der Tastatur. Daher meine Bitte mir zum Verständnis des Ablaufes zu helfen.
    Bei der Anwendung ist mir klar, dass die Routine ein Behelf ist - interuptgetriebene Timer wären da meist sinnvoller, die verwende ich auch. Nichts desto trotz würde ich gerne die Geheimnisse dieses Codes verstehen ;
    Code:
    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      void wms ( uint16_t ms )      // 128 kHz-wms; Pausenwert ist nur experimentell !
                                    //      M E I S T   keine Millisekunde ! ! !
    // - - - - - - - - - - - - - - - -
      {                             // Weiter Messwerte in L3_tmr10.c und früher (siehe dort)
        for(; ms>0; ms--)
        {                           //
        uint16_t __c =  26;         // Ergibt am 14Dez21,17h34 1,001 sec bei wms=1000
                                    // => bei wms ( 100) ist der (periphere) Einfluss
                                    //      des Programmablaufs deutlich messbar !!
          __asm__ volatile (        //
             "1: sbiw %0,1" "\n\t"  //      schwankt >> mit Temperatur ! ! !
             "brne 1b"
             : "=w" (__c)          : "0" (__c)
          );
        }
      }
    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Meine Hoffung auf ein besseres Verständnis meinerseits durch Auflösen der Klammern erfüllten sich nie - das wird dadurch eher noch kryptischer.
    Code:
    // - - - - - - - - - - - - - - - -
      {                             //
      for(; ms>0; ms--)
        {                           //
          uint16_t __c =  26;       // Dieser Wert dient zur Anpassung an ControllerClock etc.
          __asm__ volatile ( "1: sbiw %0,1" "\n\t" "brne 1b" : "=w" (__c)  : "0" (__c)  );
        }
      }
    // - - - - - - - - - - - - - - - -
    Auch das Studium des entsprechenden Abschnittes der *.lls-Datei half mir nie. Daher die Bitte um Hilfe.

    Ausschnitt "*.lls"
    Code:
    00000032 <wms>:
        for(; ms>0; ms--) 
        {                           //
        uint16_t __c =  26;         // Ergibt am 14Dez21,17h34 1,001 sec bei wms=1000
                                    // => bei wms ( 100) ist der (periphere) Einfluss
                                    //      des Programmablaufs deutlich messbar !!
          __asm__ volatile (        //
      32:    2a e1           ldi    r18, 0x1A    ; 26
      34:    30 e0           ldi    r19, 0x00    ; 0
    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      void wms ( uint16_t ms )      // 128 kHz-wms; Pausenwert ist nur experimentell !
                                    //      M E I S T   keine Millisekunde ! ! !
    // - - - - - - - - - - - - - - - -
      {                             // Weiter Messwerte in L3_tmr10.c und früher
        for(; ms>0; ms--) 
      36:    04 c0           rjmp    .+8          ; 0x40 <__SREG__+0x1>
        {                           //
        uint16_t __c =  26;         // Ergibt am 14Dez21,17h34 1,001 sec bei wms=1000
                                    // => bei wms ( 100) ist der (periphere) Einfluss
                                    //      des Programmablaufs deutlich messbar !!
          __asm__ volatile (        //
      38:    f9 01           movw    r30, r18
      3a:    31 97           sbiw    r30, 0x01    ; 1
      3c:    f1 f7           brne    .-4          ; 0x3a <__CCP__+0x6>
    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      void wms ( uint16_t ms )      // 128 kHz-wms; Pausenwert ist nur experimentell !
                                    //      M E I S T   keine Millisekunde ! ! !
    // - - - - - - - - - - - - - - - -
      {                             // Weiter Messwerte in L3_tmr10.c und früher
        for(; ms>0; ms--) 
      3e:    01 97           sbiw    r24, 0x01    ; 1
      40:    00 97           sbiw    r24, 0x00    ; 0
      42:    d1 f7           brne    .-12         ; 0x38 <__CCP__+0x4>
             "1: sbiw %0,1" "\n\t"  //      schwankt >> mit Temperatur ! ! !
             "brne 1b" 
             : "=w" (__c)          : "0" (__c) 
          ); 
        } 
      }
      44:    08 95           ret
    
    00000046 <main>:
    // ============================================================================= =
    Danke im Voraus für Eure Mühe.
    Ciao sagt der JoeamBerg

  2. #2
    Erfahrener Benutzer Robotik Einstein Avatar von Searcher
    Registriert seit
    07.06.2009
    Ort
    NRW
    Beiträge
    1.703
    Blog-Einträge
    133
    Hallo Joe,
    ich versuche mich mal am Assembler Teil:

    Da wird in C mit Inline Assembler in einer Schleife eine Millisekunde versucht zu
    erzeugen. Bei 128kHz ist ein Maschinenzyklus 1/128000Hz = 7,8125µs lang. Die Sequenz
    3a: 31 97 sbiw r30 , 0x01
    3c: f1 f7 brne .-4 ; 0x3a

    subtrahiert von dem, in der Routine eingestellten Wert 26 (uint16_t __c = 26 ) den Wert eins solange, bis dieser 0 ist. 25 Subtraktionen und Sprünge auf Adresse 3a brauchen je 4 Zyklen. 4 * 25 * 7,8125 sind 781,25µs + 23,1375 = 804,6875µs. 3 * 7,8125µs = 23,1375µs für den letzten Schleifendurchlauf ohne Sprung.

    Wenn 26 auf 0 runtergezählt ist, wird vom übergebenen Millisekundenwert eine Millisekunde abgezogen, und wenn nicht Null, dann Sprung auf 0x38 und wieder eine knappe Millisekunde (0,804..ms) Wartezeit erzeugt. Dafür wird bei Adresse 0x38 zunächst wieder der Wert 26 mit movw herausgekramt und dann wieder die Warteschleife durchlaufen. Jedes sbiw verschlingt 2 Maschinenzyklen. Der nachfolgende Vergleich mit Sprung auf 3x38 auch nochmal 2 Zyklen. Um zu ergründen, warum da mal eine 0 abgezogen wird, müßte man tiefer in asm einsteigen. Möglicherweise richten des Carry Flags.

    Justierung der genauen Millisekunde also am Wert von uint16_t __c unter Berücksichtigung des Maschinentaktes.

    Code:
    00000032 <wms>:
        for(; ms>0; ms--) 
        {                           //
        uint16_t __c =  26;         // Ergibt am 14Dez21,17h34 1,001 sec bei wms=1000
                                    // => bei wms ( 100) ist der (periphere) Einfluss
                                    //      des Programmablaufs deutlich messbar !!
          __asm__ volatile (        //
    ** der Wert 26 wird als 2 Byte in die Prozessorregister 18, 19 geladen.
      32:    2a e1           ldi    r18, 0x1A    ; 26
      34:    30 e0           ldi    r19, 0x00    ; 0
    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      void wms ( uint16_t ms )      // 128 kHz-wms; Pausenwert ist nur experimentell !
                                    //      M E I S T   keine Millisekunde ! ! !
    // - - - - - - - - - - - - - - - -
      {                             // Weiter Messwerte in L3_tmr10.c und früher
        for(; ms>0; ms--) 
      36:    04 c0           rjmp    .+8          ; 0x40 <__SREG__+0x1>
        {                           //
        uint16_t __c =  26;         // Ergibt am 14Dez21,17h34 1,001 sec bei wms=1000
                                    // => bei wms ( 100) ist der (periphere) Einfluss
                                    //      des Programmablaufs deutlich messbar !!
          __asm__ volatile (        //
    ** der Wert 26 wird aus r18, r19 nach r30, r31 übertragen
      38:    f9 01           movw    r30, r18
    ** Beginn der Schleife für eine ms
      3a:    31 97           sbiw    r30, 0x01    ; 1
    ** Sprung nach 3a wenn noch nicht Null
      3c:    f1 f7           brne    .-4          ; 0x3a <__CCP__+0x6>
    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      void wms ( uint16_t ms )      // 128 kHz-wms; Pausenwert ist nur experimentell !
                                    //      M E I S T   keine Millisekunde ! ! !
    // - - - - - - - - - - - - - - - -
      {                             // Weiter Messwerte in L3_tmr10.c und früher
        for(; ms>0; ms--) 
    ** Abzug von eins vom übergebenen Millisekundenwert, der sich anscheinend in r24, r25 befindet.
      3e:    01 97           sbiw    r24, 0x01    ; 1
      40:    00 97           sbiw    r24, 0x00    ; 0
    ** Wenn übergebener ms Wert noch nicht Null, dann nochmal Sprung zur 1ms Laufschleife
      42:    d1 f7           brne    .-12         ; 0x38 <__CCP__+0x4>
             "1: sbiw %0,1" "\n\t"  //      schwankt >> mit Temperatur ! ! !
             "brne 1b" 
             : "=w" (__c)          : "0" (__c) 
          ); 
        } 
      }
    ** Rücksprung zu Aufrufort
      44:    08 95           ret
    
    00000046 <main>:
    // =============================================================================

    Gruß
    Searcher
    Hoffentlich liegt das Ziel auch am Weg
    ..................................................................Der Weg zu einigen meiner Konstruktionen

  3. #3
    Erfahrener Benutzer Robotik Visionär Avatar von oberallgeier
    Registriert seit
    01.09.2007
    Ort
    Oberallgäu
    Beiträge
    8.692
    .. ich versuche mich mal am Assembler Teil: ..
    Danke für Deine Mühe, das hilft mir schon mal etwas weiter. Die eigentlichen Assemblerbefehle hatte ich schon aus dem Studio4 rausgelesen; die hatte ich zwar nicht parat, aber in meinen AVR-Anfangszeiten hatte ich wenige, einfache Aufgaben (z.B. Servotester mit Potiabfrage etc) in Assembler programmiert. Daher sagten mir zB sbiw oder brne schon etwas. (Es gibt auch noch Rudimente zu 8086- oder Z80-Assembler . . . aus der Studienzeit).

    Code:
    ;Auszug aus Servotester; beispielhaft
      mc_init:    ;=== Initialisiere Mikrocontroller
                  ; PB0 (servo1) + PB3 (servo2) = Ausgang, PB4 (poti1) = ADC-Eingang
        ldi    r16,0b00001000    ;Ausgang (1) auf PB3, Eingang (0) auf PB4
        out    ddrb,r16          ;Datenrichtungsbits setzen, Port ist Ausgang
        ldi    r16,0b11100111    ;Datenrichtungsbits setzen
        out    portb,r16         ;  und initialisieren
        ret                     ;=====----->>>>>
    Unverständlich sind mir noch Zahlendarstellung dieser Art : rjmp .+8 , brne .-4 , brne .-12.

    Ebenso unverständlich ist mir noch der Zusammenhang zwischen Assemblercode und Kommentar wie hier :
    ........rjmp .+8 ; 0x40 <__SREG__+0x1>
    ........brne .-4 ; 0x3a <__CCP__+0x6>
    ........brne .-12 ; 0x38 <__CCP__+0x4>

    SREG hatte ich in der Studio4-Assemblerhilfe gefunden, ist klar. CCP dagegen gibts weder im Instruction Set Summary der Controllerdokumentation noch in der Studio4-Hilfe.

    Lücken über Lücken - auch die Zeilen
    ........sbiw %0,1
    ........\n\t
    ........brne 1b
    ........: "=w" (__c) : "0" (__c)
    sind geheimnisvoll.

    Ich sehe, dass ich sehr umfassende Wissenslücken zu Assembler habe. Daher sollte ich die Titelfrage ändern in: Wo kann man diese Fragen - und alle sonstigen Feinheiten - zu Assembler lernen? Buch? (www-)Tutorial? oder . . .

    Danke für die Aufmerksamkeit
    Ciao sagt der JoeamBerg

  4. #4
    Erfahrener Benutzer Robotik Einstein Avatar von Searcher
    Registriert seit
    07.06.2009
    Ort
    NRW
    Beiträge
    1.703
    Blog-Einträge
    133
    Hallo,

    Zitat Zitat von oberallgeier Beitrag anzeigen
    Unverständlich sind mir noch Zahlendarstellung dieser Art : rjmp .+8 , brne .-4 , brne .-12.
    Ich benutze immer das Instruction set
    http://ww1.microchip.com/downloads/e...set-manual.pdf

    Da sieht man, daß das Sprungziel bei den meisten Sprünge wie auch die Sprünge bei Verzweigungen wie bei brne relativ mit einer Konstanten k angegeben werden. Der Assembler/Compiler rechnet die Konstante anhand des Sprunglabels aus und trägt es in den Befehl ein. Die .+8 bzw .-4 ist die relative Sprungweite. Dahinter schreibt der Assembler nochmal die Programmcounter Adresse als absoluten Wert wie 0x3A oder so ein. Die relative Sprungweite scheint zu groß zu sein, hat aber vermutlich damit zu tun, daß der Programmcounter schon auf den nächsten Befehl zeigt.


    Ebenso unverständlich ist mir noch der Zusammenhang zwischen Assemblercode und Kommentar wie hier :
    ........rjmp .+8 ; 0x40 <__SREG__+0x1>
    ........brne .-4 ; 0x3a <__CCP__+0x6>
    ........brne .-12 ; 0x38 <__CCP__+0x4>

    SREG hatte ich in der Studio4-Assemblerhilfe gefunden, ist klar. CCP dagegen gibts weder im Instruction Set Summary der Controllerdokumentation noch in der Studio4-Hilfe.
    Der Compiler erzeugt auch eine .lst Datei. Dort findet man die Zuordnung der Namen zu Adressen unter DEFINED SYMBOLS ganz unten in der Datei.
    Die 0x38 beim letzen brne liegt 0x4 Adressen über der Adresse von __CCP__ (0x3a liegt 0x6 über __CCP__). __CCP__ war bei mir das MCUSR Register (Schulterzuck )
    Das gleiche mit __SREG__ . Wozu die Angabe gut ist? Keine Ahnung.


    Lücken über Lücken - auch die Zeilen
    ........sbiw %0,1
    ........\n\t
    ........brne 1b
    ........: "=w" (__c) : "0" (__c)
    sind geheimnisvoll.
    Das sind die Inline Assembler Geheimnisse/Syntax von C. Aufschluß könnte der Artikel im RN-Wissen geben:
    https://rn-wissen.de/wiki/index.php/...ler_in_avr-gcc

    Ich sehe, dass ich sehr umfassende Wissenslücken zu Assembler habe. Daher sollte ich die Titelfrage ändern in: Wo kann man diese Fragen - und alle sonstigen Feinheiten - zu Assembler lernen? Buch? (www-)Tutorial? oder . . .
    Da habe ich leider keine spezielle Empfehlung. Ich hatte meine ersten Assembler Erfahrungen mit dem C64. Jetzt versuche ich andere Beispielcodes zu verstehen bzw wie andere Authoren bestimmte Probleme lösen und nutze für meine asm-Routinen hauptsächlich das o.g. Instruction Set Manual.

    Gruß
    Searcher
    Hoffentlich liegt das Ziel auch am Weg
    ..................................................................Der Weg zu einigen meiner Konstruktionen

  5. #5
    Erfahrener Benutzer Robotik Visionär Avatar von oberallgeier
    Registriert seit
    01.09.2007
    Ort
    Oberallgäu
    Beiträge
    8.692
    .. Ich benutze immer das Instruction set ..
    Danke, prima, das AVR-Instruction-Set-Manual hatte ich noch nicht. Nie gesehen. DAS ist ja wirklich sehr informativ - viel Lesestoff für diesen Winter *gg*. Dann noch der RN-Wissen-Abschnitt. Ja, so wirds wohl durchsichtig.

    Erstmal vielen, vielen Dank!
    Ciao sagt der JoeamBerg

Ähnliche Themen

  1. Code läuft nicht feherfrei, bitte um Hilfe! Input Capture und UART nicht gleichzeitig
    Von Accenter im Forum Basic-Programmierung (Bascom-Compiler)
    Antworten: 14
    Letzter Beitrag: 04.06.2013, 21:00
  2. Antworten: 0
    Letzter Beitrag: 29.01.2013, 20:33
  3. [ERLEDIGT] Eine Bitte bezüglich des Code-Tag Buttons
    Von TobiKa im Forum Anregungen/Kritik/Fragen zum Forum und zum Wiki RN-Wissen
    Antworten: 4
    Letzter Beitrag: 23.03.2011, 15:44
  4. C Anfänger! Bitte code angucken
    Von Freakster235 im Forum C - Programmierung (GCC u.a.)
    Antworten: 7
    Letzter Beitrag: 21.02.2008, 17:10
  5. Schaut euch mal bitte diesen Code an:
    Von jagdfalke im Forum C - Programmierung (GCC u.a.)
    Antworten: 6
    Letzter Beitrag: 26.11.2005, 20:49

Berechtigungen

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

12V Akku bauen