mikrocontroller.net

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


Autor: DS (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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>

Autor: Stefan B. (stefan) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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)

Autor: DS (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht 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

Autor: Stefan B. (stefan) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: DS (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.