You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
187 lines
4.1 KiB
C
187 lines
4.1 KiB
C
/*
|
|
* ----------------------------------------------------------------------------
|
|
* "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$
|
|
*/
|
|
|
|
#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;
|
|
}
|
|
|