Merged in f5soh/librepilot/update_credits (pull request #529)
[librepilot.git] / flight / pios / stm32f0x / pios_usart.c
blob6154341c397c056fd482a3c4270407d4326cb086
1 /**
2 ******************************************************************************
3 * @addtogroup PIOS PIOS Core hardware abstraction layer
4 * @{
5 * @addtogroup PIOS_USART USART Functions
6 * @brief PIOS interface for USART port
7 * @{
9 * @file pios_usart.c
10 * @author The LibrePilot Project, http://www.librepilot.org, Copyright (c) 2016-2017.
11 * The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010.
12 * @brief USART commands. Inits USARTs, controls USARTs & Interupt handlers. (STM32 dependent)
13 * @see The GNU Public License (GPL) Version 3
15 *****************************************************************************/
17 * This program is free software; you can redistribute it and/or modify
18 * it under the terms of the GNU General Public License as published by
19 * the Free Software Foundation; either version 3 of the License, or
20 * (at your option) any later version.
22 * This program is distributed in the hope that it will be useful, but
23 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
24 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
25 * for more details.
27 * You should have received a copy of the GNU General Public License along
28 * with this program; if not, write to the Free Software Foundation, Inc.,
29 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
32 #include "pios.h"
34 #ifdef PIOS_INCLUDE_USART
36 #include <pios_usart_priv.h>
38 #define PIOS_UART_TX_BUFFER 10
39 /* Provide a COM driver */
40 static void PIOS_USART_ChangeBaud(uint32_t usart_id, uint32_t baud);
41 #ifndef BOOTLOADER
42 static void PIOS_USART_ChangeConfig(uint32_t usart_id, enum PIOS_COM_Word_Length word_len, enum PIOS_COM_Parity parity, enum PIOS_COM_StopBits stop_bits, uint32_t baud_rate);
43 #endif
44 static void PIOS_USART_RegisterRxCallback(uint32_t usart_id, pios_com_callback rx_in_cb, uint32_t context);
45 static void PIOS_USART_RegisterTxCallback(uint32_t usart_id, pios_com_callback tx_out_cb, uint32_t context);
46 static void PIOS_USART_TxStart(uint32_t usart_id, uint16_t tx_bytes_avail);
47 static void PIOS_USART_RxStart(uint32_t usart_id, uint16_t rx_bytes_avail);
49 const struct pios_com_driver pios_usart_com_driver = {
50 .set_baud = PIOS_USART_ChangeBaud,
51 #ifndef BOOTLOADER
52 .set_config = PIOS_USART_ChangeConfig,
53 #endif
54 .tx_start = PIOS_USART_TxStart,
55 .rx_start = PIOS_USART_RxStart,
56 .bind_tx_cb = PIOS_USART_RegisterTxCallback,
57 .bind_rx_cb = PIOS_USART_RegisterRxCallback,
60 enum pios_usart_dev_magic {
61 PIOS_USART_DEV_MAGIC = 0x11223344,
64 struct pios_usart_dev {
65 enum pios_usart_dev_magic magic;
66 const struct pios_usart_cfg *cfg;
67 USART_InitTypeDef init;
68 pios_com_callback rx_in_cb;
69 uint32_t rx_in_context;
70 pios_com_callback tx_out_cb;
71 uint32_t tx_out_context;
73 uint32_t rx_dropped;
75 uint8_t tx_buffer[PIOS_UART_TX_BUFFER];
76 uint8_t tx_len;
77 uint8_t tx_pos;
78 uint8_t irq_channel;
81 static struct pios_usart_dev *PIOS_USART_validate(uint32_t usart_id)
83 struct pios_usart_dev *usart_dev = (struct pios_usart_dev *)usart_id;
85 bool valid = (usart_dev->magic == PIOS_USART_DEV_MAGIC);
87 PIOS_Assert(valid);
88 return usart_dev;
91 #if defined(PIOS_INCLUDE_FREERTOS)
92 static struct pios_usart_dev *PIOS_USART_alloc(void)
94 struct pios_usart_dev *usart_dev;
96 usart_dev = (struct pios_usart_dev *)pvPortMalloc(sizeof(struct pios_usart_dev));
97 if (!usart_dev) {
98 return NULL;
101 memset(usart_dev, 0, sizeof(struct pios_usart_dev));
102 usart_dev->magic = PIOS_USART_DEV_MAGIC;
103 return usart_dev;
105 #else
106 static struct pios_usart_dev pios_usart_devs[PIOS_USART_MAX_DEVS];
107 static uint8_t pios_usart_num_devs;
108 static struct pios_usart_dev *PIOS_USART_alloc(void)
110 struct pios_usart_dev *usart_dev;
112 if (pios_usart_num_devs >= PIOS_USART_MAX_DEVS) {
113 return NULL;
116 usart_dev = &pios_usart_devs[pios_usart_num_devs++];
118 memset(usart_dev, 0, sizeof(struct pios_usart_dev));
119 usart_dev->magic = PIOS_USART_DEV_MAGIC;
121 return usart_dev;
123 #endif /* if defined(PIOS_INCLUDE_FREERTOS) */
125 /* Bind Interrupt Handlers
127 * Map all valid USART IRQs to the common interrupt handler
128 * and provide storage for a 32-bit device id IRQ to map
129 * each physical IRQ to a specific registered device instance.
131 static void PIOS_USART_generic_irq_handler(uint32_t usart_id);
133 static uint32_t PIOS_USART_1_id;
134 void USART1_IRQHandler(void) __attribute__((alias("PIOS_USART_1_irq_handler")));
135 static void PIOS_USART_1_irq_handler(void)
137 PIOS_USART_generic_irq_handler(PIOS_USART_1_id);
140 static uint32_t PIOS_USART_2_id;
141 void USART2_IRQHandler(void) __attribute__((alias("PIOS_USART_2_irq_handler")));
142 static void PIOS_USART_2_irq_handler(void)
144 PIOS_USART_generic_irq_handler(PIOS_USART_2_id);
146 #if defined(STM32F072)
147 static uint32_t PIOS_USART_3_id;
148 void USART3_IRQHandler(void) __attribute__((alias("PIOS_USART_3_irq_handler")));
149 static void PIOS_USART_3_irq_handler(void)
151 PIOS_USART_generic_irq_handler(PIOS_USART_3_id);
153 #endif
155 static int32_t PIOS_USART_SetIrqPrio(struct pios_usart_dev *usart_dev, uint8_t irq_prio)
157 NVIC_InitTypeDef init = {
158 .NVIC_IRQChannel = usart_dev->irq_channel,
159 .NVIC_IRQChannelPriority = irq_prio,
160 .NVIC_IRQChannelCmd = ENABLE,
163 NVIC_Init(&init);
165 return 0;
169 * Initialise a single USART device
171 int32_t PIOS_USART_Init(uint32_t *usart_id, const struct pios_usart_cfg *cfg)
173 PIOS_DEBUG_Assert(usart_id);
174 PIOS_DEBUG_Assert(cfg);
176 uint32_t *local_id;
177 uint8_t irq_channel;
179 /* Enable USART clock */
180 switch ((uint32_t)cfg->regs) {
181 case (uint32_t)USART1:
182 local_id = &PIOS_USART_1_id;
183 irq_channel = USART1_IRQn;
184 break;
185 case (uint32_t)USART2:
186 local_id = &PIOS_USART_2_id;
187 irq_channel = USART2_IRQn;
188 break;
189 #if defined(STM32F072)
190 case (uint32_t)USART3:
191 local_id = &PIOS_USART_3_id;
192 irq_channel = USART3_4_IRQn;
193 break;
194 #endif
195 default:
196 goto out_fail;
199 struct pios_usart_dev *usart_dev;
201 usart_dev = (struct pios_usart_dev *)PIOS_USART_alloc();
202 if (!usart_dev) {
203 return -1;
206 /* Bind the configuration to the device instance */
207 usart_dev->cfg = cfg;
208 usart_dev->irq_channel = irq_channel;
210 /* Initialize the comm parameter structure */
211 USART_StructInit(&usart_dev->init); // 9600 8n1
213 /* We will set modes later, depending on installed callbacks */
214 usart_dev->init.USART_Mode = 0;
216 *usart_id = (uint32_t)usart_dev;
217 *local_id = (uint32_t)usart_dev;
219 PIOS_USART_SetIrqPrio(usart_dev, PIOS_IRQ_PRIO_MID);
221 /* Disable overrun detection */
222 USART_OverrunDetectionConfig(usart_dev->cfg->regs, USART_OVRDetection_Disable);
224 return 0;
226 out_fail:
227 return -1;
230 static void PIOS_USART_Setup(struct pios_usart_dev *usart_dev)
232 /* Configure RX GPIO */
233 if ((usart_dev->init.USART_Mode & USART_Mode_Rx) && (usart_dev->cfg->rx.gpio)) {
234 if (usart_dev->cfg->remap) {
235 GPIO_PinAFConfig(usart_dev->cfg->rx.gpio,
236 __builtin_ctz(usart_dev->cfg->rx.init.GPIO_Pin),
237 usart_dev->cfg->remap);
240 GPIO_Init(usart_dev->cfg->rx.gpio, (GPIO_InitTypeDef *)&usart_dev->cfg->rx.init);
242 USART_ITConfig(usart_dev->cfg->regs, USART_IT_RXNE, ENABLE);
245 /* Configure TX GPIO */
246 if ((usart_dev->init.USART_Mode & USART_Mode_Tx) && usart_dev->cfg->tx.gpio) {
247 if (usart_dev->cfg->remap) {
248 GPIO_PinAFConfig(usart_dev->cfg->tx.gpio,
249 __builtin_ctz(usart_dev->cfg->tx.init.GPIO_Pin),
250 usart_dev->cfg->remap);
253 GPIO_Init(usart_dev->cfg->tx.gpio, (GPIO_InitTypeDef *)&usart_dev->cfg->tx.init);
256 /* Write new configuration */
257 { // fix parity stuff
258 USART_InitTypeDef init = usart_dev->init;
260 if ((init.USART_Parity != USART_Parity_No) && (init.USART_WordLength == USART_WordLength_8b)) {
261 init.USART_WordLength = USART_WordLength_9b;
264 USART_Init(usart_dev->cfg->regs, &init);
268 * Re enable USART.
270 USART_Cmd(usart_dev->cfg->regs, ENABLE);
273 static void PIOS_USART_RxStart(uint32_t usart_id, __attribute__((unused)) uint16_t rx_bytes_avail)
275 const struct pios_usart_dev *usart_dev = PIOS_USART_validate(usart_id);
277 USART_ITConfig(usart_dev->cfg->regs, USART_IT_RXNE, ENABLE);
279 static void PIOS_USART_TxStart(uint32_t usart_id, __attribute__((unused)) uint16_t tx_bytes_avail)
281 const struct pios_usart_dev *usart_dev = PIOS_USART_validate(usart_id);
283 USART_ITConfig(usart_dev->cfg->regs, USART_IT_TXE, ENABLE);
287 * Changes the baud rate of the USART peripheral without re-initialising.
288 * \param[in] usart_id USART name (GPS, TELEM, AUX)
289 * \param[in] baud Requested baud rate
291 static void PIOS_USART_ChangeBaud(uint32_t usart_id, uint32_t baud)
293 struct pios_usart_dev *usart_dev = PIOS_USART_validate(usart_id);
295 /* Use our working copy of the usart init structure */
296 usart_dev->init.USART_BaudRate = baud;
298 PIOS_USART_Setup(usart_dev);
300 #ifndef BOOTLOADER
302 * Changes configuration of the USART peripheral without re-initialising.
303 * \param[in] usart_id USART name (GPS, TELEM, AUX)
304 * \param[in] word_len Requested word length
305 * \param[in] stop_bits Requested stop bits
306 * \param[in] parity Requested parity
309 static void PIOS_USART_ChangeConfig(uint32_t usart_id,
310 enum PIOS_COM_Word_Length word_len,
311 enum PIOS_COM_Parity parity,
312 enum PIOS_COM_StopBits stop_bits,
313 uint32_t baud_rate)
315 struct pios_usart_dev *usart_dev = PIOS_USART_validate(usart_id);
317 switch (word_len) {
318 case PIOS_COM_Word_length_8b:
319 usart_dev->init.USART_WordLength = USART_WordLength_8b;
320 break;
321 case PIOS_COM_Word_length_9b:
322 usart_dev->init.USART_WordLength = USART_WordLength_9b;
323 break;
324 default:
325 break;
328 switch (stop_bits) {
329 case PIOS_COM_StopBits_1:
330 usart_dev->init.USART_StopBits = USART_StopBits_1;
331 break;
332 case PIOS_COM_StopBits_1_5:
333 usart_dev->init.USART_StopBits = USART_StopBits_1_5;
334 break;
335 case PIOS_COM_StopBits_2:
336 usart_dev->init.USART_StopBits = USART_StopBits_2;
337 break;
338 default:
339 break;
342 switch (parity) {
343 case PIOS_COM_Parity_No:
344 usart_dev->init.USART_Parity = USART_Parity_No;
345 break;
346 case PIOS_COM_Parity_Even:
347 usart_dev->init.USART_Parity = USART_Parity_Even;
348 break;
349 case PIOS_COM_Parity_Odd:
350 usart_dev->init.USART_Parity = USART_Parity_Odd;
351 break;
352 default:
353 break;
356 if (baud_rate) {
357 usart_dev->init.USART_BaudRate = baud_rate;
360 PIOS_USART_Setup(usart_dev);
362 #endif /* BOOTLOADER */
364 static void PIOS_USART_RegisterRxCallback(uint32_t usart_id, pios_com_callback rx_in_cb, uint32_t context)
366 struct pios_usart_dev *usart_dev = PIOS_USART_validate(usart_id);
369 * Order is important in these assignments since ISR uses _cb
370 * field to determine if it's ok to dereference _cb and _context
372 usart_dev->rx_in_context = context;
373 usart_dev->rx_in_cb = rx_in_cb;
375 usart_dev->init.USART_Mode |= USART_Mode_Rx;
377 PIOS_USART_Setup(usart_dev);
380 static void PIOS_USART_RegisterTxCallback(uint32_t usart_id, pios_com_callback tx_out_cb, uint32_t context)
382 struct pios_usart_dev *usart_dev = PIOS_USART_validate(usart_id);
385 * Order is important in these assignments since ISR uses _cb
386 * field to determine if it's ok to dereference _cb and _context
388 usart_dev->tx_out_context = context;
389 usart_dev->tx_out_cb = tx_out_cb;
391 usart_dev->init.USART_Mode |= USART_Mode_Tx;
393 PIOS_USART_Setup(usart_dev);
396 static void PIOS_USART_generic_irq_handler(uint32_t usart_id)
398 struct pios_usart_dev *usart_dev = PIOS_USART_validate(usart_id);
400 /* Force read of dr after sr to make sure to clear error flags */
403 /* Check if RXNE flag is set */
404 bool rx_need_yield = false;
406 if (usart_dev->cfg->regs->ISR & USART_ISR_RXNE) {
407 uint8_t byte = usart_dev->cfg->regs->RDR & 0xFF;
408 if (usart_dev->rx_in_cb) {
409 uint16_t rc;
410 rc = (usart_dev->rx_in_cb)(usart_dev->rx_in_context, &byte, 1, NULL, &rx_need_yield);
411 if (rc < 1) {
412 /* Lost bytes on rx */
413 usart_dev->rx_dropped += 1;
418 /* Check if TXE flag is set */
419 bool tx_need_yield = false;
420 if (usart_dev->cfg->regs->ISR & USART_ISR_TXE) {
421 if (usart_dev->tx_out_cb) {
422 if (!usart_dev->tx_len) {
423 usart_dev->tx_len = (usart_dev->tx_out_cb)(usart_dev->tx_out_context, usart_dev->tx_buffer, PIOS_UART_TX_BUFFER, NULL, &tx_need_yield);
424 usart_dev->tx_pos = 0;
426 if (usart_dev->tx_len > 0) {
427 /* Send the byte we've been given */
428 usart_dev->cfg->regs->TDR = usart_dev->tx_buffer[usart_dev->tx_pos++];
429 usart_dev->tx_len--;
430 } else {
431 /* No bytes to send, disable TXE interrupt */
432 usart_dev->cfg->regs->CR1 &= ~(USART_CR1_TXEIE);
434 } else {
435 /* No bytes to send, disable TXE interrupt */
436 usart_dev->cfg->regs->CR1 &= ~(USART_CR1_TXEIE);
439 usart_dev->cfg->regs->ICR = USART_ICR_ORECF | USART_ICR_TCCF;
440 #if defined(PIOS_INCLUDE_FREERTOS)
441 if (rx_need_yield || tx_need_yield) {
442 vPortYield();
444 #endif /* PIOS_INCLUDE_FREERTOS */
447 #endif /* PIOS_INCLUDE_USART */
450 * @}
451 * @}