// ----------------------------------------------------------------------------- /// @file uart.c /// @brief Description // ----------------------------------------------------------------------------- // Micro-Key bv // Industrieweg 28, 9804 TG Noordhorn // Postbus 92, 9800 AB Zuidhorn // The Netherlands // Tel: +31 594 503020 // Fax: +31 594 505825 // Email: support@microkey.nl // Web: www.microkey.nl // ----------------------------------------------------------------------------- /// $Revision$ /// $Author$ /// $Date$ // (c) 2017 Micro-Key bv // ----------------------------------------------------------------------------- /// @file uart.c /// @ingroup {group_name} // ----------------------------------------------------------------------------- // Include files // ----------------------------------------------------------------------------- #include "FreeRTOS.h" #include "semphr.h" #include "stm32f10x_usart.h" #include "uart.h" #include "misc.h" // ----------------------------------------------------------------------------- // Constant and macro definitions // ----------------------------------------------------------------------------- // ----------------------------------------------------------------------------- // Type definitions // ----------------------------------------------------------------------------- // ----------------------------------------------------------------------------- // File-scope variables // ----------------------------------------------------------------------------- // ----------------------------------------------------------------------------- // Function declarations // ----------------------------------------------------------------------------- static ErrorStatus write(const struct IODevice* self, const char* buffer, size_t length); static ErrorStatus read(const struct IODevice* self, char* buffer, size_t length, size_t* actualLength); // ----------------------------------------------------------------------------- // Function definitions // ----------------------------------------------------------------------------- ErrorStatus Uart_construct(struct Uart* self, struct UartParameters* parameters) { ErrorStatus returnValue = SUCCESS; if(!self->initialized) { IODevice_construct(&self->device, read, write); //! Create semaphore to synchronize with USART interrupt handler vSemaphoreCreateBinary(self->txSemaphore); USART_DeInit(self->USART_TypeDef); // Initialise the UART self->USART_InitStruct.USART_BaudRate = parameters->baudrate; self->USART_InitStruct.USART_WordLength = parameters->wordlength; self->USART_InitStruct.USART_StopBits = parameters->stopbits; self->USART_InitStruct.USART_Parity = parameters->parity; self->USART_InitStruct.USART_Mode = parameters->mode; self->USART_InitStruct.USART_HardwareFlowControl = parameters->hwFlowControl; USART_Init(self->USART_TypeDef, &self->USART_InitStruct); //! Enable USART interface USART_Cmd(self->USART_TypeDef, ENABLE); //! Create a new FREERTOS queue to handle data from app to USART output self->txQueue = xQueueCreate(parameters->txQueueSize, sizeof(struct usartQueueItem)); //! Create a new FREERTOS queue to handle data from USART input to app self->rxQueue = xQueueCreate(parameters->rxQueueSize, sizeof(struct usartQueueItem)); //! Queue identifier must not be 0 (0 means that the queue is not available) if (self->txQueue == 0) { //! Queue identifier is 0 -> error returnValue = ERROR; //! Set error flag } if (self->rxQueue == 0) { //! Queue identifier is 0 -> error returnValue = ERROR; //! Set error flag } //! Queue identifier is not 0 -> queue is available //! take txSemaphore if (xSemaphoreTake(self->txSemaphore, 0) == pdFALSE) { //! An error has occurred returnValue = ERROR; } if (returnValue == SUCCESS) { //! Enable the UART RX not empty interrupt USART_ITConfig(self->USART_TypeDef, USART_IT_RXNE, ENABLE); self->initialized = true; } } else { returnValue = ERROR; } return returnValue; } ErrorStatus Uart_getDefaultParameters(struct UartParameters* parameters) { ErrorStatus returnValue = SUCCESS; parameters->baudrate = UART_DEF_BAUDRATE; parameters->wordlength = UART_DEF_WORDLENGTH; parameters->stopbits = UART_DEF_STOPBITS; parameters->mode = UART_DEF_MODE; parameters->parity = UART_DEF_PARITY; parameters->hwFlowControl = UART_DEF_HW_FLOW_CONTROL; parameters->txQueueSize = UART_DEF_TX_QUEUE; parameters->rxQueueSize = UART_DEF_RX_QUEUE; return returnValue; } static ErrorStatus write(const struct IODevice* self, const char* buffer, size_t length) { return Uart_write((struct Uart*)self, buffer, length); } static ErrorStatus read(const struct IODevice* self, char* buffer, size_t length, size_t* actualLength) { return Uart_read((struct Uart*)self, buffer, length, actualLength); } ErrorStatus Uart_write(struct Uart* self, const char* buffer, int length) { struct usartQueueItem usartTxItem; ErrorStatus returnValue = SUCCESS; //! Define return variable int txCounter; //! Define a loop counter var if (self->initialized) { //! Copy the incoming data into UART data structure for (txCounter = 0; txCounter < length; txCounter++) { usartTxItem.byte = buffer[txCounter]; //! Copy current data in struct if (uxQueueSpacesAvailable(self->txQueue) == 2) { USART_ITConfig(self->USART_TypeDef, USART_IT_TXE, ENABLE); } //! Add the current set of data to UART transmission queue if (pdTRUE != xQueueSend(self->txQueue, &usartTxItem, portMAX_DELAY)) { //! Adding item was NOT successful - break out of loop returnValue = ERROR; //! Set return value to FALSE break; } } if (returnValue == SUCCESS) { //! Semaphore has been taken //! Enable the USARTx TXE (transmission empty) interrupt USART_ITConfig(self->USART_TypeDef, USART_IT_TXE, ENABLE); //! Try to take Semaphore - If the USART transmission is still busy, the //! Semaphore cannot be taken - FREERTOS will suspend this task until the //! Semaphore is released again xSemaphoreTake(self->txSemaphore, portMAX_DELAY); /** Enabling the TX interrupt will immediately cause an interrupt because * the transmission register is still empty. The ISR will get the data * from the uart transmission queue and transmit byte-wise until the * queue is empty. * An empty queue will cause the transmission complete flag (TC) to be set, * which is polled */ while (USART_GetFlagStatus(self->USART_TypeDef, USART_FLAG_TC) == RESET) { //! The software must wait until TC=1. The TC flag remains cleared during //! all data transfers and it is set by hardware at the last frame's //! end of transmission } } else { //! Do nothing } } else { returnValue = ERROR; } return (returnValue); //! Return result to caller } ErrorStatus Uart_read (struct Uart* self, char* buffer, size_t length, size_t* actualLength) { ErrorStatus returnValue = SUCCESS; int loopCounter = 0; *actualLength = 0; struct usartQueueItem usartRxItem; if (self->initialized) { for (loopCounter = 0; loopCounter < length; loopCounter++) { if (xQueueReceive(self->rxQueue, &usartRxItem, 0) != pdFALSE) { // Item successfully fetched from Queue buffer[loopCounter] = usartRxItem.byte; *actualLength = *actualLength + 1; } else { break; } } } else { returnValue = ERROR; } return returnValue; } /** ---------------------------------------------------------------------------- * @brief Function: USART1_IRQHandler * * Dedicated Interrupt Service Routine for USART1 * * @return void * * @todo * ----------------------------------------------------------------------------- */ void USART1_IRQHandler(void) { signed portBASE_TYPE higherPriorityTaskWoken = pdFALSE; //! Transmission register empty interrupt if(USART_GetITStatus(USART1, USART_IT_TXE) != RESET) { //! Receive element from usart transmission queue struct usartQueueItem usartTxItem; xQueueReceiveFromISR(uart1->txQueue, &usartTxItem, &higherPriorityTaskWoken); //! Write one byte to the transmit data register USART_SendData(USART1, usartTxItem.byte); //! check if queue is empty -> all bytes transmit if(pdTRUE == xQueueIsQueueEmptyFromISR(uart1->txQueue)) { //! Disable the COMPORT Transmit interrupt and release semaphore USART_ITConfig(USART1, USART_IT_TXE, DISABLE); xSemaphoreGiveFromISR(uart1->txSemaphore, &higherPriorityTaskWoken); } } //! Current interrupt is triggered by USART_RXNE (receive register not empty) if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) { //! Read one byte from the receive data register struct usartQueueItem usartRxItem; //! Reading from reception register automatically clears the RXNE interrupt usartRxItem.byte = USART_ReceiveData(USART1); //! Add the byte to the USART RX queue //! In case of a full queue, the data is dumped (void)xQueueSendFromISR(uart1->rxQueue, &usartRxItem, &higherPriorityTaskWoken); } portEND_SWITCHING_ISR(higherPriorityTaskWoken); } /** ---------------------------------------------------------------------------- * @brief Function: USART3_IRQHandler * * Dedicated Interrupt Service Routine for USART3 * * @return void * * @todo * ----------------------------------------------------------------------------- */ void USART3_IRQHandler(void) { signed portBASE_TYPE higherPriorityTaskWoken = pdFALSE; //! Transmission register empty interrupt if(USART_GetITStatus(USART3, USART_IT_TXE) != RESET) { //! Receive element from usart transmission queue struct usartQueueItem usartTxItem; xQueueReceiveFromISR(uart3->txQueue, &usartTxItem, &higherPriorityTaskWoken); //! Write one byte to the transmit data register USART_SendData(USART3, usartTxItem.byte); //! check if queue is empty -> all bytes transmit if(pdTRUE == xQueueIsQueueEmptyFromISR(uart3->txQueue)) { //! Disable the COMPORT Transmit interrupt and release semaphore USART_ITConfig(USART3, USART_IT_TXE, DISABLE); xSemaphoreGiveFromISR(uart3->txSemaphore, &higherPriorityTaskWoken); } } //! Current interrupt is triggered by USART_RXNE (receive register not empty) if(USART_GetITStatus(USART3, USART_IT_RXNE) != RESET) { //! Read one byte from the receive data register struct usartQueueItem usartRxItem; //! Reading from reception register automatically clears the RXNE interrupt usartRxItem.byte = (char)USART_ReceiveData(USART3); //! Add the byte to the USART RX queue //! In case of a full queue, the data is dumped (void)xQueueSendFromISR(uart3->rxQueue, &usartRxItem, &higherPriorityTaskWoken); } portEND_SWITCHING_ISR(higherPriorityTaskWoken); }