Forum: Mikrocontroller und Digitale Elektronik Impulse pro Minute


von Bernd (Gast)


Lesenswert?

Hallo zusammen
Ich würde gerne mit einem AtMega8 die Zeit zwischen 2 Impulsen messen 
(zwischen 0,3 und 1,5 Sekunden)und anschließend die Anzahl der Impulse 
pro Minute ausgeben.
Dazu hatte ich vor Timer1 zu benutzen (Prescaler =1)und Input Capture zu 
aktivieren. Dann ist ja:
             Capturewert2 - Capturewert1 = Abstand in Timerschritten

diesen Abstand müsste ich dann mit der Zeit zwischen zwei Timerschritten 
multiplizieren und 60(Sekunden) durch diesen Wert teilen.

Allerdings hab ich das Problem, dass ich wenig Ahnung vom Programmieren 
habe und nicht weiß ob das so funktionieren kann oder vielleicht sogar 
einfacher geht. Außerdem weiß ich auch nach langem rumstöbern im 
Internet nicht wie ich die Multiplikation und die Division durchführe. 
Ich hoffe Ihr könnt mir da helfen.

Bernd

von Kai F. (kai-) Benutzerseite


Lesenswert?

die Grundidee ist schonmal gar nicht schlecht, allerdings steckt der 
Teufel im Detail...
Wenn du als Prescaler 1 nimmst und das ganze mit 8Mhz laufen lässt, wird 
der Timer1, der nur bis 65535 zählen kann in einer Sekunde genau 122 Mal 
überlaufen. Vergiss also nicht das overflow Interrupt, wo du die Anzahl 
der Overflows zählst und später mit 65536 * gespeicherter Wert wieder 
zurück rechnest

Solltest du in C programmieren wollen, kannst du eine Multiplikation mit 
* und eine Division mit / durchführen.

von der Art her sollte das Programm so aussehen:

                // Zeit ab hier stoppen
TCNT1 = 0;      // Timer 1 auf 0 setzen
overflow = 0;   // Variable zum Zählen der overflows zurücksetzen

// warten bis gestoppt wird

dummy = TCNT1   // wichtig, dass der Wert als erstes gespeichert wird,
                // dass keine Zeit verloren geht
dummy = dummy + 65536 * overflow
// dummy sollte mindestens als long definiert werden, da der Wert sehr
// groß werden kann
// mit dummy kann jetzt weiter gerechnet werden

von Bernd (Gast)


Lesenswert?

So vielen Dank für deine hilfreiche Antwort. Das mit dem Overflow hätte
mir eigentlich auffallen müssen, hab mich da wohl irgentwie verrechnet,
ist aber ja auch eigentlich kein großes Problem. Jetzt muss ich es nur
noch schaffen soweit mit C zurechtzukommen (hab sonst nur ein bisschen
mit Assembler rumbrobiert ), dass ich das Ganze auch programmieren kann.

Ich hätte da noch eine Frage: Wenn ich den Wert habe wie schaffe ich es
ihn auf 7-Segmentanzeigen oder einem LCD auszugeben?

Bernd

von Christoph db1uq K. (christoph_kessler)


Lesenswert?

http://www.mikrocontroller.net/articles/AVR_Arithmetik
eine ähnliche Aufgabe ist die Auswertung eines Geigerzählers, aber da 
reicht eine Messung nicht, da die Abstände zufällig sind.

von Kai F. (kai-) Benutzerseite


Lesenswert?

Die Anzeige auf einem LCD ist relativ leicht, auf einem 7 Segment etwas 
komplizierter.
Wenn du CodevisionAVR benutzt, hast du ein LCD innerhalb weniger Minuten 
in Betrieb, bei dem C GNU Compiler sollte das aber auch recht leicht 
möglich sein, musst eben schauen wo du die LCD Routinen herbekommst, 
kenn mich da nicht so aus.

Wenn du 7-Segmente nehmen willst, musst du dir überlegen wie du die 
ansteuern willst. Direkt über den Mikrocontoller multiplexen oder doch 
einen MAX7921 (glaub das war der richtige) benutzen.
Außerdem ist der Anschluss von 7 Segmentern komplizierter, weil jede 
Ziffer 9 Anschlüsse braucht, bei einem LCD brauchst du nur 8 oder 9

Wenn es dir also egal ist wie die Zahl angezeigt wird, rate ich dir zu 
einem LCD

von Bernd (Gast)


Lesenswert?

So ich hab jetzt versucht mit Codevision ein Programm zu schreiben, das 
Problem dabei ist allerdings, dass ich von C wenig Ahnung hab und auch 
das GCC-Tutorial hilft mir da wenig.
ich habe nun Codevision das Programm soweit schreiben lassen und:

- In die Overflow Interrupt Routine
        Overflow = Overflow + 1;
  geschrieben.

- In die Input Capture Interrupt Routine
         Dummy = TCNT1;
         Dummy = Dummy + 65536 * Overflow;
         Dummy = Dummy * 0,000000125;
         Impulse = 60 / Zeit;

- Bei Declare your global variables
         typedef unsigned char uint8_t Overflow;
         typedef unsigned long uint32_t Dummy;
         typedef unsigned char uint8_t Impulse;

-Und im main Teil des Programms
         Overflow = 0x00

was ist daran jetzt falsch? Wie muss ich das ändern?
und wie gebe ich bei Codevision die Variable Impulse über das LCD aus?

von Karl H. (kbuchegg)


Lesenswert?

> - In die Input Capture Interrupt Routine
>          Dummy = TCNT1;
>          Dummy = Dummy + 65536 * Overflow;
>          Dummy = Dummy * 0,000000125;
>          Impulse = 60 / Zeit;

Warum rechnest du da wie wild mit einer Variablen Dummy
herum, wenn du dann das Ergebnis der Rechnerei überhaupt
nicht benutzt?

von rino (Gast)


Lesenswert?

Vollständiges Programm zeigen, sonst kann man dir nicht gescheit helfen.

von Kai F. (kai-) Benutzerseite


Lesenswert?

lad doch einfach mal die C Datei hoch, sonst ist es wirklich schwer dir 
zu helfen.
Davon abgesehen, dass deine Rechnung keinen Sinn macht, weil du das 
Ergebnis nicht benutzt, ist sie sehr langsam und kompliziert! Lass den 
Controller nur so viel rechnen wie unbedingt sein muss.
Woran siehst du eigentlich, dass es nicht geht bzw was erwartest du zu 
sehen?

von Bernd (Gast)


Lesenswert?

Also das mit der unsinnigen Berechnung: "zeit" muss Dummy heißen hab ich 
falsch abgeschrieben ...
und hier jetzt das ganze (unfertige) Programm:
1
#include <mega8.h>   
2
3
// Alphanumeric LCD Module functions
4
#asm
5
   .equ __lcd_port=0x18 ;PORTB
6
#endasm
7
#include <lcd.h>
8
9
// Timer 1 overflow interrupt service routine
10
interrupt [TIM1_OVF] void timer1_ovf_isr(void)
11
{
12
  Overflow = Overflow + 1;
13
}
14
15
// Timer 1 input capture interrupt service routine
16
interrupt [TIM1_CAPT] void timer1_capt_isr(void)
17
{
18
  Dummy = TCNT1;
19
  Dummy = Dummy + 65536 * Overflow; 
20
  Dummy = Dummy * 0,000000125;
21
  Impulse = 60 / Dummy;
22
  
23
}
24
25
// Declare your global variables here   
26
typedef unsigned char uint8_t Overflow ;
27
typedef unsigned long uint32_t Dummy ;
28
typedef unsigned char uint8_t Inmpulse ; 
29
  
30
void main(void)
31
{
32
  
33
PORTB=0x00;
34
DDRB=0x00;
35
36
PORTC=0x00;
37
DDRC=0x00;
38
39
PORTD=0x00;
40
DDRD=0x00;
41
42
// Timer/Counter 0 initialization
43
// Clock source: System Clock
44
// Clock value: Timer 0 Stopped
45
TCCR0=0x00;
46
TCNT0=0x00;
47
48
// Timer/Counter 1 initialization
49
// Clock source: System Clock
50
// Clock value: 4000,000 kHz
51
// Mode: Normal top=FFFFh
52
// OC1A output: Discon.
53
// OC1B output: Discon.
54
// Noise Canceler: Off
55
// Input Capture on Rising Edge
56
// Timer 1 Overflow Interrupt: On
57
// Input Capture Interrupt: On
58
// Compare A Match Interrupt: Off
59
// Compare B Match Interrupt: Off
60
TCCR1A=0x00;
61
TCCR1B=0x41;
62
TCNT1H=0x00;
63
TCNT1L=0x00;
64
ICR1H=0x00;
65
ICR1L=0x00;
66
OCR1AH=0x00;
67
OCR1AL=0x00;
68
OCR1BH=0x00;
69
OCR1BL=0x00;
70
71
// Timer/Counter 2 initialization
72
// Clock source: System Clock
73
// Clock value: Timer 2 Stopped
74
// Mode: Normal top=FFh
75
// OC2 output: Disconnected
76
ASSR=0x00;
77
TCCR2=0x00;
78
TCNT2=0x00;
79
OCR2=0x00;
80
81
// External Interrupt(s) initialization
82
// INT0: Off
83
// INT1: Off
84
MCUCR=0x00;
85
86
// Timer(s)/Counter(s) Interrupt(s) initialization
87
TIMSK=0x24;
88
89
// Analog Comparator initialization
90
// Analog Comparator: Off
91
// Analog Comparator Input Capture by Timer/Counter 1: Off
92
ACSR=0x80;
93
SFIOR=0x00;  
94
95
Overflow = 0x00
96
97
// LCD module initialization
98
lcd_init(16);
99
100
// Global enable interrupts
101
#asm("sei")
102
103
while (1)
104
      {
105
      // Place your code here
106
107
      };
108
}

Soll bis jetzt eben die Zeit zwischen zwei Impulsen berechnen und in 
Impulse pro Minute umrechnen. Was noch fehlt ist die Ausgabe der Impulse 
pro Minute über das LCD.

von Karl H. (kbuchegg)


Lesenswert?

Ui. Da fehlts aber an allen Ecken und Enden.
Du solltest dir dringend ein Buch über C zulegen. So ein
Wizard ist kein Ersatz für fehlende Kentnisse.

> hab ich falsch abgeschrieben ...
Du sollst überhaupt nicht abschreiben. Denn beim Abschreiben
entstehen Tippfehler. Und dann jagen 20 Leute Tippfehlern hinterher,
die so gar nicht in deinem Code enthalten sind. Cut&Paste ist
schon lange erfunden.

Aber zur Sache:
1
// Timer 1 overflow interrupt service routine
2
interrupt [TIM1_OVF] void timer1_ovf_isr(void)
3
{
4
  Overflow = Overflow + 1;
5
}
6
7
// Timer 1 input capture interrupt service routine
8
interrupt [TIM1_CAPT] void timer1_capt_isr(void)
9
{
10
  Dummy = TCNT1;
11
  Dummy = Dummy + 65536 * Overflow; 
12
  Dummy = Dummy * 0,000000125;
13
  Impulse = 60 / Dummy;
14
  
15
}

Ein C Compiler arbeitet deinen Source Code von oben nach unten durch.
Variablen müssen definiert (oder deklariert) werden, bevor sie
das erste mal benutzt werden können.

In deiner Overflow ISR benutzt du zb. eine Variable namens 'Overflow'.
Wenn ich aber von dieser Codestelle in Richtung Anfang des Source
Codes durchschaue, dann findet sich keine Variable dieses Namens.
Selbiges für die Variable Dummy und Impulse in der zweiten ISR.

Und by the way. Das hier
1
// Declare your global variables here   
2
typedef unsigned char uint8_t Overflow ;
3
typedef unsigned long uint32_t Dummy ;
4
typedef unsigned char uint8_t Inmpulse ;

sind keine Variablendefinitionen, sondern die Vereinbarungen für
neue Datentypen. Du definierst, dass der Datentyp 'Overflow' synonym
zu einem unsigned char uint8_t sein soll. Halt, Moment, das
ist insgesamt noch nicht mal ein korrekter typedef sondern
ein Syntax Error.

Von diversen fehlenden ; reden wir mal gar nicht.

In Programmiersprachen wird normalerweise meistens eine
Dezimalpunkt als . geschrieben und nicht als ,
, hat in C eine ganz spezielle Bedeutung.
1
// some data types to simplify typing
2
typedef unsigned char uint8_t;
3
typedef unsigned long uint32_t;
4
typedef unsigned char uint8_t;
5
6
// Declare your global variables here   
7
uint8_t Overflow;
8
uint32_t Dummy;
9
uint8_t Inmpulse;
10
11
// Timer 1 overflow interrupt service routine
12
interrupt [TIM1_OVF] void timer1_ovf_isr(void)
13
{
14
  Overflow = Overflow + 1;
15
}
16
17
// Timer 1 input capture interrupt service routine
18
interrupt [TIM1_CAPT] void timer1_capt_isr(void)
19
{
20
  Dummy = TCNT1;
21
  Dummy = Dummy + 65536 * Overflow; 
22
  Dummy = Dummy * 0.000000125;
23
  Impulse = 60 / Dummy;
24
}

Ob bei der Multiplikation mit 0.000000125 jemals etwas anderes
als 0 herauskommen wird? Hängt wohl vom Wert für TCNT1 ab.
Mal nachrechnen. TCNT1 muss mindestens einen Wert von 8000000
haben, damit die Multiplikation etwas ergibt, was größer als 1
ist. Blöd ist nur, dass TCNT1 eine 16 Bit Variable ist und daher
niemals größer als 65535 werden kann. OK. Overflow könnte dich
noch retten und den Wert entsprechend hoch treiben. Dann mussten
aber pro Messung mindestens 123 Overflows anfallen.
In allen anderen Fällen ist Dummy dann einfach nur 0. Wodurch
sofort das nächste Problem entsteht

   Impulse = 60 / Dummy;

Da Dummy den Wert 0 haben wird, ergibt das eine Division durch 0.
Autsch!

Also so gehts nicht. Da musst du dir schon eine andere Berechnung
einfallen lassen, um aus dem Timerwert auf die Frequenz zurückzurechnen.
Und denk dran: Du hast nur ganze Zahlen! Keine Kommastellen, auch
nicht in Zwischenergebnissen.

Im Übrigen bezweifle ich mal die letzte Umrechnung auf Impulse/Minute.
Wenn du in 1 Sekunde 1 Impuls misst, dann misst du in 1 Minute
60 Impulse.
   Imulse = 60 * Dummy;

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.