attiny2313, USART mit interner Clock, Fehler sobald Timer aktiviert

Gast #3681352
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
}
Gast #3681389
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?
#3681438
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.
Gast #3681548
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
}
#3681639
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?
#3681669
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.
#3681672
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');

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren