Forum: Mikrocontroller und Digitale Elektronik Nested Interrupt - Atmega2560


von Sefco (Gast)


Lesenswert?

Guten Morgen,

ich nutze einen Atmega2560 in einem Modellbauauto mit 16 MHz.
Dieser uC steuert mein Lenkservo mit einer PWM, meinen Motor mit einer 
PWM und nimmt über SPI mit einem Funkmodul Befehle von meiner 
Fernsteuerung entgegen um mein Auto fahren zu lassen.

Diese zeitkritischen Funktionen sind komplett in Interrupts realisiert, 
weil ich die while(1)-Schleife zum Steuern diverser LEDs mit Delays 
brauche.

Leider ist hierdurch die ISR, die die Daten aus dem Fifo meines 
Funkmoduls liest und diese in eine PWM-Breite für Servo und Motor 
umrechnet, etwas länglich. Die ISR dauert auf 16 MHz irgendwas im 
kleinen, zweistelligen us Bereich.

Dies führt dazu, dass die Timer ISRs manchmal auflaufen und meine Servo 
PWM leicht verreißen. Das Servo zuckt dann kurz.

Mit diesem Bastelprojekt komme ich an die Grenzen eines Atmega2560 auf 
16 MHz. Mein Problem ließe sich lösen, wenn die längliche ISR (und nur 
diese!) durch nested Interrupts weitere ISRs zulässt. Meine übrigen ISRs 
sind alle kurz.

Ich habe den Parameter ISR_NOBLOCK übergeben was dazu führt, dass mein 
uC die while(1)-Schleife nicht mehr anläuft.

Kann das überhaupt so funktionieren, dass eine ISR weitere ISRs zulässt? 
Habe ich wenn die while(1)-Schleife nicht mehr angelaufen wird sofort 
einen Stack-Overflow?

Herzlichen Dank!

von Peter D. (peda)


Lesenswert?

Die typischen Dateninterrupts (UART, SPI, I2C) dürfen nicht Interrupts 
enablen, da sie ihr Flag nicht beim Eintritt automatisch löschen, d.h. 
sie unterbrechen sich sofort selber, bis der Stack überläuft. Der SRAM 
wird quasi durch den Wolf gedreht.

Du mußt also erst den betreffenden Dateninterrupt disablen und dann erst 
die Interrupts wieder global enablen. Und am Ende dann umgekehrt.

Clever wäre von den AVR-Entwicklern gewesen, daß jeder Interrupt, der 
sein Pending-Bit nicht beim Eintritt löscht, dann dafür einfach sein 
Enable-Bit löscht. Aber soweit haben sie leider nicht gedacht.

von Trumpeltier (Gast)


Lesenswert?

So etwas geht, dann solltest Du aber wissen, was Du tust. Ich habe es in 
einem Projekt so gelöst, daß die erlaubten Interrupts das 
Status-Register nicht beeinflussen können.
Das sieht dann in etwa so aus:
1
ISR (TIMER1_COMPA_vect) {
2
  TIMSK &= ~(1 << OCIE1A);
3
  if( UCSRB & (1 << UDRIE) ) {
4
    UCSRB &= ~(1 << UDRIE);
5
    statusbyte.udr_ie = 1;
6
  }
7
  sei();
8
9
...viiiel Code
10
11
  cli();
12
  TIMSK |= (1 << OCIE1A);                   // Enable compare match interrupt.
13
  if( statusbyte.udr_ie ) {
14
    UCSRB |= (1 << UDRIE);
15
  }
16
17
  statusbyte.udr_ie = 0;
18
}
19
20
ISR (TIMER0_COMPA_vect, ISR_NAKED) {
21
  USICR |= (1 << USITC);                    // Toggle clock output pin for USI
22
  asm volatile ( "reti" );
23
}
24
25
ISR (USI_OVERFLOW_vect, ISR_NAKED) {
26
  asm volatile (                            // disable Timer0 OCR0 Interrupt without affecting the SREG register
27
    "push r16"    "\n\t"                    // store the auxiliary register in the stack memory
28
    "ldi r16, %0" "\n\t"                    // load new value for TIMSK to auxiliary register...
29
    "out %1, r16" "\n\t"                    // and put to TIMSK
30
    "pop r16"     "\n\t"                    // restore the auxiliary register
31
    :
32
    : "M" ((0 << OCIE0A) | \
33
           (1 << OCIE1A)),
34
      "M" (_SFR_IO_ADDR (TIMSK))
35
  );
36
  USISR |= (1 << USIOIF);                   // clear USI counter overflow interrupt flag
37
  statusbyte.usi_spi_tx = IDLE;
38
  asm volatile ( "reti" );
39
}
40
41
ISR (INT0_vect, ISR_NAKED) {
42
  statusbyte.zerocross = true;
43
  asm volatile ( "reti" );
44
}

Wie man sehen kann, wird im Timer1-Interrupt alles außer dem 
UART-Interrupt zugelassen. Die erlaubten Interrupts sind dann aber schön 
knackig kurz.
Es schwirrt hier auf dieser Seite auch irgend ein Tutorial rum, mußt Du 
mal danach suchen, ich bin gerade zu faul dazu. ;-)

von Stefan E. (sternst)


Lesenswert?

Sefco schrieb:
> Mein Problem ließe sich lösen, wenn die längliche ISR (und nur
> diese!) durch nested Interrupts weitere ISRs zulässt. Meine übrigen ISRs
> sind alle kurz.

Die deutlich bessere Lösung wäre, das Konzept etwas zu ändern.

Sefco schrieb:
> weil ich die while(1)-Schleife zum Steuern diverser LEDs mit Delays
> brauche.

Nimm die Delays da raus. Man kann z.B. auch einen Hardware-Zähler 
auslesen, und damit entscheiden, ob eine LED nun lange genug an oder aus 
war.

Sefco schrieb:
> Leider ist hierdurch die ISR, die die Daten aus dem Fifo meines
> Funkmoduls liest und diese in eine PWM-Breite für Servo und Motor
> umrechnet, etwas länglich.

Verschiebe das in die Hauptschleife. Setze in der ISR nur ein 
Beim-Funkmodul-stehen-Daten-zur-Verfügung-Flag.

von Sefco (Gast)


Lesenswert?

> Du mußt also erst den betreffenden Dateninterrupt disablen und dann erst
> die Interrupts wieder global enablen. Und am Ende dann umgekehrt.

Genau daran habe ich auch gedacht. Müsste das Ganze dann so gehen?
1
ISR(INT7_vect)
2
{
3
    // Disable external interrupt 
4
    EIMSK &= ~(1<<INT7);
5
6
    sei();
7
8
    // My Code            
9
}


> Die deutlich bessere Lösung wäre, das Konzept etwas zu ändern.

Es geht hier konkret um ein selbstgebautes Modell einer amerikanischen 
Fire Engine. Ich habe an dem Teil über 50 LEDs, die alle 
unterschiedliche Blitzen sollen mir Doppel- und Dreifachblitzen etc. So 
wie in der Realität. Sowas bekommt man mit Timern kaum hin.

von Stefan E. (sternst)


Lesenswert?

Trumpeltier schrieb:
> Die erlaubten Interrupts sind dann aber schön
> knackig kurz.

Und sie sind fehlerhaft, weil sie den Inhalt von Registern verändern 
ohne sie zu sichern und wiederherzustellen.

von Trumpeltier (Gast)


Lesenswert?

Ne gute Idee wäre, das globale Interrupt-Flag wieder zu setzen und den 
externen Interrupt wieder zuzulassen. Also in etwa so:
1
ISR(INT7_vect)
2
{
3
    // Disable external interrupt 
4
    EIMSK &= ~(1<<INT7);
5
6
    sei();
7
8
    // My Code
9
10
    cli();
11
    EIMSK |= (1<<INT7);          
12
}
@Stefan Ernst: Sind sie nicht, denn statusbyte."irgendwas" bezieht sich 
auf GPIOR0. Es werden nur Bits gesetzt oder gelöscht.

von Sefco (Gast)


Lesenswert?

Wieso noch ein cli() am Ende?

von Peter D. (peda)


Lesenswert?

Sefco schrieb:
> Wieso noch ein cli() am Ende?

Weil ein Interrupt hinter der letzten sichtbaren Zeile nicht zuende ist 
sondern erst noch seinen Epilog (nen Haufen POPs) ausführen muß und sich 
dabei wieder selber unterbrechen könnte.

von Trumpeltier (Gast)


Lesenswert?


von Sefco (Gast)


Lesenswert?

> Jetzt habe ich doch mal für Dich gesucht.
> https://www.mikrocontroller.net/articles/AVR-GCC-T...

Vielen Dank! Das habe ich selber nicht gefunden, weil ich nach "nested" 
gesucht habe :P

von Sefco (Gast)


Lesenswert?

Alternativ habe ich noch überlegt, den 16 MHz Quarz mal ganz unauffällig 
durch 20 MHz zu ersetzen. Laut Google haben das schon einige Leute 
gemacht und es sollte wunderbar klappen. Hat da jemand Erfahrungen 
gemacht?

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Stefan E. schrieb:
> Nimm die Delays da raus. Man kann z.B. auch einen Hardware-Zähler
> auslesen, und damit entscheiden, ob eine LED nun lange genug an oder aus
> war.

Delays in der Hauptschleife sind unkritisch - sie sind jederzeit 
unterbrechbar und kein Hindernis für die Ausführung von Interrupts.

Sefco schrieb:
> Dies führt dazu, dass die Timer ISRs manchmal auflaufen und meine Servo
> PWM leicht verreißen. Das Servo zuckt dann kurz.

Da liegt m.E. das Problem. Die PWM Erzeugung sollte besser komplett in 
Hardware laufen, so das gar keine ISR benötigt wird. Mit 16-Bit Timern 
sollte das in genügender Auflösung zu lösen sein, ohne eine ISR in 
Anspruch zu nehmen.
Weiterhin ist es natürlich nicht dumm, die Sache mit dem 
'SPI-Umrechnen-auf-PWM' nochmal zu optimieren.

Sefco schrieb:
> Hat da jemand Erfahrungen
> gemacht?

Das geht auf jeden Fall, solange du den MC mit ausreichender Spannung 
versorgst. Du verletzt bei 5V ja keine Specs oder so. Mega328 mit 
19,6MHz läuft jedenfalls wie Schmidts Katze.

: Bearbeitet durch User
von Stefan E. (sternst)


Lesenswert?

Sefco schrieb:
> Ich habe an dem Teil über 50 LEDs, die alle
> unterschiedliche Blitzen sollen mir Doppel- und Dreifachblitzen etc. So
> wie in der Realität. Sowas bekommt man mit Timern kaum hin.

Kein wirkliches Problem. Ein Timer, der den Basistakt vorgibt, dann für 
jede LED zwei Zähler und ein Array mit dem An-Aus-Muster.

Das mit Delays zu machen ist eine Frickellösung. Und eine Frickellösung 
zieht dann die nächste (nested Interrupts) nach sich. Und wenn du dann 
noch irgendeine Funktionalität integrieren willst, endet das Ganze 
früher oder später im Chaos.

von Felix F. (wiesel8)


Lesenswert?

Sefco schrieb:
> Es geht hier konkret um ein selbstgebautes Modell einer amerikanischen
> Fire Engine. Ich habe an dem Teil über 50 LEDs, die alle
> unterschiedliche Blitzen sollen mir Doppel- und Dreifachblitzen etc. So
> wie in der Realität. Sowas bekommt man mit Timern kaum hin.
Wenn es mit while(1) und Delays funktioniert, dann funktioniert es schon 
3x besser wenn du lediglich ein besseres Systemdesign verwendest. Nutzt 
du dann noch die entsprechende HW (Timer, Interrupt etc), funktioniert 
es min. 10x besser ;)

Sefco schrieb:
> Alternativ habe ich noch überlegt, den 16 MHz Quarz mal ganz unauffällig
> durch 20 MHz zu ersetzen. Laut Google haben das schon einige Leute
> gemacht und es sollte wunderbar klappen. Hat da jemand Erfahrungen
> gemacht?
Ja, funktioniert. Dein Controller könnte aber vorzeitig ableben.

mfg

von S. Landolt (Gast)


Lesenswert?

Zum ATmega2560 kann ich nichts sagen, aber ein ATmega8515 läuft hier 
seit Jahren mit 22 MHz (+38 %) und ein ATmega1284P seit einiger Zeit mit 
25 MHz (+25 %), beide bei 5.3 V.

von Stefan E. (sternst)


Lesenswert?

Matthias S. schrieb:
> Stefan E. schrieb:
>> Nimm die Delays da raus. Man kann z.B. auch einen Hardware-Zähler
>> auslesen, und damit entscheiden, ob eine LED nun lange genug an oder aus
>> war.
>
> Delays in der Hauptschleife sind unkritisch - sie sind jederzeit
> unterbrechbar und kein Hindernis für die Ausführung von Interrupts.

Ohne die Delays hat er aber einen deutlich schnelleren Durchlauf der 
Hauptschleife und kann problemlos weitere Sachen dort mit aufnehmen, wie 
eben z.B. die Bearbeitung der empfangenen Funknachrichten.

von Sefco (Gast)


Lesenswert?

>Da liegt m.E. das Problem. Die PWM Erzeugung sollte besser komplett in
>Hardware laufen, so das gar keine ISR benötigt wird. Mit 16-Bit Timern
>sollte das in genügender Auflösung zu lösen sein, ohne eine ISR in
>Anspruch zu nehmen.

Ich brauche nicht einfach eine PWM, ich brauche eine PWM ihren 
Duty-Cycle abhängig von Lenkhebel ändert und eine Periode von 20 ms hat. 
Ist habe das schon soweit wie möglich in HW ausgelagert, aber an 
irgendeiner Stelle muss ich zumindest mal den Timer zurücksetzen oder 
den Wert für den Compare-Match ändern.

>Weiterhin ist es natürlich nicht dumm, die Sache mit dem
>'SPI-Umrechnen-auf-PWM' nochmal zu optimieren.

Das sollte ich tatsächlich nochmal versuchen. Eventuell berechne ich das 
auf dem Atmega2560 meiner Funke. Der hat nämlich langeweile.

>Kein wirkliches Problem. Ein Timer, der den Basistakt vorgibt, dann für
>jede LED zwei Zähler und ein Array mit dem An-Aus-Muster.

Puh...ich bin da ziemlich perfektionistisch und verändere meine Delays 
teilweise um 10 ms bei einzelnen LEDs. Das wird ja ein schönes gecode. 
Ich glaube da ändere ich lieber den Parameter in _delay_ms(xx) in der 
main. Ehrlich gesagt verstehe ich auch nicht so ganz wie du das meinst. 
Da muss ich ja zumindest im Timer eine Varibale/den Zählerstand 
vergleichen und dann ca. 50 LEDs abhängig davon schalten.

>Ja, funktioniert. Dein Controller könnte aber vorzeitig ableben.
Wieso? Hitze? Kann mir kaum vorstellen das der bei 4 MHz mehr so heiß 
wird. Außerdem kann man da noch einen Kühlkörper draufsetzen.

von Robin S. (der_r)


Lesenswert?


von Stefan E. (sternst)


Lesenswert?

Sefco schrieb:
> Puh...ich bin da ziemlich perfektionistisch und verändere meine Delays
> teilweise um 10 ms bei einzelnen LEDs.

Und? Dann lässt du halt deinen Basistakt 10ms lang sein.

Sefco schrieb:
> Das wird ja ein schönes gecode.

Wieso? Um eine Zeit um 10ms zu ändern, musst du nur einen Eintrag in 
einem Array verändern. Und du änderst damit dann auch wirklich nur das 
Verhalten dieser einen LED, denn ...

Sefco schrieb:
> Ich glaube da ändere ich lieber den Parameter in _delay_ms(xx) in der
> main.

Und änderst damit gleich das Verhalten aller LEDs. Denn alle LEDs, die 
dann gerade an sind, sind 10ms länger an, und alle die dann gerade aus 
sind, sind 10ms länger aus.

Sefco schrieb:
> Da muss ich ja zumindest im Timer eine Varibale/den Zählerstand
> vergleichen und dann ca. 50 LEDs abhängig davon schalten.

Das machst du dann in der Hauptschleife. Der Timer setzt nur ein Flag.

von Sefco (Gast)


Lesenswert?

Könntest du da Pseudo-Code mäßig ein Beispiel für 2 LEDs posten?

von DraconiX (Gast)


Lesenswert?

Sefco schrieb:
> Es geht hier konkret um ein selbstgebautes Modell einer amerikanischen
> Fire Engine. Ich habe an dem Teil über 50 LEDs, die alle
> unterschiedliche Blitzen sollen mir Doppel- und Dreifachblitzen etc. So
> wie in der Realität. Sowas bekommt man mit Timern kaum hin.

Gerade dafür eignen sich Timer überaus herausragend. Lass den Timer 
laufen, gebe deinen 50+ LEDs jeweils einen eigenen "Bereich" im Timer. 
Kostet dann kaum Rechenleistung, Platz, und die 50+ LEDs sind völlig 
unabhängig voneinander (nicht wie mit delays in der Main, wo eine 
Änderung gleich alle betrifft).

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Sefco schrieb:
> aber an
> irgendeiner Stelle muss ich zumindest mal den Timer zurücksetzen

Öhh, Timer zurücksetzen? Hast du dir evtl. mal den Fast PWM mit TOP in 
ICRn Modus angeschaut?
Gehen wir mal von 20 MHz Takt aus. Du setzt Timer 1,3,4 oder 5 (oder 
alle) auf Fast PWM Mode 14, setzt den Prescaler auf 8 und schreibst in 
ICRn eine 39999. (20Mhz/8/40000 = 50Hz = 20ms).

OCRA, OCRB und OCRC liefern dann Servopulse, wenn du die COM Bits 
richtig setzt. Ein Wert von 2000 in OCRn gibt einen 1ms Puls und ein 
Wert von 4000 einen 2ms Puls.
Da der Mega2560 4 solche Timer hat, kannst du also theoretisch 12 Servos 
rein in Hardware treiben. Du musst lediglich einen neuen OCR Wert 
reinschreiben, wenn du Zeit dafür hast und ein Servo eine neue Stellung 
einnehmen soll.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Sefco schrieb:
>> Jetzt habe ich doch mal für Dich gesucht.
>> https://www.mikrocontroller.net/articles/AVR-GCC-T...
>
> Vielen Dank! Das habe ich selber nicht gefunden, weil ich nach "nested"
> gesucht habe :P

ISR_NOBLOCK ist hier aber nur dann geeignet, wenn 100% sicher ist, dass 
die ISR sich nicht selbt unterbrechen kann.

Das obige Beispiel mit sei + cli und Löschen + Setzen des entsprechenden 
IRQ-Flags würde ich den Vorzug geben.

Am Ende der ISR kann auch getestet werden, ob ihr IRQ-Flag schon wieder 
ansteht.  Falls das der Fall ist, würde ein ISR-Epilog und dann direkt 
wieder ein ISR-Prolog folgen, was ja nach Komplexität der ISR viel Zeit 
verschwendet.  Stattdessen kann man in dem Falle dann auch einfach zum 
Anfang der ISR springen, was einen kompletten ISR-Frame (Prolog + 
Epilog) and Zeit einspart.

von Sefco (Gast)


Lesenswert?

> Um eine Zeit um 10ms zu ändern, musst du nur einen Eintrag in
> einem Array verändern. Und du änderst damit dann auch wirklich nur das
> Verhalten dieser einen LED, denn ...

> Gerade dafür eignen sich Timer überaus herausragend. Lass den Timer
> laufen, gebe deinen 50+ LEDs jeweils einen eigenen "Bereich" im Timer.
> Kostet dann kaum Rechenleistung, Platz, und die 50+ LEDs sind völlig
> unabhängig voneinander (nicht wie mit delays in der Main, wo eine
> Änderung gleich alle betrifft).

Könnt ihr mir bitte nochmal erklären wie das gemeint ist?
Wenn ich die Delays umgehen will, nutze ich einen Timer. Aber statt dem 
Delay habe ich dann in der while(1) jede Menge if-Abfragen die abhängig 
vom Timer Wert oder der Anzahl der Überläufe (die ich mitzähle) dann 
LEDs an und aus machen. Meint ihr das so?

Und was ist mit "Array" gemeint? Wie soll ich denn PORTB |= (1<<7) mit 
einem Array machen?

von Felix F. (wiesel8)


Lesenswert?

Schreib doch mal ganz genau wie die LEDs geschaltet werden sollen und in 
welchen Zeitabständen etc. oder poste den Code. Dann kann man auch eine 
vernünftige Antwort geben. Nicht jeder hier besitzt eine Glaskugel...

mfg

von Sefco (Gast)


Lesenswert?

Aktuell sieht das Ganze so aus:
1
[...]
2
    uint8_t led_delay = 50;
3
     
4
5
    uint8_t em_lights = 1;
6
     
7
    while (1)
8
    {
9
         
10
        if (em_lights == 1)
11
        {
12
     
13
            LED_BR_WHI1_ON;
14
         
15
            LED_BL_RED1_ON; 
16
            LED_BL_RED3_ON;
17
            LED_BR_YEL1_ON;
18
            ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
19
            _delay_ms(led_delay);///////////////////////////////////////////////////////////////////////////////////////////////////////////
20
            ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
21
 
22
            LED_BR_WHI1_OFF;
23
            LED_BL_WHI1_ON;
24
            LED_BL_YEL1_OFF;        
25
            ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
26
            _delay_ms(led_delay);///////////////////////////////////////////////////////////////////////////////////////////////////////////
27
            ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
28
            LED_BR_WHI1_ON;
29
            LED_BL_WHI1_OFF;        
30
         
31
            LED_BR_RED2_ON;
32
            LED_BL_RED1_OFF;    
33
            LED_BL_RED3_OFF;
34
            LED_BR_YEL1_OFF;    
35
            ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
36
            _delay_ms(led_delay);///////////////////////////////////////////////////////////////////////////////////////////////////////////
37
            ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
38
            LED_BL_WHI1_ON;
39
            LED_BR_RED1_ON;
40
            LED_BL_RED1_ON; 
41
            LED_BR_RED3_ON;
42
            LED_BL_RED3_ON;
43
            LED_BL_RED2_ON; 
44
            LED_BL_YEL1_ON;     
45
            ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
46
            _delay_ms(led_delay);///////////////////////////////////////////////////////////////////////////////////////////////////////////
47
            ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
48
49
            LED_BR_WHI1_OFF;
50
            LED_BR_RED2_OFF;
51
            LED_BL_RED1_OFF;    
52
            LED_BL_RED3_OFF;
53
            LED_BR_YEL1_ON; 
54
            ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
55
            _delay_ms(led_delay);///////////////////////////////////////////////////////////////////////////////////////////////////////////
56
            ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
57
            LED_BL_WHI1_OFF;
58
            LED_BR_RED1_OFF;
59
            LED_BR_RED2_ON;
60
            LED_BR_RED3_OFF;
61
            LED_BL_RED2_OFF;
62
            LED_BL_YEL1_OFF;        
63
            ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
64
            _delay_ms(led_delay);///////////////////////////////////////////////////////////////////////////////////////////////////////////
65
            ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
66
         
67
            LED_BR_RED1_ON;
68
            LED_BR_RED2_OFF;
69
            LED_BR_RED3_ON;
70
            LED_BL_RED2_ON; 
71
            LED_BR_YEL1_OFF;        
72
            ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
73
            _delay_ms(led_delay);///////////////////////////////////////////////////////////////////////////////////////////////////////////
74
            ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////        
75
         
76
            LED_BR_RED1_OFF;
77
         
78
            LED_BR_RED3_OFF;
79
         
80
            LED_BL_RED2_OFF;    
81
            LED_BL_YEL1_ON;
82
            ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
83
            _delay_ms(led_delay);///////////////////////////////////////////////////////////////////////////////////////////////////////////
84
            ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
85
        }
86
   }

Makros:
1
#define DDR_LED DDRB
2
#define PORT_LED PORTB
3
#define PIN_LED 7
4
#define LED_ON PORT_LED |= (1<<PIN_LED)
5
#define LED_OFF PORT_LED &= ~(1<<PIN_LED)

BR/BL = Backright/-left

von Peter D. (peda)


Lesenswert?

Sefco schrieb:
> Aktuell sieht das Ganze so aus:

Also keine PWM.
Dann sollte Dir das hier helfen:
Beitrag "Re: AVR Sleep Mode / Knight Rider"

von Felix F. (wiesel8)


Lesenswert?

Ohne dein aktuelles Programm groß zu ändern....
Gibt natürlich noch schönere oder bessere Lösungen ;)

(Pseudocode)
1
led_delay = 50;
2
state = 0;
3
count = 0;
4
5
void Task_LED()
6
{
7
    switch(state)
8
    {
9
     case 0:
10
        led1 = on;
11
        led2 = off;
12
        
13
        // next state
14
        state++;
15
        break;
16
        
17
     case 1:
18
        led1 = off;
19
        led2 = on;
20
        
21
        // next state
22
        state++;
23
        break;
24
        
25
     case 2:
26
        led3 = on;
27
        led4 = on;
28
        
29
        // next state
30
        state++;
31
        break;
32
        
33
     case 3:
34
     
35
     ...
36
     
37
     
38
    }
39
}
40
41
42
// 1ms timer ISR
43
ISR(...)
44
{
45
    if(count++ > led_delay) {
46
        count = 0;
47
        
48
        Task_LED();
49
    }
50
}

mfg

: Bearbeitet durch User
von Sefco (Gast)


Lesenswert?

Danke dir Felix! Diese Lösung gefällt mir und wird gleich direkt 
getestet. Trotzdem würde mich mal interessieren was die Kollegen da mit 
einem Array reißen wollten...

Kann man mit der Switch-Case Lösung auch eine PWM realisieren? Ich 
brauche nämlich bei manchen LEDs noch eine Software PWM.

von Ralf G. (ralg)


Lesenswert?

Sefco schrieb:
> Diese Lösung gefällt mir

...vertrödelt aber wieder Zeit (+ Flash) in der ISR!

'Task_LED()' gehört in die 'main()'. In der ISR wird nur ein Bit in 
einer globalen Variable ('StatusISR' oder so ähnlich, jede ISR bekommt 
da ein...zwei Bits zugewiesen) gesetzt, welches in der 'main()' in eine 
temporäre Variable kopiert wird. Mit Hilfe dieser 'copyStatusISR' 
(wieder: oder so ähnlich) werden dann die diversen Routinen aufgerufen. 
So, zum Beispiel, auch 'Task_LED()'.

Sichtbarkeit von Daten:
'count' gehört 'static' in die ISR. 'led_delay' würde ich auch so in die 
ISR integrieren bzw. nur als Makro definieren. 'state' entsprechend nach 
'Task_LED()'.

: Bearbeitet durch User
von Felix F. (wiesel8)


Lesenswert?

Ralf G. schrieb:
> Sefco schrieb:
>> Diese Lösung gefällt mir
>
> ...vertrödelt aber wieder Zeit (+ Flash) in der ISR!
>
> 'Task_LED()' gehört in die 'main()'. In der ISR wird nur ein Bit in
> einer globalen Variable ('StatusISR' oder so ähnlich, jede ISR bekommt
> da ein...zwei Bits zugewiesen) gesetzt, welches in der 'main()' in eine
> temporäre Variable kopiert wird. Mit Hilfe dieser 'copyStatusISR'
> (wieder: oder so ähnlich) werden dann die diversen Routinen aufgerufen.
> So, zum Beispiel, auch 'Task_LED()'.
>
> Sichtbarkeit von Daten:
> 'count' gehört 'static' in die ISR. 'led_delay' würde ich auch so in die
> ISR integrieren bzw. nur als Makro definieren. 'state' entsprechend nach
> 'Task_LED()'.

Leute wie du gefallen mir....
Nichts Beigetragen aber sofort kritisieren, dass Pseudocode nicht 
perfekt ist...

Und wer vertrödelt hier Zeit und Flash?
Du bist doch derjenige, der temporäre Variablen anlegen möchte und alles 
dauern in der Main pollen will. Bei meiner Lösung ist die Main aktuell 
komplett frei und es muss nichts gepollt werden. Die Task_LED wird nur 
aufgerufen, wenn sie auch benötigt wird.

Außerdem habe ich geschrieben, dass es besseres, schöneres gibt. Man 
könnte den Timer auch auf 10ms setzen, wenn die HW es mitmacht, dann ist 
noch weniger Last vorhanden. Für seinen Zweck ist das aber völlig 
ausreichend.

mfg

: Bearbeitet durch User
von Ralf G. (ralg)


Lesenswert?

Felix F. schrieb:
> Nichts Beigetragen aber sofort kritisieren
Bis zum 03.03. 16:15 war alles soweit okay, das muss ich doch nicht 
wiederholen.

Sefco schrieb:
> Dies führt dazu, dass die Timer ISRs manchmal auflaufen
                        ^^^          ^
@Felix F.
Hast du mal nachgerechnet, was der Funktionsaufruf in der ISR kostet?

von Felix F. (wiesel8)


Lesenswert?

Ralf G. schrieb:
> @Felix F.
> Hast du mal nachgerechnet, was der Funktionsaufruf in der ISR kostet?
Leider nicht, im aktuellen Atmelstudio wird die Funktion nämlich 
geinlined. Dementsprechend interessiere ich mich nicht dafür.

Aber du hast es doch sicher nachgerecht? Wie viele Befehle extra sind es 
denn? Vlt erweist sich das bei einem zukünftigen Projekt mal als 
nützlich :)

mfg

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.