-         

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

Thema: Problem bei 4 Kanal Servo PWM

  1. #1
    Erfahrener Benutzer Roboter-Spezialist
    Registriert seit
    12.06.2006
    Beiträge
    473

    Problem bei 4 Kanal Servo PWM

    Anzeige

    Hallo Hallo,

    ich bin dabei mir einen 4 Kanal Servo PWM Generator bauen.
    Prinzip:

    Takt = 8MHz

    Ich habe zwei ISR:
    die eine wird angesprungen, wenn eine ADC fertig ist.
    hier werden AD Werte in die Ram Adressen 108 bis 111 geladen
    und der AD Kanal um 1 erhöht.

    Die andere bei einem Comparematch des TIMER 2 mit Prescaller = 1 und OCR2 = 30 zum Zug. Dort werden PINS des PORTD LOW gelegt und ein Zycluszähler erhöht.



    Zu Begin des Programs werden 4 Analogwerte eingelsen.
    Diese werden der Größe nach sortiert und mit dem Zugehörigen Pin in den Ramadressen 100 - 108 gespeichert.
    ************************************************** *******
    Nach dem Sortieren kommt eine Verzögerungsschleife um ca. 18ms zu erreichen.
    Dann wird der PORTD mit 01010101 belegt.(die Servo sind immer eins versetzt)
    Nun folgt eine Verzögerung von 1ms.
    Bis hierhin geht auch alles.

    Der Pegel bleibt solange HIGH bis der Zyclus den selben Wert hat, wie
    der dazugehörige PIN. Das Problem ist jetzt, dass diese Schleife bei etwa 3,8 ms liegt.(Osszi)
    Findet sich im Code mit einer !!!!!-Reihe.

    Im Simulator des AVR Studio läuft das Ganze.
    Hat jemand eine Idee?

    Code:
    .include "m8def.inc"
    
    .def tmp = r16
    .def lowe = r17
    .def lowz = r18
    .def highe = r19
    .def merker = r20
    .def konst = r21
    .def zyclus = r22
    .def kanal = r23
    .def zyclusnummer = r24
    .def pin = r25
    
    .def xlow = r26
    .def xhigh = r27
    .def ylow = r28
    .def yhigh = r29
    .def zlow = r30
    .def zhigh = r31
    
    .org 0x000
      rjmp reset
    
    .org OC2addr	; OCR2 Interrupt Vector Address
        rjmp hitvalue
    
    .org ADCCaddr
      rjmp adcc
      
    reset:
       ;Stack wird bei Interrupts benötigt! 
       ldi r16,HIGH(RAMEND) 
       out SPH,r16 
       ldi r16,LOW(RAMEND) 
       out SPL,r16
    ldi tmp,0b00000010
    out TCCR0,tmp
    ldi konst,8
    ldi xlow,100
    
    
    ldi ylow,116
    ldi tmp,1
    st y+,tmp
    ldi tmp,4
    st y+,tmp
    ldi tmp,16
    st y+,tmp
    ldi tmp,64
    st y+,tmp
    
    ldi ylow,108
    ldi zlow,108
    
    ldi lowe,255
    ldi tmp,0b00100000
    out DDRC,tmp
    ldi tmp,0b01010101
    out DDRD,tmp
    ldi tmp,0b011000000
    out ADMUX,tmp
    ldi tmp,30
    out OCR2,tmp
    
    nop 
    nop 
    nop
    
    sei
    main:
    ;*****************************
    ;***ADC Freerun + Interrupt***
    ;*****************************
    ldi r16,0
    ldi r17,255
    ldi r18,0
    ldi r19,0
    ldi r20,0
    ldi r21,0
    ldi r22,0
    ldi r23,0
    ldi r24,0
    ldi r25,0
    ldi r26,100
    ldi r27,0
    ldi r28,108
    ldi r29,0
    ldi r30,108
    ldi r31,0
    
    ldi kanal,0b011000000
    out ADMUX,kanal
    ldi tmp,0b11001101    ;PRESCALER FEHLT
    out ADCSRA,tmp
    
    ldi xlow,108
    
    pos:
    cpi r26,112
    breq voll2
    rjmp pos
    voll2:
    ldi xlow,100
    ldi lowe,255
    
    
    
    schleife2:
    
       schleife1:
    
          ld lowz,y ;108 - 109 - 110 - 111
          cp lowz,lowe
          brsh istgrossr
          mov lowe,lowz
          mov zlow,ylow ;Z-Pointer zeigt auf Adresse mit kleinstem Wert     (KW)
          istgrossr:                                                       ;(KW)
          inc ylow ;109 - 110 - 111 - 112                                  ;(KW)
    
       cpi r28,112                                                         ;(KW)
       brlo schleife1                                                      ;(KW)
       add r30,konst  ;0b10000000 => Bereich 136 bis 139                   ;(KW + 4)
       ld merker,z ;dort ist der PIN Wert gespeichert                      ;(KW + 4)
       subi r30,8                                                          ;(KW)
       
       st x+,lowe ;100 - 102 - 104 - 106                                   ;(KW)
       st x+,merker;101 - 103 - 105 - 107                                  ;(KW)
       ldi lowe,255                                                        ;(KW)
       
       st z,lowe                                                           ;(KW)
       ldi ylow,108
       ldi zlow,108
    
    cpi xlow,108
    brlo schleife2
    
     ldi r26,100
    ld r0,x+;100 zyclus 
    ld r1,x+;101 pin
    ld r2,x+;102
    ld r3,x+;103
    ld r4,x+;104
    ld r5,x+;105
    ld r6,x+;106
    ld r7,x+;107
    ldi r26,100
    ldi r30,108
    ldi tmp,0
    sorten:
    
    ld lowe,x+ ;100 - 107
    st z+,lowe ;108 - 115
    ld pin,x+
    add pin,tmp
    mov tmp,pin
    st z+,pin  ;108 - 115
    ld lowz,x
    
    cp lowz,lowe
    brne diff
    dec r30
    dec r30
    rjmp gleich
    diff:
    ldi tmp,0
    gleich:
    cpi r26,108
    brne sorten
    
    
    
    
    
    ldi r26,108
    ld r8,x+;108 zyclus 
    ld r9,x+;109 pin
    ld r10,x+;110
    ld r11,x+;112
    ld r12,x+;113
    ld r13,x+;114
    ld r14,x+;115
    ld r15,x+;116
    
    ldi r26,0
    ldi tmp,0
    out TCNT0,tmp
    
    waita:
       in tmp,TCNT0
       cpi tmp,252
       brlo waita
       ldi tmp,0
       out TCNT0,tmp
       inc r26
       cpi r26,72
    brlo waita
    
    
    
    ldi tmp,0b00001001 ;RPESCALER 1
    out TCCR2,tmp
    
    ldi r26,108
    ldi tmp,0
    out TCNT2,tmp
    
    
    
    ldi tmp,0b01010101
    out PORTD,tmp
    ldi tmp,0b10000000  
    out TIMSK,tmp
    ldi zyclus,0
    warten1:
    cpi zyclus,250
    breq wigger
    
    rjmp warten1
    wigger:
    sbi PORTC,5
    ldi zyclus,0
    ;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    pulse:
    
       ld zyclusnummer,x+
       ld pin,x+
       in tmp,PORTD
       com pin
       and pin,tmp
    
       loop:
       cp zyclusnummer,zyclus
       brne loop
       
       in tmp,PORTD
    
       cpi tmp,0
    brne pulse
    cbi PORTC,5
    warten:
    cpi zyclus,249
    brsh ende
    rjmp warten
    ende:
    
    ldi tmp,0
    out TIMSK,tmp
    
    ldi ylow,108
    ldi r30,108
    ldi r26,100
    ldi tmp,0
    nullen:
    st x+,tmp
    cpi r26,116
    brlo nullen
    ldi merker,0
    ldi zyclusnummer,0
    
    rjmp main
    
    
    
    
    ;**********************************
    ;speichert den ADC Wert in die 
    ;Vektoradresse und schaltet auf den 
    ;nächsten ADC Kanal
    ;**********************************
    adcc:             
    in tmp,ADCH
    
    andi tmp,0b11111100
    st x+,tmp  ;108=>109=>110=>111=>112
    cpi kanal,0b011000011
    brsh fertig
    inc kanal
    out ADMUX,kanal
    ldi tmp,0b11001101    ;PRESCALER FEHLT
    out ADCSRA,tmp
    fertig:
    reti
    
    hitvalue:
    inc zyclus
    cp zyclus,zyclusnummer
    brne nicht
    out PORTD,pin
    nicht:
    reti
    Danke,
    The Man

  2. #2
    Erfahrener Benutzer Robotik Visionär
    Registriert seit
    26.11.2005
    Ort
    bei Uelzen (Niedersachsen)
    Beiträge
    7.942
    Das mit dem Sortieren habe ich nicht verstanden. Bei den ISR routinen ist noch ein Fehler drinn:
    Am anfang der ISR Routine muß man das Porzessor status register sichern, sonst werden die Flags für das Hauptprogramm geändert. Ähnlich muß man entweder seperate Register für die ISR routinen nehmen oder die Register vorher retten.

  3. #3
    Erfahrener Benutzer Roboter-Spezialist
    Registriert seit
    12.06.2006
    Beiträge
    473
    Was hat das mit dem Prozessor Register auf sich? Kann ich einfach sagen z.B.
    in tmp,SREG ?

    Was das ändern von registern in den ISR angeht, dass ist schon so gewollt.

    Zum sortieren:
    Ich lese AD Kanäle 0 bis 3 ein.
    Dabei ist jeder Wert nachher entsprechend seinem Kanal für einen bestimmten PIN. Jetzt wollte ich das nicht so machen, dass ich die HIGH Pegel hinter einander schachtele, sondern alle in einem einzigen Durchgang behandele. Dabei löst der TIMER2 mit Presc = 1 bei OCR2 = 32 und 8MHz einen Comparematch Interrupt aus. In der ISR wird die Anzahl mit gezählt. Stimmt diese mit dem ADC Wert einer der PIN´s überein, wird der entsprechende LOW gesetzt. Da die AD Werte ja nicht der Größe nach von Kanal 0 bis 3 ansteigen, muss man sie vorher sortieren und ggf. selbe kombinieren. Der Witz an der Sache ist, das ich theoretisch einige mehr als acht PWM erzeugen könnte - wenn ich das irgenwann mal vorhabe und außerdem ist der µC nicht nur ausschließlich mit der Generierung beschäfftigt und könnte sich in den ewig langen 18 ms um etliches anderes kümmern.

    Greetings
    Chuck Norris kann Windows Vista auf einem Atmel in Assembler implementieren!
    Chuck Norris coded mit 3 Tasten:"1","0" und "compile"

  4. #4
    Erfahrener Benutzer Robotik Visionär
    Registriert seit
    26.11.2005
    Ort
    bei Uelzen (Niedersachsen)
    Beiträge
    7.942
    Die idee mit in tmp,SREG ist schon mal richtig. Man hat dann allerdings tmp mit SREG belegt und den alten inhalt von tmp überschreiben. Eine typische ISR routine fängt daher so an:
    push tmp
    in tmp,SREG
    push tmp
    Das retten der Register ist ein ganz wesentlicher Teil der ISR programmierung. Am besten noch mal ein Tutorial lesen, die könne das bestimmt besser erklären. Wenn man genug register frei hat, kann man auch extra Register für die Interrupts reservieren und kann sich das retten auf den Stack sparen.
    Das mit dem sortieren hab ich jetzt verstanden, ist mal ein anderer Ansatz als üblich.

  5. #5
    Erfahrener Benutzer Roboter-Spezialist
    Registriert seit
    12.06.2006
    Beiträge
    473
    Ich geb zu, dass mit dem Pusg und so hab ich schon gemacht, ohne auf die Antwort zu warten.
    Geht aber noch nicht, muss ich mir mal mit nem Osszi ansehen.

    Was währe denn eine übliche Methode?

  6. #6
    Erfahrener Benutzer Robotik Visionär
    Registriert seit
    26.11.2005
    Ort
    bei Uelzen (Niedersachsen)
    Beiträge
    7.942
    Zum debuggen wäre der Simulator von AVRStudio bestimmt besser als ein Oszilloskop.
    Die üblich Methode um das PWM Signal zu erzeugen, ist es die Kanäle weitgehend unabhängig zu berechnen. Also einen Zähler hochlaufen lassen und für die Kanäle eine 1 ausgeben, für die der Wert größer als der Zähler ist. Der eine Zähler kann für mehrere Kanäle benutzt werden, aber sonst sind Kanäle unabhängig. Wenn man es mit Interrrupt realisieren will, dann macht der Interrupt im wesentliche folgendes:
    1) Kanal 1 mit Zähler vergleiche und 1 oder 0 ausgeben
    2) Kanal 2,3,... mit Zähler vergleiche ...
    3) Zähler hochsetzen
    4) testen of Zähler fertig ist -> neu von 0 Starten


    Für die Servos muss nach dem Puls noch eine Lange pause dazu.

  7. #7
    Erfahrener Benutzer Roboter-Spezialist
    Registriert seit
    12.06.2006
    Beiträge
    473
    Der Witz ist, im Simulator läuft das Program vorallem auch beliebig oft.
    Nur auf dem µC dann nicht mehr... Und das der zu langsam ist kann nicht sein, da der Simulator mit 4 und der AVR dann mit 8MHz läuft.

    Ok, mein Ziel war es, während des HIGH Pegels möglichst wenig Aufwand zu haben und alles was man vorher machen kann zu erledigen. Aber dann versuche ich es mal mit der Variante.

    Danke,
    The Man
    Chuck Norris kann Windows Vista auf einem Atmel in Assembler implementieren!
    Chuck Norris coded mit 3 Tasten:"1","0" und "compile"

  8. #8
    Erfahrener Benutzer Robotik Visionär
    Registriert seit
    26.11.2005
    Ort
    bei Uelzen (Niedersachsen)
    Beiträge
    7.942
    Im Simulator ist zwar eine taktfrequenz eingetragen, die dient aber nur zur umrechnung in Zeiten. Die tatsächliche Geschwindigkeit mit der simuliert wird ist deutlich langsamer und hängt vom PC ab.
    Gerade wenn etwas mit dem Retten der Register im interrrupt nicht richtg ist, kann das eine ganze weile (ein paar sekunden ?) gut gehen. Der simulator ist so langsa, dass man kaum die Geduld aufbringt so lange zu simulieren. Man kann solche fehler am besten erkennen wenn man an den Anfang der ISR Routine einen Breakpoint setzt und dann die OSR Routine im Einzelschritt durchläuft. Vor dem RTI müssen alle normalen Register wieder so sein wie zuvor. Sonst poste doch noch mal den Code. Die eigenen Fehler findet man halt oft schwieriger als fremde.

  9. #9
    Erfahrener Benutzer Roboter-Spezialist
    Registriert seit
    12.06.2006
    Beiträge
    473
    Also ich hab hier jetzt mal die übliche Variante angewendet.
    in

    ich starte den ADC mit Kanal 0
    und lese das ein, wenn die Conversion fertig ist.

    das läuft dann vier mal, jedes mal mit dem nächsten ADC Kanal und einem anderen Register zum speichern.

    dann ca.8ms verzögern

    dann 1 ms verzögern

    und dann in eine schleife bis PORTD = 0 ist und danach warten bis auch die zweite ms vorbei ist.

    Das seltsame ist, ich beeinflusse alle AD Werte mit allen Potis?!!?

    Das register "pause" stellt sicher, dass der Port nicht schon während der ersten ms bearbeitet wird. Sieht man auch in der ISR.

    hier der Code:
    Code:
    .include "m8def.inc"
    
    .def tmp = r16
    .def k0 = r17
    .def k1 = r18
    .def k2 = r19
    .def k3 = r20
    .def zyclus = r21
    .def kanal = r22
    .def pause = r23
    .org 0x000
      rjmp reset
    
    .org OC2addr	; OCR2 Interrupt Vector Address
        rjmp hitvalue
    
    
      
    reset:
       ;Stack wird bei Interrupts benötigt! 
       ldi r16,HIGH(RAMEND) 
       out SPH,r16 
       ldi r16,LOW(RAMEND) 
       out SPL,r16
    ldi tmp,0b00000010
    out TCCR0,tmp
    
    ldi tmp,0b00100000
    out DDRC,tmp
    ldi tmp,0b01010101
    out DDRD,tmp
    ldi tmp,0b011000000
    out ADMUX,tmp
    ldi tmp,30
    out OCR2,tmp
    
    nop 
    nop 
    nop
    
    
    sei
    main:
    
    ldi kanal,0b01100000
    out ADMUX,kanal
    ldi tmp,0b11000101    ;PRESCALER 32
    out ADCSRA,tmp
    kanal0:
    in tmp,ADCSRA
    sbrs tmp,4
    rjmp kanal0
    in k0,ADCH
    
    
    
    ldi kanal,0b01100001
    out ADMUX,kanal
    ldi tmp,0b11000101    ;PRESCALER 32
    out ADCSRA,tmp
    kanal1:
    in tmp,ADCSRA
    sbrs tmp,4
    rjmp kanal1
    in k1,ADCH
    
    
    
    ldi kanal,0b01100010
    out ADMUX,kanal
    ldi tmp,0b11000101    ;PRESCALER 32
    out ADCSRA,tmp
    kanal2:
    in tmp,ADCSRA
    sbrs tmp,4
    rjmp kanal2
    in k2,ADCH
    
    
    ldi kanal,0b01100011
    out ADMUX,kanal
    ldi tmp,0b11000101    ;PRESCALER 32
    out ADCSRA,tmp
    kanal3:
    in tmp,ADCSRA
    sbrs tmp,4
    rjmp kanal3
    in k3,ADCH
    
    
    ldi tmp,0
    out TCNT0,tmp
    
    ;_________________18ms Pause_____________-
    waita:
       in tmp,TCNT0
       cpi tmp,252
       brlo waita
       ldi tmp,0
       out TCNT0,tmp
       inc r26
       cpi r26,74
    brlo waita
    ;________________________________________-
    
    
    ldi tmp,0b00001001 ;RPESCALER 1
    out TCCR2,tmp
    
    ldi tmp,0
    out TCNT2,tmp
    
    
    
    ldi tmp,0b01010101
    out PORTD,tmp
    ldi tmp,0b10000000  
    out TIMSK,tmp
    ldi zyclus,0
    ldi r26,0
    
    ;*******************1ms Offset*****************
    warten1:
    cpi zyclus,250
    breq wigger
    
    rjmp warten1
    wigger:
    ;**********************************************
    
    sbi PORTC,5
    ldi zyclus,0
    out TCNT2,zyclus
    ;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    ldi pause,1
    
    pulse:
       
    in tmp,PORTD
       cpi tmp,0
    brne pulse
    
    warten:
    cpi zyclus,249
    brsh ende
    rjmp warten
    ende:
    cbi PORTC,5
    
    ldi zyclus,0
    out TCCR2,zyclus
    ldi pause,0
    ldi kanal,0b01100000
    out ADMUX,kanal
    rjmp main
    
    
    
    
    ;**********************************
    ;speichert den ADC Wert in die 
    ;Vektoradresse und schaltet auf den 
    ;nächsten ADC Kanal
    ;**********************************
    
    
    hitvalue:
    cpi pause,0
    breq null
    
    cp zyclus,k0
    brne ka
    cbi PORTD,0
    ka:
    
    cp zyclus,k1
    brne kb
    cbi PORTD,2
    kb:
    
    cp zyclus,k2
    brne kc
    cbi PORTD,4
    kc:
    
    cp zyclus,k3
    brne kd
    cbi PORTD,6
    
    kd:
    
    null:
    inc zyclus
    reti
    The Man

  10. #10
    Erfahrener Benutzer Robotik Visionär
    Registriert seit
    26.11.2005
    Ort
    bei Uelzen (Niedersachsen)
    Beiträge
    7.942
    In der ISR routinge feht immer noch das retten des Status registers. Alsoe z.B. in sr_save,SREG am Anfang der ISR und out SREG,SR_save vor RTI. Ohne retten des Status ist das Programm ziehmlich unberechenbar (soweit das mit ner CPU geht).

    Insgesammt ist das Porgramm schwer zu verstehen, weil fast keine Kommentare vorhanden sind. Das gegenseitige Beinflussen der AD Kanäle tritt typischerweise auf, wenn die Potis zu hochohmig sind (> 20 KOhm). Ein kleiner Kondensator (1nF...100nF) gegen Masse an jedem AD Eingang kann da Abhilfe schaffen.

Seite 1 von 2 12 LetzteLetzte

Berechtigungen

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