322 lines
8.8 KiB
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);
|
||
|
}
|