diff options
Diffstat (limited to 'avr/usart.c')
| -rw-r--r-- | avr/usart.c | 569 |
1 files changed, 569 insertions, 0 deletions
diff --git a/avr/usart.c b/avr/usart.c new file mode 100644 index 0000000..ed5dabf --- /dev/null +++ b/avr/usart.c @@ -0,0 +1,569 @@ +/************************************************************************/ +/* */ +/* File: avr/usart.c */ +/* Description: serial line device. */ +/* Version: 1.0 */ +/* Author: Otto Mattik */ +/* */ +/* (C)Copyright Otto Mattik 2014-2021. */ +/* */ +/* This file is a part of 'armen' (a tiny operating system). */ +/* 'armen' is distributed under the CeCILL-V2.1 licence. For more */ +/* details about this licence, please visit the website cecill.info */ +/* */ +/************************************************************************/ + +#ifndef AVR_UART_C + #error "do not use this code directly, it must be included via avr_uart.c" +#endif + +/* +#define OUTPUT_INTERRUPT +*/ + +#include <avr/io.h> +#include <avr/interrupt.h> + +/* + * the uart is configured with 8 data bits, no parity and 1 stop bit. +*/ + +#if defined(__AVR_ATtiny841__) + + #define UART0_INPUT_PIN PA2 /* receive */ + #define UART0_OUTPUT_PIN PA1 /* transmit */ + #define UART0_CONFIG_PINS() (\ + DDRA = (DDRA|(1<<UART0_OUTPUT_PIN))\ + & ~(1<<UART0_INPUT_PIN)\ + ) + #define UART1_INPUT_PIN PA4 /* receive */ + #define UART1_OUTPUT_PIN PA5 /* transmit */ + #define UART1_CONFIG_PINS() (\ + DDRA = (DDRA|(1<<UART1_OUTPUT_PIN))\ + & ~(1<<UART1_INPUT_PIN)\ + ) +#elif defined(__AVR_ATtiny1634__) + + #define UART0_INPUT_PIN PA7 /* receive */ + #define UART0_OUTPUT_PIN PB0 /* transmit */ + #define UART0_CONFIG_PINS() (\ + DDRA = (DDRA|(1<<UART0_OUTPUT_PIN))\ + & ~(1<<UART0_INPUT_PIN)\ + ) + #define UART1_INPUT_PIN PB1 /* receive */ + #define UART1_OUTPUT_PIN PB2 /* transmit */ + #define UART1_CONFIG_PINS() (\ + DDRB = (DDRB|(1<<UART1_OUTPUT_PIN))\ + & ~(1<<UART1_INPUT_PIN)\ + ) +#elif defined(__AVR_ATmega328P__) + + #define UART0_INPUT_PIN PD0 /* receive (uno:0) */ + #define UART0_OUTPUT_PIN PD1 /* transmit (uno:1) */ + #define UART0_CONFIG_PINS() (\ + DDRD = (DDRD|(1<<UART0_OUTPUT_PIN))\ + & ~(1<<UART0_INPUT_PIN)\ + ) +#elif defined(__AVR_ATmega2560__) + + #define UART0_INPUT_PIN PE0 + #define UART0_OUTPUT_PIN PE1 + #define UART0_CONFIG_PINS() (\ + DDRE = (DDRE|(1<<UART0_OUTPUT_PIN))\ + & ~(1<<UART0_INPUT_PIN)\ + ) + #define UART1_INPUT_PIN PD2 /* receive (mega:19) */ + #define UART1_OUTPUT_PIN PD3 /* transmit (mega:18) */ + #define UART1_CONFIG_PINS() (\ + DDRD = (DDRD|(1<<UART1_OUTPUT_PIN))\ + & ~(1<<UART1_INPUT_PIN)\ + ) + #define UART2_INPUT_PIN PH0 /* receive (mega:17) */ + #define UART2_OUTPUT_PIN PH1 /* transmit (mega:16) */ + #define UART2_CONFIG_PINS() (\ + DDRH = (DDRH|(1<<UART2_OUTPUT_PIN))\ + & ~(1<<UART2_INPUT_PIN)\ + ) + #define UART3_INPUT_PIN PJ0 /* receive (mega:15) */ + #define UART3_OUTPUT_PIN PJ1 /* transmit (mega:14) */ + #define UART3_CONFIG_PINS() (\ + DDRJ = (DDRJ|(1<<UART3_OUTPUT_PIN))\ + & ~(1<<UART3_INPUT_PIN)\ + ) +#else + #error "no or unavailable cpu defined" +#endif + +#if defined(USART_RX_vect) + #define UART0_INPUT_INT USART_RX_vect + #define UART0_OUTPUT_INT USART_UDRE_vect +#else + #define UART0_INPUT_INT USART0_RX_vect + #define UART0_OUTPUT_INT USART_UDRE_vect +#endif + +#define UART0_RX_BYTE UDR0 +#define UART0_RX_INT_ENABLE() (UCSR0B |= (1<<RXCIE0)) +#define UART0_RX_INT_DISABLE() (UCSR0B &= ~(1<<RXCIE0)) +#define UART0_TX_BYTE UDR0 +#define UART0_TX_INT_ENABLE() (UCSR0B |= (1<<UDRIE0)) +#define UART0_TX_INT_DISABLE() (UCSR0B &= ~(1<<UDRIE0)) +#define UART0_TX_NOT_READY() ((UCSR0A & (1<<UDRE0))==0) +#define UART0_CONFIG_BAUDRATE(b) (\ + UCSR0A = (1<<U2X0),\ + UBRR0H = (b)>>8, UBRR0L = (b)&0xFF\ + ) +#define UART0_CONFIG_DEVICE() (\ + UCSR0B = (1<<RXEN0)|(1<<TXEN0),\ + UCSR0C = (1<<UCSZ00)|(1<<UCSZ01)\ + ) +#define UART0_CONFIG(b) (\ + UART0_CONFIG_PINS(),\ + UART0_CONFIG_BAUDRATE(b),\ + UART0_CONFIG_DEVICE()\ + ) + + +#define UART1_INPUT_INT USART1_RX_vect +#define UART1_OUTPUT_INT USART1_UDRE_vect + +#define UART1_RX_BYTE UDR1 +#define UART1_RX_INT_ENABLE() (UCSR1B |= (1<<RXCIE1)) +#define UART1_RX_INT_DISABLE() (UCSR1B &= ~(1<<RXCIE1)) +#define UART1_TX_BYTE UDR1 +#define UART1_TX_INT_ENABLE() (UCSR1B |= (1<<UDRIE1)) +#define UART1_TX_INT_DISABLE() (UCSR1B &= ~(1<<UDRIE1)) +#define UART1_TX_NOT_READY() ((UCSR1A & (1<<UDRE1))==0) +#define UART1_CONFIG_BAUDRATE(b) (\ + UCSR1A = (1<<U2X1),\ + UBRR1H = (b)>>8, UBRR1L = (b)&0xFF\ + ) +#define UART1_CONFIG_DEVICE() (\ + UCSR1B = (1<<RXEN1)|(1<<TXEN1),\ + UCSR1C = (1<<UCSZ10)|(1<<UCSZ11)\ + ) +#define UART1_CONFIG(b) (\ + UART1_CONFIG_PINS(),\ + UART1_CONFIG_BAUDRATE(b),\ + UART1_CONFIG_DEVICE()\ + ) + + +#define UART2_INPUT_INT USART2_RX_vect +#define UART2_OUTPUT_INT USART2_UDRE_vect + +#define UART2_RX_BYTE UDR2 +#define UART2_RX_INT_ENABLE() (UCSR2B |= (1<<RXCIE2)) +#define UART2_RX_INT_DISABLE() (UCSR2B &= ~(1<<RXCIE2)) +#define UART2_TX_BYTE UDR2 +#define UART2_TX_INT_ENABLE() (UCSR2B |= (1<<UDRIE2)) +#define UART2_TX_INT_DISABLE() (UCSR2B &= ~(1<<UDRIE2)) +#define UART2_TX_NOT_READY() ((UCSR2A & (1<<UDRE2))==0) +#define UART2_CONFIG_BAUDRATE(b) (\ + UCSR2A = (1<<U2X2),\ + UBRR2H = (b)>>8, UBRR2L = (b)&0xFF\ + ) +#define UART2_CONFIG_DEVICE() (\ + UCSR2B = (1<<RXEN2)|(1<<TXEN2),\ + UCSR2C = (1<<UCSZ10)|(1<<UCSZ11)\ + ) +#define UART2_CONFIG(b) (\ + UART2_CONFIG_PINS(),\ + UART2_CONFIG_BAUDRATE(b),\ + UART2_CONFIG_DEVICE()\ + ) + + +#define UART3_INPUT_INT USART3_RX_vect +#define UART3_OUTPUT_INT USART3_UDRE_vect + +#define UART3_RX_BYTE UDR3 +#define UART3_RX_INT_ENABLE() (UCSR3B |= (1<<RXCIE3)) +#define UART3_RX_INT_DISABLE() (UCSR3B &= ~(1<<RXCIE3)) +#define UART3_TX_BYTE UDR3 +#define UART3_TX_INT_ENABLE() (UCSR3B |= (1<<UDRIE3)) +#define UART3_TX_INT_DISABLE() (UCSR3B &= ~(1<<UDRIE3)) +#define UART3_TX_NOT_READY() ((UCSR3A & (1<<UDRE3))==0) +#define UART3_CONFIG_BAUDRATE(b) (\ + UCSR3A = (1<<U2X3),\ + UBRR3H = (b)>>8, UBRR3L = (b)&0xFF\ + ) +#define UART3_CONFIG_DEVICE() (\ + UCSR3B = (1<<RXEN3)|(1<<TXEN3),\ + UCSR3C = (1<<UCSZ10)|(1<<UCSZ11)\ + ) +#define UART3_CONFIG(b) (\ + UART3_CONFIG_PINS(),\ + UART3_CONFIG_BAUDRATE(b),\ + UART3_CONFIG_DEVICE()\ + ) + +static uint16_t uart_bauds[8] = { +#if (F_CPU == 16000000UL ) + 1666, 832, 416, 207, 103, 51, 34, 16 +#elif (F_CPU == 12000000UL ) + 1249, 624, 312, 155, 77, 38, 25, 12 +#elif (F_CPU == 8000000UL ) + 832, 416, 207, 103, 51, 34, 16, 8 +#elif (F_CPU == 4000000UL ) + 416, 207, 103, 51, 25, 16, 8, 3 +#else + #error "no parameters for the clock frequency" +#endif +}; + +#define UBRR_VALUE(b) (((F_CPU)+4UL*(b))/(8UL*(b))-1UL) + +#define UART_OPEN 0x80 +#define UART_ECHOO_CHECK 0x40 +#define UART_TX_PROGRESS 0x20 +#define UART_CONFIG_MASK 0x0F + +static struct { + uint8_t state; /* state and configuration */ + uint8_t cd_byte; /* received byte if collision detection */ + uint8_t rx_head; /* fifo index */ + uint8_t rx_tail; /* " " */ + uint8_t rx_buffer[UART_RX_BUFFER_SIZE+1]; +} volatile uarts[ARMEN_UARTS]; + +static void _uart_recv_byte( uint8_t device, uint8_t byte ) +{ + if( uarts[device].state & UART_ECHOO_CHECK ) + { + uarts[device].cd_byte = byte; + uarts[device].state &= ~UART_ECHOO_CHECK; + } + else + { + uint8_t head; + + head = uarts[device].rx_head + 1; + if( head == (UART_RX_BUFFER_SIZE + 1) ) + head = 0; + if( head != uarts[device].rx_tail ) + { + uarts[device].rx_buffer[head] = byte; + uarts[device].rx_head = head; + _wakeup( EVENT_UART0 + device, 0 ); + } + } +} + +/* + * occurs at end of byte reception +*/ +ISR( UART0_INPUT_INT ) +{ + uint8_t byte = UART0_RX_BYTE; + _uart_recv_byte( 0, byte ); +} + +#if (ARMEN_UARTS >= 2) +ISR( UART1_INPUT_INT ) +{ + uint8_t byte = UART1_RX_BYTE; + _uart_recv_byte( 1, byte ); +} +#endif + +#if (ARMEN_UARTS >= 3) +ISR( UART2_INPUT_INT ) +{ + uint8_t byte = UART2_RX_BYTE; + _uart_recv_byte( 2, byte ); +} +#endif + +#if (ARMEN_UARTS == 4) +ISR( UART3_INPUT_INT ) +{ + uint8_t byte = UART3_RX_BYTE; + _uart_recv_byte( 3, byte ); +} +#endif + +/* + * occurs at end of byte transmission +*/ +#ifdef OUTPUT_INTERRUPT + ISR( UART0_OUTPUT_INT ) + { + uarts[0].state &= ~UART_TX_PROGRESS; + UART0_TX_INT_DISABLE( ); + } + + #if (ARMEN_UARTS >= 2) + ISR( UART1_OUTPUT_INT ) + { + uarts[1].state &= ~UART_TX_PROGRESS; + UART1_TX_INT_DISABLE( ); + } + #endif + + #if (ARMEN_UARTS >= 3) + ISR( UART2_OUTPUT_INT ) + { + uarts[2].state &= ~UART_TX_PROGRESS; + UART2_TX_INT_DISABLE( ); + } + #endif + + #if (ARMEN_UARTS == 4) + ISR( UART3_OUTPUT_INT ) + { + uarts[3].state &= ~UART_TX_PROGRESS; + UART3_TX_INT_DISABLE( ); + } + #endif +#endif + +/* + * initialize device + * + * input: + * device identifier + * config uart configuration + * output: + * 0 if success, otherwise -1 +*/ +static int8_t _uart_open( uint8_t device, uint8_t config ) +{ + uint8_t bauds = config & BAUDRATE; + uint16_t ubrr = uart_bauds[bauds]; + +#ifdef CHECK_PARAM + if( (uarts[device].state & UART_OPEN) != 0 || bauds > B115200 ) + return( -1 ); +#endif + uarts[device].rx_head = uarts[device].rx_tail = 0; + uarts[device].state = (config & UART_CONFIG_MASK) | UART_OPEN; + uarts[device].rx_head = uarts[device].rx_tail = 0; + if( device == 0 ) + { + UART0_CONFIG( ubrr ); + UART0_RX_INT_ENABLE( ); + } +#if (ARMEN_UARTS >= 2) + else if( device == 1 ) + { + UART1_CONFIG( ubrr ); + UART1_RX_INT_ENABLE( ); + } +#endif +#if (ARMEN_UARTS >= 3) + else if( device == 2 ) + { + UART2_CONFIG( ubrr ); + UART2_RX_INT_ENABLE( ); + } +#endif +#if (ARMEN_UARTS == 4) + else + { + UART3_CONFIG( ubrr ); + UART3_RX_INT_ENABLE( ); + } +#endif +} + +/* + * release device + * + * input: + * device identifier + * output: + * 0 if success, otherwise -1 +*/ +static int8_t _uart_close( uint8_t device ) +{ +#ifdef CHECK_PARAM + if( (uarts[device].state & UART_OPEN) == 0 ) + return( -1 ); +#endif + if( device == 0 ) + { + UART0_RX_INT_DISABLE( ); +#ifdef OUTPUT_INTERRUPT + UART0_TX_INT_DISABLE( ); +#endif + } +#if (ARMEN_UARTS >= 2) + else if( device == 1 ) + { + UART1_RX_INT_DISABLE( ); + #ifdef OUTPUT_INTERRUPT + UART1_TX_INT_DISABLE( ); + #endif + } +#endif +#if (ARMEN_UARTS >= 3) + else if( device == 2 ) + { + UART2_RX_INT_DISABLE( ); + #ifdef OUTPUT_INTERRUPT + UART2_TX_INT_DISABLE( ); + #endif + } +#endif +#if (ARMEN_UARTS == 4) + else + { + UART3_RX_INT_DISABLE( ); + #ifdef OUTPUT_INTERRUPT + UART3_TX_INT_DISABLE( ); + #endif + } +#endif + uarts[device].state = 0; + uarts[device].rx_head = uarts[device].rx_tail = 0; + return( 0 ); +} + +/* + * read from device + * + * input: + * device identifier + * byte address of byte to read + * output: + * 0 if success, otherwise -1 +*/ +static int8_t _uart_read( uint8_t device, uint8_t* byte ) +{ + uint8_t tail; + +#ifdef CHECK_PARAM + if( (uarts[device].state & UART_OPEN) == 0 || byte == (uint8_t*)0 ) + return( -1 ); +#endif + tail = uarts[device].rx_tail; + if( tail == uarts[device].rx_head ) + wait_event( EVENT_UART0 + device ); + if( ++tail == (UART_RX_BUFFER_SIZE + 1) ) + tail = 0; + *byte = uarts[device].rx_buffer[tail]; + uarts[device].rx_tail = tail; + return( 0 ); +} + +/* + * write to device + * + * input: + * device identifier + * byte byte to write + * output: + * 0 if success, otherwise -1 +*/ +static int8_t _uart_write( uint8_t device, uint8_t byte ) +{ + if( (uarts[device].state & UART_OPEN) == 0 ) + return( -1 ); + +#ifdef OUTPUT_INTERRUPT + uarts[device].state |= UART_TX_PROGRESS; +#endif + if( uarts[device].state & ECHOO ) + uarts[device].state |= UART_ECHOO_CHECK; + if( device == 0 ) + { +#ifdef OUTPUT_INTERRUPT + UART0_TX_BYTE = byte; + UART0_TX_INT_ENABLE( ); +#else + while( UART0_TX_NOT_READY( ) ) ; + UART0_TX_BYTE = byte; +#endif + } +#if (ARMEN_UARTS >= 2) + else if( device == 1 ) + { + #ifdef OUTPUT_INTERRUPT + UART1_TX_BYTE = byte; + UART1_TX_INT_ENABLE( ); + #else + while( UART1_TX_NOT_READY( ) ) ; + UART1_TX_BYTE = byte; + #endif + } +#endif +#if (ARMEN_UARTS >= 3) + else if( device == 2 ) + { + #ifdef OUTPUT_INTERRUPT + UART2_TX_BYTE = byte; + UART2_TX_INT_ENABLE( ); + #else + while( UART2_TX_NOT_READY( ) ) ; + UART2_TX_BYTE = byte; + #endif + } +#endif +#if (ARMEN_UARTS == 4) + else + { + #ifdef OUTPUT_INTERRUPT + UART3_TX_BYTE = byte; + UART3_TX_INT_ENABLE( ); + #else + while( UART3_TX_NOT_READY( ) ) ; + UART3_TX_BYTE = byte; + #endif + } +#endif + +#ifdef OUTPUT_INTERRUPT + while( uarts[device].state & UART_TX_PROGRESS ) ; +#else + if( device == 0 ) + while( UART0_TX_NOT_READY( ) ) ; + #if (ARMEN_UARTS >= 2) + else if( device == 1 ) + while( UART1_TX_NOT_READY( ) ) ; + #endif + #if (ARMEN_UARTS >= 3) + else if( device == 2 ) + while( UART2_TX_NOT_READY( ) ) ; + #endif + #if (ARMEN_UARTS == 4) + else + while( UART3_TX_NOT_READY( ) ) ; + #endif +#endif + if( uarts[device].state & ECHOO ) + { + while( uarts[device].state & UART_ECHOO_CHECK ) ; + if( uarts[device].cd_byte != byte ) + return( -1 ); + } + return( 0 ); +} + +/* + * device special feature + * + * input: + * device identifier + * request device dependent + * args list of arguments + * output: + * 0 if success, otherwise -1 +*/ +#ifdef UART_IOCTL +static int8_t _uart_ioctl( uint8_t device, uint8_t request, va_list args ) +{ + if( (uarts[device].state & UART_OPEN) != 0 ) + { + if( request == UART_GETP ) + { + uint8_t* p = va_arg( args, uint8_t* ); + + *p = uarts[device].state & UART_CONFIG_MASK; + return( 0 ) + } + } + return( -1 ); +} +#endif + |
