/** @file hal/micro/cortexm3/uart.c * @brief STM32W uart drivers, supporting IAR's standard library * IO routines. * * */ #include PLATFORM_HEADER #include "hal/micro/micro-common.h" #include "hal/micro/cortexm3/micro-common.h" #include "uart.h" #ifdef __GNUC__ #include #include #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) uint8_t rxQ[RECEIVE_QUEUE_SIZE]; uint16_t rxHead; uint16_t rxTail; uint16_t rxUsed; ////////////////////////////////////////////////////////////////////////////// // Initialization void uartInit(uint32_t baudrate, uint8_t databits, SerialParity parity, uint8_t stopbits) { uint32_t tempcfg; uint32_t 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 ) { uint8_t errors = SC1_UARTSTAT & (SC_UARTFRMERR | SC_UARTRXOVF | SC_UARTPARERR ); uint8_t incoming = (uint8_t) 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) { uint32_t 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(uint8_t *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); }