This repository has been archived by the owner on Aug 12, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathuart.c
193 lines (175 loc) · 3.99 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
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
// serial port driver
#include "types.h"
// UART control registers
// defined only when needed
// see http://byterunner.com/16550.html
#define RHR 0 // Receive Holding Register (READ)
#define THR 0 // Transmit Holding Register (WRITE)
#define DLL 0 // Divisor Latch Least Significant Bit (WRITE)
#define DLM 1 // Divisor Latch Most Significant Bit (WRITE)
#define IER 1 // Interrupt Enable Register (WRITE)
#define LCR 3 // Line Control Register (WRITE)
#define LSR 5 // Line Status Register (READ)
// Special bits of registers
#define LCR_BAUD_LATCH (1 << 7) // 0 disabled (default), 1 enabled
#define LCR_WORD_LEN_8 3 // bit 0 and 1 both set to 1 for word length 8
#define LSR_TR_IDLE (1 << 5) // 0 THR is empty, previous data transmitted
#define LSR_RX_READY 1 // first bit is 1 if RHR receives data
/* conventional abbreviations:
RX: receive
TX: transmit
*/
volatile uint8 *get_reg(int reg)
{
// return the memory location of given register name
return (volatile uint8 *)(reg + UART0);
}
void uart_write_reg(int reg, uint8 val)
{
volatile uint8 *p = get_reg(reg);
*p = val;
return;
}
uint8 uart_read_reg(int reg)
{
return *(get_reg(reg));
}
/*
Initialize the UART control register for basic I/O.
*/
void uart_init(void)
{
// enable interrupts
int ier = uart_read_reg(IER);
ier = ier | (1 << 0);
uart_write_reg(IER, ier);
// set baud rate
// first, change LCR's bit 7 to 1
// make sure not to modify any other bits.
uint8 current_LCR = uart_read_reg(LCR);
uart_write_reg(LCR, current_LCR | LCR_BAUD_LATCH);
// second, set divisor latch to 3
// which makes baud rate 38.4K
// separate to LSB and MSB
uart_write_reg(DLL, 0x03);
uart_write_reg(DLM, 0x00);
// set asynchronous data communication format
// by set LCR's bits
// 8 bits word length without parity
uint8 new_LCR = 0;
new_LCR |= LCR_WORD_LEN_8;
uart_write_reg(LCR, new_LCR);
return;
}
/*
Print a character on terminal via UART.
*/
void uart_putchar(char c)
{
uint8 current_LSR;
while (1)
{
current_LSR = uart_read_reg(LSR);
if (current_LSR | LSR_TR_IDLE)
{
break;
}
}
uart_write_reg(THR, c);
return;
}
/*
Print a string on terminal via UART.
*/
void uart_puts(char *s)
{
while (*s)
{
uart_putchar(*s);
s++;
}
return;
}
/*
Print binary value of a given char.
Mainly used for testing.
*/
void uart_print_char_val(uint8 c)
{
uint8 i = 7;
while (1)
{
uint8 bit = ((c & (1 << i)) ? '1' : '0');
uart_putchar(bit);
if (i == 0)
{
break;
}
i--;
}
return;
}
/*
Read in a character from UART
and echo it back as feedback to user.
Basically waiting for instructions.
*/
uint8 uart_getchar(void)
{
uint8 current_LSR = uart_read_reg(LSR);
if (current_LSR & LSR_RX_READY)
{
uint8 c = uart_read_reg(RHR);
// make input visible
uart_putchar(c);
// uart_print_char_val(c); // check binary of input
if (c == '\r')
{
// handle carrige return new line
uart_putchar('\n');
}
else if (c == '\b')
{
// handle backspace
uart_puts(" \b"); // whitespace cover previous input
}
return c;
}
return -1;
}
/*
Read in a character but do not echo back.
Mainly for keyboard interruption.
*/
uint8 uart_read_char(void)
{
if (uart_read_reg(LSR) & LSR_RX_READY)
{
uint8 c = uart_read_reg(RHR);
return c;
}
return -1;
}
/*
When keyboard interrupt arise by UART,
read in all input from buffer and
echo back them one by one.
AKA UART interrupt handler.
*/
void uart_interrupt(void)
{
while (1)
{
uint8 c = uart_read_char();
if (c == 255) // unsigned char is 255 for -1
{
uart_putchar('\n');
break;
}
else
{
uart_putchar(c);
}
}
return;
}