Forum: Mikrocontroller und Digitale Elektronik Timer/Compare löst dauert Reset aus


von Jörn (Gast)


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();

von johnny.m (Gast)


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.

von Jörn (Gast)


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

von Jörn (Gast)


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

von johnny.m (Gast)


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!

von Karl heinz B. (kbucheg)


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



von johnny.m (Gast)


Lesenswert?

BTW: Wenn Du bei
1
 ...
 jeweils das "ode" weglässt (also nur "C"), dann kriegste auch ne 
vernünftige Darstellung des Codes.

von Karl heinz B. (kbucheg)


Lesenswert?

> [Code]

Das was du willst erreichst du mit


Eckige_Klammer_auf C Eckige_Klammer_zu

Hier dein Code

Eckige_Klammer_auf /C Eckige_Klammer_zu


so wie hier:
1
  int main()
2
  {
3
  }

  

von Jörn (Gast)


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
    {}

von johnny.m (Gast)


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"?

von Jörn (Gast)


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

von Jörn (Gast)


Lesenswert?

ups war ne testversion

muss natürlich sein:
1
ORTB = 0xFF;
2
  TCCR1A = _BV(COM1A0);
3
  TCCR1B = _BV(WGM12) | _BV(CS11);
4
  OCR1A = 0x7E;

von Jörn (Gast)


Lesenswert?

Also im debugger funktioniert das ganze wunderbar. Er Setzt den OC1 
Ausgang immer schon hin her. aber in Realität bleibt es konstant auf 5 
V!!

Wieso? Ich kann keine Fehler entdecken...

von johnny.m (Gast)


Lesenswert?

Ich sehe im Programm nirgends eine Anweisung, die den betreffenden 
Portpin als Ausgang konfiguriert. Da müsste irgendwas sein wie
1
DDRB |= 1 << DDB1;
Sonst gehts nicht...

von johnny.m (Gast)


Lesenswert?

BTW: Du solltest Deine konstanten Faktoren nicht als Variablen 
deklarieren...

von Jörn (Gast)


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?

von Karl heinz B. (kbucheg)


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.

von Karl heinz B. (kbucheg)


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.

von Jörn (Gast)


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!!

von johnny.m (Gast)


Lesenswert?

1
UDR = (unsigned char) Zahl;
2
//...warten bis gesendet...
3
UDR = (unsigned char) (Zahl >> 8);
4
//...warten bis gesendet...

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.