diff options
| author | Sebastian Plamauer <oeplse@gmail.com> | 2016-10-31 20:18:39 +0100 |
|---|---|---|
| committer | Scott Shawcroft <scott.shawcroft@gmail.com> | 2016-12-19 13:03:50 -0800 |
| commit | 1598e44231a9ea2dbf20f487fcd05ded90e80d9a (patch) | |
| tree | 2020360e16b0a59b3207838274b5159ac9fe3b73 /atmel-samd/common-hal/nativeio | |
| parent | af17b64a58a421342ebad876b81ab68b91c13473 (diff) | |
atmel-samd: Add preliminary support for UART
Diffstat (limited to 'atmel-samd/common-hal/nativeio')
| -rw-r--r-- | atmel-samd/common-hal/nativeio/SPI.c | 4 | ||||
| -rw-r--r-- | atmel-samd/common-hal/nativeio/UART.c | 368 | ||||
| -rw-r--r-- | atmel-samd/common-hal/nativeio/types.h | 16 |
3 files changed, 386 insertions, 2 deletions
diff --git a/atmel-samd/common-hal/nativeio/SPI.c b/atmel-samd/common-hal/nativeio/SPI.c index f31bae1e1..70b096579 100644 --- a/atmel-samd/common-hal/nativeio/SPI.c +++ b/atmel-samd/common-hal/nativeio/SPI.c @@ -125,12 +125,12 @@ void common_hal_nativeio_spi_construct(nativeio_spi_obj_t *self, &config_spi_master.pinmux_pad3}; *pinmuxes[clock_pad] = clock_pinmux; self->clock_pin = clock->pin; - self->MOSI_pin = 0; + self->MOSI_pin = NO_PIN; if (!mosi_none) { *pinmuxes[mosi_pad] = mosi_pinmux; self->MOSI_pin = mosi->pin; } - self->MISO_pin = 0; + self->MISO_pin = NO_PIN; if (!miso_none) { *pinmuxes[miso_pad] = miso_pinmux; self->MISO_pin = miso->pin; diff --git a/atmel-samd/common-hal/nativeio/UART.c b/atmel-samd/common-hal/nativeio/UART.c new file mode 100644 index 000000000..8fd4e0b4a --- /dev/null +++ b/atmel-samd/common-hal/nativeio/UART.c @@ -0,0 +1,368 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2016 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "shared-bindings/microcontroller/__init__.h" +#include "shared-bindings/nativeio/UART.h" + +#include "py/gc.h" +#include "py/mperrno.h" +#include "py/nlr.h" +#include "py/stream.h" +#include "samd21_pins.h" +#include "tick.h" + +#include "asf/sam0/drivers/sercom/sercom_interrupt.h" + +#undef ENABLE + +nativeio_uart_obj_t *_uart_instances[SERCOM_INST_NUM]; + +static void _sercom_default_handler( + const uint8_t instance) +{ + Assert(false); +} + +static void _nativeio_uart_interrupt_handler(uint8_t instance) +{ + /* Temporary variables */ + uint16_t interrupt_status; + uint8_t error_code; + + /* Get device instance from the look-up table */ + struct usart_module *module + = (struct usart_module *)_sercom_instances[instance]; + + nativeio_uart_obj_t *self = _uart_instances[instance]; + + /* Pointer to the hardware module instance */ + SercomUsart *const usart_hw = &(module->hw->USART); + + /* Wait for the synchronization to complete */ + _usart_wait_for_sync(module); + + /* 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); + /* CTS status should not be considered as an error */ + if(error_code & SERCOM_USART_STATUS_CTS) { + error_code &= ~SERCOM_USART_STATUS_CTS; + } + /* 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_hw->STATUS.reg = SERCOM_USART_STATUS_FERR; + } else if (error_code & SERCOM_USART_STATUS_BUFOVF) { + /* Store the error code and clear flag by writing 1 to it */ + usart_hw->STATUS.reg = SERCOM_USART_STATUS_BUFOVF; + } else if (error_code & SERCOM_USART_STATUS_PERR) { + /* Store the error code and clear flag by writing 1 to it */ + usart_hw->STATUS.reg = SERCOM_USART_STATUS_PERR; + } + self->rx_error = true; + } else { + /* Read current packet from DATA register, + * increment buffer pointer and decrement buffer length */ + uint16_t received_data = (usart_hw->DATA.reg & SERCOM_USART_DATA_MASK); + + common_hal_mcu_disable_interrupts(); + /* Read value will be at least 8-bits long */ + uint32_t buffer_end = (self->buffer_start + self->buffer_size) % self->buffer_length; + self->buffer[buffer_end] = received_data; + self->buffer_size++; + + if (module->character_size == USART_CHARACTER_SIZE_9BIT) { + /* 9-bit data, write next received byte to the buffer */ + self->buffer[buffer_end + 1] = (received_data >> 8); + self->buffer_size++; + } + + if (self->buffer_size > self->buffer_length) { + self->buffer_start++; + if (module->character_size == USART_CHARACTER_SIZE_9BIT) { + self->buffer_start++; + } + self->buffer_size = self->buffer_length; + } + common_hal_mcu_enable_interrupts(); + } + } +} + +void common_hal_nativeio_uart_construct(nativeio_uart_obj_t *self, + const mcu_pin_obj_t * tx, const mcu_pin_obj_t * rx, uint32_t baudrate, + uint8_t bits, uart_parity_t parity, uint8_t stop, uint32_t timeout, + uint8_t receiver_buffer_size) { + Sercom* sercom = NULL; + uint32_t rx_pinmux = PINMUX_UNUSED; + uint8_t rx_pad = 5; // Unset pad + uint32_t tx_pinmux = PINMUX_UNUSED; + uint8_t tx_pad = 5; // Unset pad + for (int i = 0; i < NUM_SERCOMS_PER_PIN; i++) { + Sercom* potential_sercom = NULL; + if (tx != NULL) { + potential_sercom = tx->sercom[i].sercom; + if (potential_sercom == NULL || + potential_sercom->I2CM.CTRLA.bit.ENABLE != 0 || + !(tx->sercom[i].pad == 0 || + tx->sercom[i].pad == 2)) { + continue; + } + tx_pinmux = PINMUX(tx->pin, (i == 0) ? MUX_C : MUX_D); + tx_pad = tx->sercom[i].pad; + if (rx == NULL) { + sercom = potential_sercom; + break; + } + } + for (int j = 0; j < NUM_SERCOMS_PER_PIN; j++) { + if (((tx == NULL && rx->sercom[j].sercom->I2CM.CTRLA.bit.ENABLE == 0) || + potential_sercom == rx->sercom[j].sercom) && + rx->sercom[j].pad != tx_pad) { + rx_pinmux = PINMUX(rx->pin, (j == 0) ? MUX_C : MUX_D); + rx_pad = rx->sercom[j].pad; + sercom = rx->sercom[j].sercom; + break; + } + } + if (sercom != NULL) { + break; + } + } + if (sercom == NULL) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, + "No hardware support available with those pins.")); + } + if (tx == NULL) { + tx_pad = 0; + if (rx_pad == 0) { + tx_pad = 2; + } + } + if (rx == NULL) { + rx_pad = (tx_pad + 1) % 4; + } + struct usart_config config_usart; + usart_get_config_defaults(&config_usart); + config_usart.mux_setting = (SERCOM_USART_CTRLA_RXPO(rx_pad) | SERCOM_USART_CTRLA_TXPO(tx_pad / 2)); + + if (parity == PARITY_ODD) { + config_usart.parity = USART_PARITY_ODD; + } else if (parity == PARITY_EVEN) { + config_usart.parity = USART_PARITY_EVEN; + } + config_usart.stopbits = stop - 1; + config_usart.character_size = bits % 8; + config_usart.baudrate = baudrate; + + // Map pad to pinmux through a short array. + uint32_t *pinmuxes[4] = {&config_usart.pinmux_pad0, + &config_usart.pinmux_pad1, + &config_usart.pinmux_pad2, + &config_usart.pinmux_pad3}; + // Pin muxes have a default pin, set them to unused so that no other pins are changed. + for (int i = 0; i < 4; i++) { + *pinmuxes[i] = PINMUX_UNUSED; + } + + self->rx_pin = NO_PIN; + config_usart.receiver_enable = rx != NULL; + if (rx != NULL) { + *pinmuxes[rx_pad] = rx_pinmux; + self->rx_pin = rx->pin; + } + + self->tx_pin = NO_PIN; + config_usart.transmitter_enable = tx != NULL; + if (tx != NULL) { + *pinmuxes[tx_pad] = tx_pinmux; + self->tx_pin = tx->pin; + } + + self->timeout_ms = timeout; + + self->buffer_length = receiver_buffer_size; + self->buffer_length *= (bits + 7) / 8; + self->buffer = (uint8_t *) gc_alloc(self->buffer_length * sizeof(uint8_t), false); + if (self->buffer == NULL) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, + "Unable to allocate RX buffer.")); + } + + if (usart_init(&self->uart_instance, sercom, &config_usart) != STATUS_OK) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "Unable to init UART")); + } + + // We use our own interrupt handler because we want a circular buffer + // instead of the jobs that ASF provides. + uint8_t instance_index = _sercom_get_sercom_inst_index(self->uart_instance.hw); + _sercom_set_handler(instance_index, _nativeio_uart_interrupt_handler); + _sercom_instances[instance_index] = &self->uart_instance; + _uart_instances[instance_index] = self; + + /* Enable Global interrupt for module */ + system_interrupt_enable(_sercom_get_interrupt_vector(self->uart_instance.hw)); + + usart_enable(&self->uart_instance); + self->uart_instance.hw->USART.INTENSET.bit.RXC = true; +} + +void common_hal_nativeio_uart_deinit(nativeio_uart_obj_t *self) { + self->uart_instance.hw->USART.INTENCLR.bit.RXC = true; + + uint8_t instance_index = _sercom_get_sercom_inst_index(self->uart_instance.hw); + _sercom_set_handler(instance_index, &_sercom_default_handler); + _sercom_instances[instance_index] = NULL; + _uart_instances[instance_index] = NULL; + + system_interrupt_disable(_sercom_get_interrupt_vector(self->uart_instance.hw)); + + usart_disable(&self->uart_instance); + reset_pin(self->rx_pin); + reset_pin(self->tx_pin); +} + +// Read characters. +size_t common_hal_nativeio_uart_read(nativeio_uart_obj_t *self, uint8_t *data, size_t len, int *errcode) { + size_t total_read = 0; + uint64_t start_ticks = ticks_ms; + while (total_read < len && ticks_ms - start_ticks < self->timeout_ms) { + if (self->buffer_size > 0) { + common_hal_mcu_disable_interrupts(); + data[total_read] = self->buffer[self->buffer_start]; + if (self->uart_instance.character_size == USART_CHARACTER_SIZE_9BIT) { + data[total_read + 1] = self->buffer[self->buffer_start + 1]; + self->buffer_start += 2; + self->buffer_size -= 2; + } else { + self->buffer_start++; + self->buffer_size--; + } + self->buffer_start = self->buffer_start % self->buffer_length; + common_hal_mcu_enable_interrupts(); + // Reset the timeout every character read. + total_read++; + start_ticks = ticks_ms; + } + } + if (total_read == 0) { + *errcode = MP_EAGAIN; + return MP_STREAM_ERROR; + } + return total_read; +} + +// Write characters. +size_t common_hal_nativeio_uart_write(nativeio_uart_obj_t *self, const uint8_t *data, size_t len, int *errcode) { + /* Check that the transmitter is enabled */ + if (!(self->uart_instance.transmitter_enabled)) { + *errcode = MP_EIO; + return MP_STREAM_ERROR; + } + + /* Get a pointer to the hardware module instance */ + SercomUsart *const usart_hw = &(self->uart_instance.hw->USART); + + /* Wait until synchronization is complete */ + _usart_wait_for_sync(&self->uart_instance); + + uint16_t tx_pos = 0; + + bool ok = true; + uint64_t start_ticks = 0; + /* Blocks while buffer is being transferred */ + while (len--) { + /* Wait for the USART to be ready for new data and abort + * operation if it doesn't get ready within the timeout*/ + ok = false; + start_ticks = ticks_ms; + while (ticks_ms - start_ticks < self->timeout_ms) { + if (usart_hw->INTFLAG.reg & SERCOM_USART_INTFLAG_DRE) { + ok = true; + break; + } + } + + if (!ok) { + break; + } + + /* Data to send is at least 8 bits long */ + uint16_t data_to_send = data[tx_pos++]; + + /* Check if the character size exceeds 8 bit */ + if (self->uart_instance.character_size == USART_CHARACTER_SIZE_9BIT) { + data_to_send |= (data[tx_pos++] << 8); + } + + /* Send the data through the USART module */ + + enum status_code status = usart_write_wait(&self->uart_instance, data_to_send); + if (status != STATUS_OK) { + ok = false; + } + } + + /* Wait until Transmit is complete or timeout */ + if (ok) { + ok = false; + start_ticks = ticks_ms; + while (ticks_ms - start_ticks < self->timeout_ms) { + if (usart_hw->INTFLAG.reg & SERCOM_USART_INTFLAG_TXC) { + ok = true; + break; + } + } + } + + if (!ok && tx_pos == 0) { + *errcode = MP_EAGAIN; + return MP_STREAM_ERROR; + } + return tx_pos; +} + +uint32_t common_hal_nativeio_uart_rx_characters_available(nativeio_uart_obj_t *self) { + if (self->uart_instance.character_size == USART_CHARACTER_SIZE_9BIT) { + return self->buffer_size / 2; + } + return self->buffer_size; +} + +bool common_hal_nativeio_uart_ready_to_tx(nativeio_uart_obj_t *self) { + if (!(self->uart_instance.transmitter_enabled)) { + return false; + } + return self->uart_instance.hw->USART.INTFLAG.bit.DRE; +} diff --git a/atmel-samd/common-hal/nativeio/types.h b/atmel-samd/common-hal/nativeio/types.h index 365345a2e..9f83effb1 100644 --- a/atmel-samd/common-hal/nativeio/types.h +++ b/atmel-samd/common-hal/nativeio/types.h @@ -42,6 +42,7 @@ #include "asf/sam0/drivers/dac/dac.h" #include "asf/sam0/drivers/sercom/i2c/i2c_master.h" #include "asf/sam0/drivers/sercom/spi/spi.h" +#include "asf/sam0/drivers/sercom/usart/usart.h" #include "asf/sam0/drivers/tc/tc.h" #include "asf/sam0/drivers/tcc/tcc.h" // Only support TouchIn when external SPI flash is used. @@ -103,4 +104,19 @@ typedef struct { #endif } nativeio_touchin_obj_t; +typedef struct { + mp_obj_base_t base; + struct usart_module uart_instance; + uint8_t rx_pin; + uint8_t tx_pin; + uint32_t timeout_ms; + bool rx_error; + // Index of the oldest received character. + uint32_t buffer_start; + // Index of the next available spot to store a character. + uint32_t buffer_size; + uint32_t buffer_length; + uint8_t* buffer; +} nativeio_uart_obj_t; + #endif // __MICROPY_INCLUDED_ATMEL_SAMD_COMMON_HAL_NATIVEIO_TYPES_H__ |
