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 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
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
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
;
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
,
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
));
101 memset(usart_dev
, 0, sizeof(struct pios_usart_dev
));
102 usart_dev
->magic
= PIOS_USART_DEV_MAGIC
;
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
) {
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
;
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
);
180 switch ((uint32_t)cfg
->regs
) {
181 case (uint32_t)USART1
:
182 local_id
= &PIOS_USART_1_id
;
183 irq_channel
= USART1_IRQn
;
185 case (uint32_t)USART2
:
186 local_id
= &PIOS_USART_2_id
;
187 irq_channel
= USART2_IRQn
;
189 case (uint32_t)USART3
:
190 local_id
= &PIOS_USART_3_id
;
191 irq_channel
= USART3_IRQn
;
193 case (uint32_t)UART4
:
194 local_id
= &PIOS_UART_4_id
;
195 irq_channel
= UART4_IRQn
;
197 case (uint32_t)UART5
:
198 local_id
= &PIOS_UART_5_id
;
199 irq_channel
= UART5_IRQn
;
206 /* this port is already open */
207 *usart_id
= *local_id
;
211 struct pios_usart_dev
*usart_dev
;
213 usart_dev
= (struct pios_usart_dev
*)PIOS_USART_alloc();
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;
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
);
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 */
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",
300 usart_dev
->init
.USART_BaudRate
,
301 usart_dev
->init
.USART_WordLength
== USART_WordLength_8b
? 8 : 9,
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
);
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
);
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
);
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
);
357 if (usart_dev
->config_locked
) {
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
,
380 struct pios_usart_dev
*usart_dev
= (struct pios_usart_dev
*)usart_id
;
382 bool valid
= PIOS_USART_validate(usart_dev
);
386 if (usart_dev
->config_locked
) {
391 case PIOS_COM_Word_length_8b
:
392 usart_dev
->init
.USART_WordLength
= USART_WordLength_8b
;
394 case PIOS_COM_Word_length_9b
:
395 usart_dev
->init
.USART_WordLength
= USART_WordLength_9b
;
402 case PIOS_COM_StopBits_1
:
403 usart_dev
->init
.USART_StopBits
= USART_StopBits_1
;
405 case PIOS_COM_StopBits_1_5
:
406 usart_dev
->init
.USART_StopBits
= USART_StopBits_1_5
;
408 case PIOS_COM_StopBits_2
:
409 usart_dev
->init
.USART_StopBits
= USART_StopBits_2
;
416 case PIOS_COM_Parity_No
:
417 usart_dev
->init
.USART_Parity
= USART_Parity_No
;
419 case PIOS_COM_Parity_Even
:
420 usart_dev
->init
.USART_Parity
= USART_Parity_Even
;
422 case PIOS_COM_Parity_Odd
:
423 usart_dev
->init
.USART_Parity
= USART_Parity_Odd
;
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
);
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
);
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
);
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
) {
493 if (usart_dev
->rx_in_cb
) {
495 rc
= (usart_dev
->rx_in_cb
)(usart_dev
->rx_in_context
, &byte
, 1, NULL
, &rx_need_yield
);
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
) {
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
;
516 /* No bytes to send, disable TXE interrupt */
517 USART_ITConfig(usart_dev
->cfg
->regs
, USART_IT_TXE
, DISABLE
);
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
) {
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
);
540 /* some sort of locking would be great to have */
542 uint32_t cr1_ue
= usart_dev
->cfg
->regs
->CR1
& USART_CR1_UE
;
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
);
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
);
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
);
577 case PIOS_IOCTL_USART_GET_DSMBIND
:
578 case PIOS_IOCTL_USART_GET_RXGPIO
:
579 *(struct stm32_gpio
*)param
= usart_dev
->cfg
->rx
;
581 case PIOS_IOCTL_USART_GET_TXGPIO
:
582 *(struct stm32_gpio
*)param
= usart_dev
->cfg
->tx
;
584 case PIOS_IOCTL_USART_LOCK_CONFIG
:
585 usart_dev
->config_locked
= *(bool *)param
;
591 usart_dev
->cfg
->regs
->CR1
|= cr1_ue
;
596 #endif /* PIOS_INCLUDE_USART */