/******************************************************************************* | |
* (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 |