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


von Ephi (Gast)


Angehängte Dateien:

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:
1
#include <stdlib.h>
2
#include <avr/io.h>
3
#include <avr/interrupt.h>
4
#include <avr/iom8.h> 
5
6
#define UMFANG 68.173  // Umfang des Schaufelrades in cm
7
 
8
#define BAUD 2400L      // Baudrate
9
10
// Berechnung
11
#define UBRR_VAL F_CPU/16/BAUD-1
12
13
void USART_Init(unsigned int ubrr)
14
{
15
  // Set baud rate
16
  UBRRH = (unsigned char)(ubrr>>8);
17
  UBRRL = (unsigned char) ubrr;
18
  // Enable transmitter 
19
  UCSRB = (1<<TXEN);
20
  // Set frame format: 8data, 1stop bit 
21
  UCSRC = (1<<URSEL)|(3<<UCSZ0);
22
}
23
24
void USART_Transmit(unsigned char data)
25
{
26
  // Wait for empty transmit buffer
27
  while(!( UCSRA & (1<<UDRE)));
28
  // Put data into buffer, sends the data
29
  UDR = data;
30
}
31
32
void USART_Putstring(char *s)
33
{
34
    while (*s)
35
    {                   // so lange *s != '\0' also ungleich dem "String-Endezeichen"
36
        USART_Transmit(*s);
37
        s++;
38
    }
39
}
40
41
char tempbuff[8];            // buffer
42
43
void USART_Putfloat(float speed)
44
{
45
  dtostrf(speed, 6, 3, tempbuff); 
46
    USART_Putstring(tempbuff);
47
}
48
49
// interrupts initialisieren
50
void INT_Init(void)
51
{
52
  GIMSK |= (1 << 6);          // int0 aktivieren
53
  MCUCR |= (1 << 1);          // bei fallender flanke interrupt auslösen
54
  MCUCR &= ~(1 << 0);         // /
55
  sei();                // interrupts ein
56
}
57
58
// 16-bit timer initialisieren
59
void TIMER_Init(void)
60
{
61
  TCCR1A = (1 << WGM12);        // CTC Mode
62
  TCCR1B = (1 << CS12)|(1 << CS10);  // F_CPU/1024 from prescaler
63
  OCR1A  =  0xFFFF;          // compare value
64
  TIMSK  = (1 << OCIE1A);        // enable compare match interrupt
65
}
66
67
// globale variablen
68
uint8_t dist = 0, zwi = 0;
69
volatile uint8_t upm = 0, calc = 0, usek = 0;
70
float speed;
71
72
// interrupt routinen
73
74
// INT0 Pin
75
ISR(INT0_vect)
76
{
77
  upm++;
78
}
79
80
// Timer
81
ISR(TIMER1_COMPA_vect)
82
{
83
  calc = 1;
84
  usek = upm;
85
  upm = 0;
86
}
87
88
89
int main(void)
90
{
91
  USART_Init(UBRR_VAL);        // uart initialisieren
92
93
  TIMER_Init();            // timer initialisieren
94
95
  INT_Init();              // interrupts initialisieren
96
97
98
  while(1)
99
  {
100
    if(calc)
101
    {
102
      dist = usek*UMFANG;      // streckenberechnung in cm
103
      zwi = dist/10000;        // umrechnung in km
104
      speed = zwi*6*60;        // umrechnung in km/h
105
      USART_Putfloat(speed);     // ausgabe auf USART
106
      calc = 0;
107
    }
108
  }
109
 
110
    return 0;
111
}

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!

von 2919 (Gast)


Lesenswert?

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

von crazy horse (Gast)


Lesenswert?

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

von Ephi (Gast)


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!

von Ephi (Gast)


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

von 2919 (Gast)


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.

von Ephi (Gast)


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

von Nico (Gast)


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.

von Ephi (Gast)


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;
}
[/]

von Ephi (Gast)


Lesenswert?

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

von Falk B. (falk)


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.
1
      calc = 0;
2
      speed = temp*6*60*UMFANG*usek/10000;       // umrechnung in km/h
3
      USART_Putfloat(speed);             // ausgabe auf USART
4
      calc = 0;


MFG
Falk

von Karl H. (kbuchegg)


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.

von Ephi (Gast)


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

von Ephi (Gast)


Lesenswert?

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

von 2920 (Gast)


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.

von Falk B. (falk)


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

von Ephi (Gast)


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

von Ephi (Gast)


Lesenswert?

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

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

INT handler:
1
// Timer
2
ISR(TIMER1_COMPA_vect)
3
{
4
  if(intcount == 10)
5
  {
6
    intcount = 0;
7
    calc = 1;
8
    usek = upm;
9
    upm = 0;
10
  }
11
  else
12
    intcount++;
13
}

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!

von Ephi (Gast)


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:
1
// 16-bit timer initialisieren
2
void TIMER_Init(void)
3
{
4
  TCCR1B |= (1 << WGM12);        // CTC Mode
5
  TCCR1B |= (1 << CS12);        // F_CPU/256 from prescaler
6
  OCR1A  =  0x7A12;          // compare value
7
  TIMSK  = (1 << OCIE1A);        // enable compare match interrupt
8
}

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

ok, jetzt geht der timer schonmal freu

von Ephi (Gast)


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?

von Falk B. (falk)


Lesenswert?

@ Ephi (Gast)

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

Entprellung

MFG
Falk

von Ephi (Gast)


Lesenswert?

ok, werd mal diese schaltung probieren:
http://www.mikrocontroller.net/articles/Bild:Taster_entprellen.png

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

von Falk B. (falk)


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

von Ephi (Gast)


Lesenswert?

na dann ;)

habs ausprobiert, und klappt ;)

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

von 2920 (Gast)


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.

von Karl H. (kbuchegg)


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.

von Ephi (Gast)


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.

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.