VictimOfMyOwn
18.05.2006, 23:19
hi ho...
ich bin erst kürzlich vom atmel 89cxx51 auf den atmega8 umgestiegen.
mit dem 4051 hatte ich bereits erfolgreich einen drehzahlmesser zurechtgetüftelt welcher bis (mindestens) 20.000 upm auf +/- 1 upm genau war und die drehzahl auf einem 2x16 LCD ausgegeben hat (inklusive menüführung mit etlichen einstellmöglichkeiten etc.)
das selbe versuche ich nun auch mit dem atmega8. nur leider funzt das noch net so wie es soll.
das grundprinzip ist nach wie vor das gleiche:
elektromotor mit loch- bzw. schlitzscheibe und gabellichtschranke, welche ihr signal an INT0 weitergibt. TIMER0 inkrementiert bei jedem timerüberlauf eine variable. die INT0 funktion schreibt diese variable, welche die überläufe innerhalb einer umdrehung erhält in eine temp - variable (um diese dann weiterverarbeiten zu können) und nullt danach die variable mit den überlaufen sofort wieder.
im main (in der while schleife) wird dann die drehzahl berechnet und auf dem LCD ausgegeben.
bis zu einer gewissen drehzahl funktioniert das ganze auch. ich lasse mir auf dem LCD zur zeit die drehzahl und die überläufe einer umdrehung ausgeben. wird die drehzahl dann "zu hoch" springt die anzahl der überläufe ab und zu auf einen sehr niedrigen wert, als wenn der timer die variable nicht bei jedem überlauf inkrementieren würde. aus folge dessen springt dann auch die ausgegebene drehzahl immerzu auf völlig falsche werte.
habe dann mal den prescale von TIMER0 auf 64 gesetzt, damit der TIMER0 interrupt nicht so oft ausgelöst wird...nun läuft das ganze schon etwas stabiler...bis zu einer drehzahl von ~5.000 upm...darüber spinnt es dann wieder rum.
wie gesagt lasse ich mir die gezählten überläufe auch auf dem LCD ausgeben...irgendwann fängt es eben an von z.b. 200 überlaufen/umdrehung kurzzeitig auf 40 überläufe/umdrehung zu springen...was natürlich nicht sein kann, da die motordrehzahl gleich bleibt.
ich tippe auf interruptkonflikte. bei dem 4051 hat es aber wie gesagt immer wunderbar funktioniert...hier war die berechnung sogar im INT0 eingebunden...und der 4051 (12mhz) war 16 mal langsamer als der atmega8 mit 16mhz quarz.
ich stell hier mal den atmega8 quellcode rein...denke mal, daß ich einfach noch zu unsauber programmiere...vielleicht könnt ihr mir ja beim meinem problem helfen und mir evtl. ein paar programmiertips geben.
#include<io.h>
#include<interrupt.h>
#define F_CPU 16000000UL
#include<util/delay.h>
#define RS PC4
#define RW PC5
#define E PB0
#define D4 PC0
#define D5 PC1
#define D6 PC2
#define D7 PC3
// Funktionsdeklaration
void sendd(unsigned char datenh, unsigned char datenl);
void sendb(unsigned char datenh, unsigned char datenl);
void ausgabe(unsigned int zahl);
// Variablen deklarieren
unsigned char htausender, ztausender, tausender, hunderter, zehner, einer;
unsigned long int ueberlauf, ueberlauf_speicher, zaehler, teiler, upm;
int main(void)
{
// Register initialisieren
GICR = 0x40; // INT0 Enable
MCUCR = 0x03; // INT0 reagiert auf steigende Flanke
TCCR0 = 0x03; // Timer0 64 Prescale
TIMSK = 0x01; // Timer0 Interrupt enable
// Ports und Variablen initialisieren
DDRC = 0xFF; // Port C als Ausgang
DDRD = 0x01; // Port D als Ausgang
DDRB = 0x01; // Port B.0 als Ausgang
// LCD initialisieren
/*
PORT C:
7 6 5 4 3 2 1 0
- - RW RS D7 D6 D5 D4 */
// 4 bit 2 zeilen 5x7 dots -> 00101000
sendb(0b0010, 0b1000);
// display on, cursor off, blink off -> 00001100
sendb(0b0000, 0b1100);
// Clear Display & Cursor Home
sendb(0b0000, 0b0001);
sei(); // Interrupts aktivieren SREG = 0x80 (I = High)
while(1)
{
teiler++;
if(teiler == 500000)
{
cli();
ueberlauf_speicher = ueberlauf_speicher * 64;
teiler = 0;
sendb(0b1000, 0b0000);
ausgabe(ueberlauf_speicher);
upm = (unsigned long int)60000000 / (ueberlauf_speicher * 16);
sendb(0b1100, 0b0000);
ausgabe(upm);
sei();
}
}
}
void sendb(unsigned char datenh, unsigned char datenl)
{
PORTC = datenh; // High - Nibble
PORTB |= 1 << E; // Enable 1
_delay_us(40);
PORTB &= ~1 << E; // Enable 0
PORTC = datenl; // Low - Nibble
PORTB |= 1 << E; // Enable 1
_delay_us(40);
PORTB &= ~1 << E; // Enable 0
}
void sendd(unsigned char datenh, unsigned char datenl)
{
PORTC = datenh + 16; // High - Nibble
PORTB |= 1 << E; // Enable 1
_delay_us(40);
PORTB &= ~1 << E; // Enable 0
PORTC = datenl + 16; // Low - Nibble
PORTB |= 1 << E; // Enable 1
_delay_us(40);
PORTB &= ~1 << E; // Enable 0
}
void ausgabe(unsigned int zahl)
{
unsigned int rest;
htausender = (unsigned int)zahl / 100000;
rest = zahl%100000;
ztausender = (unsigned int)rest / 10000;
rest = rest%10000;
tausender = (unsigned int)rest / 1000;
rest = rest%1000;
hunderter = (unsigned int)rest / 100;
rest = rest%100;
zehner = (unsigned int)rest / 10;
einer = rest%10;
sendd(0b0011, htausender);
sendd(0b0011, ztausender);
sendd(0b0011, tausender);
sendd(0b0011, hunderter);
sendd(0b0011, zehner);
sendd(0b0011, einer);
}
ISR(INT0_vect)
{
ueberlauf_speicher = ueberlauf;
ueberlauf = 0;
zaehler++;
}
ISR(TIMER0_OVF_vect)
{
ueberlauf++;
}
mfg
ich bin erst kürzlich vom atmel 89cxx51 auf den atmega8 umgestiegen.
mit dem 4051 hatte ich bereits erfolgreich einen drehzahlmesser zurechtgetüftelt welcher bis (mindestens) 20.000 upm auf +/- 1 upm genau war und die drehzahl auf einem 2x16 LCD ausgegeben hat (inklusive menüführung mit etlichen einstellmöglichkeiten etc.)
das selbe versuche ich nun auch mit dem atmega8. nur leider funzt das noch net so wie es soll.
das grundprinzip ist nach wie vor das gleiche:
elektromotor mit loch- bzw. schlitzscheibe und gabellichtschranke, welche ihr signal an INT0 weitergibt. TIMER0 inkrementiert bei jedem timerüberlauf eine variable. die INT0 funktion schreibt diese variable, welche die überläufe innerhalb einer umdrehung erhält in eine temp - variable (um diese dann weiterverarbeiten zu können) und nullt danach die variable mit den überlaufen sofort wieder.
im main (in der while schleife) wird dann die drehzahl berechnet und auf dem LCD ausgegeben.
bis zu einer gewissen drehzahl funktioniert das ganze auch. ich lasse mir auf dem LCD zur zeit die drehzahl und die überläufe einer umdrehung ausgeben. wird die drehzahl dann "zu hoch" springt die anzahl der überläufe ab und zu auf einen sehr niedrigen wert, als wenn der timer die variable nicht bei jedem überlauf inkrementieren würde. aus folge dessen springt dann auch die ausgegebene drehzahl immerzu auf völlig falsche werte.
habe dann mal den prescale von TIMER0 auf 64 gesetzt, damit der TIMER0 interrupt nicht so oft ausgelöst wird...nun läuft das ganze schon etwas stabiler...bis zu einer drehzahl von ~5.000 upm...darüber spinnt es dann wieder rum.
wie gesagt lasse ich mir die gezählten überläufe auch auf dem LCD ausgeben...irgendwann fängt es eben an von z.b. 200 überlaufen/umdrehung kurzzeitig auf 40 überläufe/umdrehung zu springen...was natürlich nicht sein kann, da die motordrehzahl gleich bleibt.
ich tippe auf interruptkonflikte. bei dem 4051 hat es aber wie gesagt immer wunderbar funktioniert...hier war die berechnung sogar im INT0 eingebunden...und der 4051 (12mhz) war 16 mal langsamer als der atmega8 mit 16mhz quarz.
ich stell hier mal den atmega8 quellcode rein...denke mal, daß ich einfach noch zu unsauber programmiere...vielleicht könnt ihr mir ja beim meinem problem helfen und mir evtl. ein paar programmiertips geben.
#include<io.h>
#include<interrupt.h>
#define F_CPU 16000000UL
#include<util/delay.h>
#define RS PC4
#define RW PC5
#define E PB0
#define D4 PC0
#define D5 PC1
#define D6 PC2
#define D7 PC3
// Funktionsdeklaration
void sendd(unsigned char datenh, unsigned char datenl);
void sendb(unsigned char datenh, unsigned char datenl);
void ausgabe(unsigned int zahl);
// Variablen deklarieren
unsigned char htausender, ztausender, tausender, hunderter, zehner, einer;
unsigned long int ueberlauf, ueberlauf_speicher, zaehler, teiler, upm;
int main(void)
{
// Register initialisieren
GICR = 0x40; // INT0 Enable
MCUCR = 0x03; // INT0 reagiert auf steigende Flanke
TCCR0 = 0x03; // Timer0 64 Prescale
TIMSK = 0x01; // Timer0 Interrupt enable
// Ports und Variablen initialisieren
DDRC = 0xFF; // Port C als Ausgang
DDRD = 0x01; // Port D als Ausgang
DDRB = 0x01; // Port B.0 als Ausgang
// LCD initialisieren
/*
PORT C:
7 6 5 4 3 2 1 0
- - RW RS D7 D6 D5 D4 */
// 4 bit 2 zeilen 5x7 dots -> 00101000
sendb(0b0010, 0b1000);
// display on, cursor off, blink off -> 00001100
sendb(0b0000, 0b1100);
// Clear Display & Cursor Home
sendb(0b0000, 0b0001);
sei(); // Interrupts aktivieren SREG = 0x80 (I = High)
while(1)
{
teiler++;
if(teiler == 500000)
{
cli();
ueberlauf_speicher = ueberlauf_speicher * 64;
teiler = 0;
sendb(0b1000, 0b0000);
ausgabe(ueberlauf_speicher);
upm = (unsigned long int)60000000 / (ueberlauf_speicher * 16);
sendb(0b1100, 0b0000);
ausgabe(upm);
sei();
}
}
}
void sendb(unsigned char datenh, unsigned char datenl)
{
PORTC = datenh; // High - Nibble
PORTB |= 1 << E; // Enable 1
_delay_us(40);
PORTB &= ~1 << E; // Enable 0
PORTC = datenl; // Low - Nibble
PORTB |= 1 << E; // Enable 1
_delay_us(40);
PORTB &= ~1 << E; // Enable 0
}
void sendd(unsigned char datenh, unsigned char datenl)
{
PORTC = datenh + 16; // High - Nibble
PORTB |= 1 << E; // Enable 1
_delay_us(40);
PORTB &= ~1 << E; // Enable 0
PORTC = datenl + 16; // Low - Nibble
PORTB |= 1 << E; // Enable 1
_delay_us(40);
PORTB &= ~1 << E; // Enable 0
}
void ausgabe(unsigned int zahl)
{
unsigned int rest;
htausender = (unsigned int)zahl / 100000;
rest = zahl%100000;
ztausender = (unsigned int)rest / 10000;
rest = rest%10000;
tausender = (unsigned int)rest / 1000;
rest = rest%1000;
hunderter = (unsigned int)rest / 100;
rest = rest%100;
zehner = (unsigned int)rest / 10;
einer = rest%10;
sendd(0b0011, htausender);
sendd(0b0011, ztausender);
sendd(0b0011, tausender);
sendd(0b0011, hunderter);
sendd(0b0011, zehner);
sendd(0b0011, einer);
}
ISR(INT0_vect)
{
ueberlauf_speicher = ueberlauf;
ueberlauf = 0;
zaehler++;
}
ISR(TIMER0_OVF_vect)
{
ueberlauf++;
}
mfg