/* -------------------------------------------------------------------- softwareuart.c Sourcedatei fuer das Einbinden eines softwarebasierenden UART. Fuer das Timing des UART wird Timer 2 im Interruptbetrieb verwendet. Da der PFS154 nur einen einzigen Interruptvektor fuer alle Quellen hat, ist die ISR in eine Funktion uart_sample ausgelagert worden. Diese Funktion muss innhalb des main-Programms im Interruptvektor eingetragen werden. Usage: void interrupt(void) __interrupt(0) { // Timer2 Interrupt if (INTRQ & INTRQ_TM2) { uart_sample(); INTRQ &= ~INTRQ_TM2; } // hier koennen andere Interruptquellen eingehaengt werden } Dieses Vorgehen ermoeglicht, auch andere Interruptquellen zu bedienen MCU : PFS1xx Compiler : SDCC 4.01 oder neuer 10.06.2026 R. Seelig -------------------------------------------------------------------- */ #include "softwareuart.h" // -------------------------------------------------------- // fuer Sampling innerhalb des ISR // -------------------------------------------------------- volatile uint8_t uart_rx_byte; volatile uint8_t uart_rx_ready = 0; volatile uint8_t uart_clktick = 0; volatile uint8_t rx_shift = 0; // -------------------------------------------------------- // Ringbuffer fuer UART // -------------------------------------------------------- uint8_t uart_buf[UART_BUF_SIZE]; uint8_t uart_head = 0; uint8_t uart_tail = 0; /* -------------------------------------------------------- tim2_init Beteiligte Register: TM2C (timer2 control register) TM2CT (timer2 counter register) TM2S (timer2 scalar reigster) TM2B (Boundaryregister, aehnlich einem compare match) siehe timer_2_3_reg.txt Initialisiert Timer2 fuer Intervallaufruf, Intervall wird Receive mit 4-fach Oversampling betreiben. Uebergabe: mode: 0 = 52us fuer 4800 Bd bei 4-fach Oversampling mode: 1 = 104us fuer 2400 Bd mode: 2 = 208us fuer 1200 Bd -------------------------------------------------------- */ void tim2_init(uint8_t mode) { TM2C = TM2C_CLK_SYSCLK; // SYSCLK verwenden (F_CPU = 8 MHz) switch (mode) { case 0 : // Intervall-Init 52 us => 4800 Bd { TM2S = TM2S_PRESCALE_NONE | TM2S_SCALE_DIV4; break; } case 1 : // Intervall-Init 104 us => 2400 Bd { TM2S = TM2S_PRESCALE_NONE | TM2S_SCALE_DIV8; break; } case 2 : // Intervall-Init 104 us => 2400 Bd { TM2S = TM2S_PRESCALE_NONE | TM2S_SCALE_DIV16; break; } default : break; } TM2B = 103; // Mode 0: 0.5 * 104 = 52 us // Mode 0: 1 * 104 = 52 us TM2CT= 0x00; // Counter auf 0 setzen __engint(); // grundsaetzlich Interrupt zulassen INTEN |= INTEN_TM2; // Timerinterrupt zulassen } /* -------------------------------------------------------- uart_bufferadd fuegt dem Ringbuffer ein neues Zeichen hinzu Uebergabe: c : einzufuegendes Zeichen -------------------------------------------------------- */ void uart_bufferadd(uint8_t c) { uint8_t next = (uart_head + 1) & (UART_BUF_SIZE - 1); if (next == uart_tail) { uart_tail = (uart_tail + 1) & (UART_BUF_SIZE - 1); // overwrite old } uart_buf[uart_head] = c; uart_head = next; } /* -------------------------------------------------------- uart_bufferget liest ein Zeichen aus dem Ringbuffer Uebergabe: *c : Pointer auf ein uint8_t, in den das Zeichen eingelesen wird Rueckgabe: 0 => kein Zeichen verfuegbar 1 => Zeichen wurde gelesen -------------------------------------------------------- */ uint8_t uart_bufferget(uint8_t *c) { if (uart_head == uart_tail) return 0; *c = uart_buf[uart_tail]; uart_tail = (uart_tail + 1) & (UART_BUF_SIZE - 1); return 1; } /* -------------------------------------------------------- uart_sample da es nur einen einzigen Interrupthandler gibt wird uart_sample der Uebersichtlichkeit vom Handler aufgerufen. uart_sample detektiert ein Startbit und erfasst mittels 4-fach Oversampling ein ein- gehendes Zeichen auf der Software-Uart. Beschreibt die folgende globale Variable: uart_rx_ready : 0 => kein Zeichen verfuegbar 1 => Zeichen eingegangen, muss vom Userprogramm zurueckgesetzt werden uart_rx_byte : eingegangenes Zeichen Wird Timer2 fuer Intervall alle 52us initialisiert, entspricht das einer Baudrate von 4800 bd. -------------------------------------------------------- */ void uart_sample(void) { volatile static uint8_t uart_active = 0; volatile static uint8_t sample_active = 0; volatile static uint8_t sample_cnt = 4; volatile static uint8_t bit_cnt = 0; // globaler Clock-Tigger als Zeitbasis fuer Transmitter 4-fach Oversampling uart_clktick++; if (uart_clktick == 4) uart_clktick= 0; // ------------------------------- // idle: auf Startbit warten // ------------------------------- if (!uart_active) { if (!is_rxpin()) { uart_active = 1; // Startbit erkannt sample_cnt = 6; // Bitmitte wird gesampelt bit_cnt = 0; rx_shift = 0; } INTRQ &= ~INTRQ_TM2; // Interruptanforderung quittieren return; } // ------------------------------- // active: Zeichenempfang nach // Startbit // ------------------------------- if (uart_active) { sample_cnt--; if (!sample_cnt) { // fuer Debugzwecke mittels Logicanalyzer // debugpin_toggle(); if (bit_cnt < 8) { if (is_rxpin()) { rx_shift |= (1 << bit_cnt); } bit_cnt++; sample_cnt= 4; INTRQ &= ~INTRQ_TM2; // Interruptanforderung quittieren return; } bit_cnt++; if (bit_cnt > 16) { // Sample nach Stopbit beenden // gelesenes Zeichen im Ringbuffer einfuegen uart_bufferadd(rx_shift); uart_rx_ready = 1; sample_cnt= 4; sample_active= 0; uart_active = 0; INTRQ &= ~INTRQ_TM2; // Interruptanforderung quittieren return; } } } } /* -------------------------------------------------------- uart_ischar stellt fest, ob ein Zeichen im Receive-Buffer verfuegbar ist, liest dieses aber nicht ein. -------------------------------------------------------- */ uint8_t uart_ischar(void) { return (uart_head != uart_tail); } /* -------------------------------------------------------- uart_getchar wartet auf den Eingang eines Zeichens und gibt dieses als Funktionsergebnis zurueck -------------------------------------------------------- */ uint8_t uart_getchar(void) { uint8_t c; // warten bis ein Zeichen eintrifft, blockierendes Zeichen lesen while (uart_head == uart_tail); c = uart_buf[uart_tail]; uart_tail = (uart_tail + 1) & (UART_BUF_SIZE - 1); return c; } /* -------------------------------------------------------- uart_putchar sendet ein Zeichen als auf Tx-Pin. Uebergabe: ch : zu sendendes Zeichen -------------------------------------------------------- */ void uart_putchar(uint8_t ch) { volatile uint8_t oldticker; uint8_t i; oldticker= uart_clktick; while(oldticker== uart_clktick); // damit alle Bits (inkl. Startbit) dieselbe // Zyklusdauer haben while(uart_clktick); // fuer den Fall, dass uart_clktick Wert von 1..3 hat // Startbit txpin_clr(); while(!uart_clktick); // uart_clktick wird aus Interrupt gezaehlt while(uart_clktick); // 8 Datenbits for (i= 0; i< 8; i++) { if (ch & (1 << i)) { txpin_set(); } else { txpin_clr(); } while(!uart_clktick); while(uart_clktick); } // Stopbit txpin_set(); while(uart_clktick); while(!uart_clktick); } /* ------------------------------------------------------------ uart_puts gibt einen AsciiZ String aus * Uebergabe: *p : Zeiger auf auszugebenden AsciiZ String ------------------------------------------------------------ */ void uart_puts(char *p) { while(*p) { uart_putchar(*p); p++; } } /* ------------------------------------------------------------ uart_init Initialisierung von: - Timer 2 fuer Baudrateneinstellung - der Pins rxpin / txpin als Input / Output ------------------------------------------------------------ */ void uart_init(void) { #if (baudrate == 4800) tim2_init(0); #elif (baudrate == 2400) tim2_init(1); #elif (baudrate == 1200) tim2_init(2); #endif rxpin_init(); txpin_init(); txpin_set(); }