260 lines
6.3 KiB
C
260 lines
6.3 KiB
C
|
/** @file hal/micro/cortexm3/uart.c
|
||
|
* @brief STM32W uart drivers, supporting IAR's standard library
|
||
|
* IO routines.
|
||
|
*
|
||
|
* <!--(C) COPYRIGHT 2010 STMicroelectronics. All rights reserved. -->
|
||
|
*/
|
||
|
|
||
|
#include PLATFORM_HEADER
|
||
|
#include "hal/micro/micro-common.h"
|
||
|
#include "hal/micro/cortexm3/micro-common.h"
|
||
|
#include "uart.h"
|
||
|
#ifdef __GNUC__
|
||
|
#include <sys/stat.h>
|
||
|
#include <stdio.h>
|
||
|
#define _LLIO_STDIN ((int) stdin)
|
||
|
#define _LLIO_STDOUT ((int) stdout)
|
||
|
#define _LLIO_STDERR ((int) stderr)
|
||
|
#define _LLIO_ERROR (-1)
|
||
|
#define __write _write
|
||
|
#define __read _read
|
||
|
#undef putchar
|
||
|
void __io_putchar( char c );
|
||
|
int putchar (int c)
|
||
|
{
|
||
|
__io_putchar((char) c);
|
||
|
return c;
|
||
|
}
|
||
|
#endif
|
||
|
#define RECEIVE_QUEUE_SIZE (128)
|
||
|
|
||
|
int8u rxQ[RECEIVE_QUEUE_SIZE];
|
||
|
int16u rxHead;
|
||
|
int16u rxTail;
|
||
|
int16u rxUsed;
|
||
|
|
||
|
//////////////////////////////////////////////////////////////////////////////
|
||
|
// Initialization
|
||
|
|
||
|
void uartInit(int32u baudrate, int8u databits, SerialParity parity, int8u stopbits)
|
||
|
{
|
||
|
int32u tempcfg;
|
||
|
int32u tmp;
|
||
|
|
||
|
assert( (baudrate >= 300) && (baudrate <=921600) );
|
||
|
|
||
|
tmp = (2*12000000L + baudrate/2) / baudrate;
|
||
|
SC1_UARTFRAC = tmp & 1;
|
||
|
SC1_UARTPER = tmp / 2;
|
||
|
|
||
|
if(databits == 7) {
|
||
|
tempcfg = 0;
|
||
|
} else {
|
||
|
tempcfg = SC_UART8BIT;
|
||
|
}
|
||
|
|
||
|
if (parity == PARITY_ODD) {
|
||
|
tempcfg |= SC_UARTPAR | SC_UARTODD;
|
||
|
} else if( parity == PARITY_EVEN ) {
|
||
|
tempcfg |= SC_UARTPAR;
|
||
|
}
|
||
|
|
||
|
if ((stopbits & 0x0F) >= 2) {
|
||
|
tempcfg |= SC_UART2STP;
|
||
|
}
|
||
|
SC1_UARTCFG = tempcfg;
|
||
|
|
||
|
SC1_MODE = SC1_MODE_UART;
|
||
|
|
||
|
rxHead=0;
|
||
|
rxTail=0;
|
||
|
rxUsed=0;
|
||
|
|
||
|
halGpioConfig(PORTB_PIN(1),GPIOCFG_OUT_ALT);
|
||
|
halGpioConfig(PORTB_PIN(2),GPIOCFG_IN);
|
||
|
|
||
|
// Make the RX Valid interrupt level sensitive (instead of edge)
|
||
|
SC1_INTMODE = SC_RXVALLEVEL;
|
||
|
// Enable just RX interrupts; TX interrupts are controlled separately
|
||
|
INT_SC1CFG |= (INT_SCRXVAL |
|
||
|
INT_SCRXOVF |
|
||
|
INT_SC1FRMERR |
|
||
|
INT_SC1PARERR);
|
||
|
INT_SC1FLAG = 0xFFFF; // Clear any stale interrupts
|
||
|
INT_CFGSET = INT_SC1;
|
||
|
}
|
||
|
|
||
|
//////////////////////////////////////////////////////////////////////////////
|
||
|
// Transmit
|
||
|
|
||
|
// IAR Standard library hook for serial output
|
||
|
size_t __write(int handle, const unsigned char * buffer, size_t size)
|
||
|
{
|
||
|
size_t nChars = 0;
|
||
|
|
||
|
/* This template only writes to "standard out" and "standard err",
|
||
|
* for all other file handles it returns failure. */
|
||
|
if (handle != _LLIO_STDOUT && handle != _LLIO_STDERR) {
|
||
|
return _LLIO_ERROR;
|
||
|
}
|
||
|
|
||
|
if (buffer == 0) {
|
||
|
// This means that we should flush internal buffers.
|
||
|
//spin until TX complete (TX is idle)
|
||
|
while ((SC1_UARTSTAT&SC_UARTTXIDLE)!=SC_UARTTXIDLE) {}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
// ensure port is configured for UART
|
||
|
if(SC1_MODE != SC1_MODE_UART) {
|
||
|
return _LLIO_ERROR;
|
||
|
}
|
||
|
|
||
|
while(size--) {
|
||
|
//spin until data register has room for more data
|
||
|
while ((SC1_UARTSTAT&SC_UARTTXFREE)!=SC_UARTTXFREE) {}
|
||
|
SC1_DATA = *buffer;
|
||
|
buffer++;
|
||
|
|
||
|
++nChars;
|
||
|
}
|
||
|
|
||
|
return nChars;
|
||
|
}
|
||
|
#ifdef __GNUC__
|
||
|
int fflush (FILE *f)
|
||
|
#endif
|
||
|
#ifdef __ICCARM__
|
||
|
size_t fflush(int handle)
|
||
|
#endif
|
||
|
{
|
||
|
return __write(_LLIO_STDOUT, NULL, 0);
|
||
|
}
|
||
|
|
||
|
static void halInternalUart1TxIsr(void)
|
||
|
{
|
||
|
// Nothing for now, as ouput is blocking from the __write function
|
||
|
}
|
||
|
|
||
|
|
||
|
//////////////////////////////////////////////////////////////////////////////
|
||
|
// Receive
|
||
|
|
||
|
// IAR Standard library hook for serial input
|
||
|
size_t __read(int handle, unsigned char * buffer, size_t size)
|
||
|
{
|
||
|
int nChars = 0;
|
||
|
|
||
|
/* This template only reads from "standard in", for all other file
|
||
|
* handles it returns failure. */
|
||
|
if (handle != _LLIO_STDIN)
|
||
|
{
|
||
|
return _LLIO_ERROR;
|
||
|
}
|
||
|
|
||
|
for(nChars = 0; (rxUsed>0) && (nChars < size); nChars++) {
|
||
|
ATOMIC(
|
||
|
*buffer++ = rxQ[rxTail];
|
||
|
rxTail = (rxTail+1) % RECEIVE_QUEUE_SIZE;
|
||
|
rxUsed--;
|
||
|
)
|
||
|
}
|
||
|
|
||
|
return nChars;
|
||
|
}
|
||
|
|
||
|
static void halInternalUart1RxIsr(void)
|
||
|
{
|
||
|
|
||
|
// At present we really don't care which interrupt(s)
|
||
|
// occurred, just that one did. Loop reading RXVALID
|
||
|
// data, processing any errors noted
|
||
|
// along the way.
|
||
|
while ( SC1_UARTSTAT & SC_UARTRXVAL ) {
|
||
|
int8u errors = SC1_UARTSTAT & (SC_UARTFRMERR |
|
||
|
SC_UARTRXOVF |
|
||
|
SC_UARTPARERR );
|
||
|
int8u incoming = (int8u) SC1_DATA;
|
||
|
|
||
|
if ( (errors == 0) && (rxUsed < (RECEIVE_QUEUE_SIZE-1)) ) {
|
||
|
rxQ[rxHead] = incoming;
|
||
|
rxHead = (rxHead+1) % RECEIVE_QUEUE_SIZE;
|
||
|
rxUsed++;
|
||
|
} else {
|
||
|
// IAR standard library doesn't allow for any error handling in the
|
||
|
// case of rx errors or not having space in the receive queue, so the
|
||
|
// errors are just silently dropped.
|
||
|
}
|
||
|
} // end of while ( SC1_UARTSTAT & SC1_UARTRXVAL )
|
||
|
}
|
||
|
|
||
|
|
||
|
//////////////////////////////////////////////////////////////////////////////
|
||
|
// Interrupts
|
||
|
|
||
|
void halSc1Isr(void)
|
||
|
{
|
||
|
int32u interrupt;
|
||
|
|
||
|
//this read and mask is performed in two steps otherwise the compiler
|
||
|
//will complain about undefined order of volatile access
|
||
|
interrupt = INT_SC1FLAG;
|
||
|
interrupt &= INT_SC1CFG;
|
||
|
|
||
|
while (interrupt != 0) {
|
||
|
|
||
|
INT_SC1FLAG = interrupt; // acknowledge the interrupts early
|
||
|
|
||
|
// RX events
|
||
|
if ( interrupt & (INT_SCRXVAL | // RX has data
|
||
|
INT_SCRXOVF | // RX Overrun error
|
||
|
INT_SC1FRMERR | // RX Frame error
|
||
|
INT_SC1PARERR ) // RX Parity error
|
||
|
) {
|
||
|
halInternalUart1RxIsr();
|
||
|
}
|
||
|
|
||
|
// TX events
|
||
|
if ( interrupt & (INT_SCTXFREE | // TX has room
|
||
|
INT_SCTXIDLE ) // TX idle (more room)
|
||
|
) {
|
||
|
halInternalUart1TxIsr();
|
||
|
}
|
||
|
|
||
|
interrupt = INT_SC1FLAG;
|
||
|
interrupt &= INT_SC1CFG;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*******************************************************************************
|
||
|
* Function Name : __io_getcharNonBlocking
|
||
|
* Description : Non blocking read
|
||
|
* Input : none
|
||
|
* Output : dataByte: buffer containing the read byte if any
|
||
|
* Return : TRUE if there is a data, FALSE otherwise
|
||
|
*******************************************************************************/
|
||
|
boolean __io_getcharNonBlocking(int8u *data)
|
||
|
{
|
||
|
if (__read(_LLIO_STDIN,data,1))
|
||
|
return TRUE;
|
||
|
else
|
||
|
return FALSE;
|
||
|
}/* end serialReadByte() */
|
||
|
|
||
|
void __io_putchar( char c )
|
||
|
{
|
||
|
__write(_LLIO_STDOUT, (unsigned char *)&c, 1);
|
||
|
}
|
||
|
|
||
|
int __io_getchar()
|
||
|
{
|
||
|
unsigned char c;
|
||
|
__read(_LLIO_STDIN, &c, 1);
|
||
|
return (int)(c);
|
||
|
}
|
||
|
|
||
|
void __io_flush( void )
|
||
|
{
|
||
|
__write(_LLIO_STDOUT, NULL, 0);
|
||
|
}
|