Forum: Mikrocontroller und Digitale Elektronik Hilfe zu Interruptverhalten ATMEGA8 gesucht


von Jonas A. (Firma: GreenWire-Elektronik) (padrejohn)


Lesenswert?

Guten Abend liebes Forum,

ich sitze hier nun seit Tagen an einem Programm und ich komme einfach 
nicht dahinter, was schief läuft. Irgendwie beachte ich irgendeine 
Programmiervorschrift nicht - ich hoffe, ihr könnt mir auf die Sprünge 
helfen.

Ich habe zwei Probleme, beide haben mit Interrupts zu tun.
Zuersteinmal, ich nutze einen ATMEGA8-16 TQ und programmiere in der 
Platine mit nem AVRISP MK2. Meine Platine hat ein LCD tc1602a-09, ein 
25LC256 SPI Flash, ein Inkrementalgeber sowie einen Komparator bestückt, 
welcher einen Münzeinwurf detektiert.
Was passieren soll: Münze wird eingeworfen, Text vom Flash gelesen, 
ausgeben - und das alles in einer Schleife.
Beim Hochlauf des uC möchte ich vorher noch 2 Ausgaben allgemeiner Art 
machen, die eine kurze Zeit eingeblendet werden, bevor ich in die 
Münzschleife falle.
Relevant sind in meinem Fall nur TMR2 und der INT1 - TMR2 soll zu Beginn 
steuern, wie lang die Einblendungen sind; INT1 soll auf Low-Flanken für 
den Münzeinwurf reagieren.

Zu Beginn der main-function ist entsprechend folgende Init:
1
cli();
2
MCUCR |= (1 << ISC01) | (1 << ISC11);
3
TCCR2 |= (1 << CS22) | (1 << CS21) | (1 << CS20);
4
5
DDRD = 0xF0;
6
DDRB = 0xFF;
7
PORTB = 0x00;
8
DDRC = 0x3F;

Danach kommt noch ein wenig SPI-Init.
Nun kommt die LCD-Init und das erste mal Warten:
1
    lcd_clrscr();
2
    itoa(quote_count, buffer, 10);
3
    lcd_puts(buffer);
4
    lcd_puts(" Eintraege\ngefunden");
5
6
    TMR2_done = 0;
7
8
    TCNT2 = tmr0_preload;
9
    TIMSK |= (1 << TOIE2);
10
11
    sei();
12
13
    while(1) {
14
      nop();
15
      nop();
16
      nop();
17
      quote_count = scan_eeprom();
18
      if (TMR2_done)
19
        break;
20
      nop();
21
    }

Dazu die ISR
1
ISR( TIMER2_OVF_vect ) {
2
  cli();
3
  TIMSK &= (0 << TOIE2);
4
  TMR2_done = 1;
5
  sei();
6
}
TMR2_done wird global mit static int TMR2_done = 0; initialisiert. 
Gedacht war, dass die Anzeige soweit stehen bleibt, bis TMR2 abläuft und 
das Flag auf 1 setzt. Die nop()s sowie den SPI-Scanvorgang hab ich 
eingefügt, weil ich vermutet hatte, dass aus mir unerfindlichen Gründen 
die Schleife zu kurz zum Interrupten ist (das ist mehr Verzweiflung 
gewesen - Hinweis: SPI funktioniert ohne Ints). Leider bleibt der 
unendlich in der Schleife drin und ich weiß nicht wieso.
Was mich daran verwirrt, anschließend kommt die klassische 
while(1)-Schleife - innerhalb der Schleife funktioniert solch ein 
Konstrukt dann (es ist hier dann mit TMR1 gemacht) :

Direkt im Anschluß zum obrigen Code - einfach das Laden des Timers und 
die Warteschleife wegdenken
1
int init = 1;
2
while(1) {
3
   if (init) {        
4
      TMR1_done = 0;
5
      TCNT1H = 0xC0;        
6
      TCNT1L = tmr1l_preload;      
7
      TIMSK |= (1 << TOIE1);
8
      sei();
9
      init = 0;
10
   }
11
   if (scroll & TMR1_done) {
12
      ...

Kann mir das jemand erklären? Ich weiß nich obs daran liegt, dass ich 
das immer nach der Arbeit mach und die geistige Höhe fehlt oder es 
einfach zu lang her is - ich komm einfach nich drauf - und 
Debugmöglichkeiten bietet das AVRISP MK2 ja keine in dem Fall.
Eine hübschere Lösung wie diese hier funktioniert wiederrum nicht - 
warum auch immer -.-
1
if (init == 1) {
2
        if (TMR1_done == 1) {
3
          init = 0;
4
    lcd_gotoxy(0,0);
5
    lcd_clrscr();
6
                GICR |= (1 << INT1);  
7
  }
8
  else {
9
    TMR1_done = 0;
10
    TCNT1H = 0xC0;
11
    TCNT1L = tmr1l_preload;
12
    TIMSK |= (1 << TOIE1);
13
    sei();
14
  }
15
}
TMR1_done wird mit 0 initialisiert.

Mein zweites Problem habe ich mit INT1. Wenn die Init durch ist, wird 
INT1 aktiviert und in der Hauptschleife wird auf das Signal scroll 
gewartet. Ablauf: Wenn scroll == 1, Lade den Text, Gebe ihn aus, lösche 
Signal scroll - warte auf INT1.
Nach dem Auslösen von INT1 wird INT1 deaktiviert und nach Löschen des 
Signals wieder aktiviert - ich kann in der Zeit mit den Ints eh nichts 
anfangen.
Deaktiviert wird der Interrupt mit GICR &= (0 << INT1);
Die ISR ist auch denkbar einfach:
1
ISR( INT1_vect ) {
2
  cli();
3
  GICR &= (0 << INT1);
4
  scroll = 1;
5
  sei();
6
  return;
7
}

Ich hoffe ihr könnt mir helfen - ich dreh mich im Kreis :(

Vielen Dank

Gruß

Jonas

von Karlheinz (Gast)


Lesenswert?

Hallo,

geh dein Programm nochmal in aller Ruhe durch und überlege jede Zeile

z.B haben in kurzen ISR cli und sei nichts zu suchen
und was tut das return; da

Jonas Arndt schrieb:
> ISR( INT1_vect ) {
>   cli();
>   GICR &= (0 << INT1);
>   scroll = 1;
>   sei();
>   return;
> }

auch eine while-Schleife mit

Jonas Arndt schrieb:
> if (TMR2_done)
>         break;

zu beenden ist suboptimal

von Karl H. (kbuchegg)


Lesenswert?

1
static int TMR2_done = 0;

das muss ein
1
volatile int TMR2_done = 0;
ein.

FAQ: Was hat es mit volatile auf sich


und nächstes mal zeig bitte deinen ganzen Code im Zusammenhang. Auch 
Details, wie hier das volatile, können und sind wichtig.

von Karl H. (kbuchegg)


Lesenswert?

1
  TIMSK &= (0 << TOIE2);

so werden keine Bits gelöscht.

Bitmanipulation


> Deaktiviert wird der Interrupt mit GICR &= (0 << INT1);

Du hättest genausogut
1
  GICR = 0;
schreiben können. Denn genau darauf läuft deine Anweisung hinaus.


Hinweis: ein
 0<<irgendwas
ist niemals sinnvoll. Denn eine binäre 0 kannst du nach links schieben 
so oft du willst. Das Ergebnis ist trotzdem wieder 0. 0 mal irgendeine 
Zahl ist nun mal 0.

von Karl H. (kbuchegg)


Lesenswert?

1
    while(1) {
2
      nop();
3
      nop();
4
      nop();
5
      quote_count = scan_eeprom();
6
      if (TMR2_done)
7
        break;
8
      nop();
9
    }

der ganze Ansatz mit Warten ist verquirxt. Das ist nix anderes als eine 
Warteschleife. Nur möglichst kompliziert gemacht.

Das hier ....
1
  for( i = 0; i < 100; ++i )
2
  {
3
    quote_count = scan_eeprom();
4
    _delay_ms( 100 );
5
  }
(Ich hab die Zeit jetzt nicht nachgerechnet)
... würde genau dasselbe machen. Nur einfacher und durchsichtiger.

von Karl H. (kbuchegg)


Lesenswert?

1
ISR( TIMER2_OVF_vect ) {
2
  cli();
3
  TIMSK &= (0 << TOIE2);
4
  TMR2_done = 1;
5
  sei();
6
}

in einer ISR haben cli() und sei() nichts verloren.
Wenn die ISR betreten wird, sind die Interrupts sowieso abgeschaltet und 
beim Verlassen der ISR werden sie wieder aktiviert. Der sei() in deiner 
ISR ist ein effektives Problem, weil die Interrupts dadurch einen Tick 
zu früh aktiviert werden.

von Karl H. (kbuchegg)


Lesenswert?

1
Mein zweites Problem habe ich mit INT1.

wozu brauchst du zum banalen Abfragen eines Eingangs einen Interrupt. 
Deine Münze wird wohl kaum so schnell fallen, dass deiner µC das 
verpassen könnte.

Generell: Wenn es nicht unbedingt sein muss (weil man sonst mit der 
Eingabepulsdauer nicht hin kommt) - keine externe Interrupts für 
Eingabepins. Letzten Endes verkompliziert das alles nur und du kriegst 
nichts dafür.

von Karl H. (kbuchegg)


Lesenswert?

1
    TCNT1H = 0xC0;
2
    TCNT1L = tmr1l_preload;

wenn du solche Dinge hast, in denen 2 Register zusammengehören und 
logisch gesehen ein 16 Bit Register bilden, aber aus technischen Gründen 
als 2 Stück 8 Bit Register ausgeführt sind, dann nimmt der der Compiler 
die Arbeit des Auteils ab. Du kannst ganz einfach schreiben
1
  TCNT1 = 400;
und der Compiler sorgt dafür, dass die Zahl entsprechend in Bytes 
zerlegt und in die richtigen Register bugsiert wird.
Lass sowas den Compiler machen, denn bei manchen derartigen 
Registerpärchen ist die Reihenfolge in der die Zuweisungen gemacht 
werden wichtig!

von Karl H. (kbuchegg)


Lesenswert?

Wenn du Dinge wie einen Automaten nachprogrammierst, dann bietet sich 
ein Ansatz an, den man in der Informatik eine "Zustandsmaschine" nennt.
Statemachine

Die Idee hinter so einer abstrakten Maschine ist es genau, einen 
dertigen Automaten nachzubauen, bzw. den Lösungsansatz seiner 
Problemstellung in so etwas zu überführen.
Daher eignen sich derartige Dinge ganz hervorragend, um mit diesem 
Mechanismus abgebildet zu werden.

von isnah (Gast)


Lesenswert?

Hoffentlich goutiert Jonas Arndt beim Morgenessen dieses 
"Privat-Tutorial".
Gratulation zu dieser engagierten Forumsbetreuung.
isnah

von Jonas A. (Firma: GreenWire-Elektronik) (padrejohn)


Lesenswert?

isnah schrieb:
> Hoffentlich goutiert Jonas Arndt beim Morgenessen dieses
> "Privat-Tutorial".
> Gratulation zu dieser engagierten Forumsbetreuung.
> isnah

In der Tat bin ich überwältigt von euren Beiträgen - Vielen lieben Dank!

Die meisten Optimierungshinweise hatte ich erwartet, vorallem der Tipp 
mit dem Zustandsautomaten - diese Dinge sind mir bewusst, nur ist dieses 
Programm zur Inbetriebnahme der Hardware gedacht gewesen - daher hatte 
ich noch keinen saubereren Ansatz gewählt.

Ich merke an den ganzen kleinen Hinweisen, dass es einfach verdammt lang 
her ist mit dem Zeug - und sich wohl Dinge von ASM und C gemischt haben, 
vorallem beim deaktivieren/aktivieren der INTs in den ISRs.

Wenn ich heute abend wieder von Arbeit komm, werd ich nochmal näher 
drauf eingehen - nochmals vielen lieben Dank!

Gruß

Jonas

von Wolfgang H. (frickelkram)


Lesenswert?

Hi Jonas,
Karl Heinz hat ja schon auch auf viele Code-Probleme hin gewiesen. 
Generell solltest Du Dir aber Gedanken über die grundsätzliche 
Programmstruktur machen (dabei spielt es übrigens keine Rolle ob Du in 
Assembler oder C programmierst).
Die Ausgabe am Start kannst Du z.B. ohne jegliche ISR machen. Warte doch 
einfach in einer Delay-Schleife nach der Ausgabe. Es handelt sich ja um 
eine Statusanzeige, unmittelbar nach dem Einschalten. Oder erwartest Du 
dann schon das jemand eine Münze ein wirft und der Automat darauf 
reagieren muss?
Was die Münzerkennung angeht ist es wirklich besser keine ISR Routine zu 
verwenden. Nutze den Timer als Taktquelle mit konstanter Frequenz. Du 
kannst in der ISR (gesteuert durch den Taktgeber, nicht den 
Port-Eingang) eine Pin-Abfrage per Polling machen 
(https://www.mikrocontroller.net/articles/Entprellung). Das funktioniert 
zuverlässig und Du hast gleichzeitig eine Zeitbasis mit der Du z.B. LED 
blinken lassen, oder die LCD-Anzeige Texte abwechselnd darstellen lassen 
kannst. Die ISR ist dabei immer nur der Verwalter der Ereignisse. Da die 
Verweildauer der CPU in der ISR kurz sein sollte werden hier keine 
langen Berechnungen oder Schleifen bearbeitet.
Deine Main-Routine wertet die Ereignisse aus und führt dann die Arbeiten 
aus, enthält also den programmierten Automaten.

von Karl H. (kbuchegg)


Lesenswert?

Jonas Arndt schrieb:

> Die meisten Optimierungshinweise

Optimierung?
Du hast das falsch verstanden. Da gehts nicht um 'Optimierung'. Die 
meisten gemeldeten Dinge sind ernsthafte Probleme!

von Peter D. (peda)


Lesenswert?

Jonas Arndt schrieb:
1
    while(1) {
2
      nop();
3
      nop();
4
      nop();
5
      quote_count = scan_eeprom();
6
      if (TMR2_done)
7
        break;
8
      nop();
9
    }

Verrate mir dochmal, was die vielen NOPs sollen.

Wirst Du nach Zeilen bezahlt oder nach Flashverbrauch?

von Wolfgang H. (frickelkram)


Lesenswert?

Hallo Karl-Heinz,

Karl Heinz Buchegger schrieb:
> Jonas Arndt schrieb:
>
>> Die meisten Optimierungshinweise
>
> Optimierung?
> Du hast das falsch verstanden. Da gehts nicht um 'Optimierung'. Die
> meisten gemeldeten Dinge sind ernsthafte Probleme!

Optimierung wahr sicherlich das falsche Wort, enschuldige bitte.

von Wolfgang H. (frickelkram)


Lesenswert?

Hi Peter,

Peter Dannegger schrieb:
> Jonas Arndt schrieb:
>
1
>     while(1) {
2
>       nop();
3
>       nop();
4
>       nop();
5
>       quote_count = scan_eeprom();
6
>       if (TMR2_done)
7
>         break;
8
>       nop();
9
>     }
10
>
>
> Verrate mir dochmal, was die vielen NOPs sollen.
>
> Wirst Du nach Zeilen bezahlt oder nach Flashverbrauch?

er hat es oben im Text angedeutet. Ich verbuche das einfach unter 
"Verzweiflungstat". Das ist aber sicher das kleinste Problem.

Danke übrigens für Deine Entprellroutine!
Ich verwende die C-Variante sehr gerne und erfolgreich. Die Kritik an 
der Komplexität kann ich nicht nach vollziehen. Der Code ist vielleicht 
nicht auf Anhieb einfach zu verstehen, aber man kann ihn gut und einfach 
einsetzen, auch wenn man nur eine einzige Taste abfragt und die Repeat 
Funktion nicht benötigt.

von Jonas A. (Firma: GreenWire-Elektronik) (padrejohn)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Jonas Arndt schrieb:
>
>> Die meisten Optimierungshinweise
>
> Optimierung?
> Du hast das falsch verstanden. Da gehts nicht um 'Optimierung'. Die
> meisten gemeldeten Dinge sind ernsthafte Probleme!

Hallo Karl-Heinz,

das sollte keine Geringschätzung sein - ich werde die Tipps alle 
mitnehmen und hoffentlich heute Abend umsetzen. D.h. ich werd das 
Programm fix neuschreiben, es ist ja überschaubar. Ich wollte nur 
andeuten, dass vorallem Dinge wie der Programmablauf zum einen dem 
geschuldet sind, dass das Programm selbst einfach wuchs, um die 
einzelnen Hardwareteile zu testen, zum anderen sind Teile wie die nop()s 
wie oben beschrieben dann durch die Verzweiflung eingezogen - 
klassisches Trial'n'Error.

Was mich aber weiterhin wundert, dass man hier immer wieder liest, bloß 
keine Interrupts zu verwenden. Wenn ich beim Beispiel mit dem 
Münzeinwurf bleibe - Ich habe einen Zustandsautomaten im Zustand 
"Münzeinwurf abwarten" - warum sollte ich nun nicht das Steuersignal für 
diesen Zustand durch nen ISR steuern? Nichts anderes sollte das werden.
Die Timer nutz ich zum Teil als Taktgeber schon - wenn ich 
beispielsweise die Buchstaben übers Display scrolle. Bin aber selbst 
wenig zufrieden mit den Timern - ein Kahlschlag und Neukonzept sollte 
das Richtige sein :)


Bis heute Abend

Besteht weiterhin der Wunsch, das jetztige komplette Programm 
anzuhängen?

@Peter - Wie Wolfgang schon schrieb, ich hatte es oben erklärt - es war 
einfach ne Idee um etwas ausschließen zu können.

von Karl H. (kbuchegg)


Lesenswert?

Jonas Arndt schrieb:

> Was mich aber weiterhin wundert, dass man hier immer wieder liest, bloß
> keine Interrupts zu verwenden.

Das ist so nicht richtig.
Interrupts schon. Aber die richtigen.

externe Interrupts braucht man selten. Eigentlich nur dann, wenn man 
wirklich in µs bzw. Bruchteilen davon auf ein Signal reagieren muss, 
bzw. wenn die Pulslänge sich in diesem Bereich abspielt.

Als Faustregel: Alles was mit Mensch zu tun hat, sei es Tastendruck oder 
sonst irgendwas banal mechanisches, ist so langsam, dass es aus Sicht 
des µC einer extremen Superzeitlupe gleichkommt.

> Wenn ich beim Beispiel mit dem
> Münzeinwurf bleibe - Ich habe einen Zustandsautomaten im Zustand
> "Münzeinwurf abwarten" - warum sollte ich nun nicht das Steuersignal für
> diesen Zustand durch nen ISR steuern?

Weil es das Problem nicht vereinfacht. Was bringt dir ein Interrupt 
wenn, um beim Beispiel zu bleiben, es ein
1
   while( PINB & ( 1 << PB0 ) )
2
     ;
auch tut um darauf zu warten, dass der Pin auf 0 geht?


oder eben im Falle einer Statemachine eine entsprechende Abfrage samt 
Verzweigung in einen anderen Zustand?
1
  switch State
2
  {
3
    case State1:
4
      ...
5
      if( PINB & ( 1 << PB0 ) )    // AH Benutzer hat gedrückt
6
        State = State2;
7
      ...

Was genau macht dir da ein externer Interrupt auf der Taste einfacher?

> Nichts anderes sollte das werden.
> Die Timer nutz ich zum Teil als Taktgeber schon - wenn ich
> beispielsweise die Buchstaben übers Display scrolle. Bin aber selbst
> wenig zufrieden mit den Timern - ein Kahlschlag und Neukonzept sollte
> das Richtige sein :)

Praktisch jedes ernsthafte Programm benötigt sowas wie einen internen 
Taktgeber in Form eines Timers, der zb im Millisekundenabstand eine ISR 
aufruft. Und das ist dann auch der perfekte Platz um dort eine 
Tastenabfrage und Entprellung unterzubringen

Siehe Entprellung
(Komfortroutinen)

Stellt man das ganze so ein, dass die ISR alle 5ms kommt (hier gehts um 
die Größenordnung. ob das 5 oder 10ms sind, ist hingegen wieder 
wurscht), dann übersieht diese Entprellung keinen Tastendruck. So 
schnell bist du als Mensch nicht, dass du eine Taste schnell genug 
drücken und wieder loslassen könntest, dass ein µC der 200 mal pro 
Sekunde einen Blick auf den Portpin wirft, das nicht mitbekommen würde.

Und ja: Man kann durchaus in ein und derselben ISR auch mehrere 
Aufgabenstellungen behandeln. Hab ich eine ISR, die alle 5ms kommt, dann 
kann ich neben der Entprellung zb darauf aufbauend auch noch eine Uhr 
machen, oder Ablauftimer oder eben auch Display Scrollen.

von Karl H. (kbuchegg)


Lesenswert?

Karl Heinz Buchegger schrieb:

>> Wenn ich beim Beispiel mit dem
>> Münzeinwurf bleibe - Ich habe einen Zustandsautomaten im Zustand
>> "Münzeinwurf abwarten"

Nenne den Zustand um in 'Grundzustand'.
Denn dann ist völlig klar, dass ein Münzeinwurf nichts anderes ist, als 
eine Möglichkeit wie die Zustandsmaschine aus diesem Grundzustand heraus 
in einen anderen Zustand übergeht.
D.h. die Betonung liegt nicht mehr darauf, dass hier auf einen 
Münzeinwurf gewartet wird.
Es kann ein Münzeinwurf vorkommen.
Es könnte aber auch ein Timer ablaufen, der dem Zustand signalisiert den 
Text im Display um 1 Stelle weiterzurücken.
Es könnte auch jemand auf eine Taste gedrückt haben um sich zu 
informieren, was denn der Kaffee kostet.

d.h. an dieser Stelle wird nicht explizit auf einen Münzeinwurf 
gewartet, sondern ein Münzeinwurf ist EINE Möglichkeit, was in diesem 
Zustand an Ereignissen passieren kann. Eine Möglichkeit, aber beileibe 
nicht die einzige.

Wenn geht, vermeide auch umgangssprachlich das Wort 'warten'. Denn es 
führt dich oft in die falsche Richtung. Dein Programm wartet nicht. 
Sondern es ist in einem Zustand und durch Ereignisse kann es in andere 
Zustände übergehen.

von Peter D. (peda)


Lesenswert?

Jonas Arndt schrieb:
> @Peter - Wie Wolfgang schon schrieb, ich hatte es oben erklärt - es war
> einfach ne Idee um etwas ausschließen zu können.

Was erwartest Du denn, was ein bzw. sogar 3 NOPs ausschließen könnten?
Wenn ich Code einfüge, dann muß ich mir dabei doch was gedacht haben.
Auch beim Debuggen sollte man das Gehirn nicht total abschalten.

Bei erfahrenen Programmierern wirst Du kaum ein NOP finden. Das NOP ist 
die mit Abstand am wenigsten genutzte Instruktion.

In früheren Zeiten gab es Lochkarten als Speicher, da konnte man 
fehlerhaften Code durch NOPs ersetzten. Die NOPs dienten als 
Platzhalter, damit Sprünge und Calls weiterhin auf das richtige Ziel 
zeigten und nicht sämtliche Karten nochmal neu gestanzt werden mußten.
Heutzutage ist das NOP aber fast ausgestorben.

von Oliver (Gast)


Lesenswert?

Na ja, so mancher Computervirus braucht jede Menge davon. Stichwort 
"Nop-Rutsche" ;)

Auf einem AVR benötigt man NOPs immer noch genau dann, wenn man 
zyklengenau Zeit vebraten muß. Ab und an kommt das mal vor.

Oliver

von Jonas A. (Firma: GreenWire-Elektronik) (padrejohn)


Lesenswert?

Peter Dannegger schrieb:
> Jonas Arndt schrieb:
>> @Peter - Wie Wolfgang schon schrieb, ich hatte es oben erklärt - es war
>> einfach ne Idee um etwas ausschließen zu können.
>
> Was erwartest Du denn, was ein bzw. sogar 3 NOPs ausschließen könnten?
> Wenn ich Code einfüge, dann muß ich mir dabei doch was gedacht haben.
> Auch beim Debuggen sollte man das Gehirn nicht total abschalten.
>
> Bei erfahrenen Programmierern wirst Du kaum ein NOP finden. Das NOP ist
> die mit Abstand am wenigsten genutzte Instruktion.
>
> In früheren Zeiten gab es Lochkarten als Speicher, da konnte man
> fehlerhaften Code durch NOPs ersetzten. Die NOPs dienten als
> Platzhalter, damit Sprünge und Calls weiterhin auf das richtige Ziel
> zeigten und nicht sämtliche Karten nochmal neu gestanzt werden mußten.
> Heutzutage ist das NOP aber fast ausgestorben.

Es war wie gesagt schon recht verzweifelt - im Übrigen habe ich damals 
auf dem 8052 von Atmel einen Bug gehabt, der sich eben genau mit dem 
Einfügen von NOPs beheben ließ - allerdings in ASM, aber gut.

Ich hab den Wink mit dem Zaunpfahl schon verstanden, mich mal wieder auf 
alle brav gelernten Programmierstrukturen zu besinnen - ich hoffe ich 
werde die Erwartungen entsprechend mit der nächsten Programmversion 
erfüllen.

von Jonas A. (Firma: GreenWire-Elektronik) (padrejohn)


Angehängte Dateien:

Lesenswert?

Guten Abend liebe Forumsleser,

so, ich habs fertig, das neue Programm - es funktioniert wunderbar. Eine 
Funktion get_actualquote() muss noch implementiert werden - das mach ich 
später, ich brauch nach dem Erfolg erstmal ein wenig was zu essen.

Ich hänge das Programm mal an, würde mich  über Kritik und Hinweise 
natürlich sehr freuen.

Vielen Dank nochmal an euch und eure Geduld :)

Grüße

Jonas

PS: Includiert ist die Lib von Peter Fleury

PPS: Kann der uC zur Laufzeit sich umprogrammieren? Ich würde gerne ein 
Zählbyte speichern, sodass es bei Strom weg nicht verloren geht. Ich hab 
zwar ein EEPROM dran, aber mich würd interessieren ob es da nichts 
internes gibt - dem Datenblatt nach wohl nicht :/

von Wolfgang H. (frickelkram)


Lesenswert?

Hi Jonas,
sieht doch gut aus! Deine ISR ist ja extrem kurz :-) Wenn Du es ganz 
genau nimmst kannst Du die zwei Zeilen in der ISR noch vertauschen. Das 
erhöht die Genauigkeit beim Timing ein wenig. Sichtbar ist das in Deiner 
Anwendung aber sicherlich nicht.

Ich würde dann noch die Deklaration der SM_ Variablen als ENUM 
schreiben. Das ist aber eher Geschmackssache. Meiner Meinung nach erhöht 
das die Lesbarkeit.

Diesen Codeblock:

  SPDR = SPI_READ;
  while(!(SPSR & (1<<SPIF)));
  SPDR = EEPROM_START_HIGH;
  while(!(SPSR & (1<<SPIF)));
  SPDR = EEPROM_START_LOW;
  while(!(SPSR & (1<<SPIF)));

Kannst Du noch als Makro codieren. Dann kannst Du dem einen sinnvollen 
Namen geben und Änderungen wirken sich an den zwei Stellen im Code 
gleichzeitig aus.

Du hast ein externes EEPROM dran? Der MEGA8 hat doch eines eingebaut. 
Kannst Du das nicht nehmen, oder ist der Speicher zu klein. Für den 
Zugriff auf das interne EEPROM gibt es spezielle Makros.

https://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#EEPROM

Das sich der ATMEGA8 selbst programmieren kann ist mir nicht bekannt. 
Wenn Du einen zweiten einbaust können die sie gegenseitig programmieren. 
Aber ich vermute mal das interne EEPROM hilft Dir weiter.

von Jonas A. (Firma: GreenWire-Elektronik) (padrejohn)


Lesenswert?

Wolfgang Heinemann schrieb:
> Hi Jonas,
> sieht doch gut aus! Deine ISR ist ja extrem kurz :-) Wenn Du es ganz
> genau nimmst kannst Du die zwei Zeilen in der ISR noch vertauschen. Das
> erhöht die Genauigkeit beim Timing ein wenig. Sichtbar ist das in Deiner
> Anwendung aber sicherlich nicht.
>
> Ich würde dann noch die Deklaration der SM_ Variablen als ENUM
> schreiben. Das ist aber eher Geschmackssache. Meiner Meinung nach erhöht
> das die Lesbarkeit.
>
> Diesen Codeblock:
>
>   SPDR = SPI_READ;
>   while(!(SPSR & (1<<SPIF)));
>   SPDR = EEPROM_START_HIGH;
>   while(!(SPSR & (1<<SPIF)));
>   SPDR = EEPROM_START_LOW;
>   while(!(SPSR & (1<<SPIF)));
>
> Kannst Du noch als Makro codieren. Dann kannst Du dem einen sinnvollen
> Namen geben und Änderungen wirken sich an den zwei Stellen im Code
> gleichzeitig aus.
>
> Du hast ein externes EEPROM dran? Der MEGA8 hat doch eines eingebaut.
> Kannst Du das nicht nehmen, oder ist der Speicher zu klein. Für den
> Zugriff auf das interne EEPROM gibt es spezielle Makros.
>
> https://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#EEPROM
>
> Das sich der ATMEGA8 selbst programmieren kann ist mir nicht bekannt.
> Wenn Du einen zweiten einbaust können die sie gegenseitig programmieren.
> Aber ich vermute mal das interne EEPROM hilft Dir weiter.

Hallo Wolfgang,

danke für das Feedback. Das mit den Anweisungen im ISR war mir bewusst, 
aber wie du selbst sagst, ist diese Ungenauigkeit verkraftbar :)

Das mit dem Makro ist ne super Idee, danke!

Ich brauch 25KByte Platz, da ist das interne EEPROM leider raus. Für das 
Ablegen von ein paar Byte wäre es natürlich ideal - Danke für den Link - 
genau danach hab ich gesucht.

Gruß

Jonas

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.