Timer/Compare löst dauert Reset aus

Gast #444591
Lesenswert?

hy

Ich möchte mit dem 16Bit Timer (MEga8 8MHz) ein 4kHz Signal genieren, 
das an OC1 ausgegeben werden soll. Leider beginnnt mein Programm immer 
wieder von vornen hinter Sei()...

wo liegt der Fehler?

  TCCR1A = _BV(COM1A0);
  TCCR1B = _BV(WGM12) | _BV(CS11);
  TIMSK = _BV(OCIE1A);
  OCR1A = 0x7E;

  sei();
Gast #444598
Lesenswert?

Wenn Du keinen kompletten Code schickst, kann man nur Vermutungen 
anstellen. Meine Vermutung: Du hast keine Interrupt-Routine definiert 
oder hast sie falsch definiert, so dass bei Auftreten des Interrupts der 
"Spurious Interrupt Handler" aufgerufen wird, der zu einem RESET führt.
Gast #444599
Lesenswert?

ja das könnte gut sein.

ich dachte der Timer macht alles selber im Hintergrund, bzw setzt den 
Port selber um durch das toggeln, ohne das ich es extra aufrufen muss...

Sonst müsste ich ja mit Signal oder so wieder extra eine Routine 
aufrufen...
Gast #444604
Lesenswert?

1
#define   F_CPU 8000000UL
2
#include  <avr/io.h>
3
#include  <stdio.h>
4
#include  <stdlib.h>
5
#include   <inttypes.h>
6
#include   <avr/interrupt.h>
7

8
#define BAUD     38400UL
9
#define UBRR_BAUD  ((F_CPU)/(16*(BAUD))-1)
10
....
11

12

13
int main (void) 
14
{
15
  UCSRB   |= (1 << TXEN) | ( 1 << RXEN );  // UART TX, RX einschalten
16
   UCSRC   |= ( 1 << URSEL )|( 3<<UCSZ0 );  // Asynchron 8N1
17
   UBRRH  = (uint8_t) (UBRR_BAUD>>8);      // USART Baud
18
    UBRRL  = (uint8_t) UBRR_BAUD;
19

20
  ADCSRA = _BV(ADEN) | _BV(ADPS1) | _BV(ADPS2);  // AD anschalten & Prescaler 64 Takt = (CLK/Prescaler) 50-200kHz
21
  MCUCR = _BV(ISC00);  // Interupt 0 an
22
  GICR  = _BV(INT0);    // Eingang an
23
  uart_puts("System wurde gestartet\r");  
24
    
25

26
  TCCR1A = _BV(COM1A0);
27
  TCCR1B = _BV(WGM12) | _BV(CS11);
28
  TIMSK = _BV(OCIE1A);
29
  OCR1A = 0x7E;
30

31
  sei();
32

33
  for (;;) 
34
  {
35
  ...
36
  }
37
}

so
Gast #444606
Lesenswert?

Wenn Du den Interrupt aktivierst, brauchst Du auch einen 
Interrupt-Handler. Wenn Du nur einen Pin toggeln willst, dann 
deaktiviere den Interrupt. Den brauchst Du dafür nicht. Also lass die 
Zeile
1
TIMSK = _BV(OCIE1A);
einfach weg!
#444608
Lesenswert?

> ich dachte der Timer macht alles selber im Hintergrund, bzw setzt den
> Port selber um durch das toggeln, ohne das ich es extra aufrufen muss...

Das kann er auch.
Nur: Wenn du den zugehörigen Interrupt
aktivierst
   TIMSK = _BV(OCIE1A);

wird der Interrupt Handler auch aufgerufen.
Wenn es keinen gibt -> Reset


Gast #444629
Lesenswert?

irgendwie unterbricht ADMUX bzw ADCRSA meinen Timer A und setzt ihn auf 
NUll bevor 0x7C erreicht ist...!!

nutzen die beide den selben? wie kann ich es umgehen?
1
ADMUX = 0;  // AD Kanal A.0
2
ADCSRA |= _BV(ADSC);  // AD Messung starten
3
while (ADCSRA & _BV(ADSC))  // bis ein Wert eingelesen ist warten
4
    {}
Gast #444635
Lesenswert?

> nutzen die beide den selben?
Den selben was? Der ADC hat mit dem Timer nichts zu tun. Was gibt Dir 
die Gewissheit, dass der Timer tatsächlich zurückgesetzt wird? Bei dem 
Codeschnipsel gilt selbiges wie oben: Ohne kompletten Code kann da 
niemand was zu sagen, außer vielleicht, dass das Starten des ADC 
anscheinend korrekt ist.

Übrigens: Wer ist "Timer A"? Beim AVR gibts Timer 0, Timer 1... aber 
keinen Timer A. Meinst Du vielleicht "Compare A"?
Gast #444666
Lesenswert?

1
#define   F_CPU 8000000UL
2
#include  <avr/io.h>
3
#include  <stdio.h>
4
#include  <stdlib.h>
5
#include   <inttypes.h>
6
#include   <avr/interrupt.h>
7

8
#define BAUD     38400UL
9
#define UBRR_BAUD  ((F_CPU)/(16*(BAUD))-1)
10

11
char LFC[15];
12
char TC[15];
13
char LFKC[15];
14
int t=0;
15
int lf=0;
16
int uart_putc(unsigned char c)
17
{
18
  while (!(UCSRA & (1<<UDRE)));
19

20
  UDR = c;
21
  return 0;
22
}
23

24
int uart_puts( char* str )
25
{
26
  while( *str )
27
    uart_putc( *str++ );
28
  return 0;
29
}
30

31

32
int main (void) 
33
{
34

35
  double   Faktor = 0.0048828125;
36
  double  LF = 0;
37
  double  T = 0;
38
  double   LFkomp = 0;
39

40

41
  UCSRB   |= (1 << TXEN) | ( 1 << RXEN );  // UART TX, RX einschalten
42
   UCSRC   |= ( 1 << URSEL )|( 3<<UCSZ0 );  // Asynchron 8N1
43
   UBRRH  = (uint8_t) (UBRR_BAUD>>8);      // USART Baud
44
    UBRRL  = (uint8_t) UBRR_BAUD;
45

46
  ADCSRA = _BV(ADEN) | _BV(ADPS1) | _BV(ADPS2);  // AD anschalten & Prescaler 64 Takt = (CLK/Prescaler) 50-200kHz
47
  MCUCR = _BV(ISC00);                // Interupt 0 an
48
  GICR  = _BV(INT0);  
49
                // Eingang an
50
//  uart_puts("System wurde gestartet\r");  
51
  
52
  PORTB = 0xFF;
53
  TCCR1A = _BV(COM1B0);
54
  TCCR1B = _BV(WGM12) | _BV(CS11);
55
  OCR1B = 0x7E;
56

57
  sei();
58

59

60
  for (;;) 
61
  {
62
                      // Rohleitfähigkeit einlesen
63
                    // 0-5V 0-24 mS => 
64
    ADMUX = 0;              // AD Kanal A.0
65
    ADCSRA |= _BV(ADSC);        // AD Messung starten
66
    while (ADCSRA & _BV(ADSC))      // bis ein Wert eingelesen ist warten
67
    {}
68
    LF = ADCW;              // Weise den Wert b zu  
69
    LF = LF*(double)Faktor;
70
    LF = LF * 5.42857;          // Umrechnung Spannung pro mS
71
    
72
                      // Temperatur einlesen
73
                      // 0-5V ,0-60 C
74
    ADMUX = 0x01;                 // AD Kanal A.1
75
    ADCSRA |= _BV(ADSC);            // AD Messung starten
76
    while (ADCSRA & _BV(ADSC) )     // bis ein Wert eingelesen ist
77
    {}
78
    
79
    T = ADCW;              // Weise den Wert b zu    
80
    T= (T*Faktor)*28.869-27.713;
81
    T = T*100;
82
    
83
    int loesung = 1;
84
    
85
    if(loesung == 1)  
86
      {
87
      double a = 0.021;
88
      LFkomp = LF+LF*a*(T-25);
89
      LFkomp=LFkomp*100;    
90
      }
91
    
92
    else if(loesung == 2)  
93
      {
94
      double a = 0.023;
95
      LFkomp = LF+a*(T-25);    
96
      LFkomp=LFkomp*100;
97
      }
98
    t=T;
99
    lf=LF;
100

101
    dtostrf(LF,6,4,LFC);
102
    dtostrf(T,6,4,TC);
103
    dtostrf(LFkomp,6,4,LFKC);    
104
    }
105
}
106

107
ISR (SIG_INTERRUPT0)
108
{
109

110
    uart_puts("\r");
111
    uart_puts( "Rohleitfähigkeit  : " );
112
    uart_puts(LFC);
113
    uart_puts("[mS/cm]\r");
114
      uart_puts("\r");
115
    uart_puts( "Temperatur        : " );
116
    uart_puts(TC);
117
    uart_puts("[°C]\r");
118
    uart_puts("\r");
119
      uart_puts( "Endleitfähigkeit  : " );
120
    uart_puts(LFKC);
121
    uart_puts("[mS/cm]\r");
122
    uart_puts("\r");
123
    
124
}

ja ich meinte Timer0 mit CompareA
Gast #444743
Lesenswert?

oh man vielen Dank!!! Hab nicht gesehen das ich PORTB anstatt DDRB 
geschrieben hatte

jetzt klappt es !!

Ja mit den Kontanten muss ich noch alles ändern.

Die Umwandlung in char dauert leider einfach viel zu lange.
dtostrf(LF,6,4,LFC);

1. Wie kann ich sie als INT übertragen? wollte die zahl *100 nehmen und 
dann übertragen ohne Komma...

Oder kann ich eine int Zahl 0-3000 in Hex umwandeln und dann direkt als 
Binärcode per UART senden...?

oder geht das einfacher?
#444769
Lesenswert?

> 1. Wie kann ich sie als INT übertragen? wollte die zahl *100 nehmen und
> dann übertragen ohne Komma...

Hatten wir das nicht vor Kurzem schon mal.

Der Weg den ich gehen würde:
Von vorne herein gar keine double nehmen, sondern alles
als Fixed Point Arithmetik aufbauen.

Fixed Point Arithmetik: Das kennst du schon nur war es dir
bisher nicht bewusst :-)
Angenomen du willst mit Kilometer rechnen.
Du rechnest zb 3.456 Kilometer plus 2.729 Kilometer

      3.456
      2.729
    -------
      6.185   Kilometer

Dazu brauchst du natürlich Gleitkomma, also double.

Aber warum eigentlich. Du kannst das Ganze auch in Meter
rechnen:

        3456  Meter
  plus  2729  Meter
       -----
        6185  Meter

Wenn deine Ausgabe unbedingt Kilometer sein müssen, dann
reicht es doch die 4185 Meter von oben zu nehmen und
während der Ausgabe nach der Tausenderstelle ein Komma
einzuschmuggeln.

Du musst aber nicht Meter nehmen. Du könntest auch Millimeter
nehmen. Oder, weil du weist dass du nie über 10 Kilometer
hinauskommen wirst, alles in 1/3000 Kilometer ausdrücken.

10 Kilometer sind dann ein Zahlenwert von 30000
 1 Kilometer wäre dann                     3000

3.456 Kilometer wären dann 10368     (3.456 * 3000)
2.729 Kilometer wären dann  8187     (2.729 * 3000)

Die Summe ergibt 1855, was nach Rückrechnung (/3000) wieder
die bewussten 6.185 Kilometer ergibt.

Natürlich wird man nicht mit 3000 arbeiten. Wenn schon dann hat
man meist glatte 10er Potenzen oder 2er Potenzen für sowas.

Aber das Prinzip ist einfach: Untersuche deine Formeln, ob die
Genauigkeit reicht, wenn du alles mal 1000 nimmst und dafür
in int oder long rechnen kannst (long müsste auf jeden Fall
ausreichen).

Anstatt

   T= (T*Faktor)*28.869-27.713;

rechnest du zb

   long Faktor = 5;    // 0.0048828125 * 1000, gerundet
   T = ( T  Faktor  28869L ) / 1000 - 27713L;

Falls die Genauigkeit mit einem 1000er Vielfachen nicht
reicht (vorher untersuchen, ob das so ist), dann nimmst
du halt noch eine Kommastelle dazu und machst ein 10000er
Vielfaches.

Bei der Ausgabe dann noch an der richtigen Stelle ein
Komma einschmuggeln und du bist intern die double los,
fährst maximalen Speed und am anderen Ende der UART
kann niemand den Unterschied feststellen.
#444771
Lesenswert?

> Oder kann ich eine int Zahl 0-3000 in Hex umwandeln und dann direkt
> als Binärcode per UART senden...?

Du brauchst nichts in HEX umwandeln. Du kannst auch die
Bytes direkt senden. Kommt halt immer darauf an, wer
am anderen Ende der UART lauscht. Wenn das ein Programm
ist, dann ist das kein Problem (double würde ich so nicht
verschicken, da es verschiedene Gleitkommaformate gibt).
Wenn da aber ein Mensch sitzt, und sei es nur um während
der Entwicklung die Übertragung zu kontrollieren, dann
würde ich ihm das nicht antun.
Gast #444810
Lesenswert?

also als Hex senden klar ist nix sehr menschenfreundlich.

aber wie kann ich INT Zahlen versenden? das würde schon reichen.

0-65536 würde ja reichen und so hät ich einen 16bit Wert.
Senden kann ich ja jeweils 8Bit.
also müsste ich das ganze aufteilen und zwei teile senden...
1
while (!(UCSRA & (1<<UDRE)));
2
UDR = Zahl;

so geht es ja nicht!!

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