update credits
[librepilot.git] / flight / pios / stm32f10x / pios_usart.c
blob0f664429e3e8d6eade68cf59fc174f045ca20bd1
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>
37 #include <pios_usart.h>
39 /* Provide a COM driver */
40 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);
41 static void PIOS_USART_ChangeBaud(uint32_t usart_id, uint32_t baud);
42 static void PIOS_USART_RegisterRxCallback(uint32_t usart_id, pios_com_callback rx_in_cb, uint32_t context);
43 static void PIOS_USART_RegisterTxCallback(uint32_t usart_id, pios_com_callback tx_out_cb, uint32_t context);
44 static void PIOS_USART_TxStart(uint32_t usart_id, uint16_t tx_bytes_avail);
45 static void PIOS_USART_RxStart(uint32_t usart_id, uint16_t rx_bytes_avail);
46 static int32_t PIOS_USART_Ioctl(uint32_t usart_id, uint32_t ctl, void *param);
48 const struct pios_com_driver pios_usart_com_driver = {
49 .set_baud = PIOS_USART_ChangeBaud,
50 .set_config = PIOS_USART_ChangeConfig,
51 .tx_start = PIOS_USART_TxStart,
52 .rx_start = PIOS_USART_RxStart,
53 .bind_tx_cb = PIOS_USART_RegisterTxCallback,
54 .bind_rx_cb = PIOS_USART_RegisterRxCallback,
55 .ioctl = PIOS_USART_Ioctl,
58 enum pios_usart_dev_magic {
59 PIOS_USART_DEV_MAGIC = 0x11223344,
62 struct pios_usart_dev {
63 enum pios_usart_dev_magic magic;
64 const struct pios_usart_cfg *cfg;
65 USART_InitTypeDef init;
66 pios_com_callback rx_in_cb;
67 uint32_t rx_in_context;
68 pios_com_callback tx_out_cb;
69 uint32_t tx_out_context;
70 bool config_locked;
71 uint32_t rx_dropped;
72 uint8_t irq_channel;
75 static bool PIOS_USART_validate(struct pios_usart_dev *usart_dev)
77 return usart_dev->magic == PIOS_USART_DEV_MAGIC;
80 const struct pios_usart_cfg *PIOS_USART_GetConfig(uint32_t usart_id)
82 struct pios_usart_dev *usart_dev = (struct pios_usart_dev *)usart_id;
84 bool valid = PIOS_USART_validate(usart_dev);
86 PIOS_Assert(valid);
88 return usart_dev->cfg;
91 static int32_t PIOS_USART_SetIrqPrio(struct pios_usart_dev *usart_dev, uint8_t irq_prio)
93 NVIC_InitTypeDef init = {
94 .NVIC_IRQChannel = usart_dev->irq_channel,
95 .NVIC_IRQChannelPreemptionPriority = irq_prio,
96 .NVIC_IRQChannelCmd = ENABLE,
99 NVIC_Init(&init);
101 return 0;
104 #if defined(PIOS_INCLUDE_FREERTOS)
105 static struct pios_usart_dev *PIOS_USART_alloc(void)
107 struct pios_usart_dev *usart_dev;
109 usart_dev = (struct pios_usart_dev *)pios_malloc(sizeof(struct pios_usart_dev));
110 if (!usart_dev) {
111 return NULL;
114 memset(usart_dev, 0, sizeof(struct pios_usart_dev));
115 usart_dev->magic = PIOS_USART_DEV_MAGIC;
116 return usart_dev;
118 #else
119 static struct pios_usart_dev pios_usart_devs[PIOS_USART_MAX_DEVS];
120 static uint8_t pios_usart_num_devs;
121 static struct pios_usart_dev *PIOS_USART_alloc(void)
123 struct pios_usart_dev *usart_dev;
125 if (pios_usart_num_devs >= PIOS_USART_MAX_DEVS) {
126 return NULL;
129 usart_dev = &pios_usart_devs[pios_usart_num_devs++];
131 memset(usart_dev, 0, sizeof(struct pios_usart_dev));
132 usart_dev->magic = PIOS_USART_DEV_MAGIC;
134 return usart_dev;
136 #endif /* if defined(PIOS_INCLUDE_FREERTOS) */
138 /* Bind Interrupt Handlers
140 * Map all valid USART IRQs to the common interrupt handler
141 * and provide storage for a 32-bit device id IRQ to map
142 * each physical IRQ to a specific registered device instance.
144 static void PIOS_USART_generic_irq_handler(uint32_t usart_id);
146 static uint32_t PIOS_USART_1_id;
147 void USART1_IRQHandler(void) __attribute__((alias("PIOS_USART_1_irq_handler")));
148 static void PIOS_USART_1_irq_handler(void)
150 PIOS_USART_generic_irq_handler(PIOS_USART_1_id);
153 static uint32_t PIOS_USART_2_id;
154 void USART2_IRQHandler(void) __attribute__((alias("PIOS_USART_2_irq_handler")));
155 static void PIOS_USART_2_irq_handler(void)
157 PIOS_USART_generic_irq_handler(PIOS_USART_2_id);
160 static uint32_t PIOS_USART_3_id;
161 void USART3_IRQHandler(void) __attribute__((alias("PIOS_USART_3_irq_handler")));
162 static void PIOS_USART_3_irq_handler(void)
164 PIOS_USART_generic_irq_handler(PIOS_USART_3_id);
168 * Initialise a single USART device
170 int32_t PIOS_USART_Init(uint32_t *usart_id, const struct pios_usart_cfg *cfg)
172 PIOS_DEBUG_Assert(usart_id);
173 PIOS_DEBUG_Assert(cfg);
175 uint32_t *local_id;
176 uint8_t irq_channel;
178 switch ((uint32_t)cfg->regs) {
179 case (uint32_t)USART1:
180 local_id = &PIOS_USART_1_id;
181 irq_channel = USART1_IRQn;
182 RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
183 break;
184 case (uint32_t)USART2:
185 local_id = &PIOS_USART_2_id;
186 irq_channel = USART2_IRQn;
187 RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
188 break;
189 case (uint32_t)USART3:
190 local_id = &PIOS_USART_3_id;
191 irq_channel = USART3_IRQn;
192 RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);
193 break;
194 default:
195 goto out_fail;
198 if (*local_id) {
199 /* this port is already open */
200 *usart_id = *local_id;
201 return 0;
204 struct pios_usart_dev *usart_dev;
206 usart_dev = (struct pios_usart_dev *)PIOS_USART_alloc();
207 if (!usart_dev) {
208 goto out_fail;
211 /* Bind the configuration to the device instance */
212 usart_dev->cfg = cfg;
213 usart_dev->irq_channel = irq_channel;
215 /* Initialize the comm parameter structure */
216 USART_StructInit(&usart_dev->init); // 9600 8n1
218 /* We will set modes later, depending on installed callbacks */
219 usart_dev->init.USART_Mode = 0;
221 /* DTR handling? */
223 #ifdef PIOS_USART_INVERTER_PORT
224 /* Initialize inverter gpio and set it to off */
225 if (usart_dev->cfg->regs == PIOS_USART_INVERTER_PORT) {
226 GPIO_InitTypeDef inverterGPIOInit = {
227 .GPIO_Pin = PIOS_USART_INVERTER_PIN,
228 .GPIO_Mode = GPIO_Mode_Out_PP,
229 .GPIO_Speed = GPIO_Speed_2MHz,
231 GPIO_Init(PIOS_USART_INVERTER_GPIO, &inverterGPIOInit);
233 GPIO_WriteBit(PIOS_USART_INVERTER_GPIO,
234 PIOS_USART_INVERTER_PIN,
235 PIOS_USART_INVERTER_DISABLE);
237 #endif
239 *usart_id = (uint32_t)usart_dev;
240 *local_id = (uint32_t)usart_dev;
242 PIOS_USART_SetIrqPrio(usart_dev, PIOS_IRQ_PRIO_MID);
244 return 0;
246 out_fail:
247 return -1;
250 static void PIOS_USART_Setup(struct pios_usart_dev *usart_dev)
252 /* Configure RX GPIO */
253 if ((usart_dev->init.USART_Mode & USART_Mode_Rx) && (usart_dev->cfg->rx.gpio)) {
254 if (usart_dev->cfg->remap) {
255 GPIO_PinRemapConfig(usart_dev->cfg->remap, ENABLE);
258 GPIO_Init(usart_dev->cfg->rx.gpio, (GPIO_InitTypeDef *)&usart_dev->cfg->rx.init);
260 USART_ITConfig(usart_dev->cfg->regs, USART_IT_RXNE, ENABLE);
263 /* Configure TX GPIO */
264 if ((usart_dev->init.USART_Mode & USART_Mode_Tx) && usart_dev->cfg->tx.gpio) {
265 if (usart_dev->cfg->remap) {
266 GPIO_PinRemapConfig(usart_dev->cfg->remap, ENABLE);
269 GPIO_Init(usart_dev->cfg->tx.gpio, (GPIO_InitTypeDef *)&usart_dev->cfg->tx.init);
272 /* Write new configuration */
273 { // fix parity stuff
274 USART_InitTypeDef init = usart_dev->init;
276 if ((init.USART_Parity != USART_Parity_No) && (init.USART_WordLength == USART_WordLength_8b)) {
277 init.USART_WordLength = USART_WordLength_9b;
280 USART_Init(usart_dev->cfg->regs, &init);
285 * Re enable USART.
287 USART_Cmd(usart_dev->cfg->regs, ENABLE);
291 static void PIOS_USART_RxStart(uint32_t usart_id, __attribute__((unused)) uint16_t rx_bytes_avail)
293 struct pios_usart_dev *usart_dev = (struct pios_usart_dev *)usart_id;
295 bool valid = PIOS_USART_validate(usart_dev);
297 PIOS_Assert(valid);
299 USART_ITConfig(usart_dev->cfg->regs, USART_IT_RXNE, ENABLE);
301 static void PIOS_USART_TxStart(uint32_t usart_id, __attribute__((unused)) uint16_t tx_bytes_avail)
303 struct pios_usart_dev *usart_dev = (struct pios_usart_dev *)usart_id;
305 bool valid = PIOS_USART_validate(usart_dev);
307 PIOS_Assert(valid);
309 USART_ITConfig(usart_dev->cfg->regs, USART_IT_TXE, ENABLE);
313 * Changes the baud rate of the USART peripheral without re-initialising.
314 * \param[in] usart_id USART name (GPS, TELEM, AUX)
315 * \param[in] baud Requested baud rate
317 static void PIOS_USART_ChangeBaud(uint32_t usart_id, uint32_t baud)
319 struct pios_usart_dev *usart_dev = (struct pios_usart_dev *)usart_id;
321 bool valid = PIOS_USART_validate(usart_dev);
323 PIOS_Assert(valid);
325 if (usart_dev->config_locked) {
326 return;
329 /* Use our working copy of the usart init structure */
330 usart_dev->init.USART_BaudRate = baud;
332 PIOS_USART_Setup(usart_dev);
336 * Changes configuration of the USART peripheral without re-initialising.
337 * \param[in] usart_id USART name (GPS, TELEM, AUX)
338 * \param[in] word_len Requested word length
339 * \param[in] stop_bits Requested stop bits
340 * \param[in] parity Requested parity
341 * \param[in] baud_rate Requested baud rate
342 * \param[in] mode Requested mode
345 static void PIOS_USART_ChangeConfig(uint32_t usart_id,
346 enum PIOS_COM_Word_Length word_len,
347 enum PIOS_COM_Parity parity,
348 enum PIOS_COM_StopBits stop_bits,
349 uint32_t baud_rate)
351 struct pios_usart_dev *usart_dev = (struct pios_usart_dev *)usart_id;
353 bool valid = PIOS_USART_validate(usart_dev);
355 PIOS_Assert(valid);
357 if (usart_dev->config_locked) {
358 return;
361 switch (word_len) {
362 case PIOS_COM_Word_length_8b:
363 usart_dev->init.USART_WordLength = USART_WordLength_8b;
364 break;
365 case PIOS_COM_Word_length_9b:
366 usart_dev->init.USART_WordLength = USART_WordLength_9b;
367 break;
368 default:
369 break;
372 switch (stop_bits) {
373 case PIOS_COM_StopBits_0_5:
374 usart_dev->init.USART_StopBits = USART_StopBits_0_5;
375 break;
376 case PIOS_COM_StopBits_1:
377 usart_dev->init.USART_StopBits = USART_StopBits_1;
378 break;
379 case PIOS_COM_StopBits_1_5:
380 usart_dev->init.USART_StopBits = USART_StopBits_1_5;
381 break;
382 case PIOS_COM_StopBits_2:
383 usart_dev->init.USART_StopBits = USART_StopBits_2;
384 break;
385 default:
386 break;
389 switch (parity) {
390 case PIOS_COM_Parity_No:
391 usart_dev->init.USART_Parity = USART_Parity_No;
392 break;
393 case PIOS_COM_Parity_Even:
394 usart_dev->init.USART_Parity = USART_Parity_Even;
395 break;
396 case PIOS_COM_Parity_Odd:
397 usart_dev->init.USART_Parity = USART_Parity_Odd;
398 break;
399 default:
400 break;
403 if (baud_rate) {
404 usart_dev->init.USART_BaudRate = baud_rate;
407 PIOS_USART_Setup(usart_dev);
410 static void PIOS_USART_RegisterRxCallback(uint32_t usart_id, pios_com_callback rx_in_cb, uint32_t context)
412 struct pios_usart_dev *usart_dev = (struct pios_usart_dev *)usart_id;
414 bool valid = PIOS_USART_validate(usart_dev);
416 PIOS_Assert(valid);
419 * Order is important in these assignments since ISR uses _cb
420 * field to determine if it's ok to dereference _cb and _context
422 usart_dev->rx_in_context = context;
423 usart_dev->rx_in_cb = rx_in_cb;
425 usart_dev->init.USART_Mode |= USART_Mode_Rx;
427 PIOS_USART_Setup(usart_dev);
430 static void PIOS_USART_RegisterTxCallback(uint32_t usart_id, pios_com_callback tx_out_cb, uint32_t context)
432 struct pios_usart_dev *usart_dev = (struct pios_usart_dev *)usart_id;
434 bool valid = PIOS_USART_validate(usart_dev);
436 PIOS_Assert(valid);
439 * Order is important in these assignments since ISR uses _cb
440 * field to determine if it's ok to dereference _cb and _context
442 usart_dev->tx_out_context = context;
443 usart_dev->tx_out_cb = tx_out_cb;
445 usart_dev->init.USART_Mode |= USART_Mode_Tx;
447 PIOS_USART_Setup(usart_dev);
450 static void PIOS_USART_generic_irq_handler(uint32_t usart_id)
452 struct pios_usart_dev *usart_dev = (struct pios_usart_dev *)usart_id;
454 bool valid = PIOS_USART_validate(usart_dev);
456 PIOS_Assert(valid);
458 /* Force read of dr after sr to make sure to clear error flags */
459 volatile uint16_t sr = usart_dev->cfg->regs->SR;
460 volatile uint8_t dr = usart_dev->cfg->regs->DR;
462 /* Check if RXNE flag is set */
463 bool rx_need_yield = false;
464 if (sr & USART_SR_RXNE) {
465 uint8_t byte = dr;
466 if (usart_dev->rx_in_cb) {
467 uint16_t rc;
468 rc = (usart_dev->rx_in_cb)(usart_dev->rx_in_context, &byte, 1, NULL, &rx_need_yield);
469 if (rc < 1) {
470 /* Lost bytes on rx */
471 usart_dev->rx_dropped += 1;
476 /* Check if TXE flag is set */
477 bool tx_need_yield = false;
478 if (sr & USART_SR_TXE) {
479 if (usart_dev->tx_out_cb) {
480 uint8_t b;
481 uint16_t bytes_to_send;
483 bytes_to_send = (usart_dev->tx_out_cb)(usart_dev->tx_out_context, &b, 1, NULL, &tx_need_yield);
485 if (bytes_to_send > 0) {
486 /* Send the byte we've been given */
487 usart_dev->cfg->regs->DR = b;
488 } else {
489 /* No bytes to send, disable TXE interrupt */
490 USART_ITConfig(usart_dev->cfg->regs, USART_IT_TXE, DISABLE);
492 } else {
493 /* No bytes to send, disable TXE interrupt */
494 USART_ITConfig(usart_dev->cfg->regs, USART_IT_TXE, DISABLE);
498 #if defined(PIOS_INCLUDE_FREERTOS)
499 if (rx_need_yield || tx_need_yield) {
500 vPortYield();
502 #endif /* PIOS_INCLUDE_FREERTOS */
505 static int32_t PIOS_USART_Ioctl(uint32_t usart_id, uint32_t ctl, void *param)
507 struct pios_usart_dev *usart_dev = (struct pios_usart_dev *)usart_id;
509 bool valid = PIOS_USART_validate(usart_dev);
511 PIOS_Assert(valid);
513 /* First try board specific IOCTL to allow overriding default functions */
514 if (usart_dev->cfg->ioctl) {
515 int32_t ret = usart_dev->cfg->ioctl(usart_id, ctl, param);
516 if (ret != COM_IOCTL_ENOSYS) {
517 return ret;
521 switch (ctl) {
522 case PIOS_IOCTL_USART_SET_IRQ_PRIO:
523 return PIOS_USART_SetIrqPrio(usart_dev, *(uint8_t *)param);
525 #ifdef PIOS_USART_INVERTER_PORT
526 case PIOS_IOCTL_USART_SET_INVERTED:
527 if (usart_dev->cfg->regs != PIOS_USART_INVERTER_PORT) {
528 return COM_IOCTL_ENOSYS; /* don't know how */
530 GPIO_WriteBit(PIOS_USART_INVERTER_GPIO,
531 PIOS_USART_INVERTER_PIN,
532 (*(enum PIOS_USART_Inverted *)param & PIOS_USART_Inverted_Rx) ? PIOS_USART_INVERTER_ENABLE : PIOS_USART_INVERTER_DISABLE);
534 break;
535 #endif /* PIOS_USART_INVERTER_PORT */
536 case PIOS_IOCTL_USART_GET_DSMBIND:
537 #ifdef PIOS_USART_INVERTER_PORT
538 if (usart_dev->cfg->regs == PIOS_USART_INVERTER_PORT) {
539 return -2; /* do not allow dsm bind on port with inverter */
541 #endif /* otherwise, return RXGPIO */
542 case PIOS_IOCTL_USART_GET_RXGPIO:
543 *(struct stm32_gpio *)param = usart_dev->cfg->rx;
544 break;
545 case PIOS_IOCTL_USART_GET_TXGPIO:
546 *(struct stm32_gpio *)param = usart_dev->cfg->tx;
547 break;
548 case PIOS_IOCTL_USART_SET_HALFDUPLEX:
549 USART_HalfDuplexCmd(usart_dev->cfg->regs, *(bool *)param ? ENABLE : DISABLE);
550 break;
551 case PIOS_IOCTL_USART_LOCK_CONFIG:
552 usart_dev->config_locked = *(bool *)param;
553 break;
554 default:
555 return COM_IOCTL_ENOSYS; /* unknown ioctl */
558 return 0;
561 #endif /* PIOS_INCLUDE_USART */
564 * @}
565 * @}