Forum: Mikrocontroller und Digitale Elektronik Zeitmangel BASCOM nach ASSEMBLER


von Werner (Gast)


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

von Stefan B. (stefan) Benutzerseite


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.

von Werner (Gast)


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

von Spess53 (Gast)


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

von Andi (Gast)


Lesenswert?

such nach NOSAVE in der Anleitung

von Ulirch (Gast)


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.

von Werner (Gast)


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)

von Spess53 (Gast)


Lesenswert?

Hi

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

Richtig. Was sollen diese Codeschnipsel?

MfG Spess

von Werner (Gast)


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

von Spess53 (Gast)


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

von Werner (Gast)


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

von Karl H. (kbuchegg)


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.

von Oilaf (Gast)


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

von MWS (Gast)


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.

von Werner (Gast)


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

von MWS (Gast)


Lesenswert?

Bitte sehr, nichts zu danken :D
Laufzeit Routine <= 45 Takte, zzgl. ISR JMP & RETI 7 Takte
1
Const PWM_Upper = 1100
2
3
Up:
4
   !cbi     ddrb,     1
5
   !cbi     ddrb,     2
6
   !PUSH    R16
7
   !IN      R16,      SREG
8
   !PUSH    R16
9
   !PUSH    R24
10
   !PUSH    R25
11
   !IN      R24,      {PWM1A}
12
   !IN      R25,      {PWM1A +1}
13
   !SBIC    PortB,    0
14
   !RJMP    Incr_PWM
15
!Decr_PWM:
16
   !SBIW    R24,      1
17
   !BRMI    Up_End
18
   !OUT     {PWM1A}, R24
19
   !OUT     {PWM1A +1}, R25
20
   !OUT     {PWM1B}, R24
21
   !OUT     {PWM1B +1}, R25
22
   !RJMP    Up_End
23
!Incr_PWM:
24
   !ADIW    R24,      1
25
   !LDI     R16,      lbyte(PWM_Upper)
26
   !CP      R16,      R24
27
   !LDI     R16,      hbyte(PWM_Upper)
28
   !CPC     R16,      R25
29
   !BRCS    Up_End
30
   !OUT     {PWM1A}, R24
31
   !OUT     {PWM1A +1}, R25
32
   !OUT     {PWM1B}, R24
33
   !OUT     {PWM1B +1}, R25
34
!Up_End:
35
   !POP     R25
36
   !POP     R24
37
   !POP     R16
38
   !OUT    SREG,     R16
39
   !POP     R16
40
Return

von Werner (Gast)


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

von Werner (Gast)


Lesenswert?

@ MWS:

das gab einen rasanten Zeitgewinn.

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

nochmal danke

von MWS (Gast)


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.

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.