Forum: Mikrocontroller und Digitale Elektronik Problem mit Mega16 Initialisierung


von Johannes (Gast)


Lesenswert?

Hallo,

ich verzweifle seit einem Tag an einem Problem mit meinem Mega16. Ich
habe ein simples Protokoll implementiert und alle Befehle
funktionieren; mit der Kommunikation uC <-> PC gibts keine Porbleme.
Ich habe den Eindruck, dass das Hauptprogramm nicht normal lauft, Es
werden offensichtlich Befehle ausgefuehrt, die nicht in der
Endlosschleife sind... Ich denke nicht, dass es ungewuenschte
"eigenstaendige resets" gibt, da ich einen MAX705 Ueberwacher habe
und bislang alles einwandfrei funktioniert hat.

Hier das Hauptprogramm:
int main(void)
{

/*= Initialisation ========*/

// Initialising port settings
DDRC  = 0x00;
DDRC |= 0xBB;

DDRB  = 0xFF;  //used for tracing
PORTB = 0x00;

//setting signals.
PORTC |= (1<<CSn1);
PORTC |= (1<<CLK1);
PORTC &= ~(1<<MODE1);

//Initialising UART: setting baudrate and enabling TXD,RXD
UBRRH=(uint8_t)(UART_BAUD_CALC(UART_BAUD_RATE,F_CPU)>>8);
UBRRL=(uint8_t)UART_BAUD_CALC(UART_BAUD_RATE,F_CPU);

UCSRB |= (1<<RXEN) | (1<<TXEN);
UCSRB |= (1<<TXCIE)| (1<<RXCIE);

//Enabling the INTERRUPT0
  GICR  |= (1<<INT0);
        MCUCR |= (1<<ISC11);
  MCUCR &= ~(1<<ISC10);

  get_AS_data(); //dies ist eine funktion die Daten aus einem   Sensor
ausliest (das tut sie auch ganz gut)
  position_zero = 1008; //volatile uint16_t variable

  bytecountSen   = 0;
  sendComplete  = 1;
  request   = 0;
  sei();        //enabling interrupts globally

/* ==== main loop ==== */
  while(1)
  {
   SREG &= ~(1<<SREG_I);
    get_AS_data();  //nicht unterbrechen
   SREG |= (1<<SREG_I);

   if (sendComplete && request)
    {
     generate_command();  // generate the protocol string
     bytecountSen = 0;
     sendComplete = 0;
     UCSRB |= (1<<UDRIE);        //generate UDRE-Interrupt
     request=0;
    }//if

  } //while(1)
}

So also die Struktur des main Programms. In der Funktion
generate_command()  veraendere ich die Variable position_zero, die dann
im Protokoll uebertragen wird. Die funktioniert auch richtig; wenn ich
allerdings diese Variable uebers Protokoll schicken lasse, ohne sie in
dieser Funktion veraendert zu haben hat sie immer den Wert 1008 aus der
Initialisierung!!! Wie ist das moeglich?!? einmal in der while(1)
Schleife kann man doch unmoeglich wider in die Initialisierung?!?!

Es sieht mir nach Reset aus, allerdings ist elektrisch alles total
stabil, ich hab den MAX705 drin, auf meinem FLUKE Multimeter ist keine
Bewegung der reset-Spannungen zu entdecken... Wie kann man ein reset
anzeigen oder speichern? Oszi hab ich leider keines...

Hat jemand schonmal aehnliche Probleme gehabt?
Bin dankbar fuer jeden Tip!!

macht's gut,
Johannes

von ThomasB (Gast)


Lesenswert?

Hi,
kontrollier Deine Interruptfunktionen. Wenn da was nicht stimmt, kann
der Stack durcheinandergeraten und es gibt einen Software-Reset.
Erzeuge Dir einen lo/high/lo -Wechsel an einem freien Port direkt
nachdem main() startet. Überwach den Pin mit dem Oszi und Du kannst
zumindest rausfinden, ob es wirklich einen Reset gab.
Ist nur so eine Idee...

Ciao Thomas.

von Magnus Müller (Gast)


Lesenswert?

Kann es sein, daß deine Funktion generate_command() in einem
include-file steht?

Gruß,
Magnetus

von Johannes (Gast)


Lesenswert?

Hi Thomas,

Oszi hab ich leider keines...

Hi Magnus,
generate_command() hab ich selber geschrieben. Im gleichen c file
oberhalb von main().

Ich hab jetzt ins EEPROM eine Variable geschrieben, die mit 0
initialisiert wird beim Flashen des uC. Ich lese sie in der
initialisierung aus und inkrementier sie. Es geschieht folgendes:
jedesmal wenn ich einen Befehl schicke ('a', 'b' usw) lasse ich mir
diese Variable im Protokoll anzeigen. Sie wird bei jedem Sende-Empfang
Vorgang um 1 incrementiert. Es muss an meinen Funktionen liegen, ich
komm aber beim besten willen nicht drauf, wie  es zu einem Reset kommen
kann...
hier meine ISRs zum Senden und Empfangen:

SIGNAL(SIG_UART_DATA)
//ISR for transmitting via serial Interface
{
  if (bytecountSen >= PLENGTH)
     {
      bytecountSen=0;
      UCSRB &= ~(1<<UDRIE);   // UDRE-Interrupt wieder sperren
      sendComplete = 1;
     }
  else
    {
     UDR = cmd[bytecountSen++]; // Daten senden
    }
}

SIGNAL(SIG_UART_RECV)
// ISR for receiving a sign via serial Interface
{

 switch (UDR)
 {
  case 'a': request = 'a'; break;
  case 'b': request = 'b'; break;
  case 'c': request = 'c'; break;
  case 'd': request = 'd'; break;
  case 'e': request = 'e'; break;
  case 'i': request = 'i'; break;
  default : request = 0; break;
 }
}

Wie kann es hier passieren, dass es zu einem Reset kommt?!?!?

mg,
Johannes

von Magnus Müller (Gast)


Lesenswert?

Wurden "bytecountSen" und "sendComplete" als volatile deklariert?

von Johannes (Gast)


Lesenswert?

Variablendeklaration:

volatile uint16_t position,position_zero;
volatile uint8_t flags, request;

volatile uint8_t cmd[PLENGTH],bytecountSen,sendComplete;

volatile uint8_t reset EEMEM = 123;
volatile uint8_t myByte;

alles was veraendert in verschiedenen Funktionen veraendert wird ist
als volatile gleich nach den #defines deklariert.

Johannes

von Magnus Müller (Gast)


Lesenswert?

Bist Du dir sicher, daß dein µC resetet? Zur Kontrolle könntest Du
direkt vor der while(1) Schleife ein definiertes Zeichen senden,
welches sonst nirgends im Protokoll vorkommt. Wenn der µC wirklich
resetet müsstest Du das am PC feststellen können (weil dann ja immer
das "böse" Zeichen kommt)

Gruß,
Magnetus

von Johannes (Gast)


Lesenswert?

aha,
gute Idee

von Johannes (Gast)


Lesenswert?

tja,
 so eine einfache idee, ich hatte sie sogar schonmal verwendet, aber in
meiner verzweiflung bin ich heut nicht drauf gekommen...

der uC resettet tatsaechlich, und zwar hab ich den eindruck, dass er
immer dann resettet wenn ich UDRIE zu Null setz, sprich jeweils nach
der Uebertragung des letzten bytes des Protokolls...

Vor diesem Test hat er offensichtlich nach jedem uebertragenen
Protokoll (7byte) resettiert. Jetzt resettiert er nachdem er
uebertragen hat und kommt wieder beim uebertragen an... und kommt
dadurchgar nicht zur Endlosschleife...

nach diesem Befehl oder eben am Ende meiner SIG_UART_DATA ISR wird
resettiert:
UCSRB &= ~(1<<UDRIE);   // UDRE-Interrupt wieder sperren

Der Reset kommt also nicht ueber die Hardware (haette mich auch sehr
gewundert).
Aber woher kommt er nun?!? wo in der Software kann sowas passieren.

@ Magnetus: danke schonmal fuer das Gluehbirnchen. Immerhin bin ich
jetzt einen Schritt voran gekommen.

mg,
Johannes

von johnny.m (Gast)


Lesenswert?

Schau dir mal das Register MCUCSR an. Da stehen eine Reihe von Flags
drin, die nach einem Reset benutzt werden können, um die Reset-Quelle
zu lokalisieren. Wenn Du die am Anfang abfragst und dann löschst,
kannst Du den Fehler vielleicht eingrenzen.

von Johannes (Gast)


Lesenswert?

Hallo,

Abschnitt aus dem Mega16 Datenblatt (Seite 42):
"13 $018 USART, UDRE USART Data Register Empty"
Der Vector nummer 13 ist also der USART DataRegisterEmpty vector

Abschnitt aus iom16.h
"
/* USART Data Register Empty */
#define USART_UDRE_vect      _VECTOR(12)
#define SIG_USART_DATA      _VECTOR(12)
#define SIG_UART_DATA      _VECTOR(12)
"

?!?!?!

ich probier mal ob es daran liegt, das kommt mir aber sehr komisch
vor...

mg,
Johannes

von Johannes (Gast)


Lesenswert?

Hallo,

hab folgendes probiert:

if(MCUCSR & (1<<EXTRF)) PORTB |=0x01;

Anstelle von EXTRF(external reset flag) hab ich auch WDRF(watchdog
reset flag) probiert. Leuchten tut meine LED nur bei EXTRF... Also
kommt von Extern irgendwoher ein reset?!?!? Ob mein MAX705 in der
Schaltung drin ist oder nicht spielt dabei keine Rolle, die LED
leuchtet unabhaengig davon auf.  :(((

Ich hab die Versorgungsspannung mit dem Resetpin probeweise verbunden.
Die external reset flag LED leuchtet immer noch auf ?!?!?

grrrrrrrrrrrrrrrrrr mein Gemuetszustand faellt in immer tiefere
Abgruende....

mg,
Johannes

von Johannes (Gast)


Lesenswert?

Hallo Leute,

ich hab eine Variante gefunden um das Problem einfach zu umgehen. Ich
hab die ISR zum senden einfach geaendert. Anstatt SIGNAL(SIG_UART_UDRE)
verwende ich SIGNAL(SIG_UART_TRANS). Das ergibt eine kleine Aenderung im
Programmcode und die ISR abarbeitung ist anders, da  nach jedem
gesendeten Zeichen noch in der Sende-ISR das Interruptflag fuer das
naechste Zeichen gesendet wird, bis man beim letzten Zeichen ankommt...
Das isr ISR technisch nicht die schoenste Loesung, ich weis. Aber es
funktioniert.

Hier noch einmal die ISR die nach dem Senden des letzten Zeichens ein
Interrupt erzeugt, das der uC ATMega16 mir als EXTRF (external reset
flag) anzeigt.

SIGNAL(SIG_UART_DATA)
//ISR for transmitting via serial Interface
{
  if (bytecountSen >= PLENGTH) //PLENGTH = Protocol length
     {
      bytecountSen=0;
      UCSRB &= ~(1<<UDRIE);   // UDRE-Interrupt wieder sperren
      sendComplete = 1;
     }
  else
    {
     UDR = cmd[bytecountSen++]; // Daten senden
    }
}

Mit all meinen uC-Kuensten und euren Tips bin ich recht eindeutig zu
dieser Erkenntnis gekommen. Trotzdem scheint sie mir recht zweifelhaft,
weil ich in ihr keine Logik erkennen kann. Wieso wird in dieser
Sende-ISR ein "externer Interrupt" erzeugt?!?!?!?
Falls jemand versteht wie es evtl dazu kommen koennte bitte ich auch um
Benachrichtigung.

mg,
Johannes

von ThomasB (Gast)


Lesenswert?

Hallo Johannes,
die ganze Interrupt-an-und-abschalterei ist m.E. nicht notwendig, wenn
Du:
- einen Pufferspeicher für zu sendende Bytes einrichtest und getrennte
Schreib und Lesezeiger für den Zugriff benutzt

- eine Funktion putchar(unsigned char c) nutzt, um das Byte
  in UDR zu schreiben, falls UDRE gesetzt ist, anderenfalls in den
  Puffer mit Erhöhung des entsprechenden Schreibzeigers

- eine Interrupt-Routine SIGNAL (SIG_UART_TRANS) verwendest, die immer
dann automatisch aufgerufen wird, wenn ein byte den AVR "verlassen"
hat. Die Funktion muss schauen, ob noch ein weiteres Byte vom Puffer
nach UDR zu kopieren ist. Anderenfalls tut sie einfach nichts.

Das Prinzip funzt in meinen Projekten sehr gut.

Ciao ThomasB

von Magnus Müller (Gast)


Lesenswert?

Hallo Johannes,

ich habe mich heute auf dem Arbeitsweg etwas intensiver mit dem Fehler
beschäftigt (die Geschichte hat mir einfach keine Ruhe gelassen) und
bin nach vielen (eigentlich unnötigen) Simulationen auf die
Fehlerursache gestossen.

Ich zitiere an dieser Stelle einmal deine UART-Initialisierung:

<c>
//Initialising UART: setting baudrate and enabling TXD,RXD
UBRRH=(uint8_t)(UART_BAUD_CALC(UART_BAUD_RATE,F_CPU)>>8);
UBRRL=(uint8_t)UART_BAUD_CALC(UART_BAUD_RATE,F_CPU);

UCSRB |= (1<<RXEN) | (1<<TXEN);
UCSRB |= (1<<TXCIE)| (1<<RXCIE);
</c>

Der Fehler versteckt sich in der Zeile
<c>
UCSRB |= (1<<TXCIE)| (1<<RXCIE);
</c>

Du gibst zusätzlich zum RX-Complete-Interrupt auch den
TX-Complete-Interrupt frei, ohne für diesen einen Interrupthandler
bereit zu stellen. Wenn der TXC-Interrupt auftritt (und dies ist am
Ende des Datenpakets der Fall) springt der Controller in einen
"Catch-all Interrupthandler", welcher netterweise den Resetvektor
aufruft.

Auszug aus "avr_libc_user_manual.pdf":

"Catch-all interrupt vector: If an unexpected interrupt occurs
(interrupt is enabled and no handler is installed, which usually
indicates a bug), then the default action is to reset the device by
jumping to the reset vector"

Wenn Du also
<c>
UCSRB |= (1<<TXCIE)| (1<<RXCIE);
</c>
durch
<c>
UCSRB |= (1<<RXCIE);
</c>
ersetzt, dürfte das Problem beseitigt sein.

Gruß,
Magnetus

von Magnus Müller (Gast)


Lesenswert?

Hmmm.... das mit dem Syntax-Highlighting hat wohl nicht richtig
funktioniert... deswegen jetzt nochmal (hoffentlich mit richtigem
Syntax-Highlighting):

Hallo Johannes,

ich habe mich heute auf dem Arbeitsweg etwas intensiver mit dem Fehler
beschäftigt (die Geschichte hat mir einfach keine Ruhe gelassen) und
bin nach vielen (eigentlich unnötigen) Simulationen auf die
Fehlerursache gestossen.

Ich zitiere an dieser Stelle einmal deine UART-Initialisierung:
1
//Initialising UART: setting baudrate and enabling TXD,RXD
2
UBRRH=(uint8_t)(UART_BAUD_CALC(UART_BAUD_RATE,F_CPU)>>8);
3
UBRRL=(uint8_t)UART_BAUD_CALC(UART_BAUD_RATE,F_CPU);
4
  
5
UCSRB |= (1<<RXEN) | (1<<TXEN);    
6
UCSRB |= (1<<TXCIE)| (1<<RXCIE);

Der Fehler versteckt sich in der Zeile
1
UCSRB |= (1<<TXCIE)| (1<<RXCIE);

Du gibst zusätzlich zum RX-Complete-Interrupt auch den
TX-Complete-Interrupt frei, ohne für diesen einen Interrupthandler
bereit zu stellen. Wenn der TXC-Interrupt auftritt (und dies ist am
Ende des Datenpakets der Fall) springt der Controller in einen
"Catch-all Interrupthandler", welcher netterweise den Resetvektor
aufruft.

Auszug aus "avr_libc_user_manual.pdf":

"Catch-all interrupt vector: If an unexpected interrupt occurs
(interrupt is enabled and no handler is installed, which usually
indicates a bug), then the default action is to reset the device by
jumping to the reset vector"

Wenn Du also
1
UCSRB |= (1<<TXCIE)| (1<<RXCIE);
durch
1
UCSRB |= (1<<RXCIE);
ersetzt, dürfte das Problem beseitigt sein.

Gruß,
Magnetus

von Johannes (Gast)


Lesenswert?

Hallo Thomas und Magnetus!

vielen Dank fuer eure Tips und Erklaerungen...
die Erklaerung von Magnetus klingt plausibel. Vielen Dank fuer die
Aufklaerung. Ich werd sie mir fett hinter die Ohren schreiben. Ich hab
letzte Nacht tatsaechlich schlechter geschlafen :) weil ich den Fehler
bis zuletzt nicht verstanden hatte.

mg,
Johannes

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.