/* * ---------------------------------------------------------------------------- * "THE BEER-WARE LICENSE" (Revision 42): * wrote this file. As long as you retain this notice you * can do whatever you want with this stuff. If we meet some day, and you think * this stuff is worth it, you can buy me a beer in return. Joerg Wunsch * ---------------------------------------------------------------------------- * * Stdio demo, UART implementation * * $Id$ */ #include "defines.h" #include #include #include #include "uart.h" /* * Initialize the UART to 9600 Bd, tx/rx, 8N1. */ void uart_init(void) { #if F_CPU < 2000000UL && defined(U2X) UCSRA = _BV(U2X); /* improve baud rate error by using 2x clk */ UBRRL = (F_CPU / (8UL * UART_BAUD)) - 1; #else UBRRL = (F_CPU / (16UL * UART_BAUD)) - 1; #endif UCSRB = _BV(TXEN) | _BV(RXEN); /* tx/rx enable */ } /* * Send character c down the UART Tx, wait until tx holding register * is empty. */ int uart_putchar(char c, FILE *stream) { if (c == '\a') { fputs("*ring*\n", stderr); return 0; } if (c == '\n') uart_putchar('\r', stream); loop_until_bit_is_set(UCSRA, UDRE); UDR = c; return 0; } /* * Receive a character from the UART Rx. * * This features a simple line-editor that allows to delete and * re-edit the characters entered, until either CR or NL is entered. * Printable characters entered will be echoed using uart_putchar(). * * Editing characters: * * . \b (BS) or \177 (DEL) delete the previous character * . ^u kills the entire input buffer * . ^w deletes the previous word * . ^r sends a CR, and then reprints the buffer * . \t will be replaced by a single space * * All other control characters will be ignored. * * The internal line buffer is RX_BUFSIZE (80) characters long, which * includes the terminating \n (but no terminating \0). If the buffer * is full (i. e., at RX_BUFSIZE-1 characters in order to keep space for * the trailing \n), any further input attempts will send a \a to * uart_putchar() (BEL character), although line editing is still * allowed. * * Input errors while talking to the UART will cause an immediate * return of -1 (error indication). Notably, this will be caused by a * framing error (e. g. serial line "break" condition), by an input * overrun, and by a parity error (if parity was enabled and automatic * parity recognition is supported by hardware). * * Successive calls to uart_getchar() will be satisfied from the * internal buffer until that buffer is emptied again. */ int uart_getchar(FILE *stream) { uint8_t c; char *cp, *cp2; static char b[RX_BUFSIZE]; static char *rxp; if (rxp == 0) for (cp = b;;) { loop_until_bit_is_set(UCSRA, RXC); if (UCSRA & _BV(FE)) return _FDEV_EOF; if (UCSRA & _BV(DOR)) return _FDEV_ERR; c = UDR; /* behaviour similar to Unix stty ICRNL */ if (c == '\r') c = '\n'; if (c == '\n') { *cp = c; uart_putchar(c, stream); rxp = b; break; } else if (c == '\t') c = ' '; if ((c >= (uint8_t)' ' && c <= (uint8_t)'\x7e') || c >= (uint8_t)'\xa0') { if (cp == b + RX_BUFSIZE - 1) uart_putchar('\a', stream); else { *cp++ = c; uart_putchar(c, stream); } continue; } switch (c) { case 'c' & 0x1f: return -1; case '\b': case '\x7f': if (cp > b) { uart_putchar('\b', stream); uart_putchar(' ', stream); uart_putchar('\b', stream); cp--; } break; case 'r' & 0x1f: uart_putchar('\r', stream); for (cp2 = b; cp2 < cp; cp2++) uart_putchar(*cp2, stream); break; case 'u' & 0x1f: while (cp > b) { uart_putchar('\b', stream); uart_putchar(' ', stream); uart_putchar('\b', stream); cp--; } break; case 'w' & 0x1f: while (cp > b && cp[-1] != ' ') { uart_putchar('\b', stream); uart_putchar(' ', stream); uart_putchar('\b', stream); cp--; } break; } } c = *rxp++; if (c == '\n') rxp = 0; return c; }