| /******************************************************************************* | |
| * (c) Copyright 2007 Actel Corporation. All rights reserved. | |
| * | |
| * SmartFusion Microcontroller Subsystem UART bare metal software driver | |
| * implementation. | |
| * | |
| * SVN $Revision: 1898 $ | |
| * SVN $Date: 2009-12-21 17:27:57 +0000 (Mon, 21 Dec 2009) $ | |
| */ | |
| #include "mss_uart.h" | |
| #include "../../CMSIS/mss_assert.h" | |
| #ifdef __cplusplus | |
| extern "C" { | |
| #endif | |
| /******************************************************************************* | |
| * defines | |
| */ | |
| #define TX_READY 0x01U | |
| #define TX_COMPLETE 0U | |
| #define TX_FIFO_SIZE 16U | |
| #define FCR_TRIG_LEVEL_MASK 0xC0U | |
| #define IIRF_MASK 0x0FU | |
| /******************************************************************************* | |
| * Possible values for Interrupt Identification Register Field. | |
| */ | |
| #define IIRF_MODEM_STATUS 0x00U | |
| #define IIRF_THRE 0x02U | |
| #define IIRF_RX_DATA 0x04U | |
| #define IIRF_RX_LINE_STATUS 0x06U | |
| #define IIRF_DATA_TIMEOUT 0x0CU | |
| /******************************************************************************* | |
| * Cortex-M3 interrupt handler functions implemented as part of the MSS UART | |
| * driver. | |
| */ | |
| #if defined(__GNUC__) | |
| __attribute__((__interrupt__)) void UART0_IRQHandler( void ); | |
| #else | |
| void UART0_IRQHandler( void ); | |
| #endif | |
| #if defined(__GNUC__) | |
| __attribute__((__interrupt__)) void UART1_IRQHandler( void ); | |
| #else | |
| void UART1_IRQHandler( void ); | |
| #endif | |
| /******************************************************************************* | |
| * Local functions. | |
| */ | |
| static void MSS_UART_isr( mss_uart_instance_t * this_uart ); | |
| /******************************************************************************* | |
| * | |
| */ | |
| mss_uart_instance_t g_mss_uart0; | |
| mss_uart_instance_t g_mss_uart1; | |
| /***************************************************************************//** | |
| * UART_init. | |
| * Initialises the UART with default configuration. | |
| */ | |
| void | |
| MSS_UART_init | |
| ( | |
| mss_uart_instance_t* this_uart, | |
| uint32_t baud_rate, | |
| uint8_t line_config | |
| ) | |
| { | |
| uint16_t baud_value; | |
| uint32_t pclk_freq; | |
| /* The driver expects g_mss_uart0 and g_mss_uart1 to be the only | |
| * mss_uart_instance_t instances used to identfy UART0 and UART1. */ | |
| ASSERT( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) ); | |
| /* Force the value of the CMSIS global variables holding the various system | |
| * clock frequencies to be updated. */ | |
| SystemCoreClockUpdate(); | |
| if ( this_uart == &g_mss_uart0 ) | |
| { | |
| this_uart->hw_reg = UART0; | |
| this_uart->hw_reg_bit = UART0_BITBAND; | |
| this_uart->irqn = UART0_IRQn; | |
| pclk_freq = g_FrequencyPCLK0; | |
| /* reset UART0 */ | |
| SYSREG->SOFT_RST_CR |= SYSREG_UART0_SOFTRESET_MASK; | |
| /* Clear any previously pended UART0 interrupt */ | |
| NVIC_ClearPendingIRQ( UART0_IRQn ); | |
| /* Take UART0 out of reset. */ | |
| SYSREG->SOFT_RST_CR &= ~SYSREG_UART0_SOFTRESET_MASK; | |
| } | |
| else | |
| { | |
| this_uart->hw_reg = UART1; | |
| this_uart->hw_reg_bit = UART1_BITBAND; | |
| this_uart->irqn = UART1_IRQn; | |
| pclk_freq = g_FrequencyPCLK1; | |
| /* Reset UART1 */ | |
| SYSREG->SOFT_RST_CR |= SYSREG_UART1_SOFTRESET_MASK; | |
| /* Clear any previously pended UART1 interrupt */ | |
| NVIC_ClearPendingIRQ( UART1_IRQn ); | |
| /* Take UART1 out of reset. */ | |
| SYSREG->SOFT_RST_CR &= ~SYSREG_UART1_SOFTRESET_MASK; | |
| } | |
| /* disable interrupts */ | |
| this_uart->hw_reg->IER = 0U; | |
| /* | |
| * Compute baud value based on requested baud rate and PCLK frequency. | |
| * The baud value is computed using the following equation: | |
| * baud_value = PCLK_Frequency / (baud_rate * 16) | |
| * The baud value is rounded up or down depending on what would be the remainder | |
| * of the divide by 16 operation. | |
| */ | |
| baud_value = (uint16_t)(pclk_freq / baud_rate); | |
| if ( baud_value & 0x00000008U ) | |
| { | |
| /* remainder above 0.5 */ | |
| baud_value = (baud_value >> 4U) + 1U; | |
| } | |
| else | |
| { | |
| /* remainder below 0.5 */ | |
| baud_value = (baud_value >> 4U); | |
| } | |
| /* set divisor latch */ | |
| this_uart->hw_reg_bit->LCR_DLAB = (uint32_t)1; | |
| /* msb of baud value */ | |
| this_uart->hw_reg->DMR = (uint8_t)(baud_value >> 8); | |
| /* lsb of baud value */ | |
| this_uart->hw_reg->DLR = (uint8_t)baud_value; | |
| /* reset divisor latch */ | |
| this_uart->hw_reg_bit->LCR_DLAB = (uint32_t)0; | |
| /* set the line control register (bit length, stop bits, parity) */ | |
| this_uart->hw_reg->LCR = line_config; | |
| /* FIFO configuration */ | |
| this_uart->hw_reg->FCR = (uint8_t)MSS_UART_FIFO_SINGLE_BYTE; | |
| /* disable loopback */ | |
| this_uart->hw_reg_bit->MCR_LOOP = (uint32_t)0; | |
| /* Instance setup */ | |
| this_uart->tx_buff_size = TX_COMPLETE; | |
| this_uart->tx_buffer = (const uint8_t *)0; | |
| this_uart->tx_idx = 0U; | |
| this_uart->rx_handler = (mss_uart_rx_handler_t)0; | |
| } | |
| /***************************************************************************//** | |
| * See mss_uart.h for details of how to use this function. | |
| */ | |
| void | |
| MSS_UART_polled_tx | |
| ( | |
| mss_uart_instance_t * this_uart, | |
| const uint8_t * pbuff, | |
| uint32_t tx_size | |
| ) | |
| { | |
| uint32_t char_idx; | |
| uint32_t status; | |
| ASSERT( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) ); | |
| for ( char_idx = 0U; char_idx < tx_size; char_idx++ ) | |
| { | |
| /* Wait for UART to become ready to transmit. */ | |
| do { | |
| status = this_uart->hw_reg_bit->LSR_THRE; | |
| } while ( (status & TX_READY) == 0U ); | |
| /* Send next character in the buffer. */ | |
| this_uart->hw_reg->THR = pbuff[char_idx]; | |
| } | |
| } | |
| /***************************************************************************//** | |
| * See mss_uart.h for details of how to use this function. | |
| */ | |
| void | |
| MSS_UART_polled_tx_string | |
| ( | |
| mss_uart_instance_t * this_uart, | |
| const uint8_t * p_sz_string | |
| ) | |
| { | |
| uint32_t char_idx; | |
| uint32_t status; | |
| ASSERT( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) ); | |
| char_idx = 0U; | |
| while ( p_sz_string[char_idx] != 0U ) | |
| { | |
| /* Wait for UART to become ready to transmit. */ | |
| do { | |
| status = this_uart->hw_reg_bit->LSR_THRE; | |
| } while ( (status & TX_READY) == 0U); | |
| /* Send next character in the buffer. */ | |
| this_uart->hw_reg->THR = p_sz_string[char_idx]; | |
| ++char_idx; | |
| } | |
| } | |
| /***************************************************************************//** | |
| * See mss_uart.h for details of how to use this function. | |
| */ | |
| void | |
| MSS_UART_irq_tx | |
| ( | |
| mss_uart_instance_t * this_uart, | |
| const uint8_t * pbuff, | |
| uint32_t tx_size | |
| ) | |
| { | |
| ASSERT( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) ); | |
| if ( tx_size > 0U ) | |
| { | |
| /*Initialise the transmit info for the UART instance with the arguments.*/ | |
| this_uart->tx_buffer = pbuff; | |
| this_uart->tx_buff_size = tx_size; | |
| this_uart->tx_idx = (uint16_t)0; | |
| /* enables TX interrupt */ | |
| this_uart->hw_reg_bit->IER_ETBEI = (uint32_t)1; | |
| /* Enable UART instance interrupt in Cortex-M3 NVIC. */ | |
| NVIC_EnableIRQ( this_uart->irqn ); | |
| } | |
| } | |
| /***************************************************************************//** | |
| * See mss_uart.h for details of how to use this function. | |
| */ | |
| int8_t | |
| MSS_UART_tx_complete | |
| ( | |
| mss_uart_instance_t * this_uart | |
| ) | |
| { | |
| int8_t ret_value = 0; | |
| uint32_t transmit_empty = this_uart->hw_reg_bit->LSR_TEMT; | |
| ASSERT( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) ); | |
| if ( ( TX_COMPLETE == this_uart->tx_buff_size ) && transmit_empty ) | |
| { | |
| ret_value = 1; | |
| } | |
| return ret_value; | |
| } | |
| /***************************************************************************//** | |
| * See mss_uart.h for details of how to use this function. | |
| */ | |
| size_t | |
| MSS_UART_get_rx | |
| ( | |
| mss_uart_instance_t * this_uart, | |
| uint8_t * rx_buff, | |
| size_t buff_size | |
| ) | |
| { | |
| size_t rx_size = 0U; | |
| ASSERT( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) ); | |
| while (( this_uart->hw_reg_bit->LSR_DR != 0U) && ( rx_size < buff_size ) ) | |
| { | |
| rx_buff[rx_size] = this_uart->hw_reg->RBR; | |
| ++rx_size; | |
| } | |
| return rx_size; | |
| } | |
| /***************************************************************************//** | |
| * Interrupt service routine triggered by the Transmitter Holding Register | |
| * Empty (THRE) interrupt or Received Data Available (RDA). | |
| * On THRE irq this routine will transmit the data from the transmit buffer. | |
| * When all bytes are transmitted, this routine disables the THRE interrupt | |
| * and resets the transmit counter. | |
| * On RDA irq this routine will call the user's receive handler routine previously | |
| * registered with the UART driver through a call to UART_set_rx_handler(). | |
| */ | |
| static void | |
| MSS_UART_isr | |
| ( | |
| mss_uart_instance_t * this_uart | |
| ) | |
| { | |
| uint8_t iirf; | |
| uint32_t tx_empty; | |
| ASSERT( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) ); | |
| iirf = this_uart->hw_reg->IIR & IIRF_MASK; | |
| switch ( iirf ) | |
| { | |
| case IIRF_MODEM_STATUS: | |
| break; | |
| case IIRF_THRE: /* Transmitter Holding Register Empty */ | |
| tx_empty = this_uart->hw_reg_bit->LSR_TEMT; | |
| if ( tx_empty ) | |
| { | |
| uint32_t i; | |
| uint32_t fill_size = TX_FIFO_SIZE; | |
| uint32_t tx_remain = this_uart->tx_buff_size - this_uart->tx_idx; | |
| if ( tx_remain < TX_FIFO_SIZE ) | |
| { | |
| fill_size = tx_remain; | |
| } | |
| /* Fill up FIFO */ | |
| for ( i = 0U; i < fill_size; ++i ) | |
| { | |
| this_uart->hw_reg->THR = this_uart->tx_buffer[this_uart->tx_idx]; | |
| ++this_uart->tx_idx; | |
| } | |
| } | |
| else | |
| { | |
| this_uart->hw_reg->THR = this_uart->tx_buffer[this_uart->tx_idx]; | |
| ++this_uart->tx_idx; | |
| } | |
| if ( this_uart->tx_idx == this_uart->tx_buff_size ) | |
| { | |
| this_uart->tx_buff_size = TX_COMPLETE; | |
| /* disables TX interrupt */ | |
| this_uart->hw_reg_bit->IER_ETBEI = 0U; | |
| } | |
| break; | |
| case IIRF_RX_DATA: /* Received Data Available */ | |
| case IIRF_DATA_TIMEOUT: | |
| if (this_uart->rx_handler != 0) | |
| { | |
| (*(this_uart->rx_handler))(); | |
| } | |
| break; | |
| case IIRF_RX_LINE_STATUS: | |
| break; | |
| default: | |
| /* Disable other interrupts */ | |
| this_uart->hw_reg_bit->IER_ELSI = 0U; | |
| this_uart->hw_reg_bit->IER_EDSSI = 0U; | |
| break; | |
| } | |
| } | |
| /***************************************************************************//** | |
| * See mss_uart.h for details of how to use this function. | |
| */ | |
| void | |
| MSS_UART_set_rx_handler | |
| ( | |
| mss_uart_instance_t * this_uart, | |
| mss_uart_rx_handler_t handler, | |
| mss_uart_rx_trig_level_t trigger_level | |
| ) | |
| { | |
| ASSERT( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) ); | |
| this_uart->rx_handler = handler; | |
| /* Set the receive interrupt trigger level. */ | |
| this_uart->hw_reg->FCR = (this_uart->hw_reg->FCR & (uint8_t)(~((uint8_t)FCR_TRIG_LEVEL_MASK))) | (uint8_t)trigger_level; | |
| /* Enable receive interrupt. */ | |
| this_uart->hw_reg_bit->IER_ERBFI = 1U; | |
| /* Enable UART instance interrupt in Cortex-M3 NVIC. */ | |
| NVIC_EnableIRQ( this_uart->irqn ); | |
| } | |
| /***************************************************************************//** | |
| * See mss_uart.h for details of how to use this function. | |
| */ | |
| void | |
| MSS_UART_set_loopback | |
| ( | |
| mss_uart_instance_t * this_uart, | |
| mss_uart_loopback_t loopback | |
| ) | |
| { | |
| ASSERT( (this_uart == &g_mss_uart0) || (this_uart == &g_mss_uart1) ); | |
| if ( loopback == MSS_UART_LOOPBACK_OFF ) | |
| { | |
| this_uart->hw_reg_bit->MCR_LOOP = 0U; | |
| } | |
| else | |
| { | |
| this_uart->hw_reg_bit->MCR_LOOP = 1U; | |
| } | |
| } | |
| /***************************************************************************//** | |
| * UART0 interrupt service routine. | |
| * UART0_IRQHandler is included within the Cortex-M3 vector table as part of the | |
| * Fusion 2 CMSIS. | |
| */ | |
| #if defined(__GNUC__) | |
| __attribute__((__interrupt__)) void UART0_IRQHandler( void ) | |
| #else | |
| void UART0_IRQHandler( void ) | |
| #endif | |
| { | |
| MSS_UART_isr( &g_mss_uart0 ); | |
| NVIC_ClearPendingIRQ( UART0_IRQn ); | |
| } | |
| /***************************************************************************//** | |
| * UART1 interrupt service routine. | |
| * UART2_IRQHandler is included within the Cortex-M3 vector table as part of the | |
| * Fusion 2 CMSIS. | |
| */ | |
| #if defined(__GNUC__) | |
| __attribute__((__interrupt__)) void UART1_IRQHandler( void ) | |
| #else | |
| void UART1_IRQHandler( void ) | |
| #endif | |
| { | |
| MSS_UART_isr( &g_mss_uart1 ); | |
| NVIC_ClearPendingIRQ( UART1_IRQn ); | |
| } | |
| #ifdef __cplusplus | |
| } | |
| #endif |