Forum: Mikrocontroller und Digitale Elektronik Sleep Modus und falsche UART Sendungen


von Owen S. (senmeis)


Lesenswert?

Hi,

ich möchte ein Byte über UART im Atmega169 pro Sekunde senden lassen. 
Ein 32.768kHz Quarz wird für Timer2 eingesetzt und der interne RC 
Oszillator wird als Systemtakt verwendet.

Das Problem ist, wenn der 169 aus dem Schlafmodus „Power Save“ erweckt 
wird, sendet dieser falsche Bytes. Ohne Schlafmodus ist alles OK.

Zu bemerken: In diesen falschen Bytes gibt es sogar Regel: Das richtige 
Byte 0x5A wird alle 25 Bytes gesendet, dazwischen nur 0x00 und 0xEB, 
also
5A 00 EB 00 EB 00 EB 00 EB 00 EB 00 EB 00 EB 00 EB 00 EB 00 EB 00 EB 00 
EB 5A

Ich vermute mal, dass der Systemtakt von 1MHz nach dem Schlafmodus 
verstellt ist, aber wieso? Inzwischen habe ich auch versucht, alle 
Initialisierungen nach jedem Schlafmodus neu zu machen, erfolgslos!

Der Code ist hier:
1
int main( void )
2
{
3
  clock_init_1MHz();
4
  
5
  RTC_init();
6
  USART_Init(12); // 9600bps bei 1MHz
7
 
8
  __enable_interrupt();
9
  
10
  for(;;)
11
  { 
12
    Delay(1000);    // wait for 1 sec to let the Xtal stabilize after a power-on
13
    Usart_Tx(0x5A);
14
    while(!(UCSR0A & (1<<TXC0)));
15
    
16
    SMCR = (3<<SM0) | (1<<SE);      // Enable Power-save mode
17
    __sleep();                      // Go to sleep
18
                
19
    SMCR = 0;                       // Just woke, disable sleep
20
  }
21
}
22
23
void clock_init_1MHz(void)
24
{
25
  CLKPR = (1<<CLKPCE);        // set Clock Prescaler Change Enable
26
    
27
  // set prescaler = 8, Inter RC 8Mhz / 8 = 1Mhz
28
  CLKPR = (1<<CLKPS1) | (1<<CLKPS0); 
29
}
30
31
void Delay(unsigned int millisec)
32
{
33
    int i;
34
    
35
    while (millisec--)
36
        for (i=0; i<125; i++);
37
}
38
39
void RTC_init(void)
40
{
41
    Delay(1000);            // wait for 1 sec to let the Xtal stabilize after a power-on,
42
43
    __disable_interrupt();  // disabel global interrupt
44
45
    cbi(TIMSK2, TOIE2);             // disable OCIE2A and TOIE2
46
47
    ASSR = (1<<AS2);        // select asynchronous operation of Timer2
48
49
    TCNT2 = 0;              // clear TCNT2A
50
    TCCR2A |= (1<<CS22) | (1<<CS20);             // select precaler: 32.768 kHz / 128 = 1 sec between each overflow
51
    
52
    while((ASSR & (0x01 | 0x04)));       // wait for TCN2UB and TCR2UB to be cleared
53
54
    TIFR2 = 0xFF;           // clear interrupt-flags
55
    sbi(TIMSK2, TOIE2);     // enable Timer2 overflow interrupt
56
57
    OCR2A = 200;    // set timer2 compare value
58
    
59
    __enable_interrupt();                 // enable global interrupt
60
}
61
62
#pragma vector = TIMER2_OVF_vect
63
__interrupt void TIMER2_OVF_interrupt(void)
64
{
65
}
66
67
void USART_Init(unsigned int baudrate)
68
{
69
    // Set baud rate
70
    UBRR0H = (unsigned char)(baudrate>>8);
71
    UBRR0L = (unsigned char)baudrate;
72
73
    // Enable 2x speed
74
    UCSR0A = (1<<U2X0);
75
76
    // Enable receiver and transmitter
77
    UCSR0B = (1<<RXEN0)|(1<<TXEN0)|(0<<RXCIE0)|(0<<UDRIE0);
78
79
    // Async. mode, 8N1
80
    UCSR0C = (0<<UMSEL0)|(0<<UPM00)|(0<<USBS0)|(3<<UCSZ00)|(0<<UCPOL0);
81
}
82
83
void Usart_Tx(char data)
84
{
85
    while (!(UCSR0A & (1<<UDRE0)));
86
    UDR0 = data;
87
}

Hat jemand Ideen?

Gruss
Owen
von Daniel P. (dpolz)


Lesenswert?

Woher beziehst du denTakt für UART? Ist der RC-Oszillator abgeglichen? 
Für UART sollte man Quarze verwenden, da der RC temperaturabhängig ist. 
Vielleicht muss nach dem Aufwachen erst noch alles sauber einschwingen, 
obwohl RCs da in der Regel die schnellsten sind. Trotzdem würde ich mal 
beim Takt anfangen zu suchen und mit einem schnellen Quarz testen.
von Daniel P. (dpolz)


Lesenswert?

Du hast nicht zufällig ein Oszi, mit dem du mal den Takt ausmessen 
könntest?
von Turbo J (Gast)


Lesenswert?

> Das Problem ist, wenn der 169 aus dem Schlafmodus „Power Save“ erweckt
> wird, sendet dieser falsche Bytes. Ohne Schlafmodus ist alles OK.

In den Sparmodi unterhalb von "IDLE" sind die UARTs nicht mehr 
verfügbar, siehe: Sleep Mode.

Die "komischen" Zeichen enstehen, weil dem UART mitten im Byte die 
Taktquelle verloren geht und er dann einfach seinen Pegel hält. Da die 
Übertragung asynchron ist, erhält der PC dann verwürfelte Daten.

Abhilfe: Tiefere Stromspar-Modi nur verwenden, wenn nichts über UART 
gesendet oder empfangen werden muss.
von Owen S. (senmeis)


Lesenswert?

Vielen Dank für Deine Abhilfe.

Das funktioniert wirklich wenn der "Idle" Modus gewählt wird. Trotzdem 
verstehe ich dies nicht. In meinem Beispielcode geht der Controller in 
den "Power Save" Modus nachdem das Byte bereits fertiggeschickt wird. 
Warum wird die Taktquelle verloren?

Gruss
Owen
von Oliver J. (skriptkiddy)


Lesenswert?

Vielleicht hilft es einen Moment zu warten, bevor du nach dem Aufwachen 
wieder Daten sendest und/oder die Startup-Time höher zu stellen.

Gruß Oliver
von Peter D. (peda)


Lesenswert?

Du mußt vor dem Schlafen warten, bis das Senden zuende ist (TXCIE0-Bit).


Peter
von Dietrich L. (dietrichl)


Lesenswert?

Turbo J schrieb:
> Die "komischen" Zeichen enstehen, weil dem UART mitten im Byte die
> Taktquelle verloren geht und er dann einfach seinen Pegel hält. Da die
> Übertragung asynchron ist, erhält der PC dann verwürfelte Daten.

Wenn das so ist, dann ist nur das letzte (abgebrochene) Zeichen falsch. 
Dann ist Ruhe, da ja keine neuen Startzeichen kommen.

Die Ursache muss also eine andere sein.

Gruß Dietrich
von Peter D. (peda)


Lesenswert?

Dietrich L. schrieb:
> Dann ist Ruhe, da ja keine neuen Startzeichen kommen.

Nö, das Byte kann ja weitere 1-0 Flanken enthalten und wird erst zuende 
gesendet, wenn der Takt wieder da ist.


Peter
von Dietrich L. (dietrichl)


Lesenswert?

Peter Dannegger schrieb:
> Nö, das Byte kann ja weitere 1-0 Flanken enthalten und wird erst zuende
> gesendet, wenn der Takt wieder da ist.
OK
von Stefan E. (sternst)


Lesenswert?

Owen Senmeis schrieb:
> In meinem Beispielcode geht der Controller in
> den "Power Save" Modus nachdem das Byte bereits fertiggeschickt wird.

Nö. Da du das Flag nie löscht, funktioniert dein Warten nur genau ein 
Mal.
von Owen S. (senmeis)


Lesenswert?

Ich habe die Methode Usart_Tx() so geändert und das funktioniert auch 
nicht beim „Power Save“ Modus.
1
void Usart_Tx(char data)
2
{
3
    while (!(UCSR0A & (1<<UDRE0)));
4
    UDR0 = data;
5
    while (!(UCSR0A & (1<<TXC0)));
6
}

Es scheint mit dem Flag nichts zu tun.

Cu
Owen
von Peter D. (peda)


Lesenswert?

1
void Usart_Tx(char data)
2
{
3
    UCSR0A = UCSR0A;               // clear TXC0 !!!
4
    UDR0 = data;
5
    while (!(UCSR0A & (1<<TXC0)));
6
}


Peter
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.