aboutsummaryrefslogtreecommitdiff
path: root/avr/usart.c
diff options
context:
space:
mode:
authorOtto Mattik <otto@mattik.org>2021-07-08 18:10:55 +0200
committerOtto Mattik <otto@mattik.org>2021-07-08 18:10:55 +0200
commitda34d97efb21719b2b332f8c60b2750d11bcde1f (patch)
tree2de9fe89f6d79b8ebfcde64c5e86204e904aedf2 /avr/usart.c
downloadarmen-a861fb554ced7709555d2d1b639c534e3f45a83f.tar.gz
armen-a861fb554ced7709555d2d1b639c534e3f45a83f.zip
git: update to v1.0HEADv1.0master
Diffstat (limited to 'avr/usart.c')
-rw-r--r--avr/usart.c569
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
+