Forum: Mikrocontroller und Digitale Elektronik Hilfe Ladereglung Atmega16 Assembler


von werner (Gast)


Angehängte Dateien:

Lesenswert?

Hallo liebe Mitstreiter,
Ich weis, manche können die Überschrift schon gar nicht mehr lesen,
schon wieder einer der es nicht hinbekommt. Da ich mich ja nun geoutet 
habe
(mein Gott wie das klingt) hoffe ich doch Ihr helft mir weiter...Danke 
im vorraus allen Beteiligten.
Nun denn, ich bin seit Wochen damit beschäftigt eine vernünftige 
Ladereglung für eine Projektarbeit zu konstruieren.
Das größte Problem war bisher der StepDown und dessen Ansteuerung, was 
aber nun halbwegs zufriedenstellend funktioniert.
Nun ist es aber so das der StepDown lastabhängig fungiert, heist wenn 
ich z.B. eine 12V/20W Halogenlampe anschließe muss ich mit anderen 
Vorladewerten (tc0/ocr0) arbeiten als mit einem Akku .
Ich versuche gerade mittels Vergleich von einer vorher ermittelten 
festen Spannungsvorgabe von 13.6V die sich ändernde Ausgangsspannung am 
StepDown zu regeln.
Wie Ihr euch wahrscheinlich denken könnt ohne Erfolg ( sonst gäbe es ja 
diesen Beitrag nicht).
Verwendete Schaltung und Programm sind dabei, vielleicht hat ja einer ne 
Idee, auch wie man es vielleicht anders machen könnte aber ohne fertige 
Spannungsregler wie z.B lm2596 und die anderen
üblichen Verdächtigen (find ich herrlich diese Formulierung)
Ich hab auch schon viel selber recherchiert, aber irgendwie nix 
brauchbares gefunden.
Für die C-Freunde unter euch, es muss in Assembler sein weil ich das 
langsam zu verstehen anfange.
Gruß Werner

von werner (Gast)


Lesenswert?

Ich hab noch vergessen zu erwähnen das als Spannungsquelle für den 
Versuchsaufbau ein Kleinspannungsstelltrafo mit Gleichspannungsausgang 
fungiert, welchen ich auf 17V gestellt habe.

Gruß Werner

von Ben _. (burning_silicon)


Lesenswert?

Ich find schon mal unschön, daß der FET nicht aktiv gesperrt wird. Ohne 
einen vernünftigen FET-Treiber brauchst Du keinen Step-Down bauen weil 
das vom Wirkungsgrad her nicht viel besser wird als ein Linearregler. So 
ein Wandler braucht 40-50kHz damit er vernünftig funktioniert. Unter 10 
Mhz CPU-Takt für einen 8 Bit Timer würd ich da nichts probieren (gibt 
dann 39kHz für die PWM).

EDIT: die 5V an ARef sind auch mal **würg**... Genau wie das nicht 
verstandene und nur ausprobierte Widerstandsgewirre um den Transistor.

Ich glaube wir sollten erstmal die Grundlagen durchkauen.

von Karl H. (kbuchegg)


Lesenswert?

werner schrieb:

> Für die C-Freunde unter euch, es muss in Assembler sein weil ich das
> langsam zu verstehen anfange.

Mit Verlaub, aber das ist ganz großer Unsinn, solange du sowas ...
1
  call timer0ini ;Timer 0  initialisieren
2
  call adini    ;A-D Wandler AD0 initialisieren
3
4
....
5
6
adini:  
7
    cli
8
    ldi temp1, 0b01000000  ;;AREF, linkssbündig,pa0
9
    out ADMUX, temp1
10
    ldi  temp1,0b11101101
11
    out  ADCSR,temp1     ;adcen,adcie,adcfr,teiler 32
12
        sei            ;Interrupts global aktivieren                  
13
    jmp main
14
    *********


... schreibst.

Du hast Assembler nicht verstanden und du verfügst auch nicht über die 
Programmierdisziplin, die dazu notwendig ist. Du machst dir nur selber 
das Leben schwer, indem du keinen Compiler benutzen willst, der dich von 
diesem ganzen Kleinkram befreit.

von Ben _. (burning_silicon)


Lesenswert?

- Wieso initialisierst du den Stack doppelt? (einmal falsch, dann 
richtig)

- Wieso verarbeitest Du am Anfang irgendwelche ADC-Ergebnisse obwohl 
noch gar keine Wandlung gestartet wurde?

- Was sollen die NOPs?

- Dein Programm hat keinerlei Zeitbasis, es ist sehr wahrscheinlich daß 
es ständig zwischen irgendwelchen PWM-Werten hin und her pendelt ohne 
sich auf den Akku einstellen zu können...

- Feste RAM-Adressen sind K*cke, da sollte man Labels verwenden.

- Wo ist die Stromregelung fürs Laden? Wenn Du da einen Akku dranklemmst 
der 50A wegschluckt wird Dein Programm niemals die Akkuspannung 
erreichen. Du kannst also nicht nur auf die Akkuspannung einregeln...

- Jo, Funktionen nicht mit RET abgeschlossen bzw. Stackprinzip nicht 
verstanden... nur der Vollständigkeit halber.

von holger (Gast)


Lesenswert?

>nur der Vollständigkeit halber.

  ori r18, 0b00000010
  out timsk, r18      ;ocie0-löst beim erreichen das ladewertes irq aus

Fehlende Interruptroutinen;)

von Ben _. (burning_silicon)


Lesenswert?

Jop, fehlende Interrupttabelle -> crash...

von Krapao (Gast)


Lesenswert?

Das Programm hat gravierende Fehler und Unschönheiten und kann IMHO nie 
funktioniert haben.

Ich erkenne keinen Sinn in .org 0x30.

Der Stack wird zweimal initialisiert, Code wirkt dadurch 
zusammenkopiert.

Konfigurationen werden max. unportabel und unlesbar in 
Absolutschreibweise gemacht, obwohl es Bitnamen dafür gibt. (Folgendes 
gemäß Kommentare; Absolutwerte kontrolliere ich nicht mehr)

Der ADC wird ausgelesen, ohne dass geprüft wird, ob ein regulärer Wert 
vorliegt. Die erste Wandlung wird ausgelesen, ohne dass überhaupt eine 
Wandlung gestartet wurde.

Es werden IRQs (adcie, ocie0) freigegeben für die es keine ISR gibt.

Es wird eine Variable wild ins RAM (0x0072) gespeichert (und nie daraus 
gelesen). Wenn es eine dauerhafte Sicherung sein soll, liegt liegt 
eventuell ein Fehlverständnis RAM EEPROM vor. Temporäre Sicherung im RAM 
könnte einfacher mit dem Stack gemacht werden.

Bei Prescaler 0 (ori r17, 0b01101010    ;tccr0, fast pwm, prescaler 0, 
lösche oc0 bei gleichstand) läuft kein Timer!

In der Initialisierung von Timer0 ist nur ein Teil der Initialisierung 
vorhanden. Es fehlt die Angabe des Vergleichswertes.

Der Code hat Durchfall (nach der Stelle brlo down).

von Karl H. (kbuchegg)


Lesenswert?

Karl Heinz Buchegger schrieb:

> Du hast Assembler nicht verstanden

Ein anderes Beispiel
1
down:      
2
      dec v_akkupwm      ;akkuspannung zu hoch, zaehle runter  ;
3
      cp v_store, v_akkupwm  ;und vergleiche mit gespeicherter spannung
4
      brne down        ;noch nicht gleich? springe zu down
5
      breq sichern      ;wenn gleich, sichern

das ist eine maximalkomplizierte Variante von (zusammen mit der 
Vorbedingung, dass down nur im "größer" Fall angesprungen wird)
1
down:      
2
      mov v_akkupwm, v_store
3
      jump sichern

von werner (Gast)


Angehängte Dateien:

Lesenswert?

Vielen Dank für Euere Antworten,

Ben
"die 5V an ARef sind auch mal **würg**... Genau wie das nicht
verstandene und nur ausprobierte Widerstandsgewirre um den Transistor."

werden die 5V denn nicht so an ARef angeschlossen, ich dächte das so 
gelesen zu haben.
Und vollkommen richtig erkannt mit dem "nur ausprobierte 
Widerstandsgewirre um den Transistor" weil ich nur so ein brauchbares 
Signal am Fet habe und er durchschaltet und damit Uds gering bleibt.
Ich habe verschiedene Ansätze zu Ansteuerung probiert, unter anderem 
angehängtes Beispiel. Habe den Emitterwiderstand so dimensioniert das
ca. 13mA fließen und über dem Kollectorwiderstand 10V abfallen aber ohne 
Erfolg. Das "nur ausprobierte Widerstandsgewirre um den Transistor" war 
bis dato die beste Lösung.

Karl Heinz Buchegger
Wenn du schreibst das das Unsinn ist was ich da geschrieben habe, dann 
glaub ich Dir das erstmal, aber ich habe trotzdem keinen Schimmer was da 
Unsinn ist! Avr-Studio hat es nicht angemeckert.
Vielleicht hast du ja einen genauern Hinweis was verkehrt ist.

Gruß Werner

von Ben _. (burning_silicon)


Lesenswert?

Ein Assembler meckert über nichts außer Syntaxfehler. Ansonsten kannst 
Du jeden Scheiß machen (das ist ja das Tolle an Assembler). Du kannst 
gleich mit der ersten Anweisung ins Nirvana springen oder den Stack in 
die IO-Register klatschen... Das meiste davon ist Unsinn, manches birgt 
ungeahnte Möglichkeiten, die eine Hochsprache wie C niemals zulassen 
würde.

Der Haken an der Sache ist meiner Meinung, daß Du einfach nicht der 
geborene Programmierer bist. Du hast viele logische Fehler drin, 
entweder weil Du nicht programmieren kannst und noch Übung brauchst oder 
weil Du den Aufbau eines (Assembler-) Programms noch nicht verstanden 
hast. Das Programm macht wirklich einen sehr zusammenkopierten 
Eindruck... so als hättest Du Flicken zu je drei-fünf Zeilen aus 
irgendwelchen Beispielen herauskopiert ohne daß Du sie oder das ganze 
Programm verstehst.

An ARef gehört ein 100nF gegen Masse. Die 5Vcc als Referenz werden dann 
intern gewählt. AVcc gehört sauber entstört wenn Du gerade so dicht an 
einem Schaltregler akkurate Messergebnisse haben willst.

Dein Gate-Treiber ist vom Prinzip her richtig, aber unnötig kompliziert. 
Da wurde versucht mit den Widerständen die Gate-Spannung zu begrenzen 
damit's dem FET bei 36V nicht das Gate zerreißt. Da gibts einfachere 
Varianten solange Du unter 20V Eingangsspannung bleibst.

von werner (Gast)


Lesenswert?

Hallo Krapao,

das der Stack 2 mal intiallisiert wird ist mir simmulieren aufgefallen, 
aber ich habe keinen Grund dafür gefunden.
Du hast recht mit dem auslesen des adch,adcl ..ich hatte den Start erst 
vor dem Auslesen stehen, dachte dann aber das die Zeit zum Auslesen 
nicht reicht um hab es verändert. Ich hatte folgende Abfrage drin:

fertig:
    sbis ADCSR,ADIF    ;warten bis wandlung
    jmp fertig    ;beendet

aber dort blieb der Simulator immer hängen.
Prescaler ist 8, habe ich im Kommentar vergessen zu ändern, sorry.
Der ocr0 Vergleichswert soll nach der Wandlung und dem Vergleich mit 
v_store
ins ocr0 geschrieben werden.
Wenn ich das lese dann hab ich den Timer0 bis jetzt wohl immer falsch 
verwendet, ich habe immer mit dem Verändern von ocr0 die Pulsweite 
varriert??

Gruß Werner

von Karl H. (kbuchegg)


Lesenswert?

werner schrieb:

> Karl Heinz Buchegger
> Wenn du schreibst das das Unsinn ist was ich da geschrieben habe, dann
> glaub ich Dir das erstmal, aber ich habe trotzdem keinen Schimmer was da
> Unsinn ist! Avr-Studio hat es nicht angemeckert.

Das bestärkt mich eigentlich noch viel mehr in meiner Meinung, dass du 
Assembler nicht verstanden hast.

Du argumentierst wie jemand, der sagt, dass er "Deutsch als 
Fremdsprache" verstehen würde und dann

 "Das U-Boot schält die Gabel im Keller des Lastwagens."

von sich gibt.
Formal, also grammatikmässig ist das ein völlig legitimer deutscher 
Satz, an dem es nichts auszusetzen gibt. Jede Syntaxprüfung würde dir 
ein "OK" bescheinigen.
Und trotzdem ist der Satz so unsinnig, wie er nur sein kann.

von werner (Gast)


Lesenswert?

Hallo Ben

die 36V Variante ist hier aus dem Forum, ich hab das für 18V 
ausgerechnet und es hat trotzdem nicht funktioniert. Du schreibst für 
unter 20V gibts besser Möglichkeiten, ja bitte ich bin für jeden 
Vorschlag dankbar.
Wie im Schaltplan beschrieben liegt Vpmax bei 17,35V.
Klar hab ich mir anderen ihren Code angesehen und hab dann alles so 
geschrieben wie ich mir das vorstelle.
Die Auswahl mit dem Vergleich hab ich mir komplett selber ausgedacht.
Den Rest hab ich Anhand der Dokumentation zusammengesucht.

Gruß Werner

von Karl H. (kbuchegg)


Lesenswert?

Mch dir doch erst mal einen gedankliche Stütze in Form eines 
Ablaufdiagrams, wie deine Aufgabenstellung eigentlich gelöst werden 
soll.
1
+->  messe ADC Spannung
2
|              |                                Ja
3
|    Spannung kleiner als die Vorgabe ? ---------------+
4
|              | Nein                                  |            Ja
5
|              |                     PWM Wert kann erhöht werden?  -----+
6
|              |                                       | Nein           |
7
|              |                                       |         erhöhe PWM Wert um 1
8
|              |                                       |                |
9
|              |                                       |                +------------------------+
10
|              |                                       +---------------------------------------+ |
11
|              |                         Ja                                                    | |
12
|    Spannung größer als die Vorgabe ? --------+                                               | |
13
|              |                               |                        Ja                     | |
14
|              |                     PWM Wert kann verringert werden? -----+                   | |
15
|              |                               | Nein                      |                   | |
16
|              |                               |                   verringere PWM Wert um 1    | |
17
|              |                               |                           |                   | |
18
|              |<------------------------------+                           |                   | |
19
|              |<----------------------------------------------------------+                   | |
20
|              |<------------------------------------------------------------------------------+ |
21
|              |<--------------------------------------------------------------------------------+
22
|              |
23
+--------------+

und mit dem 'Plan' kannst du jetzt mal anfangen, dein Programm zu 
schreiben.

Aber ohne so einen Plan, einfach drauflos programmieren, wird das 
nichts. In diesem Ablaufplan kannst du erst mal losgelöst von jeglicher 
Programmiersprache überprüfen, ob deine Logik sinnvoll ist.

von Ben _. (burning_silicon)


Angehängte Dateien:

Lesenswert?

Sorry für den Plan als JPG, gabs bei Gugel nicht anders.

Für Dein aktuelles Projekt würde ich die Interrupts erstmal komplett 
vergessen, dafür braucht man sie nicht unbedingt. Also alles raus was 
damit zu tun hat. Damit kannst Du mal in einem Testsystem rumspielen um 
zu lernen wie das funktioniert.

Mach Dich unbedingt schlau wie der Stack funktioniert. Sonst kriegst Du 
kein einziges Unterprogramm in Assembler zustande. Stichworte: 
CALL/RCALL/RET, PUSH/POP.

EDIT: @Karl Heinz Buchegger
Dein Diagramm ist toll, aber dafür brauche ich kein Programm. Das kann 
jeder Step-Down-Regler-IC in Hardware... ;)

von Karl H. (kbuchegg)


Lesenswert?

Ben _ schrieb:

> EDIT: @Karl Heinz Buchegger
> Dein Diagramm ist toll, aber dafür brauche ich kein Programm. Das kann
> jeder Step-Down-Regler-IC in Hardware... ;)

Wenn er es doch programmieren will :-)

von werner (Gast)


Lesenswert?

Hallo Karl Heinz

ja so ähnlich hab ich das auch gemacht und was dabei rausgekommen ist 
hast du ja gesehen, hab allerdings den PWM-Wert nicht mit einbezogen 
sondern mich dann nur auf die enstehende Vergleichsspannung 
konzentriert.
Ist denn mein Gedankengang dahingehend richtig den ocr0 Wert zu 
verändern?
Es wird auch bemängelt das ich oben .org 0x30 geschrieben habe, müssen 
denn nicht die Interuptvektoren übersprungen werden. Ich habe 
festgestellt das manche die Vektoren mit reinschreiben und manche nicht. 
Wäre das so besser:

.org adccaddr
    jmp adini  ;Einsprung AD - Wandlung mit Interrupt

.org ovf0addr  jmp timer0ini  ;Einsprung Timer 0 Interrupt

das hab ich probiert und eine Fehlermeldung erhalten.

Gruß Werner

von werner (Gast)


Lesenswert?

Hallo Ben,

danke für den Plan, probier ich morgen gleich aus.
Was ist den an der Stackpointerinitialisierung falsch, es wird das obere 
und
unter Ende des SRAM definiert.

                        LDI temp1, high (RAMEND)
      OUT SPH, temp1  ;SPH am Ende phys. SRAM
      LDI temp1, low (RAMEND)
      OUT SPL, temp1  ;SPL am Ende phys. SRAM

??????????????????

von Ben _. (burning_silicon)


Lesenswert?

oO ... Bitte befasse Dich auch mit 16 Bit Zahlen auf einem 8 Bit µC. 
Dann verstehst Du was es mit SPH,SPL und high(), low() auf sich hat.

Das untere Ende vom RAM interessiert den Stack bzw. den µC nicht. Wenn 
Du zuviel auf den Stack legst z.B. mit

labelx: push r16
        rjmp labelx

dann schreibt er Dir gnadenlos bis in die IO-Register alles mit dem 
Inhalt von R16 voll und crasht deswegen irgendwann.

Die Interrupts machen wir später... Nur soviel: Jeder Interrupt braucht 
in der Interrupttabelle eine Ansprungaddresse. Diese ist fest vorgeben 
und enthält den Befehl was der µC machen soll wenn ein Interrupt 
aufgetreten ist - meistens ein JMP isrX. Steht dort nichts weil keine 
Interrupttabelle definiert wurde dann interpretiert der µC dieses Nichts 
als Befehl und macht irgendwas was Du nicht willst... Endet so gut wie 
sicher mit einem Crash des Programms. Sprich ohne Interrupttabelle (und 
Stack) kannst Du keine Interrupts verwenden.

Was Deinen Fehler dazu angeht: Mir kommts langsam so vor als ob Du nicht 
mal das Datenblatt gelesen hast... Mal eben kopiert aus dem Datenblatt 
des ATMega32:

---[schnipp]----------
$000 jmp RESET ; Reset Handler
$002 jmp EXT_INT0 ; IRQ0 Handler
$004 jmp EXT_INT1 ; IRQ1 Handler
$006 jmp EXT_INT2 ; IRQ2 Handler
$008 jmp TIM2_COMP ; Timer2 Compare Handler
$00A jmp TIM2_OVF ; Timer2 Overflow Handler
$00C jmp TIM1_CAPT ; Timer1 Capture Handler
$00E jmp TIM1_COMPA ; Timer1 CompareA Handler
$010 jmp TIM1_COMPB ; Timer1 CompareB Handler
$012 jmp TIM1_OVF ; Timer1 Overflow Handler
$014 jmp TIM0_COMP ; Timer0 Compare Handler
$016 jmp TIM0_OVF ; Timer0 Overflow Handler
$018 jmp SPI_STC ; SPI Transfer Complete Handler
$01A jmp USART_RXC ; USART RX Complete Handler
$01C jmp USART_UDRE ; UDR Empty Handler
$01E jmp USART_TXC ; USART TX Complete Handler
$020 jmp ADC ; ADC Conversion Complete Handler
$022 jmp EE_RDY ; EEPROM Ready Handler
$024 jmp ANA_COMP ; Analog Comparator Handler
$026 jmp TWI ; Two-wire Serial Interface Handler
$028 jmp SPM_RDY ; Store Program Memory Ready Handler
---[schnapp]----------

Da hast Du alle Interruptadressen... Wird ein Interrupt nicht benötigt 
sollte man anstelle des JMP einen RETI schreiben. Das verhindert 
meistens einen Crash wenn ein undefinierter Interrupt wider Erwarten 
doch mal auftritt.

Weiter gehts dann mit:
RESET: ldi r16,high(RAMEND) ; Main program start
       out SPH,r16 ; Set Stack Pointer to top of RAM
       ldi r16,low(RAMEND)
       out SPL,r16
       se_ NEIN das darfst Du erst machen wenn Du den ganzen Rest 
verstanden hast und Dir einigermaßen sicher bist, daß Du damit umgehen 
kannst.

Wenn der Timer korrekt läuft und Dein Programm nicht laufend durch 
Stack-Overflows oder irgendwelche undefinierten Interrupts in den Reset 
getrieben wird dann kannst Du mit OCR0 problemlos das PWM-Verhältnis 
einstellen. Aber vorher machste Dich erstmal schlau mit welcher Frequenz 
so ein Step-Down laufen muß oder am Besten... wie so ein Ding 
funktioniert!

von Krapao (Gast)


Lesenswert?

> Es wird auch bemängelt das ich oben .org 0x30 geschrieben habe, müssen
> denn nicht die Interuptvektoren übersprungen werden. Ich habe
> festgestellt das manche die Vektoren mit reinschreiben und manche nicht.

Das hängt vom Programmierstil, der Programmlogik und von den vorhandenen 
ROM-Resourcen ab.

Zunächst muss man wissen, dass der AVR bei Adresse 0 startet. Dort 
gehört also der Sprung in die eigene erste Programmanweisung hin. Dein 
.org 0x30 funktioniert nur, weil der Programmzähler von Adresse 0 über 
ein paar NOPs auf deinen JMP an Adresse 0x30 schlittert...

Hinter dem ersten Sprung ab Adresse 0 sind Adressen, die beim Auftreten 
von Interrupts von der Interrupt-Hardware des AVR angesprungen werden. 
Das ist die sog. Interruptvektortabelle.

Hat man viel Platz, lässt man allen Interruptvektoren ihren Platz in der 
Tabelle und richtet eventuell auch noch die unbelegten Vektoren auf 
einen gemeinsamen ISR Programmcode.

Ist dieser Platz aber zu kostbar, weil man knapp an ROM ist und will man 
dort statt Vektoren das letzte Quentchen eigenen Code haben, kann man 
seinen Hauptcode mit .org an bestimmte Positionen legen z.B. hinter den 
letzten benutzten Interruptvektor.

von Ben _. (burning_silicon)


Lesenswert?

Der AVR hat kein ROM, sondern Flash... eine Variante des EEPROM. ROM 
find ich da ein wenig mißverständlich.

von Krapao (Gast)


Lesenswert?

> Dein .org 0x30 funktioniert nur, weil der Programmzähler von Adresse 0
> über ein paar NOPs auf deinen JMP an Adresse 0x30 schlittert...

Sorry, du hast das richtig, du hast den JMP an Adresse 0 und du 
überspringst die Interruptvektoren mit dem .org. das ist richtig so.

> .org adccaddr
>    jmp adini  ;Einsprung AD - Wandlung mit Interrupt
>
> .org ovf0addr  jmp timer0ini  ;Einsprung Timer 0 Interrupt
>
> das hab ich probiert und eine Fehlermeldung erhalten.

Im Prinzip richtig
1
  JMP start
2
3
.org OVF0addr
4
  jmp timer0ini
5
6
.org ADCCaddr
7
  jmp adini
8
9
.org 0x30 ; optional
10
start:

Bezog sich die Fehlermeldung auf Schreibweise (hier Großschreibung gemäß 
m16def.inc) und Anordnung (hier aufsteigend gemäß m16def.inc)

von Ben _. (burning_silicon)


Lesenswert?

> .org ovf0addr  jmp timer0ini  ;Einsprung Timer 0 Interrupt
>
> das hab ich probiert und eine Fehlermeldung erhalten.
Das funktioniert nur nicht weil das .org und der JMP nicht auf der 
gleichen Zeile stehen dürfen.

von werner (Gast)


Angehängte Dateien:

Lesenswert?

Hallo Ben,
vielen Dank für deine Zeilen, ich hab jetzt gesehen was Krapao gemeint 
hat mit den Stack 2 mal initialisieren, war ja totaler Mist von mir.
Ich hab den Code mal geändert (Stack,Interruptvekt.,AD-Wandlung) und 
dran gehangen... ist das so richtig?
Hab gerade Krapo´s Text gesehen. Die Fehlermeldung weis ich nicht mehr, 
und ja ich habs in eine Zeile geschrieben.
Ich hab die Interruptvekt.Tabelle eingefügt und hab wieder 
Fehlermeldungen,
(mit+++++++gekennzeichnet) wenn das mit der Tabelle irgendwann 
funktioniert muss ich dann weiter unten den Timer0 und den AD-Wandler 
nochmal mit call aufrufen.

Gruß Werner

von Krapao (Gast)


Lesenswert?

Jetzt ist der erste Sprung zweimal im Programm. Das verschiebt alle 
Vektoren :-(
1
jmp reset           ;führe reset aus
2
3
4
jmp RESET ; Reset Handler

Mach es wie Ben in Beitrag "Re: Hilfe Ladereglung Atmega16 Assembler" 
schreibt:
1
jmp reset      ; führe reset aus
2
reti ;EXT_INT0 ; IRQ0 Handler
3
... hier die anderen JMPs zu den Handlern
4
reti ;SPM_RDY  ; Store Program Memory Ready Handler
5
... hier die defs 
6
    (obwohl ich die nach der .include Zeile bevorzuge/erwarte)
7
8
.org 0x30             ;programm ab flashadresse 30h speichern
9
;=============== stackpointer ini ===========================
10
reset:
11
  
12
      LDI temp1, high (RAMEND)

Und denk dran: Du brauchst noch den Programmcode für die Funktionen 
TIM0_OVF und ADC!

von Krapao (Gast)


Lesenswert?

Die Fehlermeldung bei ++++++++++++ kann davon kommen, dass TIM0_OVF und 
ADC unkompatibel definierte Namen sind oder weil du diese Funktionen 
nirgends im Code hast.

1
...
2
  jmp timer0_ovf_isr
3
...
4
  jmp adc_isr
5
...

Und später im Code

1
; ISR Funktionen (Minimalversion)
2
timer0_ovf_isr:
3
  reti
4
5
adc_isr:
6
  reti

von Krapao (Gast)


Lesenswert?

Wobei TIM0_OVF der falsche Interruptvektor für diesen Kommentar 
(Remember: Bitwerte in 0b00000010 kontrolliere ich nicht) ist:
1
  ori r18, 0b00000010
2
  out timsk, r18      ;ocie0-löst beim erreichen das ladewertes irq aus

Der Interruptvektor für "ocie0" heisst anders und liegt an einer anderen 
Adresse in der Interruptvektortabelle!

von werner (Gast)


Angehängte Dateien:

Lesenswert?

Hallo  Krapao,
erstmal vielen Dank für Eure Hilfe, ich hab den Code nochmal geändert,
klar wenn man drüber nachdenkt oder draufgestoßen wird ist es logisch 
das da die Bezeichnungen hin müssen die im Programm stehen.

Du hattest oben geschrieben "Der Code hat Durchfall (nach der Stelle 
brlo down)." , was meinst du damit, ist die Verzweigung falsch gemacht?

Gruß Werner

von werner (Gast)


Lesenswert?

Hallo Krapao

der Timer0 soll eigentlich keinen Interrupt auslösen.
Er soll nur eine konstante Frequenz ausgeben und ich will mit dem 
Vorladewert
in ocr0 spielen, oder hab ich da einen (ha "einen") Denkfehler?

Gruß Werner

von Krapao (Gast)


Lesenswert?

Diese Stelle...
1
      cp v_store, v_akku  ;vergleiche gespeicherte mit gemessener
2
      brsh up      ;akkuspannung, wähle aus ob höher oder niedriger
3
      brlo down    ;und springe zu up oder down
4
      ; ###
5
down:      
6
      dec v_akkupwm      ;akkuspannung zu hoch, zaehle runter  ;

ist gemeint. Allgemein erwartet man bei einem Muster
1
  Rechenanweisung zum Prüfen 
2
  Wenn x zutrifft mache Aktion zu x
3
  Wenn y zutrifft mache Aktion zu y
4
  Wenn weder x,y zutrifft mach ganz was anderes
5
6
Aktion X
7
...
8
9
Aktion Y
10
...

In deinem Code fehlt "Wenn weder x,y zutrifft mach ganz was anderes" 
sondern an dieser Stelle stehen Anweisungen aus Aktion X (down: dec 
v_akkupwm...). Der Programmzähler fällt durch...

In diesem Fall ist das nicht kritisch, weil BRSH und BRLO komplementär 
zu einander sind, d.h. wenn die eine nicht zutrifft, dann die andere. 
Und beide ändern das Ergebnis (Carry) der CP Prüfung nicht.

Bei komplexerem Code programmiert man so schnell Fehler. Anschaulicher 
ist der Code IMHO, wenn er so geschrieben ist:

;auswahl
      cp v_store, v_akku
      brsh up   ; Sprung wenn v_store >= v_akku
      brlo down ; Sprung wenn v_store <  v_akku
      jmp main

Down:
      ...

von Krapao (Gast)


Lesenswert?

Wenn du gemäß Kommatar "ocie0-löst beim erreichen das ladewertes irq 
aus" einen IRQ auslöst, musst du eine ISR haben.

Wenn dein Code anders als der Kommentar keinen IRQ auslöst, brauchst du 
keine ISR zu haben. Man kann eine Hardware-PWM ohne ISR programmieren, 
wenn man keinen IRQ auslöst (enabled). Das ist eine Einstellungssache 
der Bits u.a. in TIMSK.

von Krapao (Gast)


Lesenswert?

Mach mal eine Pause :)

Du hast in Beitrag "Re: Hilfe Ladereglung Atmega16 Assembler" immer 
noch den doppelten jmp reset drin. Schlaf eine Runde drüber und morgen 
frisch ans Werk.

von werner (Gast)


Angehängte Dateien:

Lesenswert?

Hallo Krapao
Einen hab ich noch,
ich hab gestern beiligenden Code wirklich selber geschrieben, natürlich 
mit den heute gelernten Anpassungen versehen.
Ein 20k Poti an PA0 regelt dort die Pulsweite mittels ocr0.
Das Wandlungsergebnis muss verstärkt werden (mul) sonst passiert nicht 
viel.
Mit dem Versuch wollte ich heute weitermachen und die Spannung selber 
Regeln lassen...das war eigentlich das Ziel.
Danke nochmal, vielleicht schaut ja nochmal einer rein.

Gruß Werner

von spess53 (Gast)


Lesenswert?

Hi

>Danke nochmal, vielleicht schaut ja nochmal einer rein.

>jmp RESET ; Reset Handler
>reti ;EXT_INT0 ; IRQ0 Handler
>reti ;EXT_INT1 ; IRQ1 Handler <<<<<<<<<<<<<<< Müll

In der Interruptvektortabelle belegt jeder Eintrag 2 Word (jmp). Ein 
'reti' belegt aber nur 1 Word. Damit sind die nachfolgenden Einträge 
falsch.

MfG spess

von werner (Gast)


Lesenswert?

Hallo spess53,

wenn ich aber reti in jmp ändere bekomme ich lauter fehlermeldungen von 
den eben geänderten !?

Gruß Werner

von spess53 (Gast)


Lesenswert?

Hi

>wenn ich aber reti in jmp ändere bekomme ich lauter fehlermeldungen von
>den eben geänderten !?

Welche?

MfG Spess

von werner (Gast)


Angehängte Dateien:

Lesenswert?

Alle mit ++++

Gruß Werner

von spess53 (Gast)


Angehängte Dateien:

Lesenswert?

Hi

>Alle mit ++++

Kann es sein, das dir einfach die Grundlagen fehlen?

Selbstverständlich meckert er dich an, wenn du ein 'jmp' ohne Sprungziel 
verwendest. Ein Programmgerüst für einen ATMega32 würde bei mir so wie 
im Anhang aussehen.

Mfg Spess

von werner (Gast)


Lesenswert?

Hallo Spess53

danke erstmal, also muss die irq, die ich gar nicht brauche auch nicht
hinschreiben, oder?

Gruß Werner

von Krapao (Gast)


Lesenswert?

> danke erstmal, also muss die irq, die ich gar nicht brauche auch nicht
> hinschreiben, oder?

Du hast zwei Optionen 
(Beitrag "Re: Hilfe Ladereglung Atmega16 Assembler"):

1/ Alle hinschreiben. Dabei dürfen nicht benutzte auch auf die gleiche 
ISR verweisen z.B. eine die man unbenutzten_irg_handler nennt.

2/ Nicht alle hinschreiben, nur die benötigten. Diese und der Beginn des 
Programmcodes (i.A. die Stackinitialisierung und folgenden Code) müssen 
aber an den richtigen Adressen liegen. Das macht man mit passenden .org 
<adresse> Assembleranweisungen. Um sich nicht zu vertun, ist <adresse> 
meist keine direkte Zahl(Adresse), sondern ein vordefinierter Makroname 
aus dem Includedatei des betreffenden µCs z.B. ADCCaddr

Egal ob Option 1 oder 2: Alle ISRs deren Sprungadressen in der Tabelle 
eingetragen sind, müssen auch als Programmcode im Programm vorhanden 
sein. Andernfalls Absturz beim Auftreten des IRQs. Alle ISRs enden mit 
einem RETI.

von spess53 (Gast)


Lesenswert?

Hi

>danke erstmal, also muss die irq, die ich gar nicht brauche auch nicht
>hinschreiben, oder?

Wohin?

Du ersetzt für einen benutzten Interrupt die entsprechende Zeile

xyz: reti

durch deinen Code für den Interrupthandler. Fertig.

MfG Spess

von werner (Gast)


Angehängte Dateien:

Lesenswert?

Hallo Spess

Ich hab die Interrupttabelle des Atmega16 jetzt eingefügt und Fehler
bei der AD Wandlung beseitig. Jetzt funktioniert auch das Warten
auf das Ende Wandlung . Dann hab ich noch den Interrupt beim timer0 
rausgenommen.
Aber das Programm tut immer noch nicht was es soll, nähmlich die 
Ausgangsspannung durch Vergleich mit einem Sollwert und der aktuellen 
Ausgangsspannung durch Verändern des Duty Cycle zu regeln.
Hat noch jemand ne Idee????

Gruß Werner

von Karl H. (kbuchegg)


Lesenswert?

1
    ldi temp1, 0b01000000  ;;AREF, linkssbündig,pa0
2
    out ADMUX, temp1
3
    ldi  temp1,0b10010101
4
    out  ADCSR,temp1         ;adcen,adif,teiler 32

du kannst dir gar nicht vorstellen, wie verhasst diese Schreibweise im 
Forum ist. Um dir zu helfen, bleibt nichts anderes übrig, als das 
Datenblatt rauszukramen und mit viel Scrollen zu identifizieren, welche 
Bits da auf 1 gesetzt sind um erst mal rauszukriegen, wie sie heißen und 
dann (wieder mit viel gescrolle) nachzulesen, was dieses Bit bewirkt. 
Ok, den letzten Teil weiß man oft auswendig, dazu sind ja die Namen da. 
Aber an welcher Bitposition im Register welches Bit sitzt, wissen die 
wenigsten auswendig. Und da interessiert auch dein Kommentar nicht. Denn 
der kann falsch sein.

Warum nicht
1
    ldi temp1, (1<<REFS0)  ;; AVcc als Referenz
2
    out ADMUX, temp1
3
    ldi  temp1, (1<<ADEN) | (1<<ADIF) | (1<<ADPS2) | (1<<ADPS0)
4
    out  ADCSR,temp1

ist dir das zu lesbar?

1
      sbi  ADCSR,ADSC      ;Wandlung starten
2
fertig:      
3
      sbic ADCSR,ADIF      ;warten bis wandlung
4
        rjmp weiter
5
      rjmp fertig        ;beendet    
6
weiter:
das hast du aber so nicht aus dem Tutorial. Man kann natürlich ADIF zur 
ABfrage benutzen, ob der ADC fertig ist. Nur: Wenn kein ADC Interrupt 
gemacht wird, dann ist man selber dafür zuständig dieses Flag wieder zu 
löschen. Daher wird im Tutorial ganz einfach ADSC für diesen Zweck 
abgefragt: Dein Programm setzt es auf 1 und wenn der ADC fertig ist 
setzt er es wieder auf 0. Ziemlich banal.


Tritt zwar bei dir nicht auf - aber
1
jmp  RESET ;External Pin, Power-on Reset, Brown-out
2
reti  ;INT0 ;External Interrupt Request 0
3
reti  ;INT1 ;External Interrupt Request 1
4
reti  ;TIMER2 COMP ;Timer/Counter2 Compare Match
5
reti  ;TIMER2 OVF ;Timer/Counter2 Overflow
6
reti  ;TIMER1 CAPT ;Timer/Counter1 Capture Event
7
reti  ;TIMER1 COMPA ;Timer/Counter1 Compare Match A
8
reti  ;TIMER1 COMPB ;Timer/Counter1 Compare Match B
9
reti  ;TIMER1 OVF ;Timer/Counter1 Overflow
10
jmp  timer0ini  ;Timer/Counter0 Overflow
11
reti  ;SPI, STC ;Serial Transfer Complete
12
reti  ;USART, RXC ;USART, Rx Complete
13
reti  ;USART, UDRE ;USART Data Register Empty
14
reti  ;USART, TXC ;USART, Tx Complete
15
jmp  adini ;ADC Conversion Complete
16
reti  ;EE_RDY ;EEPROM Ready
17
reti  ;ANA_COMP ;Analog Comparator
18
reti  ;TWI ;Two-wire Serial Interface
19
reti  ;INT2 ;External Interrupt Request 2
20
reti  ;TIMER0 COMP ;Timer/Counter0 Compare Match
21
reti  ;SPM_RDY ;Store Program Memory Ready

Bist du dir ganz sicher, dass die adequate Reaktion auf das Auftreten 
eines Timer bzw. ADC Interrupts darin besteht, das 'Gerät' neu zu 
initialisieren? Ausserdem: Ich habs zwar auch nicht mehr 100% im Kopf, 
aber ich bin mir ziemlich sicher, dass das falsch ist, weil ein reti 
nicht die richtige Befehlslänge hat, damit sich bei den vielen reti in 
Folge es sich so ausgeht, dass die jmp an der richtigen Stelle sitzen.

von Karl H. (kbuchegg)


Lesenswert?

1
up:      
2
      inc v_akkupwm
3
      cp v_store, v_akkupwm
4
      brne up
5
      breq sichern
6
      jmp main

Hä?
Wenn du nach Up kommst, steht schon fest, wie das Verhältnis von v_store 
zu v_akkupwm ist. Du musst da nichts mehr abfragen. Und vor allen Dingen 
musst du nicht in einer Schleife solange v_akkupwm erhöhen, bis die sich 
angenähert haben. Da kannst du auch gleich ganz einfach v_akkupwm auf 
den Wert von v_store setzen! Das hat genau den gleiche Effekt.

Wenn du EINMAL nach up kommst, dann wird der Wert v_akkupwm um 1 erhöht! 
Nicht mehr. Das wars schon. Mehr ist nicht zu tun. Der um 1 erhöhte Wert 
wird dann an die PWM gegeben, woraufhin sich die Spannung erhöht. 
Theoretisch. Danach beginnt wieder alles von vorne, mit dem ADC wird die 
jetzt anstehende Spannung gemessen und wenn die dann immer noch kleiner 
als die Vorgabe ist, dann wird eben wieder um 1 erhöht - im nächsten 
Durchlauf, bei der nächsten Auswertung der gemessenen Spannung.

Jetzt hab ich dir so ein schönes Bildchen gemalt. Aber scheinbar hast du 
es nicht studiert - oder nicht verstanden.

von Karl H. (kbuchegg)


Lesenswert?

1
      in r17, ADCL      ;ad lowbyte in r17 merken
2
      in r18, ADCH      ;ad highbyte in r18 merken
3
      MOV v_akku, r18

das nenn ich mutig: ohne gesetztes ADLAR Bit nur mit dem Wert von ADCH 
eine Regelung aufzubauen.

von Karl H. (kbuchegg)


Lesenswert?

Ausserdem hast du etwas ganz wesentliches übersehen.
Gesetz den Fall, das alles würde so erst  mal korrigiert, funktioniert 
deine Regelung immer noch nicht. Warum? Weil die PWM erst mal mindestens 
1 Periode benötigt, ehe sich der neue OCR Wert überhaupt auf die externe 
Spannung auswirkt. Je nach Aussenbeschaltung dauert das sogar noch 
länger.

von Dietrich L. (dietrichl)


Lesenswert?

werner schrieb:
> Hat noch jemand ne Idee????

Du solltest zuerst mal die grundsätzlichen Fehler, die bereits mehrfach 
genannt wurden, korrigieren:
- Karl Heinz Buchegger:
"adini:" muss mit "ret" enden (nicht mit "jmp main"), da Du es mit "call 
adini" als Unterprogramm aufrufst.
- spess:
In der Interruptvektortabelle belegt jeder Eintrag 2 Word (jmp). Ein
'reti' belegt aber nur 1 Word. Damit sind die nachfolgenden Einträge
falsch.
Lösung: schreibe statt "reti" besser "jmp noint"; dann irgendwo
"noint:
    reti"
(bei allen nicht benutzten Interrupts Sprung zur Marke "noint" = kein 
Interrupt, wo dann der "reti" gemacht wird).

Gruß Dietrich

von werner (Gast)


Lesenswert?

Hallo Dietrich,

danke für Deine Antwort, ich hab es jetzt so eingepflegt wie du 
geschrieben hast ,und Spess53 auch.
Ich kenn das nicht das man da eine Interruptvektortabelle anlegen muß, 
sonder nur das die Interruptvektoren mit einem Sprung ala "0x30" zu 
überspringen sind damit in dem Bereich keine Programme laufen.
Frage: Also ist die Interruptvektortabelle immer notwendig oder nur wenn 
man auch Interrupts verwendet?
Das mit dem jmp main hab ich vergessen gestern noch rauszunehmen . Misst 
wenn mans nicht gleich macht vergissts man es wieder.

Gruß Werner

von Karl H. (kbuchegg)


Lesenswert?

werner schrieb:

> Frage: Also ist die Interruptvektortabelle immer notwendig oder nur wenn
> man auch Interrupts verwendet?

Wenn du keine Interrupts benutzt, werden die Vektoren auch nie 
angesprungen.

Was folgt daraus?

von werner (Gast)


Lesenswert?

Hallo Karl Heinz,

ich weis garn nicht wo ich anfangen soll.
Zu meiner Verteidigung ADCH auslesen geht nur mit ADLAR , das weis ich 
und hab es als Kommentar "linksbündig" in der adini beachtet.
Die von Dir bevorzugte Schreibweise hat schon was für sich, aber ich 
habs mir so (0b0000...) angewöhnt weil ich die Begriffe noch nicht so 
gut kenne und ,wie peinlich eigentlich, weil ich den ||||| Strich auf 
der Tastatur damals nicht gefunden habe. Ich werd mal versuchen die 
Schreibweise zu übernehmen.
Wegen der "fertig" Abfrage: Die hat ewig nicht funktioniert , bis ich 
dann gestern Abend herausgekommen habe,das das ADIE aus sein muss damit 
es funktioniert. Du schreibst das es banal ist , ich hab gefeiert als es 
ging...!
Wie geht es denn anders , vielleicht auch richtiger/besser?
Das kann natürlich stimmen das die Hardware zu langsam ist. Dann wäre es 
besser einen Timer z.B 1 laufen zu lassen der zu jeder Sekunde die 
Wandlung anstößt, oder?
Ich hab mir dein schönes Diagramm, das ist wirklich ernst gemeint... wie 
das manche so toll hinbekommen im Editor so was zu kreieren, angesehen 
und danach im Simulator mit gesetzten ADCH Werten die "auswahl" 
durchlaufen lassen und das hat so wie ich mir das vorgestellt habe 
funktioniert.
Außer den ocr0 Wert, den hab ich nie im Simulator eingetragen gesehen.
Kann das daran liegen wie du schon schreibst das erstmal ein Durchlauf 
gemacht wird.

Hat eigentlich schon mal einer von Euch so was wie ich hier Versuche 
gemacht? Nicht das das vielleicht gar nicht gehen kann..warum auch 
immer!


Vielen Dank

Gruß Werner

von werner (Gast)


Lesenswert?

Hallo Dietrich,

das ich Sie nicht hinschreiben muss????

Gruß Werner

von Karl H. (kbuchegg)


Lesenswert?

werner schrieb:

> Zu meiner Verteidigung ADCH auslesen geht nur mit ADLAR , das weis ich
> und hab es als Kommentar "linksbündig" in der adini beachtet.

Jetzt hast du ja gesehen, welche Falle da lauert.
Kommentare sind Schall und Rauch. Die zählen nichts.

Schreib ich es so
1
    ldi temp1, (1<<REFS0) | (1<<ADLAR)  ;; AVcc als Referenz
2
    out ADMUX, temp1

brauch ich im Prinzip nicht mehr kommentieren, dass ADLAR gesetzt ist. 
Steht alles im 'relevanten' Teil: Im Code selber.

> Wegen der "fertig" Abfrage: Die hat ewig nicht funktioniert , bis ich
> dann gestern Abend herausgekommen habe,das das ADIE aus sein muss damit
> es funktioniert. Du schreibst das es banal ist , ich hab gefeiert als es
> ging...!
> Wie geht es denn anders , vielleicht auch richtiger/besser?

So wie im Tut
1
sample_adc:
2
    sbi     ADCSRA, ADSC        ; den ADC starten
3
 
4
wait_adc:
5
    sbic    ADCSRA, ADSC        ; wenn der ADC fertig ist, wird dieses Bit gelöscht
6
    rjmp    wait_adc
7
 
8
; ADC einlesen:
9
 
10
    in      adlow, ADCL         ; immer zuerst LOW Byte lesen
11
    in      adhigh, ADCH        ; danach das mittlerweile gesperrte High Byte

dazu ist das Tut ja geschrieben worden, damit man sich dort Dinge 
abschauen kann. Kein Interrupt Flag notwendig. Und da man dieses Bit 
nicht beachten muss, muss man es auch nicht zurücksetzen.

> Das kann natürlich stimmen das die Hardware zu langsam ist. Dann wäre es
> besser einen Timer z.B 1 laufen zu lassen der zu jeder Sekunde die
> Wandlung anstößt, oder?

Wäre eine Möglichkeit, wobei natürlich 1 Sekunde schon wieder ein Extrem 
in die andere Richtung ist. Du musst bedenken, dass die PWM ja nur 
Schrittweise auf die Sollspannung nachgeführt wird, wenn die also 20 OCR 
Einheiten daneben liegt, dann würde das dann auch 20 Sekunden dauern, 
bis sie so eingestellt ist, dass sich wieder die gewünschte 
Ausgangsspannung ergibt.
Also: Ein bischen schneller darf es schon sein.


> Hat eigentlich schon mal einer von Euch so was wie ich hier Versuche
> gemacht?

Natürlich.

von werner (Gast)


Lesenswert?

Hallo Karl Heinz

Mensch klar das ADSC Bit, so einfach wäre es wenn man sich das tut 
genauer angeschaut hätte.

> Hat eigentlich schon mal einer von Euch so was wie ich hier Versuche
> gemacht?

Natürlich.

Und....was ist dabei rausgekommen. Mach mir mal ein bisschen Hoffnung,
ich sitze seit Oktober 2011 und versuche die Aufgabe zu lösen.
Ist noch ein bisschen mehr, muss noch an RS232 ausgegeben werden, den 
Gesamtertrag/Verbrauch der Anlage ermitteln und wenn noch Zeit ist, 
woran ich nicht mehr glaube, auf einem Display anzeigen.
Also probier ich mal mit ner halben Sekunde.

Gruß Werner

von Karl H. (kbuchegg)


Lesenswert?

werner schrieb:

> Natürlich.
>
> Und....was ist dabei rausgekommen.

Was soll dabei rauskommen.

Per Poti stell ich die gewünschte Ausgangsspannung ein, die dann auch 
per PWM (und Siebglied) erreicht wird und am Voltmeter kann ich die 
Spannung ablesen. Häng ich einen Lastwiderstand an, dann seh ich am LCD 
wie der OCR Wert von der PWM sofort höher geht um die eingestellte 
Ausgangsspannung zu halten. d.h. ein bischen bricht sie ein, fängt sich 
aber sofort wieder. AM Voltmeter seh ich nur ganz kurz, dass die 
Spannung offenbar ein bischen abweicht. Danach ist wieder alles paletti 
und das Voltmeter zeigt mir wieder die Spannung die ich auch am LCD 
durch die Potivorgabe angezeigt habe.

Der interessantere Teil war dann, den simplen Regler durch einen PID 
Regler zu ersetzen :-)

> ich sitze seit Oktober 2011

Echt?
Lange Zeit für ein 1 Abend-Spielprojekt.

von Dietrich L. (dietrichl)


Lesenswert?

werner schrieb:
> das ich Sie nicht hinschreiben muss????

Das verstehe ich nicht....

Aber wenn Du die Interrupt-Tabelle meinst: müssen musst Du nur die 
Interrupts eintragen, die Du auch verwendest (=eingeschaltet hast).
Aber da Du den Platz im Code ja überspringst, ist der Bereich ja frei. 
Du hast also nur Schreibarbeit, wenn Du dort die Liste hast. Dann stehen 
die Jumps auch schon an der richtigen Adresse (Interrupt-Vektor). Du 
brauchst also (nur) bei den nicht benutzten Interrupts eine Sprung zu 
einer Default-Interruptroutine (mein Vorschlag war "noint: reti") 
eintragen.
- Nachteil: Liste schreiben
- Vorteil: wird im Programm "aus Versehen" mal ein Interrupt 
eingeschaltet oder ausgelöst, für den es keine Routine gibt, dann 
schmiert das Programm nicht ab.

Der Rest ist Geschmacksache...

Gruß Dietrich

von werner (Gast)


Lesenswert?

Hallo Dietrich,

> das ich Sie nicht hinschreiben muss????

war nur rein hypothetisch,
ich werde die Tabelle jetzt immer mit in den Code übernehmen.
Ist sicherer wie du schon geschrieben hast. Mir war eben nur nicht klar 
was es für Auswirkungen hat wenn sie fehlt. Wieder was gelernt.Danke.
Verstanden hab ich jetzt auch das an der Stelle des Interrupts in der 
Tabelle, meine Sprungmarke (Name wie adini,timer0ini) stehen muss.

Gruß Werner

von werner (Gast)


Lesenswert?

Hallo Dietrich,

noch was,

ich hab das noint:
                   reti

ans Ende des Codes setzen müssen, weil der Cursor immer wieder während 
des Programms dort hin gesprungen ist.

von Dietrich L. (dietrichl)


Lesenswert?

werner schrieb:
> Verstanden hab ich jetzt auch das an der Stelle des Interrupts in der
> Tabelle, meine Sprungmarke (Name wie adini,timer0ini) stehen muss.

Aber die Sprungmarke der ISR (Interrupt Service Routine). Die 
Unterprogramme "adini", "timer0ini" dürfen da ganz sicher nicht stehen! 
Das ist doch Deine Initialisierung, die nur 1x ganz am Anfang ausgeführt 
wird.

Die ISR tut das, was auf Grund des Ereignises (das den Interrupt 
ausgelöst hat) getan werden soll; z.B. Timerinterrupt: Zählen einer 
Zeit, die im Programm benötigt wird, Entprellen von Tastern ...
Hinweis:
Unterprogramme enden mit "ret" (zurück zur Adresse, wo das Programm 
aufgerufen wurde).
ISRs enden mit "reti" (zurück zur Adresse, wo das Programm durch den 
Interrupt unterbrochen wurde, mit zusätzlichem (wieder) Freigeben des 
Interrupts (wie "sei").

werner schrieb:
> ich hab das noint:
>                    reti
>
> ans Ende des Codes setzen müssen, weil der Cursor immer wieder während
> des Programms dort hin gesprungen ist.

Welcher Cursor? Zeig mal das aktuelle Programm!

Gruß Dietrich

von werner (Gast)


Angehängte Dateien:

Lesenswert?

Hallo Dietrich,

dann hab ichs doch falsch verstanden.
Naja du wirst ja sehen wie ich mir das gedacht habe.
Momentan hab ich das Problem das 2 Frequenzen an PB3 rauskommen, die von 
Timer0, ist gewollt, und die von Timer1, ist nicht gewollt.

Gruß Werner

von oldmax (Gast)


Lesenswert?

Hi Werner
Wenn du schon so lange an deinem programm arbeitest... oha, da wird es 
aber mal zeit, die Grundlagen von ASM zu verstehen. Ich fall jetzt nicht 
in den Tenor ein und mach dich fertig, denn Assembler ist ein schönes 
mächtiges Werkzeug. Doch du musst verstehen, wie es funktioniert. Ein 
Interrupt leitet den µC an eine feste Adresse in der 
Interrupt-Vektor-Tabelle. Wenn da ein RETI steht, ist die Sache für ihn 
erledigt. Steht dort ein JMP ... wird lediglich der nächste Befehl aus 
der interrupt-Service-Routine abgerufen und dort auch die Bearbeitung 
fortgesetzt. Ist manchmal etwas unverständlich, das ein JMP auf einen 
Programmteil mit RETI beendet werden muß.
Mal einfach
1
MAin
2
  ...
3
  RCALL  irgendwas
4
  ...
5
6
7
Irgendwas:
8
  a ...
9
  b ...
10
  c ...
11
  d ...
12
  e ...
13
RET
Nun tun wir mal so, als wenn an der Stelle, wo "Irgendwas:" steht, nur 1 
Befehl platz hätte, also halt nur Platz für ein "RET" oder einen "JMP" 
wäre.
Dann könnte man ja mit einem JMP auf einen freien Speicherbereich 
"springen" und dort weitere Befehle unterbringen.
1
MAin
2
  ...
3
  RCALL  irgendwas
4
  ...
5
6
7
Irgendwas:    JMP ABCDE
8
9
10
ABCDE:
11
  a ...
12
  b ...
13
  c ...
14
  d ...
15
  e ...
16
RET
Damit erreiche ich im Prinzip das selbe. Bei der IVT ist es so, das ein 
Interrupt eine feste Adresse hat, die angesprungen wird. Nun weis aber 
niemand, wie groß letztendlich so eine Bearbeitung eines Interrupts 
wird. So läßt man halt nur den Sprung zu einer ISR zu, und dann reicht 
es aus, nur soviel Speicher für einen Befehl zuzuteilen. Ich hoffe, das 
dir das ein wenig hilft.
Um dir das Arbeiten mit Assembler etwas einfacher zu machen, solltest du 
auch hier ruhig mehr Unterprogramme aufbauen. Dazu überlegst du, was 
dein programm braucht. Eine Eingabe, eine Ausgabe, einen Regler, 
Bearbeitung für zu klein und Bearbeitung für zu groß.
Zerlege deine Aufgabe in möglichst kleine Funktionseinheiten.
Dann fügst du nach und nach diese in dein Programm ein. Vorteil: sind 
die Funktionseinheiten geprüft und laufen stabil, dann kannst du sie mit 
einem Haken versehen und brauchst nicht immer wieder darin 
herumzufummeln. Und wenn dein Programm schon gewachsen ist, zögere 
nicht, eventuell neu anzufangen und mach nicht den Fehler und fang das 
Stricken an. Es ist sicherlich nicht falsch, wenn du mit Papier und 
Bleistift verschiedene Elemente in Form eines PAP's zur Ansicht bringst 
und dich daran orientierst. Den Faden nicht zu verlieren ist keine 
Kunst, wenn man sich an die Regeln hält. Fängst du aber an, mit 
Klimmzügen hier und da Korrekturen reinzubasteln, ist schnell der FAden 
weg.
Gruß oldmax

von werner (Gast)


Lesenswert?

Hallo Oldmax,

in meinem Programm soll aller 235ms der AD-Wandler angestoßen werden.
Das soll Timer1 machen. Wenn der Interrupt kommt springt der in der IVT 
auf den "adpa0" und dann im Programm auf diese Stelle.

Ich hab gerade gesehen das "main:" muss kurz über "jmp main".
Das ist der Fehler mit der Frequenzänderung und nicht der Timer1.

Gruß Werner

von Dietrich L. (dietrichl)


Lesenswert?

Hallo Werner,
es scheint so, dass Du mit Interrupts noch nicht so richtig vertraut 
bist.
Weist Du, was Interrupts sind und wozu man sie verwendet? Lies mal
http://www.mikrocontroller.net/articles/AVR-Tutorial:_Interrupts.

Grundsätzliches: Interrupts sind Ereignisse, die von einer Hardware 
innerhalb des µC-Chips (sei es Timer, AD-Wandler etc.) oder außerhalb 
(z.B. INT0) ausgelöst werden, also an einer beliebigen Stelle des 
Hauptprogramms stattfinden können.
Deiner Anwendung benötigt wohl noch überhaupt keine Interrupts, wenn Du 
noch gar nicht an ISR gedacht hast (also: was tue ich bei dem 
Interrupt). Vielleicht kannst Du das Ganze Thema "Interrupt" auch erst 
mal vergessen - und dann solltest Du im Programm auch keinen einzelnen 
Interrupt und den gesamten (per "sei") freigeben.
Es ist oft nicht nötig, mit Interrupts zu arbeiten, aber oft ergeben 
sich damit "schönere" Programme bzw. Programme mit wesentlich besserem 
Zeitverhalten.
Wenn man beliebig viel Zeit hat, braucht man (fast?) nie Interrupts!

Gruß Dietrich

Edit: Ich sehe, Du brauchst (oder willst) doch den Timer-Interrupt.

von Dietrich L. (dietrichl)


Lesenswert?

Ich sehe gerade noch: dort, wo Dein "noint:" stand, geht das nicht, da 
Du nach "Reset:" ja direkt in diese Routine hineinläufst und bei "reti" 
in die Wüste springst (auf dem Stack ist noch keine Rückkehradresse 
gespeichert; das würde erst ein "call" oder "Interrupt" (=call über 
Hardware ausgelöst) machen.

Ein von der Logik her "schöner" Platz für das "noint:" wäre z.B.:
1
.org 0x30             ;programm ab flashadresse 30h speichern
2
;=========== ISR fuer nicht geplante Interrupts ============= 
3
noint:
4
      reti
5
;=============== stackpointer ini ===========================
6
reset:
7
      .....

Gruß Dietrich

von werner (Gast)


Angehängte Dateien:

Lesenswert?

Hallo Dietrich,

ich hab nochmal im TUT nachgesehen und mir Gedanken zur IVT gemacht.
Es gibt da so viele Varianten welche ist denn nun richtig?
Die unterste Variante ist die die nach meiner Meinung dann auf mein 
Programm zutreffen müsste.

Gruß Werner

von spess53 (Gast)


Lesenswert?

Hi

>Die unterste Variante ist die die nach meiner Meinung dann auf mein
>Programm zutreffen müsste.

>.org  ADC    ;ADC Conversion Complete
>  rjmp adini

Nein . Die Initialisierung des ADC ist kein Interrupt! Genau so 
wenig wie die Timerinitialisierungen.

Der ADC-Interrupt wird z.B. ausgelöst, wenn er freigegeben ist und der 
ADC mit einer Wandlung fertig ist. Genau dann springt der Controller zur 
Adresse $1C. und dort findet er den 'jmp' zu der Routine, die 
abgearbeitet werden soll, wenn eine Wandlung beendet ist. Und das ist 
nicht die Initialisierung.

MfG Spess

von werner (Gast)


Angehängte Dateien:

Lesenswert?

Ich habs ins Programm kopiert und alles rot.......

Dann hab ich die Bezeichnungen aus der m16def.inc verwendet und siehe da 
es geht.

Gruß Werner

von werner (Gast)


Lesenswert?

Hallo Sess,

also ist es genau umgedreht wie ich mir das gedacht habe,
aus dem TUT

.include "m8def.inc"

.def temp = r16

.org 0x000
         rjmp main            ; Reset Handler
.org INT0addr                          3.)+++und in der routine 
ausgeführt
         rjmp int0_handler    ; IRQ0 Handler 2.)+++dann hier zugeordnet
.org INT1addr
         rjmp int1_handler    ; IRQ1 Handler

.
.
.
int0_handler:       1.)+++ und hier wird der interrupt (Taste) ausgelöst
         sbi PORTB, 0
         reti

Gruß Werner

von oldmax (Gast)


Lesenswert?

Hi Werner
Was tust du ? Willst du noch ein halbes Jahr darauf warten, das dir ein 
Licht aufgeht ?
1
;============== hauptprogramm ====================================
2
3
4
5
adpa0:     
6
      sbi  ADCSR,ADSC      ;Wandlung starten
7
fertig:      
8
      sbic ADCSR,ADSC      ;warten bis wandlung
9
        rjmp fertig        ;beendet    
10
    
11
      in r17, ADCL      ;ad lowbyte in r17 merken
12
      in r18, ADCH      ;ad highbyte in r18 merken
13
      MOV v_akku, r18
14
      MOV v_akkupwm, v_akku
15
    
16
17
;auswahl
18
      cp v_store, v_akku  ;vergleiche gespeicherte mit gemessener
19
      brsh up      ;akkuspannung, wähle aus ob höher oder niedriger
20
      brlo down    ;und springe zu up oder down
21
      jmp main
22
down:      
23
      dec v_akkupwm      ;akkuspannung zu hoch, zaehle runter  ;
24
      cp v_store, v_akkupwm  ;und vergleiche mit gespeicherter spannung
25
      brne down        ;noch nicht gleich? springe zu down
26
      breq sichern      ;wenn gleich, sichern
27
      jmp main
28
up:      
29
      inc v_akkupwm
30
      ;cp v_store, v_akkupwm
31
      ;brne up
32
      jmp sichern
33
      jmp main
34
sichern:
35
      sts 0x0072, v_akkupwm  ;wert im sram gesichert, fuer spaeter
36
      mul r19,r21        ;ergebnis mit r20 verstaerken
37
      mov r19, r0        ;und in r19 verschieben
38
      out ocr0, v_akkupwm      ;als vorladewert nach ocr0
39
      
40
      ;out ocr0,r19     ;und ins vorladeregister von tc0 gespeichert
41
main:                  ; lt. simulation klappt das nicht, ich weis
42
                  ;mir keinen rat warum???  
43
      jmp main
Schau dir doch mal deinen Code an und versuche zu folgen. Der Grundsatz 
beim Assembler ist:
Befehl 1
Befehl 2
Befehl 3
usw
Es kann immer ein Befehl kommen, der heißt:
Springe zu Befehl 1
Wenn du in deinem Programm u.U. folgendes stehen hast:
     JMP Main
dann tut das der Controller auch, auch wenn die Marke "Main" VOR dem 
Sprungbefehl steht. Das Ergebnis kannst du dir dann ja denken, oder ?
Lies nochmal alle hilfreichen Antworten, versuche eine Struktur in dein 
Programm zu bekommen. Die Initialisierungen rufst du mit Calls auf, 
warum bleibst du nicht bei dieser Technik und baust in der 
Programmschleife auch Calls ein
1
Start:                      ; wird nur 1x durchlaufen
2
   ....                     ; zuerst stack initialisieren
3
   RCALL Init_IO            ; Ein-und Ausgabe Ports initialisieren
4
   RCALL Timer_0            ; Timer 0
5
   RCALL Timer_1            ; und Timer 1 initialisieren
6
   RCALL Analog             ; 
7
   RCALL usw                ; weitere Initialisierungen
8
9
Loop:         ; Hier beginnt die Programmschleife.
10
   RCALL Read_IO            ; lesen der Eingänge
11
   RCALL Read_Analog        ; Analogwert lesen
12
   RCALL Chk_Analogwert     ; Prüfe irgendwas
13
   .....
14
   RCALL Write_IO           ; Setze Ausgänge
15
RJMP  Loop
Wenn du so deine Schleife aufbaust, dann kannst du die Unterprogramme 
nach und nach mit Inhalten füllen.
Damit dein Programm aber auch ohne diese Inhalte läuft, setzt du bei 
einem Unterprogramm erst mal ein RET. So wächst dein Programm langsam 
und du kannst jederzeit weitere Bearbeitungsschritte einfügen.
Gruß oldmax

von werner (Gast)


Lesenswert?

Hallo Oldmax,

ich hab was ausprobiert und vergessen das main wieder zurück 
zuschreiben,
ehe ich die Datei hochlade.

Gruß Werner

von werner (Gast)


Angehängte Dateien:

Lesenswert?

Hallo,

ich hab mal Oldmax seine Hinweise versucht in die Tat umzusetzen
und das Programm strukturiert.
Hinzugekommen ist das Auslesen von PA1+2 mittels OVF1addr.

Gruß Werner

von Dietrich L. (dietrichl)


Lesenswert?

- Du hast immer_noch in der Interrupt-Vektortabelle
    "timer1ini"
    "timer0ini"
    "adini"
  die dort nichts zu suchen haben!
Da Du zur Zeit noch keine Anwendung für den Interrupt hast (?), spring 
zumindest an eine im Moment noch leere ISR ("Marke:" + "reti").

- in "auswahl:" hast Du noch mehrere "jmp main". Ein Unterprogramm darf 
nur mit "ret" verlassen werden!!!

- "adwand:" wird (noch?) nicht verwendet; ist das eine "stille Reserve"?

Gruß Dietrich

von Karl H. (kbuchegg)


Lesenswert?

Du machst derartig unmotivierte Änderungen, dass einem schwindlig wird.

Du musst SORGFÄLTIGER arbeiten, viel sorgfältiger. Noch mehr 
sorgfältiger.

1
main:
2
    rcall auswahl
3
4
5
6
jmp main
7
;============== auswahl+pwm ausgabe ====================================
8
auswahl:
9
      MOV v_akku, r18
10
      MOV v_akkupwm, v_akku
11
    
12
13
14
      cp v_store, v_akku  ;vergleiche gespeicherte mit gemessener
15
      brsh up      ;akkuspannung, wähle aus ob höher oder niedriger
16
      brlo down    ;und springe zu up oder down
17
      jmp main

wieso geht es hier mit einem jmp main zu main zurück? auswahl ist per 
rcall aufgerufen worden! Also muss es hier mit einem ret zurückgehen!


Die Sache, was in die Interrupt Vektoren reinkommt, hast du auch noch 
nicht bereinigt. Da stehen immer noch die Calls zu den Init Funktionen 
drinnen (wenn auch jetzt die Syntax in Ordnung ist).

Weiters hast du eine ADC-Init Funktion, die du auch aufrufst, aber in 
der Wandlungsfunktion änderst du die komplette Konfiguration wieder, was 
du musst, weil du mit 2 Kanälen arbeiten willst. Dann schmeiss das Zeug 
aus der Init Funktion raus! Wenn du den ADC mit ADLAR betreibst, dann 
brauchst du auch das Low-Byte vom ADC nicht auslesen. Lass überflüssige 
Sachen weg! Sie verwirren nur und ziehen das Programm unnötig in die 
Länge und verringern dadurch die Übersicht! Gerade in Assembler ist es 
wichtig die Übersicht zu behalten. Du tust dir nichts gutes damit, wenn 
du Anweisungen drinnen lässt, die du nicht brauchst. Im Sprichwort heißt 
das dann: Man sieht den Wald vor lauter Bäumen nicht mehr.

Und zur Initialisierung der Timer, bzw. generell zur Verwendung von 
Zahlensystemen. Man benutzt immer das Zahlensystem, das in der Situation 
am logischten ist. Wenn du den Timer auf 0 initialisieren willst, dann 
schreib das auch so

    ldi  temp1, 0
    out  TNCT1L, temp1

Oder (im Zusammenhang mit 16 Bit Zahlen)

    ldi  temp1, HIGH(0)
    out  TCNT1H, temp1
    ldi  temp1, LOW(0)
    out  TNCT1L, temp1

an dieser Stelle gibt es keinen wie auch immer gearteten Grund, die Zahl 
die nach TCNT1 geladen werden soll, als Binärzahl anzugeben. Wenn du den 
Timer mit 8765 laden willst, dann schreibst du

    ldi  temp1, HIGH(8765)
    out  TCNT1H, temp1
    ldi  temp1, LOW(8765)
    out  TNCT1L, temp1

und rechnest dir nicht die Bitmuster aus. Denn die Bitmuster sagen dir 
als Mensch, der mit dem Dezimalsystem aufgewachsen ist, nichts. Niente, 
nada, nothing. Aber als Dezimalzahl sagt dir das was. Mit 8765 
verbindest du eine Vorstellung über die Größe der Zahl. Mit der 
identischen Zahl als Bitmuster  00100010 00111101 verbindest du erst mal 
gar nichts, ausser das das irgendwelche 0-en und 1-en sind.

Und zum Thema: Initialisierung des Timer 0. Da kann ich nur den Kopf 
schütteln. Wieder magische Binärkonstanten anstelle sprechender 
Bitnamen.

von Karl H. (kbuchegg)


Lesenswert?

Der Käse
1
down:      
2
      dec v_akkupwm      ;akkuspannung zu hoch, zaehle runter  ;
3
      cp v_store, v_akkupwm  ;und vergleiche mit gespeicherter spannung
4
      brne down        ;noch nicht gleich? springe zu down
5
      breq sichern      ;wenn gleich, sichern

ist auch noch immer da.

von Karl H. (kbuchegg)


Lesenswert?

Und was die Klammerung in den Initfunktionen

   cli
   ....
   sei

bringen soll, muss mir auch einmal wer erklären.

Progammaufbau:
1
   rjmp init
2
3
  ... Interrupt Vektoren
4
5
init:
6
7
  ... Initialisierungen, Hardware in den korrekten Zustand bringen
8
  ... Timer initialisieren etc.
9
10
  sei
11
12
main:
13
14
   ... Hauptlogik des Programms
15
16
   rjmp main
17
18
...


Da gibt es EINEN sei. Der steht am Programmbeginn, nachdem die Hardware 
konfiguriert wurde und bevor dann die Hauptschleife übernimmt. Aber 
versteck ihn dir NICHT in den Konfigurierfunktionen! Und vor allen 
Dingen nicht mehrere.
Wieder: Schaff dir Übersicht. Funktionen sollen das tun, wie sie heißen! 
Lass unnötige Dinge weg. Du brauchst in den Init Funktionen keine 
cli-sei Klammerung, also lass sie weg. Ganz im Gegenteil: Der 
Programmteil 'die Hardware bzw. benötigte Komponenten initialisieren' 
muss meistens sowieso ohne eingeschaltete Interrupts ablaufen. Und zwar 
komplett! Denn was hilft es dir wenn Komponente A schon fertig 
konfiguriert ist, Komponente B aber noch nicht und aus irgendeinem Grund 
wird ein Interrupt ausgelöst? Das Gesamtsystem ist noch nicht fertig 
konfiguriert - also machen Interrupts in den meisten Fällen hier noch 
gar keinen Sinn.

Du brauchst aber, wenn du Interrupts benutzen willst (und nur dann), 
EINEN sei, der gemacht werden muss, nachdem  alles korrekt eingestellt 
wurde. Genau das ist der Fall an der Übergabestelle vom Vorgeplänkel 
(alles initialisieren) zur Hauptschleife - wenn der Teil nach 'init' zu 
Ende geht und 'main' beginnt.

von Karl H. (kbuchegg)


Lesenswert?

Dietrich L. schrieb

> Da Du zur Zeit noch keine Anwendung für den Interrupt hast (?), spring
> zumindest an eine im Moment noch leere ISR ("Marke:" + "reti").

Hab ich auch gedacht.
Aber siehe: Initialisierung des Timer 1
1
    ori temp1, (1<<TOIE1)    ;Timer1 Overflow Interrupt aktiviert    
2
    out TIMSK, temp1

Ab jetzt gibt es tatsächlich auftretende Interrupts.
Was natürlich den Aufruf der Init Funktion nicht besser macht.

von werner (Gast)


Angehängte Dateien:

Lesenswert?

Hallo Dietrich und Karl Heinz,

danke für eure Hinweise, die jmp main in auswahl habe ich so von 
"krapao"
weiter oben übernommen. Ich habe jetzt die IVT bereinigt und den "Käse" 
rausgenommen, das "sei" hab ich am Ende der letzten ini des "adini" 
hingeschrieben oder muss das unter den letzten rcall Aufruf der inis.
Die Schreibweise  ldi  temp1, HIGH(8765) ist mir nicht geläufig deshalb 
verwende ich sie auch nicht, um Fehler zu vermeiden.
Nochmal zur IVT, da ja Timer1 jetzt läuft, muss der da nun mit erwähnt 
werden so wie ich es gemacht habe.... siehe neuen Code?

Gruß Werner

von Karl H. (kbuchegg)


Lesenswert?

werner schrieb:
> Hallo Dietrich und Karl Heinz,
>
> danke für eure Hinweise, die jmp main in auswahl habe ich so von
> "krapao"
> weiter oben übernommen.

Grundsätzlich darfst du Code NIEMALS einfach nur so übernehmen!
Du musst verstehen, was der Code macht, dieses Verständnis in den 
Kontext deines Programmes setzen und dann überlegen, was der Vorschlag 
jetzt für dich heißt.
Aber Code einfach unbesehen zu kopieren, ist ein sicherer Weg ins 
Nirwana. Es gibt ein paar Ausnahmen von dieser Regel aber im großen und 
ganzen: Copy%Paste funktioniert nicht.

von Karl H. (kbuchegg)


Lesenswert?

werner schrieb:
> das "sei" hab ich am Ende der letzten ini des "adini"
> hingeschrieben

manchmal hat man das Gefühl, man redet gegen eine Wand

Beitrag "Re: Hilfe Ladereglung Atmega16 Assembler"

von Karl H. (kbuchegg)


Lesenswert?

> Die Schreibweise  ldi  temp1, HIGH(8765) ist mir nicht geläufig
> deshalb verwende ich sie auch nicht, um Fehler zu vermeiden.

Jetzt ist sie dir geläufig.
Also verwende sie.

von Karl H. (kbuchegg)


Lesenswert?

werner schrieb:

> Nochmal zur IVT, da ja Timer1 jetzt läuft, muss der da nun mit erwähnt
> werden so wie ich es gemacht habe.... siehe neuen Code?
1
.org  OVF1addr  
2
  jmp timer1ini   ;TIMER1 OVF ;Timer/Counter1 Overflow

und tschüss.

Tippen wir eigentlich so undeutlich, oder wie ist das.

Die richtige Aktion, die bei einem Overflow des Timers zu machen ist, 
ist NICHT ihn neu zu intialisieren.
Die richtige Aktion wird etwas anderes sein, was du noch nicht hast, 
aber es ist auf keinen Fall ein Aufruf der Init-Funktion des Timers!

Mach halte einen reti rein, wenn du die Aktion noch nicht hast.

Und nein. Das ist kein Kavaliersdelikt, das ist ein schwerer Fehler!
Andere wurden schon wegen weniger geteert und gefedert.

von werner (Gast)


Angehängte Dateien:

Lesenswert?

Hallo Karl Heinz,

"manchmal hat man das Gefühl, man redet gegen eine Wand"

Ich bin mir nur nicht sicher deswegen hab nochmal nachgefragt.
An der Stelle ist doch die Initialisierung abgeschlossen,oder doch 
lieber

        rcall timer1ini ;TIMER 1 initialisieren
  rcall timer0ini ;Timer 0  initialisieren
        rcall adini     ;A-D Wandler AD0 initialisieren

da      sei   hinschreiben?????
Aber ich muss Euch wirklich Danken, seit dem Ihr Kommentiert sieht doch 
das Programm viel Übersichtlicher aus. Es wäre jetzt unverschämt hier
" weiter so " hinzuschreiben aber wüsste ohne Euch nicht weiter ;-)
Misst ist eben bloß das es noch nicht funktioniert.

Gruß Werner

von Karl H. (kbuchegg)


Lesenswert?

werner schrieb:
> Hallo Karl Heinz,
>
> "manchmal hat man das Gefühl, man redet gegen eine Wand"
>
> Ich bin mir nur nicht sicher deswegen hab nochmal nachgefragt.
> An der Stelle ist doch die Initialisierung abgeschlossen,

Und was tust du, wenn dann irgendwann zb die LCD Initialisierung dazu 
kommt? Verschiebst du dann den sei von der ADC Initialisierung in die 
LCD Initialisierung?

Mach ihn doch gleich dort hin, wo er auf immer und ewig bleiben kann!

von werner (Gast)


Lesenswert?

Hallo Karl Heinz,

Gott sei Dank ist ein dünnes Kabel zwischen uns, wo weder Teer noch 
Federn durchpassen !!!
Der Timer1 soll aller 239ms den AD-Wandler "adwand" anstoßen, also muss 
der da hin.
Ich dachte wenn ich das ins SFIOR schreibe langt das, wieder falsch.

Gruß Werner

von Dietrich L. (dietrichl)


Lesenswert?

werner schrieb:
> Der Timer1 soll aller 239ms den AD-Wandler "adwand" anstoßen

Dann sag mal, wo das "anstoßen" passiert!

von Karl H. (kbuchegg)


Lesenswert?

werner schrieb:
> Hallo Karl Heinz,
>
> Gott sei Dank ist ein dünnes Kabel zwischen uns, wo weder Teer noch
> Federn durchpassen !!!
> Der Timer1 soll aller 239ms den AD-Wandler "adwand" anstoßen, also muss
> der da hin.

Ja.
Aber sicher nicht dadurch, dass du die INIT Funktion aufrufst!

Die Init Funktion kommt einmal zum Einsatz. EINMAL.

Eine Funktion, die über einen Interrupt Vektor aufgerufen wird, muss 
nämlich etwas anders aussehen. Daher kannst du die jetzige Funktion 
nicht benutzen!
Ausserdem macht sie das falsche.

von Karl H. (kbuchegg)


Lesenswert?

Mal eine Zwischenfrage:

Ist das dein erstes Programm?
Du hast da so dermassen Schwächen, dass ich mich Frage was wir im 
Tutorial falsch gemacht haben. Ich bin immer noch der Meinung: Wer 
langsam und behutsam das Tutorial durcharbeitet, arbeitet nicht 
durchlesen!, der kann auf deine Probleme gar nicht stossen. Das eine 
oder andere mag missverständlich geschrieben sein, aber bei dir fehlts, 
verzeih, an allen Ecken und Enden.

Tutorial durcharbeiten heißt nicht: Ich les mir das durch, nicke 2 mal 
mit dem Kopf und geh weiter zum nächsten Kapitel. Tutorial durcharbeiten 
heißt: Ich programmier die Dinge, die im jeweiligen Kapitel vielleicht 
nur angerissen wurden zu Ende, ich mach Variationen davon, ich denk mir 
selbst ähnliche Problemstellungen aus und erst dann, wenn ich das Gefühl 
habe, eine gewisse Sattelfestigkeit erreicht zu haben, gehts weiter zum 
nächsten Kapitel.

Ich merke bei dir irgendwie nicht, dass du verstehst was du da tust. Mir 
kommt vor, du malst im wahrsten Sinne des Wortes den Code nur ab, ohne 
dir Gedanken darüber zu machen, was da im Code eigentlich passiert, 
warum der so aussieht, etc. Eben wie jemand, der sofort beim vorletzten 
Kapitel im Tutorial einsteigt und meint das davor brauche er alles 
nicht. Und dann stolpert er von einem Problem ins nächste.

von werner (Gast)


Lesenswert?

Hallo Karl Heinz,

Das ist das erste umfangreichere Programm was ich schreiben will.
Vorher waren das nur kleine Progrämmschen als Trockenübung im Simulator.
Jetzt ist alles auf dem Steckbrett.
Das Tut hab ich mir schon gut angesehen, aber ich habe z.B. nicht 
gefunden wie man den AD-Wandler mit Timer1 aller x s/ms anstößt.
Im DB habe ich dann gesehen das im ADCSRA das (1<<ADATE) setzen muß (was 
ich wie ich eben gesehen habe vergessen hab reinzuschreiben)und im SFIOR 
als Triggerquelle den Overflow des Timer1 auswählen 
(1<<ADTS2)|(1<<ADTS1), richtig??

Gruß Werner

von oldmax (Gast)


Lesenswert?

Hi
Es erstaunt mich doch immer wieder, wie viel Geduld man haben kann. Hab 
mir grad nochmal den Eingangspost durchgelesen.... da denkt man doch, es 
mit jemandem zu tun zu haben, der nicht nur lesen und schreiben kann, 
sondern der auch sonst noch etwas helle ist und mit der Materie 
vertraut....
Zitat:
>Nun denn, ich bin seit Wochen damit beschäftigt eine vernünftige
>Ladereglung für eine Projektarbeit zu konstruieren.
>Das größte Problem war bisher der StepDown und dessen Ansteuerung, was
>aber nun halbwegs zufriedenstellend funktioniert.
>Nun ist es aber so das der StepDown lastabhängig fungiert, heist wenn
>ich z.B. eine 12V/20W Halogenlampe anschließe muss ich mit anderen
>Vorladewerten (tc0/ocr0) arbeiten als mit einem Akku .
Vielleicht hab ich da doch etwas viel hinein interpretiert....
Da es den Anschein hat, das Werner doch große Schwierigkeiten mit dem 
Lesen und Verstehen hat, gebe ich hier auf. Entweder er macht sich einen 
Spaß darraus uns zu verarschen, oder er ist 12 und besucht noch die 
Grundschule.....
oder er hat einfach einen schlechten Dozenten. Ich weiß es nicht, aber 
im Tut steht fast alles, was er braucht. Es sind soviele Tips gegeben 
und der Murks ist immer noch ein völlig verkorkstes Programmgerüst, was 
immer wieder mit kleinen (unsinnigen) Änderungen präsentiert wird.
Ich bin raus
Gruß oldmax

von werner (Gast)


Lesenswert?

Hallo Oldmax,

zuerst vielen Dank für Deine Geduld.
Ich will hier wirklich keinen Verarschen , warum auch.
Die Programmschnippsel die wir mit unserem Dozenten gemacht haben 
unterscheiden sich schon gewaltig von dem wie Ihr das macht.
Vielleicht stelle ich mich ja wirklich zu doof an , aber ich gebe 
trotzdem nicht auf. Was für Dich bestimmt kleine unsinnige Veränderungen 
sind , ist für mich wieder was gelernt zu haben.
Ich habe versucht das Programm nach Euren Hinweisen zu strukturieren, 
wenn es
da Unterschiede im Verständnis gibt ist das nur allzu menschlich, da ich 
ja nicht wissen kann wie deine Vorstellungen sind. Mein 12.Lebensjahr 
ist auch schon eine Weile her, vielleicht ist das ja auch mit ein Grund 
das ich so meine Problemchen mit der Mikroelektronik habe, weil ich noch 
Relaissteuerungen hatte , wo man sehen konnte was passiert und was nicht 
geht konnte man dann auch ausmessen. Heute sitzt man vor einer Blackbox
und kann nur hoffen das man es Ihr richtig sagt und wenns dann nicht 
geht ist es ein Problem herauszufinden warum.

Gruß Werner

von Karl H. (kbuchegg)


Lesenswert?

werner schrieb:
> Hallo Karl Heinz,
>
> Das ist das erste umfangreichere Programm was ich schreiben will.

Verzeih.
Aber das bisherige ist eben NICHT umfangreich. Das ist

  ADC initialisieren
  PWM initialisieren

  main:                                  <------+
                                                |
    ADC auslesen -> Wert                        |
                                                |
    if( Wert kleiner Vorgabewert )              | Hauptschleife
      PWM um 1 erhöhen                          |
                                                |
    if( Wert größer Vorgabewert )               |
      PWM um 1 erniedrigen                      |
                                                |
    bischen warten                              |
                                                |
    rjmp main                              >----+

fertig. Das ist alles für eine erste funktionierende Version. Das ist 
keineswegs umfangreich. Auch in Assembler sollte so etwas nicht länger 
als 1 bis 2 Abende (a 3 oder 4 Stunden) dauern, ehe das sauber läuft.

Jeder der eine LED blinken lassen kann, ala


    main:                             <-----+
                                            |
       LED einschalten                      |
                                            |
       bischen warten                       |
                                            |
       LED ausschalten                      |
                                            |
       bischen warten                       |
                                            |
       rjmp main                       >----+

sollte das eigentlich hinkriegen, wenn er die zusätzlichen Bausteine
'ADC', 'Timer mit PWM' und 'wie vergleiche ich Werte' unter Kontrolle 
hat.

Und da kann man dann jetzt anfangen sich zu fragen, wie man zb den Teil 
'bischen warten' loswerden kann und durch etwas besseres ersetzt und wie 
dann die Struktur aussehen muss.

> Das Tut hab ich mir schon gut angesehen, aber ich habe z.B. nicht
> gefunden wie man den AD-Wandler mit Timer1 aller x s/ms anstößt.

Und genau da ist das Problem. Das findet sich so auch nicht im Tutorial. 
Im Tutorial findet sich, wie man den ADC bedient und es findet sich wie 
man mit einem Timer regelmässige Dinge tut.
Und jetzt müsstest du die beiden kombinieren.
Im Tutorial ist das am Beispiel einer Uhr gezeigt, die regelmässig den 
(gedachten) Sekundenzeiger um 1 weiterstellt und bei dir wäre es dann 
eben das Auslösen 1 "Regelschritts" der aus "mit dem ADC messen und 
gegebenenfalls die PWM nachstellen" besteht. Ich hätt sogar kein Problem 
damit, wenn du dafür kein Job-Flag benutzt sondern das alles in der 
Interrupt Service Routine machst - aber: soweit bist du noch lange 
nicht.

Es ist wie beim Schachspielen: Du lernst wie die Figuren ziehen und dann 
geht es los, wie Kombinationen von diesen Zügen etwas bewirken.

Die Programmierung steckt in den Kombinationen! Die einzelnen Dinge 
(ANweisungen) einer Programmierpsrache sind vergleichsweise einfach und 
trivial. Die Kombination macht die Power. Nur bei dir fehlt es an vielen 
Stellen an den 'Einzelzügen'. Im ganzen Thread, der sich jetzt schon 
über weite Strecken hinzieht, geht es in Wirklichkeit noch gar nicht 
darum, dass wir uns über Kombinationen unterhalten und verschiedene 
Möglichkeiten und deren Vorteile/Nachteile unterhalten, sondern wir 
stecken bildlich gesprochen immer noch dort fest, ob ein Bauer beim 
Schach jetzt 1 Feld oder 2 Felder vorrücken darf.
Und das ist dann etwas frustrierend, wenn man eigentlich darüber reden 
möchte (durchaus auch von dir angestossen), ob eine Rochade an dieser 
Stelle jetzt sinnvoll ist oder nicht, sich dann immer wieder in den 
Niederungen wiederzufinden, dass man dich wieder und wieder ermahnen 
muss, dass ein Läufer nur diagonal ziehen kann.
Da hilft es auch nichts, wenn du irgendwo eine Schachpartie 'Bobby 
Fischer gegen Spasski' ausgräbst und dir von dort eine Finesse des 
Endspiels abschaust. Das ist mit deinem Kentnissstand noch viel zu 
schwer, das alles zu überblicken und es bringt auch nichts, wenn dir 
hier wer erklärt, warum der Turmzug eine gute Idee war und er dadurch 
Kontrolle über das Zentrum erhielt. Das ist ein völlig anderer Level auf 
dem wir uns da unterhalten müssten.

von werner (Gast)


Angehängte Dateien:

Lesenswert?

Hallo Karl Heinz,

auch ein Schachspieler. Ist bei mir schon eine Weile her, ich habs mal 
von meinem Vater gelernt, meistens verloren, bin dann eine Weile in 
einem Schachklub gewesen, habe eine Weile auch aktiv gespielt. Dann hat 
mein Vater meistens verloren und er hatte keine Lust mehr. Ich habe 
immer die Simultanspieler bewundert die in einem Raum mit mehreren 
gespielt haben und
nicht mal ans Brett gegangen sind um den nächsten Zug zu machen! Na ja 
lang her.
Vielen Dank für deinen Anstoss, ich habe das Programm verändert und es 
sollte nun eigentlich den vorgeschlagenen Ablauf folgen.
Die Warte-Routine habe ich aus dem LCD TUT genommen.

Gruß Werner

von spess53 (Gast)


Lesenswert?

Hi

>      ldi temp1,(1<<ADSC)
>      out  ADCSRA,temp1  ;Wandlung pa0 starten

Nein. Damit machst du deine vorherigen Einstellungen in ADCSRA platt.

Die Ausführung von Karl-Heinz sollten dir eigentlich sagen, das dir 
selbst für ein so simples Programm die Basics fehlen. Dem schließe ich 
mich an.

MfG Spess

von werner (Gast)


Lesenswert?

Hallo Spess,

vielen Dank für deinen Hinweis.
Das war wirlich ein böser Fehler. Ich wollte eigentlich nur das ADSC Bit 
setzen. Hab es gleich korrigiert

  sbi  ADCSRA,ADSC

ist besser so.

Gruß Werner

von spess53 (Gast)


Lesenswert?

Hi

Und was soll das:

>      sts 0x0072, v_akkupwm  ;wert im sram gesichert, fuer spaeter  ????

Überlasse die Ramverwaltung dem Assembler:
1
   .dseg
2
3
akkupwm:     .byte 1
4
5
   .cseg
6
7
....
8
   sts akkupwm, v_akkupwm

MfG Spess

von werner (Gast)


Angehängte Dateien:

Lesenswert?

Hallo Spess53,

danke , hab es hoffentlich richtig korrigiert , dazugelernt.

Gruß Werner

von Karl H. (kbuchegg)


Lesenswert?

OK.

Ich hab mal aus deinem Programm einen Auszug gemacht, um auf etwas 
hinzuweisen und dir eine Frage dazu zu stellen
1
.def v_akku = r12    ;Messwert Akku-Spannung PA1
2
...
3
.def v_akkupwm = r19  ;ermittellter Vorladewert ocr0 für 13.4v
4
...
5
6
auswahl:
7
       mov v_akkupwm, v_akku

kannst du mir logisch begründen, warum du hier die gemessene Spannung 
(v_akku) an den OCR Wert (v_akkupwm) zuweisen willst?

Der OCR Wert wird doch vollkommen lösgelöst von der Akkuspannung 
ermittelt.
Ist die gemessene Spannung höher als der Sollwert, dann wird der OCR 
Wert verringert. Ist die gemessene SPannung geringer als der Sollwert, 
dann wird die OCR erhöht. Der Zusammenhang OCR Wert zu gemessener 
Spannung ist nur indirekt. Genau das ist ja der ganze Witz an einer 
Regelgung: Dass sich der OCR Wert von selbst ergibt, in dem er solange 
verändert wird, bis gemessener Wert und Sollwert übereinstimmen.

Wenn du die Stellgröße (den OCR Wert) direkt per Formel an eine andere 
Größe koppelst, dann hast du einen "Steller".
   Ein Poti wird gedreht und je nach Potistellung leuchtet eine
   Lampe dunkler oder hellt. Am Poti ist eine Skala: Will ich
   die Lampe x Einheiten hell haben, dann drehe ich das Poti
   auf eine bestimmte Stellung.

Ein Regler hingegen vergleicht eine Messgröße mit der Sollgröße und 
abhängig vom Ergebnis wird die Stellgröße verändert. Es existiert kein 
direkter formelmässiger Zusammenhang mehr zwischen der Stellgröße und 
der Sollgröße. Der ergibt sich von selbst, indem man die Stellgröße (den 
OCR Wert) solange verändert, bis sich das gewünschte Ergebnis einstellt. 
Das ist dann ein "Regler".
   Eine Photozelle misst die Helligkeit der Lampe und wenn die zu
   dunkel ist, dreht sie das Poti weiter auf. Ist sie zu hell
   dreht sie das Poti zurück.
   Es gibt keine Skala mehr am Poti. Sie ist ersetzt durch den
   Vergleich "gemessener Wert - gewünschter Wert" und dem
   daraus sich ergebenden "Weiter nach Links" oder "Weiter nach Rechts"
   drehen des Potis.

von werner (Gast)


Lesenswert?

Hallo Karl Heinz,

ich will hier den gemessene akkuspannung in die v_akkupwm übergeben, 
damit der gemessene akkuspannungswert nicht verändert wird.
Ich betrachte sozusagen v_akkupwm als Arbeitsregister für die Ermittlung 
des
ocr0-Wertes.

Gruß Werner

von Karl H. (kbuchegg)


Lesenswert?

werner schrieb:
> Hallo Karl Heinz,
>
> ich will hier den gemessene akkuspannung in die v_akkupwm übergeben,
> damit der gemessene akkuspannungswert nicht verändert wird.
> Ich betrachte sozusagen v_akkupwm als Arbeitsregister für die Ermittlung
> des
> ocr0-Wertes.

Dann solltest du vielleicht mal deinen Kommentar noch einmal lesen, 
welchen Wert eigentlich das Register v_akkupwm repräsentiert!

Und auch mal den Rest deines Programmes lesen, was mit diesem Wert 
weiter passiert. v_akkupwm IST dein ocr Wert. Nur eben in einem 
Arbeitsregister und nicht direkt im OCR Register.

von werner (Gast)


Lesenswert?

Hallo Karl Heinz,

ja schon es ist das Register in welchem der ocr0 Wert ermittelt wird um 
Ihn dann an das ocr0 zu übergeben.
Man merkt schon das der AD-Wandler jetzt funktioniert, aber bei 
Lastwechsel sehe ich keine optische Veränderung an der PWM und die 
Spannung wird auch nicht nachgeregelt. Allerdings bricht der 
Spannungswert jetzt nicht mehr so stark ein.

Gruß Werner

von Dietrich L. (dietrichl)


Lesenswert?

werner schrieb:
> ja schon es ist das Register in welchem der ocr0 Wert ermittelt wird um
> Ihn dann an das ocr0 zu übergeben.

Dann schau mal in Deinem Programm (wie Karl Heinz schon bemerkte):
1
auswahl:
2
      mov v_akkupwm, v_akku

jetzt steht in v_akkupwm die Akku-Spannung ...
1
      ....
2
down:      
3
      dec v_akkupwm      ;akkuspannung zu hoch, zaehle runter  ;

...die Du jetzt runterzählst (oder bei "up:" rauf)...
1
      jmp sichern      ;sprung zum sichern/ausgabe ocr0
2
      ....
3
sichern:
4
      
5
      sts vakkupwm, v_akkupwm     ;wert im sram gesichert, fuer spaeter
6
      out ocr0, v_akkupwm      ;als vorladewert nach ocr0
7
ret

... dann sicherst und in das ocr0-register lädst (also immer noch die 
Spannung!).
Das gesicherte wird nie gelesen, also auch nie benutzt.
Merkst Du was?
Ich verrate es schon mal (sonst zieht sich das ja noch ewig hin und so 
jung bin ich auch nicht mehr):
Du musst den gesicherten Wert "vakkupwm" holen und dann diesen mit "dec" 
bzw. "inc" verändern!

Gruß Dietrich

Edit: Wenn Du das Programm änderst, musst Du "vakkupwm" auch noch 
initialisieren, damit nach Einschalten das PWM nicht unkontrolliert 
beginnt. Außerdem muss noch sicher gestellt werden, dass bei "inc" und 
"dec" der Wert innerhalb eines gültigen Bereiches bleibt.

von Karl H. (kbuchegg)


Lesenswert?

Dietrich L. schrieb:

> Ich verrate es schon mal (sonst zieht sich das ja noch ewig hin und so
> jung bin ich auch nicht mehr):
> Du musst den gesicherten Wert "vakkupwm" holen und dann diesen mit "dec"
> bzw. "inc" verändern!

Oder aber er macht ganz einfach mit v_akkupwm weiter und pfeift komplett 
auf den gesicherten Wert (der momentan sowieso keine Funktion erfüllt).


Hat ja sowieso keinen Zweck. Mit Hinweisen kommen wir nicht weiter
1
auswahl:
2
      cp v_store, v_akku  ; vergleiche messwert mit sollwert
3
      breq doReturn       ; gleich? dann gibts nichts zu tun
4
      brsh up             ; zu groß?
5
                          ; -> nicht zu groß, also muss es zu klein sein
6
down:      
7
      dec v_akkupwm       ; pwm bischen verringern
8
      jmp sichern
9
      
10
up:      
11
      inc v_akkupwm       ; pwm bischen vergrößern
12
13
sichern:
14
      out ocr0, v_akkupwm ; und externe PWM setzen
15
doReturn:
16
      ret

von Dietrich L. (dietrichl)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Hat ja sowieso keinen Zweck. Mit Hinweisen kommen wir nicht weiter

Da hast Du leider recht! Der Wirkungsgrad diese Threads bewegt sich 
maximal im einstelligen %-Bereich. Ich wollte ja auch schon aufgeben, 
aber dann überkam es mich nochmal...

Gruß Dietrich

von Karl H. (kbuchegg)


Lesenswert?

Jetzt kann nur noch das dec und das inc vertauscht sein.
Je nachdem wie die PWM sich auf die Spannung auswirkt.

von werner (Gast)


Angehängte Dateien:

Lesenswert?

Hallo Dietrich,

ich hab mir jetzt deinen Wink mit dem Gartenzaun ein paar mal 
durchgelesen,
aber ich raffs einfach nicht.
Der Wert v_akkupwm ist doch gleich dem Wert vakkupwm(sram)...

ich glaub jetzt hab ichs , ich lade ja immer wieder den selben akkuwert 
aus v_akku in v_akkupwm, also wird der ja nie kleiner oder 
größer...richtig?

Gruß Werner

von Karl H. (kbuchegg)


Lesenswert?

werner schrieb:

> ich glaub jetzt hab ichs , ich lade ja immer wieder den selben akkuwert
> aus v_akku in v_akkupwm, also wird der ja nie kleiner oder
> größer...richtig?

Und der Wert in v_akku ist was?

Das ist dein Messwert vom ADC.

Also hast du letztendlich einfach nur den Messwert vom ADC auf die PWM 
gegeben. Ein bischen verschleiert, aber darauf läuft es hinaus.

Das ist aber genau nicht das was du willst.
Du willst:


  PWM Wetr sei 0
  dein Vorgabewert für die zu erzeugende Spannung sei 45

jetzt läuft dein Programm los.
Der ADC misst die Spannung und stellt fest: 0

Jetzt kommt die Logik zum Zug. Die stellt fest: zu klein

Also wird die PWM ein bischen erhöht: Neuer Wert für die PWM -> 1

Also Folge davon erzeugt deine PWM eine etwas höhere SPannung. Keine 
Ahnung, 0.2V

wieder misst der ADC die externe Spannung und kommt mit einem Messwert 
hoch: 10

immer noch zu klein. Also wird der PWM Wert wieder um 1 erhöht. Die PWM 
erzeugt jetzt wieder eine etwas höhere Spannung: 0.3V

Nächster Durchlauf durch die Schleife.

Der ADC misst die Spannung und kommt mit einem Wert an: 13

13 ist immer noch kleiner als die Vorgabe von 45

Also wird der Wert für die PWM wieder um 1 erhöht.

usw. usw. Das ganze Spiel läuft so lange, bis durch immer weitere 
Schleifendurchläufe in main der PWM Wert solange in kleinen Schritten um 
1 erhöht wurde, bis die PWM eine Spannung erzeugt, die genau so groß 
ist, dass der ADC einen Wert von 45 misst. Dann hört das Erhöhen auf.

Jetzt belastest du von aussen die Spannung etwas. Als Folge davon bricht 
sie ein. Der ADC misst nur noch 43. Für deine Auswahllogik bedeutet 
dass, das der Messwert kleiner als die Vorgabe geworden ist, also wird 
die PWM um 1 erhöht. Die von der PWM erzeugte Spannung wird etwas 
angehoben. Die Regelung hat begonnen, den Abfall auszugleichen, indem 
der PWM Wert höher wird.

Und auch umgekehrt. Du nimmst die externe Last wieder weg. Die externe 
Spannung steigt wieder. Der ADC misst 46. Die Ausgleichslogik stellt 
fest: Das ist zu viel! Also wird der PWM Wert um 1 erniedrigt. Als Folge 
davon sinkt die Spannung die die PWM erzeugt.


Merkst du was: Der PWM Wert wird NIE zugewiesen sondern immer nur 
entweder um 1 erhöht oder um 1 erniedrigt (oder es passiert gar nichts). 
Was der PWM Wert macht (machen soll), hängt davon ab, ob das was der ADC 
misst kleiner oder größer als die Vorgabe ist.


Und jetzt regelst du und stellst nicht nur!


Du machst doch mit deinem Auto genau das gleiche. Wenn du 50 fahren 
sollst und du merkst du bist zu langsam, dann steigst du ein bischen 
mehr aufs Gas, bis es passt. Bist du umgekehrt zu schnell, dann lässt du 
das Pedal etwas rauskommen, bis es wieder passt. Aber wo das Pedal genau 
steht - das interesiert dich nicht die Bohne. Du korregierst einfach nur 
die Pedalstellung abhängig davon ob du zu schell oder zu langsam bist.

von werner (Gast)


Angehängte Dateien:

Lesenswert?

Hallo,

ich möchte mich recht herzlich bei allen die zum gelingen beigetragen 
haben bedanken, besonderen Dank möchte ich hiermit Dietrich und Karl 
Heinz aussprechen die bis zum Schluß an die Intelligenz im Menschen 
geglaubt haben.
Anbei der bereinigte Code.
So da das ja so gut geklappt hat können wir nun mit Teil 2 der 
mehrteiligen Sendereihe fortfahren ;-)).
Ich will ja noch den Ertrag und Verbrauch ermitteln. Das funktioniert ja 
nicht ohne eine Zeitmessung, ist es zwingend dafür eine Uhr zu benutzen 
oder hat jemand noch ne andere Idee?

Gruß Werner

von Karl H. (kbuchegg)


Lesenswert?

werner schrieb:

> So da das ja so gut geklappt hat können wir nun mit Teil 2 der
> mehrteiligen Sendereihe fortfahren ;-)).
> Ich will ja noch den Ertrag und Verbrauch ermitteln. Das funktioniert ja
> nicht ohne eine Zeitmessung, ist es zwingend dafür eine Uhr zu benutzen
> oder hat jemand noch ne andere Idee?


Da muss ich dich enttäuschen. Das tu ich mir in Assembler sicher nicht 
mehr an. Da schreib ich lieber den 3 Zeiler in C, lass den Compiler sich 
den Kopf zerbrechen, wie er die Berechnungen umsetzt und geh heut Abend 
lieber auf ein Bier, während der Mega8 fleissig mAh zählt und auf dem 
LCD (das du auch noch nicht hast) ausgibt. Und auf die 5% Performance 
Verlust durch den C-Compiler ist gesch.....

von Dietrich L. (dietrichl)


Lesenswert?

werner schrieb:
> Anbei der bereinigte Code.

... wobei Du "Regelung" immer noch nicht kapiert hast und Karl Heinz's 
Vorschlag vom 15.02.2012 19:38 ignoriert hast ...

Ich gebe es auf.

und Tschüss Dietrich

von Karl H. (kbuchegg)


Lesenswert?

Dietrich L. schrieb:
> werner schrieb:
>> Anbei der bereinigte Code.
>
> ... wobei Du "Regelung" immer noch nicht kapiert hast und Karl Heinz's
> Vorschlag vom 15.02.2012 19:38 ignoriert hast ...

Hab noch gar nicht nachgesehen. Ich geh ja doch davon aus ...

< Gleich mal ansehen. >

Jup. Wo ist die nächste Tischkante?

von werner (Gast)


Lesenswert?

Hallo Dietrich und Karl Heinz,

das ist mir aber jetzt unangenehm. Ich hab das gestern (19.22 und 19.38) 
nicht mehr gelesen und heute früh einfach geantwortet.

Sorry.

Karl Heinz, ich hatte befürchtet das du sowas schreibst:

"geh heut Abend
lieber auf ein Bier, während der Mega8 fleissig mAh zählt und auf dem
LCD (das du auch noch nicht hast) ausgibt. Und auf die 5% Performance
Verlust durch den C-Compiler ist gesch....."

nicht das ich dir das Bier nicht gönne, sonder das es in C wohl etwas 
einfacher zu machen sein soll, aber davon habe ich ja nun überhaupt 
keine Ahnung.
Vielen Dank nochmal euch beiden, auch wenn es Streckenweise nicht 
einfach mit mir war. Ich probiere das jetzt gleich mal aus.

Gruß Werner

von Karl H. (kbuchegg)


Lesenswert?

werner schrieb:

> nicht das ich dir das Bier nicht gönne, sonder das es in C wohl etwas
> einfacher zu machen sein soll, aber davon habe ich ja nun überhaupt
> keine Ahnung.

Das hab ich schon befürchtet, das du sowas schreibst.
Der springende Punkt ist: Du hast ja auch von Assembler eigentlich keine 
Ahnung.


Vergleich mal deine Assembler Lösung mit
1
#include <avr/io.h>
2
#include <util/delay.h>
3
4
uint8_t AkkuSpannung;
5
uint8_t SollWert;
6
uint8_t PWMWert;
7
8
int main()
9
{
10
  SollWert = 139;
11
12
  DDRB = ( 1 << PB3 );      // PORTB, 3  auf Ausgang, das ist OC0
13
14
  ADCSRA = (1<<ADEN)|(1<<ADPS2)|(1<<ADPS1);
15
  ADMUX  = (1<<REFS0)|(1<<ADLAR);              // AVcc,linksbuendig,pa0
16
17
  TCCR0 = 0b01101010;  // fast pwm, prescaler 8, lösche oc0 bei gleichstand
18
19
  while( 1 == 1 ) {
20
21
    //
22
    // ADC auslesen
23
    // wegen ADLAR interessiert nur ADCH
24
    //
25
    ADCSRA |= (1<<ADSC);
26
    while( ADCSRA & (1<<ADSC) )
27
      ;
28
    AkkuSpannung = ADCH;
29
30
    //
31
    // und PWM gegebenenfalls nachstellen
32
    //
33
    if( AkkuSpannung < SollWert )
34
      PWMWert = PWMWert + 1;
35
36
    else if( AkkuSpannung > SollWert )
37
      PWMWert = PWMWert - 1;
38
39
    OCR0 = PWMWert;  
40
41
    //
42
    // damit sich die Spannung am PWM Pin einstellen kann:
43
    // warte 5 Millisekunden, das sollte reichen.
44
    _delay_ms( 5 );
45
  }
46
}

Es findet sich alles wieder. Die Hardware-Register haben genau die 
gleichen Namen, nur dass man nicht mit Zwischenregistern rummachen muss. 
Um so Dinge wie Interrupt Vektoren oder Stack Initialisierung muss ich 
mich überhaupt nicht kümmern.
Und dann sind halt noch eine handvoll C-Schlüsselwörter, deren Bedeutung 
man kennen muss. Die sind aber nicht so schwer

   if( Bedingung )
     mach was;

if übersetzt sich zu:   Wenn
else übersetzt sich zu: anderfalls

< heißt wie in der MAthematik kleiner, > ist größer.
== ist Vergleich auf Gleichheit und != ist Vergleich auf ungleich.


   while( Bedingung )
   {
     mach was
   }

while übersetzt sich: solange wie.
Etwas wird also wiederholt, so lange wie eine bestimmte Bedingung 
erfüllt ist.

Ich wette, dass du dieses Programm ziemlich problemlos lesen kannst. 
(OK, über | bzw. & müsste man noch reden, aber für den Moment ist das 
geschenkt)

Das heißt jetzt um Gottes Willen NICHT; dass ich dir Assembler ausreden 
will. Ich will dir nur zeigen, dass es auch andere Dinge gibt, bei denen 
man sich nicht um die vielen, vielen kleinen Details kümmern muss. Wenn 
ich nur daran denke, wie lange es gedauert hat, bis deine Interrupt 
Vektor Tabelle richtig war .....

von oldmax (Gast)


Lesenswert?

Hi
Es juckt halt in den Fingern.... denn das war der erste Teil...?!
Werner, tu dir selbst einen Gefallen und verstehe erst mal das Wort 
"Programmieren". Du hast doch nach eigenen Aussagen Vorkenntnisse , wenn 
auch mit Relaissteuerungen. Das ist nix anderes, nur statt Befehl halt 
Draht.
Wenn du ein wenig Zeit übrig hast, bei AVR-Praxis steht unter FAQ ein 
Beitrag "keine Angst vor Assembler". Vielleicht tust du dir den erst mal 
an, bevor du hier wieder versuchst, einen Megathread anzuleiern. Denk 
dran, Tischkanten sind auch nicht endlos und ich hätte an KHB's Stelle 
mindestens alle 10cm reingebissen...
Ich will nicht sagen, das es zwecklos ist, aber du mußt dir endlich 
angewöhnen, die gebotene Hilfe zu lesen, geistig zu verstehen und dann 
einzusetzen und nicht einfach die Schnipsel zu nehmen und irgendwo in 
deinen vermurksten Code einzubauen. Du hast ja versucht, die Hinweise 
umzusetzen, aber immer nur soweit, wie sie dir vorgekaut wurden. Sobald 
eigene Befehle dazukamen, war's wieder zum Haare raufen. Als Dozent von 
dir wär ich vermutlich nach Feierabend mit einem Moralischen völlig 
versackt. Du willst doch programmieren... dann reiß dich zusammen, ordne 
deine Gedanken und wenn ich mich hier wiederhole: Nimm Papier und 
Bleistift dazu. Halte fest, welches Register, welche Variable wofür 
eingesetzt ist und nimm dieses Blatt dann beim Programmieren zu Hilfe. 
Mal dir die Funktionen deines Programmes als Blackbox auf und verbinde 
sie in der Reihenfolge, die logisch ist. Dann öffne deine schwarzen 
Kisten und befüll sie mit, wie du so schön gesagt hast, "Intelligenz"
Das ist vom Programmgerüst her relativ einfach. Jede Blackbox steht für 
ein Unterprogramm. Und da du die Funktionalität nach und nach 
einpflegst, steht erst mal nur der Name (die Marke) und ein RET
1
;--------------- Einlesen der Analogwerte --------------
2
;*******************************************************
3
;* Diese Routine liest die Werte für "Soll" und "Ist"  *
4
;* in in den Variablen "Sollwert" und "Istwert"        *
5
;*******************************************************
6
7
Lese_Analog:            ; eigenes Unterprogramm
8
                        ;Code kommt später
9
RET
10
11
Lese_Digital:           ; eigenes Unterprogramm
12
                        ; Code kommt später
13
RET
14
15
Regler:                 ; Unterprogramm Regler
16
  RCALL auf             ; regel hoch
17
  RCALL ab              ; regel runter
18
                        ; weitere Schritte im Regler
19
RET
20
21
;--------------- Regelbaustein -------------------------
22
;*******************************************************
23
;* Die Soll-und Istwerte sind durch das Unterprogramm  *
24
;* "Lese_Analog" in die Variablen "Sollwert" und       *
25
;* "Istwert" eingetragen worden. Diese Werte werden    *
26
;* jetzt in die Register 16 und 17 für den Vergleich   *
27
;* eingelesen.                                         *
28
;*******************************************************
29
Auf:                    ; mach die Abfrage hier, ob 
30
                        ; hochgeregelt werden muß
31
  LDS  R16, Istwert
32
  LDS  R17, Sollwert
33
  CP   R16, R17
34
  BRLO Regeln_Auf       ; Istwert ist kleiner Sollwert
35
  RJMP End_Auf
36
Regeln_Auf:
37
                        ; hier kommt der Code für Regeln auf hin
38
End_Auf:
39
RET

Zugegeben, es ist jetzt nicht unbedingt die beste Lösung, aber bevor du 
dich an große Programmblöcke wagst, ist der Weg der kleinen Schritte 
besser. Außerdem kannst du dein Programm auch optisch mit 
Kommentarzeilen aufbessern, wie ich es bei der Eingabe und beim Regler 
angedeutet habe. So ist ein Programm hinterher prima lesbar. So kannst 
du auch beispielsweise Rückgabewerte definieren.
Der Vorteil dieser Methode ist, das du dir das komplette Programmgerüst 
aufbauen und zwischendurch auch den Fortschritt testen kannst. Zur Not, 
indem du Rückgabewerte erst mal manuell reinsetzt. Dann hast du 
Anhaltspunkte für die Suche in Tutorials, wie etwas gelöst werden muß 
Bei den Initialisierungen bist du ja auch den Weg gegangen. Also, setz 
dich erst mal hin, trink dir einen Tee oder auch was anderes und 
versuche, deine Gedanken zu ordnen. Dann mach eine Bestandsaufnahme: Was 
hast du, was kannst du.  Das gilt für die Hard- und Software 
gleichermaßen. Dann erstell deinen Ablaufplan. Spätestens hier merkst 
du, wo deine Gedanken noch wirr sind. Daher, wieder tief durchatmen und 
daran denken "es ist kein Zauberwerk".
Es wird schon....
Gruß oldmax

von spess53 (Gast)


Lesenswert?

Hi

>Vergleich mal deine Assembler Lösung mit

Na so viel aufwendiger ist eine Assemblerlösung auch nicht. Das 
Rumgehüpfe kann man sich sparen:
1
RESET:      ldi r16,high(RAMEND)
2
            out SPH,r16
3
            ldi r16,Low(RAMEND)
4
            out SPL,r16
5
6
            ;************* AD_CONVERTER *****************
7
            ldi r16,1<<REFS0|1<<ADLAR
8
            out ADMUX,r16
9
10
            ldi r16,1<<ADEN|1<<ADPS2|1<<ADPS1
11
            out ADCSRA,r16
12
13
            clr r16
14
            mov r2,r16            ; Null
15
            mov r14,r16           ; PWM-Wert Start
16
17
            ldi r16, 139          ; Sollwert
18
            mov r3,r16
19
20
mainloop:   sbi ADCSRA,ADSC
21
22
loop:       sbis ADCSRA,ADSC
23
            rjmp loop
24
25
            in r15,ADCH           ; ADC-Wert
26
27
            cp r15,r3             ; ADC-Wert-Sollwert
28
            breq mainloop         ; Wenn gleich->fertig
29
30
            adc r14,r2            ; wenn ADC-Wert kleiner->PWM+1
31
32
            cp r3,r15             ; Sollwert-ADC-Wert
33
            sbc r14,r2            ; wenn ADC-Wert größer->PWM-1
34
35
            out OCR0, r14
36
            jmp mainloop

MfG Spess

von werner (Gast)


Angehängte Dateien:

Lesenswert?

Hallo Ihr Drei,

der C-Code sieht ja eigentlich sehr übersichtlich aus. Ich vermute mal 
in der io.h steht die IVT oder so was ähnliches und die delay.h sagt ja 
schon der Name wird die Zeitschleife sein. Ein Freund von mir hatte mich 
auch schon angesprochen warum ich das nicht in C mache. Hab nun mal mit 
Assembler angefangen, aber es wäre ein Versuch Wert, doch erst wenn 
hiermit fertig bin.
Die Aufteilung in Unterprogramme hab ich ja schon weitestgehend gemacht,
ich hab halt die Auswahl/Hoch/Runter entscheidung zusammengelassen weil 
es für mich übersichtlicher war.
Den Code von Karl Heinz
auswahl:
      cp v_store, v_akku  ; vergleiche messwert mit sollwert
      breq doReturn       ; gleich? dann gibts nichts zu tun
      brsh up             ; zu groß?
                          ; -> nicht zu groß, also muss es zu klein sein
down:
      dec v_akkupwm       ; pwm bischen verringern
      jmp sichern

up:
      inc v_akkupwm       ; pwm bischen vergrößern

sichern:
      out ocr0, v_akkupwm ; und externe PWM setzen
doReturn:
      ret

hab ich so ins Programm übernommen, aber jetzt regelt er die Spannung 
ständig hoch und runter, auf der PWM sieht das aus als wenn ständig 
durchgezählt wird.
Ich hab Spess seinen Code mal ins Programm integriert und zeigt sich 
auch keine Raktion auf Spannungsschwankungen, man sieht da ein ganz 
seltsames Verhalten der PWM , es ist ein ständiger Wechsel von großem 
und kleinem Duty Cycle zu sehen.
Ich hab mal das Programm mir Karl Heinz seinem Code von gestern Abend 
und meine weiterentwicklung mit RS232+BCD Umwandlung mit drangehangen.
Es ist schon seltsam, wenn man dem Prorammablauf folgt, auf die 
Auswahlund das Hoch und Runterzählen bezogen, dann müßte es doch 
eigentlich funktionieren. Ich hab schon am Wandlereingang ein kleinen C 
drangehangen um event. Schwankungen auszugleichen, aber nix.
Wenn ich den AD-Eingang abziehe ist aber immer eine Reaktion zu sehen, 
also funktioniert der ja.

Gruß Werner

von werner (Gast)


Angehängte Dateien:

Lesenswert?

Huch wo ist denn das 2.Programm. Hier noch das andere Programm.

Gruß Werner

von Karl H. (kbuchegg)


Lesenswert?

werner schrieb:

> hab ich so ins Programm übernommen, aber jetzt regelt er die Spannung
> ständig hoch und runter, auf der PWM sieht das aus als wenn ständig
> durchgezählt wird.

Klar. Weil keine Überprüfung eingebaut ist, ob der PWM Wert überhaupt 
weiter erhöht werden darf. Wenn er 255 erreicht hat, kann er nicht mehr 
erhöht werden. Mehr als 255 geht nicht. Wenn du allerdings zu 255 noch 1 
dazuzählst, dann landest du wieder bei 0. Und geneu das passiert. Der 
Regler möchte gerne den PWM Wert weiter erhöhen, nur wenn er das tut 
macht er aus 255 eben nicht 256 sondern 0.

Bei eine entsprechende Sicherung ein (ebenso in der anderen Richtung, 
kleiner als 0 geht nicht).

Wie sieht eigentlich deine Aussenbeschaltung aus?

von werner (Gast)


Lesenswert?

Hallo Karl Heinz,

ok mache ich , meine Aussenbeschaltung steht ganz oben im ersten 
Beitrag, ich bin aber immer noch nicht ganz zufrieden mit der P-Fet 
Ansteuerung.
Uds ist immer noch zu groß, da verbrate ich nur Energie.
Hab schon mit verschiedenen Varianten experimentiert.

Gruß Werner

von werner (Gast)


Angehängte Dateien:

Lesenswert?

Hallo

ich weiß, ich habe mich lange nicht gemeldet, aber ich wollte erst meine 
Hausaufgaben machen.
Es funktioniert...das ist so geil, vielen vielen Dank allen die mir 
geholfen
haben und besonders KH, spess53 und oldmax die trotz allem durchgehalten 
haben. Ich habe mich die letzten Tage mit der Ansteuerung des P-Fets und 
dem Problem des großem Uds angenommen. Ein Freund hat mir da auch 
hilfreich zur Seite gestanden. Die angehängte Schaltung habe ich so 
ähnlich auch schon versucht, aber mich nie getraut RE1+2 so klein werden 
zu lassen...war ein Fehler...wieder was gelernt!
Ich habe mit beiden Widerständen noch etwas experimentiert und für meine 
Schaltung ist das wie auf dem Bild optimal. Das Programm hab ich auch 
nochmal dran gehangen mit dem ich die Schaltung ausprobiert habe, 
vielleicht hilft es anderen weiter.

Gruß Werner

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.