rt-thread-official/bsp/samd21/board/uart.c

322 lines
8.8 KiB
C

// From module: SERCOM Callback API
#include <samd21.h>
#include <sercom.h>
// #include <sercom_interrupt.h>
// From module: SERCOM USART - Serial Communications (Callback APIs)
#include <usart.h>
// #include <usart_interrupt.h>
#include <rtdevice.h>
typedef struct _samd2x_uart_t
{
struct rt_serial_device *serial;
struct usart_module *instance;
Sercom *com;
enum usart_signal_mux_settings mux_setting;
uint32_t pinmux_pad0;
uint32_t pinmux_pad1;
uint32_t pinmux_pad2;
uint32_t pinmux_pad3;
enum system_interrupt_vector vector;
} SAMD2x_UART_T;
static struct rt_serial_device _serial3;
static struct usart_module _uart3_instance;
static SAMD2x_UART_T _uart3 = {
&_serial3,
&_uart3_instance,
SERCOM3,
USART_RX_1_TX_0_XCK_1,
PINMUX_PA22C_SERCOM3_PAD0,
PINMUX_PA23C_SERCOM3_PAD1,
PINMUX_UNUSED,
PINMUX_UNUSED,
SYSTEM_INTERRUPT_MODULE_SERCOM3
};
// static struct rt_serial_device _serial2;
// static struct rt_serial_device _serial3;
// static struct rt_serial_device _serial4;
static rt_err_t _uart_cfg(struct rt_serial_device *serial, struct serial_configure *cfg)
{
SAMD2x_UART_T *uart;
RT_ASSERT(serial != RT_NULL);
RT_ASSERT(cfg != RT_NULL);
uart = (SAMD2x_UART_T *)serial->parent.user_data;
//! [setup_config]
struct usart_config config_usart;
//! [setup_config]
//! [setup_config_defaults]
usart_get_config_defaults(&config_usart);
//! [setup_config_defaults]
config_usart.baudrate = cfg->baud_rate;
switch (cfg->data_bits )
{
case DATA_BITS_8:
config_usart.character_size = USART_CHARACTER_SIZE_8BIT;
break;
case DATA_BITS_5:
config_usart.character_size = USART_CHARACTER_SIZE_5BIT;
break;
case DATA_BITS_6:
config_usart.character_size = USART_CHARACTER_SIZE_6BIT;
break;
case DATA_BITS_7:
config_usart.character_size = USART_CHARACTER_SIZE_7BIT;
break;
case DATA_BITS_9:
config_usart.character_size = USART_CHARACTER_SIZE_9BIT;
break;
default:
config_usart.character_size = USART_CHARACTER_SIZE_8BIT;
break;
}
switch (cfg->parity)
{
case PARITY_NONE:
config_usart.parity = USART_PARITY_NONE;
break;
case PARITY_EVEN:
config_usart.parity = USART_PARITY_EVEN;
break;
case PARITY_ODD:
config_usart.parity = USART_PARITY_ODD;
break;
default:
config_usart.parity = USART_PARITY_NONE;
break;
}
config_usart.stopbits = USART_STOPBITS_1;
if (cfg->stop_bits != USART_STOPBITS_1)
{
config_usart.stopbits = USART_STOPBITS_2;
}
config_usart.data_order = USART_DATAORDER_LSB;
if (cfg->bit_order != BIT_ORDER_LSB)
{
config_usart.data_order = USART_DATAORDER_MSB;
}
config_usart.mux_setting = uart->mux_setting;
config_usart.pinmux_pad0 = uart->pinmux_pad0;
config_usart.pinmux_pad1 = uart->pinmux_pad1;
config_usart.pinmux_pad2 = uart->pinmux_pad2;
config_usart.pinmux_pad3 = uart->pinmux_pad3;
config_usart.receiver_enable = false;
config_usart.transmitter_enable = true;
while (usart_init(uart->instance, uart->com, &config_usart) != STATUS_OK) {
}
usart_enable(uart->instance);
/* Wait for the synchronization to complete */
_usart_wait_for_sync(uart->instance);
return RT_EOK;
}
static rt_err_t _uart_ctrl(struct rt_serial_device *serial, int cmd, void *arg)
{
SAMD2x_UART_T *uart;
RT_ASSERT(serial != RT_NULL);
uart = (SAMD2x_UART_T *)(serial->parent.user_data);
switch (cmd)
{
/* disable interrupt */
case RT_DEVICE_CTRL_CLR_INT:
uart->com->USART.INTENCLR.reg = SERCOM_USART_INTFLAG_RXC;
usart_disable_transceiver(uart->instance, USART_TRANSCEIVER_RX);
system_interrupt_disable(uart->vector);
/* Wait for the synchronization to complete */
_usart_wait_for_sync(uart->instance);
break;
/* enable interrupt */
case RT_DEVICE_CTRL_SET_INT:
/* Enable RX interrupt. */
/* Enable the RX Complete Interrupt */
uart->com->USART.INTENSET.reg = SERCOM_USART_INTFLAG_RXC;
usart_enable_transceiver(uart->instance, USART_TRANSCEIVER_RX);
system_interrupt_enable(uart->vector);
/* Wait for the synchronization to complete */
_usart_wait_for_sync(uart->instance);
break;
default:
return -RT_ERROR;
}
return RT_EOK;
}
static int _uart_putc(struct rt_serial_device *serial, char c)
{
SAMD2x_UART_T *uart;
RT_ASSERT(serial != RT_NULL);
// while (!(uart->com->USART.INTFLAG.reg & SERCOM_USART_INTFLAG_DRE)) {
// }
uart = (SAMD2x_UART_T *)(serial->parent.user_data);
/* Write data to USART module */
uart->com->USART.DATA.reg = c;
while (!(uart->com->USART.INTFLAG.reg & SERCOM_USART_INTFLAG_TXC)) {
/* Wait until data is sent */
}
return 1;
}
static int _uart_getc(struct rt_serial_device *serial)
{
int ch;
SAMD2x_UART_T *uart;
RT_ASSERT(serial != RT_NULL);
uart = (SAMD2x_UART_T *)(serial->parent.user_data);
/* Check if USART has new data */
if (!(uart->com->USART.INTFLAG.reg & SERCOM_USART_INTFLAG_RXC)) {
/* Return error code */
return -1;
}
ch = uart->com->USART.DATA.reg & 0x1FF;
return ch;
}
static struct rt_uart_ops _uart_ops = {
_uart_cfg,
_uart_ctrl,
_uart_putc,
_uart_getc
};
static void uart_int_cb(SAMD2x_UART_T *uart_handle)
{
/* Temporary variables */
uint16_t interrupt_status;
uint8_t error_code;
struct usart_module *module = uart_handle->instance;
/* Pointer to the hardware module instance */
SercomUsart *const usart_hw = &(module->hw->USART);
/* Read and mask interrupt flag register */
interrupt_status = usart_hw->INTFLAG.reg;
interrupt_status &= usart_hw->INTENSET.reg;
/* Check if the Receive Complete interrupt has occurred, and that
* there's more data to receive */
if (interrupt_status & SERCOM_USART_INTFLAG_RXC) {
/* Read out the status code and mask away all but the 4 LSBs*/
error_code = (uint8_t)(usart_hw->STATUS.reg & SERCOM_USART_STATUS_MASK);
#if !SAMD20
/* CTS status should not be considered as an error */
if(error_code & SERCOM_USART_STATUS_CTS) {
error_code &= ~SERCOM_USART_STATUS_CTS;
}
#endif
#ifdef FEATURE_USART_LIN_MASTER
/* TXE status should not be considered as an error */
if(error_code & SERCOM_USART_STATUS_TXE) {
error_code &= ~SERCOM_USART_STATUS_TXE;
}
#endif
/* Check if an error has occurred during the receiving */
if (error_code) {
/* Check which error occurred */
if (error_code & SERCOM_USART_STATUS_FERR) {
/* clear flag by writing 1 to it */
usart_hw->STATUS.reg = SERCOM_USART_STATUS_FERR;
} else if (error_code & SERCOM_USART_STATUS_BUFOVF) {
/* clear flag by writing 1 to it */
usart_hw->STATUS.reg = SERCOM_USART_STATUS_BUFOVF;
} else if (error_code & SERCOM_USART_STATUS_PERR) {
/* clear flag by writing 1 to it */
usart_hw->STATUS.reg = SERCOM_USART_STATUS_PERR;
}
#ifdef FEATURE_USART_LIN_SLAVE
else if (error_code & SERCOM_USART_STATUS_ISF) {
/* clear flag by writing 1 to it */
usart_hw->STATUS.reg = SERCOM_USART_STATUS_ISF;
}
#endif
#ifdef FEATURE_USART_COLLISION_DECTION
else if (error_code & SERCOM_USART_STATUS_COLL) {
/* clear flag by writing 1 to it */
usart_hw->STATUS.reg = SERCOM_USART_STATUS_COLL;
}
#endif
} else {
rt_hw_serial_isr(uart_handle->serial, RT_SERIAL_EVENT_RX_IND);
}
}
#ifdef FEATURE_USART_HARDWARE_FLOW_CONTROL
if (interrupt_status & SERCOM_USART_INTFLAG_CTSIC) {
/* Disable interrupts */
usart_hw->INTENCLR.reg = SERCOM_USART_INTENCLR_CTSIC;
/* Clear interrupt flag */
usart_hw->INTFLAG.reg = SERCOM_USART_INTFLAG_CTSIC;
}
#endif
#ifdef FEATURE_USART_LIN_SLAVE
if (interrupt_status & SERCOM_USART_INTFLAG_RXBRK) {
/* Disable interrupts */
usart_hw->INTENCLR.reg = SERCOM_USART_INTENCLR_RXBRK;
/* Clear interrupt flag */
usart_hw->INTFLAG.reg = SERCOM_USART_INTFLAG_RXBRK;
}
#endif
#ifdef FEATURE_USART_START_FRAME_DECTION
if (interrupt_status & SERCOM_USART_INTFLAG_RXS) {
/* Disable interrupts */
usart_hw->INTENCLR.reg = SERCOM_USART_INTENCLR_RXS;
/* Clear interrupt flag */
usart_hw->INTFLAG.reg = SERCOM_USART_INTFLAG_RXS;
}
#endif
}
void SERCOM3_Handler(void)
{
uart_int_cb(&_uart3);
}
void uart_init(void)
{
struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT;
config.bufsz = 512;
_serial3.config = config;
_serial3.ops = &_uart_ops;
rt_hw_serial_register(&_serial3, "uart3", RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX, &_uart3);
}