mikrocontroller.net

Forum: Compiler & IDEs Software-UART: Problem beim Empfangen


Autor: BruceCompanys (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

ich habe die Suche benutzt und leider konnte mir kein gefundener Beitrag 
hier im Forum (oder bei Google) helfen.

Folgendes Problem:
Ich habe für eine Facharbeit einen Software-UART für den ATtiny13 
geschrieben.
Dafür wird der Timer TCNT0 über die beiden Register OCR0A zum Senden und 
OCR0B zum Empfangen benutzt.
Zum Empfangen des Startbits bei dem Standard RS232 wird der Interrupt 
INT0 zum Erkennen von steigenden Flanken benutzt.

Aber das Empfangen klappt vorne und hinten nicht, dafür aber das Senden.
Meine Frage ist jetzt: Warum läuft das nicht? Ich hab jetzt so oft über 
den Quellcode geblinzelt, dass ich den nicht mehr sehen kann incl. 
Fehler.

Könnte jmd.eventuell mal ein Auge draufwerfen?

MfG
BruceCompanys

#include "uart.h"



static volatile uint16_t outframe;



static volatile uint16_t inframe;

static volatile uint8_t indata, inbits, received;



void uart_init()

{

  /* Sichern des Status Registers incl. I-Flag */

  uint8_t sreg = SREG;

    cli();



    SUART_TXD_DDR   |= (1 << SUART_TXD_BIT);  /* Pin als Ausgang */

  SUART_TXD_PORT   &= ~(1 << SUART_TXD_BIT);  /* Low-Pegel */



  SUART_RXD_DDR   &= ~(1 << SUART_RXD_BIT);  /* Pin als Eingang */

  SUART_RXD_PORT   |= (1 << SUART_RXD_BIT);   /* Pull-up Widerstand */



  /* Output Compare Register A 

    Anzahl der Taktzyklen für eine Bitlänge 

    

    F_CPU = 1,2 MHz; Baudrate = 9600 => 125 Taktzyklen pro Bitlänge */

  OCR0A = 125; /* OCR0A wird zum Senden verwendet */

  OCR0B = 183; /* OCR0B wird zum Empfangen verwendet. Korrigierter Stand, eig. 188! */



  /* Timer/Control Register A: setzte Clear Timer/Counter on Compare Match  */

  TCCR0A |= (1 << WGM01);



  /* Timer/Control Register B: stoppe Timer*/

  TCCR0B = 0; /* REF NO.: F02/01 */



    /* Setze Interrupt und Flags für Timer zurück */

  TIFR0  = (1 << OCF0B) | (1 << OCF0A);

  TIMSK0 = 0;



    /* Interrupt wird ausgelöst bei steigender Flanke an PB1 */

    MCUCR   |= (1 << ISC01) | (1 << ISC00);

    GIFR    = (1 << INTF0);

    GIMSK   &= ~(1 << INT0);

  

  SREG = sreg;

}



void uart_putc(const char c)

{

  /* Warte bis alles gesendet ist */

  do {

    sei(); nop(); cli();

    } while (outframe);



  /* frame = *.P.7.6.5.4.3.2.1.0.S   S=Start(0), P=Stop(1), *=Endmarke(1) */

    outframe = (3 << 9) | (((uint8_t) c) << 1);



    /* Setze Interrupt für Timer zurück */

    TIFR0   = (1 << OCF0A);



  /* Timer/Counter Interrupt Mask Register 

    OCIE0A = 1

    => Timer/Counter0 Output Compare Match A Interrupt Enable 

  

    Aktiviere Interrupt für Timer */

  TIMSK0 |= (1 << OCIE0A);

    TCCR0B |= (1 << CS00); /* starte Timer mit 1,2 MHz Takt*/

  

  sei();

}



char uart_getc()

{

    cli();

    GIFR    = (1 << INTF0);

    GIMSK   |= (1 << INT0);

  

  received = 0;

    sei();



  /* Warte bis alles empfangen ist */

  while(received == 0) {}



    TCCR0B   = 0; /* Stoppe Timer wieder REF NO.: F02/01 */

    received = 0;



  return (char) indata;

}



/* Senden - Interrupt Service Routine */

ISR(TIM0_COMPA_vect)

{

    uint16_t data = outframe;

   

    if(data & 1)   SUART_TXD_PORT &= ~(1 << SUART_TXD_BIT);

  else       SUART_TXD_PORT |=  (1 << SUART_TXD_BIT);



     /* Endmarke erreicht, kille Interrupt */

    if (1 == data) {

        TIMSK0 &= ~(1 << OCIE0A);

        TCCR0B  = 0; /* stoppe Timer REF NO.: F02/01 */

    }   

   

    outframe = data >> 1;

}



/* Empfangen - Interrupt Service Routine */

ISR(INT0_vect)

{

    OCR0B    = 181; /* REF NO.: F01/01 */

  TIFR0    = (1 << OCF0B);

  TIMSK0   |= (1 << OCIE0B);

    TCCR0B  |= (1 << CS00); /* Starte Timer mit 1,2 Mhz Takt */



    GIMSK   &= ~(1 << INT0);



  inframe  = 0;

  inbits    = 0;

}



ISR(TIM0_COMPB_vect)

{

  uint16_t data = inframe >> 1;

    OCR0B = 123;



  if(!(SUART_RXD_PIN & (1 << SUART_RXD_BIT))) {

        data |= (1 << 9);

  }



  uint8_t bits = inbits+1;



  if(bits >= 10) {

    /* Kontrolle von Start- und Stoppbit */

        if( !(data & 1) && (data >= (1 << 9)) ) {

          indata = data >> 1;

    }



        received = 1;

        TIMSK0 &= ~(1 << OCIE0B);

    } else {

        inbits = bits;

        inframe = data;

    }

}

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
BruceCompanys schrieb:
> F_CPU = 1,2 MHz; Baudrate = 9600 => 125 Taktzyklen pro Bitlänge */

Wenn man nicht super optimal programmiert, sind 125 Zyklen schneller 
vorbei, als Du denkst.
Z.B. eine 16Bit Variable im Interrupt incrementieren, dauert schon 18 
Zyklen.
Overhead für einen leeren Interrupt: 25 Zyklen usw.

Setz in mal besser auf 9,6MHz.


Peter

Autor: BruceCompanys (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Vielen Dank erst einmal für deine schnelle Hilfe!

Habe die Fusebits für 9,6 MHz gesetzt, leider funktioniert das Empfangen 
immer noch nicht richtig.

Werde mal versuchen die Werte von OCR0B weiter herunter zu drehen.

Wobei ich den Takt für den Timer bei 1,2 Mhz gelassen habe, da das 
Timer-Register sonst überläuft (und mir nichts bringt.)

MfG
Bruce

Autor: Tropenhitze (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wenn Du weder Quarz noch keram. Resonator als Frequenzgeber hast, 
kannste einen UART - ob soft oder hard - vergessen.

Autor: BruceCompanys (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Benutze den internen RC-Oszillator, der reicht mir bisher von der 
Genauigkeit.

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.