| /* |
| * ---------------------------------------------------------------------------- |
| * "THE BEER-WARE LICENSE" (Revision 42): |
| * <joerg@FreeBSD.ORG> 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: uart.c 1008 2005-12-28 21:38:59Z joerg_wunsch $ |
| */ |
| |
| #include "defines.h" |
| |
| #include <stdint.h> |
| #include <stdio.h> |
| |
| #include <avr/io.h> |
| |
| #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; |
| } |
| |