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>
38 #define PIOS_UART_TX_BUFFER 10
39 /* Provide a COM driver */
40 static void PIOS_USART_ChangeBaud(uint32_t usart_id
, uint32_t baud
);
42 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
);
44 static void PIOS_USART_RegisterRxCallback(uint32_t usart_id
, pios_com_callback rx_in_cb
, uint32_t context
);
45 static void PIOS_USART_RegisterTxCallback(uint32_t usart_id
, pios_com_callback tx_out_cb
, uint32_t context
);
46 static void PIOS_USART_TxStart(uint32_t usart_id
, uint16_t tx_bytes_avail
);
47 static void PIOS_USART_RxStart(uint32_t usart_id
, uint16_t rx_bytes_avail
);
49 const struct pios_com_driver pios_usart_com_driver
= {
50 .set_baud
= PIOS_USART_ChangeBaud
,
52 .set_config
= PIOS_USART_ChangeConfig
,
54 .tx_start
= PIOS_USART_TxStart
,
55 .rx_start
= PIOS_USART_RxStart
,
56 .bind_tx_cb
= PIOS_USART_RegisterTxCallback
,
57 .bind_rx_cb
= PIOS_USART_RegisterRxCallback
,
60 enum pios_usart_dev_magic
{
61 PIOS_USART_DEV_MAGIC
= 0x11223344,
64 struct pios_usart_dev
{
65 enum pios_usart_dev_magic magic
;
66 const struct pios_usart_cfg
*cfg
;
67 USART_InitTypeDef init
;
68 pios_com_callback rx_in_cb
;
69 uint32_t rx_in_context
;
70 pios_com_callback tx_out_cb
;
71 uint32_t tx_out_context
;
75 uint8_t tx_buffer
[PIOS_UART_TX_BUFFER
];
81 static struct pios_usart_dev
*PIOS_USART_validate(uint32_t usart_id
)
83 struct pios_usart_dev
*usart_dev
= (struct pios_usart_dev
*)usart_id
;
85 bool valid
= (usart_dev
->magic
== PIOS_USART_DEV_MAGIC
);
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
*)pvPortMalloc(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_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_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
);
146 #if defined(STM32F072)
147 static uint32_t PIOS_USART_3_id
;
148 void USART3_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 int32_t PIOS_USART_SetIrqPrio(struct pios_usart_dev
*usart_dev
, uint8_t irq_prio
)
157 NVIC_InitTypeDef init
= {
158 .NVIC_IRQChannel
= usart_dev
->irq_channel
,
159 .NVIC_IRQChannelPriority
= irq_prio
,
160 .NVIC_IRQChannelCmd
= ENABLE
,
169 * Initialise a single USART device
171 int32_t PIOS_USART_Init(uint32_t *usart_id
, const struct pios_usart_cfg
*cfg
)
173 PIOS_DEBUG_Assert(usart_id
);
174 PIOS_DEBUG_Assert(cfg
);
179 /* Enable USART clock */
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 #if defined(STM32F072)
190 case (uint32_t)USART3
:
191 local_id
= &PIOS_USART_3_id
;
192 irq_channel
= USART3_4_IRQn
;
199 struct pios_usart_dev
*usart_dev
;
201 usart_dev
= (struct pios_usart_dev
*)PIOS_USART_alloc();
206 /* Bind the configuration to the device instance */
207 usart_dev
->cfg
= cfg
;
208 usart_dev
->irq_channel
= irq_channel
;
210 /* Initialize the comm parameter structure */
211 USART_StructInit(&usart_dev
->init
); // 9600 8n1
213 /* We will set modes later, depending on installed callbacks */
214 usart_dev
->init
.USART_Mode
= 0;
216 *usart_id
= (uint32_t)usart_dev
;
217 *local_id
= (uint32_t)usart_dev
;
219 PIOS_USART_SetIrqPrio(usart_dev
, PIOS_IRQ_PRIO_MID
);
221 /* Disable overrun detection */
222 USART_OverrunDetectionConfig(usart_dev
->cfg
->regs
, USART_OVRDetection_Disable
);
230 static void PIOS_USART_Setup(struct pios_usart_dev
*usart_dev
)
232 /* Configure RX GPIO */
233 if ((usart_dev
->init
.USART_Mode
& USART_Mode_Rx
) && (usart_dev
->cfg
->rx
.gpio
)) {
234 if (usart_dev
->cfg
->remap
) {
235 GPIO_PinAFConfig(usart_dev
->cfg
->rx
.gpio
,
236 __builtin_ctz(usart_dev
->cfg
->rx
.init
.GPIO_Pin
),
237 usart_dev
->cfg
->remap
);
240 GPIO_Init(usart_dev
->cfg
->rx
.gpio
, (GPIO_InitTypeDef
*)&usart_dev
->cfg
->rx
.init
);
242 USART_ITConfig(usart_dev
->cfg
->regs
, USART_IT_RXNE
, ENABLE
);
245 /* Configure TX GPIO */
246 if ((usart_dev
->init
.USART_Mode
& USART_Mode_Tx
) && usart_dev
->cfg
->tx
.gpio
) {
247 if (usart_dev
->cfg
->remap
) {
248 GPIO_PinAFConfig(usart_dev
->cfg
->tx
.gpio
,
249 __builtin_ctz(usart_dev
->cfg
->tx
.init
.GPIO_Pin
),
250 usart_dev
->cfg
->remap
);
253 GPIO_Init(usart_dev
->cfg
->tx
.gpio
, (GPIO_InitTypeDef
*)&usart_dev
->cfg
->tx
.init
);
256 /* Write new configuration */
257 { // fix parity stuff
258 USART_InitTypeDef init
= usart_dev
->init
;
260 if ((init
.USART_Parity
!= USART_Parity_No
) && (init
.USART_WordLength
== USART_WordLength_8b
)) {
261 init
.USART_WordLength
= USART_WordLength_9b
;
264 USART_Init(usart_dev
->cfg
->regs
, &init
);
270 USART_Cmd(usart_dev
->cfg
->regs
, ENABLE
);
273 static void PIOS_USART_RxStart(uint32_t usart_id
, __attribute__((unused
)) uint16_t rx_bytes_avail
)
275 const struct pios_usart_dev
*usart_dev
= PIOS_USART_validate(usart_id
);
277 USART_ITConfig(usart_dev
->cfg
->regs
, USART_IT_RXNE
, ENABLE
);
279 static void PIOS_USART_TxStart(uint32_t usart_id
, __attribute__((unused
)) uint16_t tx_bytes_avail
)
281 const struct pios_usart_dev
*usart_dev
= PIOS_USART_validate(usart_id
);
283 USART_ITConfig(usart_dev
->cfg
->regs
, USART_IT_TXE
, ENABLE
);
287 * Changes the baud rate of the USART peripheral without re-initialising.
288 * \param[in] usart_id USART name (GPS, TELEM, AUX)
289 * \param[in] baud Requested baud rate
291 static void PIOS_USART_ChangeBaud(uint32_t usart_id
, uint32_t baud
)
293 struct pios_usart_dev
*usart_dev
= PIOS_USART_validate(usart_id
);
295 /* Use our working copy of the usart init structure */
296 usart_dev
->init
.USART_BaudRate
= baud
;
298 PIOS_USART_Setup(usart_dev
);
302 * Changes configuration of the USART peripheral without re-initialising.
303 * \param[in] usart_id USART name (GPS, TELEM, AUX)
304 * \param[in] word_len Requested word length
305 * \param[in] stop_bits Requested stop bits
306 * \param[in] parity Requested parity
309 static void PIOS_USART_ChangeConfig(uint32_t usart_id
,
310 enum PIOS_COM_Word_Length word_len
,
311 enum PIOS_COM_Parity parity
,
312 enum PIOS_COM_StopBits stop_bits
,
315 struct pios_usart_dev
*usart_dev
= PIOS_USART_validate(usart_id
);
318 case PIOS_COM_Word_length_8b
:
319 usart_dev
->init
.USART_WordLength
= USART_WordLength_8b
;
321 case PIOS_COM_Word_length_9b
:
322 usart_dev
->init
.USART_WordLength
= USART_WordLength_9b
;
329 case PIOS_COM_StopBits_1
:
330 usart_dev
->init
.USART_StopBits
= USART_StopBits_1
;
332 case PIOS_COM_StopBits_1_5
:
333 usart_dev
->init
.USART_StopBits
= USART_StopBits_1_5
;
335 case PIOS_COM_StopBits_2
:
336 usart_dev
->init
.USART_StopBits
= USART_StopBits_2
;
343 case PIOS_COM_Parity_No
:
344 usart_dev
->init
.USART_Parity
= USART_Parity_No
;
346 case PIOS_COM_Parity_Even
:
347 usart_dev
->init
.USART_Parity
= USART_Parity_Even
;
349 case PIOS_COM_Parity_Odd
:
350 usart_dev
->init
.USART_Parity
= USART_Parity_Odd
;
357 usart_dev
->init
.USART_BaudRate
= baud_rate
;
360 PIOS_USART_Setup(usart_dev
);
362 #endif /* BOOTLOADER */
364 static void PIOS_USART_RegisterRxCallback(uint32_t usart_id
, pios_com_callback rx_in_cb
, uint32_t context
)
366 struct pios_usart_dev
*usart_dev
= PIOS_USART_validate(usart_id
);
369 * Order is important in these assignments since ISR uses _cb
370 * field to determine if it's ok to dereference _cb and _context
372 usart_dev
->rx_in_context
= context
;
373 usart_dev
->rx_in_cb
= rx_in_cb
;
375 usart_dev
->init
.USART_Mode
|= USART_Mode_Rx
;
377 PIOS_USART_Setup(usart_dev
);
380 static void PIOS_USART_RegisterTxCallback(uint32_t usart_id
, pios_com_callback tx_out_cb
, uint32_t context
)
382 struct pios_usart_dev
*usart_dev
= PIOS_USART_validate(usart_id
);
385 * Order is important in these assignments since ISR uses _cb
386 * field to determine if it's ok to dereference _cb and _context
388 usart_dev
->tx_out_context
= context
;
389 usart_dev
->tx_out_cb
= tx_out_cb
;
391 usart_dev
->init
.USART_Mode
|= USART_Mode_Tx
;
393 PIOS_USART_Setup(usart_dev
);
396 static void PIOS_USART_generic_irq_handler(uint32_t usart_id
)
398 struct pios_usart_dev
*usart_dev
= PIOS_USART_validate(usart_id
);
400 /* Force read of dr after sr to make sure to clear error flags */
403 /* Check if RXNE flag is set */
404 bool rx_need_yield
= false;
406 if (usart_dev
->cfg
->regs
->ISR
& USART_ISR_RXNE
) {
407 uint8_t byte
= usart_dev
->cfg
->regs
->RDR
& 0xFF;
408 if (usart_dev
->rx_in_cb
) {
410 rc
= (usart_dev
->rx_in_cb
)(usart_dev
->rx_in_context
, &byte
, 1, NULL
, &rx_need_yield
);
412 /* Lost bytes on rx */
413 usart_dev
->rx_dropped
+= 1;
418 /* Check if TXE flag is set */
419 bool tx_need_yield
= false;
420 if (usart_dev
->cfg
->regs
->ISR
& USART_ISR_TXE
) {
421 if (usart_dev
->tx_out_cb
) {
422 if (!usart_dev
->tx_len
) {
423 usart_dev
->tx_len
= (usart_dev
->tx_out_cb
)(usart_dev
->tx_out_context
, usart_dev
->tx_buffer
, PIOS_UART_TX_BUFFER
, NULL
, &tx_need_yield
);
424 usart_dev
->tx_pos
= 0;
426 if (usart_dev
->tx_len
> 0) {
427 /* Send the byte we've been given */
428 usart_dev
->cfg
->regs
->TDR
= usart_dev
->tx_buffer
[usart_dev
->tx_pos
++];
431 /* No bytes to send, disable TXE interrupt */
432 usart_dev
->cfg
->regs
->CR1
&= ~(USART_CR1_TXEIE
);
435 /* No bytes to send, disable TXE interrupt */
436 usart_dev
->cfg
->regs
->CR1
&= ~(USART_CR1_TXEIE
);
439 usart_dev
->cfg
->regs
->ICR
= USART_ICR_ORECF
| USART_ICR_TCCF
;
440 #if defined(PIOS_INCLUDE_FREERTOS)
441 if (rx_need_yield
|| tx_need_yield
) {
444 #endif /* PIOS_INCLUDE_FREERTOS */
447 #endif /* PIOS_INCLUDE_USART */