mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik Anemometer - Timer, Interrupts, USART


Autor: Ephi (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Hi @ all,

Ich muss für die schule derzeit ein Anemometer bauen.
Die Hardware hab ich auch schon komplett fertig. (Elektronix, und 
windmessmechanik)
Vom prinzip her sieht das so aus:
ein reed kontakt löst bei jeder Umdrehung am INT0 Pin einen Interrupt 
aus. (das klappt soweit) In der interrupt routine wird eine variable 
hochgezählt. (bei jedem durchlauf var++) paralell dazu läuft ein 16-bit 
Timer, der alle 10 sek den ComparMatch1A Interrupt auslöst. in dieser 
interrupt routine wird die zählvariable kopiert, die hauptfunktion dazu 
veranlasst die windgeschwindigkeit zu berechnen, und dann die 
zählvariable wieder auf 0 setzt. Die Hauptfunktion berechnet die 
geschwindigkeit, und sendet den wert dann via USART an den PC (der USART 
teil funktionier auch)

Jetzt zu meinen Problemen:
1. Ich bekomme auf einem in Delphi selbst geschriebenen Terminal (das 
terminal funktioniert nachweislich) immer 0,000km/h. Das ist irgendwie 
mysteriös...

2. Der Timer sendet bei aktueller einstellung immer alle 8,8 sek den 
Compare match interrupt, was ich ja noch einstellen könnte, aaaber:
er sendet den interrupt immer alle 8,8 sekunden, egal was ich in OCR1A 
schreib. Auch mysteriös...

für weitere codeverbesserungen bin ich auch gerne offen!

Achja, ich verwende einen Atmega8 @ 8Mhz

und hier noch mein Code:
#include <stdlib.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/iom8.h> 

#define UMFANG 68.173  // Umfang des Schaufelrades in cm
 
#define BAUD 2400L      // Baudrate

// Berechnung
#define UBRR_VAL F_CPU/16/BAUD-1

void USART_Init(unsigned int ubrr)
{
  // Set baud rate
  UBRRH = (unsigned char)(ubrr>>8);
  UBRRL = (unsigned char) ubrr;
  // Enable transmitter 
  UCSRB = (1<<TXEN);
  // Set frame format: 8data, 1stop bit 
  UCSRC = (1<<URSEL)|(3<<UCSZ0);
}

void USART_Transmit(unsigned char data)
{
  // Wait for empty transmit buffer
  while(!( UCSRA & (1<<UDRE)));
  // Put data into buffer, sends the data
  UDR = data;
}

void USART_Putstring(char *s)
{
    while (*s)
    {                   // so lange *s != '\0' also ungleich dem "String-Endezeichen"
        USART_Transmit(*s);
        s++;
    }
}

char tempbuff[8];            // buffer

void USART_Putfloat(float speed)
{
  dtostrf(speed, 6, 3, tempbuff); 
    USART_Putstring(tempbuff);
}

// interrupts initialisieren
void INT_Init(void)
{
  GIMSK |= (1 << 6);          // int0 aktivieren
  MCUCR |= (1 << 1);          // bei fallender flanke interrupt auslösen
  MCUCR &= ~(1 << 0);         // /
  sei();                // interrupts ein
}

// 16-bit timer initialisieren
void TIMER_Init(void)
{
  TCCR1A = (1 << WGM12);        // CTC Mode
  TCCR1B = (1 << CS12)|(1 << CS10);  // F_CPU/1024 from prescaler
  OCR1A  =  0xFFFF;          // compare value
  TIMSK  = (1 << OCIE1A);        // enable compare match interrupt
}

// globale variablen
uint8_t dist = 0, zwi = 0;
volatile uint8_t upm = 0, calc = 0, usek = 0;
float speed;

// interrupt routinen

// INT0 Pin
ISR(INT0_vect)
{
  upm++;
}

// Timer
ISR(TIMER1_COMPA_vect)
{
  calc = 1;
  usek = upm;
  upm = 0;
}


int main(void)
{
  USART_Init(UBRR_VAL);        // uart initialisieren

  TIMER_Init();            // timer initialisieren

  INT_Init();              // interrupts initialisieren


  while(1)
  {
    if(calc)
    {
      dist = usek*UMFANG;      // streckenberechnung in cm
      zwi = dist/10000;        // umrechnung in km
      speed = zwi*6*60;        // umrechnung in km/h
      USART_Putfloat(speed);     // ausgabe auf USART
      calc = 0;
    }
  }
 
    return 0;
}

ah nochwas: im anhang schick ich mal noch mein Delphi Terminal mit.
Achtung: Die durchschnittsgeschwindigkeitberechnung ist nochnicht ganz 
korrekt.
UND: die datums-zeitangabe und das "km/h" am schluss werden vom Programm 
eiingefügt. Vom µC kommt nur der eigentlich wert!

Autor: 2919 (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Was soll der Comparematch ? I wuerd einen Timer hochlaufen lassen, mir 
alle sekunden die Anzahl Impulse kopieren und null setzen.

Autor: crazy horse (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
also ich öffne hier keine .exe. Was ist da drin? Noch irgendwas 
Relevantes?

Autor: Ephi (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@2919:
wie genau meinst du das? kannst du das mal konkretisieren (oder steh ih 
auf der leitung?)

@crazy horse: wie oben beschrieben ist die .exe nur mein 
selbergeschriebenes messterminal. Nicht direkt relevant!

Autor: Ephi (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
also wie gesagt, ich bin auch für alternative lösungsvorschläge 
bezüglich des timers und so offen!

ich bin halt noch relativ am anfang der microcontroller...

Autor: 2919 (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ja machn Int0 so dass er eine Variable hochzaehlt. Und dann machst einen 
Timer0, zB alle 10ms der zaehlt auf einen Sekunde, oder auf eine Minute 
oder irgendwas. Wenn diese Zeit vorueber ist, wird die 
Int0-Zaehl-Variable gelese und zurueckgesetzt.

Autor: Ephi (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ja und wie sag ich dem, das er genau auf eine sekunde (oder auf 10) 
zählt?
hab da im datenblatt nix zu gefunden

Autor: Nico (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Bei einer Division mit Integer-Zahlen werden die Kommastellen 
abgeschnitten.
Im Code wird folgendes gerechnet:

uint8_t dist = 0, zwi = 0;
dist = usek*UMFANG;      // streckenberechnung in cm
zwi = dist/10000;        // umrechnung in km
speed = zwi*6*60;        // umrechnung in km/h

Die Variable zwi ist nur dann ungleich Null, wenn die Variable dist 
einen grösseren Wert als 10000 besitzt. Aber da es nur einen "uint8_t" 
ist, kommt diese nur auf max. 255. Somit ergibt sich für die Variable 
zwi immer eine Null.

Autor: Ephi (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ah, ja klar...
ok, hab das mal angepasst.
hab noch nen fehler bei meinem compare match teil bemerkt: WGM12 ist ja 
im TCCR1B, nicht im TCCR1A...

naja

trotz der anpassung der variablen für die berechnung, hab ich noch was 
merkwürdiges: bei der messung kommt jetzt am Terminal entweder 0,000 
oder 360,000 an. kann ja irgendwie nicht sein!?

am timer arbeite ich noch...

hier der aktuelle code:
[c]#include <stdlib.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/iom8.h>

#define UMFANG 68.173  // Umfang des Schaufelrades in cm

#define BAUD 2400L      // Baudrate

// Berechnung
#define UBRR_VAL F_CPU/16/BAUD-1

void USART_Init(unsigned int ubrr)
{
  // Set baud rate
  UBRRH = (unsigned char)(ubrr>>8);
  UBRRL = (unsigned char) ubrr;
  // Enable transmitter
  UCSRB = (1<<TXEN);
  // Set frame format: 8data, 1stop bit
  UCSRC = (1<<URSEL)|(3<<UCSZ0);
}

void USART_Transmit(unsigned char data)
{
  // Wait for empty transmit buffer
  while(!( UCSRA & (1<<UDRE)));
  // Put data into buffer, sends the data
  UDR = data;
}

void USART_Putstring(char *s)
{
    while (*s)
    {                   // so lange *s != '\0' also ungleich dem 
"String-Endezeichen"
        USART_Transmit(*s);
        s++;
    }
}

char tempbuff[8];            // buffer

void USART_Putfloat(float speed)
{
  dtostrf(speed, 6, 3, tempbuff);
    USART_Putstring(tempbuff);
}

// interrupts initialisieren
void INT_Init(void)
{
  GIMSK |= (1 << 6);          // int0 aktivieren
  MCUCR |= (1 << 1);          // bei fallender flanke interrupt auslösen
  MCUCR &= ~(1 << 0);         // /
  sei();                // interrupts ein
}

// 16-bit timer initialisieren
void TIMER_Init(void)
{
  TCCR1B = (1 << WGM12);        // CTC Mode
  TCCR1B = (1 << CS12)|(1 << CS10);  // F_CPU/1024 from prescaler
  OCR1A  =  0x00FF;          // compare value
  TIMSK  = (1 << OCIE1A);        // enable compare match interrupt
}

// globale variablen
uint16_t dist = 0;
volatile uint8_t upm = 0, calc = 0, usek = 0;
float speed, temp;

// interrupt routinen

// INT0 Pin
ISR(INT0_vect)
{
  upm++;
}

// Timer
ISR(TIMER1_COMPA_vect)
{
  calc = 1;
  usek = upm;
  upm = 0;
}


int main(void)
{
  USART_Init(UBRR_VAL);        // uart initialisieren

  TIMER_Init();            // timer initialisieren

  INT_Init();              // interrupts initialisieren


  while(1)
  {
    if(calc)
    {
      dist = usek*UMFANG;      // streckenberechnung in cm
      temp = dist/10000;      // umrechnung in km
      speed = temp*6*60;      // umrechnung in km/h
      USART_Putfloat(speed);     // ausgabe auf USART
      calc = 0;
    }
  }

    return 0;
}
[/]

Autor: Ephi (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
oh, kann ein admin mal bitte noch das c im endtag [/] hinmachen?

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@  Ephi (Gast)

>ah, ja klar...
>trotz der anpassung der variablen für die berechnung, hab ich noch was
>merkwürdiges: bei der messung kommt jetzt am Terminal entweder 0,000
>oder 360,000 an. kann ja irgendwie nicht sein!?

Festkommaarithmetik

Floating Point ist hier überflüssig. Erst alle Multiplikationen, dann 
alle Divisionen!

>am timer arbeite ich noch...

soooo lange?

>hier der aktuelle code:

Solche langen Sachen sind besser als Anhang.

Es fehlt das #define für F_CPU! Das Flag calc sollte man als erstes 
zurücksetzen. Ist hier zwar unkritisch, kann aber in anderen Programmen 
komische Wirkung haben, wenn die Rechnung sehr lang wird.

Wenn schon, dann so.
      calc = 0;
      speed = temp*6*60*UMFANG*usek/10000;       // umrechnung in km/h
      USART_Putfloat(speed);             // ausgabe auf USART
      calc = 0;


MFG
Falk

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> trotz der anpassung der variablen für die berechnung, hab ich noch was
> merkwürdiges: bei der messung kommt jetzt am Terminal entweder 0,000
> oder 360,000 an. kann ja irgendwie nicht sein!?

Was ist daran merkwürdig.

>      temp = dist/10000;      // umrechnung in km
>      speed = temp*6*60;      // umrechnung in km/h

da sowohl dist als auch 10000 integer Datentypen sind, wird
die Division auch als Integer Division ausgeführt.
Und da ich nicht davon ausgehe, dass die Spitzen deines
Anemometers mehr als 1 km in 10 Sekunden zurücklegen
werden, wird das Ergebnis deiner Division entweder 0 oder 1
sein. Und zwar auch dann, wenn du eigentlich ein Ergebnis von
0.0001 erwartet hättest

Aber wie Falk schon sagte: Auch float ist an dieser Stelle
überflüssig. Wenn man die Berechnung etwas umstellt, dann kriegt
man da auch schöne Ergebnisse komplett ohne float.

Autor: Ephi (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi,

ertma vielen dank! ich habs jetzt erstmal mit fließkomma eiinigermaßen 
hin gebracht, ich werds heut abend mal mit festkomma probieren!

ich hatte den code extra nicht als anhang gemacht, da ich dachte man 
kann ihn so schneller einsehn.

F_CPU hab ich in den compileroptionen definiert gehabt...

>>am timer arbeite ich noch...

>soooo lange?

ja. ich hätte eher schreiben sollen, am timer rätsele ich noch ;)
ich weis nämlich wirklich noch nicht, wie ich dem beibringe so und so 
viel ms, oder auch s zu warten. das hängt doch alles von der 
taktfrequenz ab.

mit dem compare match hätte ich halt so lange rumprobiert bis ich nen 
compare wert gefunden hätte, dann ziemlich genau 10sek. entspricht (5sek 
wär jetzt auch kein beinbruch..) allerdings ist der compare match 
interrupt intervall trotz meiner korrektur in der INIT routine 
unabhängig vom inhalt von OCR1A immer gleich :confused:

P.S.: sorry wen hin und wieder mal ein c fehlt, meine tastatur hängt ;)

Autor: Ephi (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ich war wohl zu langsam. okok, ich guck mir das mit dem festkommazeugs 
an!

Autor: 2920 (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Der Timer besteht ja nur aus einer handvoll Register. Davon braucht man 
nur etwa 2 stueck. Da ist man selbst mit wahllosem Permutieren 
schneller. Sonst kann man auch mit Google nach "AVR timer ASM" suchen. 
Gibt beliebig viele Eintraege.

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@ Ephi (Gast)

>ja. ich hätte eher schreiben sollen, am timer rätsele ich noch ;)
>ich weis nämlich wirklich noch nicht, wie ich dem beibringe so und so
>viel ms, oder auch s zu warten. das hängt doch alles von der
>taktfrequenz ab.

Ach neee!

>mit dem compare match hätte ich halt so lange rumprobiert bis ich nen
>compare wert gefunden hätte, dann ziemlich genau 10sek. entspricht

AUA!!!!!!

Schon mal was von Rechnen gehört?

Wenn der Timer mit 1 MHz läuft, sind das 1 Millionen Takte pro Sekunde. 
Na, dämmerts?

Mfg
Falk

Autor: Ephi (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ok, sry, seid etwas nachsichtig mit mir, ih bin wohl atm nicht so ganz 
hell auf der platte ;) soll vorkommen...

aalso ich hab mal etwas gerechnet:

wenn ich den 16bit timer mit F_CPU/256 vom prescaler versorge,
läuft der timer bei 8Mhz systemtakt mit 31250Hz
wenn ih jetzt erstmal bei meiner lösung mit dem compare match bleibe,
müsste ich im OCR1A als compare value 0x7A12 festlegen.

dann wird der compare match interrupt jede sekunde ausgelöst. jetzt noch 
ne kleine verzweigung in der int routine, die auf 10 zählt, und dann zur 
berechnung veranlasst.

ich werd das mal ausprobieren, aber selbstverständlich mal noch nach 
anderen timermethoden googlen

gruß
Ephi

Autor: Ephi (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
hm, komisch, ih krig jetzt folgende messabstände:
[22.10.2007 - 17:36:17:640] 0,000km/h
[22.10.2007 - 17:36:40:703] 0,000km/h
[22.10.2007 - 17:37:03:781] 0,000km/h
[22.10.2007 - 17:37:26:843] 0,000km/h

hier noch meine jetzige init und interrupt funktion:
Timer INIT:
// 16-bit timer initialisieren
void TIMER_Init(void)
{
  TCCR1B = (1 << WGM12);        // CTC Mode
  TCCR1B = (1 << CS12);        // F_CPU/256 from prescaler
  OCR1A  =  0x7A12;          // compare value
  TIMSK  = (1 << OCIE1A);        // enable compare match interrupt
}

INT handler:
// Timer
ISR(TIMER1_COMPA_vect)
{
  if(intcount == 10)
  {
    intcount = 0;
    calc = 1;
    usek = upm;
    upm = 0;
  }
  else
    intcount++;
}

ich hab das gefühl, der löst den COMPA_vect, immer blos beim overflow, 
und nicht bei meinem value aus :(

hab die init auch nochmal anhand vom datenblatt geprüft
sollte soweit korrekt sein!

Autor: Ephi (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ich hab das problem:
ih hab scheiße gebaut beim init: beim setzten des prescalers hab ich die 
ctc einstellung grad wieder gelöscht
so muss es heißen:
// 16-bit timer initialisieren
void TIMER_Init(void)
{
  TCCR1B |= (1 << WGM12);        // CTC Mode
  TCCR1B |= (1 << CS12);        // F_CPU/256 from prescaler
  OCR1A  =  0x7A12;          // compare value
  TIMSK  = (1 << OCIE1A);        // enable compare match interrupt
}

und im int handler in der ifabfrage natürlich == 9, und nicht == 10

ok, jetzt geht der timer schonmal freu

Autor: Ephi (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ok, auch die fließkomma arithmetik klappt soweit.

allerdings hab ich jetzt noch ein problem:
und zwar war mir nicht bewusst, das reed kontakte ja auch prellen :(

und jetzt weis ich wirklich nihtmehr weiter, wie ich einen reed kontakt 
entprelle. das ist zwar kein problem, aber wie soll ih das am interrupt 
machen?

evtl ne hardware entprellung? wie würde sowas aussehen?

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@ Ephi (Gast)

>evtl ne hardware entprellung? wie würde sowas aussehen?

Entprellung

MFG
Falk

Autor: Ephi (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ok, werd mal diese schaltung probieren:
http://www.mikrocontroller.net/articles/Bild:Taste...

da hab ih nämlich die teile da...

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@ Ephi (Gast)

>http://www.mikrocontroller.net/articles/Bild:Taste...
>da hab ih nämlich die teile da...

Beim AVR sind die Schmit-trigger schon eingebaut. Du braucht nur zwei Rs 
und ein C.

MFG
Falk

Autor: Ephi (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
na dann ;)

habs ausprobiert, und klappt ;)

wozu eig überhaupt die ganze softwareentprellerei, wenns so einfach in 
hw geht?

Autor: 2920 (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ehi, die Fliessskommarechnung wuerd ich mal rausnehmen, bis ueberhaut 
etwas Sinnvolles kommt. Lass dir doch mal rausgeben, wieviele Impulse 
denn im Messzyklus ankommen. Und falls die unrealistisch sind, aendere 
die Software bis das zuerst mal stimmt.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ephi wrote:
> na dann ;)
>
> habs ausprobiert, und klappt ;)
>
> wozu eig überhaupt die ganze softwareentprellerei, wenns so einfach in
> hw geht?

Weil bei 10 Tasten dann doch schon einiges an externer Hardware
dazukommt, der
* bezahlt werden muss
* Platz auf der Platine braucht

Und dann natürlich noch: Entprellen ist eine Seite der Medaille.
Aber die 'klassische' PeDa-Entprellung liefert dir gleich noch
einiges mehr mit: Kein Tastendruck geht verloren, sie kostet praktisch
keine CPU Last, Unterscheidung zwischen langem und kurzem Tasten-
druck, Autorepeat.

Autor: Ephi (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@2920

flieskomma ist schon lang raus!
das mit dem impulse ausgeben hab ich auh schon gemacht, nach der 
entprellung passt das nun!

@karl heinz buchegger
ja ok, stimmt schon, aber in meinem fall brauch ich das ja alles nicht.

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.