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
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
;
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
);
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
,
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
));
114 memset(usart_dev
, 0, sizeof(struct pios_usart_dev
));
115 usart_dev
->magic
= PIOS_USART_DEV_MAGIC
;
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
) {
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
;
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
);
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
);
184 case (uint32_t)USART2
:
185 local_id
= &PIOS_USART_2_id
;
186 irq_channel
= USART2_IRQn
;
187 RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2
, ENABLE
);
189 case (uint32_t)USART3
:
190 local_id
= &PIOS_USART_3_id
;
191 irq_channel
= USART3_IRQn
;
192 RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3
, ENABLE
);
199 /* this port is already open */
200 *usart_id
= *local_id
;
204 struct pios_usart_dev
*usart_dev
;
206 usart_dev
= (struct pios_usart_dev
*)PIOS_USART_alloc();
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;
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
);
239 *usart_id
= (uint32_t)usart_dev
;
240 *local_id
= (uint32_t)usart_dev
;
242 PIOS_USART_SetIrqPrio(usart_dev
, PIOS_IRQ_PRIO_MID
);
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
);
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
);
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
);
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
);
325 if (usart_dev
->config_locked
) {
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
,
351 struct pios_usart_dev
*usart_dev
= (struct pios_usart_dev
*)usart_id
;
353 bool valid
= PIOS_USART_validate(usart_dev
);
357 if (usart_dev
->config_locked
) {
362 case PIOS_COM_Word_length_8b
:
363 usart_dev
->init
.USART_WordLength
= USART_WordLength_8b
;
365 case PIOS_COM_Word_length_9b
:
366 usart_dev
->init
.USART_WordLength
= USART_WordLength_9b
;
373 case PIOS_COM_StopBits_0_5
:
374 usart_dev
->init
.USART_StopBits
= USART_StopBits_0_5
;
376 case PIOS_COM_StopBits_1
:
377 usart_dev
->init
.USART_StopBits
= USART_StopBits_1
;
379 case PIOS_COM_StopBits_1_5
:
380 usart_dev
->init
.USART_StopBits
= USART_StopBits_1_5
;
382 case PIOS_COM_StopBits_2
:
383 usart_dev
->init
.USART_StopBits
= USART_StopBits_2
;
390 case PIOS_COM_Parity_No
:
391 usart_dev
->init
.USART_Parity
= USART_Parity_No
;
393 case PIOS_COM_Parity_Even
:
394 usart_dev
->init
.USART_Parity
= USART_Parity_Even
;
396 case PIOS_COM_Parity_Odd
:
397 usart_dev
->init
.USART_Parity
= USART_Parity_Odd
;
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
);
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
);
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
);
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
) {
466 if (usart_dev
->rx_in_cb
) {
468 rc
= (usart_dev
->rx_in_cb
)(usart_dev
->rx_in_context
, &byte
, 1, NULL
, &rx_need_yield
);
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
) {
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
;
489 /* No bytes to send, disable TXE interrupt */
490 USART_ITConfig(usart_dev
->cfg
->regs
, USART_IT_TXE
, DISABLE
);
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
) {
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
);
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
) {
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
);
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
;
545 case PIOS_IOCTL_USART_GET_TXGPIO
:
546 *(struct stm32_gpio
*)param
= usart_dev
->cfg
->tx
;
548 case PIOS_IOCTL_USART_SET_HALFDUPLEX
:
549 USART_HalfDuplexCmd(usart_dev
->cfg
->regs
, *(bool *)param
? ENABLE
: DISABLE
);
551 case PIOS_IOCTL_USART_LOCK_CONFIG
:
552 usart_dev
->config_locked
= *(bool *)param
;
555 return COM_IOCTL_ENOSYS
; /* unknown ioctl */
561 #endif /* PIOS_INCLUDE_USART */