]>
Commit | Line | Data |
---|---|---|
6b81b39f | 1 | /* |
35edb766 | 2 | * (C) Copyright 2014 Leo C. <erbl259-lmu@yahoo.de> |
6b81b39f | 3 | * |
35edb766 | 4 | * SPDX-License-Identifier: GPL-2.0+ |
6b81b39f L |
5 | */ |
6 | ||
7 | #include <errno.h> | |
8 | #include <stdio.h> | |
9 | #include <unistd.h> | |
10 | ||
11 | #include <libopencm3/stm32/gpio.h> | |
12 | #include <libopencm3/stm32/usart.h> | |
13 | #include <libopencm3/cm3/nvic.h> | |
14 | #include "serial.h" | |
15 | ||
16 | ||
17 | #define USART_CONSOLE USART1 | |
18 | ||
19 | int _write(int fd, char *ptr, int len) __attribute__((used)); | |
20 | ||
21 | ||
22 | struct ring { | |
23 | uint8_t *data; | |
24 | int size; | |
25 | volatile int begin; | |
26 | volatile int end; | |
27 | }; | |
28 | ||
29 | ||
30 | #define BUFFER_SIZE 256 | |
31 | ||
32 | struct ring rx_ring; | |
33 | struct ring tx_ring; | |
34 | uint8_t rx_ring_buffer[BUFFER_SIZE]; | |
35 | uint8_t tx_ring_buffer[BUFFER_SIZE]; | |
36 | ||
37 | ||
38 | static void ring_init(struct ring *ring, uint8_t *buf, int size) | |
39 | { | |
40 | ring->data = buf; | |
41 | ring->size = size; | |
42 | ring->begin = 0; | |
43 | ring->end = 0; | |
44 | } | |
45 | ||
46 | static int ring_write_ch(struct ring *ring, uint8_t ch) | |
47 | { | |
48 | int ep = (ring->end + 1) % ring->size; | |
49 | ||
50 | if ((ep) != ring->begin) { | |
51 | ring->data[ring->end] = ch; | |
52 | ring->end = ep; | |
53 | return 1; | |
54 | } | |
55 | ||
56 | return -1; | |
57 | } | |
58 | ||
59 | static int ring_write(struct ring *ring, uint8_t *data, int size) | |
60 | { | |
61 | int i; | |
62 | ||
63 | for (i = 0; i < size; i++) { | |
64 | if (ring_write_ch(ring, data[i]) < 0) | |
65 | return -i; | |
66 | } | |
67 | ||
68 | return i; | |
69 | } | |
70 | ||
71 | static int ring_read_ch(struct ring *ring) | |
72 | { | |
73 | int ret = -1; | |
74 | ||
75 | if (ring->begin != ring->end) { | |
76 | ret = ring->data[ring->begin]; | |
77 | ring->begin = (ring->begin +1) % ring->size; | |
78 | } | |
79 | ||
80 | return ret; | |
81 | } | |
82 | ||
83 | void usart_setup(void) | |
84 | { | |
85 | /* Initialize output ring buffer. */ | |
86 | ring_init(&rx_ring, rx_ring_buffer, BUFFER_SIZE); | |
87 | ring_init(&tx_ring, tx_ring_buffer, BUFFER_SIZE); | |
88 | ||
89 | /* Enable the USART1 interrupt. */ | |
90 | nvic_enable_irq(NVIC_USART1_IRQ); | |
91 | ||
92 | /* Setup GPIO pin GPIO_USART1_TX/LED_GREEN_PIN on GPIO port A for transmit. */ | |
93 | /* TODO: USART1 --> USART_CONSOLE */ | |
94 | ||
95 | gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_50_MHZ, | |
96 | GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, GPIO_USART1_TX); | |
97 | ||
98 | /* Setup GPIO pin GPIO_USART1_RE_RX on GPIO port B for receive. */ | |
99 | gpio_set_mode(GPIOA, GPIO_MODE_INPUT, | |
100 | GPIO_CNF_INPUT_FLOAT, GPIO_USART1_RX); | |
101 | ||
102 | /* Setup UART parameters. */ | |
103 | // usart_set_baudrate(USART_CONSOLE, 38400); | |
104 | usart_set_baudrate(USART_CONSOLE, 115200); | |
105 | usart_set_databits(USART_CONSOLE, 8); | |
106 | usart_set_stopbits(USART_CONSOLE, USART_STOPBITS_1); | |
107 | usart_set_parity(USART_CONSOLE, USART_PARITY_NONE); | |
108 | usart_set_flow_control(USART_CONSOLE, USART_FLOWCONTROL_NONE); | |
109 | usart_set_mode(USART_CONSOLE, USART_MODE_TX_RX); | |
110 | ||
111 | /* Enable USART1 Receive interrupt. */ | |
112 | USART_CR1(USART1) |= USART_CR1_RXNEIE; | |
113 | ||
114 | /* Finally enable the USART. */ | |
115 | usart_enable(USART_CONSOLE); | |
116 | } | |
117 | ||
118 | void usart1_isr(void) | |
119 | { | |
120 | /* Check if we were called because of RXNE. */ | |
121 | if (((USART_CR1(USART1) & USART_CR1_RXNEIE) != 0) && | |
122 | ((USART_SR(USART1) & USART_SR_RXNE) != 0)) { | |
123 | ||
124 | /* Retrieve the data from the peripheral. */ | |
125 | ring_write_ch(&rx_ring, usart_recv(USART1)); | |
126 | ||
127 | } | |
128 | ||
129 | /* Check if we were called because of TXE. */ | |
130 | if (((USART_CR1(USART1) & USART_CR1_TXEIE) != 0) && | |
131 | ((USART_SR(USART1) & USART_SR_TXE) != 0)) { | |
132 | ||
133 | int data; | |
134 | ||
135 | data = ring_read_ch(&tx_ring); | |
136 | ||
137 | if (data == -1) { | |
138 | /* Disable the TXE interrupt, it's no longer needed. */ | |
139 | USART_CR1(USART1) &= ~USART_CR1_TXEIE; | |
140 | } else { | |
141 | /* Put data into the transmit register. */ | |
142 | usart_send(USART1, data); | |
143 | } | |
144 | } | |
145 | } | |
146 | ||
147 | /*--------------------------------------------------------------------------*/ | |
148 | ||
149 | void serial_setup(void) | |
150 | { | |
151 | usart_setup(); | |
152 | } | |
153 | ||
154 | /*--------------------------------------------------------------------------*/ | |
155 | ||
156 | /** | |
157 | * Use USART_CONSOLE as a console. | |
158 | * This is a syscall for newlib | |
159 | * @param fd | |
160 | * @param ptr | |
161 | * @param len | |
162 | * @return | |
163 | */ | |
164 | int _write(int fd, char *ptr, int len) | |
165 | { | |
166 | int i; | |
167 | ||
168 | if (fd == STDOUT_FILENO || fd == STDERR_FILENO) { | |
169 | for (i = 0; i < len; i++) { | |
170 | if (ptr[i] == '\n') { | |
171 | serial_putc('\r'); | |
172 | } | |
173 | serial_putc(ptr[i]); | |
174 | } | |
175 | return i; | |
176 | } | |
177 | errno = EIO; | |
178 | return -1; | |
179 | } | |
180 | ||
181 | int serial_getc(void) | |
182 | { | |
183 | return ring_read_ch(&rx_ring); | |
184 | } | |
185 | ||
186 | void serial_putc(uint8_t data) | |
187 | { | |
188 | while (ring_write_ch(&tx_ring, data) < 0) | |
189 | ; | |
190 | ||
191 | /* Enable the TXE interrupt. */ | |
192 | USART_CR1(USART1) |= USART_CR1_TXEIE; | |
193 | } |