/** ****************************************************************************** * @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) 2010. * Parts by Thorsten Klose (tk@midibox.org) (tk@midibox.org) * @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 */ /* 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 int32_t PIOS_USART_TxBufferPutMoreNonBlocking(uint32_t usart_id, const uint8_t *buffer, uint16_t len); static int32_t PIOS_USART_TxBufferPutMore(uint32_t usart_id, const uint8_t *buffer, uint16_t len); static int32_t PIOS_USART_RxBufferGet(uint32_t usart_id); static int32_t PIOS_USART_RxBufferUsed(uint32_t usart_id); const struct pios_com_driver pios_usart_com_driver = { .set_baud = PIOS_USART_ChangeBaud, .tx_nb = PIOS_USART_TxBufferPutMoreNonBlocking, .tx = PIOS_USART_TxBufferPutMore, .rx = PIOS_USART_RxBufferGet, .rx_avail = PIOS_USART_RxBufferUsed, }; static bool PIOS_USART_validate(struct pios_usart_dev * usart_dev) { return (usart_dev->magic == PIOS_USART_DEV_MAGIC); } #if defined(PIOS_INCLUDE_FREERTOS) && 0 static struct pios_usart_dev * PIOS_USART_alloc(void) { struct pios_usart_dev * usart_dev; usart_dev = (struct pios_usart_dev *)malloc(sizeof(*usart_dev)); if (!usart_dev) return(NULL); 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 /** * 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; /* Clear buffer counters */ fifoBuf_init(&usart_dev->rx, usart_dev->rx_buffer, sizeof(usart_dev->rx_buffer)); fifoBuf_init(&usart_dev->tx, usart_dev->tx_buffer, sizeof(usart_dev->tx_buffer)); /* Enable the USART Pins Software Remapping */ if (usart_dev->cfg->remap) { GPIO_PinRemapConfig(usart_dev->cfg->remap, ENABLE); } /* Initialize the USART Rx and Tx pins */ GPIO_Init(usart_dev->cfg->rx.gpio, &usart_dev->cfg->rx.init); GPIO_Init(usart_dev->cfg->tx.gpio, &usart_dev->cfg->tx.init); /* Enable USART clock */ switch ((uint32_t)usart_dev->cfg->regs) { case (uint32_t)USART1: RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); break; case (uint32_t)USART2: RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE); break; case (uint32_t)USART3: RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE); break; } /* Configure the USART */ USART_Init(usart_dev->cfg->regs, &usart_dev->cfg->init); *usart_id = (uint32_t)usart_dev; /* Configure USART Interrupts */ NVIC_Init(&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); /* Enable USART */ USART_Cmd(usart_dev->cfg->regs, ENABLE); return(0); out_fail: return(-1); } /** * 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); } /** * Returns number of used bytes in receive buffer * \param[in] USART USART name * \return > 0: number of used bytes * \return 0 if USART not available */ static int32_t PIOS_USART_RxBufferUsed(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) return (fifoBuf_getUsed(&usart_dev->rx)); } /** * Gets a byte from the receive buffer * \param[in] USART USART name * \return -1 if no new byte available * \return >= 0: actual byte received */ static int32_t PIOS_USART_RxBufferGet(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) if (fifoBuf_getUsed(&usart_dev->rx) == 0) { /* Nothing new in the buffer */ return -1; } /* get byte - this operation should be atomic! */ uint8_t b = fifoBuf_getByte(&usart_dev->rx); /* Return received byte */ return b; } /** * puts a byte onto the receive buffer * \param[in] USART USART name * \param[in] b byte which should be put into Rx buffer * \return 0 if no error * \return -1 if buffer full (retry) */ static int32_t PIOS_USART_RxBufferPut(uint32_t usart_id, uint8_t b) { struct pios_usart_dev * usart_dev = (struct pios_usart_dev *)usart_id; bool valid = PIOS_USART_validate(usart_dev); PIOS_Assert(valid) if (fifoBuf_getFree(&usart_dev->rx) < 1) { /* Buffer full (retry) */ return -1; } /* Copy received byte into receive buffer */ /* This operation should be atomic! */ fifoBuf_putByte(&usart_dev->rx, b); /* No error */ return 0; } /** * returns number of used bytes in transmit buffer * \param[in] USART USART name * \return number of used bytes * \return 0 if USART not available */ static int32_t PIOS_USART_TxBufferUsed(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) return (fifoBuf_getUsed(&usart_dev->tx)); } /** * gets a byte from the transmit buffer * \param[in] USART USART name * \return -1 if no new byte available * \return >= 0: transmitted byte */ static int32_t PIOS_USART_TxBufferGet(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) if (fifoBuf_getUsed(&usart_dev->tx) == 0) { /* Nothing new in the buffer */ return -1; } /* get byte - this operation should be atomic! */ PIOS_IRQ_Disable(); uint8_t b = fifoBuf_getByte(&usart_dev->tx); PIOS_IRQ_Enable(); /* Return received byte */ return b; } /** * puts more than one byte onto the transmit buffer (used for atomic sends) * \param[in] USART USART name * \param[in] *buffer pointer to buffer to be sent * \param[in] len number of bytes to be sent * \return 0 if no error * \return -1 if buffer full or cannot get all requested bytes (retry) */ static int32_t PIOS_USART_TxBufferPutMoreNonBlocking(uint32_t usart_id, const uint8_t *buffer, uint16_t len) { struct pios_usart_dev * usart_dev = (struct pios_usart_dev *)usart_id; bool valid = PIOS_USART_validate(usart_dev); PIOS_Assert(valid) if (len >= fifoBuf_getFree(&usart_dev->tx)) { /* Buffer cannot accept all requested bytes (retry) */ return -1; } /* Copy bytes to be transmitted into transmit buffer */ /* This operation should be atomic! */ PIOS_IRQ_Disable(); uint16_t used = fifoBuf_getUsed(&usart_dev->tx); fifoBuf_putData(&usart_dev->tx,buffer,len); if (used == 0) { /* enable sending when buffer was previously empty */ USART_ITConfig(usart_dev->cfg->regs, USART_IT_TXE, ENABLE); } PIOS_IRQ_Enable(); /* No error */ return 0; } /** * puts more than one byte onto the transmit buffer (used for atomic sends)
* (blocking function) * \param[in] USART USART name * \param[in] *buffer pointer to buffer to be sent * \param[in] len number of bytes to be sent * \return 0 if no error */ static int32_t PIOS_USART_TxBufferPutMore(uint32_t usart_id, const uint8_t *buffer, uint16_t len) { int32_t rc; while ((rc = PIOS_USART_TxBufferPutMoreNonBlocking(usart_id, buffer, len)) == -1); return rc; } void PIOS_USART_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 */ if (sr & USART_SR_RXNE) { if (PIOS_USART_RxBufferPut(usart_id, dr) < 0) { /* Here we could add some error handling */ } } /* Check if TXE flag is set */ if (sr & USART_SR_TXE) { if (PIOS_USART_TxBufferUsed(usart_id) > 0) { int32_t b = PIOS_USART_TxBufferGet(usart_id); if (b < 0) { /* Here we could add some error handling */ usart_dev->cfg->regs->DR = 0xff; } else { usart_dev->cfg->regs->DR = b & 0xff; } } else { /* Disable TXE interrupt (TXEIE=0) */ USART_ITConfig(usart_dev->cfg->regs, USART_IT_TXE, DISABLE); } } } #endif /** * @} * @} */