2 ******************************************************************************
3 * @addtogroup PIOS PIOS Core hardware abstraction layer
5 * @addtogroup PIOS_USART USART Functions
6 * @brief PIOS interface for USART port
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
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
33 * @todo This is virtually identical to the F1xx driver and should be merged.
38 #ifdef PIOS_INCLUDE_USART
40 #include <pios_usart_priv.h>
42 /* Provide a COM driver */
43 static void PIOS_USART_ChangeBaud(uint32_t usart_id
, uint32_t baud
);
44 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
);
45 static void PIOS_USART_SetCtrlLine(uint32_t usart_id
, uint32_t mask
, uint32_t state
);
46 static void PIOS_USART_RegisterRxCallback(uint32_t usart_id
, pios_com_callback rx_in_cb
, uint32_t context
);
47 static void PIOS_USART_RegisterTxCallback(uint32_t usart_id
, pios_com_callback tx_out_cb
, uint32_t context
);
48 static void PIOS_USART_TxStart(uint32_t usart_id
, uint16_t tx_bytes_avail
);
49 static void PIOS_USART_RxStart(uint32_t usart_id
, uint16_t rx_bytes_avail
);
50 static int32_t PIOS_USART_Ioctl(uint32_t usart_id
, uint32_t ctl
, void *param
);
52 const struct pios_com_driver pios_usart_com_driver
= {
53 .set_baud
= PIOS_USART_ChangeBaud
,
54 .set_config
= PIOS_USART_ChangeConfig
,
55 .set_ctrl_line
= PIOS_USART_SetCtrlLine
,
56 .tx_start
= PIOS_USART_TxStart
,
57 .rx_start
= PIOS_USART_RxStart
,
58 .bind_tx_cb
= PIOS_USART_RegisterTxCallback
,
59 .bind_rx_cb
= PIOS_USART_RegisterRxCallback
,
60 .ioctl
= PIOS_USART_Ioctl
,
63 enum pios_usart_dev_magic
{
64 PIOS_USART_DEV_MAGIC
= 0x4152834A,
67 struct pios_usart_dev
{
68 enum pios_usart_dev_magic magic
;
69 const struct pios_usart_cfg
*cfg
;
70 USART_InitTypeDef init
;
71 pios_com_callback rx_in_cb
;
72 uint32_t rx_in_context
;
73 pios_com_callback tx_out_cb
;
74 uint32_t tx_out_context
;
79 static bool PIOS_USART_validate(struct pios_usart_dev
*usart_dev
)
81 return usart_dev
->magic
== PIOS_USART_DEV_MAGIC
;
84 const struct pios_usart_cfg
*PIOS_USART_GetConfig(uint32_t usart_id
)
86 struct pios_usart_dev
*usart_dev
= (struct pios_usart_dev
*)usart_id
;
88 bool valid
= PIOS_USART_validate(usart_dev
);
92 return usart_dev
->cfg
;
95 static int32_t PIOS_USART_SetIrqPrio(struct pios_usart_dev
*usart_dev
, uint8_t irq_prio
)
97 NVIC_InitTypeDef init
= {
98 .NVIC_IRQChannel
= usart_dev
->irq_channel
,
99 .NVIC_IRQChannelPreemptionPriority
= irq_prio
,
100 .NVIC_IRQChannelCmd
= ENABLE
,
108 #if defined(PIOS_INCLUDE_FREERTOS)
109 static struct pios_usart_dev
*PIOS_USART_alloc(void)
111 struct pios_usart_dev
*usart_dev
;
113 usart_dev
= (struct pios_usart_dev
*)pios_malloc(sizeof(struct pios_usart_dev
));
118 memset(usart_dev
, 0, sizeof(struct pios_usart_dev
));
119 usart_dev
->magic
= PIOS_USART_DEV_MAGIC
;
123 static struct pios_usart_dev pios_usart_devs
[PIOS_USART_MAX_DEVS
];
124 static uint8_t pios_usart_num_devs
;
125 static struct pios_usart_dev
*PIOS_USART_alloc(void)
127 struct pios_usart_dev
*usart_dev
;
129 if (pios_usart_num_devs
>= PIOS_USART_MAX_DEVS
) {
133 usart_dev
= &pios_usart_devs
[pios_usart_num_devs
++];
135 memset(usart_dev
, 0, sizeof(struct pios_usart_dev
));
136 usart_dev
->magic
= PIOS_USART_DEV_MAGIC
;
140 #endif /* if defined(PIOS_INCLUDE_FREERTOS) */
142 /* Bind Interrupt Handlers
144 * Map all valid USART IRQs to the common interrupt handler
145 * and provide storage for a 32-bit device id IRQ to map
146 * each physical IRQ to a specific registered device instance.
148 static void PIOS_USART_generic_irq_handler(uint32_t usart_id
);
150 static uint32_t PIOS_USART_1_id
;
151 void USART1_IRQHandler(void) __attribute__((alias("PIOS_USART_1_irq_handler")));
152 static void PIOS_USART_1_irq_handler(void)
154 PIOS_USART_generic_irq_handler(PIOS_USART_1_id
);
157 static uint32_t PIOS_USART_2_id
;
158 void USART2_IRQHandler(void) __attribute__((alias("PIOS_USART_2_irq_handler")));
159 static void PIOS_USART_2_irq_handler(void)
161 PIOS_USART_generic_irq_handler(PIOS_USART_2_id
);
164 static uint32_t PIOS_USART_3_id
;
165 void USART3_IRQHandler(void) __attribute__((alias("PIOS_USART_3_irq_handler")));
166 static void PIOS_USART_3_irq_handler(void)
168 PIOS_USART_generic_irq_handler(PIOS_USART_3_id
);
171 static uint32_t PIOS_USART_4_id
;
172 void USART4_IRQHandler(void) __attribute__((alias("PIOS_USART_4_irq_handler")));
173 static void PIOS_USART_4_irq_handler(void)
175 PIOS_USART_generic_irq_handler(PIOS_USART_4_id
);
178 static uint32_t PIOS_USART_5_id
;
179 void USART5_IRQHandler(void) __attribute__((alias("PIOS_USART_5_irq_handler")));
180 static void PIOS_USART_5_irq_handler(void)
182 PIOS_USART_generic_irq_handler(PIOS_USART_5_id
);
185 static uint32_t PIOS_USART_6_id
;
186 void USART6_IRQHandler(void) __attribute__((alias("PIOS_USART_6_irq_handler")));
187 static void PIOS_USART_6_irq_handler(void)
189 PIOS_USART_generic_irq_handler(PIOS_USART_6_id
);
193 * Initialise a single USART device
195 int32_t PIOS_USART_Init(uint32_t *usart_id
, const struct pios_usart_cfg
*cfg
)
197 PIOS_DEBUG_Assert(usart_id
);
198 PIOS_DEBUG_Assert(cfg
);
203 switch ((uint32_t)cfg
->regs
) {
204 case (uint32_t)USART1
:
205 local_id
= &PIOS_USART_1_id
;
206 irq_channel
= USART1_IRQn
;
208 case (uint32_t)USART2
:
209 local_id
= &PIOS_USART_2_id
;
210 irq_channel
= USART2_IRQn
;
212 #if !defined(STM32F411xE)
213 case (uint32_t)USART3
:
214 local_id
= &PIOS_USART_3_id
;
215 irq_channel
= USART3_IRQn
;
217 case (uint32_t)UART4
:
218 local_id
= &PIOS_USART_4_id
;
219 irq_channel
= UART4_IRQn
;
221 case (uint32_t)UART5
:
222 local_id
= &PIOS_USART_5_id
;
223 irq_channel
= UART5_IRQn
;
225 #endif /* STM32F411xE */
226 case (uint32_t)USART6
:
227 local_id
= &PIOS_USART_6_id
;
228 irq_channel
= USART6_IRQn
;
235 /* this port is already open */
236 *usart_id
= *local_id
;
240 struct pios_usart_dev
*usart_dev
;
242 usart_dev
= (struct pios_usart_dev
*)PIOS_USART_alloc();
247 /* Bind the configuration to the device instance */
248 usart_dev
->cfg
= cfg
;
249 usart_dev
->irq_channel
= irq_channel
;
251 /* Initialize the comm parameter structure */
252 USART_StructInit(&usart_dev
->init
); // 9600 8n1
254 /* We will set modes later, depending on installed callbacks */
255 usart_dev
->init
.USART_Mode
= 0;
258 /* If a DTR line is specified, initialize it */
259 if (usart_dev
->cfg
->dtr
.gpio
) {
260 GPIO_Init(usart_dev
->cfg
->dtr
.gpio
, (GPIO_InitTypeDef
*)&usart_dev
->cfg
->dtr
.init
);
261 PIOS_USART_SetCtrlLine((uint32_t)usart_dev
, COM_CTRL_LINE_DTR_MASK
, 0);
263 #ifdef PIOS_USART_INVERTER_PORT
264 /* Initialize inverter gpio and set it to off */
265 if (usart_dev
->cfg
->regs
== PIOS_USART_INVERTER_PORT
) {
266 GPIO_InitTypeDef inverterGPIOInit
= {
267 .GPIO_Pin
= PIOS_USART_INVERTER_PIN
,
268 .GPIO_Speed
= GPIO_Speed_2MHz
,
269 .GPIO_Mode
= GPIO_Mode_OUT
,
270 .GPIO_OType
= GPIO_OType_PP
,
271 .GPIO_PuPd
= GPIO_PuPd_UP
273 GPIO_Init(PIOS_USART_INVERTER_GPIO
, &inverterGPIOInit
);
275 GPIO_WriteBit(PIOS_USART_INVERTER_GPIO
,
276 PIOS_USART_INVERTER_PIN
,
277 PIOS_USART_INVERTER_DISABLE
);
281 *usart_id
= (uint32_t)usart_dev
;
282 *local_id
= (uint32_t)usart_dev
;
284 PIOS_USART_SetIrqPrio(usart_dev
, PIOS_IRQ_PRIO_MID
);
292 static void PIOS_USART_Setup(struct pios_usart_dev
*usart_dev
)
294 /* Configure RX GPIO */
295 if ((usart_dev
->init
.USART_Mode
& USART_Mode_Rx
) && (usart_dev
->cfg
->rx
.gpio
)) {
296 if (usart_dev
->cfg
->remap
) {
297 GPIO_PinAFConfig(usart_dev
->cfg
->rx
.gpio
,
298 __builtin_ctz(usart_dev
->cfg
->rx
.init
.GPIO_Pin
),
299 usart_dev
->cfg
->remap
);
302 GPIO_Init(usart_dev
->cfg
->rx
.gpio
, (GPIO_InitTypeDef
*)&usart_dev
->cfg
->rx
.init
);
304 /* just enable RX right away, cause rcvr modules do not call rx_start method */
305 USART_ITConfig(usart_dev
->cfg
->regs
, USART_IT_RXNE
, ENABLE
);
308 /* Configure TX GPIO */
309 if ((usart_dev
->init
.USART_Mode
& USART_Mode_Tx
) && usart_dev
->cfg
->tx
.gpio
) {
310 if (usart_dev
->cfg
->remap
) {
311 GPIO_PinAFConfig(usart_dev
->cfg
->tx
.gpio
,
312 __builtin_ctz(usart_dev
->cfg
->tx
.init
.GPIO_Pin
),
313 usart_dev
->cfg
->remap
);
316 GPIO_Init(usart_dev
->cfg
->tx
.gpio
, (GPIO_InitTypeDef
*)&usart_dev
->cfg
->tx
.init
);
319 /* Write new configuration */
320 { // fix parity stuff
321 USART_InitTypeDef init
= usart_dev
->init
;
323 if ((init
.USART_Parity
!= USART_Parity_No
) && (init
.USART_WordLength
== USART_WordLength_8b
)) {
324 init
.USART_WordLength
= USART_WordLength_9b
;
327 USART_Init(usart_dev
->cfg
->regs
, &init
);
333 USART_Cmd(usart_dev
->cfg
->regs
, ENABLE
);
336 static void PIOS_USART_RxStart(uint32_t usart_id
, __attribute__((unused
)) uint16_t rx_bytes_avail
)
338 struct pios_usart_dev
*usart_dev
= (struct pios_usart_dev
*)usart_id
;
340 bool valid
= PIOS_USART_validate(usart_dev
);
344 USART_ITConfig(usart_dev
->cfg
->regs
, USART_IT_RXNE
, ENABLE
);
346 static void PIOS_USART_TxStart(uint32_t usart_id
, __attribute__((unused
)) uint16_t tx_bytes_avail
)
348 struct pios_usart_dev
*usart_dev
= (struct pios_usart_dev
*)usart_id
;
350 bool valid
= PIOS_USART_validate(usart_dev
);
354 USART_ITConfig(usart_dev
->cfg
->regs
, USART_IT_TXE
, ENABLE
);
358 * Changes the baud rate of the USART peripheral without re-initialising.
359 * \param[in] usart_id USART name (GPS, TELEM, AUX)
360 * \param[in] baud Requested baud rate
362 static void PIOS_USART_ChangeBaud(uint32_t usart_id
, uint32_t baud
)
364 struct pios_usart_dev
*usart_dev
= (struct pios_usart_dev
*)usart_id
;
366 bool valid
= PIOS_USART_validate(usart_dev
);
370 if (usart_dev
->config_locked
) {
374 /* Use our working copy of the usart init structure */
375 usart_dev
->init
.USART_BaudRate
= baud
;
377 PIOS_USART_Setup(usart_dev
);
381 * Changes configuration of the USART peripheral without re-initialising.
382 * \param[in] usart_id USART name (GPS, TELEM, AUX)
383 * \param[in] word_len Requested word length
384 * \param[in] stop_bits Requested stop bits
385 * \param[in] parity Requested parity
386 * \param[in] baud_rate Requested baud rate
387 * \param[in] mode Requested mode
390 static void PIOS_USART_ChangeConfig(uint32_t usart_id
,
391 enum PIOS_COM_Word_Length word_len
,
392 enum PIOS_COM_Parity parity
,
393 enum PIOS_COM_StopBits stop_bits
,
396 struct pios_usart_dev
*usart_dev
= (struct pios_usart_dev
*)usart_id
;
398 bool valid
= PIOS_USART_validate(usart_dev
);
402 if (usart_dev
->config_locked
) {
407 case PIOS_COM_Word_length_8b
:
408 usart_dev
->init
.USART_WordLength
= USART_WordLength_8b
;
410 case PIOS_COM_Word_length_9b
:
411 usart_dev
->init
.USART_WordLength
= USART_WordLength_9b
;
418 case PIOS_COM_StopBits_0_5
:
419 usart_dev
->init
.USART_StopBits
= USART_StopBits_0_5
;
421 case PIOS_COM_StopBits_1
:
422 usart_dev
->init
.USART_StopBits
= USART_StopBits_1
;
424 case PIOS_COM_StopBits_1_5
:
425 usart_dev
->init
.USART_StopBits
= USART_StopBits_1_5
;
427 case PIOS_COM_StopBits_2
:
428 usart_dev
->init
.USART_StopBits
= USART_StopBits_2
;
435 case PIOS_COM_Parity_No
:
436 usart_dev
->init
.USART_Parity
= USART_Parity_No
;
438 case PIOS_COM_Parity_Even
:
439 usart_dev
->init
.USART_Parity
= USART_Parity_Even
;
441 case PIOS_COM_Parity_Odd
:
442 usart_dev
->init
.USART_Parity
= USART_Parity_Odd
;
449 usart_dev
->init
.USART_BaudRate
= baud_rate
;
452 PIOS_USART_Setup(usart_dev
);
455 static void PIOS_USART_SetCtrlLine(uint32_t usart_id
, uint32_t mask
, uint32_t state
)
457 struct pios_usart_dev
*usart_dev
= (struct pios_usart_dev
*)usart_id
;
459 bool valid
= PIOS_USART_validate(usart_dev
);
463 /* Only attempt to drive DTR if this USART has a GPIO line defined */
464 if (usart_dev
->cfg
->dtr
.gpio
&& (mask
& COM_CTRL_LINE_DTR_MASK
)) {
465 GPIO_WriteBit(usart_dev
->cfg
->dtr
.gpio
,
466 usart_dev
->cfg
->dtr
.init
.GPIO_Pin
,
467 state
& COM_CTRL_LINE_DTR_MASK
? Bit_RESET
: Bit_SET
);
471 static void PIOS_USART_RegisterRxCallback(uint32_t usart_id
, pios_com_callback rx_in_cb
, uint32_t context
)
473 struct pios_usart_dev
*usart_dev
= (struct pios_usart_dev
*)usart_id
;
475 bool valid
= PIOS_USART_validate(usart_dev
);
480 * Order is important in these assignments since ISR uses _cb
481 * field to determine if it's ok to dereference _cb and _context
483 usart_dev
->rx_in_context
= context
;
484 usart_dev
->rx_in_cb
= rx_in_cb
;
486 usart_dev
->init
.USART_Mode
|= USART_Mode_Rx
;
488 PIOS_USART_Setup(usart_dev
);
491 static void PIOS_USART_RegisterTxCallback(uint32_t usart_id
, pios_com_callback tx_out_cb
, uint32_t context
)
493 struct pios_usart_dev
*usart_dev
= (struct pios_usart_dev
*)usart_id
;
495 bool valid
= PIOS_USART_validate(usart_dev
);
500 * Order is important in these assignments since ISR uses _cb
501 * field to determine if it's ok to dereference _cb and _context
503 usart_dev
->tx_out_context
= context
;
504 usart_dev
->tx_out_cb
= tx_out_cb
;
506 usart_dev
->init
.USART_Mode
|= USART_Mode_Tx
;
508 PIOS_USART_Setup(usart_dev
);
511 static void PIOS_USART_generic_irq_handler(uint32_t usart_id
)
513 struct pios_usart_dev
*usart_dev
= (struct pios_usart_dev
*)usart_id
;
515 bool valid
= PIOS_USART_validate(usart_dev
);
519 /* Force read of dr after sr to make sure to clear error flags */
520 volatile uint16_t sr
= usart_dev
->cfg
->regs
->SR
;
521 volatile uint8_t dr
= usart_dev
->cfg
->regs
->DR
;
523 /* Check if RXNE flag is set */
524 bool rx_need_yield
= false;
525 if (sr
& USART_SR_RXNE
) {
527 if (usart_dev
->rx_in_cb
) {
528 (void)(usart_dev
->rx_in_cb
)(usart_dev
->rx_in_context
, &byte
, 1, NULL
, &rx_need_yield
);
532 /* Check if TXE flag is set */
533 bool tx_need_yield
= false;
534 if (sr
& USART_SR_TXE
) {
535 if (usart_dev
->tx_out_cb
) {
537 uint16_t bytes_to_send
;
539 bytes_to_send
= (usart_dev
->tx_out_cb
)(usart_dev
->tx_out_context
, &b
, 1, NULL
, &tx_need_yield
);
541 if (bytes_to_send
> 0) {
542 /* Send the byte we've been given */
543 usart_dev
->cfg
->regs
->DR
= b
;
545 /* No bytes to send, disable TXE interrupt */
546 USART_ITConfig(usart_dev
->cfg
->regs
, USART_IT_TXE
, DISABLE
);
549 /* No bytes to send, disable TXE interrupt */
550 USART_ITConfig(usart_dev
->cfg
->regs
, USART_IT_TXE
, DISABLE
);
554 #if defined(PIOS_INCLUDE_FREERTOS)
555 if (rx_need_yield
|| tx_need_yield
) {
558 #endif /* PIOS_INCLUDE_FREERTOS */
561 static int32_t PIOS_USART_Ioctl(uint32_t usart_id
, uint32_t ctl
, void *param
)
563 struct pios_usart_dev
*usart_dev
= (struct pios_usart_dev
*)usart_id
;
565 bool valid
= PIOS_USART_validate(usart_dev
);
569 /* First try board specific IOCTL to allow overriding default functions */
570 if (usart_dev
->cfg
->ioctl
) {
571 int32_t ret
= usart_dev
->cfg
->ioctl(usart_id
, ctl
, param
);
572 if (ret
!= COM_IOCTL_ENOSYS
) {
578 case PIOS_IOCTL_USART_SET_IRQ_PRIO
:
579 return PIOS_USART_SetIrqPrio(usart_dev
, *(uint8_t *)param
);
581 #ifdef PIOS_USART_INVERTER_PORT
582 case PIOS_IOCTL_USART_SET_INVERTED
:
583 if (usart_dev
->cfg
->regs
!= PIOS_USART_INVERTER_PORT
) {
584 return COM_IOCTL_ENOSYS
; /* don't know how */
586 GPIO_WriteBit(PIOS_USART_INVERTER_GPIO
,
587 PIOS_USART_INVERTER_PIN
,
588 (*(enum PIOS_USART_Inverted
*)param
& PIOS_USART_Inverted_Rx
) ? PIOS_USART_INVERTER_ENABLE
: PIOS_USART_INVERTER_DISABLE
);
591 #endif /* PIOS_USART_INVERTER_PORT */
592 case PIOS_IOCTL_USART_GET_DSMBIND
:
593 #ifdef PIOS_USART_INVERTER_PORT
594 if (usart_dev
->cfg
->regs
== PIOS_USART_INVERTER_PORT
) {
595 return -2; /* do not allow dsm bind on port with inverter */
597 #endif /* otherwise, return RXGPIO */
598 case PIOS_IOCTL_USART_GET_RXGPIO
:
599 *(struct stm32_gpio
*)param
= usart_dev
->cfg
->rx
;
601 case PIOS_IOCTL_USART_GET_TXGPIO
:
602 *(struct stm32_gpio
*)param
= usart_dev
->cfg
->tx
;
604 case PIOS_IOCTL_USART_SET_HALFDUPLEX
:
605 USART_HalfDuplexCmd(usart_dev
->cfg
->regs
, *(bool *)param
? ENABLE
: DISABLE
);
607 case PIOS_IOCTL_USART_LOCK_CONFIG
:
608 usart_dev
->config_locked
= *(bool *)param
;
611 return COM_IOCTL_ENOSYS
; /* unknown ioctl */
617 #endif /* PIOS_INCLUDE_USART */