/*
* serial.c
*
* Created on: 4 Jan 2016
* Author: Mike
*
* This (ab)uses the STMCubeMX HAL declarations to implement a generic STM32F1xx
* USART driver layer
*/
#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
/* returns the number of characters received by the Rx USART */
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)
{
return TX_USART_BUFF_SIZ- instance->tx_usart_count;
}
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;
}
/***!
* @brief return the next character in the Serial input buffer
* This function will wait until a character arrives.
*/
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;
instance->tx_usart_count += 1;
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_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 ();
uint32_t rxStatus; // status from USART receiver
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_count != 0)
instance->tx_usart_count -= 1;
if (instance->tx_usart_out_Ptr >= TX_USART_BUFF_SIZ)
{
instance->tx_usart_out_Ptr = 0;
}
}
if (instance->tx_usart_count == 0)
{
__HAL_UART_DISABLE_IT (instance->Handle, UART_IT_TXE);
instance->tx_usart_running = 0;
}
}
__enable_irq ();
}
void
PutCharSerial (usart_ctl *instance, uint8_t c)
{
// Put character in Crayon/Pen interface
PutCharSerialFIFO (instance, c);
}
/*
* \brief
* ResetTxBuffer(void) - resets the serial transmitter buffer
*/
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_count = 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)
{
return (0 == instance->tx_usart_count );
}
/***!
* @brief wait for transmission to finish
*/
void TxWaitEmpty(usart_ctl *instance)
{
while (instance->tx_usart_count ||
(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);
instance->tx_usart_in_Ptr = 0;
instance->tx_usart_out_Ptr = 0;
instance->tx_usart_running = 0;
instance->tx_usart_count = 0;
instance->rx_usart_in_Ptr = 0;
instance->rx_usart_out_Ptr = 0;
instance->rx_usart_buffer_full = 0;
}
void
setBaud (usart_ctl *ctl, uint32_t baud)
{
ctl->Handle->Init.BaudRate = baud;
__disable_irq ();
HAL_UART_Init (ctl->Handle);
__enable_irq ();
}
void sendString(usart_ctl *ctl, char *string, int length)
{
int i;
for (i = 0; i < length; i++)
PutCharSerial(ctl, string[i]);
}
/////////////////////////////////////////////////////////
/// 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