Archiv verlassen und diese Seite im Standarddesign anzeigen : Eine RGB-Led und ein AtTiny13
Thomas E.
07.03.2012, 21:36
Hallo!
Nach längerer Elektronik-Abstinenz wieder ein Projekt von mir. Eigentlich nur eine RGB-Led und ein AtTiny13 sowie etwas Hühnerfütter. Genannt habe ich dieses etwas "TinyRGB".
Allerdings scheinen meine Programmierkenntnisse massiv eingerostet zu sein. Die von mir geschriebene Software läuft tadellos, die RGB-Led blendet wunderbar alle Farben durch (im Modus0) und fährt auch das Programm (Modus1) durch. Allerdings möchte ich die Software wesentlich schlanker machen, meine Variante benötigt nämlich jede Menge Flash. Auch gefallen mir die ganzen IF-Abfragen nicht.
Aber mir fällt einfach nichts ein.
21786
Is doch Wurscht...
Passt rein in den Controller und funktioniert...
Also wozu noch Lebenszeit vernichten und abändern??
Schaltung aufbauen, Glücklichsein und ab zum nächsten Projekt.
MfG, dl1akp
Das hier:
If Modus = 0 Then
If State = 0 Then
Incr C_r
Decr C_b
If C_r = 255 Then State = 1
End If
If State = 1 Then
Decr C_r
Incr C_g
If C_g = 255 Then State = 2
End If
If State = 2 Then
Decr C_g
Incr C_b
If C_b = 255 Then State = 0
End If
End If
If Modus = 1 Then
If State = 0 Then
If C_r < 255 Then Incr C_r
If C_r = 255 Then State = 1
End If
If State = 1 Then
If C_b < 255 Then Incr C_b
If C_b = 255 Then State = 2
End If
If State = 2 Then
If C_g < 255 Then Incr C_g
If C_g = 255 Then State = 3
End If
If State = 3 Then
If C_r > 1 Then Decr C_r
If C_g > 50 Then Decr C_g
If C_b > 1 Then Decr C_b
If C_r = 1 Then State = 4
End If
If State = 4 Then
If C_g < 255 Then Incr C_g
If C_b < 255 Then Incr C_b
If C_b = 255 Then State = 5
End If
If State = 5 Then
If C_g > 1 Then Decr C_g
If C_b > 1 Then Decr C_b
If C_g = 1 Then State = 0
End If
End If
Kannst du z.B. so umändern:
If Modus = 0 then
Select Case State
Case 0 : Incr C_r
Decr C_b
If C_r = 255 Then State = 1
Case 1 : Decr C_r
Incr C_g
If C_g = 255 Then State = 2
Case 2 : Decr C_g
Incr C_b
If C_b = 255 Then State = 0
End Select
Und für Modus = 1 ersetzt du die ganzen If-Abfragen ebenfalls durch Select-Case. Ob es das Programm nun wirklich kleiner macht weiß ich nicht aber auf jedenfall wird es übersichtlicher.
Den Rest wüsste ich nun nicht wie man den noch optimieren kann. Das Hauptprogramm beinhaltet nicht soviel Code, wobei ich die ISR vom Timer noch schlanker machen würde. In etwa so:
ISR_Timer0:
Interruptflag_Timer0 = 1
Return
Und dann im Hauptprogramm:
Do
If Interruptflag_Timer0 = 1 then
If Pwm_count = 255 Then Pwm_count = 0 'PWM-Variable bei 255 resetten
Incr Pwm_count 'PWM-Variable erhöhen
If Div = 10000 Then 'Teilervariable bei 10000 resetten
Div = 0
Gosub Fading 'Springe bei 10000 zu Fading
End If
Incr Div 'Teilervariable erhöhen
End if
Loop
End
Dies machst du aus dem Grund, weil man eine ISR möglichst kurz halten sollte (gut in deinem Programm macht das nun nichts aus, weil du nur eine ISR hast).
Weil wenn du mehrere Interruptquellen in einem Programm hast können eventuell Interrupts verschluckt werden, weil deine ISRs zu lang sind.
Deswegen mach ich das immer so das ich in den ISRs (wenn möglich) nur ein Bit setze, weil das schön schnell geht.
Wenn du in ISRs sowas wie "Print" Befehle o.ä. reinknallst kann es gut sein, dass der Controller beim ausführen der ISR einen anderen Interrupt ignoriert, weil so ein "Print" Befehl richtig viel Zeit braucht.
So würde ich das optimieren....ich weiß allerdings nicht in wie weit das eine Optimierung ist, weil ich selber nicht so der eingefleischte Programmierer bin :D
Aber das Select-Case macht das Programm auf jedenfall überschaubarer.
Edit: Hab gerade gesehen, dass du von der ISR in ein Unterprogramm springst.....versuch sowas zu vermeiden ;)
Wie gesagt nutze die Methode wo du in den ISR einfach nur Bits setzt und die Auswertung macht dann das Hauptprogramm.
Hallo Thomas,
Ich würde das auch so sehen, dass wenn es funktioniert es doch ok ist.
Wenn du beim nächsten Mal etwas verbessern möchtest, dann würde ich das gar nicht mal so sehr in deinen IF-Abfragen sehen, obwohl die Variante von Kampi sicherlich besser zu lesen ist.
Vielmehr würde ich die SoftPWM mal überdenken.
Dein µC läuft mit 9,6MHz, dein Timer ist 8-bit, läuft also 9.600.000 / 256 = 37500 mal pro Sekunde über. Das ist viel mehr als nötig. Es führt dazu, dass dein µC fast nichts anderes mehr tut, als andauernd die ISR aufzurufen. Hätte er jetzt noch andere Aufgaben, würde dies sicherlich schiefgehen.
Ich bin sicher, dass du für den Timer einen Prescaler von 8 oder 64 angeben könntest, ohne die Funktion zu verschlechtern. Dann müsstest du allerdings auch den Div-Wert von 10000 entsprechend auf 1250 bzw. 160 ändern.
Damit würde dein µC auf einen Schlag nur noch etwa 1% seiner Leistung in die PWM stecken.
Vielleicht probierst du es mal aus.
Searcher
09.03.2012, 09:22
Hallo,
Config Timer0 = Timer , Prescale = 1
On Timer0 Isr_timer0
Enable Timer0
Start Timer0
In diesem Fall ist Start Timer0 überflüssig, da der Timer schon durch Config Timer0 gestartet wird.
Es gibt eine ISR und von der ISR wird nochmal ein Unterprogramm aufgerufen. Nach der Berechnung von dieser Seite: http://halvar.at/elektronik/kleiner_bascom_avr_kurs/speicher_hwstack_swstack_frame/ oder auch nach der englischen mcs Seite wäre der $hwstack bei Dir viel zu klein bemessen.
Ich hab mir angewöhnt, in den Programmkopfbemerkungen immer die BASCOM Version mit reinzuschreiben, in der das Programm läuft. Es gibt von Version zu Version manchmal sehr einflußreiche Änderungen.
Gruß
Searcher
Thomas E.
11.03.2012, 11:38
Hallo!
Vielen Dank für eure Antworten.
Is doch Wurscht...
Passt rein in den Controller und funktioniert...
Also wozu noch Lebenszeit vernichten und abändern??
Schaltung aufbauen, Glücklichsein und ab zum nächsten Projekt.
Diese Einstellung will ich nicht teilen. Ich möchte nicht nur ein Projekt nach dem anderen machen, sondern ich möchte auch meine Programmierkenntnisse verbessern sowie auch verstehen, warum und wieso etwas funktioniert, gut funktioniert, schlecht funktioniert oder überhaupt nicht funktioniert. Für mich gehört dies genauso zum Prozess des Lernens. Es bedeutet einen Ansporn, gute und ressourcensparende Programme zu entwicklen und ich gebe mich nicht mit einem "funktioniert, ab in die Schublade" zufrieden.
Und für Modus = 1 ersetzt du die ganzen If-Abfragen ebenfalls durch Select-Case. Ob es das Programm nun wirklich kleiner macht weiß ich nicht aber auf jedenfall wird es übersichtlicher.
Den Rest wüsste ich nun nicht wie man den noch optimieren kann. Das Hauptprogramm beinhaltet nicht soviel Code, wobei ich die ISR vom Timer noch schlanker machen würde. Dies machst du aus dem Grund, weil man eine ISR möglichst kurz halten sollte (gut in deinem Programm macht das nun nichts aus, weil du nur eine ISR hast). [...]
Ob das Ersetzen der If-Abfragen durch Select-Case Flash spart, werde ich sofort testen. Aber eines stimmt mit Sicherheit, es macht den Code übersichtlicher. Danke für den Hinweis!
Weil wenn du mehrere Interruptquellen in einem Programm hast können eventuell Interrupts verschluckt werden, weil deine ISRs zu lang sind.
Deswegen mach ich das immer so das ich in den ISRs (wenn möglich) nur ein Bit setze, weil das schön schnell geht.
Wenn du in ISRs sowas wie "Print" Befehle o.ä. reinknallst kann es gut sein, dass der Controller beim ausführen der ISR einen anderen Interrupt ignoriert, weil so ein "Print" Befehl richtig viel Zeit braucht.
Ich dachte, der Controller speichert Interrupts ab und führt sie direkt im Anschluss aus, wenn er noch in der Abarbeitung eines Interrupts steckt?
Hallo Thomas,
Ich würde das auch so sehen, dass wenn es funktioniert es doch ok ist.
Wenn du beim nächsten Mal etwas verbessern möchtest, dann würde ich das gar nicht mal so sehr in deinen IF-Abfragen sehen, obwohl die Variante von Kampi sicherlich besser zu lesen ist.
Vielmehr würde ich die SoftPWM mal überdenken.
Dein µC läuft mit 9,6MHz, dein Timer ist 8-bit, läuft also 9.600.000 / 256 = 37500 mal pro Sekunde über. Das ist viel mehr als nötig. Es führt dazu, dass dein µC fast nichts anderes mehr tut, als andauernd die ISR aufzurufen. Hätte er jetzt noch andere Aufgaben, würde dies sicherlich schiefgehen.
Ich bin sicher, dass du für den Timer einen Prescaler von 8 oder 64 angeben könntest, ohne die Funktion zu verschlechtern. Dann müsstest du allerdings auch den Div-Wert von 10000 entsprechend auf 1250 bzw. 160 ändern.
Damit würde dein µC auf einen Schlag nur noch etwa 1% seiner Leistung in die PWM stecken.
Vielleicht probierst du es mal aus.
Eine interessante Idee. Das werde ich versuchen. Danke!
Es gibt eine ISR und von der ISR wird nochmal ein Unterprogramm aufgerufen. Nach der Berechnung von dieser Seite: http://halvar.at/elektronik/kleiner_bascom_avr_kurs/speicher_hwstack_swstack_frame/ oder auch nach der englischen mcs Seite wäre der $hwstack bei Dir viel zu klein bemessen.
Also bei mir reicht der Stack aus, aber ich werde trotzdem den Sprung von der ISR in die Sub ändern. Danke für den Hinweis!
mmmh das mit den Interrupts speichern ist ein guter Einwand.....da weiß ich gerade nicht so recht bescheid drüber ob das so ist.
Mir wurde während meiner Ausbildung öfters gesagt, dass man nicht soviel Code in die ISR packen soll, damit die ISR nicht zu lang wird.
Vielleicht kann jemand anders hier Licht ins dunkel bringen ob der Controller sich die Interrups merkt wenn er eine ISR abarbeitet oder nicht :)
Würde ich auch gerne wissen.
Thomas E.
11.03.2012, 13:05
Hallo!
Ich habe nun folgende Änderungen vorgenommen:
If-Abfragen durch Select-Abfragen ersetzen
Sprung von der Timer-ISR zur Sub Fading über ein Flag in die Hauptschleife verlegt
Durch die Änderung der If-Abfragen nach Select-Abfragen konnte ich etwa 1% Flash sparen, was anschließend durch den veränderten Sprung zur Sub Fading wieder belegt wurde. Somit stehe ich wieder bei 90% Speicherbelegung. :p
Eine Veränderung des Timer-Prescalers brachte flackern mit sich, so wie ich mir das gedacht habe.:(
Im Endeffekt rufst du mit GoSub Fading ja nur das Programm auf.
Lass das GoSub weg und pack das Unterprogramm direkt dahin wo dein GoSub stand. Und in der Timer ISR setzt du nur das Flag, welches du im Mainprogramm auswertest.
Das sieht dann etwa so aus:
'#########
'## EDT ##
'#########
'---------
'TinyRGB
'V1.0
'04.03.2012
'---------
'schaltplan_tinyrgb_v1_0.dsn
'---------
'AtTiny13 @ 9,6 Mhz internal RC-Clock
'Jp1 gesetzt: Modus 0 aktiv
'Jp2 gesetzt: Modus 1 aktiv
'Modus 0: Farbwechsel über alle drei Grundfarben (R-G-B)
'Modus 1: Effekt-Farbwechsel
'---------
$regfile = "attiny13.dat"
$crystal = 9600000 '9,6 Mhz
$hwstack = 8
$swstack = 4
$framesize = 10
'CONFIG IN/OUT
Config Portb.0 = Output 'Blau
Config Portb.1 = Output 'Grün
Config Portb.2 = Output 'Rot
Config Pinb.3 = Input 'Jp1
Config Pinb.4 = Input 'Jp2
Portb.3 = 1 'Pullup Jp1 einschalten
Portb.4 = 1 'Pullup Jp2 einschalten
'ALIAS
R Alias Portb.2
G Alias Portb.1
B Alias Portb.0
Jp1 Alias Pinb.3
Jp2 Alias Pinb.4
'CONFIG TIMER
Config Timer0 = Timer , Prescale = 1
On Timer0 Isr_timer0
Enable Timer0
Start Timer0
'VARIABLEN
Dim Pwm_count As Byte 'PWM-Variable
Dim C_r As Byte 'Channel Rot
Dim C_g As Byte 'Channel Grün
Dim C_b As Byte 'Channel Blau
Dim Div As Word 'Teilervariable
Dim State As Byte 'Status
Dim Modus As Bit 'Modus
Dim Timerflag As Bit 'Flag für die Timer ISR
'INTERRUPTS EIN
Enable Interrupts
'### HAUPTSCHLEIFE ###
Do
'Vergleich Channel kleiner PWM:
If C_r < Pwm_count Then R = 1 Else R = 0
If C_g < Pwm_count Then G = 1 Else G = 0
If C_b < Pwm_count Then B = 1 Else B = 0
'Jumperabfrage:
If Jp1 = 0 Then Modus = 0
If Jp2 = 0 Then Modus = 1
If Timerflag = 1 Then
If Pwm_count = 255 Then Pwm_count = 0 'PWM-Variable bei 255 resetten
Incr Pwm_count 'PWM-Variable erhöhen
If Div = 10000 Then 'Teilervariable bei 10000 resetten
Div = 0
If Modus = 0 Then
Select Case State
Case 0 : Incr C_r
Decr C_b
If C_r = 255 Then State = 1
Case 1 : Decr C_r
Incr C_g
If C_g = 255 Then State = 2
Case 2 : Decr C_g
Incr C_b
If C_b = 255 Then State = 0
End Select
End If
If Modus = 1 Then
Select Case State
Case 0 : If C_r < 255 Then Incr C_r
If C_r = 255 Then State = 1
Case 1 : If C_b < 255 Then Incr C_b
If C_b = 255 Then State = 2
Case 2 : If C_g < 255 Then Incr C_g
If C_g = 255 Then State = 3
Case 3 : If C_r > 1 Then Decr C_r
If C_g > 50 Then Decr C_g
If C_b > 1 Then Decr C_b
If C_r = 1 Then State = 4
Case 4 : If C_g < 255 Then Incr C_g
If C_b < 255 Then Incr C_b
If C_b = 255 Then State = 5
Case 5 : If C_g > 1 Then Decr C_g
If C_b > 1 Then Decr C_b
If C_g = 1 Then State = 0
End Select
End If
End If
Incr Div 'Teilervariable erhöhen
Timerflag = 0 'Flag für die Timer ISR zurücksetzen
End If
Loop
'ISR TIMER0
Isr_timer0:
Timerflag = 1
Return
Das ist jetzt dein alter Code ohne das "Select Case" also nicht wundern :)
So sparst du dir auch das Unterprogramm, weil das alles im Hauptprogramm ausgeführt wird (statt Gosub schreibst du da halt den Code hin).
Edit: Hab einen aktuellen Code eingefügt. So in etwa meinte ich das. Musst du mal gucken ob er für dich in Ordnung ist :)
Thomas E.
11.03.2012, 16:50
Hallo Kampi!
Dadurch würde ich mir ja nur die Befehle Gosub und Return, sowie die Zeit zum Speichern aller wichtigen Register in den SRAM ersparen. Der benötigte Platz für die eigentlichen in "Fading" enthaltenen Befehle würde somit ja nicht kleiner werden, sondern steht nur an anderer Stelle im Flash. So zumindest meine Gedanken dazu.
Hallo Kampi!
Dadurch würde ich mir ja nur die Befehle Gosub und Return, sowie die Zeit zum Speichern aller wichtigen Register in den SRAM ersparen. Der benötigte Platz für die eigentlichen in "Fading" enthaltenen Befehle würde somit ja nicht kleiner werden, sondern steht nur an anderer Stelle im Flash. So zumindest meine Gedanken dazu.
Stimmt! Du hast recht :)
Ich weiß nicht aber ich glaube du kannst den Speicher nur optimieren, wenn du es in ASM machst. Im Moment wüsste ich nicht wo man noch was weglassen kann.
Auf der anderen Seite....der Tiny ist ja nicht gerade ein Speicherwunder :)
Da wirken selbst sehr kleine Codes mächtig :)
MagicWSmoke
11.03.2012, 17:46
Ein paar wenige Bytes kann man durch Weglassen von
If Pwm_count = 255 Then Pwm_count = 0 'PWM-Variable bei 255 resetten
sparen, das schon von Beginn des Threads an mitgeschleift wird.
Eine Byte-Variable läuft von allein nach 255 auf 0 über, da muss man nix extra machen.
Wenn man noch statt der doppelten Abfrage
If C_x < 255 Then Incr C_x
If C_x = 255 Then State = 1
jeweils
If C_x < 255 Then Incr C_x Else State = 1
schreibt, kommt man auf 85%, ohne viel geändert zu haben.
Thomas E.
11.03.2012, 19:02
Hallo!
Dank den Erläuterungen von User MagicWSmoke konnten nun 6% Flash gespart werden. Hier der aktuelle Stand des Programms:
21808
Vielleicht fällt noch jemanden etwas ein? Es ist schon ein interessanter Ansporn, noch etwas Speicher einzusparen, zumal dann ja Platz für weitere Fading-Möglichkeiten ist. Die Schaltung beleuchtet übrigens den Deko-Bereich auf einem Wohnzimmerschrank und wird von zwei NiMH-Akkus versorgt, die tagsüber von Solarenergie geladen werden.
Hey :)
kannst du den "Schaltplan" der Schaltung posten?
Ich hab mir gestern 10 RGB LEDs bestellt und weiß nicht wofür ich sie verwenden soll (die haben 3€ das Stück gekostet....ka ob sie was taugen oder nicht aber für 3€ sag ich nicht nein) :D
Und nun bin ich auf der Suche nach schönen Bastelleien mit RGB LEDs :).....btw. ich hab mir eine bei RS für 25€ das Stück bestellt und für 3W ist die ja mal super hell.....und auch die "Chinesen" LEDs sind 3W....mal schauen wie die sich im Test gegen die teure LED machen.
Wie gesagt wäre toll wenn man den Schaltplan noch sehen könnte. Weil so ein kleines Lichterspiel auf dem Schrank oder hinter dem Fernseher sicher toll aussieht.
Das hier:
Ich dachte, der Controller speichert Interrupts ab und führt sie direkt im Anschluss aus, wenn er noch in der Abarbeitung eines Interrupts steckt?
Pro Interrupt gibt es einen "1 Bit" Speicher, das Interruptflag. Er kann sich soviele Interrupte merken, wie es Quellen gibt. Wenn ich innerhalb eines Handler das Flag am Anfang zurücksetze, kann er einen, aber nur einen weiteren gleichen Interrupt auch während der Laufzeit des Handlers merken. Wenn der Interrupthandler unterschiedlich lange dauert oder die Interrupte in unterschiedlichen Abständen kommen, kann einen das retten.
MfG Klebwax
Searcher
11.03.2012, 20:59
Vielleicht fällt noch jemanden etwas ein?
Hallo Thomas,
folgendes bringt noch hier und da etwas:
statt:
Dim Modus As Bit 'Modus
Dim Flag_a As Bit 'Flag für Sub Fading
als Byte einrichten
Dim Modus As Byte 'Modus
Dim Flag_a As Byte 'Flag für Sub Fading
und alle
Set Modus mit Modus = 1, Reset Modus mit Modus = 0 ersetzen
Set Flag_a, Reset Flag_a gleichermaßen.
Alle "Decr Variable" mit "Variable = Variable - 1"
und alle "Incr Variable" mit "Variable + 1" ersetzen.
bringt pro Decr zwei Byte
Gruß
Searcher
Thomas E.
11.03.2012, 21:25
kannst du den "Schaltplan" der Schaltung posten?
Viel ist da nicht. Einfach ein Tiny13 mit den drei Anschlüssen einer RGB-LED über je einen Vorwiderstand an drei Pins. Dann noch eine Stiftleiste für ISP, einen Widerstand der den Reset-Pin hochzieht, zwei Jumper und ein Abblockkondensator. Das war's.
Weil so ein kleines Lichterspiel auf dem Schrank oder hinter dem Fernseher sicher toll aussieht.
Das tut es. ;)
Pro Interrupt gibt es einen "1 Bit" Speicher, das Interruptflag. Er kann sich soviele Interrupte merken, wie es Quellen gibt. Wenn ich innerhalb eines Handler das Flag am Anfang zurücksetze, kann er einen, aber nur einen weiteren gleichen Interrupt auch während der Laufzeit des Handlers merken. Wenn der Interrupthandler unterschiedlich lange dauert oder die Interrupte in unterschiedlichen Abständen kommen, kann einen das retten.
Danke für die Ausführung.
Hallo Thomas,
folgendes bringt noch hier und da etwas: [...]
Benötigt die Deklaration Var = Var + 1 tatsächlich weniger Speicher als ein einfaches Incr Var?
Ah ok :)
Was für ne LED verwendest du den? Auch so eine 3W LED oder kleiner?
Und ja das bringt Soeicher. Ich hab jetzt mal alles umgesetzt was hier vorgeschlagen wurde und das Programm benötigt nur noch 80% Speicher.
Von 90% am Anfang ist das ja schon ziemlich stark :D
Searcher
12.03.2012, 07:49
Benötigt die Deklaration Var = Var + 1 tatsächlich weniger Speicher als ein einfaches Incr Var?
Nein, hier habe ich es nicht festgestellt.
Hatte es nur mit aufgenommen, weil es nach dem Ersetzen des Decr ein besseres Bild im Listing macht. Allerdings meine ich, früher schon mal festgestellt zu haben, daß das Ersetzten von dem Incr auch eine Ersparnis bringt, bin mir jedoch nicht mehr sicher:confused: und in welchem Zusammenhang das war.
Gruß
Searcher
Nein, hier habe ich es nicht festgestellt.
Hatte es nur mit aufgenommen, weil es nach dem Ersetzen des Decr ein besseres Bild im Listing macht. Allerdings meine ich, früher schon mal festgestellt zu haben, daß das Ersetzten von dem Incr auch eine Ersparnis bringt, bin mir jedoch nicht mehr sicher:confused: und in welchem Zusammenhang das war.
Gruß
Searcher
Müsste man mal schauen, was Bascom aus einem INCR oder DECR Befehl macht. Gibt es irgendwo eine Auflistung der Bascom Befehle und ihrem Gegenstück in ASM?
Btw...danke Klebwax für die Erklärung :)
Searcher
12.03.2012, 09:47
Müsste man mal schauen, was Bascom aus einem INCR oder DECR Befehl macht. Gibt es irgendwo eine Auflistung der Bascom Befehle und ihrem Gegenstück in ASM?
Hallo Kampi,
so 'ne Auflistung kenne ich nicht. Hab das aber für was anderes mit einem Disassembler gemacht:
https://www.roboternetz.de/community/threads/56208-Int0-gibt-2-Impulse-!?p=535821&viewfull=1#post535821 (https://www.roboternetz.de/community/threads/56208-Int0-gibt-2-Impulse-%21?p=535821&viewfull=1#post535821)
Das ReAVR hatte ich von hier: http://www.jassenbaum.de/ja-tools/
Gruß
Searcher
MagicWSmoke
12.03.2012, 10:00
So werden's 72%:
Dim C_r As IRam Byte At 12 'Channel Rot
Dim C_g As IRam Byte At 13 'Channel Grün
Dim C_b As IRam Byte At 14 'Channel Blau
Und das bringt's auf 66%:
On Timer0 Isr_timer0 Nosave
' ...
'ISR TIMER0
Isr_timer0:
!push r16
!push r17
!push r20
!push r21
!push r23
!push r24
!push r26
!push r30
!push r31
!in r24, SREG
!push r24
Incr Pwm_count 'PWM-Variable erhöhen
If Div = 10000 Then 'Teilervariable bei 10000 resetten
Div = 0
Set Flag_a
End If
Div = Div + 1 'Teilervariable erhöhen
!pop r24
!out SREG,r24
!pop r31
!pop r30
!pop r26
!pop r24
!pop r23
!pop r21
!pop r20
!pop r17
!pop r16
Return
Außerdem kommt man dann ordentlich mit dem HWStack hin. Es hatte vorher lediglich keine Auswirkung wenn SWStack und Frame überschrieben wurden. Das Sichern der Register kostete 26 Bytes HWStack und der Aufruf von Incr Div nochmal 2 Bytes, 8 Bytes waren also viel zuwenig. Nun werden nicht mehr als 10 Byte Stack verwendet, bei Bedarf an mehr Platz für Variablen können folgende Stackangaben nochmal verkleinert werden, SWStack und Frame werden sowieso nicht benutzt.
$hwstack = 16
$swstack = 16
$framesize = 24
Hinweis dazu: die selektive Registersicherung per Nosave/Push/Pop ist nur solange als gültig zu betrachten, solange die ISR nicht verändert wird, evtl. muss man auf die Bascom-Version achten, hier 2.0.7.4
Interessanterweise wird mit ausgeschalteter Optimierung jeweils 1% weniger Speicher belegt.
Benötigt die Deklaration Var = Var + 1 tatsächlich weniger Speicher als ein einfaches Incr Var?
Nein, das ist falsch. Incr Byte_Var und Byte_Var = Byte_Var +1 erzeugen zwar identischen Code, hingegen wird Word_Var = Word_Var +1 Inline compiliert, dagegen Incr Word_Var als Funktionsaufruf. Es hängt also erst einmal von der Zielvariablen ab.
Eine Funktion aufzurufen, d.h. Setzen der Register und ein RCALL/CALL, kostet weniger Flashspeicher als wenn der Code Inline eingebaut wird.
Hat man viele Stellen im Code, in denen eine Word-Variable um 1 erhöht wird, so ist es also genau anders herum, man spart mit der Incr-Version Speicherplatz. Das kann sich aber mit der Bascom-Version ändern.
In unserem Fall ist Div = Div + 1 besser, da es etwas weniger Stack braucht und schneller ist.
So werden's 72%:
Dim C_r As IRam Byte At 12 'Channel Rot
Dim C_g As IRam Byte At 13 'Channel Grün
Dim C_b As IRam Byte At 14 'Channel Blau
Und das bringt's auf 66%:
On Timer0 Isr_timer0 Nosave
' ...
'ISR TIMER0
Isr_timer0:
!push r16
!push r17
!push r20
!push r21
!push r23
!push r24
!push r26
!push r30
!push r31
!in r24, SREG
!push r24
Incr Pwm_count 'PWM-Variable erhöhen
If Div = 10000 Then 'Teilervariable bei 10000 resetten
Div = 0
Set Flag_a
End If
Div = Div + 1 'Teilervariable erhöhen
!pop r24
!out SREG,r24
!pop r31
!pop r30
!pop r26
!pop r24
!pop r23
!pop r21
!pop r20
!pop r17
!pop r16
Return
Außerdem kommt man dann ordentlich mit dem HWStack hin. Es hatte vorher lediglich keine Auswirkung wenn SWStack und Frame überschrieben wurden. Das Sichern der Register kostete 26 Bytes HWStack und der Aufruf von Incr Div nochmal 2 Bytes, 8 Bytes waren also viel zuwenig. Nun werden nicht mehr als 10 Byte Stack verwendet, bei Bedarf an mehr Platz für Variablen können folgende Stackangaben nochmal verkleinert werden, SWStack und Frame werden sowieso nicht benutzt.
$hwstack = 16
$swstack = 16
$framesize = 24
Hinweis dazu: die selektive Registersicherung per Nosave/Push/Pop ist nur solange als gültig zu betrachten, solange die ISR nicht verändert wird, evtl. muss man auf die Bascom-Version achten, hier 2.0.7.4
Interessanterweise wird mit ausgeschalteter Optimierung jeweils 1% weniger Speicher belegt.
Nein, das ist falsch. Incr Byte_Var und Byte_Var = Byte_Var +1 erzeugen zwar identischen Code, hingegen wird Word_Var = Word_Var +1 Inline compiliert, dagegen Incr Word_Var als Funktionsaufruf. Es hängt also erst einmal von der Zielvariablen ab.
Eine Funktion aufzurufen, d.h. Setzen der Register und ein RCALL/CALL, kostet weniger Flashspeicher als wenn der Code Inline eingebaut wird.
Hat man viele Stellen im Code, in denen eine Word-Variable um 1 erhöht wird, so ist es also genau anders herum, man spart mit der Incr-Version Speicherplatz. Das kann sich aber mit der Bascom-Version ändern.
In unserem Fall ist Div = Div + 1 besser, da es etwas weniger Stack braucht und schneller ist.
Echt stark was du da noch rausgequetscht kriegst :O
Wenn man in der ISR jetzt nur ein Flag setzt, kann man dann trotzdem deine ISR so wie sie ist nutzen oder muss man an dem push usw. noch was ändern?
Oder sicherst du damit nur die ganzen Register, egal wie die verwendet wurden?
Kannst du das nochmal bischen genauer erläutern :). Bin jetzt neugierig geworden :D
Und das Decr und Incr besser ist wenn man ein Word hat ist ja auch interessant zu wissen.
MagicWSmoke
12.03.2012, 14:37
Wenn man in der ISR jetzt nur ein Flag setzt, kann man dann trotzdem deine ISR so wie sie ist nutzen oder muss man an dem push usw. noch was ändern?
Oder sicherst du damit nur die ganzen Register, egal wie die verwendet wurden?
Nein, ich würde vor pauschaler Nutzung immer vorsichtig sein.
Es sind insgesamt 32 Register und ich sichere nur die Verwendeten. Da ist's wichtig sich dazu das erzeugte ASM-Compilat ansehen und zu schauen was da tatsächlich hergenommen wird. Wenn Bascom-Funktionen (wie z.B. Incr) aufgerufen werden, wird's schwieriger, denn dann geht's erstmal aus der ISR per CALL/RCALL raus und mann muss alle weiteren Verzweigungen verfolgen, die der Code nehmen kann.
Darum schrieb ich auch die Version, der Compiler wird zu einem bestimmten Versionsstand bestimmte Register nutzen, aber Garantie für andere Versionen gibt's dafür nicht.
Wird dagegen nur ein Flag gesetzt wird, müssen weniger Register gesichert werden, das würde man besser per Assembler machen, eben weil man dann die volle Kontrolle hat. Am Schnellsten geht ein Bit im Bereich bis I/O &h1F zu setzen. Manche Prozessoren haben GPIO's in dem Bereich, z.B. ATM644:
On Interruptx ISR Nosave
' ...
ISR:
!SBI GPIOR0, 0
Return
Meist sitzen die Ports auch in diesem Bereich. Hat man einen Pin des Ports zur freien Verfügung, so ist das die kürzest mögliche Anweisung:
ISR:
!SBI PortA, 0
Return
Alles andere geht langsamer. Wenn man keine Fließkomma-Variablen verwendet, dann sind meist R12-R15 frei. Leider gehen einige ASM-Befehle erst ab >= R16, darum wird ein Flag setzen in R12 zu so etwas:
Dim myFlag As IRam Byte At 12
' ...
ISR:
!PUSH R16
!LDI R16, 1
!MOV R12, R16
!POP R16
Return
Ein Setzen einer SRam-Variablen würde so gehen:
Dim myFlag As Byte
' ...
ISR:
!PUSH R16
!LDI R16, 1
!STS {myFlag}, R16
!POP R16
Return
Geht in der ISR gleich schnell wie mit einer IRam-Variablen, nur in der Hauptschleife ist eine Verwendung von Bascom-Befehlen in Verbindung mit IRam immer noch ein Gewinn. Ein Sichern des Statusregisters SREG ist in keinem der Beispiele notwendig, da weder LDI, MOV, PUSH, POP noch STS das SREG verändern.
Und das Decr und Incr besser ist wenn man ein Word hat ist ja auch interessant zu wissen.
Macht sich halt nur bei vielen Anweisungen oder kleinem Speicherplatz wirklich bemerkbar.
Edit:
Pwm_count kann man noch als IRam At 15 dimensionieren, dann geht's auf 65%
MagicWSmoke
12.03.2012, 17:24
Das lram sorgt wofür?
Die Hilfe unter Dim gefiel Dir nicht ?
IRam ermöglicht es Prozessorregister unter einem Variablennamen anzusprechen. Es gibt im AVR 32 Prozessorregister, nämlich R0-R31.
Mit:
Dim myFlag As IRam Byte At 12
wird das Register R12 unter Bascomcode als myFlag ansprechbar, also:
If myFlag = 1 Then
' ...
End If
Prozessorregister dienen üblicherweise als Arbeitsregister und nur wenn diese von Bascom gerade selbst nicht genutzt werden, kann man das machen, was ich hier gezeigt hab'. Sobald z.B. eine Fließkommaberechnung stattfände, würden R12-R15 zerstört und damit die Sache unbrauchbar.
Der Vorteil von IRam-Variablen liegt darin, dass Daten nicht extra aus dem SRam geholt und wieder dahin zurückgespeichert werden müssen, sondern jederzeit verfügbar in den Prozessorregistern vorliegen. Das geht schneller und spart Code.
Danke für die Erklärung.
Ich hab unter IRam gesucht und nichts gefunden :(
Deswegen hab ich gefragt.
MagicWSmoke
12.03.2012, 17:39
Deswegen hab ich gefragt.
Kein Problem. ;-) IRam gehört weder zu den bekanntesten noch zu den meistverwandten Dingen. Tatsächlich ist's so, dass ich für mich kein IRam verwende, ich schreib dann lieber die ganze ISR in Assembler. Hier fand ich's interessant es zu erwähnen, da man so unter (fast) ausschließlicher Verwendung von Bascom-Befehlen einen deutlichen Vorteil erzielt.
Thomas E.
12.03.2012, 19:18
Was für ne LED verwendest du den? Auch so eine 3W LED oder kleiner?
Ich verwende eine kleine 5mm RGB-Led mit 4 Anschlüssen, gemeinsame Anode. Durch den geringen Vorwärtsstrom von 20mA pro Farbe konnte ich einen etwaigen Treiber einsparen. Durch großzügige Vorwiderstände (ich will es nicht so hell) wird ein Batteriebetrieb möglich. Die LED ist direkt auf die Platine aufgelötet; da die Platine - oder eher, das Platinchen - sehr klein ist, stört das nicht. Das Platinchen liegt hinter mehreren Kerzen auf der Deko-Ecke eines Kastens und beleuchtet dadurch die Raumecke. Natürlich kommt das Licht nur zur Wirkung, wenn der Raum abgedunkelt ist, aber das war ja auch der Sinn der Sache. Also sozusagen die passende lichttechnische Untermalung zum Kuscheln und Fernsehen. ;)
@MagicWSmoke
Vielen Dank für die sehr interessanten Ausführen. Es ist sehr faszinierend, wieviel du aus dem Code herausholen konntest. Mit den Assembler-Instruktionen habe ich zwar noch meine Probleme, aber das liegt wohl eher daran, dass ich mich zu meiner Schande viel zu wenig damit beschäftige.
@MagicWSmoke
Vielen Dank für die sehr interessanten Ausführen. Es ist sehr faszinierend, wieviel du aus dem Code herausholen konntest. Mit den Assembler-Instruktionen habe ich zwar noch meine Probleme, aber das liegt wohl eher daran, dass ich mich zu meiner Schande viel zu wenig damit beschäftige.
Da bist du nicht alleine. Aber das ist genial....der Code wurde von 90% auf 66% runter gedrückt :)
Ich verwende eine kleine 5mm RGB-Led mit 4 Anschlüssen, gemeinsame Anode. Durch den geringen Vorwärtsstrom von 20mA pro Farbe konnte ich einen etwaigen Treiber einsparen. Durch großzügige Vorwiderstände (ich will es nicht so hell) wird ein Batteriebetrieb möglich. Die LED ist direkt auf die Platine aufgelötet; da die Platine - oder eher, das Platinchen - sehr klein ist, stört das nicht. Das Platinchen liegt hinter mehreren Kerzen auf der Deko-Ecke eines Kastens und beleuchtet dadurch die Raumecke. Natürlich kommt das Licht nur zur Wirkung, wenn der Raum abgedunkelt ist, aber das war ja auch der Sinn der Sache. Also sozusagen die passende lichttechnische Untermalung zum Kuscheln und Fernsehen. :wink:
Ok sowas hatte ich auch geplant :) nur weil meine LED bisl größer ist muss ich noch 3 Transistoren mit auf die Platine bringen. Aber ich möchte das Ding dann auch mit einem Steckernetzteil betreiben :)
MagicWSmoke
12.03.2012, 20:33
Mit den Assembler-Instruktionen habe ich zwar noch meine Probleme, aber das liegt wohl eher daran, dass ich mich zu meiner Schande viel zu wenig damit beschäftige.
Nun, wenn Du normalerweise nicht brauchst, ist's ja ok. Assembler ist aber sehr einfach mit Bascomcode zu mischen und da stellt's 'nen echten Vorteil dar, wenn man's kann.
Thomas E.
12.03.2012, 20:38
Nun, wenn Du normalerweise nicht brauchst, ist's ja ok. Assembler ist aber sehr einfach mit Bascomcode zu mischen und da stellt's 'nen echten Vorteil dar, wenn man's kann.
Leider konnte ich mich noch nicht dazu überwinden, mich näher mit Assembler zu beschäftigen. Aber solche kleinen Codeschnipsel und Ausführungen wie weiter oben von dir sind schon ein gewisse Ansporn. ;)
Danke nochmals dafür! :)
MagicWSmoke
13.03.2012, 16:29
Aber solche kleinen Codeschnipsel und Ausführungen wie weiter oben von dir sind schon ein gewisse Ansporn.
Dann dürfte das noch etwas mehr Ansporn sein:
'#########
'## EDT ##
'#########
'---------
'TinyRGB
'V1.2
'11.03.2012
'---------
'schaltplan_tinyrgb_v1_0.dsn
'---------
'AtTiny13 @ 9,6 Mhz internal RC-Clock
'Jp1 gesetzt: Modus 0 aktiv
'Jp2 gesetzt: Modus 1 aktiv
'Modus 0: Farbwechsel über alle drei Grundfarben (R-G-B)
'Modus 1: Effekt-Farbwechsel
'---------
'Changelog:
'V1.2: * If-Abfrage zum Rücksetzen der Variable Pwm_count in der Timer-ISR entfernt
' (Variable läuft von selbst nach 0 über)
' * If-Abfragen zur Statusänderung in der Fading-Routine für Modus1 entfernt und über
' else-Statement direkt in den If-Abfragen zum Verändern der C_x-Variablen implementiert;
' einsparen von Flash
'
'V1.1: * If-Abfragen gegen Select-Abfragen getauscht; bessere Code-Lesbarkeit
' * Sprung zu Sub Fading über Flag in der Hauptschleife; spart SRAM und verkürzt die ISR-Handlung
'---------
$regfile = "attiny13.dat"
$crystal = 9600000 '9,6 Mhz
$hwstack = 16
$swstack = 16
$framesize = 24
Const R_Pin = 2
Const G_Pin = 1
Const B_Pin = 0
Const Clr_Mask = Not(2 ^ R_Pin + 2 ^ G_Pin + 2 ^ B_Pin)
Const Div_Preset = 9999
Const Clr_down = 0 ' Bei Ausgangspin Low schaltet die Led ein, bei High aus
Const Clr_up = 1 ' Bei Ausgangspin High schaltet die Led ein, bei Low aus
Const Compare_PWM_mode = Clr_down ' hier festlegen wie sich die PWM verhalten soll
'Const Compare_PWM_mode = Clr_up
PWM_Port Alias PortB
PWM_DIR Alias DDRB
R_Reg Alias R12
G_Reg Alias R13
B_Reg Alias R14
PWM_Cnt_Reg Alias R15
Dim C_r As IRam Byte At 12 'Channel Rot
Dim C_g As IRam Byte At 13 'Channel Grün
Dim C_b As IRam Byte At 14 'Channel Blau
'CONFIG IN/OUT
PWM_DIR = Bits(PB2 , PB1 , PB0)
PWM_Port = Bits(PB4 , PB3) 'Pullup Jp1 JP2 einschalten
'ALIAS
Jp1 Alias Pinb.3
Jp2 Alias Pinb.4
'CONFIG TIMER
Config Timer0 = Timer , Prescale = 1
On Timer0 Isr_timer0 Nosave
Enable Timer0
'VARIABLEN
Dim State As Byte
Dim Div As Word 'Teilervariable
Dim Modus As Byte 'Modus
Dim Flag_a As Byte 'Flag für Sub Fading
'INTERRUPTS EIN
Enable Interrupts
'### HAUPTSCHLEIFE ###
Do
'Jumperabfrage:
If Jp1 = 0 Then Modus = 0
If Jp2 = 0 Then Modus = 1
'Abfrage Flag_a:
If Flag_a = 1 Then Gosub Fading 'Wenn Flag gesetzt, springe zu Sub Fading
Loop
End
Fading:
If Modus = 0 Then
Select Case State
Case 0 :
Incr C_r
Decr C_b
If C_r = 255 Then State = 1
Case 1:
Decr C_r
Incr C_g
If C_g = 255 Then State = 2
Case 2:
Decr C_g
Incr C_b
If C_b = 255 Then State = 0
End Select
End If
If Modus = 1 Then
Select Case State
Case 0:
If C_r < 255 Then Incr C_r Else State = 1
Case 1:
If C_b < 255 Then Incr C_b Else State = 2
Case 2:
If C_g < 255 Then Incr C_g Else State = 3
Case 3:
If C_r > 0 Then Decr C_r Else State = 4
If C_g > 50 Then Decr C_g
If C_b > 0 Then Decr C_b
Case 4:
If C_g < 255 Then Incr C_g
If C_b < 255 Then Incr C_b Else State = 5
Case 5:
If C_g > 0 Then Decr C_g Else State = 0
If C_b > 0 Then Decr C_b
End Select
End If
Flag_a = 0 'Flag zurücksetzen
Return
'ISR TIMER0
Isr_timer0:
!PUSH R25
!push R24
!IN R24, SREG
!push R24
!INC PWM_Cnt_Reg
!IN R24, PWM_Port
!ANDI R24, Clr_Mask
!CP R_Reg, PWM_Cnt_Reg
#IF Compare_PWM_mode = Clr_up
!BRCS No_R
#ELSE
!BRCC No_R
#ENDIF
!SBR R24, 2 ^ R_Pin
No_R:
!CP G_Reg, PWM_Cnt_Reg
#IF Compare_PWM_mode = Clr_up
!BRCS No_G
#ELSE
!BRCC No_G
#ENDIF
!SBR R24, 2 ^ G_Pin
No_G:
!CP B_Reg, PWM_Cnt_Reg
#IF Compare_PWM_mode = Clr_up
!BRCS No_B
#ELSE
!BRCC No_B
#ENDIF
!SBR R24, 2 ^ B_Pin
No_B:
!OUT PWM_Port,R24
!LDS R24, {Div}
!LDS R25, {Div+1}
!SBIW R24, 1
!BRPL Not_Elapsed
!LDI R24, 1
!STS {Flag_a},R24
!LDI R24, lbyte(Div_Preset)
!LDI R25, hbyte(Div_Preset)
Not_Elapsed:
!STS {Div}, R24
!STS {Div+1}, R25
!POP R24
!OUT SREG, R24
!POP R24
!POP R25
Return
Edit:
Hab' noch 'ne Compileranweisung eingeführt um je nach Anschluss der Leds eine einfache Umschaltung der Polarität zu ermöglichen, ohne ASM kennen zu müssen, damit kann der ISR-Teil universell verwendet werden.
Der Vorteil der Soft-PWM in ASM statt in Basic: es werden nur ca. 45 Takte verbraucht, bei einem Prescaler von 1 und einem 8-Bit Timer0 wird die ISR alle 256 Takte aufgerufen, damit beträgt die Prozessorlast durch die ASM-ISR: 45/256 = 17,6% gegenüber ca. 55% der Basic-ISR. Der Flashverbrauch sank auf 56%.
Die Konstantenbezeichnung orientiert sich an der Syntax von Config PWM.
Thomas E.
21.04.2012, 10:58
Hallo!
Die Variante mit etwas mehr Power, von mir wegen des größeren Controllers als eigenständiges Projekt geführt, nennt sich TinyStripeRGB. Es geht primär darum einen RGB-LED-Stripe treiben zu können und etwas mehr Programmspeicher zur Verfügung zu haben.
Hinweis: Die Sicherung F1 ist relativ knapp bemessen, Strombedarf des Led-Stripes beachten!
Powered by vBulletin® Version 4.2.5 Copyright ©2024 Adduco Digital e.K. und vBulletin Solutions, Inc. Alle Rechte vorbehalten.