summaryrefslogtreecommitdiff
path: root/avr/serial.c
blob: adbc3c430e91f0aaa01ce7bf18b433ff298976a1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
/*
 * (C) Copyright 2014 Leo C. <erbl259-lmu@yahoo.de>
 *
 * SPDX-License-Identifier:	GPL-2.0+
 */

#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/atomic.h>
#include <errno.h>
#include <stdio.h>

#include "ring.h"
#include "serial.h"


static int _write(char c, FILE *stream);
static FILE mystdout = FDEV_SETUP_STREAM(_write,
		NULL, _FDEV_SETUP_WRITE);



#define BUFFER_SIZE 128

#if ((BUFFER_SIZE-1) & BUFFER_SIZE)
# error: BUFFER_SIZE not power of 2
#endif

#if ((BUFFER_SIZE) > 256)
# error: BUFFER_SIZE
#endif

struct ring rx_ring;
struct ring tx_ring;
uint8_t rx_ring_buffer[BUFFER_SIZE];
uint8_t tx_ring_buffer[BUFFER_SIZE];



/* Initialize UART */

void usart0_setup(unsigned long baud) {

	ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {

		PRR0 &= ~_BV(PRUSART0);
		UCSR0B = 0;

		/* Initialize ring buffers. */
		ring_init(&rx_ring, rx_ring_buffer, BUFFER_SIZE);
		ring_init(&tx_ring, tx_ring_buffer, BUFFER_SIZE);

		UCSR0A = 0;
		UBRR0  = F_CPU / baud / 16 - 1;
		UCSR0B = _BV(RXCIE0) | _BV(RXEN0) | _BV(TXEN0);
		UCSR0C = 3 << UCSZ00;
	};
}

/*--------------------------------------------------------------------------*/

/* UART RXC interrupt */

ISR(USART0_RX_vect)
{
	uint8_t d;

	d = UDR0;
	ring_write_ch(&rx_ring, d);
}

/* UART UDRE interrupt */

ISR(USART0_UDRE_vect)
{
	int d = ring_read_ch(&tx_ring);

	if (d < 0) {
		/* Disable TX empty interrupt. */
		UCSR0B = _BV(RXCIE0) | _BV(RXEN0) | _BV(TXEN0);
	} else {
		UDR0 = d;
	}
}

/*--------------------------------------------------------------------------*/

void serial_setup(unsigned long baud)
{
	stdout = &mystdout;
	usart0_setup(baud);
}

/*--------------------------------------------------------------------------*/

int _write(char c, FILE *stream)
{
	(void) stream;

	if (c == '\n')
		serial_putc('\r');
	serial_putc(c);

	return 0;
}

int serial_getc(void)
{
	return ring_read_ch(&rx_ring);
}

void serial_putc(char data)
{
	while (ring_write_ch(&tx_ring, data) < 0)
		;

	/* Enable the TXE interrupt. */
	UCSR0B = _BV(RXCIE0) | _BV(RXEN0) | _BV(TXEN0) | _BV(UDRIE0);
}

uint_fast8_t serial_tstc(void)
{
	return !ring_is_empty(&rx_ring);
}