Hallo, ich bin noch immer an meinem Wecker-Projekt (ATMEGA8, intern 4MHz RC, LED-7Seg) und wollte jetzt ein feature einbauen, sodass man die Helligkeit vom Display regulieren kann. Das soll sozusagen wie PWM funktionieren. Da ich nicht soviele Pins zur verfuegung habe und auch keine weiteren ICs verwenden wollte wird das Display momentan gepulst. Es ist immer nur eine der 4 Ziffern an, und die werden bisher einfach im Kreis durchlaufen, bei jedem Interrupt gehts eins weiter. Der Interrupt ist der OutputCompareMatch vom 16-bit Timer 1. Zur Helligkeitsregulierung habe ich es jetzt so gemacht, dass er bei jedem Interrupt den "status" wechselt (0/1). Und jedes mal das OutputCompareMatch Register aendert. Im Status 0 waere das Display dann komplett an, im Status 1 wuerde er eine Ziffer weiter gehen und diese anschalten. Man soll dann das Verhaeltnis der Dauer von An und Aus umstellen koennen. Prinzipiell funktioniert das auch, aber ich vermute stark, dass hier irgendwelche Interrupts verschluckt werden, wenn das Verhaeltnis zu stark von 1 abweicht, weil dann das Display teilweise kurz flackert. Ich koennte natuerlich das Verhaeltnis einfach strenger begrenzen, aber dann bringt die Helligkeitseinstellung nicht mehr soviel, wie loest man sowas also professionell? Ich hoffe ihr koennt mir helfen. Wenn ich was wichtiges vergessen habe einfach fragen. Philipp
Ich würde den zweiten Compare-Interrupt des Timers nehmen, um das Licht auszuschalten. Also der eine Compare-Int macht das Multiplexing (Datenquelle umschalten, Digit umschalten, nächsten Multiplex-Termin festlegen und jetzt zusätzlich den Ausschalt-Termin des anderen Interrupts festlegen). Der andere Compare-Int erfolgt nun mit (einstellbarer) Verzögerung dem ersten Comp-Int und macht das Licht wieder aus. Somit kann man die Einschaltdauer feinstufig einstellen. ...
Ja stimmt, gar nicht gesehen, dass es zwei CompareRegister gibt. Aber mein Problem bleibt dann immer noch. Das Problem ist naemlich der Grenzfall, wo Aus- und Einschalten zeitlich ganz nah zusammenrücken,wenn ich das Display sehr hell oder dunkel stelle. Also sehr dicht hintereinander die Interrupts kommen. Und in der einen Interruptroutine zum setzen der Segmente brauche ich schon ein paar kleine Rechnungen die kurz dauern. An sich kein problem, das interrupt flag bleibt ja gesetzt, bis die routine fertig ist, aber evtl. wird dann mein wichtigster Interrupt verschluckt, der naemlich die Zeit zaehlt...
Philipp F. wrote: > Ja stimmt, gar nicht gesehen, dass es zwei CompareRegister gibt. Aber > mein Problem bleibt dann immer noch. Dann machst Du was Grundlegendes falsch... > Das Problem ist naemlich der Grenzfall, wo Aus- und Einschalten zeitlich > ganz nah zusammenrücken,wenn ich das Display sehr hell oder dunkel > stelle. Also sehr dicht hintereinander die Interrupts kommen. Und in der > einen Interruptroutine zum setzen der Segmente brauche ich schon ein > paar kleine Rechnungen die kurz dauern. Aha... Du rechnest in der ISR? Du solltest lieber in der Mainloop rechnen und in der ISR die bereitstehenden Werte nur kopieren. Dies verkürzt die ISRs dermaßen, dass es keine Konflikte mehr gibt. > An sich kein problem, das interrupt flag bleibt ja gesetzt, gesetzt?? Während der ISR ist es meiner Meinung nach gelöscht... > bis die > routine fertig ist, aber evtl. wird dann mein wichtigster Interrupt > verschluckt, der naemlich die Zeit zaehlt... Das lässt sich alles durch das Befolgen von drei wichtigen Regeln verhindern: 1. ISRs möglichst kurz halten 2. ISRs möglichst kurz halten 3. ISRs möglichst kurz halten ...
Ich weiss ja nicht was du mit kurz meinst. Also ich habe keine Schleifen o.ae. drin:
1 | SIGNAL(SIG_OUTPUT_COMPARE1A) |
2 | {
|
3 | TCCR1B = 0; //stop (no new interrupts please) |
4 | |
5 | disp_state ^= 0b1; |
6 | |
7 | if(!disp_state) { |
8 | PORTD = 0b1111111 | cur_playl; //Turn off all segments |
9 | PORTC = (0b100<<cur_digit); //Switch to next digit (no dots) |
10 | |
11 | OCR1A = 1000-(clk_settings[SET_BRIGHTNESS]*150); |
12 | } else { |
13 | PORTC ^= dispdots; //Set dots to state wanted |
14 | |
15 | blink_cnt++; |
16 | if(blink_cnt >= BLINKINTERVAL) { |
17 | cur_blinkstate = cur_blinkstate?0:1; |
18 | blink_cnt = 0; |
19 | }
|
20 | |
21 | if(!blink_mask[cur_digit] || cur_blinkstate) |
22 | PORTD = transtbl[dispcont[cur_digit]] | cur_playl; //Set the new segments |
23 | else
|
24 | PORTD = 0b1111111 | cur_playl; //We are Blinking and digit is currently off |
25 | |
26 | cur_digit = (cur_digit+1)%4; |
27 | |
28 | OCR1A = 63+(clk_settings[SET_BRIGHTNESS]*150); |
29 | }
|
30 | |
31 | TCNT1 = 0; |
32 | TCCR1B = 0b1010; //prescaler = 8 //TOP = OCR1A, update immediatly |
33 | }
|
clk_settings[SET_BRIGHTNESS] kann man von 0 bis 5 einstellen
Sorry, für C bin ich nicht zuständig, ich werkele in Assembler. ...
Also, ich habe jetzt den Fehler gefunden. Das leichte Flackern lag naemlich gar nicht an diesem Interrupt sondern an einem anderen, der wirklich zu lang war, den ich aber leider auch nicht so einfach auslagern konnte. Also habe ich den langen jetzt mit sei() "nestable" gemacht und es klappt wunderbar. Ich habe es aber trotzdem noch mit zwei Compares (A und B) geloest. Ist einfach schoener
Man kann in der ISR ein Flag (Semaphore) setzen und dieses in der Mainloop abfragen und bei Bedarf den (angemeldeten) Job erledigen. Damit bekommt man die ISRs schön klein und schnell. Das Freigeben des Interruptes in der ISR sollte man nur in Ausnahmefällen anwenden, da es recht störanfällig ist und zu Stackfehlern führen kann. Meist gibt es eine bessere Lösung, nämlich kurze schnelle ISRs. ...
Also das geht eigentlich auch ohne Software ganz gut. Da Du ja eine Matrix verwendest, hast Du alle Segmente auf einem Treiber und alle Spalten auf einem Anderen. Nun müsstest Du also nur die Spalten Treiber noch mal über einen gemeinsamen FET schalten, der von einem der PWM Generatoren geschaltet wird. Wenn Du für die PWM ein vielfaches Deiner Spaltenfortschaltung verwendest kannst Du ohne Software Aufwand das ganze Display in vielen Stufen dimmen. Du musst nur das PWM Verhältnis durch das setzen des PWM Registers verändern. Du müsstest lediglich einen LDR oder eine Photodiode an einen der ADC Eingänge klemmen. Da gehen dann auch komplexere Dinge, wie z.B. eine verzögerte Nachführung der Helligkeit oder eine Änderung der Helligkeit bei Alarm oder was auch immer. Gruß, Ulrich
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.