-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathuart.c
More file actions
368 lines (328 loc) · 12 KB
/
Copy pathuart.c
File metadata and controls
368 lines (328 loc) · 12 KB
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
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
/**
* @file uart.c
* @brief Source file with implementation of polling-based UART functions and macros.
*
* This file contains the definitions of function implementations for polling-based UART communication on AVR-Mega-Series microcontrollers. Supports configurable baud rates, frame formats, optional echo, and stdio integration (printf/scanf).
*
* @author g.raf
* @date 2025-12-07
* @version 1.0 Release
* @copyright
* Copyright (c) 2025 g.raf
* Released under the GPLv3 License. (see LICENSE in repository)
*
* @note This file is part of a larger project and subject to the license specified in the repository. For updates and the complete revision history, see the GitHub repository.
*
* @important Interrupts are NOT implemented. Define UART_RXCIE/UART_TXCIE/UART_UDRIE macros to disable polling functions for custom interrupt handling.
*
* @see uart.h for declarations, configuration macros, and related information.
* @see uart_enums.h for UART status and error enumerations.
* @see https://github.com/0x007e/hal-avr-mega-uart "AVR ATmega UART HAL GitHub Repository"
*/
#include "uart.h"
#if UART_STDMODE > 0
// Initialize FILE stream
#if !defined(UART_TXCIE) && !defined(UART_UDRIE) && !defined(UART_RXCIE) && UART_STDMODE == 1
static FILE std_uart = FDEV_SETUP_STREAM(uart_printf, uart_scanf, _FDEV_SETUP_RW);
#elif !defined(UART_TXCIE) && !defined(UART_UDRIE) && UART_STDMODE == 2
static FILE std_uart = FDEV_SETUP_STREAM(uart_printf, NULL, _FDEV_SETUP_WRITE);
#elif !defined(UART_RXCIE) && UART_STDMODE == 3
static FILE std_uart = FDEV_SETUP_STREAM(NULL, uart_scanf, _FDEV_SETUP_READ);
#endif
#endif
#if !defined(UART_RXCIE) && !defined(UART_TXCIE) && !defined(UART_UDRIE)
#if UART_HANDSHAKE > 0
static UART_Handshake uart_handshake_sending = UART_Ready;
#endif
#endif
/**
* @brief Initialize the UART hardware interface with configured parameters.
*
* @details
* This function configures the USART peripheral for polling-based operation:
* - Sets hardware handshake pins (RTS/CTS) if UART_HANDSHAKE==2
* - Calculates and applies baud rate using setbaud.h
* - Configures frame format: data bits, parity, stop bits
* - Enables TX/RX with optional RXC echo and stdio stream assignment
*
* All configuration is derived from uart.h preprocessor macros.
*
* @note Call this function once during system initialization before using UART functions.
* @note stdio streams (stdout/stdin) are assigned only when UART_STDMODE > 0 and no interrupts defined.
*/
void uart_init(void)
{
// Check if hardware flow control is enabled
#if UART_HANDSHAKE == 2
// Setup RTS (output)/CTS (input)
UART_HANDSHAKE_DDR |= (1<<UART_HANDSHAKE_RTS_PIN);
UART_HANDSHAKE_DDR &= ~(1<<UART_HANDSHAKE_CTS_PIN);
#endif
// Check which bit sampling mode should be activated
#if USE_2X
UCSRA |= (1<<U2X); // Setup 8 samples/bit
#else
UCSRA &= ~(1<<U2X); // Setup 16 samples/bit
#endif
UBRRH = UBRRH_VALUE; // Calculated through setbaud.h
UBRRL = UBRRL_VALUE; // Calculated through setbaud.h
unsigned char SETREG = (1<<URSEL); // Activate URSEL (normally in register UCSRC)
SETREG |= ((0x03 & (UART_DATASIZE - 5))<<UCSZ0); // Setup data size
#if UART_PARITY > 0
SETREG |= ((0x03 & (UART_PARITY + 1))<<UPM0); // UART_Parity Mode
#endif
#if UART_STOPBITS > 1
SETREG |= ((0x01 & (UART_STOPBITS - 1))<<USBS); // Setup stop bits
#endif
UCSRC = SETREG; // Write SETREG settings to UCSRC
UCSRB = (1<<RXEN) | (1<<TXEN); // Activate UART transmitter and receiver
// Interrupt control
// Receiver interrupt setup
#ifdef UART_RXCIE
UCSRB |= (1<<RXCIE);
#endif
// Transmitter interrupt setup
#if defined(UART_TXCIE) && !defined(UART_UDRIE)
UCSRB |= (1<<TXCIE);
#endif
// Transmitter interrupt setup
#if !defined(UART_TXCIE) && defined(UART_UDRIE)
UCSRB |= (1<<UDRIE);
#endif
#if !defined(UART_TXCIE) && !defined(UART_UDRIE) && (UART_STDMODE == 1 || UART_STDMODE == 2)
stdout = &std_uart;
#endif
#if !defined(UART_RXCIE) && UART_STDMODE == 1 || UART_STDMODE == 3
stdin = &std_uart;
#endif
}
/**
* @brief Disable the UART hardware interface and reset configuration.
*
* @details
* This function completely disables the USART peripheral by clearing TXEN/RXEN bits and all interrupt enables. Call before reconfiguring UART or entering power-save modes.
*/
void uart_disable(void)
{
UCSRB &= ~((1<<RXEN) | (1<<TXEN));
UCSRB &= ~((1<<RXCIE) | (1<<TXCIE) | (1<<UDRIE));
UCSRA |= (1<<TXC);
#if !defined(UART_TXCIE) && !defined(UART_UDRIE) && (UART_STDMODE == 1 || UART_STDMODE == 2)
stdout = NULL;
#endif
#if !defined(UART_RXCIE) && UART_STDMODE == 1 || UART_STDMODE == 3
stdin = NULL;
#endif
#if UART_HANDSHAKE == 2
UART_HANDSHAKE_DDR &= ~((1<<UART_HANDSHAKE_RTS_PIN) | (1<<UART_HANDSHAKE_CTS_PIN));
#endif
}
#if !defined(UART_TXCIE) && !defined(UART_UDRIE)
/**
* @brief Transmit a single character via UART (blocking).
*
* @param data Character byte to transmit (0-255).
* @return Always returns 0 (success indicator for stdio compatibility).
*
* @details
* Polling implementation waits for DREIF (Data Register Empty) flag before writing to UDR register. Blocks until transmission completes.
*
* @note Only available when no TX interrupts defined (UART_TXCIE/UART_UDRIE).
*/
char uart_putchar(char data)
{
// Wait until last transmission completed
while(!(UCSRA & (1<<UDRE)));
UDR = data; // Write data to transmission register
// C99 functions needs an int as a return parameter
return 0; // Return that there was no fault
}
#if (UART_STDMODE == 1 || UART_STDMODE == 2)
/**
* @brief UART printf stream handler for stdout redirection.
*
* @param data Character to transmit.
* @param stream FILE stream pointer (unused).
* @return Result of uart_putchar().
*
* @details
* Internal callback used by avr-libc fdevopen() for printf() redirection. Only compiled when UART_STDMODE == 1 or 2 (write support).
*/
int uart_printf(char data, FILE *stream)
{
return uart_putchar(data);
}
#endif
#endif
#if !defined(UART_RXCIE)
/**
* @brief Non-blocking check for received UART data with error handling.
*
* @param[out] data Pointer to store received byte (valid only if UART_Received returned).
* @return UART_Data status: UART_Empty, UART_Received, or UART_Fault.
*
* @details
* Checks RXCIF flag and validates frame using uart_error_flags(). Handles XON/XOFF software handshake if enabled. Echoes received data if UART_RXC_ECHO defined.
*
* @note Does NOT block. Returns immediately with status.
*/
UART_Data uart_scanchar(char *data)
{
// If data has been received
if((UCSRA & (1<<RXC)))
{
// Check if an UART_Error ocurred
if(uart_error_flags() != UART_None)
{
UDR; // Clear UDR0 Data register
*data = 0;
return UART_Fault;
}
#if UART_HANDSHAKE == 1
if (*data == UART_HANDSHAKE_XON)
{
uart_handshake_sending = UART_Ready;
return UART_Empty;
}
else if (*data == UART_HANDSHAKE_XOFF)
{
uart_handshake_sending = UART_Pause;
return UART_Empty;
}
#endif
*data = UDR;
#if defined(UART_RXC_ECHO) && !defined(UART_TXCIE) && !defined(UART_UDRIE)
// Send echo of received data to UART
uart_putchar(*data);
#endif
return UART_Received;
}
return UART_Empty;
}
/**
* @brief Blocking receive single character via UART.
*
* @param[out] status Pointer to receive UART_Data status (UART_Received/UART_Fault).
* @return Received character byte.
*
* @details
* Loops calling uart_scanchar() until data available or error occurs. Status indicates if data valid (UART_Received) or error (UART_Fault).
*/
char uart_getchar(UART_Data *status)
{
UART_Data temp;
char data;
// Wait until data has been received
do
{
temp = uart_scanchar(&data);
} while (temp == UART_Empty);
*status = temp;
return data;
}
#if (UART_STDMODE == 1 || UART_STDMODE == 3)
/**
* @brief UART scanf stream handler for stdin redirection.
*
* @param stream FILE stream pointer (unused).
* @return Received character as int (for stdio compatibility).
*
* @details
* Internal callback used by avr-libc fdevopen() for scanf() redirection. Only compiled when UART_STDMODE == 1 or 3 (read support).
*/
int uart_scanf(FILE *stream)
{
return (int)uart_getchar(NULL);
}
/**
* @brief Clear UART input stream errors and discard pending character.
*
* @details
* Calls clearerr(stdin) and getchar() to reset stream state and discard any buffered input. Used to recover from scanf() failures.
*/
void uart_clear(void)
{
clearerr(stdin); // Clear error on stream
getchar(); // Remove character from stream
}
/**
* @brief Check and clear UART receive error flags.
*
* @return UART_Error code: UART_None, UART_Frame, UART_Overrun, or UART_Parity.
*
* @details
* Reads RXDATAH error bits (FERR, BUFOVF, PERR) and clears by reading RXDATAL. Returns first detected error or UART_None if no errors.
*/
UART_Error uart_error_flags(void)
{
// UART_Frame error
if(UCSRA & (1<<FE))
{
UDR; // Clear UART data register
return UART_Frame; // Return NUL
}
// Data UART_Overrun error
else if(UCSRA & (1<<DOR))
{
UDR; // Clear UART data register
return UART_Overrun; // Return NUL
}
// UART_Parity error
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// !!!ON megaDFP < 2 UPE is just PE!!!
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
else if(UCSRA & (1<<UPE))
{
UDR; // Clear UART data register
return UART_Parity; // Return NUL
}
return UART_None;
}
#endif
#endif
#if !defined(UART_RXCIE) && !defined(UART_TXCIE) && !defined(UART_UDRIE)
#if UART_HANDSHAKE > 0
/**
* @brief Manage UART hardware/software flow control signaling.
*
* @param status UART_Handshake command: UART_Ready, UART_Pause, or UART_Status (query).
* @return Current handshake state (for UART_Status query).
*
* @details
* Controls flow control signals:
* - UART_Ready: Send XON or assert RTS
* - UART_Pause: Send XOFF or deassert RTS
* - UART_Status: Return remote CTS state or XON/XOFF status
*/
UART_Handshake uart_handshake(UART_Handshake status)
{
if(status == UART_Ready)
{
#if UART_HANDSHAKE == 1
uart_putchar(UART_HANDSHAKE_XON);
#elif UART_HANDSHAKE == 2
UART_HANDSHAKE_PORT &= ~(1<<UART_HANDSHAKE_RTS_PIN);
#endif
}
else if(status == UART_Pause)
{
#if UART_HANDSHAKE == 1
uart_putchar(UART_HANDSHAKE_XOFF);
#elif UART_HANDSHAKE == 2
UART_HANDSHAKE_PORT |= (1<<UART_HANDSHAKE_RTS_PIN);
#endif
}
else
{
#if UART_HANDSHAKE == 1
return uart_handshake_sending;
#elif UART_HANDSHAKE == 2
if (!(UART_HANDSHAKE_PIN & (1<<UART_HANDSHAKE_CTS_PIN)))
return UART_Ready;
return UART_Pause;
#endif
}
return UART_Status;
}
#endif
#endif