Forum: Mikrocontroller und Digitale Elektronik Drehzahlmesser zählt nicht


von Dobby (Gast)


Lesenswert?

Guten abend zusammen,
ich habe ein kleines Problem mit meinem Drehzahlmesser Projekt, das die 
Drehzahl von einem Lüfter o.ä. auf einem LCD ausgeben soll.
Das LCD (hapsim) wird anständig initialisiert und gibt auch den 
anfangswert 0 aus.
Zum testen habe ich den INT0 zum auslösen auf eine der Hapsim Tasten 
gelegt.
Wenn ich nun eine Taste drücke und so eine fallende flanke erzeuge 
sollte ISR(INT0_vect) ausgelöst werden und mit count++ die erzeugten 
flanken zählen.
Nach 1sek liest ISR(TIMER1_COMPA_vect)den wert von count++ aus, übergibt 
ihn an das Hauptprogramm, wo ein string zur Ausgabe auf dem LCD erzeugt 
wird. Der wert auf dem LCD bleibt aber bei 0!
Eventuell kann mal jemand über den code schauen und mir bei der 
Fehlersuche helfen?
Habe versucht alles so gut wie möglich zu kommentieren.
Kann OCR1A falsch berechnet sein? (ISR(TIMER1_COMPA_vect) Soll genau 
nach 1sek auslösen)

Benutzt wird ein Attiny2313 @ 4MHz
HAPSIM zum testen der LCD ausgabe.

Vielen Dank für die Hilfe
1
#include <stdlib.h>
2
#include <avr/io.h>
3
#include <stdio.h>
4
#include <avr/interrupt.h>
5
#include <util/delay.h>
6
#include <avr/pgmspace.h>
7
#include <math.h>
8
#include "lcd.h"
9
10
11
volatile uint16_t count=0;   //Revolution Counter
12
volatile uint16_t rpm=0;   //Revolution per minute
13
char buffer[16];
14
15
16
int main(void)
17
{
18
  //INIT
19
  GIMSK |= (1<<INT0);            //INT0 enable S.60
20
  MCUCR |= (1<<ISC01);           //The falling edge of INT1 generates an interrupt request. S.60
21
  asm("sei");                //Global Interrupt enable
22
  TCCR1B |= (1<<WGM12) | (1<<CS12) | (1<<CS10);   //CTC Mode S.106
23
                          //Prescaler 1024 S.107
24
  OCR1A = 3906;              //Compare value (fCPU/(fOCnA*2*N)) (4MHz/(0.5Hz*2*1024) S.108
25
  TIMSK |= (1<<OCIE1A);           //Triggers interrupt when timer count reaches compare value S.109
26
  lcd_init(LCD_DISP_ON);
27
  lcd_clrscr();        
28
  
29
30
while(1)
31
    {
32
  lcd_clrscr();
33
  itoa( rpm, buffer, 16 );
34
  lcd_puts(buffer);
35
  _delay_ms(500);
36
    }
37
}
38
39
ISR(INT0_vect)
40
{
41
  //CPU jumps here automatically when INT0 pin detect a falling edge
42
    count++;
43
}
44
45
ISR(TIMER1_COMPA_vect)
46
{
47
  //CPU Jumps here every 1 sec
48
    rpm=count*60;
49
    count=0;
50
}

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Ich kann dir nur raten, schrittweise vorzugehen, und das Programm 
erstmal im Simulator zu prüfen. Setze dir z.B. einen Breakpoint in der 
Timer Routine und checke, ob sie wirklich jede Sekunde einmal gezündet 
wird.
Anschliessend probierst du, ob beim low setzen des INTO wirklich die ISR 
angesprungen wird und 'count' hochzählt usw.
Mach dir statt itoa auch einfach mal eine simple Routine, die dir das 
word in hex aufs LCD wirft, z.B.
1
/* Some basic routines to print hecadecimal 
2
*/
3
// table unfortunately needs to reside in RAM for now
4
char hextable[18] = "0123456789ABCDEF";
5
// very simple output to hex 
6
static void printbyte(const uint8_t data)
7
{
8
  lcd_putc(hextable[data >> 4]);
9
  lcd_putc(hextable[data & 0x0f]);
10
}
11
static void printword(const uint16_t data)
12
{
13
  printbyte(data >> 8);
14
  printbyte(data);
15
}
Oh, noch was:
1
  asm("sei");                //Global Interrupt enable
2
// kannst du auch einfach so schreiben:
3
  sei();

von J.-u. G. (juwe)


Lesenswert?

Auch wenn das derzeit nicht Ursache Deines Problems ist:

Dobby schrieb:
> volatile uint16_t rpm=0;

> itoa( rpm, buffer, 16 );

Für unsigned int statt "itoa" besser "utoa" verwenden.

von Sebastian W. (sebastian_w29)


Lesenswert?

S.84: Statt OCR1A = 3906; lieber OCR1AH = 3906 << 8; OCR1AL = 3906 & 
0xFF;

Ausserdem solltest du noch den Zugriff aif rpm in main() mit cli() sei() 
umgeben, weil rpm aus zwei Bytes besteht und daher nicht atomar gelesen 
wird.

LG; Sebastian

von Dobby (Gast)


Lesenswert?

Hallo,
ich habe es geschafft, der Drehzahlmesser läuft und es werden jede 
Sekunde die RPM auf dem LC aktuallisiert.
Jetzt habe ich noch ein "kleines" problem....

Da ich immer nur eine Sekunde den INT0 Pin auswerte und den wert auf 
umdrehungen pro minute hochrechne, habe ich recht große Sprünge in 
meinen Werten (+-30RPM pro sekunde). Um das Problem in den Griff zu 
kriegen bilde ich nun den mittelwert:

(AlterWert+NeuerWert)/2= AuszugebenderWert

Jetzt springen meine werte nurnoch um +-15, jenachdem ob eben die ISR 
einmal mehr oder weniger pro sekunde ausgelöst wurde.

Habe auch schon 4 Auseinanderfolgende Werte ausgelesen und davon den 
mittelwert ausgegeben. Mit dieser Lösung war ich aber nicht zufrieden, 
weil so nur alle 4sekunden der Wert aktuallisiert wurde und ich will 
wirklich jede Sekunde den aktuellen Wert.

Gibt es eventuell noch eine andere möglichkeit um diese Krassen Sprünge 
in den Griff zu kriegen?

Vielen Dank

von Bernhard S. (b_spitzer)


Lesenswert?

Bei niedrigen Frequenzen zählt man keine Impulse sondern misst den 
Abstand zwischen den Impulsen. Wenn die Frequenz im Bereich von wenigen 
1000U/min ist, dann liegen zwischen 2 Impulsen einige 10ms (1000U/min => 
60ms/Impuls!!).

von m.n. (Gast)


Lesenswert?


von Dobby (Gast)


Lesenswert?

Bernhard Spitzer schrieb:
> Bei niedrigen Frequenzen zählt man keine Impulse sondern misst den
> Abstand zwischen den Impulsen.

Dafür wird aber auch einer der externen Interrupts benutzt?

von Juergen R. (stumpjumper)


Lesenswert?

Warum nimmst Du nicht ein Array von z.B. 10 Messwerten und berechnest 
dadurch einen gleitenden Mittelwert, dami kannst Du das doch schon 
ziemlich glätten, je mehr Messwerte umso glatter das Ergebnis.

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.