/** ****************************************************************************** * @addtogroup PIOS PIOS Core hardware abstraction layer * @{ * @addtogroup PIOS_USART USART Functions * @brief PIOS interface for USART port * @{ * * @file pios_usart.c * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2012. * @brief USART commands. Inits USARTs, controls USARTs & Interupt handlers. (STM32 dependent) * @see The GNU Public License (GPL) Version 3 * *****************************************************************************/ /* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * @todo This is virtually identical to the F1xx driver and should be merged. */ /* Project Includes */ #include "pios.h" #if defined(PIOS_INCLUDE_USART) #include /* Provide a COM driver */ static void PIOS_USART_ChangeBaud(uint32_t usart_id, uint32_t baud); static void PIOS_USART_RegisterRxCallback(uint32_t usart_id, pios_com_callback rx_in_cb, uint32_t context); static void PIOS_USART_RegisterTxCallback(uint32_t usart_id, pios_com_callback tx_out_cb, uint32_t context); static void PIOS_USART_TxStart(uint32_t usart_id, uint16_t tx_bytes_avail); static void PIOS_USART_RxStart(uint32_t usart_id, uint16_t rx_bytes_avail); const struct pios_com_driver pios_usart_com_driver = { .set_baud = PIOS_USART_ChangeBaud, .tx_start = PIOS_USART_TxStart, .rx_start = PIOS_USART_RxStart, .bind_tx_cb = PIOS_USART_RegisterTxCallback, .bind_rx_cb = PIOS_USART_RegisterRxCallback, }; enum pios_usart_dev_magic { PIOS_USART_DEV_MAGIC = 0x4152834A, }; struct pios_usart_dev { enum pios_usart_dev_magic magic; const struct pios_usart_cfg * cfg; pios_com_callback rx_in_cb; uint32_t rx_in_context; pios_com_callback tx_out_cb; uint32_t tx_out_context; }; static bool PIOS_USART_validate(struct pios_usart_dev * usart_dev) { return (usart_dev->magic == PIOS_USART_DEV_MAGIC); } #if defined(PIOS_INCLUDE_FREERTOS) static struct pios_usart_dev * PIOS_USART_alloc(void) { struct pios_usart_dev * usart_dev; usart_dev = (struct pios_usart_dev *)pvPortMalloc(sizeof(*usart_dev)); if (!usart_dev) return(NULL); usart_dev->rx_in_cb = 0; usart_dev->rx_in_context = 0; usart_dev->tx_out_cb = 0; usart_dev->tx_out_context = 0; usart_dev->magic = PIOS_USART_DEV_MAGIC; return(usart_dev); } #else static struct pios_usart_dev pios_usart_devs[PIOS_USART_MAX_DEVS]; static uint8_t pios_usart_num_devs; static struct pios_usart_dev * PIOS_USART_alloc(void) { struct pios_usart_dev * usart_dev; if (pios_usart_num_devs >= PIOS_USART_MAX_DEVS) { return (NULL); } usart_dev = &pios_usart_devs[pios_usart_num_devs++]; usart_dev->magic = PIOS_USART_DEV_MAGIC; return (usart_dev); } #endif /* Bind Interrupt Handlers * * Map all valid USART IRQs to the common interrupt handler * and provide storage for a 32-bit device id IRQ to map * each physical IRQ to a specific registered device instance. */ static void PIOS_USART_generic_irq_handler(uint32_t usart_id); static uint32_t PIOS_USART_1_id; void USART1_IRQHandler(void) __attribute__ ((alias ("PIOS_USART_1_irq_handler"))); static void PIOS_USART_1_irq_handler (void) { PIOS_USART_generic_irq_handler (PIOS_USART_1_id); } static uint32_t PIOS_USART_2_id; void USART2_IRQHandler(void) __attribute__ ((alias ("PIOS_USART_2_irq_handler"))); static void PIOS_USART_2_irq_handler (void) { PIOS_USART_generic_irq_handler (PIOS_USART_2_id); } static uint32_t PIOS_USART_3_id; void USART3_IRQHandler(void) __attribute__ ((alias ("PIOS_USART_3_irq_handler"))); static void PIOS_USART_3_irq_handler (void) { PIOS_USART_generic_irq_handler (PIOS_USART_3_id); } static uint32_t PIOS_USART_4_id; void USART4_IRQHandler(void) __attribute__ ((alias ("PIOS_USART_4_irq_handler"))); static void PIOS_USART_4_irq_handler (void) { PIOS_USART_generic_irq_handler (PIOS_USART_4_id); } static uint32_t PIOS_USART_5_id; void USART5_IRQHandler(void) __attribute__ ((alias ("PIOS_USART_5_irq_handler"))); static void PIOS_USART_5_irq_handler (void) { PIOS_USART_generic_irq_handler (PIOS_USART_5_id); } static uint32_t PIOS_USART_6_id; void USART6_IRQHandler(void) __attribute__ ((alias ("PIOS_USART_6_irq_handler"))); static void PIOS_USART_6_irq_handler (void) { PIOS_USART_generic_irq_handler (PIOS_USART_6_id); } /** * Initialise a single USART device */ int32_t PIOS_USART_Init(uint32_t * usart_id, const struct pios_usart_cfg * cfg) { PIOS_DEBUG_Assert(usart_id); PIOS_DEBUG_Assert(cfg); struct pios_usart_dev * usart_dev; usart_dev = (struct pios_usart_dev *) PIOS_USART_alloc(); if (!usart_dev) goto out_fail; /* Bind the configuration to the device instance */ usart_dev->cfg = cfg; /* Map pins to USART function */ /* note __builtin_ctz() due to the difference between GPIO_PinX and GPIO_PinSourceX */ if (usart_dev->cfg->remap) { GPIO_PinAFConfig(usart_dev->cfg->rx.gpio, __builtin_ctz(usart_dev->cfg->rx.init.GPIO_Pin), usart_dev->cfg->remap); GPIO_PinAFConfig(usart_dev->cfg->tx.gpio, __builtin_ctz(usart_dev->cfg->tx.init.GPIO_Pin), usart_dev->cfg->remap); } /* Initialize the USART Rx and Tx pins */ GPIO_Init(usart_dev->cfg->rx.gpio, (GPIO_InitTypeDef *)&usart_dev->cfg->rx.init); GPIO_Init(usart_dev->cfg->tx.gpio, (GPIO_InitTypeDef *)&usart_dev->cfg->tx.init); /* Configure the USART */ USART_Init(usart_dev->cfg->regs, (USART_InitTypeDef *)&usart_dev->cfg->init); *usart_id = (uint32_t)usart_dev; /* Configure USART Interrupts */ switch ((uint32_t)usart_dev->cfg->regs) { case (uint32_t)USART1: PIOS_USART_1_id = (uint32_t)usart_dev; break; case (uint32_t)USART2: PIOS_USART_2_id = (uint32_t)usart_dev; break; case (uint32_t)USART3: PIOS_USART_3_id = (uint32_t)usart_dev; break; case (uint32_t)UART4: PIOS_USART_4_id = (uint32_t)usart_dev; break; case (uint32_t)UART5: PIOS_USART_5_id = (uint32_t)usart_dev; break; case (uint32_t)USART6: PIOS_USART_6_id = (uint32_t)usart_dev; break; } NVIC_Init((NVIC_InitTypeDef *)&(usart_dev->cfg->irq.init)); USART_ITConfig(usart_dev->cfg->regs, USART_IT_RXNE, ENABLE); USART_ITConfig(usart_dev->cfg->regs, USART_IT_TXE, ENABLE); // FIXME XXX Clear / reset uart here - sends NUL char else /* Enable USART */ USART_Cmd(usart_dev->cfg->regs, ENABLE); return(0); out_fail: return(-1); } static void PIOS_USART_RxStart(uint32_t usart_id, uint16_t rx_bytes_avail) { struct pios_usart_dev * usart_dev = (struct pios_usart_dev *)usart_id; bool valid = PIOS_USART_validate(usart_dev); PIOS_Assert(valid); USART_ITConfig(usart_dev->cfg->regs, USART_IT_RXNE, ENABLE); } static void PIOS_USART_TxStart(uint32_t usart_id, uint16_t tx_bytes_avail) { struct pios_usart_dev * usart_dev = (struct pios_usart_dev *)usart_id; bool valid = PIOS_USART_validate(usart_dev); PIOS_Assert(valid); USART_ITConfig(usart_dev->cfg->regs, USART_IT_TXE, ENABLE); } /** * Changes the baud rate of the USART peripheral without re-initialising. * \param[in] usart_id USART name (GPS, TELEM, AUX) * \param[in] baud Requested baud rate */ static void PIOS_USART_ChangeBaud(uint32_t usart_id, uint32_t baud) { struct pios_usart_dev * usart_dev = (struct pios_usart_dev *)usart_id; bool valid = PIOS_USART_validate(usart_dev); PIOS_Assert(valid); USART_InitTypeDef USART_InitStructure; /* Start with a copy of the default configuration for the peripheral */ USART_InitStructure = usart_dev->cfg->init; /* Adjust the baud rate */ USART_InitStructure.USART_BaudRate = baud; /* Write back the new configuration */ USART_Init(usart_dev->cfg->regs, &USART_InitStructure); } static void PIOS_USART_RegisterRxCallback(uint32_t usart_id, pios_com_callback rx_in_cb, uint32_t context) { struct pios_usart_dev * usart_dev = (struct pios_usart_dev *)usart_id; bool valid = PIOS_USART_validate(usart_dev); PIOS_Assert(valid); /* * Order is important in these assignments since ISR uses _cb * field to determine if it's ok to dereference _cb and _context */ usart_dev->rx_in_context = context; usart_dev->rx_in_cb = rx_in_cb; } static void PIOS_USART_RegisterTxCallback(uint32_t usart_id, pios_com_callback tx_out_cb, uint32_t context) { struct pios_usart_dev * usart_dev = (struct pios_usart_dev *)usart_id; bool valid = PIOS_USART_validate(usart_dev); PIOS_Assert(valid); /* * Order is important in these assignments since ISR uses _cb * field to determine if it's ok to dereference _cb and _context */ usart_dev->tx_out_context = context; usart_dev->tx_out_cb = tx_out_cb; } static void PIOS_USART_generic_irq_handler(uint32_t usart_id) { struct pios_usart_dev * usart_dev = (struct pios_usart_dev *)usart_id; bool valid = PIOS_USART_validate(usart_dev); PIOS_Assert(valid); /* Force read of dr after sr to make sure to clear error flags */ volatile uint16_t sr = usart_dev->cfg->regs->SR; volatile uint8_t dr = usart_dev->cfg->regs->DR; /* Check if RXNE flag is set */ bool rx_need_yield = false; if (sr & USART_SR_RXNE) { uint8_t byte = dr; if (usart_dev->rx_in_cb) { (void) (usart_dev->rx_in_cb)(usart_dev->rx_in_context, &byte, 1, NULL, &rx_need_yield); } } /* Check if TXE flag is set */ bool tx_need_yield = false; if (sr & USART_SR_TXE) { if (usart_dev->tx_out_cb) { uint8_t b; uint16_t bytes_to_send; bytes_to_send = (usart_dev->tx_out_cb)(usart_dev->tx_out_context, &b, 1, NULL, &tx_need_yield); if (bytes_to_send > 0) { /* Send the byte we've been given */ usart_dev->cfg->regs->DR = b; } else { /* No bytes to send, disable TXE interrupt */ USART_ITConfig(usart_dev->cfg->regs, USART_IT_TXE, DISABLE); } } else { /* No bytes to send, disable TXE interrupt */ USART_ITConfig(usart_dev->cfg->regs, USART_IT_TXE, DISABLE); } } #if defined(PIOS_INCLUDE_FREERTOS) if (rx_need_yield || tx_need_yield) { vPortYieldFromISR(); } #endif /* PIOS_INCLUDE_FREERTOS */ } #endif /** * @} * @} */