/*
* serial.c
*
* Created on: 4 Jan 2016
* Author: Mike
*
* This (ab)uses the STMCubeMX HAL declarations to implement a generic STM32F1xx
* USART driver layer
*
* Note - this requires the UART interrupt to be globally enabled in STM32CubeMX,
* but not to generate IRQ handler or HAL driver for the UART.
*
*
*/
#include "libSerial/serial.h"
/* workspaces for the USARTS being used */
#if defined SERIAL_UART1
usart_ctl uc1;
#endif
#if defined SERIAL_UART2
usart_ctl uc2;
#endif
#if defined SERIAL_UART3
usart_ctl uc3;
#endif
#if defined SERIAL_UART4
usart_ctl uc4;
#endif
#if defined SERIAL_UART5
usart_ctl uc5;
#endif
uint16_t
SerialCharsReceived(usart_ctl *instance)
{
uint16_t result = 0; // assume no characters received yet
__HAL_UART_DISABLE_IT(instance->Handle, UART_IT_RXNE);
if (instance->rx_usart_buffer_full)
{ // buffer is full...
result = RX_USART_BUFF_SIZ;
}
else if (instance->rx_usart_in_Ptr >= instance->rx_usart_out_Ptr)
{ // buffer has not wrapped...
result = instance->rx_usart_in_Ptr - instance->rx_usart_out_Ptr;
}
else
{ // buffer has possibly wrapped...
result = RX_USART_BUFF_SIZ - instance->rx_usart_out_Ptr + instance->rx_usart_in_Ptr;
}
__HAL_UART_ENABLE_IT(instance->Handle, UART_IT_RXNE);
return result;
}
uint16_t SerialTransmitSpace(usart_ctl *instance)
{
__HAL_UART_DISABLE_IT(instance->Handle, UART_IT_TXE);
uint16_t result;
if (instance->tx_usart_in_Ptr == instance->tx_usart_out_Ptr)
{
result = instance->tx_usart_running ? 0 : TX_USART_BUFF_SIZ;
}
else if (instance->tx_usart_in_Ptr > instance->tx_usart_out_Ptr)
{ // buffer has not wrapped...
result = TX_USART_BUFF_SIZ - instance->tx_usart_in_Ptr - instance->tx_usart_out_Ptr;
}
else
{ // buffer has possibly wrapped...
result = TX_USART_BUFF_SIZ - instance->tx_usart_out_Ptr + instance->tx_usart_in_Ptr;
}
if (instance->tx_usart_running)
__HAL_UART_ENABLE_IT(instance->Handle, UART_IT_TXE);
return result;
}
inline uint8_t
PollSerial(usart_ctl *instance)
{
uint8_t rc;
__HAL_UART_DISABLE_IT(instance->Handle, UART_IT_RXNE);
rc = (instance->rx_usart_buffer_full || (instance->rx_usart_in_Ptr != instance->rx_usart_out_Ptr));
__HAL_UART_ENABLE_IT(instance->Handle, UART_IT_RXNE);
return rc;
}
inline uint8_t
GetCharSerial(usart_ctl *instance)
{
uint8_t c;
__HAL_UART_DISABLE_IT(instance->Handle, UART_IT_RXNE);
while (!instance->rx_usart_buffer_full && (instance->rx_usart_in_Ptr == instance->rx_usart_out_Ptr))
{
__HAL_UART_ENABLE_IT(instance->Handle, UART_IT_RXNE);
__WFI(); /* wait for something */
__HAL_UART_DISABLE_IT(instance->Handle, UART_IT_RXNE);
}
c = instance->rx_usart_buff[instance->rx_usart_out_Ptr];
instance->rx_usart_buffer_full = 0; /* removed character */
instance->rx_usart_out_Ptr++;
if (instance->rx_usart_out_Ptr >= RX_USART_BUFF_SIZ)
{
instance->rx_usart_out_Ptr = 0;
}
__HAL_UART_ENABLE_IT(instance->Handle, UART_IT_RXNE);
return c;
}
/*
* \brief
* void EnableSerialRxInterrupt(void) - this function is used from the interrupt handler and the main scheduler loop
* to enable the serial rx interrupt after resetting the serial rx buffer...
*/
inline void
EnableSerialRxInterrupt(usart_ctl *instance)
{
/* cheat here - this is a macro and I have the same Instance member as the HAL handle, with the same meaning */
__HAL_UART_ENABLE_IT(instance->Handle, UART_IT_RXNE);
}
/****!
* @brief send a character to the serial USART via placing it in the serial buffer
*
*/
static void
PutCharSerialFIFO(usart_ctl *instance, uint8_t c)
{
__HAL_UART_DISABLE_IT(instance->Handle, UART_IT_TXE);
instance->tx_usart_buff[instance->tx_usart_in_Ptr++] = c;
if (instance->tx_usart_in_Ptr >= TX_USART_BUFF_SIZ)
{
instance->tx_usart_in_Ptr = 0;
}
/* Handle overrun by losing oldest characters */
if (instance->tx_usart_in_Ptr == instance->tx_usart_out_Ptr)
{
instance->tx_usart_overruns++;
instance->tx_usart_out_Ptr++;
if (instance->tx_usart_out_Ptr >= TX_USART_BUFF_SIZ)
{
instance->tx_usart_out_Ptr = 0;
}
}
instance->tx_usart_running = 1;
__HAL_UART_ENABLE_IT(instance->Handle, UART_IT_TXE);
}
void UART_IRQHandler(usart_ctl *instance)
{
__disable_irq();
// status from USART receiver
uint32_t rxStatus = instance->Handle->Instance->SR; // read the status bits - this resets all the hardware signalling flags
if ((rxStatus & USART_SR_LBD))
__HAL_UART_CLEAR_FLAG(instance->Handle, USART_SR_LBD);
if ((rxStatus & USART_SR_RXNE) != RESET)
{
// no error has occurred...
uint8_t rxChar = (uint8_t)(instance->Handle->Instance->DR & 0xff); // read the bottom 8-bits only
if (!instance->rx_usart_buffer_full)
{
instance->rx_usart_buff[instance->rx_usart_in_Ptr++] = rxChar;
if (instance->rx_usart_in_Ptr >= RX_USART_BUFF_SIZ)
{
instance->rx_usart_in_Ptr = 0;
}
if (instance->rx_usart_in_Ptr == instance->rx_usart_out_Ptr)
{
instance->rx_usart_buffer_full = 1; /* buffer overrun */
}
}
}
/* check for transmitter interrupt : this code is used */
if ((rxStatus & USART_SR_TXE) != RESET)
{
/* Only enable the transmitter when baud detect has completed or check expired.
* and the software is ready for it to be enabled as programming mode is wanting
* to receive a response and that can get blocked if we're streaming a lot of debug messages*/
if (instance->tx_usart_in_Ptr != instance->tx_usart_out_Ptr)
{
instance->Handle->Instance->DR =
instance->tx_usart_buff[instance->tx_usart_out_Ptr++];
if (instance->tx_usart_out_Ptr >= TX_USART_BUFF_SIZ)
{
instance->tx_usart_out_Ptr = 0;
}
}
if (instance->tx_usart_in_Ptr == instance->tx_usart_out_Ptr)
{
__HAL_UART_DISABLE_IT(instance->Handle, UART_IT_TXE);
instance->tx_usart_running = 0;
}
}
__enable_irq();
}
void PutCharSerial(usart_ctl *instance, uint8_t c)
{
PutCharSerialFIFO(instance, c);
}
void ResetTxBuffer(usart_ctl *instance)
{
instance->tx_usart_out_Ptr = 0;
instance->tx_usart_running = 0;
instance->tx_usart_in_Ptr = 0; /* setup in pointer last to drop any chars come in */
instance->tx_usart_overruns = 0;
}
/*
* \brief
* void ResetRxBuffer(void) - resets the serial receiver buffer
*/
void ResetRxBuffer(usart_ctl *instance)
{
instance->rx_usart_out_Ptr = 0;
instance->rx_usart_buffer_full = 0;
instance->rx_usart_in_Ptr = 0; /* setup in pointer last to drop any chars come in */
}
/***!
* @brief Flush Serial input and output buffers
*/
void FlushSerial(usart_ctl *instance)
{
ResetRxBuffer(instance);
ResetTxBuffer(instance);
}
/***!
* @brief check if tx buffer is empty...
*/
uint8_t
TxBufferEmpty(usart_ctl *instance)
{
if (instance->tx_usart_running)
return 0;
return (instance->Handle->Instance->SR & USART_SR_TC) == 0;
}
/***!
* @brief wait for transmission to finish
*/
void TxWaitEmpty(usart_ctl *instance)
{
// no wait if not running
if (!instance->tx_usart_running)
return;
// wait for it to finish
while (instance->tx_usart_running ||
(instance->Handle->Instance->SR & USART_SR_TC) != RESET)
{
};
}
/****
* @brief Initialise control structure
*/
void init_usart_ctl(usart_ctl *instance, UART_HandleTypeDef *handle)
{
instance->Handle = handle;
/* cheat here - this is a macro and I have the same Instance member as the HAL handle, with the same meaning */
__HAL_UART_DISABLE_IT(instance->Handle, UART_IT_TXE);
__HAL_UART_DISABLE_IT(instance->Handle, UART_IT_RXNE);
FlushSerial(instance);
}
void setBaud(usart_ctl *ctl, uint32_t baud)
{
ctl->Handle->Init.BaudRate = baud;
__disable_irq();
HAL_UART_Init(ctl->Handle);
__enable_irq();
}
/////////////////////////////////////////////////////////
/// Moved from generated code to avoid crappy HAL handler
#if defined SERIAL_UART1
void USART1_IRQHandler(void)
{
UART_IRQHandler(&uc1);
}
#endif
#if defined SERIAL_UART2
void USART2_IRQHandler(void)
{
UART_IRQHandler(&uc2);
}
#endif
#if defined SERIAL_UART3
void USART3_IRQHandler(void)
{
UART_IRQHandler(&uc3);
}
#endif
#if defined SERIAL_UART4
void UART4_IRQHandler(void)
{
UART_IRQHandler(&uc4);
}
#endif
#if defined SERIAL_UART5
void UART5_IRQHandler(void)
{
UART_IRQHandler(&uc5);
}
#endif