Forum: Mikrocontroller und Digitale Elektronik attiny2313, USART mit interner Clock, Fehler sobald Timer aktiviert


von Tobi R. (Gast)


Lesenswert?

Hallo liebe Mikrocontroller-Fans,

ich habe einen attiny2313 programmiert, um eine genaue Sekunde zu 
erzeugen und über den USART an ein Terminal zu übermitteln.

In der main-Funktion findet ihr eine auskommentierte while-Schleife.
Innerhalb dieser Schleife teste ich den USART und sende einen Text.
Diese Datenübermittlung funktioniert problemlos.

Lässt man die Schleife aber auskommentiert, so werden ein Timer und ein 
Compare-Interrupt aktiviert.
In einer weiteren Schleife werden die Daten über den USART gesendet.
Diese kommen jedoch nur noch selten fehlerfrei am Terminal an.

Ist die Lösung in diesem Fall einen externen Quarz zu verwenden oder 
liegt der Fehler woanders?

Viele Grüße
Tobias


1
#define BAUD 9600UL      // Baudrate
2
3
// Berechnungen
4
#define UBRR_VAL ((F_CPU+BAUD*8)/(BAUD*16)-1)   // clever runden
5
#define BAUD_REAL (F_CPU/(16*(UBRR_VAL+1)))     // Reale Baudrate
6
#define BAUD_ERROR ((BAUD_REAL*1000)/BAUD)      // Fehler in Promille, 1000 = kein Fehler.
7
8
#if ((BAUD_ERROR<990) || (BAUD_ERROR>1010))
9
  #error Systematischer Fehler der Baudrate grösser 1% und damit zu hoch!
10
#endif
11
12
13
#include <avr/io.h>
14
#include <avr/interrupt.h>
15
#include <util/delay.h>
16
#include <stdlib.h>
17
#include <string.h>
18
19
#define INTERNALCLCK    8000000L
20
#define DEBOUNCE  256L    // debounce clock (256Hz = 4msec)
21
22
#define uint unsigned int
23
24
unsigned char volatile prescaler;
25
unsigned char volatile second;      // count seconds
26
27
unsigned char minuten;
28
unsigned char stunden;
29
unsigned char prescalerchange;
30
unsigned char secondchange;
31
32
SIGNAL (SIG_OUTPUT_COMPARE1A)
33
{
34
35
  if( --prescaler == 0 ){
36
    prescaler = (unsigned char)DEBOUNCE;
37
    second++;            // exact one second over
38
    PORTB ^= (1<<PB0);
39
  }
40
}
41
42
43
void USART_Init(void)
44
{
45
/* Set baud rate */
46
UBRRH = UBRR_VAL >> 8;
47
UBRRL = UBRR_VAL & 0xFF;
48
/* Enable transmitter*/
49
UCSRB = (1<<TXEN);
50
/* Set frame format:asynchronous, 8data, 1stop bit, no parity bit */
51
UCSRC = (3<<UCSZ0);
52
}
53
54
void USART_Transmit( unsigned char data )
55
{
56
/* Wait for empty transmit buffer */
57
while ( !( UCSRA & (1<<UDRE)) );
58
/* Put data into buffer, sends the data */
59
UDR = data;
60
}
61
62
void USART_Puts (char *s)
63
{
64
    while (*s)
65
    {   /* so lange *s != '\0' also ungleich dem "String-Endezeichen(Terminator)" */
66
        USART_Transmit(*s);
67
        s++;
68
    }
69
}
70
71
int main( void )
72
{
73
74
  //Initialisierung
75
  second = 0;
76
  secondchange=0;
77
  minuten=0;
78
  stunden=0;
79
  prescaler = (unsigned char)DEBOUNCE;
80
  prescalerchange=prescaler;
81
82
  DDRB = 0xFF;
83
  DDRA = 0;
84
  USART_Init();
85
  
86
/*
87
  while(1)
88
    {
89
      _delay_ms(1000);
90
      PORTB ^= (1<<PB0);
91
92
      USART_Puts("hh:mm:ss\n\r");
93
          USART_Puts("60\n\r");
94
    }
95
*/
96
  
97
  //Timer und Interrupts
98
  TCCR1B = (1<<WGM12) | (1<<CS10);
99
  OCR1A = INTERNALCLCK / DEBOUNCE - 1;
100
  TCNT1 = 0;
101
  TIMSK = 1<<OCIE1A;
102
  sei();
103
104
105
106
  while(1){
107
108
    if( second == 60 )
109
    {
110
          second = 0;
111
        minuten++;
112
    }
113
  if(minuten == 60 )
114
    {
115
      second = 0;
116
      minuten = 0;
117
      stunden++;
118
    }
119
  if(prescalerchange != prescaler)
120
  {
121
    prescalerchange= prescaler;
122
    if (secondchange != second)
123
    {
124
      cli(); //nur zum Test. Muss später wieder gelöscht werden
125
      //_delay_ms(100);
126
      secondchange = second;
127
128
      USART_Puts("hh:mm:ss\n\r");
129
      char Buffer[20]; // in diesem {} lokal
130
         itoa( second, Buffer, 10 );
131
         strcat(Buffer,"\n\r");
132
      USART_Puts(Buffer);
133
      sei(); //nur zum Test. Muss später wieder gelöscht werden
134
    }
135
  }
136
  }
137
}

von Thomas E. (thomase)


Lesenswert?

Tobi R. schrieb:
> Ist die Lösung in diesem Fall einen externen Quarz zu verwenden oder
> liegt der Fehler woanders?
Mit dem internen Oszillator kriegst du weder eine fehlerfreie 
Datenübertragung noch eine genaue Sekunde hin. Also was soll der ganze 
Aufwand? Natürlich brauchst du einen Quarz.

mfg.

von Tobi R. (Gast)


Lesenswert?

Ich wunder mich nur, dass die Übertragung im einen Fall so perfekt 
funktioniert trotz Verwendung des internen Oszillators.

Daher dachte ich, ich hätte im anderen Fall einen Fehler gemacht und es 
läge eventuell nicht daran, dass ein externer Quarz fehlt.

Ändert sich die Frequenz des internen Oszillators dadurch, dass der 
Timer angeschaltet wird?
Erhitzt sich der Mikrocontroller oder wie ist das zu erklären?

von Falk B. (falk)


Lesenswert?

@ Tobi R. (Gast)

>In einer weiteren Schleife werden die Daten über den USART gesendet.
>Diese kommen jedoch nur noch selten fehlerfrei am Terminal an.

Erstmal ist es sehr ungünstig, deine Uhr so zu zerreißen. Das Hochzählen 
packt man besser in den Interrupt. Oder wenn schon, dann zählt man die 
Sekunden KOMPLETT incl. Überlauf in der ISR und setz dann ein Flag, 
welches von der Hauptschleife erkannt, ausgewertet und gelöscht wird. 
Das ist solide, siehe Interrupt.

SIGNAL (SIG_OUTPUT_COMPARE1A)

Das ist absolut veraltet, nimm ISR().

>Ist die Lösung in diesem Fall einen externen Quarz zu verwenden oder

Ist anzuraten.

>liegt der Fehler woanders?

Wahrscheinlich.

von Tobi R. (Gast)


Lesenswert?

Ersteinmal vielen Dank für die Tipps!

Ich habe den Code jetzt abgeändert und ISR verwendet.

Einen simplen Text per UART zu übermitteln klappt jetzt. Auch wenn der 
Timer läuft und das Interrupt aktiviert ist.

Allerdings funktioniert der auskommentierte Code in der Hauptschleife 
nicht.

Ich glaube, irgendwas läuft mit den String-Operationen schief.
1
/*
2
 * main.c
3
 *
4
 *  Created on: 06.06.2014
5
 *      Author: tobias.reingen
6
 */
7
8
9
/* uC: Attiny2313 */
10
11
12
#define BAUD 9600UL      // Baudrate
13
14
// Berechnungen
15
#define UBRR_VAL ((F_CPU+BAUD*8)/(BAUD*16)-1)   // clever runden
16
#define BAUD_REAL (F_CPU/(16*(UBRR_VAL+1)))     // Reale Baudrate
17
#define BAUD_ERROR ((BAUD_REAL*1000)/BAUD) // Fehler in Promille, 1000 = kein Fehler.
18
19
#if ((BAUD_ERROR<990) || (BAUD_ERROR>1010))
20
  #error Systematischer Fehler der Baudrate grösser 1% und damit zu hoch!
21
#endif
22
23
24
25
26
#include <avr/io.h>
27
#include <avr/interrupt.h>
28
#include <stdlib.h>
29
#include <string.h>
30
31
32
//Variablen für die Zeit
33
volatile unsigned int achtelmillisekunden;
34
volatile unsigned int millisekunden;
35
volatile unsigned int sekunde;
36
volatile unsigned int minute;
37
volatile unsigned int stunde;
38
39
unsigned int letztesekunde;
40
41
void USART_Init(void)
42
{
43
/* Set baud rate */
44
UBRRH = UBRR_VAL >> 8;
45
UBRRL = UBRR_VAL & 0xFF;
46
/* Enable transmitter*/
47
UCSRB = (1<<TXEN);
48
/* Set frame format:asynchronous, 8data, 1stop bit, no parity bit */
49
UCSRC = (3<<UCSZ0);
50
}
51
52
void USART_Transmit( unsigned char data )
53
{
54
/* Wait for empty transmit buffer */
55
while ( !( UCSRA & (1<<UDRE)) );
56
/* Put data into buffer, sends the data */
57
UDR = data;
58
}
59
60
void USART_Puts (char *s)
61
{
62
    while (*s)
63
    {   /* so lange *s != '\0' also ungleich dem "String-Endezeichen(Terminator)" */
64
        USART_Transmit(*s);
65
        s++;
66
    }
67
}
68
69
70
int main(void)
71
{
72
  DDRB = 1<<PB0; //LED an PB0
73
74
  USART_Init(); //USART wird initialisiert
75
76
77
  // Timer 0 konfigurieren
78
  TCCR0A = (1<<WGM01); // CTC Modus
79
  TCCR0B |= (1<<CS01); // Prescaler 8
80
  // ((1000000/8)/1000) = 125
81
  OCR0A = 125-1;
82
83
  // Compare Interrupt erlauben
84
  TIMSK |= (1<<OCIE0A);
85
86
  // Global Interrupts aktivieren
87
  sei();
88
89
  while(1)
90
  {
91
    /*Hier kann die aktuelle Zeit
92
      ausgeben werden*/
93
94
    if(letztesekunde != sekunde)
95
    {
96
      USART_Puts("test");
97
98
      letztesekunde = sekunde;
99
/*
100
      USART_Puts("mm:ss\n");
101
102
      char minuteBuffer[20]; // in diesem {} lokal
103
      char sekundeBuffer[20];
104
105
      char gesamtBuffer[40];
106
107
      if(minute< 10) strcat(gesamtBuffer, "0");
108
109
      itoa( minute, minuteBuffer, 10 );
110
111
      strcat(gesamtBuffer, minuteBuffer);
112
      strcat(gesamtBuffer,":");
113
114
      itoa(sekunde, sekundeBuffer, 10);
115
116
      strcat(gesamtBuffer,sekundeBuffer);
117
      strcat(gesamtBuffer,"\n");
118
119
      USART_Puts(gesamtBuffer);
120
*/
121
    }
122
123
124
  }
125
}
126
127
/*
128
Der Compare Interrupt Handler
129
wird aufgerufen, wenn
130
TCNT0 = OCR0A = 125-1
131
ist (125 Schritte), d.h. genau alle 1 ms
132
*/
133
ISR (TIMER0_COMPA_vect)
134
{
135
  achtelmillisekunden++;
136
  if(achtelmillisekunden == 8)
137
  {
138
    millisekunden++;
139
    achtelmillisekunden = 0;
140
     if(millisekunden == 1000)
141
     {
142
       sekunde++;
143
       PORTB ^= 1<<PB0;
144
       millisekunden = 0;
145
       if(sekunde == 60)
146
       {
147
         minute++;
148
         sekunde = 0;
149
       }
150
       if(minute == 60)
151
       {
152
         stunde++;
153
         minute = 0;
154
       }
155
       if(stunde == 24)
156
       {
157
         stunde = 0;
158
       }
159
     }
160
  }
161
162
}

von Falk B. (falk)


Lesenswert?

@ Tobi R. (Gast)

>Ich habe den Code jetzt abgeändert und ISR verwendet.

Dort ist noch ein Fehler, ab minute==60, das muss noch in die vorherige 
Klammerebene.

>Einen simplen Text per UART zu übermitteln klappt jetzt. Auch wenn der
>Timer läuft und das Interrupt aktiviert ist.

Schon mal ein Fortschritt.

>Allerdings funktioniert der auskommentierte Code in der Hauptschleife
>nicht.

>Ich glaube, irgendwas läuft mit den String-Operationen schief.

Sieht erstmal OK aus. Was kommt den auf dem Terminal an?

von Falk B. (falk)


Lesenswert?

Ach nee!

Die ERSTE Operation darf KEIN strcat sein! Sonst machst du deinen String 
immer länger!

Das muss am Anfang stehen!

gesamtBuffer[0]=0;  // String rücksetzen

von Karl H. (kbuchegg)


Lesenswert?

Nicht das du nicht mit den str...Funktionen üben sollst. Aber so
1
...
2
    char Buffer[40];
3
    sprintf( Buffer, "%02d:%02d:%02d\n", stunde, minute, sekunde );
4
    USART_Puts( Buffer );
5
....
ist es erst mal um einiges einfacher. Und da ist dann die Ausgabe von 
führenden 0en auch schon mit drinnen.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Falk Brunner schrieb:
> Ach nee!
>
> Die ERSTE Operation darf KEIN strcat sein! Sonst machst du deinen String
> immer länger!
>
> Das muss am Anfang stehen!
>
> gesamtBuffer[0]=0;  // String rücksetzen

Oder als erste Operation einen strcpy benutzen und keinen strcat

von Tobi R. (ccommander)


Lesenswert?

Vielen Dank für den Hinweis!

Ich habe im folgendem Code alle Strings zurückgesetzt.
Leider kommt immernoch nur manchmal das Richtige am Terminal an.
1
      letztesekunde = sekunde;
2
3
      USART_Puts("mm:ss\n");
4
5
      char minuteBuffer[20]; // in diesem {} lokal
6
      char sekundeBuffer[20];
7
8
      char gesamtBuffer[40];
9
10
      //Strings zurücksetzen
11
      gesamtBuffer[0]=0;
12
      minuteBuffer[0]= 0;
13
      sekundeBuffer[0]=0;
14
15
      if(minute< 10) strcat(gesamtBuffer, "0");
16
17
      itoa( minute, minuteBuffer, 10 );
18
19
      strcat(gesamtBuffer, minuteBuffer);
20
      strcat(gesamtBuffer,":");
21
22
      if(sekunde< 10) strcat(gesamtBuffer, "0");
23
24
      itoa(sekunde, sekundeBuffer, 10);
25
26
      strcat(gesamtBuffer,sekundeBuffer);
27
      strcat(gesamtBuffer,"\n");
28
29
      USART_Puts(gesamtBuffer);

Terminal:
1
‚Ò‚²Rmm:ss!‚:ºRmm:ss
2
‚Ò08
3
mm:ss
4
‚Ò‚ÊRmm:ss
5
00:LRmm:ss
6
‚ÒŠŠRmm:ss
7
00:12
8
mm:ss
9
‚ÒŠšRmm:ss
10
00:14
11
mm:ss
12
‚ÒŠ5
13
mm:ss
14
00:16
15
mm:ss
16
00:17
17
mm:ss
18
‚ÒŠ8
19
mm:ss
20
00:19
21
mm:ss
22
‚Ò’‚R


Folgender Code funktioniert hingegen:
1
      char c;
2
3
      letztesekunde = sekunde;
4
5
      c= (stunde -(stunde%10)) / 10 + '0';
6
      USART_Transmit(c);
7
      c= (stunde % 10) + '0';
8
      USART_Transmit(c);
9
10
      USART_Puts(":");
11
12
      c= (minute -(minute%10)) / 10 + '0';
13
      USART_Transmit(c);
14
      c= (minute % 10) + '0';
15
      USART_Transmit(c);
16
17
      USART_Puts(":");
18
19
      c= (sekunde -(sekunde%10)) / 10 + '0';
20
      USART_Transmit(c);
21
      c= (sekunde % 10) + '0';
22
      USART_Transmit(c);
23
24
      USART_Transmit('\n');

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.