PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Umstieg von 89cxx51 auf Atmega8 - Interruptkonflikte ?



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

SprinterSB
20.05.2006, 21:05
Guck mal da: da steht's:

Fallstricke bei der C-Programmierung#Nicht-atomarer Code (https://www.roboternetz.de/wissen/index.php/Fallstricke_bei_der_C-Programmierung#Nicht-atomarer_Code)

Ausserdem sind die in den ISRs angefassten Variablen flüchtig.
Dementsprechend gehören die Dinger als volatile qualifiziert. Such mal danach, und du wirst überschüttet ;-)

VictimOfMyOwn
20.05.2006, 21:23
hi ho...

danke für den link. werd mich da mal durcharbeiten.

ich dachte, daß es auch ohne volatile geht wenn ich die variablen global deklariere...allerdings habe ich die codeoptimierung auf "ganz hoch"...dadurch werden die wohl wieder flüchtig...?

desweiteren habe ich auch festgestellt, daß die lichtschranke "krucklige" signale ausgibt wenn sich der motor schnell dreht und anfängt zu vibrieren. beim versuch mit frequenzgenerator scheint es keine probleme zu geben.

am motorrad hat es vorhin auch ohne probleme gefunzt.

allerdings wurde das schwanken der ausgegeben drehzahl mit höherem vorteiler wie gesagt weniger...also ist der quellcode auf jeden fall noch nicht perfekt.

mfg

SprinterSB
21.05.2006, 10:35
Was meinst du mit "ganz hoch"? Von -O3 ist abzuraten, die Optimierungen, die dort gemacht werden, sind für µC nicht angebracht. Am besten ist -Os.

Flüchtig sind diese Variablen immer. Ohne Optimierung werden allerdings alle Variablen implizit wie flüchtige behandelt.

Auch mit volatile muss der Zugriff auf mehrbytige Variablen atomar erfolgen. In den ISRs ist er trivialerweise atomar, ausserhalb aber nicht.

Wenn du die ISR-Rate senkst, dann senkst du natürlich auch die Wahrscheinlichkeit für den Fehler, was aber nicht heisst, daß das Programm ok ist.

VictimOfMyOwn
22.05.2006, 11:40
hi ho...

japp, ich meinte -Os...

das mit abschalten kritischer interrupts sobald ein interrupt aufgerufen wurde habe ich auch schon probiert...bringt aber nüscht. ist es nicht eh so, daß wenn während eines interrupts ein anderer auslöst, erst der aktuelle abgearbeitet und dann in den nächsten gesprungen wird ?

volatile hatte ich meine ich auch schon versucht, allerdings auch ohne erfolg...werde es dennoch nochmal probieren.

also einfach die variablen, welche in den interrupts verwendet werden global mit volatile deklarieren, ja ?

desweiteren werde ich mir mal das AVR GCC tutorial angucken...wusste nicht, daß es hier auch eins gibt, kannte bis jetzt nur das von mikrocontroller.net.

mfg