]>
Commit | Line | Data |
---|---|---|
b1a276a1 | 1 | /* |
b8dc4070 | 2 | * (C) Copyright 2014 - 2017 Leo C. <erbl259-lmu@yahoo.de> |
b1a276a1 L |
3 | * |
4 | * SPDX-License-Identifier: GPL-2.0 | |
5 | */ | |
6 | ||
7 | #include "serial.h" | |
8 | #include "config.h" | |
9 | #include <errno.h> | |
10 | #include <stdio.h> | |
11 | #include <unistd.h> | |
12 | #include <libopencm3/stm32/rcc.h> | |
13 | #include <libopencm3/stm32/gpio.h> | |
14 | #include <libopencm3/stm32/usart.h> | |
15 | #include <libopencm3/cm3/nvic.h> | |
16 | ||
17 | #if (USART_CONSOLE == 1) | |
18 | #define CON_PORT A | |
19 | #define CON_GPIO GPIOA | |
20 | #elif (USART_CONSOLE == 2) | |
21 | #define CON_PORT A | |
22 | #define CON_GPIO GPIOA | |
23 | #elif (USART_CONSOLE == 3) | |
24 | #define CON_PORT B | |
25 | #define CON_GPIO GPIOB | |
26 | #else | |
27 | #error USART_CONSOLE undefined or invalid value | |
28 | #endif | |
29 | ||
30 | #define CONCAT2(a,b) a##b | |
31 | #define CONCAT(a,b) CONCAT2(a,b) | |
32 | ||
33 | ||
34 | #define CON_USART CONCAT(USART,USART_CONSOLE) | |
35 | #define CON_IRQ CONCAT(CONCAT(NVIC_USART, USART_CONSOLE), _IRQ) | |
36 | #define CON_isr CONCAT(CONCAT(usart, USART_CONSOLE), _isr) | |
37 | #define CON_GPIO_RX CONCAT(CONCAT(GPIO_USART, USART_CONSOLE), _RX) | |
38 | #define CON_GPIO_TX CONCAT(CONCAT(GPIO_USART, USART_CONSOLE), _TX) | |
39 | #define RCC_USART_CON CONCAT(RCC_USART, USART_CONSOLE) | |
40 | #define RCC_GPIO_CON CONCAT(RCC_GPIO, CON_PORT) | |
41 | ||
42 | ||
43 | int _write(int fd, char *ptr, int len) __attribute__((used)); | |
44 | ||
45 | ||
46 | struct ring { | |
47 | uint8_t *data; | |
48 | int size; | |
49 | volatile int begin; | |
50 | volatile int end; | |
51 | }; | |
52 | ||
53 | ||
54 | #define BUFFER_SIZE 256 | |
55 | ||
56 | struct ring rx_ring; | |
57 | struct ring tx_ring; | |
58 | uint8_t rx_ring_buffer[BUFFER_SIZE]; | |
59 | uint8_t tx_ring_buffer[BUFFER_SIZE]; | |
60 | ||
61 | ||
62 | static void ring_init(struct ring *ring, uint8_t *buf, int size) | |
63 | { | |
64 | ring->data = buf; | |
65 | ring->size = size; | |
66 | ring->begin = 0; | |
67 | ring->end = 0; | |
68 | } | |
69 | ||
70 | static int ring_write_ch(struct ring *ring, uint8_t ch) | |
71 | { | |
72 | int ep = (ring->end + 1) % ring->size; | |
73 | ||
74 | if ((ep) != ring->begin) { | |
75 | ring->data[ring->end] = ch; | |
76 | ring->end = ep; | |
77 | return 1; | |
78 | } | |
79 | ||
80 | return -1; | |
81 | } | |
82 | ||
83 | #if 0 /* unused */ | |
84 | static int ring_write(struct ring *ring, uint8_t *data, int size) | |
85 | { | |
86 | int i; | |
87 | ||
88 | for (i = 0; i < size; i++) { | |
89 | if (ring_write_ch(ring, data[i]) < 0) | |
90 | return -i; | |
91 | } | |
92 | ||
93 | return i; | |
94 | } | |
95 | #endif | |
96 | ||
97 | static int ring_read_ch(struct ring *ring) | |
98 | { | |
99 | int ret = -1; | |
100 | ||
101 | if (ring->begin != ring->end) { | |
102 | ret = ring->data[ring->begin]; | |
103 | ring->begin = (ring->begin +1) % ring->size; | |
104 | } | |
105 | ||
106 | return ret; | |
107 | } | |
108 | ||
109 | void usart_setup(int baud) | |
110 | { | |
111 | /* Initialize output ring buffer. */ | |
112 | ring_init(&rx_ring, rx_ring_buffer, BUFFER_SIZE); | |
113 | ring_init(&tx_ring, tx_ring_buffer, BUFFER_SIZE); | |
114 | ||
115 | rcc_periph_clock_enable(RCC_USART_CON); | |
116 | ||
117 | /* Enable the USART_CONSOLE interrupt. */ | |
118 | nvic_enable_irq(CON_IRQ); | |
119 | nvic_set_priority(CON_IRQ, 6*16); | |
120 | ||
121 | /* Setup GPIO pins for CONSOLE TX/RX */ | |
122 | rcc_periph_clock_enable(RCC_GPIO_CON); | |
123 | gpio_set_mode(CON_GPIO, GPIO_MODE_OUTPUT_2_MHZ, | |
124 | GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, CON_GPIO_TX); | |
125 | gpio_set_mode(CON_GPIO, GPIO_MODE_INPUT, | |
126 | GPIO_CNF_INPUT_FLOAT, CON_GPIO_RX); | |
127 | ||
128 | /* Setup UART parameters. */ | |
129 | usart_set_baudrate(CON_USART, baud); | |
130 | usart_set_databits(CON_USART, 8); | |
131 | usart_set_stopbits(CON_USART, USART_STOPBITS_1); | |
132 | usart_set_parity(CON_USART, USART_PARITY_NONE); | |
133 | usart_set_flow_control(CON_USART, USART_FLOWCONTROL_NONE); | |
134 | usart_set_mode(CON_USART, USART_MODE_TX_RX); | |
135 | ||
136 | /* Enable CONSOLE Receive interrupt. */ | |
137 | USART_CR1(CON_USART) |= USART_CR1_RXNEIE; | |
138 | ||
139 | /* Finally enable the USART. */ | |
140 | usart_enable(CON_USART); | |
141 | } | |
142 | ||
143 | ||
144 | void CON_isr(void) | |
145 | { | |
146 | /* Check if we were called because of RXNE. */ | |
147 | if (((USART_CR1(CON_USART) & USART_CR1_RXNEIE) != 0) && | |
148 | ((USART_SR(CON_USART) & USART_SR_RXNE) != 0)) { | |
149 | ||
150 | /* Retrieve the data from the peripheral. */ | |
151 | ring_write_ch(&rx_ring, USART_DR(CON_USART) & USART_DR_MASK); | |
152 | ||
153 | } | |
154 | ||
155 | /* Check if we were called because of TXE. */ | |
156 | if (((USART_CR1(CON_USART) & USART_CR1_TXEIE) != 0) && | |
157 | ((USART_SR(CON_USART) & USART_SR_TXE) != 0)) { | |
158 | ||
159 | int data; | |
160 | ||
161 | data = ring_read_ch(&tx_ring); | |
162 | ||
163 | if (data == -1) { | |
164 | /* Disable the TXE interrupt, it's no longer needed. */ | |
165 | USART_CR1(CON_USART) &= ~USART_CR1_TXEIE; | |
166 | } else { | |
167 | /* Put data into the transmit register. */ | |
168 | usart_send(CON_USART, data); | |
169 | } | |
170 | } | |
171 | } | |
172 | ||
173 | /*--------------------------------------------------------------------------*/ | |
174 | ||
175 | void serial_setup(int baud) | |
176 | { | |
177 | usart_setup(baud); | |
178 | } | |
179 | ||
180 | void serial_setbaudrate(int baud) | |
181 | { | |
182 | usart_set_baudrate(CON_USART, baud); | |
183 | } | |
184 | ||
185 | /*--------------------------------------------------------------------------*/ | |
186 | ||
187 | /** | |
188 | * Use USART_CONSOLE as a console. | |
189 | * This is a syscall for newlib | |
190 | * @param fd | |
191 | * @param ptr | |
192 | * @param len | |
193 | * @return | |
194 | */ | |
195 | int _write(int fd, char *ptr, int len) | |
196 | { | |
197 | int i; | |
198 | ||
199 | if (fd == STDOUT_FILENO || fd == STDERR_FILENO) { | |
200 | for (i = 0; i < len; i++) { | |
201 | if (ptr[i] == '\n') { | |
202 | serial_putc('\r'); | |
203 | } | |
204 | serial_putc(ptr[i]); | |
205 | } | |
206 | return i; | |
207 | } | |
208 | errno = EIO; | |
209 | return -1; | |
210 | } | |
211 | ||
212 | int serial_getc(void) | |
213 | { | |
214 | return ring_read_ch(&rx_ring); | |
215 | } | |
216 | ||
217 | void serial_putc(int data) | |
218 | { | |
219 | while (ring_write_ch(&tx_ring, data) < 0) | |
220 | ; | |
221 | ||
222 | /* Enable the TXE interrupt. */ | |
223 | USART_CR1(CON_USART) |= USART_CR1_TXEIE; | |
224 | } |