Forum: Compiler & IDEs AT90CAN128 Problem mit I/O und Timer0


von DS (Gast)


Lesenswert?

Hallo liebes Forum,
ich habe ein Problem mit dem PORT B und dem Timer0 beim AT90CAN128 auf 
dem STK500 und hoffe auf hilfreiche Antworten.

Der Timer hat ein OC0A Ausgang und liefert an diesem ein Rechtecksignal, 
das gleiche Rechtecksignal möchte ich in der ISR auch erzeugen. Das 
Problem bei unten stehendem Code ist, dass das Bit PORTB1 relativ häufig 
beim verlassen der ISR nochmal toggelt, d.h. es kommen entweder kurze 
Impulse oder ein high bzw. low Pegel, der doppelt so lange ist.

Das Problem kann man beheben, indem man in der main vor dem Port-Zugriff 
den globalen Interrupt deaktiviert und danach wieder aktiviert oder man 
im AVR-Studio die Code-Optimierung auf -Os stellt.

Hat jemand eine Lösung unter der Voraussetzung, dass ich den Interrupt 
in der main nicht deaktivieren möchte und die Code-Optimierung auf -O0 
lasse?

Dass ich keine Tastenentprellung habe ist mir bewusst, ändert aber an 
dem Phänomen nichts.

Besten Dank für Eure Hilfe

<c>
#include <avr/io.h>
#include <avr/interrupt.h>

ISR(TIMER0_COMP_vect)
{
    PORTB ^= (1<<PB4);
}


int main(void)
{
    // Port B - LED´s, STK500
    PORTB = 0x00;   // Output Low
    DDRB  = 0xff;   // Output

    // Port E - Taster, STK500
    PORTE = 0xff;   // Pull-up
    DDRE  = 0x00;   // Input


    // Timer0
    TCCR0A = 0x51;
    TCNT0 = 0;
    OCR0A = 0xFF;
    TIMSK0 |= (1<<OCIE0A);
    TIFR0 |= (1<<OCF0A);


    sei();             // Globaler Interrupt Enable

    while(1)
    {

        if(PINE & (1<<PB0))
             PORTB |= (1<<PB1);
        else
            PORTB &= ~(1<<PB1);
    }
    return(0);
}
</c>

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Ganz verstehe ich deine Problembeschreibung nicht.

Du erzeugst ein Signal an PB4 (in der ISR) und PB1 (in main-while als 
Reaktion auf ein Eingangssignal auf PINE.0) und du erzeugst ein Signal 
an OC0A?

Das Signal an PB1 soll synchron zu OC0A sein. Wie soll das gehen, wenn 
PINE.0 ein Taster (unentprellt) ist? Oder hast du OC0A mit PINE.0 
verbunden?

Ansonsten zu dem Problem der Unterberechung einer I0-Portmanipulation 
durch einen Interrupt:

Schau nach, ob sich beim AT90CAN128 PB1 an PORTB bereits toggeln lässt 
indem du das PB1 Bit in PINB setzt. Bei "modernen" AVR ist das möglich 
und da das eine Maschinenanweisung ist, unterbricht der Timer das nicht.

Wenn das nicht geht, würde ich Inline Assembler mit den Instruktionen 
SBI und CBI benutzen, um das eine Bit zu setzen bzw. zu löschen. Damit 
bekommst du auch eine nicht unterbrechbare Maschinenanweisung. Also 
quasi von Hand programmieren, was der Optimizer bei -Os macht (s. 
Disassemblerlistung)

von DS (Gast)


Angehängte Dateien:

Lesenswert?

Hallo Stefan,
danke für die schnelle Antwort. In deinem ersten Abschnitt hast du den 
Sachverhalt richtig erkannt. nicht PORTB1 soll synchron zu OC0A sein 
sondern PORTB4, siehe ISR. OC0A ist fest auf PORTB7 und gibt mir ein 
Rechtecksignal, dass ich mit folgendem Register TCCR0A = 0x51 (hier 
hatte ich ein Fehler, es muss 0x1D heißen) einstelle. Es wird nicht 
synchron sein, aber sagen wir quasi synchron (die rund 10 Takte sollen 
keine Rolle spielen). Zum Taster, den betätige ich zu keinem Zeitpunkt, 
dennoch tritt das Problem durch die Zeile -> PORTB |= (1<<PB1); auf. Das 
Bit beim schreiben in PINB funktioniert, ändert aber am Verhalten 
nichts.
Hinweis, würde ich in der main statt dem PORTB einen beliebig anderen 
PORT ansteuern, so tritt das Problem nicht auf. Wie funktioniert das mit 
dem Inline-Assembler?
Im Anhang zeig ich mal ein Oszillogramm, rot=OC0A; blau=PORTB4

Gruß
DS

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Ah mit dem Diagramm wird das klarer!

Also das OC0A = PORTB.7 Signal wird gestört. Das sieht nach einem 
Interrupt in dieser Sequenz aus:

PORTB &= ~(1<<PB1);

Das ist ja aufgeschlüsselt so etwas:

tmp = PORTB;      // hier ist OC0A z.b. LOW
tmp &= ~(1<<PB1); // hier irgendwo wechselt OC0A nach HIGH
PORTB = tmp;      // hier bekommt OC0A den alten LOW Wert zurück :-(

Ich würde hier die CBI/SBI Inline Assembler Methode für die Manipulation 
von PORTB.1 benutzen, wenn -Os nicht möglich ist.

Inline Assembler bin ich nicht sattelfest, d.h. ich müsste auch erst 
hier nachsehen: 
http://www.nongnu.org/avr-libc/user-manual/inline__asm.html

Vielleicht so etwas:

asm("cbi %0, 1" : : "I" (_SFR_IO_ADDR(PORTB)) ); // PORTB.1 löschen
asm("sbi %0, 1" : : "I" (_SFR_IO_ADDR(PORTB)) ); // PORTB.1 setzen

von DS (Gast)


Lesenswert?

Vielen Dank für die schnelle Hilfe.

if(PINE & (1<<TASTER0))
    asm("sbi %0, 1" : : "I" (_SFR_IO_ADDR(PORTB)) );
else
    asm("cbi %0, 1" : : "I" (_SFR_IO_ADDR(PORTB)) );

hat das Problem gelöst.

Schönen Abend noch

Gruß
DS

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.