www.mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik Zeitmangel BASCOM nach ASSEMBLER


Autor: Werner (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo zusammen,

ich habe dieses BASCOM Code-Schippsel in einer Interrupt Routine.

Up:

cbi ddrb,1          ' disconnect OC1A
cbi ddrb,2          ' disconnect OC1B

If Portb.0 = 0 Then
   If Pwm1a > 0 Then
      Decr Pwm1a
      Pwm1b = Pwm1a
   End If
Else
   If Pwm1a < 1100 Then
      Incr Pwm1a
      Pwm1b = Pwm1a
   End If
End If

Return

Es geht soweit, nur ich würde gerne etwas schneller werden.

In BASCOM werden alle Register ge-pushed und ge-popt.
Das verbraucht Zeit.

Kann mir ein ASSEMBLER Spezi etwas aus dem Ärmel schütteln, dass
abhängig von PORTB.0 OCR1A und OCR1B um eins erhöht oder erniedrigt
ohne Null zu unterschreiten bzw. 1100 zu überschreiten.

oder , welche Register muss ich Pushen-Popen um NOSAVE benutzen zu 
können.


Danke schon 'mal.

Werner

Autor: Stefan B. (stefan) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Das liegt in der Natur einer Interruptroutine. BASCOM muss hier alle 
Register retten, die im Anwendungsprogramm benutzt werden könnten. Also 
im Prinzip alle.

Wenn du einige Push/Pop sparen willst, musst du mit dem Läusekamm dein 
Disassemblerlistung untersuchen und eine Registerbenutzungstabelle 
machen. Dann siehst du, welche Register nicht benutzt werden. Die 
Prozedur ist bei jeder Sourcecodeänderung und jedem Compilerupdate zu 
wiederholen.

Am Disassmblerlisting selbst siehst du aber auch, wie BASCOM den 
Abschnitt in Assembler umgesetzt hat. Vielleicht (ohne viel Hoffnung) 
sieht man, andere Punkte für eine Handoptimierung.

Wobei stört dich denn die momentane Geschwindigkeit der Routine? 
Vielleicht ist der Bremshebel ja woanders angezogen.

Autor: Werner (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Stefan,

das ist in einem push-pull DC-DC Wandler.
Ich habe eine Weile mit Regelschwingungen ( Unsymetrien ) gekämpft.
Jetzt habe ich einen Code der recht gut geht...
nur ab 3 kHz stolpert er aus Zeitmangel.
Ich berechne OCRnx wenn OC1A und OC1B aus sind , will aber bis nahe 50%
duty cycle, dann bleibt wenig Zeit.

OC1A und OC1B laufen in FastPulseWithModulation.
Das Programm sucht eine Pulsbreite bei der am Ende des Zyklus
U ist (wieder) < U soll
und eine Pulsbreite an deren Ende
U ist (möglichst) = U soll ist.

Mikro ATmega8 , 7,238--- MHz

Grüße
Werner

Autor: Spess53 (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi

>Kann mir ein ASSEMBLER Spezi etwas aus dem Ärmel schütteln,

Nein. Das Problem ist nicht der Code, sondern die 'Verwaltung' der 
Variablen unter BASCOM.

Wozu soll das:

>cbi ddrb,1          ' disconnect OC1A
>cbi ddrb,2          ' disconnect OC1B

gut sein? Das OC-Register wird erst beim Overflow gesetzt.

MfG Spess

Autor: Andi (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
such nach NOSAVE in der Anleitung

Autor: Ulirch (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
BASCOM ist ziemlich einheitlich in der Codeerzeugung. Wenn man einmal 
weiss welche Register wirklich gebraucht werden, wird sich daran normal 
nichts mehr ändern.  Wenn man ganz sicher gehen will, kann man auch den 
erzeugten Code dann als inline ASM einfügen - ggf. kann man da auch noch 
was Optimieren.

Autor: Werner (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Spess53 schrieb:
>>cbi ddrb,1          ' disconnect OC1A
>>cbi ddrb,2          ' disconnect OC1B
>
>Wozu soll das  gut sein? Das OC-Register wird erst beim Overflow gesetzt.

OC1A und OC1B werden alternierend mit einem Zweig des push-pull 
Inverters verbunden. "cbi ddrb,x"  macht beide definitiv aus um den 
"glitch" bei "Ovf1" zu vermeiden. "DDRB,1 und DDRB,2" schalten ein und 
aus.
"Ein" eben nur wenn U ist < U soll und dann 1-2-3 CPU Takte nach "Ovf1"

An AIN1 hängt ein Komparator.
Bezug ist die interne "Bandgap"
ACI triggert wenn U ist > U soll wird.

...
Config Aci = On , Compare = Off , Trigger = Falling
Acsr.6 = 1
' select internal reference
...
On Ovf1 Top Nosave
On Oc1b Up
On Aci Reached Nosave
Enable Aci
Enable Ovf1
Enable Oc1b

...
Up:

cbi ddrb,1          ' disconnect OC1A
cbi ddrb,2          ' disconnect OC1B

If Portb.0 = 0 Then
   If Pwm1a > 0 Then
      Decr Pwm1a
      Pwm1b = Pwm1a
   End If
Else
   If Pwm1a < 1100 Then
      Incr Pwm1a
      Pwm1b = Pwm1a
   End If
End If

Return

Reached:

sbic ddrb,2         ' B still on
   cbi portb,0      ' increase PWM
sbic ddrb,1         ' A still on
   cbi portb,0      ' increase PWM
cbi ddrb,2          ' voltage has turned high , disconnect OC1B
cbi ddrb,1          ' voltage has turned high , disconnect OC1A

Return


Top:

push r24
in r24,acsr
sbrs r24,5          ' voltage is still high at the end of cycle ?
   cbi portb,0      ' yes , reduce PWM
sbrc r24,5
   sbi portb,0      ' no , increase PWM

sbis portd,4        ' this is A ?
   Rjmp Channela

Channelb:

Cbi portd,4         ' toggle flag A-B
sbi acsr,4          ' reset comparator interrupt
sbrc r24,5
   sbi ddrb,2       ' connect B if voltage is low

pop r24
reti

Channela:

sbi portd,4         ' toggle flag B-A
sbi acsr,4          ' reset comparator interrupt
Sbrc R24 , 5
   sbi ddrb,1       ' connect A if voltage is low

pop r24

reti

Return


Was ein Sch.. das Denglish.. nur für Eingeweihte :0)

Autor: Spess53 (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi

>Was ein Sch.. das Denglish.. nur für Eingeweihte :0)

Richtig. Was sollen diese Codeschnipsel?

MfG Spess

Autor: Werner (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
nun Du hast gefragt, warum ich beide Kanäle ausschalte, bevor Ovf1 
geschieht.
Mit weniger kann ich das nicht erklären...

ich wollte auch nur fragen wie ich OCRnx um eins erhöhen oder 
erniedrigen kann. Abhängig von PORTB.0 ohne Null zu unterschreiten und 
1100 zu überschreiten. In einer Interruptroutine (OCR1B) in möglichst 
kurzer Zeit.

mfg Werner

Autor: Spess53 (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi

>nun Du hast gefragt, warum ich beide Kanäle ausschalte, bevor Ovf1
>geschieht.
>Mit weniger kann ich das nicht erklären...

Und was soll das sein? Eigener Code, Disassembliertes BASCOM,...? Aus 
irgend einem Zusammenhang gerissen macht es keinen Sinn.

MfG Spess

Autor: Werner (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
das ist eigener, funktionierender BASCOM Code.

Alles was mein Teil braucht um einen DC-DC Wandler von 12 auf 0 - 400 
Volt
bis zu 50 Watt / max ~ 400mA zu steuern.
Ich bin etwas älter, 7-8-10 kHz nerven akustisch nicht mehr so.
Bis 2-3 kHz geht es ja, nervt aber.

U soll kommt aus einem 16 bit DA Wandler ( AD 7303 )
U ist aus einem Spannungsteiler am Ausgang des DC-DC Wandlers.
U delta aus einem Komparator ( AD 648 ) und geht an AIN1.

Mfg Werner

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
So recht will ich noch nicht glauben, dass eine Assembler Variante der 
ISR da irgendwas ändern würde.
Wenn du die Regelung nicht in den Griff kriegst, dann wird es eher daran 
liegen, dass deine 2-Punkt Regelung bei höheren Frequenzen nicht mehr 
mitkommt und überschwingt.

Mal etwas rechnen.
Bei 16Mhz Systemtakt, hast du von einem ISR Aufruf zum nächsten
16000000 / 3000 = ~5300 Takte
Zeit. 5300 Takte. Das ist massig. So schlimm kann BASCOM gar nicht sein.

Autor: Oilaf (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,
ich habe jetzt den Text nur überflogen und kann mir jetzt gerade noch 
nicht vorstellen was nun genau geregelt werden soll.
Ich schreibe aber auch gern mal zeitkritische Interruptroutine in 
Assembler und sichere bei "Nosave" nur die Register, die ich in der 
Interruptroutine auch benutze. Ich glaube schon, dass man auf diese 
Weise schnellere Interruptroutine bekommt. Der Befehl mit dem man die 
Adresse einer Variablen im Ram feststellen kann (welcher war das noch?) 
ist da sehr hilfreich.

Gruß Oilaf

Autor: MWS (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Karl heinz Buchegger schrieb:
> Bei 16Mhz Systemtakt, hast du von einem ISR Aufruf zum nächsten
> 16000000 / 3000 = ~5300 Takte

Das Sichern und Wiederherstellen des Registersatzes dauert ~110 Takte, 
die Up-ISR benötigt insgesamt nicht mehr als 200 Takte.

Aber im Prinzip macht's keinen Spaß aufgrund des Fetzerlcodes 
Hilfestellung zu geben, denn ein paar Dinge sind nur zu erraten.

Die Lösung für die Up-ISR wäre sehr einfach, denn das einzig 
Zeitkritische dort scheint das Löschen der DDR-Bits zu sein. Die 
Änderungen der Outputcompare-Register scheinen weniger zeitkritisch, 
denn sie werden aufgrund des HW-PWM Doublebufferings erst beim nächsten 
Timerzyklus bei TOP oder BOTTOM  übernommen.

Wenn man also das Löschen der Bits zuerst ausführen will, dann 
deklariert man Up-ISR mit Nosave, führt darin zuerst die CBI's aus und 
umschließt den Basic-Block mit PUSHALL/POPALL.

Evtl. könnte man nach den CBI's noch die Interrupts freigeben, um der 
ACI-ISR Vorrang zu verschaffen.

Mit der Informationsbereitstellung des TO's ist das alles schwierig zu 
beurteilen. Nicht einmal der µC-Typ ist genannt, ich hab' da keine Lust 
mal eben ein wenig Assembler zu schreiben.

Vor allem wenn's sinnlos ist, weil's woanders klemmt.

Autor: Werner (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo und danke,

es ist ein ATmega8 mit 7,3728 MHz.
Timer1 läuft mit Prescaler = 1.
Timer1 macht FPWM Mode 14.

Im Mode 14 bestimmt ICR1 das TOP und OCRnx wird bei TOP ( = BOTTOM 
)gesetzt.

"Up" und "Reached" haben in PORTB.0 hinterlassen was zu tun ist.

Ich brauche kaum mehr als 100 Takte "dead time" zwischen den Kanälen
des "push-pull".
Das Geplänkel bei TOP verbraucht bereits ein paar, beide Kanäle müssen 
aus
sein um den "Glitsch" bei OCRnx = 0 zu unterdrücken.
Ein Kanal soll auch nicht losgehen wenn Uist bereits > Usoll.

Zeitkritisch ist die Dauer von "Up".
Das Löschen der DDRB.1 und DDRB.2 ist nicht kritisch, es muss lediglich 
vor TOP geschehen.

Dauert "Up" zulange wird der neue Wert für OCRnx nicht mehr rechtzeitig
vor TOP ( ~ TOV1 ~ BOTTOM , mode 14 Sägezahn ) fertig.
Desto länger "Up" dauert um so mehr nutzbare Pulsbreite muss ich 
verschenken
( deshalb diese Notbremse: PWM1A < 1100 im Beispiel, ICR1 habe ich 
empirisch erniedrigt bis
es stolpert )
PWMmax = ICR1  minus   nTakteInUp
ICR1 wird bei höherer Frequenz immer kleiner, die TakteInUp immer 
gleich.
Das beschränkt die entnehmbare Leistung unnötig.

mfg Werner

Autor: MWS (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Bitte sehr, nichts zu danken :D
Laufzeit Routine <= 45 Takte, zzgl. ISR JMP & RETI 7 Takte
Const PWM_Upper = 1100

Up:
   !cbi     ddrb,     1
   !cbi     ddrb,     2
   !PUSH    R16
   !IN      R16,      SREG
   !PUSH    R16
   !PUSH    R24
   !PUSH    R25
   !IN      R24,      {PWM1A}
   !IN      R25,      {PWM1A +1}
   !SBIC    PortB,    0
   !RJMP    Incr_PWM
!Decr_PWM:
   !SBIW    R24,      1
   !BRMI    Up_End
   !OUT     {PWM1A}, R24
   !OUT     {PWM1A +1}, R25
   !OUT     {PWM1B}, R24
   !OUT     {PWM1B +1}, R25
   !RJMP    Up_End
!Incr_PWM:
   !ADIW    R24,      1
   !LDI     R16,      lbyte(PWM_Upper)
   !CP      R16,      R24
   !LDI     R16,      hbyte(PWM_Upper)
   !CPC     R16,      R25
   !BRCS    Up_End
   !OUT     {PWM1A}, R24
   !OUT     {PWM1A +1}, R25
   !OUT     {PWM1B}, R24
   !OUT     {PWM1B +1}, R25
!Up_End:
   !POP     R25
   !POP     R24
   !POP     R16
   !OUT    SREG,     R16
   !POP     R16
Return

Autor: Werner (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Doch Danke :0)

das habe ich gesucht...
Ich bin ein rechter Assembler dummy, hätte lange gedauert, wenn ich es
überhaupz hinbekäme...

Bis Donnerstag hänge ich auf Dienstreise, dann werde ich es probieren,

Nochmals vielen Dank
Werner

Autor: Werner (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@ MWS:

das gab einen rasanten Zeitgewinn.

Es geht nicht gleich, das highbyte muss zuerst geschrieben werden.
Dann klappt alles.
   !OUT     {PWM1A +1}, R25
   !OUT     {PWM1A}, R24
   !OUT     {PWM1B +1}, R25
   !OUT     {PWM1B}, R24

nochmal danke

Autor: MWS (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Werner schrieb:
> @ MWS:
>
> das gab einen rasanten Zeitgewinn.
>
> Es geht nicht gleich, das highbyte muss zuerst geschrieben werden.

Ein Klassiker, hab ich doch glatt übersehen ;-)

> nochmal danke

Bitte.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.