From 9b4477e1dc088d18127838310b828514cb33edea Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Wed, 21 Feb 2018 16:30:26 -0500 Subject: [PATCH] Implement UART for 3.0 + related fixes. 1. UART: ported to ASF4. Allow rx-only and tx-only. Add .baudrate r/w property. 2. Make NeoPixel timing deterministic by turning off caches during NeoPixel writes. 3. Incorporate asf4 updates: a. async USART driver b. bringing Atmel START configuration closer to what we use c. Clock initialization order now specified by CIRCUITPY_GCLK_INIT_1ST and _LAST. 4. supervisor/port.c: Move commented-out clock-test pin setting to correct location. --- ports/atmel-samd/Makefile | 6 +- ports/atmel-samd/asf4 | 2 +- .../asf4_conf/samd21/hpl_gclk_config.h | 11 + .../asf4_conf/samd21/hpl_sercom_config.h | 188 ++++++- .../asf4_conf/samd21/peripheral_clk_config.h | 166 ++----- .../asf4_conf/samd51/hpl_gclk_config.h | 18 +- .../asf4_conf/samd51/hpl_sercom_config.h | 195 +++++++- .../asf4_conf/samd51/peripheral_clk_config.h | 140 ++++-- .../atmel-samd/boards/metro_m4_express/pins.c | 2 + ports/atmel-samd/common-hal/busio/SPI.c | 2 + ports/atmel-samd/common-hal/busio/UART.c | 463 ++++++++---------- ports/atmel-samd/common-hal/busio/UART.h | 29 +- .../common-hal/neopixel_write/__init__.c | 32 +- ports/atmel-samd/supervisor/port.c | 38 +- shared-bindings/busio/UART.c | 39 +- shared-bindings/busio/UART.h | 4 + shared-bindings/busio/__init__.c | 8 +- 17 files changed, 875 insertions(+), 468 deletions(-) diff --git a/ports/atmel-samd/Makefile b/ports/atmel-samd/Makefile index a5d1fd7c0e..4660d575ec 100644 --- a/ports/atmel-samd/Makefile +++ b/ports/atmel-samd/Makefile @@ -36,6 +36,7 @@ INC += -I. \ -Iasf4/$(CHIP_FAMILY)/hal/utils/include \ -Iasf4/$(CHIP_FAMILY)/hri \ -Iasf4/$(CHIP_FAMILY)/hpl/core \ + -Iasf4/$(CHIP_FAMILY)/hpl/gclk \ -Iasf4/$(CHIP_FAMILY)/hpl/pm \ -Iasf4/$(CHIP_FAMILY)/hpl/port \ -Iasf4/$(CHIP_FAMILY)/hpl/tc \ @@ -96,6 +97,7 @@ ifeq ($(DEBUG), 1) # Turn on Python modules useful for debugging (e.g. uheap, ustack). CFLAGS += -ggdb CFLAGS += -flto + ## CFLAGS += -fno-inline ifeq ($(CHIP_FAMILY), samd21) CFLAGS += -DENABLE_MICRO_TRACE_BUFFER endif @@ -178,6 +180,7 @@ SRC_ASF := \ hal/src/hal_sleep.c \ hal/src/hal_spi_m_sync.c \ hal/src/hal_timer.c \ + hal/src/hal_usart_async.c \ hal/src/hal_usb_device.c \ hpl/adc/hpl_adc.c \ hpl/core/hpl_init.c \ @@ -194,6 +197,7 @@ SRC_ASF := \ usb/device/usbdc.c \ usb/usb_protocol.c \ hal/utils/src/utils_list.c \ + hal/utils/src/utils_ringbuffer.c \ ifeq ($(CHIP_FAMILY), samd21) SRC_ASF += \ @@ -263,6 +267,7 @@ SRC_COMMON_HAL = \ busio/__init__.c \ busio/I2C.c \ busio/SPI.c \ + busio/UART.c \ digitalio/__init__.c \ digitalio/DigitalInOut.c \ microcontroller/__init__.c \ @@ -283,7 +288,6 @@ SRC_COMMON_HAL = \ audiobusio/PDMIn.c \ audioio/__init__.c \ audioio/AudioOut.c \ - busio/UART.c \ nvm/__init__.c \ nvm/ByteArray.c \ touchio/__init__.c \ diff --git a/ports/atmel-samd/asf4 b/ports/atmel-samd/asf4 index 0efc3407dd..aaa0f42811 160000 --- a/ports/atmel-samd/asf4 +++ b/ports/atmel-samd/asf4 @@ -1 +1 @@ -Subproject commit 0efc3407dd97ef617a5655674a3516693897a961 +Subproject commit aaa0f428111fbea7d56ab548053b11c9f12068f1 diff --git a/ports/atmel-samd/asf4_conf/samd21/hpl_gclk_config.h b/ports/atmel-samd/asf4_conf/samd21/hpl_gclk_config.h index fa9bb661cc..90df195cd8 100644 --- a/ports/atmel-samd/asf4_conf/samd21/hpl_gclk_config.h +++ b/ports/atmel-samd/asf4_conf/samd21/hpl_gclk_config.h @@ -1,3 +1,14 @@ +// Circuit Python SAMD21 clock tree: +// DFLL48M (with USBCRM on to sync with external USB ref) -> GCLK0 +// GCLK0 (48MHz) -> peripherals + +// We'd like to use XOSC32K as a ref for DFLL48M on boards with a 32kHz crystal, +// but haven't figured that out yet. + +// Used in hpl/core/hpl_init.c to define which clocks should be initialized first. +#define CIRCUITPY_GCLK_INIT_1ST (1 << 0) + + /* Auto-generated config file hpl_gclk_config.h */ #ifndef HPL_GCLK_CONFIG_H #define HPL_GCLK_CONFIG_H diff --git a/ports/atmel-samd/asf4_conf/samd21/hpl_sercom_config.h b/ports/atmel-samd/asf4_conf/samd21/hpl_sercom_config.h index 91141bcfcf..85d05fc504 100644 --- a/ports/atmel-samd/asf4_conf/samd21/hpl_sercom_config.h +++ b/ports/atmel-samd/asf4_conf/samd21/hpl_sercom_config.h @@ -3,14 +3,16 @@ // // SERCOM0: SPI with hal_spi_m_sync.c driver: spi master synchronous // SERCOM1: I2C with hal_i2c_m_sync.c driver: i2c master synchronous -// SERCOM2: USART with hal_usart_sync.c driver: usart synchronous +// SERCOM2: USART with hal_usart_async.c driver: usart asynchronous +// SERCOM3: SPI with hal_spi_m_dma.c: spi master DMA #define PROTOTYPE_SERCOM_SPI_M_SYNC SERCOM0 #define PROTOTYPE_SERCOM_SPI_M_SYNC_CLOCK_FREQUENCY CONF_GCLK_SERCOM0_CORE_FREQUENCY #define PROTOTYPE_SERCOM_I2CM_SYNC SERCOM1 -#define PROTOTYPE_SERCOM_USART_SYNC SERCOM2 +#define PROTOTYPE_SERCOM_USART_ASYNC SERCOM2 +#define PROTOTYPE_SERCOM_USART_ASYNC_CLOCK_FREQUENCY CONF_GCLK_SERCOM2_CORE_FREQUENCY /* Auto-generated config file hpl_sercom_config.h */ #ifndef HPL_SERCOM_CONFIG_H @@ -543,6 +545,188 @@ #endif #endif +#include + +// Enable configuration of module +#ifndef CONF_SERCOM_3_SPI_ENABLE +#define CONF_SERCOM_3_SPI_ENABLE 1 +#endif + +// SPI DMA TX Channel <0-32> +// This defines DMA channel to be used +// spi_master_dma_tx_channel +#ifndef CONF_SERCOM_3_SPI_M_DMA_TX_CHANNEL +#define CONF_SERCOM_3_SPI_M_DMA_TX_CHANNEL 0 +#endif + +// SPI RX Channel Enable +// spi_master_rx_channel +#ifndef CONF_SERCOM_3_SPI_RX_CHANNEL +#define CONF_SERCOM_3_SPI_RX_CHANNEL 1 +#endif + +// DMA Channel <0-32> +// This defines DMA channel to be used +// spi_master_dma_rx_channel +#ifndef CONF_SERCOM_3_SPI_M_DMA_RX_CHANNEL +#define CONF_SERCOM_3_SPI_M_DMA_RX_CHANNEL 1 +#endif + +// + +// Set module in SPI Master mode +#ifndef CONF_SERCOM_3_SPI_MODE +#define CONF_SERCOM_3_SPI_MODE 0x03 +#endif + +// Basic Configuration + +// Receive buffer enable +// Enable receive buffer to receive data from slave (RXEN) +// spi_master_rx_enable +#ifndef CONF_SERCOM_3_SPI_RXEN +#define CONF_SERCOM_3_SPI_RXEN 0x1 +#endif + +// Character Size +// Bit size for all characters sent over the SPI bus (CHSIZE) +// <0x0=>8 bits +// <0x1=>9 bits +// spi_master_character_size +#ifndef CONF_SERCOM_3_SPI_CHSIZE +#define CONF_SERCOM_3_SPI_CHSIZE 0x0 +#endif + +// Baud rate <1-12000000> +// The SPI data transfer rate +// spi_master_baud_rate +#ifndef CONF_SERCOM_3_SPI_BAUD +#define CONF_SERCOM_3_SPI_BAUD 50000 +#endif + +// + +// Advanced Configuration +// spi_master_advanced +#ifndef CONF_SERCOM_3_SPI_ADVANCED +#define CONF_SERCOM_3_SPI_ADVANCED 0 +#endif + +// Dummy byte <0x00-0x1ff> +// spi_master_dummybyte +// Dummy byte used when reading data from the slave without sending any data +#ifndef CONF_SERCOM_3_SPI_DUMMYBYTE +#define CONF_SERCOM_3_SPI_DUMMYBYTE 0x1ff +#endif + +// Data Order +// <0=>MSB first +// <1=>LSB first +// I least significant or most significant bit is shifted out first (DORD) +// spi_master_arch_dord +#ifndef CONF_SERCOM_3_SPI_DORD +#define CONF_SERCOM_3_SPI_DORD 0x0 +#endif + +// Clock Polarity +// <0=>SCK is low when idle +// <1=>SCK is high when idle +// Determines if the leading edge is rising or falling with a corresponding opposite edge at the trailing edge. (CPOL) +// spi_master_arch_cpol +#ifndef CONF_SERCOM_3_SPI_CPOL +#define CONF_SERCOM_3_SPI_CPOL 0x0 +#endif + +// Clock Phase +// <0x0=>Sample input on leading edge +// <0x1=>Sample input on trailing edge +// Determines if input data is sampled on leading or trailing SCK edge. (CPHA) +// spi_master_arch_cpha +#ifndef CONF_SERCOM_3_SPI_CPHA +#define CONF_SERCOM_3_SPI_CPHA 0x0 +#endif + +// Immediate Buffer Overflow Notification +// Controls when OVF is asserted (IBON) +// <0x0=>In data stream +// <0x1=>On buffer overflow +// spi_master_arch_ibon +#ifndef CONF_SERCOM_3_SPI_IBON +#define CONF_SERCOM_3_SPI_IBON 0x0 +#endif + +// Run in stand-by +// Module stays active in stand-by sleep mode. (RUNSTDBY) +// spi_master_arch_runstdby +#ifndef CONF_SERCOM_3_SPI_RUNSTDBY +#define CONF_SERCOM_3_SPI_RUNSTDBY 0x0 +#endif + +// Debug Stop Mode +// Behavior of the baud-rate generator when CPU is halted by external debugger. (DBGSTOP) +// <0=>Keep running +// <1=>Halt +// spi_master_arch_dbgstop +#ifndef CONF_SERCOM_3_SPI_DBGSTOP +#define CONF_SERCOM_3_SPI_DBGSTOP 0 +#endif + +// + +// Address mode disabled in master mode +#ifndef CONF_SERCOM_3_SPI_AMODE_EN +#define CONF_SERCOM_3_SPI_AMODE_EN 0 +#endif + +#ifndef CONF_SERCOM_3_SPI_AMODE +#define CONF_SERCOM_3_SPI_AMODE 0 +#endif + +#ifndef CONF_SERCOM_3_SPI_ADDR +#define CONF_SERCOM_3_SPI_ADDR 0 +#endif + +#ifndef CONF_SERCOM_3_SPI_ADDRMASK +#define CONF_SERCOM_3_SPI_ADDRMASK 0 +#endif + +#ifndef CONF_SERCOM_3_SPI_SSDE +#define CONF_SERCOM_3_SPI_SSDE 0 +#endif + +#ifndef CONF_SERCOM_3_SPI_MSSEN +#define CONF_SERCOM_3_SPI_MSSEN 0x0 +#endif + +#ifndef CONF_SERCOM_3_SPI_PLOADEN +#define CONF_SERCOM_3_SPI_PLOADEN 0 +#endif + +// Receive Data Pinout +// <0x0=>PAD[0] +// <0x1=>PAD[1] +// <0x2=>PAD[2] +// <0x3=>PAD[3] +// spi_master_rxpo +#ifndef CONF_SERCOM_3_SPI_RXPO +#define CONF_SERCOM_3_SPI_RXPO 0 +#endif + +// Transmit Data Pinout +// <0x0=>PAD[0,1]_DO_SCK +// <0x1=>PAD[2,3]_DO_SCK +// <0x2=>PAD[3,1]_DO_SCK +// <0x3=>PAD[0,3]_DO_SCK +// spi_master_txpo +#ifndef CONF_SERCOM_3_SPI_TXPO +#define CONF_SERCOM_3_SPI_TXPO 1 +#endif + +// Calculate baud register value from requested baudrate value +#ifndef CONF_SERCOM_3_SPI_BAUD_RATE +#define CONF_SERCOM_3_SPI_BAUD_RATE ((float)CONF_GCLK_SERCOM3_CORE_FREQUENCY / (float)(2 * CONF_SERCOM_3_SPI_BAUD)) - 1 +#endif + // <<< end of configuration section >>> #endif // HPL_SERCOM_CONFIG_H diff --git a/ports/atmel-samd/asf4_conf/samd21/peripheral_clk_config.h b/ports/atmel-samd/asf4_conf/samd21/peripheral_clk_config.h index b8080d059c..84fc6b9e4a 100644 --- a/ports/atmel-samd/asf4_conf/samd21/peripheral_clk_config.h +++ b/ports/atmel-samd/asf4_conf/samd21/peripheral_clk_config.h @@ -4,6 +4,38 @@ // <<< Use Configuration Wizard in Context Menu >>> +// ADC Clock Source +// adc_gclk_selection + +// Generic clock generator 0 + +// Generic clock generator 1 + +// Generic clock generator 2 + +// Generic clock generator 3 + +// Generic clock generator 4 + +// Generic clock generator 5 + +// Generic clock generator 6 + +// Generic clock generator 7 + +// Select the clock source for ADC. +#ifndef CONF_GCLK_ADC_SRC +#define CONF_GCLK_ADC_SRC GCLK_CLKCTRL_GEN_GCLK0_Val +#endif + +/** + * \def CONF_GCLK_ADC_FREQUENCY + * \brief ADC's Clock frequency + */ +#ifndef CONF_GCLK_ADC_FREQUENCY +#define CONF_GCLK_ADC_FREQUENCY 48000000 +#endif + /** * \def CONF_CPU_FREQUENCY * \brief CPU's Clock frequency @@ -268,134 +300,6 @@ #define CONF_GCLK_SERCOM3_SLOW_FREQUENCY 400000 #endif -// Core Clock Source -// core_gclk_selection - -// Generic clock generator 0 - -// Generic clock generator 1 - -// Generic clock generator 2 - -// Generic clock generator 3 - -// Generic clock generator 4 - -// Generic clock generator 5 - -// Generic clock generator 6 - -// Generic clock generator 7 - -// Select the clock source for CORE. -#ifndef CONF_GCLK_SERCOM4_CORE_SRC -#define CONF_GCLK_SERCOM4_CORE_SRC GCLK_CLKCTRL_GEN_GCLK0_Val -#endif - -// Slow Clock Source -// slow_gclk_selection - -// Generic clock generator 0 - -// Generic clock generator 1 - -// Generic clock generator 2 - -// Generic clock generator 3 - -// Generic clock generator 4 - -// Generic clock generator 5 - -// Generic clock generator 6 - -// Generic clock generator 7 - -// Select the slow clock source. -#ifndef CONF_GCLK_SERCOM4_SLOW_SRC -#define CONF_GCLK_SERCOM4_SLOW_SRC GCLK_CLKCTRL_GEN_GCLK3_Val -#endif - -/** - * \def CONF_GCLK_SERCOM4_CORE_FREQUENCY - * \brief SERCOM4's Core Clock frequency - */ -#ifndef CONF_GCLK_SERCOM4_CORE_FREQUENCY -#define CONF_GCLK_SERCOM4_CORE_FREQUENCY 48000000 -#endif - -/** - * \def CONF_GCLK_SERCOM4_SLOW_FREQUENCY - * \brief SERCOM4's Slow Clock frequency - */ -#ifndef CONF_GCLK_SERCOM4_SLOW_FREQUENCY -#define CONF_GCLK_SERCOM4_SLOW_FREQUENCY 400000 -#endif - -// Core Clock Source -// core_gclk_selection - -// Generic clock generator 0 - -// Generic clock generator 1 - -// Generic clock generator 2 - -// Generic clock generator 3 - -// Generic clock generator 4 - -// Generic clock generator 5 - -// Generic clock generator 6 - -// Generic clock generator 7 - -// Select the clock source for CORE. -#ifndef CONF_GCLK_SERCOM5_CORE_SRC -#define CONF_GCLK_SERCOM5_CORE_SRC GCLK_CLKCTRL_GEN_GCLK0_Val -#endif - -// Slow Clock Source -// slow_gclk_selection - -// Generic clock generator 0 - -// Generic clock generator 1 - -// Generic clock generator 2 - -// Generic clock generator 3 - -// Generic clock generator 4 - -// Generic clock generator 5 - -// Generic clock generator 6 - -// Generic clock generator 7 - -// Select the slow clock source. -#ifndef CONF_GCLK_SERCOM5_SLOW_SRC -#define CONF_GCLK_SERCOM5_SLOW_SRC GCLK_CLKCTRL_GEN_GCLK3_Val -#endif - -/** - * \def CONF_GCLK_SERCOM5_CORE_FREQUENCY - * \brief SERCOM5's Core Clock frequency - */ -#ifndef CONF_GCLK_SERCOM5_CORE_FREQUENCY -#define CONF_GCLK_SERCOM5_CORE_FREQUENCY 48000000 -#endif - -/** - * \def CONF_GCLK_SERCOM5_SLOW_FREQUENCY - * \brief SERCOM5's Slow Clock frequency - */ -#ifndef CONF_GCLK_SERCOM5_SLOW_FREQUENCY -#define CONF_GCLK_SERCOM5_SLOW_FREQUENCY 400000 -#endif - // RTC Clock Source // rtc_clk_selection @@ -425,7 +329,7 @@ * \brief RTC's Clock frequency */ #ifndef CONF_GCLK_RTC_FREQUENCY -#define CONF_GCLK_RTC_FREQUENCY 1000000 +#define CONF_GCLK_RTC_FREQUENCY 48000000 #endif // TC Clock Source @@ -457,7 +361,7 @@ * \brief TC3's Clock frequency */ #ifndef CONF_GCLK_TC3_FREQUENCY -#define CONF_GCLK_TC3_FREQUENCY 1000000 +#define CONF_GCLK_TC3_FREQUENCY 48000000 #endif // DAC Clock Source @@ -489,7 +393,7 @@ * \brief DAC's Clock frequency */ #ifndef CONF_GCLK_DAC_FREQUENCY -#define CONF_GCLK_DAC_FREQUENCY 1000000 +#define CONF_GCLK_DAC_FREQUENCY 48000000 #endif // USB Clock Source diff --git a/ports/atmel-samd/asf4_conf/samd51/hpl_gclk_config.h b/ports/atmel-samd/asf4_conf/samd51/hpl_gclk_config.h index 9a0081c79b..300db2716d 100644 --- a/ports/atmel-samd/asf4_conf/samd51/hpl_gclk_config.h +++ b/ports/atmel-samd/asf4_conf/samd51/hpl_gclk_config.h @@ -1,7 +1,17 @@ -// The clock tree starts with 48mhz DFLL48M based on USB. GCLK5 divides it down -// to 2mhz which DPLL0 boosts to 120mhz. This is then used by GCLK0 to clock the -// core and main bus. GCLK1 is 48mhz based on DFLL48M which is used for USB. -// GCLK4 also outputs the 120mhz clock for monitoring. +// Circuit Python SAMD51 clock tree: +// DFLL48M (with USBCRM on to sync with external USB ref) -> GCLK1, GCLK5 +// GCLK1 (48MHz) -> peripherals +// GCLK5 (divided down to 2 MHz) -> DPLL0 +// DPLL0 (multiplied up to 120 MHz) -> GCLK0, GCLK4 (output for monitoring) + +// We'd like to use XOSC32K as a ref for DFLL48M on boards with a 32kHz crystal, +// but haven't figured that out yet. + +// Used in hpl/core/hpl_init.c to define which clocks should be initialized first. +// Not clear why all these need to be specified, but it doesn't work properly otherwise. + +//#define CIRCUITPY_GCLK_INIT_1ST (1 << 0 | 1 << 1 | 1 << 3 | 1 <<5) +#define CIRCUITPY_GCLK_INIT_1ST 0xffff /* Auto-generated config file hpl_gclk_config.h */ #ifndef HPL_GCLK_CONFIG_H diff --git a/ports/atmel-samd/asf4_conf/samd51/hpl_sercom_config.h b/ports/atmel-samd/asf4_conf/samd51/hpl_sercom_config.h index 1830c54900..cd411154c7 100644 --- a/ports/atmel-samd/asf4_conf/samd51/hpl_sercom_config.h +++ b/ports/atmel-samd/asf4_conf/samd51/hpl_sercom_config.h @@ -3,13 +3,16 @@ // // SERCOM0: SPI with hal_spi_m_sync.c driver: spi master synchronous // SERCOM1: I2C with hal_i2c_m_sync.c driver: i2c master synchronous -// SERCOM2: USART with hal_usart_sync.c driver: usart synchronous +// SERCOM2: USART with hal_usart_async.c driver: usart asynchronous +// SERCOM3: SPI with hal_spi_m_dma.c: spi master DMA #define PROTOTYPE_SERCOM_SPI_M_SYNC SERCOM0 #define PROTOTYPE_SERCOM_SPI_M_SYNC_CLOCK_FREQUENCY CONF_GCLK_SERCOM0_CORE_FREQUENCY #define PROTOTYPE_SERCOM_I2CM_SYNC SERCOM1 -#define PROTOTYPE_SERCOM_USART_SYNC SERCOM2 + +#define PROTOTYPE_SERCOM_USART_ASYNC SERCOM2 +#define PROTOTYPE_SERCOM_USART_ASYNC_CLOCK_FREQUENCY CONF_GCLK_SERCOM2_CORE_FREQUENCY /* Auto-generated config file hpl_sercom_config.h */ #ifndef HPL_SERCOM_CONFIG_H @@ -59,7 +62,7 @@ // Advanced Configuration // spi_master_advanced #ifndef CONF_SERCOM_0_SPI_ADVANCED -#define CONF_SERCOM_0_SPI_ADVANCED 0 +#define CONF_SERCOM_0_SPI_ADVANCED 1 #endif // Dummy byte <0x00-0x1ff> @@ -201,7 +204,7 @@ // Advanced // i2c_master_advanced #ifndef CONF_SERCOM_1_I2CM_ADVANCED_CONFIG -#define CONF_SERCOM_1_I2CM_ADVANCED_CONFIG 0 +#define CONF_SERCOM_1_I2CM_ADVANCED_CONFIG 1 #endif // TRise (ns) <0-300> @@ -377,7 +380,7 @@ // Advanced configuration // usart_advanced #ifndef CONF_SERCOM_2_USART_ADVANCED_CONFIG -#define CONF_SERCOM_2_USART_ADVANCED_CONFIG 0 +#define CONF_SERCOM_2_USART_ADVANCED_CONFIG 1 #endif // Run in stand-by @@ -561,6 +564,188 @@ #endif #endif +#include + +// Enable configuration of module +#ifndef CONF_SERCOM_3_SPI_ENABLE +#define CONF_SERCOM_3_SPI_ENABLE 1 +#endif + +// SPI DMA TX Channel <0-32> +// This defines DMA channel to be used +// spi_master_dma_tx_channel +#ifndef CONF_SERCOM_3_SPI_M_DMA_TX_CHANNEL +#define CONF_SERCOM_3_SPI_M_DMA_TX_CHANNEL 0 +#endif + +// SPI RX Channel Enable +// spi_master_rx_channel +#ifndef CONF_SERCOM_3_SPI_RX_CHANNEL +#define CONF_SERCOM_3_SPI_RX_CHANNEL 1 +#endif + +// DMA Channel <0-32> +// This defines DMA channel to be used +// spi_master_dma_rx_channel +#ifndef CONF_SERCOM_3_SPI_M_DMA_RX_CHANNEL +#define CONF_SERCOM_3_SPI_M_DMA_RX_CHANNEL 1 +#endif + +// + +// Set module in SPI Master mode +#ifndef CONF_SERCOM_3_SPI_MODE +#define CONF_SERCOM_3_SPI_MODE 0x03 +#endif + +// Basic Configuration + +// Receive buffer enable +// Enable receive buffer to receive data from slave (RXEN) +// spi_master_rx_enable +#ifndef CONF_SERCOM_3_SPI_RXEN +#define CONF_SERCOM_3_SPI_RXEN 0x1 +#endif + +// Character Size +// Bit size for all characters sent over the SPI bus (CHSIZE) +// <0x0=>8 bits +// <0x1=>9 bits +// spi_master_character_size +#ifndef CONF_SERCOM_3_SPI_CHSIZE +#define CONF_SERCOM_3_SPI_CHSIZE 0x0 +#endif + +// Baud rate <1-12000000> +// The SPI data transfer rate +// spi_master_baud_rate +#ifndef CONF_SERCOM_3_SPI_BAUD +#define CONF_SERCOM_3_SPI_BAUD 50000 +#endif + +// + +// Advanced Configuration +// spi_master_advanced +#ifndef CONF_SERCOM_3_SPI_ADVANCED +#define CONF_SERCOM_3_SPI_ADVANCED 0 +#endif + +// Dummy byte <0x00-0x1ff> +// spi_master_dummybyte +// Dummy byte used when reading data from the slave without sending any data +#ifndef CONF_SERCOM_3_SPI_DUMMYBYTE +#define CONF_SERCOM_3_SPI_DUMMYBYTE 0x1ff +#endif + +// Data Order +// <0=>MSB first +// <1=>LSB first +// I least significant or most significant bit is shifted out first (DORD) +// spi_master_arch_dord +#ifndef CONF_SERCOM_3_SPI_DORD +#define CONF_SERCOM_3_SPI_DORD 0x0 +#endif + +// Clock Polarity +// <0=>SCK is low when idle +// <1=>SCK is high when idle +// Determines if the leading edge is rising or falling with a corresponding opposite edge at the trailing edge. (CPOL) +// spi_master_arch_cpol +#ifndef CONF_SERCOM_3_SPI_CPOL +#define CONF_SERCOM_3_SPI_CPOL 0x0 +#endif + +// Clock Phase +// <0x0=>Sample input on leading edge +// <0x1=>Sample input on trailing edge +// Determines if input data is sampled on leading or trailing SCK edge. (CPHA) +// spi_master_arch_cpha +#ifndef CONF_SERCOM_3_SPI_CPHA +#define CONF_SERCOM_3_SPI_CPHA 0x0 +#endif + +// Immediate Buffer Overflow Notification +// Controls when OVF is asserted (IBON) +// <0x0=>In data stream +// <0x1=>On buffer overflow +// spi_master_arch_ibon +#ifndef CONF_SERCOM_3_SPI_IBON +#define CONF_SERCOM_3_SPI_IBON 0x0 +#endif + +// Run in stand-by +// Module stays active in stand-by sleep mode. (RUNSTDBY) +// spi_master_arch_runstdby +#ifndef CONF_SERCOM_3_SPI_RUNSTDBY +#define CONF_SERCOM_3_SPI_RUNSTDBY 0x0 +#endif + +// Debug Stop Mode +// Behavior of the baud-rate generator when CPU is halted by external debugger. (DBGSTOP) +// <0=>Keep running +// <1=>Halt +// spi_master_arch_dbgstop +#ifndef CONF_SERCOM_3_SPI_DBGSTOP +#define CONF_SERCOM_3_SPI_DBGSTOP 0 +#endif + +// + +// Address mode disabled in master mode +#ifndef CONF_SERCOM_3_SPI_AMODE_EN +#define CONF_SERCOM_3_SPI_AMODE_EN 0 +#endif + +#ifndef CONF_SERCOM_3_SPI_AMODE +#define CONF_SERCOM_3_SPI_AMODE 0 +#endif + +#ifndef CONF_SERCOM_3_SPI_ADDR +#define CONF_SERCOM_3_SPI_ADDR 0 +#endif + +#ifndef CONF_SERCOM_3_SPI_ADDRMASK +#define CONF_SERCOM_3_SPI_ADDRMASK 0 +#endif + +#ifndef CONF_SERCOM_3_SPI_SSDE +#define CONF_SERCOM_3_SPI_SSDE 0 +#endif + +#ifndef CONF_SERCOM_3_SPI_MSSEN +#define CONF_SERCOM_3_SPI_MSSEN 0x0 +#endif + +#ifndef CONF_SERCOM_3_SPI_PLOADEN +#define CONF_SERCOM_3_SPI_PLOADEN 0 +#endif + +// Receive Data Pinout +// <0x0=>PAD[0] +// <0x1=>PAD[1] +// <0x2=>PAD[2] +// <0x3=>PAD[3] +// spi_master_rxpo +#ifndef CONF_SERCOM_3_SPI_RXPO +#define CONF_SERCOM_3_SPI_RXPO 2 +#endif + +// Transmit Data Pinout +// <0x0=>PAD[0,1]_DO_SCK +// <0x1=>PAD[2,3]_DO_SCK +// <0x2=>PAD[3,1]_DO_SCK +// <0x3=>PAD[0,3]_DO_SCK +// spi_master_txpo +#ifndef CONF_SERCOM_3_SPI_TXPO +#define CONF_SERCOM_3_SPI_TXPO 0 +#endif + +// Calculate baud register value from requested baudrate value +#ifndef CONF_SERCOM_3_SPI_BAUD_RATE +#define CONF_SERCOM_3_SPI_BAUD_RATE ((float)CONF_GCLK_SERCOM3_CORE_FREQUENCY / (float)(2 * CONF_SERCOM_3_SPI_BAUD)) - 1 +#endif + // <<< end of configuration section >>> #endif // HPL_SERCOM_CONFIG_H diff --git a/ports/atmel-samd/asf4_conf/samd51/peripheral_clk_config.h b/ports/atmel-samd/asf4_conf/samd51/peripheral_clk_config.h index 830f0e83b5..c1df632113 100644 --- a/ports/atmel-samd/asf4_conf/samd51/peripheral_clk_config.h +++ b/ports/atmel-samd/asf4_conf/samd51/peripheral_clk_config.h @@ -33,7 +33,7 @@ // Select the clock source for ADC. #ifndef CONF_GCLK_ADC0_SRC -#define CONF_GCLK_ADC0_SRC GCLK_PCHCTRL_GEN_GCLK0_Val +#define CONF_GCLK_ADC0_SRC GCLK_PCHCTRL_GEN_GCLK1_Val #endif /** @@ -41,7 +41,7 @@ * \brief ADC0's Clock frequency */ #ifndef CONF_GCLK_ADC0_FREQUENCY -#define CONF_GCLK_ADC0_FREQUENCY 120000000 +#define CONF_GCLK_ADC0_FREQUENCY 48000000 #endif // DAC Clock Source @@ -73,7 +73,7 @@ // dac_gclk_selection // Select the clock source for DAC. #ifndef CONF_GCLK_DAC_SRC -#define CONF_GCLK_DAC_SRC GCLK_PCHCTRL_GEN_GCLK0_Val +#define CONF_GCLK_DAC_SRC GCLK_PCHCTRL_GEN_GCLK1_Val #endif /** @@ -81,7 +81,7 @@ * \brief DAC's Clock frequency */ #ifndef CONF_GCLK_DAC_FREQUENCY -#define CONF_GCLK_DAC_FREQUENCY 120000000 +#define CONF_GCLK_DAC_FREQUENCY 48000000 #endif // EVSYS Channel 0 Clock Source @@ -113,7 +113,7 @@ // Select the clock source for channel 0. #ifndef CONF_GCLK_EVSYS_CHANNEL_0_SRC -#define CONF_GCLK_EVSYS_CHANNEL_0_SRC GCLK_PCHCTRL_GEN_GCLK0_Val +#define CONF_GCLK_EVSYS_CHANNEL_0_SRC GCLK_PCHCTRL_GEN_GCLK1_Val #endif /** @@ -122,7 +122,7 @@ */ #ifndef CONF_GCLK_EVSYS_CHANNEL_0_FREQUENCY -#define CONF_GCLK_EVSYS_CHANNEL_0_FREQUENCY 120000000.0 +#define CONF_GCLK_EVSYS_CHANNEL_0_FREQUENCY 48000000.0 #endif // EVSYS Channel 1 Clock Source @@ -154,7 +154,7 @@ // Select the clock source for channel 1. #ifndef CONF_GCLK_EVSYS_CHANNEL_1_SRC -#define CONF_GCLK_EVSYS_CHANNEL_1_SRC GCLK_PCHCTRL_GEN_GCLK0_Val +#define CONF_GCLK_EVSYS_CHANNEL_1_SRC GCLK_PCHCTRL_GEN_GCLK1_Val #endif /** @@ -163,7 +163,7 @@ */ #ifndef CONF_GCLK_EVSYS_CHANNEL_1_FREQUENCY -#define CONF_GCLK_EVSYS_CHANNEL_1_FREQUENCY 120000000.0 +#define CONF_GCLK_EVSYS_CHANNEL_1_FREQUENCY 48000000.0 #endif // EVSYS Channel 2 Clock Source @@ -195,7 +195,7 @@ // Select the clock source for channel 2. #ifndef CONF_GCLK_EVSYS_CHANNEL_2_SRC -#define CONF_GCLK_EVSYS_CHANNEL_2_SRC GCLK_PCHCTRL_GEN_GCLK0_Val +#define CONF_GCLK_EVSYS_CHANNEL_2_SRC GCLK_PCHCTRL_GEN_GCLK1_Val #endif /** @@ -204,7 +204,7 @@ */ #ifndef CONF_GCLK_EVSYS_CHANNEL_2_FREQUENCY -#define CONF_GCLK_EVSYS_CHANNEL_2_FREQUENCY 120000000.0 +#define CONF_GCLK_EVSYS_CHANNEL_2_FREQUENCY 48000000.0 #endif // EVSYS Channel 3 Clock Source @@ -236,7 +236,7 @@ // Select the clock source for channel 3. #ifndef CONF_GCLK_EVSYS_CHANNEL_3_SRC -#define CONF_GCLK_EVSYS_CHANNEL_3_SRC GCLK_PCHCTRL_GEN_GCLK0_Val +#define CONF_GCLK_EVSYS_CHANNEL_3_SRC GCLK_PCHCTRL_GEN_GCLK1_Val #endif /** @@ -245,7 +245,7 @@ */ #ifndef CONF_GCLK_EVSYS_CHANNEL_3_FREQUENCY -#define CONF_GCLK_EVSYS_CHANNEL_3_FREQUENCY 120000000.0 +#define CONF_GCLK_EVSYS_CHANNEL_3_FREQUENCY 48000000.0 #endif // EVSYS Channel 4 Clock Source @@ -277,7 +277,7 @@ // Select the clock source for channel 4. #ifndef CONF_GCLK_EVSYS_CHANNEL_4_SRC -#define CONF_GCLK_EVSYS_CHANNEL_4_SRC GCLK_PCHCTRL_GEN_GCLK0_Val +#define CONF_GCLK_EVSYS_CHANNEL_4_SRC GCLK_PCHCTRL_GEN_GCLK1_Val #endif /** @@ -286,7 +286,7 @@ */ #ifndef CONF_GCLK_EVSYS_CHANNEL_4_FREQUENCY -#define CONF_GCLK_EVSYS_CHANNEL_4_FREQUENCY 120000000.0 +#define CONF_GCLK_EVSYS_CHANNEL_4_FREQUENCY 48000000.0 #endif // EVSYS Channel 5 Clock Source @@ -318,7 +318,7 @@ // Select the clock source for channel 5. #ifndef CONF_GCLK_EVSYS_CHANNEL_5_SRC -#define CONF_GCLK_EVSYS_CHANNEL_5_SRC GCLK_PCHCTRL_GEN_GCLK0_Val +#define CONF_GCLK_EVSYS_CHANNEL_5_SRC GCLK_PCHCTRL_GEN_GCLK1_Val #endif /** @@ -327,7 +327,7 @@ */ #ifndef CONF_GCLK_EVSYS_CHANNEL_5_FREQUENCY -#define CONF_GCLK_EVSYS_CHANNEL_5_FREQUENCY 120000000.0 +#define CONF_GCLK_EVSYS_CHANNEL_5_FREQUENCY 48000000.0 #endif // EVSYS Channel 6 Clock Source @@ -359,7 +359,7 @@ // Select the clock source for channel 6. #ifndef CONF_GCLK_EVSYS_CHANNEL_6_SRC -#define CONF_GCLK_EVSYS_CHANNEL_6_SRC GCLK_PCHCTRL_GEN_GCLK0_Val +#define CONF_GCLK_EVSYS_CHANNEL_6_SRC GCLK_PCHCTRL_GEN_GCLK1_Val #endif /** @@ -368,7 +368,7 @@ */ #ifndef CONF_GCLK_EVSYS_CHANNEL_6_FREQUENCY -#define CONF_GCLK_EVSYS_CHANNEL_6_FREQUENCY 120000000.0 +#define CONF_GCLK_EVSYS_CHANNEL_6_FREQUENCY 48000000.0 #endif // EVSYS Channel 7 Clock Source @@ -400,7 +400,7 @@ // Select the clock source for channel 7. #ifndef CONF_GCLK_EVSYS_CHANNEL_7_SRC -#define CONF_GCLK_EVSYS_CHANNEL_7_SRC GCLK_PCHCTRL_GEN_GCLK0_Val +#define CONF_GCLK_EVSYS_CHANNEL_7_SRC GCLK_PCHCTRL_GEN_GCLK1_Val #endif /** @@ -409,7 +409,7 @@ */ #ifndef CONF_GCLK_EVSYS_CHANNEL_7_FREQUENCY -#define CONF_GCLK_EVSYS_CHANNEL_7_FREQUENCY 120000000.0 +#define CONF_GCLK_EVSYS_CHANNEL_7_FREQUENCY 48000000.0 #endif // EVSYS Channel 8 Clock Source @@ -441,7 +441,7 @@ // Select the clock source for channel 8. #ifndef CONF_GCLK_EVSYS_CHANNEL_8_SRC -#define CONF_GCLK_EVSYS_CHANNEL_8_SRC GCLK_PCHCTRL_GEN_GCLK0_Val +#define CONF_GCLK_EVSYS_CHANNEL_8_SRC GCLK_PCHCTRL_GEN_GCLK1_Val #endif /** @@ -450,7 +450,7 @@ */ #ifndef CONF_GCLK_EVSYS_CHANNEL_8_FREQUENCY -#define CONF_GCLK_EVSYS_CHANNEL_8_FREQUENCY 120000000.0 +#define CONF_GCLK_EVSYS_CHANNEL_8_FREQUENCY 48000000.0 #endif // EVSYS Channel 9 Clock Source @@ -482,7 +482,7 @@ // Select the clock source for channel 9. #ifndef CONF_GCLK_EVSYS_CHANNEL_9_SRC -#define CONF_GCLK_EVSYS_CHANNEL_9_SRC GCLK_PCHCTRL_GEN_GCLK0_Val +#define CONF_GCLK_EVSYS_CHANNEL_9_SRC GCLK_PCHCTRL_GEN_GCLK1_Val #endif /** @@ -491,7 +491,7 @@ */ #ifndef CONF_GCLK_EVSYS_CHANNEL_9_FREQUENCY -#define CONF_GCLK_EVSYS_CHANNEL_9_FREQUENCY 120000000.0 +#define CONF_GCLK_EVSYS_CHANNEL_9_FREQUENCY 48000000.0 #endif // EVSYS Channel 10 Clock Source @@ -523,7 +523,7 @@ // Select the clock source for channel 10. #ifndef CONF_GCLK_EVSYS_CHANNEL_10_SRC -#define CONF_GCLK_EVSYS_CHANNEL_10_SRC GCLK_PCHCTRL_GEN_GCLK0_Val +#define CONF_GCLK_EVSYS_CHANNEL_10_SRC GCLK_PCHCTRL_GEN_GCLK1_Val #endif /** @@ -532,7 +532,7 @@ */ #ifndef CONF_GCLK_EVSYS_CHANNEL_10_FREQUENCY -#define CONF_GCLK_EVSYS_CHANNEL_10_FREQUENCY 120000000.0 +#define CONF_GCLK_EVSYS_CHANNEL_10_FREQUENCY 48000000.0 #endif // EVSYS Channel 11 Clock Source @@ -564,7 +564,7 @@ // Select the clock source for channel 11. #ifndef CONF_GCLK_EVSYS_CHANNEL_11_SRC -#define CONF_GCLK_EVSYS_CHANNEL_11_SRC GCLK_PCHCTRL_GEN_GCLK0_Val +#define CONF_GCLK_EVSYS_CHANNEL_11_SRC GCLK_PCHCTRL_GEN_GCLK1_Val #endif /** @@ -573,7 +573,7 @@ */ #ifndef CONF_GCLK_EVSYS_CHANNEL_11_FREQUENCY -#define CONF_GCLK_EVSYS_CHANNEL_11_FREQUENCY 120000000.0 +#define CONF_GCLK_EVSYS_CHANNEL_11_FREQUENCY 48000000.0 #endif /** @@ -840,6 +840,86 @@ #define CONF_GCLK_SERCOM2_SLOW_FREQUENCY 32768 #endif +// Core Clock Source +// core_gclk_selection + +// Generic clock generator 0 + +// Generic clock generator 1 + +// Generic clock generator 2 + +// Generic clock generator 3 + +// Generic clock generator 4 + +// Generic clock generator 5 + +// Generic clock generator 6 + +// Generic clock generator 7 + +// Generic clock generator 8 + +// Generic clock generator 9 + +// Generic clock generator 10 + +// Generic clock generator 11 + +// Select the clock source for CORE. +#ifndef CONF_GCLK_SERCOM3_CORE_SRC +#define CONF_GCLK_SERCOM3_CORE_SRC GCLK_PCHCTRL_GEN_GCLK1_Val +#endif + +// Slow Clock Source +// slow_gclk_selection + +// Generic clock generator 0 + +// Generic clock generator 1 + +// Generic clock generator 2 + +// Generic clock generator 3 + +// Generic clock generator 4 + +// Generic clock generator 5 + +// Generic clock generator 6 + +// Generic clock generator 7 + +// Generic clock generator 8 + +// Generic clock generator 9 + +// Generic clock generator 10 + +// Generic clock generator 11 + +// Select the slow clock source. +#ifndef CONF_GCLK_SERCOM3_SLOW_SRC +#define CONF_GCLK_SERCOM3_SLOW_SRC GCLK_PCHCTRL_GEN_GCLK3_Val +#endif + +/** + * \def CONF_GCLK_SERCOM3_CORE_FREQUENCY + * \brief SERCOM3's Core Clock frequency + */ +#ifndef CONF_GCLK_SERCOM3_CORE_FREQUENCY +#define CONF_GCLK_SERCOM3_CORE_FREQUENCY 48000000 +#endif + +/** + * \def CONF_GCLK_SERCOM3_SLOW_FREQUENCY + * \brief SERCOM3's Slow Clock frequency + */ +#ifndef CONF_GCLK_SERCOM3_SLOW_FREQUENCY +#define CONF_GCLK_SERCOM3_SLOW_FREQUENCY 32768 +#endif + // TC Clock Source // tc_gclk_selection @@ -869,7 +949,7 @@ // Select the clock source for TC. #ifndef CONF_GCLK_TC0_SRC -#define CONF_GCLK_TC0_SRC GCLK_PCHCTRL_GEN_GCLK0_Val +#define CONF_GCLK_TC0_SRC GCLK_PCHCTRL_GEN_GCLK1_Val #endif /** @@ -877,7 +957,7 @@ * \brief TC0's Clock frequency */ #ifndef CONF_GCLK_TC0_FREQUENCY -#define CONF_GCLK_TC0_FREQUENCY 120000000 +#define CONF_GCLK_TC0_FREQUENCY 48000000 #endif // USB Clock Source diff --git a/ports/atmel-samd/boards/metro_m4_express/pins.c b/ports/atmel-samd/boards/metro_m4_express/pins.c index 5889c6c685..33b69b32b3 100644 --- a/ports/atmel-samd/boards/metro_m4_express/pins.c +++ b/ports/atmel-samd/boards/metro_m4_express/pins.c @@ -13,7 +13,9 @@ STATIC const mp_map_elem_t board_global_dict_table[] = { { MP_OBJ_NEW_QSTR(MP_QSTR_D0), (mp_obj_t)&pin_PA23 }, + { MP_OBJ_NEW_QSTR(MP_QSTR_RX), (mp_obj_t)&pin_PA23 }, { MP_OBJ_NEW_QSTR(MP_QSTR_D1), (mp_obj_t)&pin_PA22 }, + { MP_OBJ_NEW_QSTR(MP_QSTR_TX), (mp_obj_t)&pin_PA22 }, { MP_OBJ_NEW_QSTR(MP_QSTR_D2), (mp_obj_t)&pin_PA04 }, { MP_OBJ_NEW_QSTR(MP_QSTR_D3), (mp_obj_t)&pin_PB16 }, { MP_OBJ_NEW_QSTR(MP_QSTR_D4), (mp_obj_t)&pin_PB13 }, diff --git a/ports/atmel-samd/common-hal/busio/SPI.c b/ports/atmel-samd/common-hal/busio/SPI.c index ca98d1d2f2..c4cd6e66f8 100644 --- a/ports/atmel-samd/common-hal/busio/SPI.c +++ b/ports/atmel-samd/common-hal/busio/SPI.c @@ -117,6 +117,8 @@ void common_hal_busio_spi_construct(busio_spi_obj_t *self, mp_raise_OSError(MP_EIO); } + // Pads must be set after spi_m_sync_init(), which uses default values from + // the prototypical SERCOM. hri_sercomspi_write_CTRLA_DOPO_bf(sercom, dopo); hri_sercomspi_write_CTRLA_DIPO_bf(sercom, miso_pad); diff --git a/ports/atmel-samd/common-hal/busio/UART.c b/ports/atmel-samd/common-hal/busio/UART.c index 79f7cf4a9e..292e01e60e 100644 --- a/ports/atmel-samd/common-hal/busio/UART.c +++ b/ports/atmel-samd/common-hal/busio/UART.c @@ -30,97 +30,25 @@ #include "mpconfigport.h" #include "py/gc.h" #include "py/mperrno.h" -#include "py/nlr.h" #include "py/runtime.h" #include "py/stream.h" -#include "samd21_pins.h" + #include "tick.h" -#include "asf/sam0/drivers/sercom/sercom_interrupt.h" +#include "hpl_sercom_config.h" +#include "peripheral_clk_config.h" -#undef ENABLE +#include "hal/include/hal_gpio.h" +#include "hal/include/hal_usart_async.h" +#include "hal/include/hpl_usart_async.h" -busio_uart_obj_t *_uart_instances[SERCOM_INST_NUM]; +#include "peripherals.h" +#include "pins.h" -static void _sercom_default_handler( - const uint8_t instance) -{ - Assert(false); -} - -static void _busio_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]; - - busio_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) { - buffer_end = (self->buffer_start + self->buffer_size) % self->buffer_length; - /* 9-bit data, write next received byte to the buffer */ - self->buffer[buffer_end] = (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(); - } - } +// Do-nothing callback needed so that usart_async code will enable rx interrupts. +// See comment below re usart_async_register_callback() +static void usart_async_rxc_callback(const struct usart_async_descriptor *const descr) { + // Nothing needs to be done by us. } void common_hal_busio_uart_construct(busio_uart_obj_t *self, @@ -128,34 +56,55 @@ void common_hal_busio_uart_construct(busio_uart_obj_t *self, 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 + uint8_t sercom_index; + uint32_t rx_pinmux = 0; + uint8_t rx_pad = 255; // Unset pad + uint32_t tx_pinmux = 0; + uint8_t tx_pad = 255; // Unset pad + + if (bits > 8) { + mp_raise_NotImplementedError("bytes > 8 bits not supported"); + } + + bool have_tx = tx != mp_const_none; + bool have_rx = rx != mp_const_none; + if (!have_tx && !have_rx) { + mp_raise_ValueError("tx and rx cannot both be None"); + } + + self->baudrate = baudrate; + self->character_bits = bits; + self->timeout_ms = timeout; + + // This assignment is only here because the usart_async routines take a *const argument. + struct usart_async_descriptor * const usart_desc_p = (struct usart_async_descriptor * const) &self->usart_desc; + for (int i = 0; i < NUM_SERCOMS_PER_PIN; i++) { Sercom* potential_sercom = NULL; - if (tx != NULL) { + if (have_tx) { potential_sercom = tx->sercom[i].sercom; + sercom_index = tx->sercom[i].index; if (potential_sercom == NULL || - potential_sercom->I2CM.CTRLA.bit.ENABLE != 0 || + potential_sercom->USART.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) { + if (rx == mp_const_none) { 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) || + if (((!have_tx && rx->sercom[j].sercom->USART.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; + sercom_index = rx->sercom[j].index; break; } } @@ -166,81 +115,96 @@ void common_hal_busio_uart_construct(busio_uart_obj_t *self, if (sercom == NULL) { mp_raise_ValueError("Invalid pins"); } - if (tx == NULL) { + if (!have_tx) { tx_pad = 0; if (rx_pad == 0) { tx_pad = 2; } } - if (rx == NULL) { + if (!have_rx) { 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; + // Set up clocks on SERCOM. + samd_peripherals_sercom_clock_init(sercom, sercom_index); - // 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; + if (rx && receiver_buffer_size > 0) { + self->buffer_length = receiver_buffer_size; + self->buffer = (uint8_t *) gc_alloc(self->buffer_length * sizeof(uint8_t), false, false); + if (self->buffer == NULL) { + common_hal_busio_uart_deinit(self); + mp_raise_msg(&mp_type_MemoryError, "Failed to allocate RX buffer"); + } + } else { + self->buffer_length = 0; + self->buffer = NULL; } - self->rx_pin = NO_PIN; - config_usart.receiver_enable = rx != NULL; - if (rx != NULL) { - *pinmuxes[rx_pad] = rx_pinmux; - self->rx_pin = rx->pin; - claim_pin(rx); + if (usart_async_init(usart_desc_p, sercom, self->buffer, self->buffer_length, NULL) != ERR_NONE) { + mp_raise_ValueError("Could not initialize UART"); } - self->tx_pin = NO_PIN; - config_usart.transmitter_enable = tx != NULL; - if (tx != NULL) { - *pinmuxes[tx_pad] = tx_pinmux; - self->tx_pin = tx->pin; + // usart_async_init() sets a number of defaults based on a prototypical SERCOM + // which don't necessarily match what we need. After calling it, set the values + // specific to this instantiation of UART. + + // Set pads computed for this SERCOM. + // TXPO: + // 0x0: TX pad 0; no RTS/CTS + // 0x1: TX pad 2; no RTS/CTS + // 0x2: TX pad 0; RTS: pad 2, CTS: pad 3 (not used by us right now) + // So divide by 2 to map pad to value. + hri_sercomusart_write_CTRLA_TXPO_bf(sercom, tx_pad / 2); + // RXPO: + // 0x0: RX pad 0 + // 0x1: RX pad 1 + // 0x2: RX pad 2 + // 0x3: RX pad 3 + hri_sercomusart_write_CTRLA_RXPO_bf(sercom, rx_pad); + + // Enable tx and/or rx based on whether the pins were specified. + hri_sercomusart_write_CTRLB_TXEN_bit(sercom, have_tx); + hri_sercomusart_write_CTRLB_RXEN_bit(sercom, have_rx); + + // Set parity, baud rate, stop bits, etc. 9-bit bytes not supported. + usart_async_set_parity(usart_desc_p, parity == PARITY_NONE ? USART_PARITY_NONE : + (parity == PARITY_ODD ? USART_PARITY_ODD : USART_PARITY_EVEN)); + usart_async_set_stopbits(usart_desc_p, stop == 1 ? USART_STOP_BITS_ONE : USART_STOP_BITS_TWO); + // This field is 0 for 8 bits, 5, 6, 7 for 5, 6, 7 bits. 1 for 9 bits, but we don't support that. + usart_async_set_character_size(usart_desc_p, bits % 8); + common_hal_busio_uart_set_baudrate(self, baudrate); + + // Turn on rx interrupt handling. The UART async driver has its own set of internal callbacks, + // which are set up by uart_async_init(). These in turn can call user-specified callbacks. + // In fact, the actual interrupts are not enabled unless we set up a user-specified callback. + // This is confusing. It's explained in the Atmel START User Guide -> Implementation Description -> + // Different read function behavior in some asynchronous drivers. As of this writing: + // http://start.atmel.com/static/help/index.html?GUID-79201A5A-226F-4FBB-B0B8-AB0BE0554836 + // Look at the ASFv4 code example for async USART. + usart_async_register_callback(usart_desc_p, USART_ASYNC_RXC_CB, usart_async_rxc_callback); + + + if (have_tx) { + gpio_set_pin_direction(tx->pin, GPIO_DIRECTION_OUT); + gpio_set_pin_pull_mode(tx->pin, GPIO_PULL_OFF); + gpio_set_pin_function(tx->pin, tx_pinmux); + self->tx_pin = tx->pin; claim_pin(tx); + } else { + self->tx_pin = NO_PIN; + } + + if (have_rx) { + gpio_set_pin_direction(rx->pin, GPIO_DIRECTION_IN); + gpio_set_pin_pull_mode(rx->pin, GPIO_PULL_OFF); + gpio_set_pin_function(rx->pin, rx_pinmux); + self->rx_pin = rx->pin; + claim_pin(rx); + } else { + self->rx_pin = NO_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) { - common_hal_busio_uart_deinit(self); - mp_raise_msg(&mp_type_MemoryError, "Failed to allocate RX buffer"); - } - - if (usart_init(&self->uart_instance, sercom, &config_usart) != STATUS_OK) { - common_hal_busio_uart_deinit(self); - mp_raise_OSError(MP_EIO); - } - - // 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, _busio_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; + usart_async_enable(usart_desc_p); } bool common_hal_busio_uart_deinited(busio_uart_obj_t *self) { @@ -251,16 +215,10 @@ void common_hal_busio_uart_deinit(busio_uart_obj_t *self) { if (common_hal_busio_uart_deinited(self)) { return; } - 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); + // This assignment is only here because the usart_async routines take a *const argument. + struct usart_async_descriptor * const usart_desc_p = (struct usart_async_descriptor * const) &self->usart_desc; + usart_async_disable(usart_desc_p); + usart_async_deinit(usart_desc_p); reset_pin(self->rx_pin); reset_pin(self->tx_pin); self->rx_pin = NO_PIN; @@ -269,123 +227,120 @@ void common_hal_busio_uart_deinit(busio_uart_obj_t *self) { // Read characters. size_t common_hal_busio_uart_read(busio_uart_obj_t *self, uint8_t *data, size_t len, int *errcode) { + if (self->rx_pin == NO_PIN) { + mp_raise_ValueError("No RX pin"); + } + + // This assignment is only here because the usart_async routines take a *const argument. + struct usart_async_descriptor * const usart_desc_p = (struct usart_async_descriptor * const) &self->usart_desc; + + if (len == 0) { + // Nothing to read. + return 0; + } + + struct io_descriptor *io; + usart_async_get_io_descriptor(usart_desc_p, &io); + 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++; + + // Busy-wait until timeout or until we've read enough chars. + while (ticks_ms - start_ticks < self->timeout_ms) { + // Read as many chars as we can right now, up to len. + size_t num_read = io_read(io, data, len); + + // Advance pointer in data buffer, and decrease how many chars left to read. + data += num_read; + len -= num_read; + total_read += num_read; + if (len == 0) { + // Don't need to read any more: data buf is full. + break; + } + if (num_read > 0) { + // Reset the timeout on every character read. start_ticks = ticks_ms; } - #ifdef MICROPY_VM_HOOK_LOOP - MICROPY_VM_HOOK_LOOP - #endif - } - if (total_read == 0) { - *errcode = MP_EAGAIN; - return MP_STREAM_ERROR; +#ifdef MICROPY_VM_HOOK_LOOP + MICROPY_VM_HOOK_LOOP +#endif } + return total_read; } // Write characters. size_t common_hal_busio_uart_write(busio_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; + if (self->tx_pin == NO_PIN) { + mp_raise_ValueError("No TX pin"); } - /* Get a pointer to the hardware module instance */ - SercomUsart *const usart_hw = &(self->uart_instance.hw->USART); + // This assignment is only here because the usart_async routines take a *const argument. + struct usart_async_descriptor * const usart_desc_p = (struct usart_async_descriptor * const) &self->usart_desc; - /* Wait until synchronization is complete */ - _usart_wait_for_sync(&self->uart_instance); + struct io_descriptor *io; + usart_async_get_io_descriptor(usart_desc_p, &io); - 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; - } - #ifdef MICROPY_VM_HOOK_LOOP - MICROPY_VM_HOOK_LOOP - #endif - } - - 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; - } - #ifdef MICROPY_VM_HOOK_LOOP - MICROPY_VM_HOOK_LOOP - #endif - } - } - - if (!ok && tx_pos == 0) { + if (io_write(io, data, len) < 0) { *errcode = MP_EAGAIN; return MP_STREAM_ERROR; } - return tx_pos; + + // Wait until write is complete or timeout. + bool done = false; + uint64_t start_ticks = ticks_ms; + // Busy-wait for timeout. + while (ticks_ms - start_ticks < self->timeout_ms) { + if (usart_async_is_tx_empty(usart_desc_p)) { + done = true; + break; + } + #ifdef MICROPY_VM_HOOK_LOOP + MICROPY_VM_HOOK_LOOP + #endif + } + + if (!done) { + *errcode = MP_EAGAIN; + return MP_STREAM_ERROR; + } + + struct usart_async_status async_status; + // Could return ERR_BUSY, but if that's true there's already a problem. + usart_async_get_status(usart_desc_p, &async_status); + return async_status.txcnt; +} + +uint32_t common_hal_busio_uart_get_baudrate(busio_uart_obj_t *self) { + return self->baudrate; +} + +void common_hal_busio_uart_set_baudrate(busio_uart_obj_t *self, uint32_t baudrate) { + // This assignment is only here because the usart_async routines take a *const argument. + struct usart_async_descriptor * const usart_desc_p = (struct usart_async_descriptor * const) &self->usart_desc; + usart_async_set_baud_rate(usart_desc_p, + // Samples and ARITHMETIC vs FRACTIONAL must correspond to USART_SAMPR in + // hpl_sercom_config.h. + _usart_async_calculate_baud_rate(baudrate, // e.g. 9600 baud + PROTOTYPE_SERCOM_USART_ASYNC_CLOCK_FREQUENCY, + 16, // samples + USART_BAUDRATE_ASYNCH_ARITHMETIC, + 0 // fraction - not used for ARITHMETIC + )); + self->baudrate = baudrate; } uint32_t common_hal_busio_uart_rx_characters_available(busio_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_busio_uart_ready_to_tx(busio_uart_obj_t *self) { - if (!(self->uart_instance.transmitter_enabled)) { + if (self->tx_pin == NO_PIN) { return false; } - return self->uart_instance.hw->USART.INTFLAG.bit.DRE; + // This assignment is only here because the usart_async routines take a *const argument. + const struct _usart_async_device * const usart_device_p = + (struct _usart_async_device * const) &self->usart_desc.device; + return _usart_async_is_byte_sent(usart_device_p); } diff --git a/ports/atmel-samd/common-hal/busio/UART.h b/ports/atmel-samd/common-hal/busio/UART.h index 5adc115147..685755a5d1 100644 --- a/ports/atmel-samd/common-hal/busio/UART.h +++ b/ports/atmel-samd/common-hal/busio/UART.h @@ -29,22 +29,25 @@ #include "common-hal/microcontroller/Pin.h" -#include "asf/sam0/drivers/sercom/usart/usart.h" +#include "hal/include/hal_usart_async.h" + #include "py/obj.h" 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; + mp_obj_base_t base; + struct usart_async_descriptor usart_desc; + uint8_t rx_pin; + uint8_t tx_pin; + uint8_t character_bits; + bool rx_error; + uint32_t baudrate; + uint32_t timeout_ms; + // 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; } busio_uart_obj_t; #endif // MICROPY_INCLUDED_ATMEL_SAMD_COMMON_HAL_BUSIO_UART_H diff --git a/ports/atmel-samd/common-hal/neopixel_write/__init__.c b/ports/atmel-samd/common-hal/neopixel_write/__init__.c index 6133415f48..5530d04085 100644 --- a/ports/atmel-samd/common-hal/neopixel_write/__init__.c +++ b/ports/atmel-samd/common-hal/neopixel_write/__init__.c @@ -32,6 +32,9 @@ #include "tick.h" #ifdef SAMD51 +#include "hri/hri_cmcc_d51.h" +#include "hri/hri_nvmctrl_d51.h" + // This magical macro makes sure the delay isn't optimized out and is the // minimal three instructions. #define delay_cycles(cycles) \ @@ -62,11 +65,30 @@ void common_hal_neopixel_write(const digitalio_digitalinout_obj_t* digitalinout, // Turn off interrupts of any kind during timing-sensitive code. mp_hal_disable_all_interrupts(); + #ifdef SAMD21 // Make sure the NVM cache is consistently timed. NVMCTRL->CTRLB.bit.READMODE = NVMCTRL_CTRLB_READMODE_DETERMINISTIC_Val; #endif + #ifdef SAMD51 + // When this routine is positioned at certain addresses, the timing logic + // below can be too fast by about 2.5x. This is some kind of (un)fortunate code + // positiong with respect to a cache line. + // Theoretically we should turn on off the CMCC caches and the + // NVM caches to ensure consistent timing. Testing shows the the NVMCTRL + // cache disabling seems to make the difference. But turn both off to make sure. + // It's difficult to test because additions to the code before the timing loop + // below change instruction placement. Testing was done by adding cache changes + // below the loop (so only the first time through is wrong). + // + // Turn off instruction, data, and NVM caches to force consistent timing. + // Invalidate existing cache entries. + hri_cmcc_set_CFG_reg(CMCC, CMCC_CFG_DCDIS | CMCC_CFG_ICDIS); + hri_cmcc_write_MAINT0_reg(CMCC, CMCC_MAINT0_INVALL); + hri_nvmctrl_set_CTRLA_CACHEDIS0_bit(NVMCTRL); + hri_nvmctrl_set_CTRLA_CACHEDIS1_bit(NVMCTRL); + #endif uint32_t pin = digitalinout->pin->pin; port = &PORT->Group[GPIO_PORT(pin)]; // Convert GPIO # to port register @@ -124,7 +146,7 @@ void common_hal_neopixel_write(const digitalio_digitalinout_obj_t* digitalinout, if(ptr >= end) break; p = *ptr++; bitMask = 0x80; - // This is the delay between bytes. Its similar to the other branch + // This is the delay between bytes. It's similar to the other branch // in the if statement except its tuned to account for the time the // above operations take. // For the SK6812 its 0.6us +- 0.15us @@ -139,6 +161,14 @@ void common_hal_neopixel_write(const digitalio_digitalinout_obj_t* digitalinout, NVMCTRL->CTRLB.bit.READMODE = NVMCTRL_CTRLB_READMODE_NO_MISS_PENALTY_Val; #endif + #ifdef SAMD51 + // Turn instruction, data, and NVM caches back on. + hri_cmcc_clear_CFG_reg(CMCC, CMCC_CFG_DCDIS | CMCC_CFG_ICDIS); + hri_nvmctrl_clear_CTRLA_CACHEDIS0_bit(NVMCTRL); + hri_nvmctrl_clear_CTRLA_CACHEDIS1_bit(NVMCTRL); + + #endif + // ticks_ms may be out of date at this point because we stopped the // interrupt. We'll risk it anyway. current_tick(&next_start_tick_ms, &next_start_tick_us); diff --git a/ports/atmel-samd/supervisor/port.c b/ports/atmel-samd/supervisor/port.c index 726a48d060..fe65915106 100644 --- a/ports/atmel-samd/supervisor/port.c +++ b/ports/atmel-samd/supervisor/port.c @@ -114,23 +114,6 @@ safe_mode_t port_init(void) { // Configure millisecond timer initialization. tick_init(); - // Uncomment to init PIN_PA17 for debugging. - // struct port_config pin_conf; - // port_get_config_defaults(&pin_conf); - // - // pin_conf.direction = PORT_PIN_DIR_OUTPUT; - // port_pin_set_config(MICROPY_HW_LED1, &pin_conf); - // port_pin_set_output_level(MICROPY_HW_LED1, false); - - // Output clocks for debugging. - // not supported by SAMD51G; uncomment for SAMD51J or update for 51G - // #ifdef SAMD51 - // gpio_set_pin_function(PIN_PA10, GPIO_PIN_FUNCTION_M); // GCLK4, D3 - // gpio_set_pin_function(PIN_PA11, GPIO_PIN_FUNCTION_M); // GCLK5, A4 - // gpio_set_pin_function(PIN_PB14, GPIO_PIN_FUNCTION_M); // GCLK0, D5 - // gpio_set_pin_function(PIN_PB15, GPIO_PIN_FUNCTION_M); // GCLK1, D6 - // #endif - // Init the nvm controller. // struct nvm_config config_nvm; // nvm_get_config_defaults(&config_nvm); @@ -221,7 +204,26 @@ void reset_port(void) { analogout_reset(); reset_all_pins(); -// + + // Set up debugging pins after reset_all_pins(). + + // Uncomment to init PIN_PA17 for debugging. + // struct port_config pin_conf; + // port_get_config_defaults(&pin_conf); + // + // pin_conf.direction = PORT_PIN_DIR_OUTPUT; + // port_pin_set_config(MICROPY_HW_LED1, &pin_conf); + // port_pin_set_output_level(MICROPY_HW_LED1, false); + + // Output clocks for debugging. + // not supported by SAMD51G; uncomment for SAMD51J or update for 51G + // #ifdef SAMD51 + // gpio_set_pin_function(PIN_PA10, GPIO_PIN_FUNCTION_M); // GCLK4, D3 + // gpio_set_pin_function(PIN_PA11, GPIO_PIN_FUNCTION_M); // GCLK5, A4 + // gpio_set_pin_function(PIN_PB14, GPIO_PIN_FUNCTION_M); // GCLK0, D5 + // gpio_set_pin_function(PIN_PB15, GPIO_PIN_FUNCTION_M); // GCLK1, D6 + // #endif + // // usb_hid_reset(); // diff --git a/shared-bindings/busio/UART.c b/shared-bindings/busio/UART.c index 3a3cb42463..1b63d8d0e0 100644 --- a/shared-bindings/busio/UART.c +++ b/shared-bindings/busio/UART.c @@ -33,6 +33,7 @@ #include "lib/utils/context_manager_helpers.h" #include "py/ioctl.h" +#include "py/objproperty.h" #include "py/runtime.h" #include "py/stream.h" @@ -48,11 +49,11 @@ //| A common bidirectional serial protocol that uses an an agreed upon speed //| rather than a shared clock line. //| -//| :param ~microcontroller.Pin tx: the pin to transmit with -//| :param ~microcontroller.Pin rx: the pin to receive on -//| :param int baudrate: the transmit and receive speed +//| :param ~microcontroller.Pin tx: the pin to transmit with, or ``None`` if this ``UART`` is receive-only. +//| :param ~microcontroller.Pin rx: the pin to receive on, or ``None`` if this ``UART`` is transmit-only. +//| :param int baudrate: the transmit and receive speed. /// :param int bits: the number of bits per byte, 7, 8 or 9. -/// :param Parity parity: the parity used for error checking +/// :param Parity parity: the parity used for error checking. /// :param int stop: the number of stop bits, 1 or 2. /// :param int timeout: the timeout in milliseconds to wait for the first character and between subsequent characters. /// :param int receiver_buffer_size: the character length of the read buffer (0 to disable). (When a character is 9 bits the buffer will be 2 * receiver_buffer_size bytes.) @@ -220,6 +221,33 @@ STATIC mp_uint_t busio_uart_ioctl(mp_obj_t self_in, mp_uint_t request, mp_uint_t return ret; } +//| .. attribute:: baudrate +//| +//| The current baudrate. +//| +STATIC mp_obj_t busio_uart_obj_get_baudrate(mp_obj_t self_in) { + busio_uart_obj_t *self = MP_OBJ_TO_PTR(self_in); + raise_error_if_deinited(common_hal_busio_uart_deinited(self)); + return MP_OBJ_NEW_SMALL_INT(common_hal_busio_uart_get_baudrate(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(busio_uart_get_baudrate_obj, busio_uart_obj_get_baudrate); + +STATIC mp_obj_t busio_uart_obj_set_baudrate(mp_obj_t self_in, mp_obj_t baudrate) { + busio_uart_obj_t *self = MP_OBJ_TO_PTR(self_in); + raise_error_if_deinited(common_hal_busio_uart_deinited(self)); + common_hal_busio_uart_set_baudrate(self, mp_obj_get_int(baudrate)); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(busio_uart_set_baudrate_obj, busio_uart_obj_set_baudrate); + + +const mp_obj_property_t busio_uart_baudrate_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&busio_uart_get_baudrate_obj, + (mp_obj_t)&busio_uart_set_baudrate_obj, + (mp_obj_t)&mp_const_none_obj}, +}; + //| .. class:: busio.UART.Parity //| //| Enum-like class to define the parity used to verify correct data transfer. @@ -274,6 +302,9 @@ STATIC const mp_rom_map_elem_t busio_uart_locals_dict_table[] = { { MP_OBJ_NEW_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&mp_stream_readinto_obj) }, { MP_OBJ_NEW_QSTR(MP_QSTR_write), MP_ROM_PTR(&mp_stream_write_obj) }, + // Properties + { MP_ROM_QSTR(MP_QSTR_baudrate), MP_ROM_PTR(&busio_uart_baudrate_obj) }, + // Nested Enum-like Classes. { MP_ROM_QSTR(MP_QSTR_Parity), MP_ROM_PTR(&busio_uart_parity_type) }, }; diff --git a/shared-bindings/busio/UART.h b/shared-bindings/busio/UART.h index 28196931a9..fa39d8ad5f 100644 --- a/shared-bindings/busio/UART.h +++ b/shared-bindings/busio/UART.h @@ -55,6 +55,10 @@ extern size_t common_hal_busio_uart_read(busio_uart_obj_t *self, extern size_t common_hal_busio_uart_write(busio_uart_obj_t *self, const uint8_t *data, size_t len, int *errcode); +extern uint32_t common_hal_busio_uart_get_baudrate(busio_uart_obj_t *self); +extern void common_hal_busio_uart_set_baudrate(busio_uart_obj_t *self, uint32_t baudrate); + + extern uint32_t common_hal_busio_uart_rx_characters_available(busio_uart_obj_t *self); extern bool common_hal_busio_uart_ready_to_tx(busio_uart_obj_t *self); diff --git a/shared-bindings/busio/__init__.c b/shared-bindings/busio/__init__.c index 6956e3c3d3..958ee062af 100644 --- a/shared-bindings/busio/__init__.c +++ b/shared-bindings/busio/__init__.c @@ -32,9 +32,9 @@ #include "shared-bindings/microcontroller/Pin.h" #include "shared-bindings/busio/__init__.h" #include "shared-bindings/busio/I2C.h" -//xxxx #include "shared-bindings/busio/OneWire.h" +#include "shared-bindings/busio/OneWire.h" #include "shared-bindings/busio/SPI.h" -//xxxx #include "shared-bindings/busio/UART.h" +#include "shared-bindings/busio/UART.h" #include "shared-bindings/busio/__init__.h" #include "py/runtime.h" @@ -89,8 +89,8 @@ STATIC const mp_rom_map_elem_t busio_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_busio) }, { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&busio_i2c_type) }, { MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&busio_spi_type) }, - //xxxx { MP_ROM_QSTR(MP_QSTR_OneWire), MP_ROM_PTR(&busio_onewire_type) }, - //xxxx { MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&busio_uart_type) }, + { MP_ROM_QSTR(MP_QSTR_OneWire), MP_ROM_PTR(&busio_onewire_type) }, + { MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&busio_uart_type) }, }; STATIC MP_DEFINE_CONST_DICT(busio_module_globals, busio_module_globals_table);