mbed library sources
Fork of mbed-src by
Diff: targets/hal/TARGET_Atmel/TARGET_SAM21/serial_api.c
- Revision:
- 579:53297373a894
- Child:
- 592:a274ee790e56
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/targets/hal/TARGET_Atmel/TARGET_SAM21/serial_api.c Wed Jul 01 09:45:11 2015 +0100 @@ -0,0 +1,957 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2013 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include <string.h> +#include "mbed_assert.h" +#include "cmsis.h" +#include "serial_api.h" +#include "pinmap.h" +#include "PeripheralPins.h" +#include "usart.h" +#include "samr21_xplained_pro.h" + +#if DEVICE_SERIAL_ASYNCH +#define pUSART_S(obj) obj->serial.usart +#define pSERIAL_S(obj) ((struct serial_s*)&(obj->serial)) +#else +#define pUSART_S(obj) obj->serial +#define pSERIAL_S(obj) ((struct serial_s*)obj) +#endif +#define _USART(obj) pUSART_S(obj)->USART +#define USART_NUM 6 + + +uint8_t serial_get_index(serial_t *obj); +IRQn_Type get_serial_irq_num (serial_t *obj); + +static uint32_t serial_irq_ids[USART_NUM] = {0}; +static uart_irq_handler irq_handler; + +int stdio_uart_inited = 0; +serial_t stdio_uart; + +extern uint8_t g_sys_init; + +static inline bool usart_syncing(serial_t *obj) +{ + /* Sanity check arguments */ + MBED_ASSERT(obj); + + return (_USART(obj).SYNCBUSY.reg); +} + +static inline void enable_usart(serial_t *obj) +{ + /* Sanity check arguments */ + MBED_ASSERT(obj); + + /* Wait until synchronization is complete */ + usart_syncing(obj); + + /* Enable USART module */ + _USART(obj).CTRLA.reg |= SERCOM_USART_CTRLA_ENABLE; +} + +static inline void disable_usart(serial_t *obj) +{ + /* Sanity check arguments */ + MBED_ASSERT(obj); + + /* Wait until synchronization is complete */ + usart_syncing(obj); + + /* Disable USART module */ + _USART(obj).CTRLA.reg &= ~SERCOM_USART_CTRLA_ENABLE; +} + +static inline void reset_usart(serial_t *obj) +{ + /* Sanity check arguments */ + MBED_ASSERT(obj); + + disable_usart(obj); + + /* Wait until synchronization is complete */ + usart_syncing(obj); + + /* Reset module */ + _USART(obj).CTRLA.reg = SERCOM_USART_CTRLA_SWRST; +} + +static enum status_code usart_set_config_default( serial_t *obj) +{ + + /* Index for generic clock */ + uint32_t sercom_index = _sercom_get_sercom_inst_index(pUSART_S(obj)); + uint32_t gclk_index = sercom_index + SERCOM0_GCLK_ID_CORE; + + /* Cache new register values to minimize the number of register writes */ + uint32_t ctrla = 0; + uint32_t ctrlb = 0; + uint16_t baud = 0; + + enum sercom_asynchronous_operation_mode mode = SERCOM_ASYNC_OPERATION_MODE_ARITHMETIC; + enum sercom_asynchronous_sample_num sample_num = SERCOM_ASYNC_SAMPLE_NUM_16; + + /* Set data order, internal muxing, and clock polarity */ + ctrla = (uint32_t)USART_DATAORDER_LSB | // data order + (uint32_t)pSERIAL_S(obj)->mux_setting; // mux setting // clock polarity is not used + + + /* Get baud value from mode and clock */ + _sercom_get_async_baud_val(pSERIAL_S(obj)->baudrate,system_gclk_chan_get_hz(gclk_index), &baud, mode, sample_num); // for asynchronous transfer mode + + /* Wait until synchronization is complete */ + usart_syncing(obj); + + /*Set baud val */ + _USART(obj).BAUD.reg = baud; + + /* Set sample mode */ + ctrla |= USART_TRANSFER_ASYNCHRONOUSLY; + + /* for disabled external clock source */ + ctrla |= SERCOM_USART_CTRLA_MODE(0x1); + + /* Set stopbits, character size and enable transceivers */ + ctrlb = (uint32_t)pSERIAL_S(obj)->stopbits | (uint32_t)pSERIAL_S(obj)->character_size | + (0x1ul << SERCOM_USART_CTRLB_RXEN_Pos) | // receiver enable + (0x1ul << SERCOM_USART_CTRLB_TXEN_Pos); // transmitter enable + + /* Check parity mode bits */ + if (pSERIAL_S(obj)->parity != USART_PARITY_NONE) { + ctrla |= SERCOM_USART_CTRLA_FORM(1); + ctrlb |= pSERIAL_S(obj)->parity; + } else { + ctrla |= SERCOM_USART_CTRLA_FORM(0); + } + + /* Wait until synchronization is complete */ + usart_syncing(obj); + + /* Write configuration to CTRLB */ + _USART(obj).CTRLB.reg = ctrlb; + + /* Wait until synchronization is complete */ + usart_syncing(obj); + + /* Write configuration to CTRLA */ + _USART(obj).CTRLA.reg = ctrla; + + return STATUS_OK; +} + +void get_default_serial_values(serial_t *obj) +{ + /* Set default config to object */ + pSERIAL_S(obj)->parity = USART_PARITY_NONE; + pSERIAL_S(obj)->stopbits = USART_STOPBITS_1; + pSERIAL_S(obj)->character_size = USART_CHARACTER_SIZE_8BIT; + pSERIAL_S(obj)->baudrate = 9600; + pSERIAL_S(obj)->mux_setting = USART_RX_1_TX_2_XCK_3; + pSERIAL_S(obj)->pinmux_pad0 = PINMUX_DEFAULT; + pSERIAL_S(obj)->pinmux_pad1 = PINMUX_DEFAULT; + pSERIAL_S(obj)->pinmux_pad2 = PINMUX_DEFAULT; + pSERIAL_S(obj)->pinmux_pad3 = PINMUX_DEFAULT; +}; + +void serial_init(serial_t *obj, PinName tx, PinName rx) +{ + if (g_sys_init == 0) { + system_init(); + g_sys_init = 1; + } + + struct system_gclk_chan_config gclk_chan_conf; + UARTName uart; + uint32_t gclk_index; + uint32_t pm_index; + uint32_t sercom_index = 0; + uint32_t muxsetting = 0; + uint32_t padsetting[4] = {0}; + + /* Disable USART module */ + disable_usart(obj); + + get_default_serial_values(obj); + + find_pin_settings(tx, rx, NC, NC, &padsetting[0]); // tx, rx, clk(rts), chipsel(cts) pad array // getting pads from pins + muxsetting = find_mux_setting(tx, rx, NC, NC); // getting mux setting from pins + sercom_index = pinmap_sercom_peripheral(tx, rx); // same variable sercom_index reused for optimization + switch (sercom_index) { + case 0: + uart = UART_0; + pUSART_S(obj) = SERCOM0; + break; + case 1: + uart = UART_1; + pUSART_S(obj) = SERCOM1; + break; + case 2: + uart = UART_2; + pUSART_S(obj) = SERCOM2; + break; + case 3: + uart = UART_3; + pUSART_S(obj) = SERCOM3; + break; + case 4: + uart = UART_4; + pUSART_S(obj) = SERCOM4; + break; + case 5: + uart = UART_5; + pUSART_S(obj) = SERCOM5; + break; + } + + pSERIAL_S(obj)->txpin = tx; + pSERIAL_S(obj)->rxpin = rx; + pSERIAL_S(obj)->mux_setting = muxsetting;//EDBG_CDC_SERCOM_MUX_SETTING; + pSERIAL_S(obj)->pinmux_pad0 = padsetting[0];//EDBG_CDC_SERCOM_PINMUX_PAD0; + pSERIAL_S(obj)->pinmux_pad1 = padsetting[1];//EDBG_CDC_SERCOM_PINMUX_PAD1; + pSERIAL_S(obj)->pinmux_pad2 = padsetting[2];//EDBG_CDC_SERCOM_PINMUX_PAD2; + pSERIAL_S(obj)->pinmux_pad3 = padsetting[3];//EDBG_CDC_SERCOM_PINMUX_PAD3; + + pm_index = sercom_index + PM_APBCMASK_SERCOM0_Pos; + gclk_index = sercom_index + SERCOM0_GCLK_ID_CORE; + + if (_USART(obj).CTRLA.reg & SERCOM_USART_CTRLA_SWRST) { + /* The module is busy resetting itself */ + } + + if (_USART(obj).CTRLA.reg & SERCOM_USART_CTRLA_ENABLE) { + /* Check the module is enabled */ + } + + /* Turn on module in PM */ + system_apb_clock_set_mask(SYSTEM_CLOCK_APB_APBC, 1 << pm_index); + + /* Set up the GCLK for the module */ + gclk_chan_conf.source_generator = GCLK_GENERATOR_0; + system_gclk_chan_set_config(gclk_index, &gclk_chan_conf); + system_gclk_chan_enable(gclk_index); + sercom_set_gclk_generator(GCLK_GENERATOR_0, false); + + /* Set configuration according to the config struct */ + usart_set_config_default(obj); + struct system_pinmux_config pin_conf; + system_pinmux_get_config_defaults(&pin_conf); + pin_conf.direction = SYSTEM_PINMUX_PIN_DIR_INPUT; + pin_conf.input_pull = SYSTEM_PINMUX_PIN_PULL_NONE; + + uint32_t pad_pinmuxes[] = { + pSERIAL_S(obj)->pinmux_pad0, pSERIAL_S(obj)->pinmux_pad1, + pSERIAL_S(obj)->pinmux_pad2, pSERIAL_S(obj)->pinmux_pad3 + }; + + /* Configure the SERCOM pins according to the user configuration */ + for (uint8_t pad = 0; pad < 4; pad++) { + uint32_t current_pinmux = pad_pinmuxes[pad]; + + if (current_pinmux == PINMUX_DEFAULT) { + current_pinmux = _sercom_get_default_pad(pUSART_S(obj), pad); + } + + if (current_pinmux != PINMUX_UNUSED) { + pin_conf.mux_position = current_pinmux & 0xFFFF; + system_pinmux_pin_set_config(current_pinmux >> 16, &pin_conf); + } + } + + if (uart == STDIO_UART) { + stdio_uart_inited = 1; + memcpy(&stdio_uart, obj, sizeof(serial_t)); + } + + /* Wait until synchronization is complete */ + usart_syncing(obj); + + /* Enable USART module */ + enable_usart(obj); + +} + +void serial_free(serial_t *obj) +{ + serial_irq_ids[serial_get_index(obj)] = 0; + disable_usart(obj); +} + +void serial_baud(serial_t *obj, int baudrate) +{ + MBED_ASSERT((baudrate == 110) || (baudrate == 150) || (baudrate == 300) || (baudrate == 1200) || + (baudrate == 2400) || (baudrate == 4800) || (baudrate == 9600) || (baudrate == 19200) || (baudrate == 38400) || + (baudrate == 57600) || (baudrate == 115200) || (baudrate == 230400) || (baudrate == 460800) || (baudrate == 921600) ); + + struct system_gclk_chan_config gclk_chan_conf; + uint32_t gclk_index; + uint16_t baud = 0; + uint32_t sercom_index = 0; + enum sercom_asynchronous_operation_mode mode = SERCOM_ASYNC_OPERATION_MODE_ARITHMETIC; + enum sercom_asynchronous_sample_num sample_num = SERCOM_ASYNC_SAMPLE_NUM_16; + + pSERIAL_S(obj)->baudrate = baudrate; + disable_usart(obj); + + sercom_index = _sercom_get_sercom_inst_index(pUSART_S(obj)); + gclk_index = sercom_index + SERCOM0_GCLK_ID_CORE; + + gclk_chan_conf.source_generator = GCLK_GENERATOR_0; + system_gclk_chan_set_config(gclk_index, &gclk_chan_conf); + system_gclk_chan_enable(gclk_index); + sercom_set_gclk_generator(GCLK_GENERATOR_0, false); + + /* Get baud value from mode and clock */ + _sercom_get_async_baud_val(pSERIAL_S(obj)->baudrate, system_gclk_chan_get_hz(gclk_index), &baud, mode, sample_num); + + /* Wait until synchronization is complete */ + usart_syncing(obj); + + /*Set baud val */ + _USART(obj).BAUD.reg = baud; + /* Wait until synchronization is complete */ + usart_syncing(obj); + + enable_usart(obj); +} + +void serial_format(serial_t *obj, int data_bits, SerialParity parity, int stop_bits) +{ + MBED_ASSERT((stop_bits == 1) || (stop_bits == 2)); + MBED_ASSERT((parity == ParityNone) || (parity == ParityOdd) || (parity == ParityEven)); + MBED_ASSERT((data_bits == 5) || (data_bits == 6) || (data_bits == 7) || (data_bits == 8) /*|| (data_bits == 9)*/); + + /* Cache new register values to minimize the number of register writes */ + uint32_t ctrla = 0; + uint32_t ctrlb = 0; + + disable_usart(obj); + + ctrla = _USART(obj).CTRLA.reg; + ctrlb = _USART(obj).CTRLB.reg; + + ctrla &= ~(SERCOM_USART_CTRLA_FORM_Msk); + ctrlb &= ~(SERCOM_USART_CTRLB_CHSIZE_Msk); + ctrlb &= ~(SERCOM_USART_CTRLB_SBMODE); + ctrlb &= ~(SERCOM_USART_CTRLB_PMODE); + + switch (stop_bits) { + case 1: + pSERIAL_S(obj)->stopbits = USART_STOPBITS_1; + break; + case 2: + pSERIAL_S(obj)->stopbits = USART_STOPBITS_2; + break; + default: + pSERIAL_S(obj)->stopbits = USART_STOPBITS_1; + } + + switch (parity) { + case ParityNone: + pSERIAL_S(obj)->parity = USART_PARITY_NONE; + break; + case ParityOdd: + pSERIAL_S(obj)->parity = USART_PARITY_ODD; + break; + case ParityEven: + pSERIAL_S(obj)->parity = USART_PARITY_EVEN; + break; + default: + pSERIAL_S(obj)->parity = USART_PARITY_NONE; + } + + switch (data_bits) { + case 5: + pSERIAL_S(obj)->character_size = USART_CHARACTER_SIZE_5BIT; + break; + case 6: + pSERIAL_S(obj)->character_size = USART_CHARACTER_SIZE_6BIT; + break; + case 7: + pSERIAL_S(obj)->character_size = USART_CHARACTER_SIZE_7BIT; + break; + case 8: + pSERIAL_S(obj)->character_size = USART_CHARACTER_SIZE_8BIT; + break; // 9 bit transfer not required in mbed + default: + pSERIAL_S(obj)->character_size = USART_CHARACTER_SIZE_8BIT; + } + + + /* Set stopbits, character size and enable transceivers */ + ctrlb = (uint32_t)pSERIAL_S(obj)->stopbits | (uint32_t)pSERIAL_S(obj)->character_size; + + /* Check parity mode bits */ + if (pSERIAL_S(obj)->parity != USART_PARITY_NONE) { + ctrla |= SERCOM_USART_CTRLA_FORM(1); + ctrlb |= pSERIAL_S(obj)->parity; + } else { + ctrla |= SERCOM_USART_CTRLA_FORM(0); + } + + /* Write configuration to CTRLB */ + _USART(obj).CTRLB.reg = ctrlb; + + /* Wait until synchronization is complete */ + usart_syncing(obj); + + /* Write configuration to CTRLA */ + _USART(obj).CTRLA.reg = ctrla; + + /* Wait until synchronization is complete */ + usart_syncing(obj); + + enable_usart(obj); +} + +#ifdef DEVICE_SERIAL_FC +void uart0_irq(); +void uart1_irq(); +void uart2_irq(); +void uart3_irq(); +void uart4_irq(); +void uart5_irq(); + +void serial_set_flow_control(serial_t *obj, FlowControl type, PinName rxflow, PinName txflow) +{ + uint32_t muxsetting = 0; + uint32_t sercom_index = 0; + uint32_t padsetting[4] = {0}; + + IRQn_Type irq_n = (IRQn_Type)0; + uint32_t vector = 0; + + switch ((int)pUSART_S(obj)) { + case UART_0: + vector = (uint32_t)uart0_irq; + break; + case UART_1: + vector = (uint32_t)uart1_irq; + break; + case UART_2: + vector = (uint32_t)uart2_irq; + break; + case UART_3: + vector = (uint32_t)uart3_irq; + break; + case UART_4: + vector = (uint32_t)uart4_irq; + break; + case UART_5: + vector = (uint32_t)uart5_irq; + break; + } + irq_n = get_serial_irq_num(obj); + + disable_usart(obj); + //TODO : assert for rxflow and txflow pis to be added + find_pin_settings(pSERIAL_S(obj)->txpin, pSERIAL_S(obj)->rxpin, rxflow, txflow, &padsetting[0]); // tx, rx, clk(rts), chipsel(cts) pad array // getting pads from pins + muxsetting = find_mux_setting(pSERIAL_S(obj)->txpin, pSERIAL_S(obj)->rxpin, rxflow, txflow); // getting mux setting from pins + + pSERIAL_S(obj)->mux_setting = muxsetting;//EDBG_CDC_SERCOM_MUX_SETTING; + pSERIAL_S(obj)->pinmux_pad0 = padsetting[0];//EDBG_CDC_SERCOM_PINMUX_PAD0; + pSERIAL_S(obj)->pinmux_pad1 = padsetting[1];//EDBG_CDC_SERCOM_PINMUX_PAD1; + pSERIAL_S(obj)->pinmux_pad2 = padsetting[2];//EDBG_CDC_SERCOM_PINMUX_PAD2; + pSERIAL_S(obj)->pinmux_pad3 = padsetting[3];//EDBG_CDC_SERCOM_PINMUX_PAD3; + + /* Set configuration according to the config struct */ + usart_set_config_default(obj); + + struct system_pinmux_config pin_conf; + system_pinmux_get_config_defaults(&pin_conf); + pin_conf.direction = SYSTEM_PINMUX_PIN_DIR_INPUT; + pin_conf.input_pull = SYSTEM_PINMUX_PIN_PULL_NONE; + + uint32_t pad_pinmuxes[] = { + pSERIAL_S(obj)->pinmux_pad0, pSERIAL_S(obj)->pinmux_pad1, + pSERIAL_S(obj)->pinmux_pad2, pSERIAL_S(obj)->pinmux_pad3 + }; + + /* Configure the SERCOM pins according to the user configuration */ + for (uint8_t pad = 0; pad < 3; pad++) { + uint32_t current_pinmux = pad_pinmuxes[pad]; + + if (current_pinmux == PINMUX_DEFAULT) { + current_pinmux = _sercom_get_default_pad(pUSART_S(obj), pad); + } + + if (current_pinmux != PINMUX_UNUSED) { + pin_conf.mux_position = current_pinmux & 0xFFFF; + system_pinmux_pin_set_config(current_pinmux >> 16, &pin_conf); + } + } + if (pSERIAL_S(obj)->pinmux_pad3 != PINMUX_UNUSED) { + pin_conf.input_pull = SYSTEM_PINMUX_PIN_PULL_UP; + pin_conf.mux_position = pSERIAL_S(obj)->pinmux_pad3 & 0xFFFF; + system_pinmux_pin_set_config(pSERIAL_S(obj)->pinmux_pad3 >> 16, &pin_conf); + } + + NVIC_SetVector(irq_n, vector); + NVIC_EnableIRQ(irq_n); + + enable_usart(obj); + _USART(obj).INTENSET.reg = SERCOM_USART_INTENCLR_CTSIC; +} + +void serial_break_set(serial_t *obj) +{ + disable_usart(obj); + _USART(obj).CTRLB.reg &= ~SERCOM_SPI_CTRLB_RXEN; + usart_syncing(obj); + enable_usart(obj); +} + +void serial_break_clear(serial_t *obj) +{ + disable_usart(obj); + _USART(obj).CTRLB.reg |= SERCOM_SPI_CTRLB_RXEN; + usart_syncing(obj); + enable_usart(obj); +} + +#endif //DEVICE_SERIAL_FC + +/****************************************************************************** + * INTERRUPTS HANDLING + ******************************************************************************/ +inline uint8_t serial_get_index(serial_t *obj) +{ + switch ((int)pUSART_S(obj)) { + case UART_0: + return 0; + case UART_1: + return 1; + case UART_2: + return 2; + case UART_3: + return 3; + case UART_4: + return 4; + case UART_5: + return 5; + } + return 0; +} +static inline void uart_irq(SercomUsart *const usart, uint32_t index) +{ + uint16_t interrupt_status; + interrupt_status = usart->INTFLAG.reg; + interrupt_status &= usart->INTENSET.reg; + + if (serial_irq_ids[index] != 0) { + if (interrupt_status & SERCOM_USART_INTFLAG_TXC) { // for transmit complete + usart->INTFLAG.reg = SERCOM_USART_INTFLAG_TXC; + irq_handler(serial_irq_ids[index], TxIrq); + } + if (interrupt_status & SERCOM_USART_INTFLAG_RXC) { // for receive complete + usart->INTFLAG.reg = SERCOM_USART_INTFLAG_RXC; + irq_handler(serial_irq_ids[index], RxIrq); + } + if (interrupt_status & SERCOM_USART_INTFLAG_CTSIC) { // hardware flow control + usart->INTENCLR.reg = SERCOM_USART_INTENCLR_CTSIC; + usart->INTFLAG.reg = SERCOM_USART_INTENCLR_CTSIC; + } + } +} + +void uart0_irq() +{ + uart_irq((SercomUsart *)UART_0, 0); +} + +void uart1_irq() +{ + uart_irq((SercomUsart *)UART_1, 1); +} + +void uart2_irq() +{ + uart_irq((SercomUsart *)UART_2, 2); +} + +void uart3_irq() +{ + uart_irq((SercomUsart *)UART_3, 3); +} + +void uart4_irq() +{ + uart_irq((SercomUsart *)UART_4, 4); +} + +void uart5_irq() +{ + uart_irq((SercomUsart *)UART_5, 5); +} + +void serial_irq_handler(serial_t *obj, uart_irq_handler handler, uint32_t id) +{ + irq_handler = handler; + serial_irq_ids[serial_get_index(obj)] = id; +} + +IRQn_Type get_serial_irq_num (serial_t *obj) +{ + switch ((int)pUSART_S(obj)) { + case UART_0: + return SERCOM0_IRQn; + case UART_1: + return SERCOM1_IRQn; + case UART_2: + return SERCOM2_IRQn; + case UART_3: + return SERCOM3_IRQn; + case UART_4: + return SERCOM4_IRQn; + case UART_5: + return SERCOM5_IRQn; + default: + MBED_ASSERT(0); + } +} + +void serial_irq_set(serial_t *obj, SerialIrq irq, uint32_t enable) +{ + IRQn_Type irq_n = (IRQn_Type)0; + uint32_t vector = 0; + + switch ((int)pUSART_S(obj)) { + case UART_0: + vector = (uint32_t)uart0_irq; + break; + case UART_1: + vector = (uint32_t)uart1_irq; + break; + case UART_2: + vector = (uint32_t)uart2_irq; + break; + case UART_3: + vector = (uint32_t)uart3_irq; + break; + case UART_4: + vector = (uint32_t)uart4_irq; + break; + case UART_5: + vector = (uint32_t)uart5_irq; + break; + } + irq_n = get_serial_irq_num(obj); + + if (enable) { + switch (irq) { + case RxIrq: + _USART(obj).INTENSET.reg = SERCOM_USART_INTFLAG_RXC; + break; + case TxIrq: + _USART(obj).INTENSET.reg = SERCOM_USART_INTFLAG_TXC; + break; + } + NVIC_SetVector(irq_n, vector); + NVIC_EnableIRQ(irq_n); + + } else { + switch (irq) { + case RxIrq: + _USART(obj).INTENCLR.reg = SERCOM_USART_INTFLAG_RXC; + break; + case TxIrq: + _USART(obj).INTENCLR.reg = SERCOM_USART_INTFLAG_TXC; + break; + } + NVIC_DisableIRQ(irq_n); + } +} + +/****************************************************************************** + * READ/WRITE + ******************************************************************************/ +int serial_getc(serial_t *obj) +{ + while (!serial_readable(obj)); + return _USART(obj).DATA.reg ; +} + +void serial_putc(serial_t *obj, int c) +{ + uint16_t q = (c & SERCOM_USART_DATA_MASK); + while (!serial_writable(obj)); + _USART(obj).DATA.reg = q; + while (!(_USART(obj).INTFLAG.reg & SERCOM_USART_INTFLAG_TXC)); // wait till data is sent +} + +int serial_readable(serial_t *obj) +{ + uint32_t status = 1; + if (!(_USART(obj).INTFLAG.reg & SERCOM_USART_INTFLAG_RXC)) { + status = 0; + } else { + status = 1; + } + return status; +} + +int serial_writable(serial_t *obj) +{ + uint32_t status = 1; + if (!(_USART(obj).INTFLAG.reg & SERCOM_USART_INTFLAG_DRE)) { + status = 0; + } else { + status = 1; + } + return status; +} + +/************************************************************************************ + * ASYNCHRONOUS HAL * + ************************************************************************************/ + +#if DEVICE_SERIAL_ASYNCH + +/************************************ + * HELPER FUNCTIONS * + ***********************************/ +void serial_tx_enable_event(serial_t *obj, int event, uint8_t enable) +{ + if(enable) { + pSERIAL_S(obj)->events |= event; + } else { + pSERIAL_S(obj)->events &= ~ event; + } +} + +void serial_rx_enable_event(serial_t *obj, int event, uint8_t enable) +{ + if(enable) { + pSERIAL_S(obj)->events |= event; + } else { + pSERIAL_S(obj)->events &= ~ event; + } +} + +void serial_tx_buffer_set(serial_t *obj, void *tx, int tx_length, uint8_t width) +{ + // We only support byte buffers for now + MBED_ASSERT(width == 8); + + if(serial_tx_active(obj)) return; + + obj->tx_buff.buffer = tx; + obj->tx_buff.length = tx_length; + obj->tx_buff.pos = 0; + + return; +} + +void serial_rx_buffer_set(serial_t *obj, void *rx, int rx_length, uint8_t width) +{ + // We only support byte buffers for now + MBED_ASSERT(width == 8); + + if(serial_rx_active(obj)) return; + + obj->rx_buff.buffer = rx; + obj->rx_buff.length = rx_length; + obj->rx_buff.pos = 0; + + return; +} + +void serial_set_char_match(serial_t *obj, uint8_t char_match) +{ + if (char_match != SERIAL_RESERVED_CHAR_MATCH) { + obj->char_match = char_match; + } +} + +/************************************ + * TRANSFER FUNCTIONS * + ***********************************/ +int serial_tx_asynch(serial_t *obj, const void *tx, size_t tx_length, uint8_t tx_width, uint32_t handler, uint32_t event, DMAUsage hint) +{ + MBED_ASSERT(tx != (void*)0); + if(tx_length == 0) return 0; + + serial_tx_buffer_set(obj, (void *)tx, tx_length, tx_width); + serial_tx_enable_event(obj, event, true); + +// if( hint == DMA_USAGE_NEVER) { //TODO: DMA to be implemented later + NVIC_ClearPendingIRQ(get_serial_irq_num(obj)); + NVIC_DisableIRQ(get_serial_irq_num(obj)); + NVIC_SetPriority(get_serial_irq_num(obj), 1); + NVIC_SetVector(get_serial_irq_num(obj), (uint32_t)handler); + NVIC_EnableIRQ(get_serial_irq_num(obj)); + + if (pUSART_S(obj)) { + _USART(obj).INTENCLR.reg = SERCOM_USART_INTFLAG_TXC; + _USART(obj).INTENSET.reg = SERCOM_USART_INTFLAG_DRE; + } +// } +} + +void serial_rx_asynch(serial_t *obj, void *rx, size_t rx_length, uint8_t rx_width, uint32_t handler, uint32_t event, uint8_t char_match, DMAUsage hint) +{ + MBED_ASSERT(rx != (void*)0); + + serial_rx_enable_event(obj, SERIAL_EVENT_RX_ALL, false); + serial_rx_enable_event(obj, event, true); + serial_set_char_match(obj, char_match); + + serial_rx_buffer_set(obj, rx, rx_length, rx_width); + +// if( hint == DMA_USAGE_NEVER) { //TODO: DMA to be implemented later + NVIC_ClearPendingIRQ(get_serial_irq_num(obj)); + NVIC_SetVector(get_serial_irq_num(obj), (uint32_t)handler); + NVIC_EnableIRQ(get_serial_irq_num(obj)); + + if (pUSART_S(obj)) { + _USART(obj).INTENSET.reg = SERCOM_USART_INTFLAG_RXC; + } +// } +} + +uint8_t serial_tx_active(serial_t *obj) +{ + return ((_USART(obj).INTENSET.reg & SERCOM_USART_INTFLAG_DRE) ? true : false); +} + +uint8_t serial_rx_active(serial_t *obj) +{ + return ((_USART(obj).INTENSET.reg & SERCOM_USART_INTFLAG_RXC) ? true : false); +} + +int serial_tx_irq_handler_asynch(serial_t *obj) +{ + _USART(obj).INTENCLR.reg = SERCOM_USART_INTFLAG_TXC; + return SERIAL_EVENT_TX_COMPLETE & obj->serial.events; +} + +int serial_rx_irq_handler_asynch(serial_t *obj) +{ + int event = 0; + /* This interrupt handler is called from USART irq */ + uint8_t *buf = (uint8_t*)obj->rx_buff.buffer; + uint8_t error_code = 0; + uint16_t received_data = 0; + + + error_code = (uint8_t)(_USART(obj).STATUS.reg & SERCOM_USART_STATUS_MASK); + + /* Check if an error has occurred during the receiving */ + if (error_code) { + /* Check which error occurred */ + if (error_code & SERCOM_USART_STATUS_FERR) { + /* Store the error code and clear flag by writing 1 to it */ + _USART(obj).STATUS.reg |= SERCOM_USART_STATUS_FERR; + return SERIAL_EVENT_RX_FRAMING_ERROR; + } else if (error_code & SERCOM_USART_STATUS_BUFOVF) { + /* Store the error code and clear flag by writing 1 to it */ + _USART(obj).STATUS.reg |= SERCOM_USART_STATUS_BUFOVF; + return SERIAL_EVENT_RX_OVERFLOW; + } else if (error_code & SERCOM_USART_STATUS_PERR) { + /* Store the error code and clear flag by writing 1 to it */ + _USART(obj).STATUS.reg |= SERCOM_USART_STATUS_PERR; + return SERIAL_EVENT_RX_PARITY_ERROR; + } + } + + /* Read current packet from DATA register, + * increment buffer pointer and decrement buffer length */ + received_data = (_USART(obj).DATA.reg & SERCOM_USART_DATA_MASK); + + /* Read value will be at least 8-bits long */ + buf[obj->rx_buff.pos] = received_data; + /* Increment 8-bit pointer */ + obj->rx_buff.pos++; + + /* Check if the last character have been received */ + if(--(obj->rx_buff.length) == 0) { + event |= SERIAL_EVENT_RX_COMPLETE; + if((buf[obj->rx_buff.pos - 1] == obj->char_match) && (obj->serial.events & SERIAL_EVENT_RX_CHARACTER_MATCH)) { + event |= SERIAL_EVENT_RX_CHARACTER_MATCH; + } + serial_rx_abort_asynch(obj); + return event & obj->serial.events; + } + + /* Check for character match event */ + if((buf[obj->rx_buff.pos - 1] == obj->char_match) && (obj->serial.events & SERIAL_EVENT_RX_CHARACTER_MATCH)) { + event |= SERIAL_EVENT_RX_CHARACTER_MATCH; + } + + /* check for final char event */ + if((obj->rx_buff.length) == 0) { + event |= SERIAL_EVENT_RX_COMPLETE & obj->serial.events; + } + + if(event != 0) { + serial_rx_abort_asynch(obj); + return event & obj->serial.events; + } +} + +int serial_irq_handler_asynch(serial_t *obj) +{ +//TODO: DMA to be implemented + uint16_t interrupt_status; + uint8_t *buf = obj->tx_buff.buffer; + + interrupt_status = _USART(obj).INTFLAG.reg; + interrupt_status &= _USART(obj).INTENSET.reg; + + if (pUSART_S(obj)) { + if (interrupt_status & SERCOM_USART_INTFLAG_DRE) { + /* Interrupt has another TX source */ + if(obj->tx_buff.pos >= obj->tx_buff.length) { + + /* Transfer complete. Switch off interrupt and return event. */ + _USART(obj).INTENCLR.reg = SERCOM_USART_INTFLAG_DRE; + serial_tx_abort_asynch(obj); + + return SERIAL_EVENT_TX_COMPLETE & obj->serial.events; + } else { + while((serial_writable(obj)) && (obj->tx_buff.pos <= (obj->tx_buff.length - 1))) { + _USART(obj).DATA.reg = buf[obj->tx_buff.pos]; + obj->tx_buff.pos++; + } + } + } + if (interrupt_status & SERCOM_USART_INTFLAG_TXC) { + serial_tx_irq_handler_asynch(obj); + } + if (interrupt_status & SERCOM_USART_INTFLAG_RXC) { + serial_rx_irq_handler_asynch(obj); + } + } + +} + +void serial_tx_abort_asynch(serial_t *obj) +{ +//TODO: DMA to be implemented + _USART(obj).INTENSET.reg = SERCOM_USART_INTFLAG_TXC; + obj->tx_buff.pos = 0; + obj->tx_buff.length = 0; +} + +void serial_rx_abort_asynch(serial_t *obj) +{ +//TODO: DMA to be implemented + _USART(obj).INTENCLR.reg = SERCOM_USART_INTFLAG_RXC; + obj->rx_buff.pos = 0; + obj->rx_buff.length = 0; +} + +#endif \ No newline at end of file