-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathuart.c
139 lines (120 loc) · 4.4 KB
/
uart.c
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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
/**
* @file uart.c
* @author Paul Nykiel
* @date 12.04.19
* @brief Implementation of the library functions for the universal-asynchronous-receiver-transmitter module.
* @ingroup HAL
*/
#include "uart.h"
#include <avr/interrupt.h>
#include <avr/io.h>
#include <math.h>
#include <stdbool.h>
#ifndef RING_BUFFER_SIZE
#define RING_BUFFER_SIZE 512
#endif
#define MIN_U2X_BAUD (F_CPU / (16L * (255L + 1L)) + 1L)
typedef struct {
volatile uart_callback_t callback;
volatile uint8_t data[RING_BUFFER_SIZE];
// Nomenclature: Data is written into the buf at head and read at tail
volatile uint8_t tail;
volatile uint8_t head;
volatile bool full;
volatile bool ready;
volatile uint8_t *const ucsra, *const ucsrb, *const ucsrc;
volatile uint16_t *const ubrr;
volatile uint8_t *const udr;
} uart_instance_t;
static volatile uart_instance_t instances[] = {
{.ucsra = &UCSR0A, .ucsrb = &UCSR0B, .ucsrc = &UCSR0C, .ubrr = &UBRR0, .udr = &UDR0},
{.ucsra = &UCSR1A, .ucsrb = &UCSR1B, .ucsrc = &UCSR1C, .ubrr = &UBRR1, .udr = &UDR1},
{.ucsra = &UCSR2A, .ucsrb = &UCSR2B, .ucsrc = &UCSR2C, .ubrr = &UBRR2, .udr = &UDR2},
{.ucsra = &UCSR3A, .ucsrb = &UCSR3B, .ucsrc = &UCSR3C, .ubrr = &UBRR3, .udr = &UDR3},
};
// Data register empty
void tx_handler(uint8_t uart_id) {
if (instances[uart_id].full || instances[uart_id].head != instances[uart_id].tail) { // Still data available
*instances[uart_id].udr = instances[uart_id].data[instances[uart_id].tail];
instances[uart_id].tail = (instances[uart_id].tail + 1) % RING_BUFFER_SIZE;
instances[uart_id].full = false;
} else {
instances[uart_id].ready = true;
}
}
// New byte
void rx_handler(uint8_t uart_id) {
if (instances[uart_id].callback) {
(*instances[uart_id].callback)(*instances[uart_id].udr);
}
}
ISR(USART0_RX_vect) {
rx_handler(0);
}
ISR(USART0_TX_vect) {
tx_handler(0);
}
ISR(USART1_RX_vect) {
rx_handler(1);
}
ISR(USART1_TX_vect) {
tx_handler(1);
}
ISR(USART2_RX_vect) {
rx_handler(2);
}
ISR(USART2_TX_vect) {
tx_handler(2);
}
ISR(USART3_RX_vect) {
rx_handler(3);
}
ISR(USART3_TX_vect) {
tx_handler(3);
}
void uart_init(uint8_t uart_id, uint32_t baud, uart_parity_t parity, uint8_t stop_bits, uart_callback_t rx_callback) {
instances[uart_id].callback = rx_callback;
instances[uart_id].tail = 0;
instances[uart_id].head = 0;
instances[uart_id].full = false;
instances[uart_id].ready = true;
*instances[uart_id].ucsra = 0b00000000; // Reset all flags, no double transmission speed
*instances[uart_id].ucsrb = 0b11011000; // RX complete isr, TX complete isr, rx, tx enabled, 8 bit data
*instances[uart_id].ucsrc = 0b00000110; // Async UART, No Parity, 1 Stop Bit, 8 data bits
*instances[uart_id].ucsrc |= (uint8_t) parity << 4U; // Set parity to actual value
if (stop_bits == 2) {
*instances[uart_id].ucsrc |= 1U << 3U; // Set stop bits to two if required
}
if (baud > MIN_U2X_BAUD) {
*instances[uart_id].ubrr = (F_CPU + 4 * baud) / (8 * baud) - 1;
*instances[uart_id].ucsra |= (1U << 1U); // Switch to double transmission speed
} else {
*instances[uart_id].ubrr = (F_CPU + 8 * baud) / (16 * baud) - 1;
}
}
void uart_send_byte(uint8_t uart_id, uint8_t data) {
if (instances[uart_id].ready) { // Can send directly
instances[uart_id].ready = false;
*instances[uart_id].udr = data;
} else if (instances[uart_id].tail != instances[uart_id].head ||
!instances[uart_id].full) { // Not empty but needs to be added to queue
instances[uart_id].data[instances[uart_id].head] = data;
instances[uart_id].head = (instances[uart_id].head + 1) % RING_BUFFER_SIZE;
if (instances[uart_id].tail == instances[uart_id].head) {
instances[uart_id].full = true;
}
} else { // Queue full
while (instances[uart_id].full)
;
instances[uart_id].data[instances[uart_id].head] = data;
instances[uart_id].head = (instances[uart_id].head + 1) % RING_BUFFER_SIZE;
if (instances[uart_id].tail == instances[uart_id].head) {
instances[uart_id].full = true;
}
}
}
void uart_send_buf(uint8_t uart_id, const uint8_t *data, uint16_t size) {
for (uint16_t c = 0; c < size; c++) {
uart_send_byte(uart_id, data[c]);
}
}