mikrocontroller.net

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


Autor: Jörn (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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();

Autor: johnny.m (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Jörn (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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...

Autor: Jörn (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
#define   F_CPU 8000000UL
#include  <avr/io.h>
#include  <stdio.h>
#include  <stdlib.h>
#include   <inttypes.h>
#include   <avr/interrupt.h>

#define BAUD     38400UL
#define UBRR_BAUD  ((F_CPU)/(16*(BAUD))-1)
....


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

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

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

  sei();

  for (;;) 
  {
  ...
  }
}

so

Autor: johnny.m (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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
TIMSK = _BV(OCIE1A);
einfach weg!

Autor: Karl heinz Buchegger (kbucheg)
Datum:

Bewertung
0 lesenswert
nicht 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



Autor: johnny.m (Gast)
Datum:

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

Autor: Karl heinz Buchegger (kbucheg)
Datum:

Bewertung
0 lesenswert
nicht 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:
  int main()
  {
  }

  

Autor: Jörn (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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?
ADMUX = 0;  // AD Kanal A.0
ADCSRA |= _BV(ADSC);  // AD Messung starten
while (ADCSRA & _BV(ADSC))  // bis ein Wert eingelesen ist warten
    {}

Autor: johnny.m (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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"?

Autor: Jörn (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
#define   F_CPU 8000000UL
#include  <avr/io.h>
#include  <stdio.h>
#include  <stdlib.h>
#include   <inttypes.h>
#include   <avr/interrupt.h>

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

char LFC[15];
char TC[15];
char LFKC[15];
int t=0;
int lf=0;
int uart_putc(unsigned char c)
{
  while (!(UCSRA & (1<<UDRE)));

  UDR = c;
  return 0;
}

int uart_puts( char* str )
{
  while( *str )
    uart_putc( *str++ );
  return 0;
}


int main (void) 
{

  double   Faktor = 0.0048828125;
  double  LF = 0;
  double  T = 0;
  double   LFkomp = 0;


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

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

  sei();


  for (;;) 
  {
                      // Rohleitfähigkeit einlesen
                    // 0-5V 0-24 mS => 
    ADMUX = 0;              // AD Kanal A.0
    ADCSRA |= _BV(ADSC);        // AD Messung starten
    while (ADCSRA & _BV(ADSC))      // bis ein Wert eingelesen ist warten
    {}
    LF = ADCW;              // Weise den Wert b zu  
    LF = LF*(double)Faktor;
    LF = LF * 5.42857;          // Umrechnung Spannung pro mS
    
                      // Temperatur einlesen
                      // 0-5V ,0-60 C
    ADMUX = 0x01;                 // AD Kanal A.1
    ADCSRA |= _BV(ADSC);            // AD Messung starten
    while (ADCSRA & _BV(ADSC) )     // bis ein Wert eingelesen ist
    {}
    
    T = ADCW;              // Weise den Wert b zu    
    T= (T*Faktor)*28.869-27.713;
    T = T*100;
    
    int loesung = 1;
    
    if(loesung == 1)  
      {
      double a = 0.021;
      LFkomp = LF+LF*a*(T-25);
      LFkomp=LFkomp*100;    
      }
    
    else if(loesung == 2)  
      {
      double a = 0.023;
      LFkomp = LF+a*(T-25);    
      LFkomp=LFkomp*100;
      }
    t=T;
    lf=LF;

    dtostrf(LF,6,4,LFC);
    dtostrf(T,6,4,TC);
    dtostrf(LFkomp,6,4,LFKC);    
    }
}

ISR (SIG_INTERRUPT0)
{

    uart_puts("\r");
    uart_puts( "Rohleitfähigkeit  : " );
    uart_puts(LFC);
    uart_puts("[mS/cm]\r");
      uart_puts("\r");
    uart_puts( "Temperatur        : " );
    uart_puts(TC);
    uart_puts("[°C]\r");
    uart_puts("\r");
      uart_puts( "Endleitfähigkeit  : " );
    uart_puts(LFKC);
    uart_puts("[mS/cm]\r");
    uart_puts("\r");
    
}

ja ich meinte Timer0 mit CompareA

Autor: Jörn (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ups war ne testversion

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


Autor: Jörn (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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...

Autor: johnny.m (Gast)
Datum:

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

Autor: johnny.m (Gast)
Datum:

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

Autor: Jörn (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Karl heinz Buchegger (kbucheg)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Karl heinz Buchegger (kbucheg)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Jörn (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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...
while (!(UCSRA & (1<<UDRE)));
UDR = Zahl;

so geht es ja nicht!!

Autor: johnny.m (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
UDR = (unsigned char) Zahl;
//...warten bis gesendet...
UDR = (unsigned char) (Zahl >> 8);
//...warten bis gesendet...

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.