-
Notifications
You must be signed in to change notification settings - Fork 333
/
Copy pathconsole.c
275 lines (252 loc) · 7.3 KB
/
console.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
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
/*
* This file is part of the libopencm3 project.
*
* Copyright (C) 2014 Chuck McManis <cmcmanis@mcmanis.com>
*
* This library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* Interrupt drive Console code (extracted from the usart-irq example)
*
*/
#define _GNU_SOURCE
#include <stdint.h>
#include <stdio.h>
#include <setjmp.h>
#include <libopencm3/stm32/gpio.h>
#include <libopencm3/stm32/rcc.h>
#include <libopencm3/stm32/usart.h>
#include <libopencm3/cm3/nvic.h>
#include <libopencm3/stm32/iwdg.h>
#include <libopencm3/cm3/scb.h>
#include <libopencm3/cm3/cortex.h>
#include "clock.h"
#include "console.h"
/* This is a ring buffer to holding characters as they are typed
* it maintains both the place to put the next character received
* from the UART, and the place where the last character was
* read by the program. See the README file for a discussion of
* the failure semantics.
*/
#define RECV_BUF_SIZE 128 /* Arbitrary buffer size */
char recv_buf[RECV_BUF_SIZE];
static volatile int recv_ndx_nxt; /* Next place to store */
static volatile int recv_ndx_cur; /* Next place to read */
/* For interrupt handling we add a new function which is called
* when receive interrupts happen. The name (usart1_isr) is created
* by the irq.json file in libopencm3 calling this interrupt for
* USART1 'usart1', adding the suffix '_isr', and then weakly binding
* it to the 'do nothing' interrupt function in vec.c.
*
* By defining it in this file the linker will override that weak
* binding and instead bind it here, but you have to get the name
* right or it won't work. And you'll wonder where your interrupts
* are going.
*/
void usart1_isr(void)
{
uint32_t reg;
int i;
do {
reg = USART_SR(CONSOLE_UART);
if (reg & USART_SR_RXNE) {
recv_buf[recv_ndx_nxt] = USART_DR(CONSOLE_UART);
#ifdef RESET_ON_CTRLC
/* Check for "reset" */
if (recv_buf[recv_ndx_nxt] == '\003') {
scb_reset_system();
}
#endif
/* Check for "overrun" */
i = (recv_ndx_nxt + 1) % RECV_BUF_SIZE;
if (i != recv_ndx_cur) {
recv_ndx_nxt = i;
}
}
} while ((reg & USART_SR_RXNE) != 0);
/* can read back-to-back interrupts */
}
/*
* console_putc(char c)
*
* Send the character 'c' to the USART, wait for the USART
* transmit buffer to be empty first.
*/
void console_putc(char c)
{
uint32_t reg;
do {
reg = USART_SR(CONSOLE_UART);
} while ((reg & USART_SR_TXE) == 0);
USART_DR(CONSOLE_UART) = (uint16_t) c & 0xff;
}
/*
* char = console_getc(int wait)
*
* Check the console for a character. If the wait flag is
* non-zero. Continue checking until a character is received
* otherwise return 0 if called and no character was available.
*
* The implementation is a bit different however, now it looks
* in the ring buffer to see if a character has arrived.
*/
char console_getc(int wait)
{
char c = 0;
while ((wait != 0) && (recv_ndx_cur == recv_ndx_nxt));
if (recv_ndx_cur != recv_ndx_nxt) {
c = recv_buf[recv_ndx_cur];
recv_ndx_cur = (recv_ndx_cur + 1) % RECV_BUF_SIZE;
}
return c;
}
/*
* void console_puts(char *s)
*
* Send a string to the console, one character at a time, return
* after the last character, as indicated by a NUL character, is
* reached.
*/
void console_puts(char *s)
{
while (*s != '\000') {
console_putc(*s);
/* Add in a carraige return, after sending line feed */
if (*s == '\n') {
console_putc('\r');
}
s++;
}
}
/*
* int console_gets(char *s, int len)
*
* Wait for a string to be entered on the console, limited
* support for editing characters (back space and delete)
* end when a <CR> character is received.
*/
int console_gets(char *s, int len)
{
char *t = s;
char c;
*t = '\000';
/* read until a <CR> is received */
while ((c = console_getc(1)) != '\r') {
if ((c == '\010') || (c == '\177')) {
if (t > s) {
/* send ^H ^H to erase previous character */
console_puts("\010 \010");
t--;
}
} else {
*t = c;
console_putc(c);
if ((t - s) < len) {
t++;
}
}
/* update end of string with NUL */
*t = '\000';
}
return t - s;
}
/*
* console_setup(int baudrate)
*
* Set the pins and clocks to create a console that we can
* use for serial messages and getting text from the user.
*/
void console_setup(int baud)
{
/* MUST enable the GPIO clock in ADDITION to the USART clock */
rcc_periph_clock_enable(RCC_GPIOA);
/* This example uses PD5 and PD6 for Tx and Rx respectively
* but other pins are available for this role on USART1 (our chosen
* USART) as well, such as PA2 and PA3. You can also split them
* so PA2 for Tx, PD6 for Rx but you would have to enable both
* the GPIOA and GPIOD clocks in that case
*/
gpio_mode_setup(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO9 | GPIO10);
/* Actual Alternate function number (in this case 7) is part
* depenedent, CHECK THE DATA SHEET for the right number to
* use.
*/
gpio_set_af(GPIOA, GPIO_AF7, GPIO9 | GPIO10);
/* This then enables the clock to the USART1 peripheral which is
* attached inside the chip to the APB2 bus. Different peripherals
* attach to different buses, and even some UARTS are attached to
* APB1 and some to APB2, again the data sheet is useful here.
*/
rcc_periph_clock_enable(RCC_USART1);
/* Set up USART/UART parameters using the libopencm3 helper functions */
usart_set_baudrate(CONSOLE_UART, baud);
usart_set_databits(CONSOLE_UART, 8);
usart_set_stopbits(CONSOLE_UART, USART_STOPBITS_1);
usart_set_mode(CONSOLE_UART, USART_MODE_TX_RX);
usart_set_parity(CONSOLE_UART, USART_PARITY_NONE);
usart_set_flow_control(CONSOLE_UART, USART_FLOWCONTROL_NONE);
usart_enable(CONSOLE_UART);
/* Enable interrupts from the USART */
nvic_enable_irq(NVIC_USART1_IRQ);
/* Specifically enable receive interrupts */
usart_enable_rx_interrupt(CONSOLE_UART);
}
static ssize_t console_read(void *cookie, char *buf, size_t size)
{
cookie = cookie; /* -Wunused-parameter */
size_t i;
for (i = 0; i < size; i++) {
char c = console_getc(1);
buf[i] = c;
if (c == '\r') {
buf[i] = '\n';
i++;
break;
}
}
return i;
}
static ssize_t console_write(void *cookie, const char *buf, size_t size)
{
cookie = cookie; /* -Wunused-parameter */
size_t i;
for (i = 0; i < size; i++) {
char c = buf[i];
if (c == '\n') {
console_putc('\r');
}
console_putc(c);
}
return size;
}
void console_stdio_setup()
{
cookie_io_functions_t console_input_fns = {
.read = console_read,
.write = NULL,
.seek = NULL,
.close = NULL
};
cookie_io_functions_t console_output_fns = {
.read = NULL,
.write = console_write,
.seek = NULL,
.close = NULL
};
stdin = fopencookie(NULL, "r", console_input_fns);
stdout = fopencookie(NULL, "w", console_output_fns);
stderr = fopencookie(NULL, "w", console_output_fns);
setlinebuf(stdout);
setbuf(stderr, NULL);
}