update credits
[librepilot.git] / flight / pios / stm32f30x / pios_usart.c
blob554b092e9e6d0918c8dea5af88c4553dd4a29b9c
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 OpenPilot Team, http://www.openpilot.org Copyright (C) 2010.
11 * @brief USART commands. Inits USARTs, controls USARTs & Interupt handlers. (STM32 dependent)
12 * @see The GNU Public License (GPL) Version 3
14 *****************************************************************************/
16 * This program is free software; you can redistribute it and/or modify
17 * it under the terms of the GNU General Public License as published by
18 * the Free Software Foundation; either version 3 of the License, or
19 * (at your option) any later version.
21 * This program is distributed in the hope that it will be useful, but
22 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
23 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
24 * for more details.
26 * You should have received a copy of the GNU General Public License along
27 * with this program; if not, write to the Free Software Foundation, Inc.,
28 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
31 #include "pios.h"
33 #ifdef PIOS_INCLUDE_USART
35 #include <pios_usart_priv.h>
37 /* Provide a COM driver */
38 static void PIOS_USART_ChangeBaud(uint32_t usart_id, uint32_t baud);
39 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);
40 static void PIOS_USART_RegisterRxCallback(uint32_t usart_id, pios_com_callback rx_in_cb, uint32_t context);
41 static void PIOS_USART_RegisterTxCallback(uint32_t usart_id, pios_com_callback tx_out_cb, uint32_t context);
42 static void PIOS_USART_TxStart(uint32_t usart_id, uint16_t tx_bytes_avail);
43 static void PIOS_USART_RxStart(uint32_t usart_id, uint16_t rx_bytes_avail);
44 static int32_t PIOS_USART_Ioctl(uint32_t usart_id, uint32_t ctl, void *param);
46 const struct pios_com_driver pios_usart_com_driver = {
47 .set_baud = PIOS_USART_ChangeBaud,
48 .set_config = PIOS_USART_ChangeConfig,
49 .tx_start = PIOS_USART_TxStart,
50 .rx_start = PIOS_USART_RxStart,
51 .bind_tx_cb = PIOS_USART_RegisterTxCallback,
52 .bind_rx_cb = PIOS_USART_RegisterRxCallback,
53 .ioctl = PIOS_USART_Ioctl,
56 enum pios_usart_dev_magic {
57 PIOS_USART_DEV_MAGIC = 0x11223344,
60 struct pios_usart_dev {
61 enum pios_usart_dev_magic magic;
62 const struct pios_usart_cfg *cfg;
63 USART_InitTypeDef init;
64 pios_com_callback rx_in_cb;
65 uint32_t rx_in_context;
66 pios_com_callback tx_out_cb;
67 uint32_t tx_out_context;
68 bool config_locked;
69 uint32_t rx_dropped;
70 uint8_t irq_channel;
73 static bool PIOS_USART_validate(struct pios_usart_dev *usart_dev)
75 return usart_dev->magic == PIOS_USART_DEV_MAGIC;
78 static int32_t PIOS_USART_SetIrqPrio(struct pios_usart_dev *usart_dev, uint8_t irq_prio)
80 NVIC_InitTypeDef init = {
81 .NVIC_IRQChannel = usart_dev->irq_channel,
82 .NVIC_IRQChannelPreemptionPriority = irq_prio,
83 .NVIC_IRQChannelCmd = ENABLE,
86 NVIC_Init(&init);
88 return 0;
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 *)pios_malloc(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_EXTI25_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_EXTI26_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);
147 static uint32_t PIOS_USART_3_id;
148 void USART3_EXTI28_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);
155 static uint32_t PIOS_UART_4_id;
156 void UART4_EXTI34_IRQHandler(void) __attribute__((alias("PIOS_UART_4_irq_handler")));
157 static void PIOS_UART_4_irq_handler(void)
159 PIOS_USART_generic_irq_handler(PIOS_UART_4_id);
162 static uint32_t PIOS_UART_5_id;
163 void UART5_EXTI35_IRQHandler(void) __attribute__((alias("PIOS_UART_5_irq_handler")));
164 static void PIOS_UART_5_irq_handler(void)
166 PIOS_USART_generic_irq_handler(PIOS_UART_5_id);
170 * Initialise a single USART device
172 int32_t PIOS_USART_Init(uint32_t *usart_id, const struct pios_usart_cfg *cfg)
174 PIOS_DEBUG_Assert(usart_id);
175 PIOS_DEBUG_Assert(cfg);
177 uint32_t *local_id;
178 uint8_t irq_channel;
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 case (uint32_t)USART3:
190 local_id = &PIOS_USART_3_id;
191 irq_channel = USART3_IRQn;
192 break;
193 case (uint32_t)UART4:
194 local_id = &PIOS_UART_4_id;
195 irq_channel = UART4_IRQn;
196 break;
197 case (uint32_t)UART5:
198 local_id = &PIOS_UART_5_id;
199 irq_channel = UART5_IRQn;
200 break;
201 default:
202 goto out_fail;
205 if (*local_id) {
206 /* this port is already open */
207 *usart_id = *local_id;
208 return 0;
211 struct pios_usart_dev *usart_dev;
213 usart_dev = (struct pios_usart_dev *)PIOS_USART_alloc();
214 if (!usart_dev) {
215 goto out_fail;
218 /* Bind the configuration to the device instance */
219 usart_dev->cfg = cfg;
220 usart_dev->irq_channel = irq_channel;
222 /* Set some sane defaults */
223 USART_StructInit(&usart_dev->init);
225 /* We will set modes later, depending on installed callbacks */
226 usart_dev->init.USART_Mode = 0;
228 /* DTR handling? */
230 *usart_id = (uint32_t)usart_dev;
231 *local_id = (uint32_t)usart_dev;
233 PIOS_USART_SetIrqPrio(usart_dev, PIOS_IRQ_PRIO_MID);
235 /* Disable overrun detection */
236 USART_OverrunDetectionConfig(usart_dev->cfg->regs, USART_OVRDetection_Disable);
238 return 0;
240 out_fail:
241 return -1;
244 static void PIOS_USART_Setup(struct pios_usart_dev *usart_dev)
246 /* Configure RX GPIO */
247 if ((usart_dev->init.USART_Mode & USART_Mode_Rx) && (usart_dev->cfg->rx.gpio)) {
248 if (usart_dev->cfg->remap) {
249 GPIO_PinAFConfig(usart_dev->cfg->rx.gpio,
250 usart_dev->cfg->rx.pin_source,
251 usart_dev->cfg->remap);
254 GPIO_Init(usart_dev->cfg->rx.gpio, (GPIO_InitTypeDef *)&usart_dev->cfg->rx.init);
256 USART_ITConfig(usart_dev->cfg->regs, USART_IT_RXNE, ENABLE);
259 /* Configure TX GPIO */
260 if ((usart_dev->init.USART_Mode & USART_Mode_Tx) && usart_dev->cfg->tx.gpio) {
261 if (usart_dev->cfg->remap) {
262 GPIO_PinAFConfig(usart_dev->cfg->tx.gpio,
263 usart_dev->cfg->tx.pin_source,
264 usart_dev->cfg->remap);
267 GPIO_Init(usart_dev->cfg->tx.gpio, (GPIO_InitTypeDef *)&usart_dev->cfg->tx.init);
270 /* Write new configuration */
271 #if 0
272 const char *dbg_parity = "?";
273 switch (usart_dev->init.USART_Parity) {
274 case USART_Parity_No: dbg_parity = "N"; break;
275 case USART_Parity_Even: dbg_parity = "E"; break;
276 case USART_Parity_Odd: dbg_parity = "O"; break;
278 const char *dbg_mode = "???";
279 switch (usart_dev->init.USART_Mode) {
280 case USART_Mode_Rx: dbg_mode = "rx"; break;
281 case USART_Mode_Tx: dbg_mode = "tx"; break;
282 case USART_Mode_Rx | USART_Mode_Tx: dbg_mode = "rx_tx"; break;
284 const char *dbg_flow_control = "???";
285 switch (usart_dev->init.USART_HardwareFlowControl) {
286 case USART_HardwareFlowControl_None: dbg_flow_control = "none"; break;
287 case USART_HardwareFlowControl_RTS: dbg_flow_control = "rts"; break;
288 case USART_HardwareFlowControl_CTS: dbg_flow_control = "cts"; break;
289 case USART_HardwareFlowControl_RTS_CTS: dbg_flow_control = "rts_cts"; break;
291 const char *dbg_stop_bits = "???";
292 switch (usart_dev->init.USART_StopBits) {
293 case USART_StopBits_1: dbg_stop_bits = "1"; break;
294 case USART_StopBits_2: dbg_stop_bits = "2"; break;
295 case USART_StopBits_1_5: dbg_stop_bits = "1.5"; break;
298 DEBUG_PRINTF(0, "PIOS_USART_Setup: 0x%08x %u %u%s%s mode=%s flow_control=%s\r\n",
299 (uint32_t)usart_dev,
300 usart_dev->init.USART_BaudRate,
301 usart_dev->init.USART_WordLength == USART_WordLength_8b ? 8 : 9,
302 dbg_parity,
303 dbg_stop_bits,
304 dbg_mode,
305 dbg_flow_control);
306 #endif /* if 0 */
307 { // fix parity stuff
308 USART_InitTypeDef init = usart_dev->init;
310 if ((init.USART_Parity != USART_Parity_No) && (init.USART_WordLength == USART_WordLength_8b)) {
311 init.USART_WordLength = USART_WordLength_9b;
314 USART_Init(usart_dev->cfg->regs, &init);
318 * Re enable USART.
320 USART_Cmd(usart_dev->cfg->regs, ENABLE);
323 static void PIOS_USART_RxStart(uint32_t usart_id, __attribute__((unused)) uint16_t rx_bytes_avail)
325 struct pios_usart_dev *usart_dev = (struct pios_usart_dev *)usart_id;
327 bool valid = PIOS_USART_validate(usart_dev);
329 PIOS_Assert(valid);
331 USART_ITConfig(usart_dev->cfg->regs, USART_IT_RXNE, ENABLE);
333 static void PIOS_USART_TxStart(uint32_t usart_id, __attribute__((unused)) uint16_t tx_bytes_avail)
335 struct pios_usart_dev *usart_dev = (struct pios_usart_dev *)usart_id;
337 bool valid = PIOS_USART_validate(usart_dev);
339 PIOS_Assert(valid);
341 USART_ITConfig(usart_dev->cfg->regs, USART_IT_TXE, ENABLE);
345 * Changes the baud rate of the USART peripheral without re-initialising.
346 * \param[in] usart_id USART name (GPS, TELEM, AUX)
347 * \param[in] baud Requested baud rate
349 static void PIOS_USART_ChangeBaud(uint32_t usart_id, uint32_t baud)
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 usart_dev->init.USART_BaudRate = baud;
363 PIOS_USART_Setup(usart_dev);
367 * Changes configuration of the USART peripheral without re-initialising.
368 * \param[in] usart_id USART name (GPS, TELEM, AUX)
369 * \param[in] word_len Requested word length
370 * \param[in] stop_bits Requested stop bits
371 * \param[in] parity Requested parity
374 static void PIOS_USART_ChangeConfig(uint32_t usart_id,
375 enum PIOS_COM_Word_Length word_len,
376 enum PIOS_COM_Parity parity,
377 enum PIOS_COM_StopBits stop_bits,
378 uint32_t baud_rate)
380 struct pios_usart_dev *usart_dev = (struct pios_usart_dev *)usart_id;
382 bool valid = PIOS_USART_validate(usart_dev);
384 PIOS_Assert(valid);
386 if (usart_dev->config_locked) {
387 return;
390 switch (word_len) {
391 case PIOS_COM_Word_length_8b:
392 usart_dev->init.USART_WordLength = USART_WordLength_8b;
393 break;
394 case PIOS_COM_Word_length_9b:
395 usart_dev->init.USART_WordLength = USART_WordLength_9b;
396 break;
397 default:
398 break;
401 switch (stop_bits) {
402 case PIOS_COM_StopBits_1:
403 usart_dev->init.USART_StopBits = USART_StopBits_1;
404 break;
405 case PIOS_COM_StopBits_1_5:
406 usart_dev->init.USART_StopBits = USART_StopBits_1_5;
407 break;
408 case PIOS_COM_StopBits_2:
409 usart_dev->init.USART_StopBits = USART_StopBits_2;
410 break;
411 default:
412 break;
415 switch (parity) {
416 case PIOS_COM_Parity_No:
417 usart_dev->init.USART_Parity = USART_Parity_No;
418 break;
419 case PIOS_COM_Parity_Even:
420 usart_dev->init.USART_Parity = USART_Parity_Even;
421 break;
422 case PIOS_COM_Parity_Odd:
423 usart_dev->init.USART_Parity = USART_Parity_Odd;
424 break;
425 default:
426 break;
429 if (baud_rate) {
430 usart_dev->init.USART_BaudRate = baud_rate;
433 PIOS_USART_Setup(usart_dev);
437 static void PIOS_USART_RegisterRxCallback(uint32_t usart_id, pios_com_callback rx_in_cb, uint32_t context)
439 struct pios_usart_dev *usart_dev = (struct pios_usart_dev *)usart_id;
441 bool valid = PIOS_USART_validate(usart_dev);
443 PIOS_Assert(valid);
446 * Order is important in these assignments since ISR uses _cb
447 * field to determine if it's ok to dereference _cb and _context
449 usart_dev->rx_in_context = context;
450 usart_dev->rx_in_cb = rx_in_cb;
452 usart_dev->init.USART_Mode |= USART_Mode_Rx;
454 PIOS_USART_Setup(usart_dev);
457 static void PIOS_USART_RegisterTxCallback(uint32_t usart_id, pios_com_callback tx_out_cb, uint32_t context)
459 struct pios_usart_dev *usart_dev = (struct pios_usart_dev *)usart_id;
461 bool valid = PIOS_USART_validate(usart_dev);
463 PIOS_Assert(valid);
466 * Order is important in these assignments since ISR uses _cb
467 * field to determine if it's ok to dereference _cb and _context
469 usart_dev->tx_out_context = context;
470 usart_dev->tx_out_cb = tx_out_cb;
472 usart_dev->init.USART_Mode |= USART_Mode_Tx;
474 PIOS_USART_Setup(usart_dev);
477 static void PIOS_USART_generic_irq_handler(uint32_t usart_id)
479 struct pios_usart_dev *usart_dev = (struct pios_usart_dev *)usart_id;
481 bool valid = PIOS_USART_validate(usart_dev);
483 PIOS_Assert(valid);
485 /* Force read of dr after sr to make sure to clear error flags */
486 volatile uint16_t sr = usart_dev->cfg->regs->ISR;
487 volatile uint8_t dr = usart_dev->cfg->regs->RDR;
489 /* Check if RXNE flag is set */
490 bool rx_need_yield = false;
491 if (sr & USART_ISR_RXNE) {
492 uint8_t byte = dr;
493 if (usart_dev->rx_in_cb) {
494 uint16_t rc;
495 rc = (usart_dev->rx_in_cb)(usart_dev->rx_in_context, &byte, 1, NULL, &rx_need_yield);
496 if (rc < 1) {
497 /* Lost bytes on rx */
498 usart_dev->rx_dropped += 1;
503 /* Check if TXE flag is set */
504 bool tx_need_yield = false;
505 if (sr & USART_ISR_TXE) {
506 if (usart_dev->tx_out_cb) {
507 uint8_t b;
508 uint16_t bytes_to_send;
510 bytes_to_send = (usart_dev->tx_out_cb)(usart_dev->tx_out_context, &b, 1, NULL, &tx_need_yield);
512 if (bytes_to_send > 0) {
513 /* Send the byte we've been given */
514 usart_dev->cfg->regs->TDR = b;
515 } else {
516 /* No bytes to send, disable TXE interrupt */
517 USART_ITConfig(usart_dev->cfg->regs, USART_IT_TXE, DISABLE);
519 } else {
520 /* No bytes to send, disable TXE interrupt */
521 USART_ITConfig(usart_dev->cfg->regs, USART_IT_TXE, DISABLE);
525 #if defined(PIOS_INCLUDE_FREERTOS)
526 if (rx_need_yield || tx_need_yield) {
527 vPortYield();
529 #endif /* PIOS_INCLUDE_FREERTOS */
532 static int32_t PIOS_USART_Ioctl(uint32_t usart_id, uint32_t ctl, void *param)
534 struct pios_usart_dev *usart_dev = (struct pios_usart_dev *)usart_id;
536 bool valid = PIOS_USART_validate(usart_dev);
538 PIOS_Assert(valid);
540 /* some sort of locking would be great to have */
542 uint32_t cr1_ue = usart_dev->cfg->regs->CR1 & USART_CR1_UE;
544 switch (ctl) {
545 case PIOS_IOCTL_USART_SET_IRQ_PRIO:
546 return PIOS_USART_SetIrqPrio(usart_dev, *(uint8_t *)param);
548 case PIOS_IOCTL_USART_SET_INVERTED:
550 enum PIOS_USART_Inverted inverted = *(enum PIOS_USART_Inverted *)param;
552 usart_dev->cfg->regs->CR1 &= ~((uint32_t)USART_CR1_UE);
554 if (usart_dev->cfg->rx.gpio != 0) {
555 GPIO_InitTypeDef gpioInit = usart_dev->cfg->rx.init;
557 gpioInit.GPIO_PuPd = (inverted & PIOS_USART_Inverted_Rx) ? GPIO_PuPd_DOWN : GPIO_PuPd_UP;
559 GPIO_Init(usart_dev->cfg->rx.gpio, &gpioInit);
561 USART_InvPinCmd(usart_dev->cfg->regs, USART_InvPin_Rx, (inverted & PIOS_USART_Inverted_Rx) ? ENABLE : DISABLE);
564 if (usart_dev->cfg->tx.gpio != 0) {
565 USART_InvPinCmd(usart_dev->cfg->regs, USART_InvPin_Tx, (inverted & PIOS_USART_Inverted_Tx) ? ENABLE : DISABLE);
568 break;
569 case PIOS_IOCTL_USART_SET_SWAPPIN:
570 usart_dev->cfg->regs->CR1 &= ~((uint32_t)USART_CR1_UE);
571 USART_SWAPPinCmd(usart_dev->cfg->regs, *(bool *)param ? ENABLE : DISABLE);
572 break;
573 case PIOS_IOCTL_USART_SET_HALFDUPLEX:
574 usart_dev->cfg->regs->CR1 &= ~((uint32_t)USART_CR1_UE);
575 USART_HalfDuplexCmd(usart_dev->cfg->regs, *(bool *)param ? ENABLE : DISABLE);
576 break;
577 case PIOS_IOCTL_USART_GET_DSMBIND:
578 case PIOS_IOCTL_USART_GET_RXGPIO:
579 *(struct stm32_gpio *)param = usart_dev->cfg->rx;
580 break;
581 case PIOS_IOCTL_USART_GET_TXGPIO:
582 *(struct stm32_gpio *)param = usart_dev->cfg->tx;
583 break;
584 case PIOS_IOCTL_USART_LOCK_CONFIG:
585 usart_dev->config_locked = *(bool *)param;
586 break;
587 default:
588 return -1;
591 usart_dev->cfg->regs->CR1 |= cr1_ue;
593 return 0;
596 #endif /* PIOS_INCLUDE_USART */
599 * @}
600 * @}