diff --git a/.github/workflows/build-bsp.yml b/.github/workflows/build-bsp.yml index fbd358b76426..5c1732eb2bf5 100644 --- a/.github/workflows/build-bsp.yml +++ b/.github/workflows/build-bsp.yml @@ -8,7 +8,7 @@ jobs: matrix: ${{ steps.set-matrix.outputs.matrix }} steps: - name: Checkout sources - uses: actions/checkout@v5 + uses: actions/checkout@v6 - id: set-matrix uses: ./.github/actions/list-BSPs @@ -26,7 +26,7 @@ jobs: matrix: ${{fromJson(needs.setup.outputs.matrix)}} steps: - name: Checkout sources - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Install Rust run: | @@ -60,7 +60,7 @@ jobs: $(${clippy_invocation}) - name: Done - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v7 with: # name needs to be unique in the workspace name: "${{ matrix.bsp.name }}-${{ matrix.toolchain }}" @@ -71,7 +71,7 @@ jobs: needs: [setup, build] steps: - name: Download artifacts - uses: actions/download-artifact@v6 + uses: actions/download-artifact@v8 with: path: successful-jobs - name: Do checks diff --git a/.github/workflows/build-hal.yml b/.github/workflows/build-hal.yml index 44f85fcf240d..72ddd0a1589a 100644 --- a/.github/workflows/build-hal.yml +++ b/.github/workflows/build-hal.yml @@ -8,7 +8,7 @@ jobs: matrix: ${{ steps.set-matrix.outputs.matrix }} steps: - name: Checkout sources - uses: actions/checkout@v5 + uses: actions/checkout@v6 - id: set-matrix uses: ./.github/actions/list-HAL-variants @@ -20,7 +20,7 @@ jobs: matrix: ${{fromJson(needs.setup.outputs.matrix)}} steps: - name: Checkout sources - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Install Rust run: | diff --git a/.github/workflows/check-docs.yml b/.github/workflows/check-docs.yml index 353ef0756e69..b24c81747b88 100644 --- a/.github/workflows/check-docs.yml +++ b/.github/workflows/check-docs.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout sources - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Install Rust run: | rustup set profile minimal diff --git a/.github/workflows/generate-docs.yml b/.github/workflows/generate-docs.yml index 5533728425ea..7443a4f1ead8 100644 --- a/.github/workflows/generate-docs.yml +++ b/.github/workflows/generate-docs.yml @@ -12,7 +12,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout sources - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Install Rust run: | @@ -80,7 +80,7 @@ jobs: echo ' ' >> "${docs_path}/index.html" echo '' >> "${docs_path}/index.html" - name: Checkout pages branch - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: ref: gh-pages path: gh-pages diff --git a/.github/workflows/release-crates.yml b/.github/workflows/release-crates.yml index 295a2bab5f6b..ebf467ad320c 100644 --- a/.github/workflows/release-crates.yml +++ b/.github/workflows/release-crates.yml @@ -15,7 +15,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: fetch-depth: 0 diff --git a/.github/workflows/rustfmt.yml b/.github/workflows/rustfmt.yml index 7efd9e8a7084..96399070a7c0 100644 --- a/.github/workflows/rustfmt.yml +++ b/.github/workflows/rustfmt.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout sources - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Install Rust run: | rustup set profile minimal diff --git a/Cargo.toml b/Cargo.toml index 6613515bbc01..c7e8accbc2ee 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,8 @@ members = [ "pac/*", "boards/*", ] +# Fragments of example files, referenced by `include!()` from BSP examples +exclude = ["boards/examples"] [profile.dev] debug = true diff --git a/README.md b/README.md index 9362a403667c..c8e35033d4a0 100644 --- a/README.md +++ b/README.md @@ -10,16 +10,16 @@ This repository holds various crates that support/enable working with Microchip The Hardware Abstraction Layer (HAL - [![Crates.io](https://img.shields.io/crates/v/atsamd_hal.svg)](https://crates.io/crates/atsamd_hal)) crate encodes a type-safe layer over the raw PACs. This crate implements traits specified by the [embedded-hal](https://github.com/rust-embedded/embedded-hal) project, making it compatible with various drivers in the embedded Rust ecosystem. Cargo features are used to enable support for specific hardware variations and features. Online documentation for commonly-used feature sets is provided: -| Chip family | Documented features | -|:------------|:----------------------------------| -| [samd11c] | samd11c dma defmt async | -| [samd11d] | samd11d dma defmt async | -| [samd21g] | samd21g usb dma defmt async | -| [samd21j] | samd21j usb dma defmt async | -| [samd51g] | samd51g usb sdmmc dma defmt async | -| [samd51j] | samd51j usb sdmmc dma defmt async | -| [samd51n] | samd51n usb sdmmc dma defmt async | -| [samd51p] | samd51p usb sdmmc dma defmt async | +| Chip family | Documented features | +|:------------|:-------------------------------------------------| +| [samd11c] | samd11c dma defmt async undoc-features | +| [samd11d] | samd11d dma defmt async undoc-features | +| [samd21g] | samd21g usb dma defmt async undoc-features | +| [samd21j] | samd21j usb dma defmt async undoc-features | +| [samd51g] | samd51g usb sdmmc dma defmt async undoc-features | +| [samd51j] | samd51j usb sdmmc dma defmt async undoc-features | +| [samd51n] | samd51n usb sdmmc dma defmt async undoc-features | +| [samd51p] | samd51p usb sdmmc dma defmt async undoc-features | [samd11c]: https://atsamd-rs.github.io/atsamd/samd11c/thumbv6m-none-eabi/doc/atsamd_hal/index.html [samd11d]: https://atsamd-rs.github.io/atsamd/samd11d/thumbv6m-none-eabi/doc/atsamd_hal/index.html @@ -153,6 +153,38 @@ If you'd like to build all the same things that the CI would build but on your l $ ./build-all.py ``` +## Undocumented chip features + +Some development board manufacturers, such as Adafruit, sell some boards with pin multiplexing configurations that aren't +explicitly allowed by the datasheet. As a convenience, we offer the option to enable these undocumented features by opting +into the `undoc-features` Cargo feature. Note that even though these have shown to work in at least some situations, we do not +provide any guarantees with respect to those. + +Currently, we provide these features undocumented features: + +### SAMD21: + +* Mark `PA00` as I2C-capable according to `circuit_playground_express`. + +* Mark `PA01` as I2C-capable according to `circuit_playground_express`. + +* Mark `PB02` as I2C-capable according to `circuit_playground_express`. + +* Mark `PB03` as I2C-capable according to `circuit_playground_express`. + +### SAMx5x devices: +* `UndocIoSet1`: Implement an undocumented `IoSet` for PA16, PA17, PB22 & + PB23 configured for `Sercom1`. The `pygamer` & `feather_m4` use this + combination, but it is not listed as valid in the datasheet. + +* `UndocIoSet2`: Implement an undocumented `IoSet` for PA00, PA01, PB22 & + PB23 configured for `Sercom1`. The `itsybitsy_m4` uses this combination, + but it is not listed as valid in the datasheet. + +* Mark `PB02` as I2C-capable according to `metro_m4`. + +* Mark `PB03` as I2C-capable according to `metro_m4`. + ## Running and debugging firmware on target hardware See our wiki page about [loading code onto the device](https://github.com/atsamd-rs/atsamd/wiki/Loading-code-onto-the-device). diff --git a/atsamd-hal-macros/devices.yaml b/atsamd-hal-macros/devices.yaml index 763d74a1003b..bcfb70f40688 100644 --- a/atsamd-hal-macros/devices.yaml +++ b/atsamd-hal-macros/devices.yaml @@ -21,7 +21,9 @@ families: - serial-numbers - dsu - clock - - gclk + - xosc32k + - xosc: { count: 1 } + - gclk: { count: 6 } - pm - sysctrl - wdt @@ -53,7 +55,9 @@ families: - serial-numbers - dsu - clock - - gclk + - xosc32k: { except: ["samd21el", "samd21gl"] } + - xosc: { count: 1 } + - gclk: { count: 8 } - pm - sysctrl - wdt @@ -96,7 +100,9 @@ families: - cmcc - dsu - clock - - gclk + - xosc32k + - xosc: { count: 2 } + - gclk: { count: 12 } - mclk - rstc - ramecc diff --git a/boards/arduino_nano33iot/CHANGELOG.md b/boards/arduino_nano33iot/CHANGELOG.md index 67c09979cbf9..267cc00ff81c 100644 --- a/boards/arduino_nano33iot/CHANGELOG.md +++ b/boards/arduino_nano33iot/CHANGELOG.md @@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.8.0](https://github.com/atsamd-rs/atsamd/compare/arduino_nano33iot-0.7.2...arduino_nano33iot-0.8.0) - 2025-12-29 + +### Dependencies + +- *(nano33iot)* [**breaking**] Update nano33iot HAL dependency to 0.23.0 ([#971](https://github.com/atsamd-rs/atsamd/pull/971)) + ## [0.7.2](https://github.com/atsamd-rs/atsamd/compare/arduino_nano33iot-0.7.1...arduino_nano33iot-0.7.2) - 2024-10-17 ### Refactored diff --git a/boards/arduino_nano33iot/Cargo.toml b/boards/arduino_nano33iot/Cargo.toml index 0bc2a015aa85..29ffe584bc9d 100644 --- a/boards/arduino_nano33iot/Cargo.toml +++ b/boards/arduino_nano33iot/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "arduino_nano33iot" -version = "0.7.2" +version = "0.8.0" authors = ["Gus Wynn "] description = "Board Support crate for the Arduino Nano 33 IOT" keywords = ["no-std", "arm", "cortex-m", "embedded-hal", "arduino"] @@ -15,11 +15,12 @@ version = "0.7" optional = true [dependencies.atsamd-hal] -version = "0.14" +version = "0.23.0" default-features = false +features = ["undoc-features"] [dependencies.usb-device] -version = "0.2" +version = "0.3.2" optional = true [dependencies.rand] @@ -28,10 +29,10 @@ default-features = false features = ["small_rng"] [dev-dependencies] -cortex-m = "0.7" -usbd-serial = "0.1" -panic-halt = "0.2" -panic-semihosting = "0.5" +cortex-m = { version = "0.7", features = ["critical-section-single-core"] } +usbd-serial = "0.2.2" +panic-halt = "1.0.0" +panic-semihosting = "0.6" embedded-graphics = "0.7" st7735-lcd = "0.8" ssd1306 = { version = "0.7", features = ["graphics"] } @@ -41,7 +42,6 @@ ssd1306 = { version = "0.7", features = ["graphics"] } default = ["rt", "atsamd-hal/samd21g"] rt = ["cortex-m-rt", "atsamd-hal/samd21g-rt"] usb = ["atsamd-hal/usb", "usb-device"] -unproven = ["atsamd-hal/unproven"] use_semihosting = [] # for cargo flash @@ -53,7 +53,7 @@ name = "blinky_basic" [[example]] name = "usb_logging" -required-features = ["usb", "unproven"] +required-features = ["usb"] [[example]] name = "serial" diff --git a/boards/arduino_nano33iot/examples/blinky_basic.rs b/boards/arduino_nano33iot/examples/blinky_basic.rs index 665d6793c6ed..eff8e6f2c167 100644 --- a/boards/arduino_nano33iot/examples/blinky_basic.rs +++ b/boards/arduino_nano33iot/examples/blinky_basic.rs @@ -20,12 +20,12 @@ fn main() -> ! { let mut peripherals = Peripherals::take().unwrap(); let core = CorePeripherals::take().unwrap(); let mut clocks = GenericClockController::with_internal_32kosc( - peripherals.GCLK, - &mut peripherals.PM, - &mut peripherals.SYSCTRL, - &mut peripherals.NVMCTRL, + peripherals.gclk, + &mut peripherals.pm, + &mut peripherals.sysctrl, + &mut peripherals.nvmctrl, ); - let pins = bsp::Pins::new(peripherals.PORT); + let pins = bsp::Pins::new(peripherals.port); let mut led: bsp::Led = pins.led_sck.into(); let mut delay = Delay::new(core.SYST, &mut clocks); diff --git a/boards/arduino_nano33iot/examples/i2c_ssd1306.rs b/boards/arduino_nano33iot/examples/i2c_ssd1306.rs index a84e40136961..41bbb464a695 100644 --- a/boards/arduino_nano33iot/examples/i2c_ssd1306.rs +++ b/boards/arduino_nano33iot/examples/i2c_ssd1306.rs @@ -28,7 +28,6 @@ use hal::clock::GenericClockController; use hal::delay::Delay; use hal::pac::{CorePeripherals, Peripherals}; use hal::prelude::*; -use hal::time::KiloHertz; use ssd1306::{prelude::*, I2CDisplayInterface, Ssd1306}; @@ -42,21 +41,21 @@ fn main() -> ! { let mut peripherals = Peripherals::take().unwrap(); let core = CorePeripherals::take().unwrap(); let mut clocks = GenericClockController::with_internal_32kosc( - peripherals.GCLK, - &mut peripherals.PM, - &mut peripherals.SYSCTRL, - &mut peripherals.NVMCTRL, + peripherals.gclk, + &mut peripherals.pm, + &mut peripherals.sysctrl, + &mut peripherals.nvmctrl, ); - let pins = bsp::Pins::new(peripherals.PORT); + let pins = bsp::Pins::new(peripherals.port); let mut delay = Delay::new(core.SYST, &mut clocks); delay.delay_ms(BOOT_DELAY_MS); let i2c = bsp::i2c_master( &mut clocks, - KiloHertz(400), - peripherals.SERCOM4, - &mut peripherals.PM, + 400.kHz(), + peripherals.sercom4, + &mut peripherals.pm, pins.sda, pins.scl, ); diff --git a/boards/arduino_nano33iot/examples/serial.rs b/boards/arduino_nano33iot/examples/serial.rs index aa7a264d9da8..1b9d7e1e5f42 100644 --- a/boards/arduino_nano33iot/examples/serial.rs +++ b/boards/arduino_nano33iot/examples/serial.rs @@ -20,19 +20,19 @@ fn main() -> ! { let mut peripherals = Peripherals::take().unwrap(); let core = CorePeripherals::take().unwrap(); let mut clocks = GenericClockController::with_internal_32kosc( - peripherals.GCLK, - &mut peripherals.PM, - &mut peripherals.SYSCTRL, - &mut peripherals.NVMCTRL, + peripherals.gclk, + &mut peripherals.pm, + &mut peripherals.sysctrl, + &mut peripherals.nvmctrl, ); - let pins = bsp::Pins::new(peripherals.PORT); + let pins = bsp::Pins::new(peripherals.port); let mut delay = Delay::new(core.SYST, &mut clocks); let mut uart = bsp::uart( &mut clocks, - 9600.hz(), - peripherals.SERCOM5, - &mut peripherals.PM, + 9600.Hz(), + peripherals.sercom5, + &mut peripherals.pm, pins.rx, pins.tx, ); diff --git a/boards/arduino_nano33iot/examples/spi_st7735.rs b/boards/arduino_nano33iot/examples/spi_st7735.rs index a867ae5ae116..235af95347db 100644 --- a/boards/arduino_nano33iot/examples/spi_st7735.rs +++ b/boards/arduino_nano33iot/examples/spi_st7735.rs @@ -17,7 +17,6 @@ use hal::clock::GenericClockController; use hal::delay::Delay; use hal::pac::{CorePeripherals, Peripherals}; use hal::prelude::*; -use hal::time::MegaHertz; use embedded_graphics::{ image::{Image, ImageRaw, ImageRawLE}, @@ -35,21 +34,21 @@ fn main() -> ! { let mut peripherals = Peripherals::take().unwrap(); let core = CorePeripherals::take().unwrap(); let mut clocks = GenericClockController::with_internal_32kosc( - peripherals.GCLK, - &mut peripherals.PM, - &mut peripherals.SYSCTRL, - &mut peripherals.NVMCTRL, + peripherals.gclk, + &mut peripherals.pm, + &mut peripherals.sysctrl, + &mut peripherals.nvmctrl, ); - let pins = bsp::Pins::new(peripherals.PORT); + let pins = bsp::Pins::new(peripherals.port); let mut delay = Delay::new(core.SYST, &mut clocks); delay.delay_ms(BOOT_DELAY_MS); let spi = bsp::spi_master( &mut clocks, - MegaHertz(16), - peripherals.SERCOM1, - &mut peripherals.PM, + 16.MHz(), + peripherals.sercom1, + &mut peripherals.pm, pins.led_sck, pins.mosi, pins.miso, diff --git a/boards/arduino_nano33iot/examples/usb_logging.rs b/boards/arduino_nano33iot/examples/usb_logging.rs index 4c0d32394c88..a8ae0f95ff70 100644 --- a/boards/arduino_nano33iot/examples/usb_logging.rs +++ b/boards/arduino_nano33iot/examples/usb_logging.rs @@ -30,19 +30,19 @@ fn main() -> ! { let mut peripherals = Peripherals::take().unwrap(); let mut core = CorePeripherals::take().unwrap(); let mut clocks = GenericClockController::with_internal_32kosc( - peripherals.GCLK, - &mut peripherals.PM, - &mut peripherals.SYSCTRL, - &mut peripherals.NVMCTRL, + peripherals.gclk, + &mut peripherals.pm, + &mut peripherals.sysctrl, + &mut peripherals.nvmctrl, ); - let pins = bsp::Pins::new(peripherals.PORT); + let pins = bsp::Pins::new(peripherals.port); let mut led: bsp::Led = pins.led_sck.into(); let bus_allocator = unsafe { USB_ALLOCATOR = Some(bsp::usb_allocator( - peripherals.USB, + peripherals.usb, &mut clocks, - &mut peripherals.PM, + &mut peripherals.pm, pins.usb_dm, pins.usb_dp, )); @@ -53,9 +53,11 @@ fn main() -> ! { USB_SERIAL = Some(SerialPort::new(&bus_allocator)); USB_BUS = Some( UsbDeviceBuilder::new(&bus_allocator, UsbVidPid(0x2222, 0x3333)) - .manufacturer("Fake company") - .product("Serial port") - .serial_number("TEST") + .strings(&[StringDescriptors::new(LangID::EN_US) + .manufacturer("Fake company") + .product("Serial port") + .serial_number("TEST")]) + .unwrap() .device_class(USB_CLASS_CDC) .build(), ); diff --git a/boards/arduino_nano33iot/src/lib.rs b/boards/arduino_nano33iot/src/lib.rs index 60e100654355..9334e288dd48 100644 --- a/boards/arduino_nano33iot/src/lib.rs +++ b/boards/arduino_nano33iot/src/lib.rs @@ -8,8 +8,7 @@ pub use hal::ehal; pub use hal::pac; use hal::clock::GenericClockController; -use hal::sercom::v2::{spi, uart, Sercom1, Sercom2, Sercom5}; -use hal::sercom::I2CMaster4; +use hal::sercom::{i2c, spi, uart, Sercom1, Sercom2, Sercom4, Sercom5}; use hal::time::Hertz; #[cfg(feature = "usb")] @@ -213,9 +212,9 @@ hal::bsp_pins!( #[cfg(feature = "usb")] pub fn usb_allocator( - usb: pac::USB, + usb: pac::Usb, clocks: &mut GenericClockController, - pm: &mut pac::PM, + pm: &mut pac::Pm, dm: impl Into, dp: impl Into, ) -> UsbBusAllocator { @@ -225,21 +224,29 @@ pub fn usb_allocator( UsbBusAllocator::new(UsbBus::new(usb_clock, pm, dm, dp, usb)) } +/// I2C Pads +pub type I2cPads = i2c::Pads; + +/// I2C device for labelled SDA & SCL pads +pub type I2c = i2c::I2c>; + /// Convenience for setting up the labelled SDA, SCL pins to /// operate as an I2C master running at the specified frequency. pub fn i2c_master( clocks: &mut GenericClockController, - bus_speed: impl Into, - sercom4: pac::SERCOM4, - pm: &mut pac::PM, + baud: impl Into, + sercom: pac::Sercom4, + pm: &mut pac::Pm, sda: impl Into, scl: impl Into, -) -> I2CMaster4 { +) -> I2c { let gclk0 = &clocks.gclk0(); let clock = &clocks.sercom4_core(&gclk0).unwrap(); - let (bus_speed, sda, scl) = (bus_speed.into(), sda.into(), scl.into()); + let freq = clock.freq(); + let baud = baud.into(); + let pads = I2cPads::new(sda.into(), scl.into()); - I2CMaster4::new(clock, bus_speed, sercom4, pm, sda, scl) + i2c::Config::new(pm, sercom, pads, freq).baud(baud).enable() } /// UART pads @@ -252,8 +259,8 @@ pub type Uart = uart::Uart, uart::Duplex>; pub fn uart( clocks: &mut GenericClockController, baud: impl Into, - sercom5: pac::SERCOM5, - pm: &mut pac::PM, + sercom5: pac::Sercom5, + pm: &mut pac::Pm, rx: impl Into, tx: impl Into, ) -> Uart { @@ -283,8 +290,8 @@ pub type Spi = spi::Spi, spi::Duplex>; pub fn spi_master( clocks: &mut GenericClockController, baud: impl Into, - sercom1: pac::SERCOM1, - pm: &mut pac::PM, + sercom1: pac::Sercom1, + pm: &mut pac::Pm, sck: impl Into, mosi: impl Into, miso: impl Into, @@ -295,7 +302,7 @@ pub fn spi_master( let pads = spi::Pads::default().data_in(miso).data_out(mosi).sclk(sclk); spi::Config::new(pm, sercom1, pads, clock.freq()) - .baud(baud) + .baud(baud.into()) .spi_mode(spi::MODE_0) .enable() } @@ -316,8 +323,8 @@ pub type NinaSpi = spi::Spi, spi::Duplex>; pub fn nina_spi_master( clocks: &mut GenericClockController, baud: impl Into, - sercom2: pac::SERCOM2, - pm: &mut pac::PM, + sercom2: pac::Sercom2, + pm: &mut pac::Pm, sck: impl Into, mosi: impl Into, miso: impl Into, @@ -328,7 +335,7 @@ pub fn nina_spi_master( let pads = spi::Pads::default().data_in(miso).data_out(mosi).sclk(sclk); spi::Config::new(pm, sercom2, pads, clock.freq()) - .baud(baud) + .baud(baud.into()) .spi_mode(spi::MODE_0) .enable() } diff --git a/boards/atsame54_xpro/CHANGELOG.md b/boards/atsame54_xpro/CHANGELOG.md index 43e077acfb14..4bee8d6fc5c1 100644 --- a/boards/atsame54_xpro/CHANGELOG.md +++ b/boards/atsame54_xpro/CHANGELOG.md @@ -7,6 +7,30 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.13.3](https://github.com/atsamd-rs/atsamd/compare/atsame54_xpro-0.13.2...atsame54_xpro-0.13.3) - 2026-03-02 + +### Other + +- updated the following local packages: atsamd-hal + +## [0.13.2](https://github.com/atsamd-rs/atsamd/compare/atsame54_xpro-0.13.1...atsame54_xpro-0.13.2) - 2026-02-20 + +### Other + +- updated the following local packages: atsamd-hal + +## [0.13.1](https://github.com/atsamd-rs/atsamd/compare/atsame54_xpro-0.13.0...atsame54_xpro-0.13.1) - 2025-12-29 + +### Other + +- updated the following local packages: atsamd-hal + +## [0.13.0](https://github.com/atsamd-rs/atsamd/compare/atsame54_xpro-0.12.2...atsame54_xpro-0.13.0) - 2025-11-19 + +### Other + +- updated the following local packages: atsamd-hal + ## [0.12.2](https://github.com/atsamd-rs/atsamd/compare/atsame54_xpro-0.12.1...atsame54_xpro-0.12.2) - 2025-06-26 ### Other diff --git a/boards/atsame54_xpro/Cargo.toml b/boards/atsame54_xpro/Cargo.toml index eafd323a3089..175936b23b97 100644 --- a/boards/atsame54_xpro/Cargo.toml +++ b/boards/atsame54_xpro/Cargo.toml @@ -11,7 +11,7 @@ license = "MIT OR Apache-2.0" name = "atsame54_xpro" readme = "README.md" repository = "https://github.com/atsamd-rs/atsamd" -version = "0.12.2" +version = "0.13.3" [dependencies.cortex-m-rt] optional = true @@ -24,7 +24,7 @@ version = "0.7" [dependencies.atsamd-hal] default-features = false path = "../../hal" -version = "0.22.2" +version = "0.23.3" [dependencies.usb-device] optional = true diff --git a/boards/atsame54_xpro/examples/blinky_rtic.rs b/boards/atsame54_xpro/examples/blinky_rtic.rs index e331bedfcfa9..32b42710b483 100644 --- a/boards/atsame54_xpro/examples/blinky_rtic.rs +++ b/boards/atsame54_xpro/examples/blinky_rtic.rs @@ -3,7 +3,7 @@ use atsame54_xpro as bsp; use bsp::hal; -use hal::clock::v2::{clock_system_at_reset, osculp32k::OscUlp1k, rtcosc::RtcOsc}; +use hal::clock::v2::{clock_system_at_reset, rtcosc::RtcOsc}; use hal::prelude::*; use hal::rtc::rtic::rtc_clock; use panic_rtt_target as _; @@ -27,26 +27,18 @@ mod app { #[init] fn init(ctx: init::Context) -> (Shared, Local) { - let mut device = ctx.device; + let device = ctx.device; let mut core: rtic::export::Peripherals = ctx.core; rtt_init_print!(); - let (_buses, clocks, tokens) = clock_system_at_reset( - device.oscctrl, - device.osc32kctrl, - device.gclk, - device.mclk, - &mut device.nvmctrl, - ); - - // Enable the 1 kHz clock from the internal 32 kHz source - let (osculp1k, _) = OscUlp1k::enable(tokens.osculp32k.osculp1k, clocks.osculp32k_base); + let (_buses, clocks, tokens) = + clock_system_at_reset(device.oscctrl, device.osc32kctrl, device.gclk, device.mclk); // Enable the RTC clock with the 1 kHz source. // Note that currently the proof of this (the `RtcOsc` instance) is not // required to start the monotonic. - let _ = RtcOsc::enable(tokens.rtcosc, osculp1k); + let _ = RtcOsc::enable(tokens.rtcosc, clocks.osculp.osculp1k); // Start the monotonic Mono::start(device.rtc); diff --git a/boards/atsame54_xpro/examples/mcan.rs b/boards/atsame54_xpro/examples/mcan.rs index 5cad635297f6..d2f9ad8fb671 100644 --- a/boards/atsame54_xpro/examples/mcan.rs +++ b/boards/atsame54_xpro/examples/mcan.rs @@ -21,7 +21,7 @@ use atsame54_xpro as bsp; use bsp::hal; -use clock::{osculp32k::OscUlp1k, rtcosc::RtcOsc}; +use clock::rtcosc::RtcOsc; use hal::clock::v2 as clock; use hal::eic::{Ch15, Eic, ExtInt, Sense}; use hal::gpio::{Interrupt as GpioInterrupt, *}; @@ -83,7 +83,7 @@ type Aux = mcan::bus::Aux< clock::types::Can1, hal::can::Dependencies< clock::types::Can1, - clock::gclk::Gclk0Id, + clock::pclk::PclkSource, bsp::Ata6561Rx, bsp::Ata6561Tx, bsp::pac::Can1, @@ -115,7 +115,7 @@ mod app { can_memory: SharedMemory = SharedMemory::new() ])] fn init(ctx: init::Context) -> (Shared, Local) { - let mut device = ctx.device; + let device = ctx.device; rtt_init_print!(); rprintln!("Application up!"); @@ -125,16 +125,12 @@ mod app { device.osc32kctrl, device.gclk, device.mclk, - &mut device.nvmctrl, ); - // Enable the 1 kHz clock from the internal 32 kHz source - let (osculp1k, _) = OscUlp1k::enable(tokens.osculp32k.osculp1k, clocks.osculp32k_base); - // Enable the RTC clock with the 1 kHz source. // Note that currently the proof of this (the `RtcOsc` instance) is not // required to start the monotonic. - let _ = RtcOsc::enable(tokens.rtcosc, osculp1k); + let _ = RtcOsc::enable(tokens.rtcosc, clocks.osculp.osculp1k); // Start the monotonic Mono::start(device.rtc); diff --git a/boards/circuit_playground_express/CHANGELOG.md b/boards/circuit_playground_express/CHANGELOG.md index f647054ab54b..d162aad62cdc 100644 --- a/boards/circuit_playground_express/CHANGELOG.md +++ b/boards/circuit_playground_express/CHANGELOG.md @@ -7,6 +7,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.12.1](https://github.com/atsamd-rs/atsamd/compare/circuit_playground_express-0.12.0...circuit_playground_express-0.12.1) - 2025-12-29 + +### Dependencies + +- *(circuit-playground-express)* Add RTIC support for CPE ([#966](https://github.com/atsamd-rs/atsamd/pull/966)) + +### Other + +- fix a few clippy errors for circuit-playground-express +- Improve cpe examples + +## [0.12.0](https://github.com/atsamd-rs/atsamd/compare/circuit_playground_express-0.11.1...circuit_playground_express-0.12.0) - 2025-11-20 + +### Dependencies + +- *(circuit-playground-express)* Update dependencies and HAL to 0.23.0 ([#953](https://github.com/atsamd-rs/atsamd/pull/953)) + ## [0.11.1](https://github.com/atsamd-rs/atsamd/compare/circuit_playground_express-0.11.0...circuit_playground_express-0.11.1) - 2024-10-17 ### Added diff --git a/boards/circuit_playground_express/Cargo.toml b/boards/circuit_playground_express/Cargo.toml index 1245b52a5c45..53ef6e5dda00 100644 --- a/boards/circuit_playground_express/Cargo.toml +++ b/boards/circuit_playground_express/Cargo.toml @@ -1,32 +1,44 @@ [package] name = "circuit_playground_express" -version = "0.11.1" +categories = ["embedded", "hardware-support", "no-std"] +version = "0.12.1" authors = ["Paul Sajna "] description = "Board Support crate for the Adafruit Circuit Playground Express" keywords = ["no-std", "arm", "cortex-m", "embedded-hal"] license = "MIT OR Apache-2.0" repository = "https://github.com/atsamd-rs/atsamd" readme = "README.md" -edition = "2018" +edition = "2024" [dependencies.cortex-m-rt] version = "0.7" optional = true [dependencies.atsamd-hal] -version = "0.14" +version = "0.23.0" default-features = false +[dependencies.cortex-m] +features = ["critical-section-single-core"] +version = "0.7" + +[dependencies.rtic] +features = ["thumbv6-backend"] +optional = true +version = "2.1.1" + [dependencies.usb-device] -version = "0.2" +version = "0.3.2" optional = true [dev-dependencies] -cortex-m = "0.7" panic-halt = "0.2" -panic-semihosting = "0.5" -usbd-serial = "0.1" +panic-semihosting = "0.6" +usbd-serial = "0.2" +nom = {version = "5", default-features = false} +rtic-monotonics = {version = "1.3.0", features = ["cortex-m-systick", "systick-10khz"]} smart-leds = "0.3.0" +heapless = "0.9.2" [dev-dependencies.ws2812-timer-delay] features = ["slow"] @@ -34,9 +46,11 @@ version = "0.3.0" [features] # ask the HAL to enable atsamd21g support -default = ["rt", "atsamd-hal/samd21g"] +# CPE uses all kinds of undoc'd i2c functionality. +default = ["rt", "atsamd-hal/samd21g", "atsamd-hal/undoc-features"] +dma = ["atsamd-hal/dma"] rt = ["cortex-m-rt", "atsamd-hal/samd21g-rt"] -unproven = ["atsamd-hal/unproven"] +rtic = ["dep:rtic", "atsamd-hal/rtic"] usb = ["atsamd-hal/usb", "usb-device"] use_semihosting = [] @@ -47,9 +61,13 @@ chip = "ATSAMD21G18A" [[example]] name = "blinky_basic" +[[example]] +name = "blinky_rtic" +required-features = ["rtic"] + [[example]] name = "uart" [[example]] name = "usb_serial" -required-features = ["usb"] +required-features = ["rtic", "usb"] diff --git a/boards/circuit_playground_express/examples/blinky_basic.rs b/boards/circuit_playground_express/examples/blinky_basic.rs index c3387886de5c..31af1cf553e9 100644 --- a/boards/circuit_playground_express/examples/blinky_basic.rs +++ b/boards/circuit_playground_express/examples/blinky_basic.rs @@ -21,12 +21,12 @@ fn main() -> ! { let mut peripherals = Peripherals::take().unwrap(); let core = CorePeripherals::take().unwrap(); let mut clocks = GenericClockController::with_internal_32kosc( - peripherals.GCLK, - &mut peripherals.PM, - &mut peripherals.SYSCTRL, - &mut peripherals.NVMCTRL, + peripherals.gclk, + &mut peripherals.pm, + &mut peripherals.sysctrl, + &mut peripherals.nvmctrl, ); - let pins = bsp::Pins::new(peripherals.PORT); + let pins = bsp::Pins::new(peripherals.port); let mut red_led: bsp::RedLed = pins.d13.into(); let mut delay = Delay::new(core.SYST, &mut clocks); loop { diff --git a/boards/circuit_playground_express/examples/blinky_rtic.rs b/boards/circuit_playground_express/examples/blinky_rtic.rs new file mode 100644 index 000000000000..86374239ce21 --- /dev/null +++ b/boards/circuit_playground_express/examples/blinky_rtic.rs @@ -0,0 +1,77 @@ +//! Uses RTIC with a software task to blink an LED. +#![no_std] +#![no_main] + +use bsp::hal; +use circuit_playground_express as bsp; +use hal::rtc::rtic::rtc_clock; + +#[cfg(not(feature = "use_semihosting"))] +use panic_halt as _; +#[cfg(feature = "use_semihosting")] +use panic_semihosting as _; + +hal::rtc_monotonic!(Mono, rtc_clock::ClockCustom<8_192>); + +#[rtic::app(device = bsp::pac, dispatchers = [EVSYS])] +mod app { + use super::*; + use bsp::{hal, pin_alias}; + use hal::clock::{ClockGenId, ClockSource, GenericClockController}; + use hal::pac::Peripherals; + use hal::prelude::*; + + #[local] + struct Local {} + + #[shared] + struct Shared { + // The LED could be a local resource, since it is only used in one task + // But we want to showcase shared resources and locking + red_led: bsp::RedLed, + } + + #[init] + fn init(cx: init::Context) -> (Shared, Local) { + let mut peripherals: Peripherals = cx.device; + let pins = bsp::Pins::new(peripherals.port); + let mut core: rtic::export::Peripherals = cx.core; + let mut clocks = GenericClockController::with_internal_32kosc( + peripherals.gclk, + &mut peripherals.pm, + &mut peripherals.sysctrl, + &mut peripherals.nvmctrl, + ); + + // Set the RTC clock to use a 8.192 kHz clock derived from the internal 32 kHz + // oscillator. + let rtc_clock_src = clocks + .configure_gclk_divider_and_source(ClockGenId::Gclk2, 4, ClockSource::Osc32k, true) + .unwrap(); + clocks.configure_standby(ClockGenId::Gclk2, true); + let _ = clocks.rtc(&rtc_clock_src).unwrap(); + + let red_led: bsp::RedLed = pin_alias!(pins.red_led).into(); + + // Start the monotonic + Mono::start(peripherals.rtc); + + // We can use the RTC in standby for maximum power savings + core.SCB.set_sleepdeep(); + + // Start the blink task + blink::spawn().unwrap(); + + (Shared { red_led }, Local {}) + } + + #[task(shared = [red_led])] + async fn blink(mut cx: blink::Context) { + loop { + // If the LED were a local resource, the lock would not be necessary + cx.shared.red_led.lock(|led| led.toggle().unwrap()); + + Mono::delay(1u64.secs()).await; + } + } +} diff --git a/boards/circuit_playground_express/examples/neopixel_rainbow.rs b/boards/circuit_playground_express/examples/neopixel_rainbow.rs index 754221562499..ccc0e4790b82 100644 --- a/boards/circuit_playground_express/examples/neopixel_rainbow.rs +++ b/boards/circuit_playground_express/examples/neopixel_rainbow.rs @@ -20,13 +20,14 @@ use circuit_playground_express as bsp; use bsp::entry; use hal::clock::GenericClockController; use hal::delay::Delay; -use hal::prelude::*; +use hal::fugit::ExtU32; use hal::timer::TimerCounter; +use hal::timer_traits::InterruptDrivenTimer; use pac::{CorePeripherals, Peripherals}; use smart_leds::{ - hsv::{hsv2rgb, Hsv}, SmartLedsWrite, + hsv::{Hsv, hsv2rgb}, }; use ws2812_timer_delay as ws2812; @@ -35,24 +36,25 @@ fn main() -> ! { let mut peripherals = Peripherals::take().unwrap(); let core = CorePeripherals::take().unwrap(); let mut clocks = GenericClockController::with_internal_32kosc( - peripherals.GCLK, - &mut peripherals.PM, - &mut peripherals.SYSCTRL, - &mut peripherals.NVMCTRL, + peripherals.gclk, + &mut peripherals.pm, + &mut peripherals.sysctrl, + &mut peripherals.nvmctrl, ); - let pins = bsp::Pins::new(peripherals.PORT); + let pins = bsp::Pins::new(peripherals.port); let mut delay = Delay::new(core.SYST, &mut clocks); let gclk0 = clocks.gclk0(); let timer_clock = clocks.tcc2_tc3(&gclk0).unwrap(); - let mut timer = TimerCounter::tc3_(&timer_clock, peripherals.TC3, &mut peripherals.PM); - timer.start(3.mhz()); + let mut timer = TimerCounter::tc3_(&timer_clock, peripherals.tc3, &mut peripherals.pm); + timer.start(1.micros()); let neopixel_pin: bsp::NeoPixel = pins.d8.into(); let mut neopixel = ws2812::Ws2812::new(timer, neopixel_pin); // Loop through all of the available hue values (colors) to make a // rainbow effect from the onboard neopixel + use hal::ehal::delay::DelayNs; loop { for j in 0..255u8 { let colors = [hsv2rgb(Hsv { @@ -61,7 +63,7 @@ fn main() -> ! { val: 2, }); 10]; neopixel.write(colors.iter().cloned()).unwrap(); - delay.delay_ms(5u8); + delay.delay_ms(5); } } } diff --git a/boards/circuit_playground_express/examples/uart.rs b/boards/circuit_playground_express/examples/uart.rs index 9ca526a645ab..91afbe5e20dc 100644 --- a/boards/circuit_playground_express/examples/uart.rs +++ b/boards/circuit_playground_express/examples/uart.rs @@ -1,3 +1,6 @@ +//! This example shows how to use the UART to perform transfers using the +//! embedded-hal-nb traits. + #![no_std] #![no_main] @@ -9,58 +12,62 @@ use panic_semihosting as _; use bsp::hal; use bsp::pac; use circuit_playground_express as bsp; +use hal::nb; -use bsp::entry; +use bsp::{entry, periph_alias, pin_alias}; use hal::clock::GenericClockController; -use hal::prelude::*; +use hal::ehal_nb::serial::{Read, Write}; +use hal::fugit::RateExtU32; -use pac::{CorePeripherals, Peripherals}; +use pac::Peripherals; #[entry] fn main() -> ! { let mut peripherals = Peripherals::take().unwrap(); - let core = CorePeripherals::take().unwrap(); let mut clocks = GenericClockController::with_internal_32kosc( - peripherals.GCLK, - &mut peripherals.PM, - &mut peripherals.SYSCTRL, - &mut peripherals.NVMCTRL, + peripherals.gclk, + &mut peripherals.pm, + &mut peripherals.sysctrl, + &mut peripherals.nvmctrl, ); - let mut pm = peripherals.PM; - let pins = bsp::Pins::new(peripherals.PORT); - let mut delay = hal::delay::Delay::new(core.SYST, &mut clocks); + let mut pm = peripherals.pm; + let pins = bsp::Pins::new(peripherals.port); - let mut red_led = pins.d13.into_push_pull_output(); + // Take peripheral and pins + let uart_sercom = periph_alias!(peripherals.uart_sercom); + let uart_rx = pin_alias!(pins.uart_rx); + let uart_tx = pin_alias!(pins.uart_tx); - // Setup UART peripheral. - let (rx_pin, tx_pin) = (pins.a6, pins.a7); - let mut uart = bsp::uart( + // Setup UART peripheral + let uart = bsp::uart( &mut clocks, - 9600.hz(), - peripherals.SERCOM4, + 9600.Hz(), + uart_sercom, &mut pm, - rx_pin, - tx_pin, + uart_rx, + uart_tx, ); - // Write out a message on start up. - for byte in b"Hello world!\r\n" { - nb::block!(uart.write(*byte)).unwrap(); - } + // Split uart in rx + tx halves + let (mut rx, mut tx) = uart.split(); + + // Make buffers to store data to send/receive + let mut rx_buffer = [0x00; 50]; + let tx_buffer = b"Hello, world!"; loop { - match uart.read() { - Ok(byte) => { - // Echo all received characters. - nb::block!(uart.write(byte)).unwrap(); + // Send data. We block on each byte, but we could also perform some tasks while + // waiting for the byte to finish sending. + for c in tx_buffer.iter() { + nb::block!(tx.write(*c)).unwrap(); + } - // Blink the red led to show that a character has arrived. - red_led.set_high().unwrap(); - delay.delay_ms(2u16); - red_led.set_low().unwrap(); - } - Err(_) => delay.delay_ms(5u16), - }; + // Receive data. We block on each byte, but we could also perform some tasks + // while waiting for the byte to finish sending. + rx.flush_rx_buffer(); + for c in rx_buffer.iter_mut() { + *c = nb::block!(rx.read()).unwrap(); + } } } diff --git a/boards/circuit_playground_express/examples/usb_serial.rs b/boards/circuit_playground_express/examples/usb_serial.rs index b99fbcd7c43e..5ebf2623aa72 100644 --- a/boards/circuit_playground_express/examples/usb_serial.rs +++ b/boards/circuit_playground_express/examples/usb_serial.rs @@ -1,103 +1,135 @@ #![no_std] #![no_main] +use bsp::hal; +use circuit_playground_express as bsp; +use core::mem::MaybeUninit; +use hal::rtc::rtic::rtc_clock; + #[cfg(not(feature = "use_semihosting"))] use panic_halt as _; #[cfg(feature = "use_semihosting")] use panic_semihosting as _; -use cortex_m::asm::delay as cycle_delay; -use cortex_m::peripheral::NVIC; -use usb_device::bus::UsbBusAllocator; -use usb_device::prelude::*; -use usbd_serial::{SerialPort, USB_CLASS_CDC}; +hal::rtc_monotonic!(Mono, rtc_clock::ClockCustom<8_192>); -use bsp::hal; -use bsp::pac; -use circuit_playground_express as bsp; +#[rtic::app(device = bsp::pac, dispatchers = [EVSYS])] +mod app { + use super::*; + use usb_device::bus::UsbBusAllocator; + use usb_device::prelude::*; + use usbd_serial::{SerialPort, USB_CLASS_CDC}; + + use bsp::pin_alias; + use hal::clock::{ClockGenId, ClockSource, GenericClockController}; + use hal::pac::Peripherals; + use hal::prelude::*; + use hal::usb::UsbBus; + + #[local] + struct Local { + // usb_allocator: UsbBusAllocator, + } + + #[shared] + struct Shared { + // The LED could be a local resource, since it is only used in one task + // But we want to showcase shared resources and locking + red_led: bsp::RedLed, + usb_bus: UsbDevice<'static, UsbBus>, + usb_serial: SerialPort<'static, UsbBus>, + } + + #[init(local=[usb_allocator: MaybeUninit> = MaybeUninit::uninit()])] + fn init(cx: init::Context) -> (Shared, Local) { + let mut peripherals: Peripherals = cx.device; + let mut core: rtic::export::Peripherals = cx.core; + let mut clocks = GenericClockController::with_internal_32kosc( + peripherals.gclk, + &mut peripherals.pm, + &mut peripherals.sysctrl, + &mut peripherals.nvmctrl, + ); + let pins = bsp::Pins::new(peripherals.port); + + let red_led: bsp::RedLed = pin_alias!(pins.red_led).into(); -use bsp::entry; -use hal::clock::GenericClockController; -use hal::prelude::*; -use hal::usb::UsbBus; -use pac::{interrupt, CorePeripherals, Peripherals}; - -#[entry] -fn main() -> ! { - let mut peripherals = Peripherals::take().unwrap(); - let mut core = CorePeripherals::take().unwrap(); - let mut clocks = GenericClockController::with_internal_32kosc( - peripherals.GCLK, - &mut peripherals.PM, - &mut peripherals.SYSCTRL, - &mut peripherals.NVMCTRL, - ); - let pins = bsp::Pins::new(peripherals.PORT); - let mut red_led: bsp::RedLed = pins.d13.into(); - - let bus_allocator = unsafe { - USB_ALLOCATOR = Some(bsp::usb_allocator( - peripherals.USB, + *cx.local.usb_allocator = MaybeUninit::new(bsp::usb_allocator( + peripherals.usb, &mut clocks, - &mut peripherals.PM, + &mut peripherals.pm, pins.usb_dm, pins.usb_dp, )); - USB_ALLOCATOR.as_ref().unwrap() - }; - - unsafe { - USB_SERIAL = Some(SerialPort::new(&bus_allocator)); - USB_BUS = Some( - UsbDeviceBuilder::new(&bus_allocator, UsbVidPid(0x16c0, 0x27dd)) + // The usb allocator is initialized just above, which makes sure that + // usb_allocator is allocated by this point. The reason it is done this + // way is to avoid runtime checks for wheather the allocator is update + // that would be required if we use an Option> = None; -static mut USB_BUS: Option> = None; -static mut USB_SERIAL: Option> = None; - -fn poll_usb() { - unsafe { - USB_BUS.as_mut().map(|usb_dev| { - USB_SERIAL.as_mut().map(|serial| { - usb_dev.poll(&mut [serial]); - let mut buf = [0u8; 64]; - - if let Ok(count) = serial.read(&mut buf) { - for (i, c) in buf.iter().enumerate() { - if i >= count { - break; - } - serial.write(&[c.clone()]).ok(); - } - }; - }); - }); - }; -} + #[task(binds = USB, shared = [usb_bus, usb_serial])] + fn poll_usb(cx: poll_usb::Context) { + let mut serial = cx.shared.usb_serial; + let mut usb_bus = cx.shared.usb_bus; -#[interrupt] -fn USB() { - poll_usb(); + (&mut serial, &mut usb_bus).lock(|s, b| { + if !b.poll(&mut [s]) { + return; + } + let mut buf = [0u8; 64]; + if let Ok(count) = s.read(&mut buf) { + for (i, c) in buf.iter().enumerate() { + if i >= count { + break; + } + s.write(&[*c]).ok(); + } + }; + }) + } } diff --git a/boards/circuit_playground_express/src/lib.rs b/boards/circuit_playground_express/src/lib.rs index 37941170e076..0862d903721c 100644 --- a/boards/circuit_playground_express/src/lib.rs +++ b/boards/circuit_playground_express/src/lib.rs @@ -8,15 +8,23 @@ pub use hal::ehal; pub use hal::pac; use hal::clock::GenericClockController; +use hal::fugit::RateExtU32; use hal::prelude::*; -use hal::sercom::v2::spi; -use hal::sercom::v2::uart::{self, BaudMode, Oversampling}; -use hal::sercom::v2::{Sercom0, Sercom3, Sercom4}; -use hal::sercom::I2CMaster5; -use hal::time::{Hertz, MegaHertz}; +use hal::sercom::i2c; +use hal::sercom::spi; +use hal::sercom::uart::{self, BaudMode, Oversampling}; +use hal::sercom::{Sercom0, Sercom3, Sercom4}; +use hal::time::Hertz; #[cfg(feature = "usb")] -use hal::usb::{usb_device::bus::UsbBusAllocator, UsbBus}; +use hal::usb::{UsbBus, usb_device::bus::UsbBusAllocator}; + +hal::bsp_peripherals!( + Sercom0 { SpiSercom } + Sercom1 { AccelSercom } + Sercom4 { UartSercom } + Sercom5 { I2cSercom } +); /// Definitions related to pins and pin aliases pub mod pins { @@ -186,9 +194,9 @@ pub type Spi = spi::Spi, spi::Duplex>; /// any OutputPin, or even a pulled up line on the slave. pub fn spi_master( clocks: &mut GenericClockController, - baud: impl Into, - sercom0: pac::SERCOM0, - pm: &mut pac::PM, + baud: Hertz, + sercom0: pac::Sercom0, + pm: &mut pac::Pm, sck: impl Into, mosi: impl Into, miso: impl Into, @@ -217,8 +225,8 @@ pub type FlashSpi = spi::Spi, spi::Duplex>; /// SPI Master. pub fn flash_spi_master( clocks: &mut GenericClockController, - sercom3: pac::SERCOM3, - pm: &mut pac::PM, + sercom3: pac::Sercom3, + pm: &mut pac::Pm, sck: impl Into, mosi: impl Into, miso: impl Into, @@ -230,7 +238,7 @@ pub fn flash_spi_master( let (sck, mosi, miso, mut cs) = (sck.into(), mosi.into(), miso.into(), cs.into()); let pads = spi::Pads::default().data_in(miso).data_out(mosi).sclk(sck); let spi = spi::Config::new(pm, sercom3, pads, freq) - .baud(MegaHertz(48)) + .baud(48.MHz()) .spi_mode(spi::MODE_0) .enable(); @@ -239,25 +247,33 @@ pub fn flash_spi_master( (spi, cs) } -/// I2C master for the labelled SDA & SCL pins -pub type I2C = I2CMaster5; +/// I2C pads for the labelled I2C peripheral +/// +/// You can use these pads with other, user-defined [`i2c::Config`]urations. +pub type I2cPads = i2c::Pads; + +/// I2C master for the labelled I2C peripheral +/// +/// This type implements [`Read`](ehal::blocking::i2c::Read), +/// [`Write`](ehal::blocking::i2c::Write) and +/// [`WriteRead`](ehal::blocking::i2c::WriteRead). +pub type I2c = i2c::I2c>; /// Convenience for setting up the labelled SDA, SCL pins to /// operate as an I2C master running at the specified frequency. pub fn i2c_master( clocks: &mut GenericClockController, - baud: impl Into, - sercom5: pac::SERCOM5, - pm: &mut pac::PM, + baud: Hertz, + sercom: I2cSercom, + pm: &mut pac::Pm, sda: impl Into, scl: impl Into, -) -> I2C { +) -> I2c { let gclk0 = clocks.gclk0(); let clock = &clocks.sercom5_core(&gclk0).unwrap(); - let baud = baud.into(); - let sda = sda.into(); - let scl = scl.into(); - I2CMaster5::new(clock, baud, sercom5, pm, sda, scl) + let freq = clock.freq(); + let pads = i2c::Pads::new(sda.into(), scl.into()); + i2c::Config::new(pm, sercom, pads, freq).baud(baud).enable() } /// UART pads for the labelled RX & TX pins @@ -270,15 +286,14 @@ pub type Uart = uart::Uart, uart::Duplex>; /// operate as a UART device running at the specified baud. pub fn uart( clocks: &mut GenericClockController, - baud: impl Into, - sercom4: pac::SERCOM4, - pm: &mut pac::PM, + baud: Hertz, + sercom4: pac::Sercom4, + pm: &mut pac::Pm, uart_rx: impl Into, uart_tx: impl Into, ) -> Uart { let gclk0 = clocks.gclk0(); let clock = &clocks.sercom4_core(&gclk0).unwrap(); - let baud = baud.into(); let pads = uart::Pads::default().rx(uart_rx.into()).tx(uart_tx.into()); uart::Config::new(pm, sercom4, pads, clock.freq()) .baud(baud, BaudMode::Fractional(Oversampling::Bits16)) @@ -288,9 +303,9 @@ pub fn uart( #[cfg(feature = "usb")] /// Convenience function for setting up USB pub fn usb_allocator( - usb: pac::USB, + usb: pac::Usb, clocks: &mut GenericClockController, - pm: &mut pac::PM, + pm: &mut pac::Pm, dm: impl Into, dp: impl Into, ) -> UsbBusAllocator { diff --git a/boards/examples/m4-adc.rs b/boards/examples/m4-adc.rs new file mode 100644 index 000000000000..0afcc86f8238 --- /dev/null +++ b/boards/examples/m4-adc.rs @@ -0,0 +1,63 @@ +// This file is included by one or more BSP examples. In normal usage, firmware +// source code needs to start with something like: +// +// ``` +// #![no_std] +// #![no_main] +// use feather_m4 as bsp; +//``` + +use atsamd_hal::adc::AdcBuilder; + +use bsp::hal; +use bsp::pac; + +#[cfg(not(feature = "use_semihosting"))] +use panic_halt as _; +#[cfg(feature = "use_semihosting")] +use panic_semihosting as _; + +use bsp::entry; +use bsp::Pins; +use pac::{CorePeripherals, Peripherals}; + +use hal::{ + adc::{Accumulation, Prescaler}, + clock::v2::{clock_system_at_reset, pclk::Pclk}, +}; + +#[entry] +fn main() -> ! { + let peripherals = Peripherals::take().unwrap(); + let _core = CorePeripherals::take().unwrap(); + + let pins = Pins::new(peripherals.port); + + let (mut buses, clocks, tokens) = clock_system_at_reset( + peripherals.oscctrl, + peripherals.osc32kctrl, + peripherals.gclk, + peripherals.mclk, + ); + + // Enable the ADC0 ABP clock... + let apb_adc0 = buses.apb.enable(tokens.apbs.adc0); + // ...and enable the ADC0 PCLK. Both of these are required for the + // ADC to run. + let (pclk_adc0, _gclk0) = Pclk::enable(tokens.pclks.adc0, clocks.gclk0); + + let mut adc = AdcBuilder::new(Accumulation::single(atsamd_hal::adc::AdcResolution::_12)) + .with_clock_cycles_per_sample(5) + // Overruns if clock divider < 32 in debug mode + .with_clock_divider(Prescaler::Div32) + .with_vref(atsamd_hal::adc::Reference::Arefa) + .enable(peripherals.adc0, apb_adc0, pclk_adc0) + .unwrap(); + let mut adc_pin = pins.a0.into_alternate(); + + loop { + let _res = adc.read(&mut adc_pin); + #[cfg(feature = "use_semihosting")] + let _ = cortex_m_semihosting::hprintln!("ADC value: {}", _res); + } +} diff --git a/boards/examples/m4-blinky_rtic.rs b/boards/examples/m4-blinky_rtic.rs new file mode 100644 index 000000000000..2a055d3c0918 --- /dev/null +++ b/boards/examples/m4-blinky_rtic.rs @@ -0,0 +1,82 @@ +// Blink an led using an RTIC software task and the RTC-based monotonic. +// +// This file is included by one or more BSP examples. In normal usage, firmware +// source code needs to start with something like: +// +// ``` +// #![no_std] +// #![no_main] +// use feather_m4 as bsp; +//``` + +use bsp::{hal, Pins, RedLed}; +#[cfg(not(feature = "use_semihosting"))] +use panic_halt as _; +#[cfg(feature = "use_semihosting")] +use panic_semihosting as _; + +use hal::clock::v2::{clock_system_at_reset, rtcosc::RtcOsc}; +use hal::prelude::*; +use hal::rtc::rtic::rtc_clock; +use rtic::app; + +hal::rtc_monotonic!(Mono, rtc_clock::Clock1k); + +#[app(device = bsp::pac, dispatchers = [EVSYS_0])] +mod app { + use super::*; + + #[local] + struct Resources {} + + #[shared] + struct Shared { + // The LED could be a local resource, since it is only used in one task + // But we want to showcase shared resources and locking + red_led: RedLed, + } + + #[init] + fn init(cx: init::Context) -> (Shared, Resources) { + let device = cx.device; + let mut core: rtic::export::Peripherals = cx.core; + + // Use v2 of the clocks API so that we can set the RTC clock source + let (_, clocks, tokens) = + clock_system_at_reset(device.oscctrl, device.osc32kctrl, device.gclk, device.mclk); + + // Enable the RTC clock with the internal 1 kHz source. + // Note that currently the proof of this (the `RtcOsc` instance) is not + // required to start the monotonic. + let _ = RtcOsc::enable(tokens.rtcosc, clocks.osculp.osculp1k); + + // Start the monotonic + Mono::start(device.rtc); + + let pins = Pins::new(device.port); + + // We can use the RTC in standby for maximum power savings + core.SCB.set_sleepdeep(); + + blink_led::spawn().ok().unwrap(); + + ( + Shared { + red_led: pins.d13.into_push_pull_output(), + }, + Resources {}, + ) + } + + /// This function is spawned and never returns. + #[task(priority = 1, shared=[red_led])] + async fn blink_led(mut cx: blink_led::Context) { + loop { + // If the LED were a local resource, the lock would not be necessary + cx.shared.red_led.lock(|led| { + led.toggle().unwrap(); + }); + Mono::delay(400u64.millis()).await; + } + } +} diff --git a/boards/feather_m0/CHANGELOG.md b/boards/feather_m0/CHANGELOG.md index 43b4b9dad14b..62b20d557e78 100644 --- a/boards/feather_m0/CHANGELOG.md +++ b/boards/feather_m0/CHANGELOG.md @@ -7,6 +7,30 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.20.3](https://github.com/atsamd-rs/atsamd/compare/feather_m0-0.20.2...feather_m0-0.20.3) - 2026-03-02 + +### Other + +- updated the following local packages: atsamd-hal + +## [0.20.2](https://github.com/atsamd-rs/atsamd/compare/feather_m0-0.20.1...feather_m0-0.20.2) - 2026-02-20 + +### Other + +- updated the following local packages: atsamd-hal + +## [0.20.1](https://github.com/atsamd-rs/atsamd/compare/feather_m0-0.20.0...feather_m0-0.20.1) - 2025-12-29 + +### Other + +- updated the following local packages: atsamd-hal + +## [0.20.0](https://github.com/atsamd-rs/atsamd/compare/feather_m0-0.19.2...feather_m0-0.20.0) - 2025-11-19 + +### Other + +- updated the following local packages: atsamd-hal + ## [0.19.2](https://github.com/atsamd-rs/atsamd/compare/feather_m0-0.19.1...feather_m0-0.19.2) - 2025-06-26 ### Other diff --git a/boards/feather_m0/Cargo.toml b/boards/feather_m0/Cargo.toml index 81afb47d90b0..cda71df03fcb 100644 --- a/boards/feather_m0/Cargo.toml +++ b/boards/feather_m0/Cargo.toml @@ -9,7 +9,7 @@ name = "feather_m0" readme = "README.md" repository = "https://github.com/atsamd-rs/atsamd" resolver = "2" -version = "0.19.2" +version = "0.20.3" # for cargo flash [package.metadata] @@ -22,7 +22,7 @@ version = "0.7" [dependencies.atsamd-hal] default-features = false path = "../../hal" -version = "0.22.2" +version = "0.23.3" [dependencies.cortex-m] features = ["critical-section-single-core"] diff --git a/boards/feather_m4/CHANGELOG.md b/boards/feather_m4/CHANGELOG.md index bd13d43e1c30..94c78dbfd744 100644 --- a/boards/feather_m4/CHANGELOG.md +++ b/boards/feather_m4/CHANGELOG.md @@ -7,6 +7,30 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.18.3](https://github.com/atsamd-rs/atsamd/compare/feather_m4-0.18.2...feather_m4-0.18.3) - 2026-03-02 + +### Other + +- updated the following local packages: atsamd-hal + +## [0.18.2](https://github.com/atsamd-rs/atsamd/compare/feather_m4-0.18.1...feather_m4-0.18.2) - 2026-02-20 + +### Other + +- updated the following local packages: atsamd-hal + +## [0.18.1](https://github.com/atsamd-rs/atsamd/compare/feather_m4-0.18.0...feather_m4-0.18.1) - 2025-12-29 + +### Other + +- updated the following local packages: atsamd-hal + +## [0.18.0](https://github.com/atsamd-rs/atsamd/compare/feather_m4-0.17.2...feather_m4-0.18.0) - 2025-11-19 + +### Changed + +- [**breaking**] Gate undocumented chip features behind the `undoc-features` Cargo feature ([#956](https://github.com/atsamd-rs/atsamd/pull/956)) + ## [0.17.2](https://github.com/atsamd-rs/atsamd/compare/feather_m4-0.17.1...feather_m4-0.17.2) - 2025-06-26 ### Other diff --git a/boards/feather_m4/Cargo.toml b/boards/feather_m4/Cargo.toml index cb9e61f21f5f..8c7da29207f7 100644 --- a/boards/feather_m4/Cargo.toml +++ b/boards/feather_m4/Cargo.toml @@ -9,7 +9,7 @@ license = "MIT OR Apache-2.0" name = "feather_m4" readme = "README.md" repository = "https://github.com/atsamd-rs/atsamd" -version = "0.17.2" +version = "0.18.3" # for cargo flash [package.metadata] @@ -26,7 +26,8 @@ version = "0.7" [dependencies.atsamd-hal] default-features = false path = "../../hal" -version = "0.22.2" +version = "0.23.3" +features = ["undoc-features"] [dependencies.usb-device] optional = true diff --git a/boards/feather_m4/examples/adc.rs b/boards/feather_m4/examples/adc.rs index b6b892cf43d3..9f90fe3682a3 100644 --- a/boards/feather_m4/examples/adc.rs +++ b/boards/feather_m4/examples/adc.rs @@ -1,59 +1,5 @@ #![no_std] #![no_main] -use atsamd_hal::adc::AdcBuilder; use feather_m4 as bsp; - -use bsp::hal; -use bsp::pac; - -#[cfg(not(feature = "use_semihosting"))] -use panic_halt as _; -#[cfg(feature = "use_semihosting")] -use panic_semihosting as _; - -use bsp::entry; -use bsp::Pins; -use pac::{CorePeripherals, Peripherals}; - -use hal::{ - adc::{Accumulation, Adc, Prescaler, Resolution}, - clock::v2::{clock_system_at_reset, pclk::Pclk}, -}; - -#[entry] -fn main() -> ! { - let mut peripherals = Peripherals::take().unwrap(); - let _core = CorePeripherals::take().unwrap(); - - let pins = Pins::new(peripherals.port); - - let (mut buses, clocks, tokens) = clock_system_at_reset( - peripherals.oscctrl, - peripherals.osc32kctrl, - peripherals.gclk, - peripherals.mclk, - &mut peripherals.nvmctrl, - ); - - // Enable the ADC0 ABP clock... - let apb_adc0 = buses.apb.enable(tokens.apbs.adc0); - // ...and enable the ADC0 PCLK. Both of these are required for the - // ADC to run. - let (pclk_adc0, _gclk0) = Pclk::enable(tokens.pclks.adc0, clocks.gclk0); - - let mut adc = AdcBuilder::new(Accumulation::single(atsamd_hal::adc::AdcResolution::_12)) - .with_clock_cycles_per_sample(5) - // Overruns if clock divider < 32 in debug mode - .with_clock_divider(Prescaler::Div32) - .with_vref(atsamd_hal::adc::Reference::Arefa) - .enable(peripherals.adc0, apb_adc0, &pclk_adc0) - .unwrap(); - let mut adc_pin = pins.a0.into_alternate(); - - loop { - let res = adc.read(&mut adc_pin); - #[cfg(feature = "use_semihosting")] - cortex_m_semihosting::hprintln!("ADC value: {}", res); - } -} +include!("../../examples/m4-adc.rs"); diff --git a/boards/feather_m4/examples/blinky_rtic.rs b/boards/feather_m4/examples/blinky_rtic.rs index d853e3f52262..812fc88b53a6 100644 --- a/boards/feather_m4/examples/blinky_rtic.rs +++ b/boards/feather_m4/examples/blinky_rtic.rs @@ -1,85 +1,5 @@ -//! Blink an led using an RTIC software task and the RTC-based monotonic. - #![no_std] #![no_main] -use bsp::{hal, Pins, RedLed}; use feather_m4 as bsp; -#[cfg(not(feature = "use_semihosting"))] -use panic_halt as _; -#[cfg(feature = "use_semihosting")] -use panic_semihosting as _; - -use hal::clock::v2::{clock_system_at_reset, osculp32k::OscUlp1k, rtcosc::RtcOsc}; -use hal::prelude::*; -use hal::rtc::rtic::rtc_clock; -use rtic::app; - -hal::rtc_monotonic!(Mono, rtc_clock::Clock1k); - -#[app(device = bsp::pac, dispatchers = [EVSYS_0])] -mod app { - use super::*; - - #[local] - struct Resources {} - - #[shared] - struct Shared { - // The LED could be a local resource, since it is only used in one task - // But we want to showcase shared resources and locking - red_led: RedLed, - } - - #[init] - fn init(cx: init::Context) -> (Shared, Resources) { - let mut device = cx.device; - let mut core: rtic::export::Peripherals = cx.core; - - // Use v2 of the clocks API so that we can set the RTC clock source - let (_, clocks, tokens) = clock_system_at_reset( - device.oscctrl, - device.osc32kctrl, - device.gclk, - device.mclk, - &mut device.nvmctrl, - ); - - // Enable the 1 kHz clock from the internal 32 kHz source - let (osculp1k, _) = OscUlp1k::enable(tokens.osculp32k.osculp1k, clocks.osculp32k_base); - - // Enable the RTC clock with the 1 kHz source. - // Note that currently the proof of this (the `RtcOsc` instance) is not - // required to start the monotonic. - let _ = RtcOsc::enable(tokens.rtcosc, osculp1k); - - // Start the monotonic - Mono::start(device.rtc); - - let pins = Pins::new(device.port); - - // We can use the RTC in standby for maximum power savings - core.SCB.set_sleepdeep(); - - blink_led::spawn().ok().unwrap(); - - ( - Shared { - red_led: pins.d13.into_push_pull_output(), - }, - Resources {}, - ) - } - - /// This function is spawned and never returns. - #[task(priority = 1, shared=[red_led])] - async fn blink_led(mut cx: blink_led::Context) { - loop { - // If the LED were a local resource, the lock would not be necessary - cx.shared.red_led.lock(|led| { - led.toggle().unwrap(); - }); - Mono::delay(400u64.millis()).await; - } - } -} +include!("../../examples/m4-blinky_rtic.rs"); diff --git a/boards/feather_m4/examples/clocking_v2.rs b/boards/feather_m4/examples/clocking_v2.rs index 7a9b763bbe14..fa86e38d160f 100644 --- a/boards/feather_m4/examples/clocking_v2.rs +++ b/boards/feather_m4/examples/clocking_v2.rs @@ -8,7 +8,6 @@ use atsamd_hal::{ self as clock, dpll::Dpll, gclk::{Gclk, GclkDiv16, GclkDiv8}, - osculp32k::OscUlp32k, pclk::Pclk, rtcosc::RtcOsc, xosc32k::{ControlGainMode, Xosc1k, Xosc32k, Xosc32kBase}, @@ -43,7 +42,7 @@ mod app { #[init] fn init(cx: init::Context) -> (SharedResources, LocalResources) { - let mut device = cx.device; + let device = cx.device; // Get the clocks & tokens let (_buses, clocks, tokens) = clock::clock_system_at_reset( @@ -51,7 +50,6 @@ mod app { device.osc32kctrl, device.gclk, device.mclk, - &mut device.nvmctrl, ); // This is required because the `sercom` and `rtc` modules have not yet @@ -113,8 +111,7 @@ mod app { // Output `OscUlp32k` on PB11 pin via `Gclk5`, without any division resulting in // 32 kHz output frequency - let (osculp32k, _osculp_base) = - OscUlp32k::enable(tokens.osculp32k.osculp32k, clocks.osculp32k_base); + let osculp32k = clocks.osculp.osculp32k; let (gclk5, _osculp32k) = Gclk::from_source(tokens.gclks.gclk5, osculp32k); let gclk5 = gclk5.enable(); let (_gclk5, _gclk5_out) = gclk5.enable_gclk_out(pins.pb11); diff --git a/boards/grand_central_m4/CHANGELOG.md b/boards/grand_central_m4/CHANGELOG.md index f7294c89d5cc..65676cbfcb2d 100644 --- a/boards/grand_central_m4/CHANGELOG.md +++ b/boards/grand_central_m4/CHANGELOG.md @@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.10.0](https://github.com/atsamd-rs/atsamd/compare/grand_central_m4-0.9.0...grand_central_m4-0.10.0) - 2025-12-29 + +### Dependencies + +- *(grand_central_m4)* Bump HAL dependency to 0.23.0 ([#968](https://github.com/atsamd-rs/atsamd/pull/968)) + ## [0.9.0](https://github.com/atsamd-rs/atsamd/compare/grand_central_m4-0.8.2...grand_central_m4-0.9.0) - 2025-06-22 ### Dependencies diff --git a/boards/grand_central_m4/Cargo.toml b/boards/grand_central_m4/Cargo.toml index 931b14403da5..5b0f31119e64 100644 --- a/boards/grand_central_m4/Cargo.toml +++ b/boards/grand_central_m4/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "grand_central_m4" -version = "0.9.0" +version = "0.10.0" authors = ["Dustin Little "] description = "Board Support crate for the Adafruit Grand Central M4 Express" keywords = ["no-std", "arm", "cortex-m", "embedded-hal"] @@ -16,7 +16,7 @@ optional = true [dependencies.atsamd-hal] default-features = false -version = "0.22.0" +version = "0.23.0" [dependencies.usb-device] version = "0.3.2" diff --git a/boards/itsybitsy_m4/Cargo.toml b/boards/itsybitsy_m4/Cargo.toml index 40d1dfe58311..2e1b51c9042c 100644 --- a/boards/itsybitsy_m4/Cargo.toml +++ b/boards/itsybitsy_m4/Cargo.toml @@ -24,7 +24,7 @@ version = "0.7" optional = true [dependencies.atsamd-hal] -version = "0.21" +version = "0.22.2" default-features = false [dependencies.usb-device] diff --git a/boards/itsybitsy_m4/examples/sercom_interrupt.rs b/boards/itsybitsy_m4/examples/sercom_interrupt.rs index af81a3811f7d..47d21d7efcd0 100644 --- a/boards/itsybitsy_m4/examples/sercom_interrupt.rs +++ b/boards/itsybitsy_m4/examples/sercom_interrupt.rs @@ -42,12 +42,12 @@ use bsp::hal::{ prelude::*, sercom::{ uart::{self, BaudMode, Flags, Oversampling}, - IoSet3, Sercom0, + Sercom0, }, time::Hertz, }; -type UartPads0 = uart::Pads; +type UartPads0 = uart::Pads; type Uart0 = uart::Uart, uart::Duplex>; /// Utility function for setting up SERCOM0 pins as an additional diff --git a/boards/itsybitsy_m4/src/lib.rs b/boards/itsybitsy_m4/src/lib.rs index 568d36e55ffc..ee3ff5aecc12 100644 --- a/boards/itsybitsy_m4/src/lib.rs +++ b/boards/itsybitsy_m4/src/lib.rs @@ -288,7 +288,7 @@ pub fn qspi_master( /// I2C pads for the labelled I2C peripheral /// /// You can use these pads with other, user-defined [`i2c::Config`]urations. -pub type I2cPads = i2c::Pads; +pub type I2cPads = i2c::Pads; /// I2C master for the labelled I2C peripheral /// @@ -318,7 +318,7 @@ pub fn i2c_master( } /// UART Pads for the labelled UART peripheral -pub type UartPads = uart::Pads; +pub type UartPads = uart::Pads; /// UART device for the labelled RX & TX pins pub type Uart = uart::Uart, uart::Duplex>; @@ -375,7 +375,7 @@ pub fn dotstar_bitbang( /// SPI pads for the labelled SPI peripheral /// /// You can use these pads with other, user-defined [`spi::Config`]urations. -pub type SpiPads = spi::Pads; +pub type SpiPads = spi::Pads; /// SPI master for the labelled SPI peripheral /// diff --git a/boards/metro_m0/CHANGELOG.md b/boards/metro_m0/CHANGELOG.md index 7f2a0b888ffa..293982cb4868 100644 --- a/boards/metro_m0/CHANGELOG.md +++ b/boards/metro_m0/CHANGELOG.md @@ -7,6 +7,30 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.20.3](https://github.com/atsamd-rs/atsamd/compare/metro_m0-0.20.2...metro_m0-0.20.3) - 2026-03-02 + +### Other + +- updated the following local packages: atsamd-hal + +## [0.20.2](https://github.com/atsamd-rs/atsamd/compare/metro_m0-0.20.1...metro_m0-0.20.2) - 2026-02-20 + +### Other + +- updated the following local packages: atsamd-hal + +## [0.20.1](https://github.com/atsamd-rs/atsamd/compare/metro_m0-0.20.0...metro_m0-0.20.1) - 2025-12-29 + +### Other + +- updated the following local packages: atsamd-hal + +## [0.20.0](https://github.com/atsamd-rs/atsamd/compare/metro_m0-0.19.2...metro_m0-0.20.0) - 2025-11-19 + +### Other + +- updated the following local packages: atsamd-hal + ## [0.19.2](https://github.com/atsamd-rs/atsamd/compare/metro_m0-0.19.1...metro_m0-0.19.2) - 2025-06-26 ### Other diff --git a/boards/metro_m0/Cargo.toml b/boards/metro_m0/Cargo.toml index b485c1154cae..103de8532a77 100644 --- a/boards/metro_m0/Cargo.toml +++ b/boards/metro_m0/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" name = "metro_m0" readme = "README.md" repository = "https://github.com/atsamd-rs/atsamd" -version = "0.19.2" +version = "0.20.3" # for cargo flash [package.metadata] @@ -25,7 +25,7 @@ version = "0.7" [dependencies.atsamd-hal] default-features = false path = "../../hal" -version = "0.22.2" +version = "0.23.3" [dependencies.rtic] features = ["thumbv6-backend"] diff --git a/boards/metro_m0/examples/blinky_basic.rs b/boards/metro_m0/examples/blinky_basic.rs index 0a41c9297814..5904df598d8f 100644 --- a/boards/metro_m0/examples/blinky_basic.rs +++ b/boards/metro_m0/examples/blinky_basic.rs @@ -11,24 +11,23 @@ use bsp::pac; use metro_m0 as bsp; use bsp::entry; -use hal::clock::GenericClockController; +use hal::clock::v2 as clock; use hal::delay::Delay; use hal::prelude::*; use pac::{CorePeripherals, Peripherals}; #[entry] fn main() -> ! { - let mut peripherals = Peripherals::take().unwrap(); + let peripherals = Peripherals::take().unwrap(); let core = CorePeripherals::take().unwrap(); - let mut clocks = GenericClockController::with_external_32kosc( - peripherals.gclk, - &mut peripherals.pm, - &mut peripherals.sysctrl, - &mut peripherals.nvmctrl, - ); + let (_buses, clocks, _tokens) = + clock::clock_system_at_reset(peripherals.gclk, peripherals.pm, peripherals.sysctrl); + + let gclk0 = clocks.gclk0; + let (mut delay, _gclk0) = Delay::new_with_source(core.SYST, gclk0); + let pins = bsp::Pins::new(peripherals.port); let mut red_led: bsp::RedLed = pins.d13.into(); - let mut delay = Delay::new(core.SYST, &mut clocks); loop { delay.delay_ms(200u8); red_led.set_high().unwrap(); diff --git a/boards/metro_m4/CHANGELOG.md b/boards/metro_m4/CHANGELOG.md index 38acf388d3f3..bf4a9de429b6 100644 --- a/boards/metro_m4/CHANGELOG.md +++ b/boards/metro_m4/CHANGELOG.md @@ -7,6 +7,30 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.19.3](https://github.com/atsamd-rs/atsamd/compare/metro_m4-0.19.2...metro_m4-0.19.3) - 2026-03-02 + +### Other + +- updated the following local packages: atsamd-hal + +## [0.19.2](https://github.com/atsamd-rs/atsamd/compare/metro_m4-0.19.1...metro_m4-0.19.2) - 2026-02-20 + +### Other + +- updated the following local packages: atsamd-hal + +## [0.19.1](https://github.com/atsamd-rs/atsamd/compare/metro_m4-0.19.0...metro_m4-0.19.1) - 2025-12-29 + +### Other + +- updated the following local packages: atsamd-hal + +## [0.19.0](https://github.com/atsamd-rs/atsamd/compare/metro_m4-0.18.2...metro_m4-0.19.0) - 2025-11-19 + +### Changed + +- [**breaking**] Gate undocumented chip features behind the `undoc-features` Cargo feature ([#956](https://github.com/atsamd-rs/atsamd/pull/956)) + ## [0.18.2](https://github.com/atsamd-rs/atsamd/compare/metro_m4-0.18.1...metro_m4-0.18.2) - 2025-06-26 ### Other diff --git a/boards/metro_m4/Cargo.toml b/boards/metro_m4/Cargo.toml index 79df242185dc..29f730f241e1 100644 --- a/boards/metro_m4/Cargo.toml +++ b/boards/metro_m4/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" name = "metro_m4" readme = "README.md" repository = "https://github.com/atsamd-rs/atsamd" -version = "0.18.2" +version = "0.19.3" # for cargo flash [package.metadata] @@ -21,7 +21,8 @@ version = "0.7" [dependencies.atsamd-hal] default-features = false path = "../../hal" -version = "0.22.2" +version = "0.23.3" +features = ["undoc-features"] [dependencies.usb-device] optional = true diff --git a/boards/metro_m4/examples/adc.rs b/boards/metro_m4/examples/adc.rs index 56c46bbd97f7..79f1675e1d1b 100644 --- a/boards/metro_m4/examples/adc.rs +++ b/boards/metro_m4/examples/adc.rs @@ -1,58 +1,5 @@ #![no_std] #![no_main] -use atsamd_hal::adc::AdcBuilder; use metro_m4 as bsp; - -use bsp::hal; -use bsp::pac; - -#[cfg(not(feature = "use_semihosting"))] -use panic_halt as _; -#[cfg(feature = "use_semihosting")] -use panic_semihosting as _; - -use bsp::entry; -use bsp::Pins; -use pac::{CorePeripherals, Peripherals}; - -use hal::{ - adc::{Accumulation, Adc, Prescaler, Resolution}, - clock::v2::{clock_system_at_reset, pclk::Pclk}, -}; - -#[entry] -fn main() -> ! { - let mut peripherals = Peripherals::take().unwrap(); - let _core = CorePeripherals::take().unwrap(); - - let pins = Pins::new(peripherals.port); - - let (mut buses, clocks, tokens) = clock_system_at_reset( - peripherals.oscctrl, - peripherals.osc32kctrl, - peripherals.gclk, - peripherals.mclk, - &mut peripherals.nvmctrl, - ); - - // Enable the ADC0 ABP clock... - let apb_adc0 = buses.apb.enable(tokens.apbs.adc0); - // ...and enable the ADC0 PCLK. Both of these are required for the - // ADC to run. - let (pclk_adc0, _gclk0) = Pclk::enable(tokens.pclks.adc0, clocks.gclk0); - - let mut adc = AdcBuilder::new(Accumulation::single(atsamd_hal::adc::AdcResolution::_12)) - .with_clock_cycles_per_sample(5) - .with_clock_divider(Prescaler::Div32) - .with_vref(atsamd_hal::adc::Reference::Arefa) - .enable(peripherals.adc0, apb_adc0, &pclk_adc0) - .unwrap(); - let mut adc_pin = pins.a0.into_alternate(); - - loop { - let res = adc.read(&mut adc_pin); - #[cfg(feature = "use_semihosting")] - cortex_m_semihosting::hprintln!("ADC Result: {}", res).unwrap(); - } -} +include!("../../examples/m4-adc.rs"); diff --git a/boards/metro_m4/examples/async_adc.rs b/boards/metro_m4/examples/async_adc.rs index 818e395734d7..a4121ba277bf 100644 --- a/boards/metro_m4/examples/async_adc.rs +++ b/boards/metro_m4/examples/async_adc.rs @@ -16,7 +16,7 @@ use bsp::Pins; use pac::{CorePeripherals, Peripherals}; use hal::{ - adc::{Accumulation, Adc, Adc0, Prescaler, Resolution}, + adc::{Accumulation, Adc0, Prescaler}, clock::v2::{clock_system_at_reset, pclk::Pclk}, }; @@ -26,7 +26,7 @@ atsamd_hal::bind_multiple_interrupts!(struct Irqs { #[embassy_executor::main] async fn main(_s: embassy_executor::Spawner) -> ! { - let mut peripherals = Peripherals::take().unwrap(); + let peripherals = Peripherals::take().unwrap(); let _core = CorePeripherals::take().unwrap(); let pins = Pins::new(peripherals.port); @@ -36,28 +36,27 @@ async fn main(_s: embassy_executor::Spawner) -> ! { peripherals.osc32kctrl, peripherals.gclk, peripherals.mclk, - &mut peripherals.nvmctrl, ); // Enable the ADC0 ABP clock... let apb_adc0 = buses.apb.enable(tokens.apbs.adc0); // ...and enable the ADC0 PCLK. Both of these are required for the // ADC to run. - let (pclk_adc0, _gclk0) = Pclk::enable(tokens.pclks.adc0, clocks.gclk0); + let (pclk_adc0, _gclk0) = Pclk::enable_dyn(tokens.pclks.adc0, clocks.gclk0); let mut adc = AdcBuilder::new(Accumulation::single(atsamd_hal::adc::AdcResolution::_12)) .with_clock_cycles_per_sample(5) .with_clock_divider(Prescaler::Div32) .with_vref(atsamd_hal::adc::Reference::Arefa) - .enable(peripherals.adc0, apb_adc0, &pclk_adc0) + .enable(peripherals.adc0, apb_adc0, pclk_adc0) .unwrap() .into_future(Irqs); let mut adc_pin = pins.a0.into_alternate(); loop { - let res = adc.read(&mut adc_pin).await; + let _res = adc.read(&mut adc_pin).await; #[cfg(feature = "use_semihosting")] - cortex_m_semihosting::hprintln!("ADC Result: {}", res).unwrap(); + cortex_m_semihosting::hprintln!("ADC Result: {}", _res).unwrap(); } } diff --git a/boards/metro_m4/examples/blinky_rtic.rs b/boards/metro_m4/examples/blinky_rtic.rs index 13f39d41d5c2..34b385542b1e 100644 --- a/boards/metro_m4/examples/blinky_rtic.rs +++ b/boards/metro_m4/examples/blinky_rtic.rs @@ -1,85 +1,5 @@ -//! Blink an led using an RTIC software task and the RTC-based monotonic. - #![no_std] #![no_main] -use bsp::{hal, Pins, RedLed}; use metro_m4 as bsp; -#[cfg(not(feature = "use_semihosting"))] -use panic_halt as _; -#[cfg(feature = "use_semihosting")] -use panic_semihosting as _; - -use hal::clock::v2::{clock_system_at_reset, osculp32k::OscUlp1k, rtcosc::RtcOsc}; -use hal::prelude::*; -use hal::rtc::rtic::rtc_clock; -use rtic::app; - -hal::rtc_monotonic!(Mono, rtc_clock::Clock1k); - -#[app(device = bsp::pac, dispatchers = [EVSYS_0])] -mod app { - use super::*; - - #[local] - struct Resources {} - - #[shared] - struct Shared { - // The LED could be a local resource, since it is only used in one task - // But we want to showcase shared resources and locking - red_led: RedLed, - } - - #[init] - fn init(cx: init::Context) -> (Shared, Resources) { - let mut device = cx.device; - let mut core: rtic::export::Peripherals = cx.core; - - // Use v2 of the clocks API so that we can set the RTC clock source - let (_, clocks, tokens) = clock_system_at_reset( - device.oscctrl, - device.osc32kctrl, - device.gclk, - device.mclk, - &mut device.nvmctrl, - ); - - // Enable the 1 kHz clock from the internal 32 kHz source - let (osculp1k, _) = OscUlp1k::enable(tokens.osculp32k.osculp1k, clocks.osculp32k_base); - - // Enable the RTC clock with the 1 kHz source. - // Note that currently the proof of this (the `RtcOsc` instance) is not - // required to start the monotonic. - let _ = RtcOsc::enable(tokens.rtcosc, osculp1k); - - // Start the monotonic - Mono::start(device.rtc); - - let pins = Pins::new(device.port); - - // We can use the RTC in standby for maximum power savings - core.SCB.set_sleepdeep(); - - blink_led::spawn().ok().unwrap(); - - ( - Shared { - red_led: pins.d13.into_push_pull_output(), - }, - Resources {}, - ) - } - - /// This function is spawned and never returns. - #[task(priority = 1, shared=[red_led])] - async fn blink_led(mut cx: blink_led::Context) { - loop { - // If the LED were a local resource, the lock would not be necessary - cx.shared.red_led.lock(|led| { - led.toggle().unwrap(); - }); - Mono::delay(400u64.millis()).await; - } - } -} +include!("../../examples/m4-blinky_rtic.rs"); diff --git a/boards/pygamer/CHANGELOG.md b/boards/pygamer/CHANGELOG.md index ef891a3e7ec5..89e8203e0e30 100644 --- a/boards/pygamer/CHANGELOG.md +++ b/boards/pygamer/CHANGELOG.md @@ -7,6 +7,30 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.16.3](https://github.com/atsamd-rs/atsamd/compare/pygamer-0.16.2...pygamer-0.16.3) - 2026-03-02 + +### Other + +- updated the following local packages: atsamd-hal + +## [0.16.2](https://github.com/atsamd-rs/atsamd/compare/pygamer-0.16.1...pygamer-0.16.2) - 2026-02-20 + +### Other + +- updated the following local packages: atsamd-hal + +## [0.16.1](https://github.com/atsamd-rs/atsamd/compare/pygamer-0.16.0...pygamer-0.16.1) - 2025-12-29 + +### Other + +- updated the following local packages: atsamd-hal + +## [0.16.0](https://github.com/atsamd-rs/atsamd/compare/pygamer-0.15.2...pygamer-0.16.0) - 2025-11-19 + +### Changed + +- [**breaking**] Gate undocumented chip features behind the `undoc-features` Cargo feature ([#956](https://github.com/atsamd-rs/atsamd/pull/956)) + ## [0.15.2](https://github.com/atsamd-rs/atsamd/compare/pygamer-0.15.1...pygamer-0.15.2) - 2025-06-26 ### Other diff --git a/boards/pygamer/Cargo.toml b/boards/pygamer/Cargo.toml index 4044a6d164ac..8393af5de64d 100644 --- a/boards/pygamer/Cargo.toml +++ b/boards/pygamer/Cargo.toml @@ -12,7 +12,7 @@ license = "MIT OR Apache-2.0" name = "pygamer" readme = "README.md" repository = "https://github.com/atsamd-rs/atsamd" -version = "0.15.2" +version = "0.16.3" [dependencies] cortex-m = {version = "0.7", features = ["critical-section-single-core"]} @@ -27,7 +27,8 @@ version = "0.7" [dependencies.atsamd-hal] default-features = false path = "../../hal" -version = "0.22.2" +version = "0.23.3" +features = ["undoc-features"] [dependencies.usb-device] optional = true diff --git a/boards/pygamer/examples/blinky_rtic.rs b/boards/pygamer/examples/blinky_rtic.rs index c4549b21702c..a13bb736f57f 100644 --- a/boards/pygamer/examples/blinky_rtic.rs +++ b/boards/pygamer/examples/blinky_rtic.rs @@ -8,7 +8,7 @@ use bsp::{hal, Pins, RedLed}; use panic_halt as _; use pygamer as bsp; -use hal::clock::v2::{clock_system_at_reset, osculp32k::OscUlp1k, rtcosc::RtcOsc}; +use hal::clock::v2::{clock_system_at_reset, rtcosc::RtcOsc}; use hal::prelude::*; use hal::rtc::rtic::rtc_clock; use rtic::app; @@ -31,25 +31,17 @@ mod app { #[init] fn init(cx: init::Context) -> (Shared, Resources) { - let mut device = cx.device; + let device = cx.device; let mut core: rtic::export::Peripherals = cx.core; // Use v2 of the clocks API so that we can set the RTC clock source - let (_, clocks, tokens) = clock_system_at_reset( - device.oscctrl, - device.osc32kctrl, - device.gclk, - device.mclk, - &mut device.nvmctrl, - ); - - // Enable the 1 kHz clock from the internal 32 kHz source - let (osculp1k, _) = OscUlp1k::enable(tokens.osculp32k.osculp1k, clocks.osculp32k_base); - - // Enable the RTC clock with the 1 kHz source. + let (_, clocks, tokens) = + clock_system_at_reset(device.oscctrl, device.osc32kctrl, device.gclk, device.mclk); + + // Enable the RTC clock with the internal 1 kHz source. // Note that currently the proof of this (the `RtcOsc` instance) is not // required to start the monotonic. - let _ = RtcOsc::enable(tokens.rtcosc, osculp1k); + let _ = RtcOsc::enable(tokens.rtcosc, clocks.osculp.osculp1k); // Start the monotonic Mono::start(device.rtc); diff --git a/boards/qt_py_m0/Cargo.toml b/boards/qt_py_m0/Cargo.toml index 2cb79b0d86b3..bb94e0bb070d 100644 --- a/boards/qt_py_m0/Cargo.toml +++ b/boards/qt_py_m0/Cargo.toml @@ -8,27 +8,33 @@ categories = ["embedded", "hardware-support", "no-std"] license = "MIT OR Apache-2.0" repository = "https://github.com/atsamd-rs/atsamd" readme = "README.md" -edition = "2018" +edition = "2024" -[dependencies] -cortex-m-rt = { version = "0.7", optional = true } -usb-device = { version = "0.2", optional = true } +[dependencies.cortex-m] +features = ["critical-section-single-core"] +version = "0.7" + +[dependencies.cortex-m-rt] +version = "0.7" +optional = true + +[dependencies.usb-device] +version = "0.3.2" +optional = true [dependencies.atsamd-hal] -version = "0.14" +version = "0.23" default-features = false [dev-dependencies] -usbd-serial = "0.1" +usbd-serial = "0.2" panic-halt = "0.2" smart-leds = "0.3" ws2812-timer-delay = { version = "0.3", features = ["slow"] } -cortex-m = "0.7" [features] default = ["rt", "atsamd-hal/samd21e"] rt = ["cortex-m-rt", "atsamd-hal/samd21e-rt"] -unproven = ["atsamd-hal/unproven"] use_semihosting = [] usb = ["atsamd-hal/usb", "usb-device"] diff --git a/boards/qt_py_m0/examples/neopixel.rs b/boards/qt_py_m0/examples/neopixel.rs index 15062e9d59e7..af8b641ae6c9 100644 --- a/boards/qt_py_m0/examples/neopixel.rs +++ b/boards/qt_py_m0/examples/neopixel.rs @@ -4,43 +4,47 @@ //! Neopixel example for the Adafruit QT Py board. Demonstrates powering up the //! neopixel using the attached GPIO line. //! -//! *NOTE*: This example currently only works in release mode. +//! *NOTE*: This example needs to be compiled with --release for the timing to +//! be correct. -use hal::ehal::digital::v1_compat::OldOutputPin; +use atsamd_hal::prelude::InterruptDrivenTimer; use panic_halt as _; -use smart_leds::hsv::hsv2rgb; -use smart_leds::hsv::Hsv; -use smart_leds::SmartLedsWrite; -use ws2812_timer_delay::Ws2812; -use bsp::entry; use bsp::hal; +use qt_py_m0 as bsp; + use bsp::Pins; +use bsp::entry; use hal::clock::GenericClockController; use hal::delay::Delay; use hal::pac::CorePeripherals; use hal::pac::Peripherals; use hal::prelude::*; use hal::timer::TimerCounter; -use qt_py_m0 as bsp; + +use smart_leds::SmartLedsWrite; +use smart_leds::hsv::Hsv; +use smart_leds::hsv::hsv2rgb; +use ws2812_timer_delay::Ws2812; #[entry] fn main() -> ! { let mut peripherals = Peripherals::take().unwrap(); let core = CorePeripherals::take().unwrap(); let mut clocks = GenericClockController::with_internal_8mhz( - peripherals.GCLK, - &mut peripherals.PM, - &mut peripherals.SYSCTRL, - &mut peripherals.NVMCTRL, + peripherals.gclk, + &mut peripherals.pm, + &mut peripherals.sysctrl, + &mut peripherals.nvmctrl, ); - let pins = Pins::new(peripherals.PORT).split(); + let pins = Pins::new(peripherals.port).split(); let gclk0 = clocks.gclk0(); let timer_clock = clocks.tcc2_tc3(&gclk0).unwrap(); - let mut timer = TimerCounter::tc3_(&timer_clock, peripherals.TC3, &mut peripherals.PM); - timer.start(3.mhz()); + let mut timer = TimerCounter::tc3_(&timer_clock, peripherals.tc3, &mut peripherals.pm); + InterruptDrivenTimer::start(&mut timer, 300.nanos()); + InterruptDrivenTimer::enable_interrupt(&mut timer); // The neopixel sources power from a GPIO pin. It must be driven high to power // up the neopixel before it can be used. @@ -50,7 +54,7 @@ fn main() -> ! { .set_high() .unwrap(); - let neopixel_data: OldOutputPin<_> = pins.neopixel.data.into_push_pull_output().into(); + let neopixel_data: bsp::NeopixelData = pins.neopixel.data.into(); let mut neopixel = Ws2812::new(timer, neopixel_data); let mut delay = Delay::new(core.SYST, &mut clocks); diff --git a/boards/qt_py_m0/examples/usb_echo.rs b/boards/qt_py_m0/examples/usb_echo.rs index 8005d32ac31b..95e8161dd02c 100644 --- a/boards/qt_py_m0/examples/usb_echo.rs +++ b/boards/qt_py_m0/examples/usb_echo.rs @@ -6,9 +6,6 @@ use panic_halt as _; -use cortex_m::asm::wfi; -use cortex_m::peripheral::NVIC; -use usb_device::bus::UsbBusAllocator; use usb_device::prelude::*; use usbd_serial::SerialPort; use usbd_serial::USB_CLASS_CDC; @@ -19,73 +16,41 @@ use qt_py_m0 as bsp; use bsp::entry; use hal::clock::GenericClockController; -use hal::usb::UsbBus; -use pac::interrupt; -use pac::CorePeripherals; use pac::Peripherals; #[entry] fn main() -> ! { let mut peripherals = Peripherals::take().unwrap(); - let mut core = CorePeripherals::take().unwrap(); let mut clocks = GenericClockController::with_internal_32kosc( - peripherals.GCLK, - &mut peripherals.PM, - &mut peripherals.SYSCTRL, - &mut peripherals.NVMCTRL, + peripherals.gclk, + &mut peripherals.pm, + &mut peripherals.sysctrl, + &mut peripherals.nvmctrl, ); - let pins = bsp::Pins::new(peripherals.PORT).split(); - - let bus_allocator = unsafe { - USB_ALLOCATOR = Some( - pins.usb - .init(peripherals.USB, &mut clocks, &mut peripherals.PM), - ); - USB_ALLOCATOR.as_ref().unwrap() - }; - - unsafe { - USB_SERIAL = Some(SerialPort::new(&bus_allocator)); - USB_BUS = Some( - UsbDeviceBuilder::new(&bus_allocator, UsbVidPid(0x16c0, 0x27dd)) - .manufacturer("Fake company") - .product("Serial port") - .serial_number("TEST") - .device_class(USB_CLASS_CDC) - .build(), - ); - } - - unsafe { - core.NVIC.set_priority(interrupt::USB, 1); - NVIC::unmask(interrupt::USB); - } + let pins = bsp::Pins::new(peripherals.port).split(); + + let usb_bus = pins + .usb + .init(peripherals.usb, &mut clocks, &mut peripherals.pm); + + let mut serial = SerialPort::new(&usb_bus); + let mut usb_device = UsbDeviceBuilder::new(&usb_bus, UsbVidPid(0x239a, 0x00cb)) + .strings(&[StringDescriptors::new(LangID::EN) + .manufacturer("Fake company") + .product("Serial port") + .serial_number("TEST")]) + .expect("Failed to set strings") + .device_class(USB_CLASS_CDC) + .build(); loop { - wfi(); + if !usb_device.poll(&mut [&mut serial]) { + continue; + } + + let mut buf = [0u8; 64]; + if let Ok(count) = serial.read(&mut buf) { + let _ = serial.write(&buf[..count]); + } } } - -static mut USB_ALLOCATOR: Option> = None; -static mut USB_BUS: Option> = None; -static mut USB_SERIAL: Option> = None; - -fn poll_usb() { - unsafe { - USB_BUS.as_mut().map(|usb_dev| { - USB_SERIAL.as_mut().map(|serial| { - usb_dev.poll(&mut [serial]); - let mut buf = [0u8; 64]; - - if let Ok(count) = serial.read(&mut buf) { - serial.write(&buf[..count]).ok(); - }; - }); - }); - }; -} - -#[interrupt] -fn USB() { - poll_usb(); -} diff --git a/boards/qt_py_m0/src/lib.rs b/boards/qt_py_m0/src/lib.rs index 4705af349fc3..182bf7959644 100644 --- a/boards/qt_py_m0/src/lib.rs +++ b/boards/qt_py_m0/src/lib.rs @@ -24,10 +24,10 @@ pub use hal::pac; use hal::bsp_pins; use hal::clock::GenericClockController; -use hal::sercom::v2::spi; -use hal::sercom::v2::uart::{self, BaudMode, Oversampling}; -use hal::sercom::v2::{Sercom0, Sercom2}; -use hal::sercom::I2CMaster1; +use hal::sercom::i2c; +use hal::sercom::spi; +use hal::sercom::uart::{self, BaudMode, Oversampling}; +use hal::sercom::{Sercom0, Sercom2}; use hal::time::Hertz; #[cfg(feature = "rt")] @@ -38,6 +38,12 @@ use hal::usb::UsbBus; #[cfg(feature = "usb")] use usb_device::bus::UsbBusAllocator; +hal::bsp_peripherals! { + Sercom0 { UartSercom } + Sercom1 { I2cSercom } + Sercom2 { SpiSercom } +} + bsp_pins! { // General purpose pins. PA02 { @@ -273,8 +279,8 @@ impl Uart { self, clocks: &mut GenericClockController, freq: impl Into, - sercom0: pac::SERCOM0, - pm: &mut pac::PM, + sercom0: pac::Sercom0, + pm: &mut pac::Pm, ) -> UartConfig { let gclk0 = clocks.gclk0(); let clock = &clocks.sercom0_core(&gclk0).unwrap(); @@ -308,9 +314,9 @@ impl Spi { pub fn init( self, clocks: &mut GenericClockController, - baud: impl Into, - sercom2: pac::SERCOM2, - pm: &mut pac::PM, + baud: Hertz, + sercom2: pac::Sercom2, + pm: &mut pac::Pm, ) -> SpiConfig { let gclk0 = clocks.gclk0(); let clock = clocks.sercom2_core(&gclk0).unwrap(); @@ -333,25 +339,32 @@ pub struct I2c { pub scl: I2cSclReset, } +/// I2C pads for the labelled I2C peripheral +/// +/// You can use these pads with other, user-defined [`i2c::Config`]urations. +pub type I2cPads = i2c::Pads; + +/// I2C master for the labelled I2C peripheral +/// +/// This type implements [`Read`](ehal::blocking::i2c::Read), +/// [`Write`](ehal::blocking::i2c::Write) and +/// [`WriteRead`](ehal::blocking::i2c::WriteRead). +pub type I2cConfig = i2c::I2c>; + impl I2c { /// Convenience function for creating an I2C host on the I2C pins. pub fn init( self, clocks: &mut GenericClockController, - freq: impl Into, - sercom1: pac::SERCOM1, - pm: &mut pac::PM, - ) -> I2CMaster1 { + baud: Hertz, + sercom: I2cSercom, + pm: &mut pac::Pm, + ) -> I2cConfig { let gclk0 = clocks.gclk0(); let clock = &clocks.sercom1_core(&gclk0).unwrap(); - I2CMaster1::new( - clock, - freq.into(), - sercom1, - pm, - self.sda.into(), - self.scl.into(), - ) + let freq = clock.freq(); + let pads = i2c::Pads::new(self.sda, self.scl); + i2c::Config::new(pm, sercom, pads, freq).baud(baud).enable() } } @@ -377,9 +390,9 @@ impl Usb { #[cfg(feature = "usb")] pub fn init( self, - usb: pac::USB, + usb: pac::Usb, clocks: &mut GenericClockController, - pm: &mut pac::PM, + pm: &mut pac::Pm, ) -> UsbBusAllocator { let gclk0 = clocks.gclk0(); let usb_clock = &clocks.usb(&gclk0).unwrap(); diff --git a/boards/samd11_bare/CHANGELOG.md b/boards/samd11_bare/CHANGELOG.md index 8e1f439274b7..66c8f829c1f4 100644 --- a/boards/samd11_bare/CHANGELOG.md +++ b/boards/samd11_bare/CHANGELOG.md @@ -7,6 +7,30 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.16.3](https://github.com/atsamd-rs/atsamd/compare/samd11_bare-0.16.2...samd11_bare-0.16.3) - 2026-03-02 + +### Other + +- updated the following local packages: atsamd-hal + +## [0.16.2](https://github.com/atsamd-rs/atsamd/compare/samd11_bare-0.16.1...samd11_bare-0.16.2) - 2026-02-20 + +### Other + +- updated the following local packages: atsamd-hal + +## [0.16.1](https://github.com/atsamd-rs/atsamd/compare/samd11_bare-0.16.0...samd11_bare-0.16.1) - 2025-12-29 + +### Other + +- updated the following local packages: atsamd-hal + +## [0.16.0](https://github.com/atsamd-rs/atsamd/compare/samd11_bare-0.15.2...samd11_bare-0.16.0) - 2025-11-19 + +### Other + +- updated the following local packages: atsamd-hal + ## [0.15.2](https://github.com/atsamd-rs/atsamd/compare/samd11_bare-0.15.1...samd11_bare-0.15.2) - 2025-06-26 ### Other diff --git a/boards/samd11_bare/Cargo.toml b/boards/samd11_bare/Cargo.toml index 1d06909ef5e8..c7653549ca54 100644 --- a/boards/samd11_bare/Cargo.toml +++ b/boards/samd11_bare/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" name = "samd11_bare" readme = "README.md" repository = "https://github.com/atsamd-rs/atsamd" -version = "0.15.2" +version = "0.16.3" # for cargo flash [package.metadata] @@ -24,7 +24,7 @@ version = "0.7" [dependencies.atsamd-hal] default-features = false path = "../../hal" -version = "0.22.2" +version = "0.23.3" [dependencies.rtic] features = ["thumbv6-backend"] diff --git a/crates.json b/crates.json index ac8230519cbd..c58ab16cbdd8 100644 --- a/crates.json +++ b/crates.json @@ -179,7 +179,8 @@ "samd11c", "dma", "defmt", - "async" + "async", + "undoc-features" ], "target": "thumbv6m-none-eabi" }, @@ -188,7 +189,8 @@ "samd11d", "dma", "defmt", - "async" + "async", + "undoc-features" ], "target": "thumbv6m-none-eabi" }, @@ -198,7 +200,8 @@ "usb", "dma", "defmt", - "async" + "async", + "undoc-features" ], "target": "thumbv6m-none-eabi" }, @@ -208,7 +211,8 @@ "usb", "dma", "defmt", - "async" + "async", + "undoc-features" ], "target": "thumbv6m-none-eabi" }, @@ -219,7 +223,8 @@ "sdmmc", "dma", "defmt", - "async" + "async", + "undoc-features" ], "target": "thumbv7em-none-eabihf" }, @@ -230,7 +235,8 @@ "sdmmc", "dma", "defmt", - "async" + "async", + "undoc-features" ], "target": "thumbv7em-none-eabihf" }, @@ -241,7 +247,8 @@ "sdmmc", "dma", "defmt", - "async" + "async", + "undoc-features" ], "target": "thumbv7em-none-eabihf" }, @@ -252,82 +259,83 @@ "sdmmc", "dma", "defmt", - "async" + "async", + "undoc-features" ], "target": "thumbv7em-none-eabihf" } }, "hal_build_variants": { "samd11c": { - "features": [ "samd11c", "dma", "rtic", "defmt", "async" ], + "features": [ "samd11c", "dma", "rtic", "defmt", "async", "undoc-features" ], "target": "thumbv6m-none-eabi" }, "samd11d": { - "features": [ "samd11d", "dma", "rtic", "defmt", "async" ], + "features": [ "samd11d", "dma", "rtic", "defmt", "async", "undoc-features" ], "target": "thumbv6m-none-eabi" }, "samd21e": { - "features": [ "samd21e", "usb", "dma", "rtic", "defmt", "async" ], + "features": [ "samd21e", "usb", "dma", "rtic", "defmt", "async", "undoc-features" ], "target": "thumbv6m-none-eabi" }, "samd21el": { - "features": [ "samd21el", "dma", "rtic", "defmt", "async" ], + "features": [ "samd21el", "dma", "rtic", "defmt", "async", "undoc-features" ], "target": "thumbv6m-none-eabi" }, "samd21g": { - "features": [ "samd21g", "usb", "dma", "rtic", "defmt", "async" ], + "features": [ "samd21g", "usb", "dma", "rtic", "defmt", "async", "undoc-features" ], "target": "thumbv6m-none-eabi" }, "samd21gl": { - "features": [ "samd21gl", "dma", "rtic", "defmt", "async" ], + "features": [ "samd21gl", "dma", "rtic", "defmt", "async", "undoc-features" ], "target": "thumbv6m-none-eabi" }, "samd21j": { - "features": [ "samd21j", "usb", "dma", "rtic", "defmt", "async" ], + "features": [ "samd21j", "usb", "dma", "rtic", "defmt", "async", "undoc-features" ], "target": "thumbv6m-none-eabi" }, "samd51g": { - "features": [ "samd51g", "usb", "dma", "sdmmc", "rtic", "defmt", "async" ], + "features": [ "samd51g", "usb", "dma", "sdmmc", "rtic", "defmt", "async", "undoc-features" ], "target": "thumbv7em-none-eabihf" }, "samd51j": { - "features": [ "samd51j", "usb", "dma", "sdmmc", "rtic", "defmt", "async" ], + "features": [ "samd51j", "usb", "dma", "sdmmc", "rtic", "defmt", "async", "undoc-features" ], "target": "thumbv7em-none-eabihf" }, "samd51n": { - "features": [ "samd51n", "usb", "dma", "sdmmc", "rtic", "defmt", "async" ], + "features": [ "samd51n", "usb", "dma", "sdmmc", "rtic", "defmt", "async", "undoc-features" ], "target": "thumbv7em-none-eabihf" }, "samd51p": { - "features": [ "samd51p", "usb", "dma", "sdmmc", "rtic", "defmt", "async" ], + "features": [ "samd51p", "usb", "dma", "sdmmc", "rtic", "defmt", "async", "undoc-features" ], "target": "thumbv7em-none-eabihf" }, "same51g": { - "features": [ "same51g", "usb", "dma", "sdmmc", "rtic", "can", "defmt", "async" ], + "features": [ "same51g", "usb", "dma", "sdmmc", "rtic", "can", "defmt", "async", "undoc-features" ], "target": "thumbv7em-none-eabihf" }, "same51j": { - "features": [ "same51j", "usb", "dma", "sdmmc", "rtic", "can", "defmt", "async" ], + "features": [ "same51j", "usb", "dma", "sdmmc", "rtic", "can", "defmt", "async", "undoc-features" ], "target": "thumbv7em-none-eabihf" }, "same51n": { - "features": [ "same51n", "usb", "dma", "sdmmc", "rtic", "can", "defmt", "async" ], + "features": [ "same51n", "usb", "dma", "sdmmc", "rtic", "can", "defmt", "async" , "undoc-features"], "target": "thumbv7em-none-eabihf" }, "same53j": { - "features": [ "same53j", "usb", "dma", "sdmmc", "rtic", "defmt", "async" ], + "features": [ "same53j", "usb", "dma", "sdmmc", "rtic", "defmt", "async", "undoc-features" ], "target": "thumbv7em-none-eabihf" }, "same53n": { - "features": [ "same53n", "usb", "dma", "sdmmc", "rtic", "defmt", "async" ], + "features": [ "same53n", "usb", "dma", "sdmmc", "rtic", "defmt", "async", "undoc-features" ], "target": "thumbv7em-none-eabihf" }, "same54n": { - "features": [ "same54n", "usb", "dma", "sdmmc", "rtic", "can", "defmt", "async" ], + "features": [ "same54n", "usb", "dma", "sdmmc", "rtic", "can", "defmt", "async", "undoc-features" ], "target": "thumbv7em-none-eabihf" }, "same54p": { - "features": [ "same54p", "usb", "dma", "sdmmc", "rtic", "can", "defmt", "async" ], + "features": [ "same54p", "usb", "dma", "sdmmc", "rtic", "can", "defmt", "async", "undoc-features" ], "target": "thumbv7em-none-eabihf" } } diff --git a/format-all.sh b/format-all.sh index 09b8408b0d6a..d1510784acb1 100755 --- a/format-all.sh +++ b/format-all.sh @@ -1,4 +1,5 @@ -#!/bin/bash +#!/usr/bin/env bash + set -xe HERE=$PWD diff --git a/hal/CHANGELOG.md b/hal/CHANGELOG.md index 12c351133f29..1ab6ecd97223 100644 --- a/hal/CHANGELOG.md +++ b/hal/CHANGELOG.md @@ -7,6 +7,66 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.23.3](https://github.com/atsamd-rs/atsamd/compare/atsamd-hal-0.23.2...atsamd-hal-0.23.3) - 2026-03-02 + +### Fixed + +- *(adc)* ADC clearing flags when reading value & make it somewhat consistent ([#989](https://github.com/atsamd-rs/atsamd/pull/989)) + +### Other + +- *(clippy)* New lint doesn't like nesting if in match ([#993](https://github.com/atsamd-rs/atsamd/pull/993)) + +## [0.23.2](https://github.com/atsamd-rs/atsamd/compare/atsamd-hal-0.23.1...atsamd-hal-0.23.2) - 2026-02-20 + +### Added + +- *(usb)* USB allocation rework and simplification ([#963](https://github.com/atsamd-rs/atsamd/pull/963)) + +### Fixed + +- *(uart)* Fix embedded_io::Read::read impl ([#983](https://github.com/atsamd-rs/atsamd/pull/983)) +- *(dmac)* Check+document that the DMAC transfers are smaller than u16::MAX long ([#984](https://github.com/atsamd-rs/atsamd/pull/984)) +- *(pwm)* Fix for off-by-one in get_max_duty() ([#959](https://github.com/atsamd-rs/atsamd/pull/959)) + +## [0.23.1](https://github.com/atsamd-rs/atsamd/compare/atsamd-hal-0.23.0...atsamd-hal-0.23.1) - 2025-12-29 + +### Added + +- *(i2c)* add I2C capabilities to labelled pins for arduino_nano33iot ([#975](https://github.com/atsamd-rs/atsamd/pull/975)) + +## [0.23.0](https://github.com/atsamd-rs/atsamd/compare/atsamd-hal-0.22.2...atsamd-hal-0.23.0) - 2025-11-19 + +### Added + +- Add PB02 and PB03 as undocumented I2C pads for SAMD21 ([#958](https://github.com/atsamd-rs/atsamd/pull/958)) +- *(aes)* Updates the `cipher` dependency. +- *(aes)* Updates the `aes` module: + +### Changed + +- [**breaking**] Gate undocumented chip features behind the `undoc-features` Cargo feature ([#956](https://github.com/atsamd-rs/atsamd/pull/956)) + +### Documentation + +- Document newly undoc I2C-capable pins (PB02,PB03) ([#960](https://github.com/atsamd-rs/atsamd/pull/960)) + +### Fixed + +- *(ci)* silence deprecation warning we have no power over ([#943](https://github.com/atsamd-rs/atsamd/pull/943)) +- *(dmac)* DMAC handler now waits for channel to report as disabled ([#938](https://github.com/atsamd-rs/atsamd/pull/938)) +- [**breaking**] Re-add GCLK0 for CAN dependencies ([#930](https://github.com/atsamd-rs/atsamd/pull/930)) +- Clippy warning about null pointers in DMAC ([#928](https://github.com/atsamd-rs/atsamd/pull/928)) +- Remove GCLK from can::Dependencies ([#919](https://github.com/atsamd-rs/atsamd/pull/919)) + +### Other + +- Remove deprecated warning in aes module ([#957](https://github.com/atsamd-rs/atsamd/pull/957)) +- fix off-by-one Pwm period ([#949](https://github.com/atsamd-rs/atsamd/pull/949)) +- Fix and improve EIC Event control ([#944](https://github.com/atsamd-rs/atsamd/pull/944)) +- Document order of calling enable_interrupt for interrupt timer ([#942](https://github.com/atsamd-rs/atsamd/pull/942)) +- *(ci)* Remove explicit link targets from docs + ## [0.22.2](https://github.com/atsamd-rs/atsamd/compare/atsamd-hal-0.22.1...atsamd-hal-0.22.2) - 2025-06-26 ### Fixed diff --git a/hal/Cargo.toml b/hal/Cargo.toml index 4fa93be1cdb4..07e236b7ca1d 100644 --- a/hal/Cargo.toml +++ b/hal/Cargo.toml @@ -20,7 +20,7 @@ name = "atsamd-hal" readme = "README.md" repository = "https://github.com/atsamd-rs/atsamd" rust-version = "1.85.1" -version = "0.22.2" +version = "0.23.3" [package.metadata.docs.rs] features = ["samd21g", "samd21g-rt", "usb", "dma", "async", "rtic"] @@ -195,8 +195,21 @@ dma = [] max-channels = ["dma"] rtic = ["rtic-monotonic", "rtic-time", "portable-atomic"] sdmmc = ["embedded-sdmmc"] -usb = ["usb-device"] use_rtt = ["jlink_rtt"] +undoc-features = [] + +# USB features +usb = ["usb-device"] # Default with 2048 byte buffer + +# USB buffer sizes +# These denote the size of the global buffer for all USB endpoints. +# The buffer is split between all device endpoints equally + +usb-buffer-1k = ["usb"] # 1024 byte buffer shared between all USB endpoints (64 bytes per endpoint) +usb-buffer-2k = ["usb"] # 2048 byte buffer shared between all USB endpoints (128 bytes per endpoint) (Default) +usb-buffer-4k = ["usb"] # 4096 byte buffer shared between all USB endpoints (256 bytes per endpoint) +usb-buffer-8k = ["usb"] # 8192 byte buffer shared between all USB endpoints (512 bytes per endpoint) +usb-buffer-16k = ["usb"] # 16384 byte buffer shared between all USB endpoints (1024 bytes per endpoint) #=============================================================================== # Implementation-details diff --git a/hal/src/clock.rs b/hal/src/clock.rs new file mode 100644 index 000000000000..df8cc9e27495 --- /dev/null +++ b/hal/src/clock.rs @@ -0,0 +1,15 @@ +//! # Clocking API +//! +//! Users are encouraged to use [`v2`] variant of an API because of the richer +//! feature set and safety. +use atsamd_hal_macros::hal_module; + +#[hal_module( + any("clock-d11", "clock-d21") => "clock/v1_thumbv6m.rs", + "clock-d5x" => "clock/v1_thumbv7em.rs", +)] +pub mod v1 {} + +pub use v1::*; + +pub mod v2; diff --git a/hal/src/peripherals/clock/d11.rs b/hal/src/clock/v1_thumbv6m.rs similarity index 99% rename from hal/src/peripherals/clock/d11.rs rename to hal/src/clock/v1_thumbv6m.rs index 0ff080c2cf17..7573da6c4d55 100644 --- a/hal/src/peripherals/clock/d11.rs +++ b/hal/src/clock/v1_thumbv6m.rs @@ -488,7 +488,7 @@ fn enable_gclk_apb(pm: &mut Pm) { /// Turn on the internal 32hkz oscillator pub fn enable_internal_32kosc(sysctrl: &mut Sysctrl) { - let calibration = super::calibration::osc32k_cal(); + let calibration = crate::calibration::osc32k_cal(); sysctrl.osc32k().write(|w| { unsafe { w.ondemand().clear_bit(); @@ -562,7 +562,7 @@ fn configure_and_enable_dfll48m(sysctrl: &mut Sysctrl, use_external_crystal: boo }); } else { // Apply calibration - let coarse = super::calibration::dfll48m_coarse_cal(); + let coarse = crate::calibration::dfll48m_coarse_cal(); let fine = 0x1ff; sysctrl.dfllval().write(|w| unsafe { diff --git a/hal/src/peripherals/clock/d5x/v1.rs b/hal/src/clock/v1_thumbv7em.rs similarity index 99% rename from hal/src/peripherals/clock/d5x/v1.rs rename to hal/src/clock/v1_thumbv7em.rs index a04166d20068..efdd9572fc9e 100644 --- a/hal/src/peripherals/clock/d5x/v1.rs +++ b/hal/src/clock/v1_thumbv7em.rs @@ -15,7 +15,7 @@ use crate::clock::v2::pclk::{Pclk, PclkSourceId, ids::*}; use crate::pac::gclk::genctrl::Srcselect::*; use crate::pac::gclk::pchctrl::Genselect::*; use crate::pac::{self, Gclk, Mclk, Nvmctrl, Osc32kctrl, Oscctrl}; -use crate::sercom::*; +// use crate::sercom::*; // TODO why does this warn now but not before? use crate::time::Hertz; pub type ClockGenId = pac::gclk::pchctrl::Genselect; @@ -439,6 +439,7 @@ clock_generator!( (tc0_tc1, Tc0Tc1Clock, TC0_TC1, Tc0Tc1), (tcc0_tcc1, Tcc0Tcc1Clock, TCC0_TCC1, Tcc0Tcc1), (tc2_tc3, Tc2Tc3Clock, TC2_TC3, Tc2Tc3), + #[hal_cfg(all("tcc2", "tcc3"))] (tcc2_tcc3, Tcc2Tcc3Clock, TCC2_TCC3, Tcc2Tcc3), #[hal_cfg(all("tc4", "tc5"))] (tc4_tc5, Tc4Tc5Clock, TC4_TC5, Tc4Tc5), diff --git a/hal/src/peripherals/clock/d5x/v2.rs b/hal/src/clock/v2.rs similarity index 93% rename from hal/src/peripherals/clock/d5x/v2.rs rename to hal/src/clock/v2.rs index c815762c2298..cbd0a036a94c 100644 --- a/hal/src/peripherals/clock/d5x/v2.rs +++ b/hal/src/clock/v2.rs @@ -65,7 +65,7 @@ //! In general, there are two classes of clock in ATSAMD chips. Some clocks map //! one-to-one (1:1) to a specific bus or peripheral. This is true for the AHB //! clocks ([`AhbClk`]s), APB clocks ([`ApbClk`]s), GCLK outputs ([`GclkOut`]s), -//! peripheral channel clocks ([`Pclk`]s), and RTC oscillator ([`RtcOsc`]). +//! peripheral channel clocks ([`Pclk`]s), and RTC oscillator (`RtcOsc`). //! Other clocks form one-to-many (1:N) relationships, like the external crystal //! oscillator ([`Xosc`]), the 48 MHz DFLL ([`Dfll`]) or the two DPLLs //! ([`Dpll`]). @@ -115,9 +115,9 @@ //! on the movement of `Producer` objects. //! //! Instead, the `clock` module takes a different approach. It uses type-level -//! programming to track, at compile-time, the number of consumer clocks, N, -//! fed by a particular producer clock. With this approach, we can move -//! `Producer` objects while still making them impossible to modify if N > 0. +//! programming to track, at compile-time, the number of consumer clocks, N, fed +//! by a particular producer clock. With this approach, we can move `Producer` +//! objects while still making them impossible to modify if N > 0. //! //! The following sections will describe the implementation of this strategy. //! @@ -175,9 +175,9 @@ //! can only `Decrement` the same producer it `Increment`ed. Stated differently, //! we need a way to track the identity of each consumer's clock source. //! -//! The [`Source`] trait is designed for this purpose. It marks -//! [`Enabled`] producer clocks, and it's associated type, [`Id`], is the -//! identity type that should be stored by consumers. +//! The [`Source`] trait is designed for this purpose. It marks [`Enabled`] producer clocks, and it's associated type, [`Id`], is the identity type +//! that should be stored by consumers. //! //! Given that all implementers of `Source` are instances of `Enabled`, //! the naïve choice for [`Source::Id`] would be `T`. However, in a moment, we @@ -194,8 +194,8 @@ //! //! While these type parameters are important and necessary for configuration of //! a given producer clock, they are not relevant to consumer clocks. A consumer -//! clock does not need to know or care which `Mode` the XOSC is using, but -//! it *does* need to track that its clock [`Source`] is XOSC0. +//! clock does not need to know or care which `Mode` the XOSC is using, but it +//! *does* need to track that its clock [`Source`] is XOSC0. //! //! From this, we can see that `Enabled, N>` should not implement //! `Source` with `Source::Id = Xosc0`, because that would require consumers @@ -219,13 +219,13 @@ //! corresponding clock. Moreover, they also fundamentally restructure the way //! registers are accessed relative to the [PAC]. //! -//! Each of the four PAC clocking structs ([`OSCCTRL`], [`OSC32KCTRL`], [`GCLK`] -//! and [`MCLK`]) is a singleton object that controls a set of MMIO registers. -//! It is impossible to create two instances of any PAC object without `unsafe`. -//! However, each object controls a large set of registers that can be further -//! sub-divided into smaller sets for individual clocks. For example, the -//! [`GCLK`] object controls registers for 12 different clock generators and 48 -//! peripheral channel clocks. +//! Each of the PAC clocking structs (which vary between targets, including +//! `OSCCTRL`, `SYSCTRL`, `OSC32KCTRL`, [`GCLK`] and `MCLK`) is a singleton +//! object that controls a set of MMIO registers. It is impossible to create two +//! instances of any PAC object without `unsafe`. However, each object controls +//! a large set of registers that can be further sub-divided into smaller sets +//! for individual clocks. For example, the [`GCLK`] object controls registers +//! for 12 different clock generators and 48 peripheral channel clocks. //! //! `Token` types serve to break up the large PAC objects into smaller, //! more-targetted pieces. And in the process, they also remove the PAC objects' @@ -238,8 +238,8 @@ //! Bus clocks are fundamentally different from the other clock types in this //! module, because they do not use mutually exclusive registers for //! configuration. For instance, the registers that control [`Dpll0`] are -//! mutually exclusive to those that control [`Dpll1`], but `ApbClk` -//! and `ApbClk` share a single register. +//! mutually exclusive to those that control `Dpll1`, but `ApbClk` and +//! `ApbClk` share a single register. //! //! This presents a challenge for memory safety, because we need some way to //! guarantee that there are no data races. For example, if both @@ -403,24 +403,24 @@ //! //! Next, we want to use a DPLL to multiply the 8 MHz crystal clock up to 100 //! MHz. Once again, we need to decide between two instances of a clock, because -//! each chip has two [`Dpll`]s. This time, however, our decision between -//! [`Dpll0`] and [`Dpll1`] is arbitrary. +//! this chip has two [`Dpll`]s. This time, however, our decision between +//! [`Dpll0`] and `Dpll1` is arbitrary. //! //! Also note that, like before, `Dpll0` and `Dpll1` are aliases for -//! `Dpll` and `Dpll`. [`Dpll0Id`] and [`Dpll1Id`] +//! `Dpll` and `Dpll`. [`Dpll0Id`] and `Dpll1Id` //! represent the *identity* of the respective DPLL, while `I` represents the //! [`Id` type](self#id-types) for the [`Source`] driving the DPLL. In this //! particular case, we aim to create an instance of `Dpll0`. //! //! Only certain clocks can drive the DPLL, so `I` is constrained by the -//! [`DpllSourceId`] trait. Specifically, only the [`Xosc0Id`], [`Xosc1Id`], -//! [`Xosc32kId`] and [`GclkId`] types implement this trait. +//! [`DpllSourceId`] trait. Specifically, only the [`Xosc0Id`], `Xosc1Id` (only +//! some targets), [`Xosc32kId`] and [`GclkId`] types implement this trait. //! //! As before, we access the [`Tokens`] struct and use the corresponding //! [`DpllToken`] when creating an instance of `Dpll`. However, unlike before, //! we are creating a new clock-tree relationship that must be tracked by the -//! type system. Because DPLL0 will now consume XOSC0, we must [`Increment`] -//! the [`Enabled`] counter for [`EnabledXosc0`]. +//! type system. Because DPLL0 will now consume XOSC0, we must [`Increment`] the +//! [`Enabled`] counter for [`EnabledXosc0`]. //! //! Thus, to create an instance of `Dpll0`, we must provide the //! `EnabledXosc0`, so that its `U0` type parameter can be incremented to `U1`. @@ -461,11 +461,11 @@ //! # ).enable(); //! let (dpll0, xosc0) = Dpll::from_xosc(tokens.dpll0, xosc0); //! ``` -//! Next, we set the DPLL pre-divider and loop divider. We must pre-divide -//! the XOSC clock down from 8 MHz to 2 MHz, so that it is within the valid -//! input frequency range for the DPLL. Then, we set the DPLL loop divider, so -//! that it will multiply the 2 MHz clock by 50 for a 100 MHz output. We do not -//! need fractional mutiplication here, so the fractional loop divider is zero. +//! Next, we set the DPLL pre-divider and loop divider. We must pre-divide the +//! XOSC clock down from 8 MHz to 2 MHz, so that it is within the valid input +//! frequency range for the DPLL. Then, we set the DPLL loop divider, so that it +//! will multiply the 2 MHz clock by 50 for a 100 MHz output. We do not need +//! fractional mutiplication here, so the fractional loop divider is zero. //! Finally, we can enable the `Dpll`, yielding an instance of //! `EnabledDpll0`. //! @@ -515,11 +515,11 @@ //! [`EnabledGclk0`] to change the base clock without disabling GCLK0 or the //! main clock. //! -//! This time we will be modifying two [`Enabled`] counters simultaneously. -//! We will [`Decrement`] the [`EnabledDfll`] count from `U1` to `U0`, and -//! we will [`Increment`] the [`EnabledDpll0`] count from `U0` to `U1`. -//! Again, we need to provide both the DFLL and DPLL clocks, so that their -//! type parameters can be changed. +//! This time we will be modifying two [`Enabled`] counters simultaneously. We +//! will [`Decrement`] the [`EnabledDfll`] count from `U1` to `U0`, and we will +//! [`Increment`] the [`EnabledDpll0`] count from `U0` to `U1`. Again, we need +//! to provide both the DFLL and DPLL clocks, so that their type parameters can +//! be changed. //! //! ```no_run //! # use atsamd_hal::{ @@ -597,8 +597,8 @@ //! ``` //! //! We have the clocks set up, but we're not using them for anything other than -//! the main clock. Our final steps will create SERCOM APB and peripheral -//! clocks and will output the raw GCLK0 to a GPIO pin. +//! the main clock. Our final steps will create SERCOM APB and peripheral clocks +//! and will output the raw GCLK0 to a GPIO pin. //! //! To enable the APB clock for SERCOM0, we must access the [`Apb`] bus struct. //! We provide an [`ApbToken`] to the [`Apb::enable`] method and receive an @@ -768,10 +768,7 @@ //! ``` //! //! [PAC]: crate::pac -//! [`OSCCTRL`]: crate::pac::Oscctrl -//! [`OSC32KCTRL`]: crate::pac::Osc32kctrl //! [`GCLK`]: crate::pac::Gclk -//! [`MCLK`]: crate::pac::Mclk //! [`Peripherals::steal`]: crate::pac::Peripherals::steal //! //! [`Ahb`]: ahb::Ahb @@ -795,9 +792,7 @@ //! [`Dpll`]: dpll::Dpll //! [`Dpll`]: dpll::Dpll //! [`Dpll0`]: dpll::Dpll0 -//! [`Dpll1`]: dpll::Dpll1 //! [`Dpll0Id`]: dpll::Dpll0Id -//! [`Dpll1Id`]: dpll::Dpll1Id //! [`DpllSourceId`]: dpll::DpllSourceId //! [`DpllToken`]: dpll::DpllToken //! [`EnabledDpll0`]: dpll::EnabledDpll0 @@ -819,15 +814,12 @@ //! [`PclkSourceId`]: pclk::PclkSourceId //! [`PclkToken`]: pclk::PclkToken //! -//! [`RtcOsc`]: rtcosc::RtcOsc -//! //! [`Xosc`]: xosc::Xosc //! [`Xosc::from_crystal`]: xosc::Xosc::from_crystal //! [`Xosc::enable`]: xosc::Xosc::enable //! [`Xosc0`]: xosc::Xosc0 //! [`Xosc0`]: xosc::Xosc0 //! [`Xosc0Id`]: xosc::Xosc0Id -//! [`Xosc1Id`]: xosc::Xosc1Id //! [`XoscToken`]: xosc::XoscToken //! [`EnabledXosc0`]: xosc::EnabledXosc0 //! [`EnabledXosc0`]: xosc::EnabledXosc0 @@ -850,10 +842,13 @@ //! [`Sub1`]: typenum::Sub1 //! [`Unsigned`]: typenum::Unsigned //! -//! [interior mutability]: https://doc.rust-lang.org/reference/interior-mutability.html +//! [interior mutability]: +//! https://doc.rust-lang.org/reference/interior-mutability.html #![allow(clippy::manual_range_contains)] +use atsamd_hal_macros::hal_module; + use typenum::U0; use crate::time::Hertz; @@ -864,14 +859,32 @@ pub mod apb; pub mod dfll; pub mod dpll; pub mod gclk; +#[hal_module( + any("clock-d11", "clock-d21") => "v2/osc.rs", +)] +pub mod osc {} +#[hal_module( + "sysctrl" => "v2/osc32k.rs", +)] +pub mod osc32k {} pub mod osculp32k; pub mod pclk; -pub mod rtcosc; +#[hal_module( + "clock-d5x" => "v2/rtcosc.rs", +)] +pub mod rtcosc {} pub mod types; pub mod xosc; -pub mod xosc32k; +#[hal_module( + "xosc32k" => "v2/xosc32k.rs" +)] +pub mod xosc32k {} -mod reset; +#[hal_module( + any("clock-d11", "clock-d21") => "v2/reset_thumbv6m.rs", + "clock-d5x" => "v2/reset_thumbv7em.rs", +)] +mod reset {} pub use reset::*; // `Token` types and memory safety diff --git a/hal/src/peripherals/clock/d5x/v2/ahb.rs b/hal/src/clock/v2/ahb.rs similarity index 95% rename from hal/src/peripherals/clock/d5x/v2/ahb.rs rename to hal/src/clock/v2/ahb.rs index 75b7ee27bd14..76b7ead58f5d 100644 --- a/hal/src/peripherals/clock/d5x/v2/ahb.rs +++ b/hal/src/clock/v2/ahb.rs @@ -122,14 +122,24 @@ //! [`Clocks`]: super::Clocks //! [`Buses`]: super::Buses -use atsamd_hal_macros::hal_macro_helper; +use atsamd_hal_macros::{hal_cfg, hal_macro_helper}; use core::marker::PhantomData; use bitflags; use paste::paste; -use crate::pac::{Mclk, mclk}; +#[hal_cfg("clock-d5x")] +mod imports { + pub use crate::pac::{Mclk as Peripheral, mclk::Ahbmask}; +} + +#[hal_cfg(any("clock-d11", "clock-d21"))] +mod imports { + pub use crate::pac::{Pm as Peripheral, pm::Ahbmask}; +} + +use imports::*; use super::types::*; @@ -160,11 +170,11 @@ impl Ahb { } #[inline] - fn ahbmask(&mut self) -> &mclk::Ahbmask { + fn ahbmask(&mut self) -> &Ahbmask { // Safety: The `Ahb` type has exclusive access to the `AHBMASK` // register. See the notes on `Token` types and memory safety in the // root of the `clock` module for more details. - unsafe { (*Mclk::PTR).ahbmask() } + unsafe { (*Peripheral::PTR).ahbmask() } } #[inline] @@ -373,6 +383,7 @@ macro_rules! define_ahb_types { } #[hal_macro_helper] +#[hal_cfg("clock-d5x")] define_ahb_types!( Hpb0 = 0, Hpb1 = 1, @@ -383,7 +394,7 @@ define_ahb_types!( Cmcc = 8, Dmac = 9, Usb = 10, - Pac = 12, + Pac0 = 12, Qspi = 13, #[hal_cfg("gmac")] Gmac = 14, @@ -400,3 +411,14 @@ define_ahb_types!( NvmCtrlSmeeProm = 22, NvmCtrlCache = 23, ); + +#[hal_cfg(any("clock-d11", "clock-d21"))] +define_ahb_types!( + Hpb0 = 0, + Hpb1 = 1, + Hpb2 = 2, + Dsu = 3, + NvmCtrl = 4, + Dmac = 5, + Usb = 6, // TODO this should be conditional. Others? +); diff --git a/hal/src/peripherals/clock/d5x/v2/apb.rs b/hal/src/clock/v2/apb.rs similarity index 65% rename from hal/src/peripherals/clock/d5x/v2/apb.rs rename to hal/src/clock/v2/apb.rs index 57944682144e..1855be8fa7ab 100644 --- a/hal/src/peripherals/clock/d5x/v2/apb.rs +++ b/hal/src/clock/v2/apb.rs @@ -121,13 +121,25 @@ //! [`Clocks`]: super::Clocks //! [`Buses`]: super::Buses -use atsamd_hal_macros::hal_macro_helper; +use atsamd_hal_macros::{hal_cfg, hal_macro_helper}; use core::marker::PhantomData; use bitflags; use paste::paste; -use crate::pac::{self, mclk}; +#[hal_cfg("clock-d5x")] +mod imports { + pub use crate::pac::Mclk as Peripheral; + pub use crate::pac::mclk::{Apbamask, Apbbmask, Apbcmask, Apbdmask, RegisterBlock as BLOCK}; +} + +#[hal_cfg(any("clock-d11", "clock-d21"))] +mod imports { + pub use crate::pac::Pm as Peripheral; + pub use crate::pac::pm::{Apbamask, Apbbmask, Apbcmask, RegisterBlock as BLOCK}; +} + +use imports::*; use crate::typelevel::Sealed; @@ -160,35 +172,37 @@ impl Apb { } #[inline] - fn mclk(&self) -> &mclk::RegisterBlock { + fn mclk(&self) -> &BLOCK { // Safety: The `Apb` type has exclusive access to the `APBXMASK` // registers, and it uses a shared reference to the register block. See // the notes on `Token` types and memory safety in the root of the // `clock` module for more details. - unsafe { &*pac::Mclk::PTR } + unsafe { &*Peripheral::PTR } } #[inline] - fn apbamask(&mut self) -> &mclk::Apbamask { + fn apbamask(&mut self) -> &Apbamask { self.mclk().apbamask() } #[inline] - fn apbbmask(&mut self) -> &mclk::Apbbmask { + fn apbbmask(&mut self) -> &Apbbmask { self.mclk().apbbmask() } #[inline] - fn apbcmask(&mut self) -> &mclk::Apbcmask { + fn apbcmask(&mut self) -> &Apbcmask { self.mclk().apbcmask() } #[inline] - fn apbdmask(&mut self) -> &mclk::Apbdmask { + #[hal_cfg("clock-d5x")] + fn apbdmask(&mut self) -> &Apbdmask { self.mclk().apbdmask() } #[inline] + #[hal_macro_helper] fn enable_mask(&mut self, mask: ApbMask) { // Safety: The mask bits are derived from a `bitflags` struct, so they // are guaranteed to be valid. @@ -206,6 +220,7 @@ impl Apb { self.apbcmask() .modify(|r, w| w.bits(r.bits() | mask.bits())); } + #[hal_cfg("clock-d5x")] ApbMask::D(mask) => { self.apbdmask() .modify(|r, w| w.bits(r.bits() | mask.bits())); @@ -215,6 +230,7 @@ impl Apb { } #[inline] + #[hal_macro_helper] fn disable_mask(&mut self, mask: ApbMask) { // Safety: The mask bits are derived from a `bitflags` struct, so they // are guaranteed to be valid. @@ -232,6 +248,7 @@ impl Apb { self.apbcmask() .modify(|r, w| w.bits(r.bits() & !mask.bits())); } + #[hal_cfg("clock-d5x")] ApbMask::D(mask) => { self.apbdmask() .modify(|r, w| w.bits(r.bits() & !mask.bits())); @@ -270,20 +287,31 @@ impl Apb { /// /// Each variant is a [`bitflags`] struct with a binary representation exactly /// matching the corresponding APB `MASK` register. +#[hal_macro_helper] enum ApbMask { A(ApbAMask), B(ApbBMask), C(ApbCMask), + #[hal_cfg("clock-d5x")] D(ApbDMask), } +/// Define several APB-related types +/// +/// Define the [`DynApbId`], `ApbXMask`, [`ApbTokens`] and [`ApbClks`] types. +/// +/// This macro uses a slight hack to simplify its implementation. It uses +/// `#[cfg(all())]` and `#[cfg(any())]` to represent `#[cfg(true)]` and +/// `#[cfg(false)]`, respectively. We can use this to selectively place each +/// APB type into the [`ApbTokens`] struct or the [`ApbClks`] struct, depending +/// on whether or not the corresponding bit is enabled at power-on reset. macro_rules! define_apb_types { ( $( $Reg:ident { $( $( #[$( $cfg:tt )+] )? - $Type:ident = $BIT:literal, + $Type:ident = ($BIT:literal, $token:ident, $clk:ident) )+ } )+ @@ -348,81 +376,235 @@ macro_rules! define_apb_types { } } } + + /// Set of [`ApbToken`]s for APB clocks that are disabled at power-on reset + pub struct ApbTokens { + $( + $( + $( #[$( $cfg )+] )? + #[cfg($token())] + pub [<$Type:snake>]: ApbToken<$Type>, + )+ + )+ + } + + impl ApbTokens { + /// Create the set of [`ApbToken`]s + /// + /// # Safety + /// + /// All invariants required by `ApbToken::new` must be upheld here as well. + #[inline] + pub(super) unsafe fn new() -> Self { + Self { + $( + $( + $( #[$( $cfg )+] )? + #[cfg($token())] + [<$Type:snake>]: unsafe { ApbToken::new() }, + )+ + )+ + } + } + } + + /// Set of [`ApbClk`]s for APB clocks that are enabled at power-on reset + pub struct ApbClks { + $( + $( + $( #[$( $cfg )+] )? + #[cfg($clk())] + pub [<$Type:snake>]: ApbClk<$Type>, + )+ + )+ + } + + impl ApbClks { + /// Create the set of [`ApbClk`]s + /// + /// # Safety + /// + /// All invariants required by `ApbToken::new` must be upheld here as well. + #[inline] + pub(super) unsafe fn new() -> Self { + Self { + $( + $( + $( #[$( $cfg )+] )? + #[cfg($clk())] + [<$Type:snake>]: ApbClk::new( unsafe { ApbToken::new() } ), + )+ + )+ + } + } + } } }; } +// (N, all, any) => include in clocks not tokens = enabled at power-on #[hal_macro_helper] +#[hal_cfg("clock-d5x")] define_apb_types!( A { - Pac = 0, - Pm = 1, - Mclk = 2, - RstC = 3, - OscCtrl = 4, - Osc32kCtrl = 5, - SupC = 6, - Gclk = 7, - Wdt = 8, - Rtc = 9, - Eic = 10, - FreqM = 11, - Sercom0 = 12, - Sercom1 = 13, - Tc0 = 14, - Tc1 = 15, + Pac0 = (0, all, any) + Pm = (1, all, any) + Mclk = (2, all, any) + RstC = (3, all, any) + OscCtrl = (4, all, any) + Osc32kCtrl = (5, all, any) + SupC = (6, all, any) + Gclk = (7, all, any) + Wdt = (8, all, any) + Rtc = (9, all, any) + Eic = (10, all, any) + FreqM = (11, any, all) + Sercom0 = (12, any, all) + Sercom1 = (13, any, all) + Tc0 = (14, any, all) + Tc1 = (15, any, all) } B { - Usb = 0, - Dsu = 1, - NvmCtrl = 2, - Port = 4, - EvSys = 7, - Sercom2 = 9, - Sercom3 = 10, - Tcc0 = 11, - Tcc1 = 12, - Tc2 = 13, - Tc3 = 14, - RamEcc = 16, + Usb = (0, any, all) + Dsu = (1, all, any) + NvmCtrl = (2, all, any) + Port = (4, all, any) + EvSys = (7, any, all) + Sercom2 = (9, any, all) + Sercom3 = (10, any, all) + Tcc0 = (11, any, all) + Tcc1 = (12, any, all) + Tc2 = (13, any, all) + Tc3 = (14, any, all) + RamEcc = (16, all, any) } C { #[hal_cfg("gmac")] - Gmac = 2, - Tcc2 = 3, + Gmac = (2, all, any) + Tcc2 = (3, any, all) #[hal_cfg("tcc3")] - Tcc3 = 4, + Tcc3 = (4, any, all) #[hal_cfg("tc4")] - Tc4 = 5, + Tc4 = (5, any, all) // TODO double check this is correct #[hal_cfg("tc5")] - Tc5 = 6, - PDec = 7, - Ac = 8, - Aes = 9, - Trng = 10, - Icm = 11, - Qspi = 13, - Ccl = 14, + Tc5 = (6, any, all) + PDec = (7, any, all) + Ac = (8, any, all) + Aes = (9, any, all) + Trng = (10, any, all) + Icm = (11, any, all) + Qspi = (13, all, any) + Ccl = (14, any, all) } D { - Sercom4 = 0, - Sercom5 = 1, + Sercom4 = (0, all, any) + Sercom5 = (1, all, any) #[hal_cfg("sercom6")] - Sercom6 = 2, + Sercom6 = (2, all, any) #[hal_cfg("sercom7")] - Sercom7 = 3, + Sercom7 = (3, all, any) #[hal_cfg("tcc4")] - Tcc4 = 4, + Tcc4 = (4, all, any) #[hal_cfg("tc6")] - Tc6 = 5, + Tc6 = (5, all, any) #[hal_cfg("tc7")] - Tc7 = 6, - Adc0 = 7, - Adc1 = 8, - Dac = 9, + Tc7 = (6, all, any) + Adc0 = (7, all, any) + Adc1 = (8, all, any) + Dac = (9, all, any) + #[hal_cfg("i2s")] + I2S = (10, all, any) + Pcc = (11, all, any) + } +); + +// SAMD21/DA1 datasheet DS40001882H, Table 12-1. Peripherals Configuration +// Summary +#[hal_macro_helper] +#[hal_cfg("clock-d21")] +define_apb_types!( + A { + Pac0 = (0, all, any) + Pm = (1, all, any) + SysCtrl = (2, all, any) + Gclk = (3, all, any) + Wdt = (4, all, any) + Rtc = (5, all, any) + Eic = (6, all, any) + } + B { + Pac1 = (0, all, any) + Dsu = (1, all, any) + NvmCtrl = (2, all, any) + Port = (3, all, any) + Dmac = (4, all, any) + #[hal_cfg("usb")] + Usb = (5, all, any) + } + C { + Pac2 = (0, any, all) + EvSys = (1, any, all) + Sercom0 = (2, any, all) + Sercom1 = (3, any, all) + Sercom2 = (4, any, all) + Sercom3 = (5, any, all) + #[hal_cfg("sercom4")] + Sercom4 = (6, any, all) + #[hal_cfg("sercom5")] + Sercom5 = (7, any, all) + Tcc0 = (8, any, all) + Tcc1 = (9, any, all) + Tcc2 = (10, any, all) + Tc3 = (11, any, all) + Tc4 = (12, any, all) + Tc5 = (13, any, all) + Adc0 = (16, any, all) + Ac = (17, any, all) + Dac = (18, any, all) + Ptc = (19, any, all) #[hal_cfg("i2s")] - I2S = 10, - Pcc = 11, + I2S = (20, any, all) + Ac1 = (21, any, all) + } +); + +// Atmel-42363H-SAM-D11-Datasheet_09/2016, Table 11-1. Peripherals Configuration +// Summary +#[hal_macro_helper] +#[hal_cfg("clock-d11")] +define_apb_types!( + A { + Pac0 = (0, all, any) + Pm = (1, all, any) + SysCtrl = (2, all, any) + Gclk = (3, all, any) + Wdt = (4, all, any) + Rtc = (5, all, any) + Eic = (6, all, any) + } + B { + Pac1 = (0, all, any) + Dsu = (1, all, any) + NvmCtrl = (2, all, any) + Port = (3, all, any) + Dmac = (4, all, any) + #[hal_cfg("usb")] + Usb = (5, all, any) + } + C { + Pac2 = (0, any, all) + EvSys = (1, any, all) + Sercom0 = (2, any, all) + Sercom1 = (3, any, all) + #[hal_cfg("sercom2")] + Sercom2 = (4, any, all) + Tcc0 = (5, any, all) + Tc1 = (6, any, all) + Tc2 = (7, any, all) + Adc0 = (8, any, all) + Ac = (9, any, all) + Dac = (10, any, all) + Ptc = (11, any, all) } ); @@ -503,177 +685,3 @@ impl ApbClk { self.token } } - -//============================================================================== -// ApbTokens -//============================================================================== - -/// Set of [`ApbToken`]s for APB clocks that are disabled at power-on reset -#[hal_macro_helper] -pub struct ApbTokens { - pub freq_m: ApbToken, - pub sercom0: ApbToken, - pub sercom1: ApbToken, - pub tc0: ApbToken, - pub tc1: ApbToken, - pub usb: ApbToken, - pub ev_sys: ApbToken, - pub sercom2: ApbToken, - pub sercom3: ApbToken, - pub tcc0: ApbToken, - pub tcc1: ApbToken, - pub tc2: ApbToken, - pub tc3: ApbToken, - #[hal_cfg("tc4")] - pub tc4: ApbToken, - pub tcc2: ApbToken, - #[hal_cfg("tcc3")] - pub tcc3: ApbToken, - #[hal_cfg("tc5")] - pub tc5: ApbToken, - pub p_dec: ApbToken, - pub ac: ApbToken, - pub aes: ApbToken, - pub trng: ApbToken, - pub icm: ApbToken, - pub ccl: ApbToken, - pub sercom4: ApbToken, - pub sercom5: ApbToken, - #[hal_cfg("sercom6")] - pub sercom6: ApbToken, - #[hal_cfg("sercom7")] - pub sercom7: ApbToken, - #[hal_cfg("tcc4")] - pub tcc4: ApbToken, - #[hal_cfg("tc6")] - pub tc6: ApbToken, - #[hal_cfg("tc7")] - pub tc7: ApbToken, - pub adc0: ApbToken, - pub adc1: ApbToken, - pub dac: ApbToken, - #[hal_cfg("i2s")] - pub i2s: ApbToken, - pub pcc: ApbToken, -} - -impl ApbTokens { - /// Create the set of [`ApbToken`]s - /// - /// # Safety - /// - /// All invariants required by `ApbToken::new` must be upheld here as well. - #[inline] - #[hal_macro_helper] - pub(super) unsafe fn new() -> Self { - unsafe { - Self { - freq_m: ApbToken::new(), - sercom0: ApbToken::new(), - sercom1: ApbToken::new(), - tc0: ApbToken::new(), - tc1: ApbToken::new(), - usb: ApbToken::new(), - ev_sys: ApbToken::new(), - sercom2: ApbToken::new(), - sercom3: ApbToken::new(), - tcc0: ApbToken::new(), - tcc1: ApbToken::new(), - tc2: ApbToken::new(), - tc3: ApbToken::new(), - #[hal_cfg("tc4")] - tc4: ApbToken::new(), - tcc2: ApbToken::new(), - #[hal_cfg("tcc3")] - tcc3: ApbToken::new(), - #[hal_cfg("tc5")] - tc5: ApbToken::new(), - p_dec: ApbToken::new(), - ac: ApbToken::new(), - aes: ApbToken::new(), - trng: ApbToken::new(), - icm: ApbToken::new(), - ccl: ApbToken::new(), - sercom4: ApbToken::new(), - sercom5: ApbToken::new(), - #[hal_cfg("sercom6")] - sercom6: ApbToken::new(), - #[hal_cfg("sercom7")] - sercom7: ApbToken::new(), - #[hal_cfg("tcc4")] - tcc4: ApbToken::new(), - #[hal_cfg("tc6")] - tc6: ApbToken::new(), - #[hal_cfg("tc7")] - tc7: ApbToken::new(), - adc0: ApbToken::new(), - adc1: ApbToken::new(), - dac: ApbToken::new(), - #[hal_cfg("i2s")] - i2s: ApbToken::new(), - pcc: ApbToken::new(), - } - } - } -} - -//============================================================================== -// ApbClks -//============================================================================== - -/// Set of [`ApbClk`]s for APB clocks that are enabled at power-on reset -#[hal_macro_helper] -pub struct ApbClks { - pub pac: ApbClk, - pub pm: ApbClk, - pub mclk: ApbClk, - pub rst_c: ApbClk, - pub osc_ctrl: ApbClk, - pub osc32k_ctrl: ApbClk, - pub sup_c: ApbClk, - pub gclk: ApbClk, - pub wdt: ApbClk, - pub rtc: ApbClk, - pub eic: ApbClk, - pub dsu: ApbClk, - pub nvm_ctrl: ApbClk, - pub port: ApbClk, - pub ram_ecc: ApbClk, - #[hal_cfg("gmac")] - pub gmac: ApbClk, - pub qspi: ApbClk, -} - -impl ApbClks { - /// Create the set of [`ApbClk`]s - /// - /// # Safety - /// - /// All invariants required by `ApbToken::new` must be upheld here as well. - #[inline] - #[hal_macro_helper] - pub(super) unsafe fn new() -> Self { - unsafe { - ApbClks { - pac: ApbClk::new(ApbToken::new()), - pm: ApbClk::new(ApbToken::new()), - mclk: ApbClk::new(ApbToken::new()), - rst_c: ApbClk::new(ApbToken::new()), - osc_ctrl: ApbClk::new(ApbToken::new()), - osc32k_ctrl: ApbClk::new(ApbToken::new()), - sup_c: ApbClk::new(ApbToken::new()), - gclk: ApbClk::new(ApbToken::new()), - wdt: ApbClk::new(ApbToken::new()), - rtc: ApbClk::new(ApbToken::new()), - eic: ApbClk::new(ApbToken::new()), - dsu: ApbClk::new(ApbToken::new()), - nvm_ctrl: ApbClk::new(ApbToken::new()), - port: ApbClk::new(ApbToken::new()), - ram_ecc: ApbClk::new(ApbToken::new()), - #[hal_cfg("gmac")] - gmac: ApbClk::new(ApbToken::new()), - qspi: ApbClk::new(ApbToken::new()), - } - } - } -} diff --git a/hal/src/peripherals/clock/d5x/v2/dfll.rs b/hal/src/clock/v2/dfll.rs similarity index 93% rename from hal/src/peripherals/clock/d5x/v2/dfll.rs rename to hal/src/clock/v2/dfll.rs index 58cbceef6af7..3dc39520b4db 100644 --- a/hal/src/peripherals/clock/d5x/v2/dfll.rs +++ b/hal/src/clock/v2/dfll.rs @@ -272,7 +272,24 @@ //! [`from_usb`]: Dfll::from_usb //! [`into_mode`]: EnabledDfll::into_mode +use atsamd_hal_macros::{hal_cfg, hal_macro_helper}; + +#[hal_cfg("oscctrl")] +mod imports { + pub use crate::pac::Oscctrl as PERIPHERAL; + pub use crate::pac::oscctrl::{ + Dfllctrla as Dfllctrl, Dfllctrlb, Dfllmul, Dfllsync, RegisterBlock, + }; +} + +#[hal_cfg("sysctrl")] +mod imports { + pub use crate::pac::Sysctrl as PERIPHERAL; + pub use crate::pac::sysctrl::{Dfllctrl, Dfllmul, RegisterBlock}; +} + use fugit::RateExtU32; +use imports::*; use typenum::U0; use crate::time::Hertz; @@ -309,51 +326,64 @@ impl DfllToken { } #[inline] - fn oscctrl(&self) -> &crate::pac::oscctrl::RegisterBlock { + fn reg_block(&self) -> &RegisterBlock { // Safety: The `DfllToken` only has access to a mutually exclusive set // of registers for the DFLL, and we use a shared reference to the // register block. See the notes on `Token` types and memory safety in // the root of the `clock` module for more details. - unsafe { &*crate::pac::Oscctrl::PTR } + unsafe { &*PERIPHERAL::PTR } + } + + #[hal_cfg("clock-d5x")] + #[inline] + fn dfllctrl(&self) -> &Dfllctrl { + self.reg_block().dfllctrla() } + #[hal_cfg(any("clock-d11", "clock-d21"))] #[inline] - fn dfllctrla(&self) -> &crate::pac::oscctrl::Dfllctrla { - self.oscctrl().dfllctrla() + fn dfllctrl(&self) -> &Dfllctrl { + self.reg_block().dfllctrl() } + #[hal_cfg("clock-d5x")] #[inline] - fn dfllctrlb(&self) -> &crate::pac::oscctrl::Dfllctrlb { - self.oscctrl().dfllctrlb() + fn dfllctrlb(&self) -> &Dfllctrlb { + self.reg_block().dfllctrlb() } #[inline] - fn dfllmul(&self) -> &crate::pac::oscctrl::Dfllmul { - self.oscctrl().dfllmul() + fn dfllmul(&self) -> &Dfllmul { + self.reg_block().dfllmul() } + #[hal_cfg("clock-d5x")] #[inline] - fn dfllsync(&self) -> &crate::pac::oscctrl::Dfllsync { - self.oscctrl().dfllsync() + fn dfllsync(&self) -> &Dfllsync { + self.reg_block().dfllsync() } + #[hal_cfg("clock-d5x")] #[inline] fn wait_sync_enable(&self) { while self.dfllsync().read().enable().bit() {} } + #[hal_cfg("clock-d5x")] #[inline] fn wait_sync_dfllmul(&self) { while self.dfllsync().read().dfllmul().bit() {} } + #[hal_cfg("clock-d5x")] #[inline] fn wait_sync_dfllctrlb(&self) { while self.dfllsync().read().dfllctrlb().bit() {} } + #[hal_cfg("clock-d5x")] #[inline] - fn configure(&mut self, settings: settings::All) { + fn enable(&mut self, settings: settings::All) { self.dfllctrlb().modify(|_, w| { w.mode().bit(settings.closed_loop); w.usbcrm().bit(settings.usb_recovery); @@ -371,27 +401,55 @@ impl DfllToken { }); self.wait_sync_dfllmul(); } - self.dfllctrla().modify(|_, w| { + self.dfllctrl().modify(|_, w| { w.runstdby().bit(settings.run_standby); - w.ondemand().bit(settings.on_demand) + w.ondemand().bit(settings.on_demand); + w.enable().set_bit() }); + self.wait_sync_enable(); } + #[hal_cfg(any("clock-d11", "clock-d21"))] #[inline] - fn enable(&mut self) { - self.dfllctrla().modify(|_, w| w.enable().set_bit()); - self.wait_sync_enable(); + fn enable(&mut self, settings: settings::All) { + if settings.closed_loop { + self.dfllmul().write(|w| + // Safety: All bit patterns are valid for these fields + unsafe { + w.mul().bits(settings.mult_factor); + w.cstep().bits(settings.coarse_max_step); + w.fstep().bits(settings.fine_max_step) + }); + } + self.dfllctrl().write(|w| { + w.mode().bit(settings.closed_loop); + w.usbcrm().bit(settings.usb_recovery); + w.ccdis().bit(!settings.chill_cycle); + w.qldis().bit(!settings.quick_lock); + w.runstdby().bit(settings.run_standby); + w.ondemand().bit(settings.on_demand); + w.enable().set_bit() + }); } #[inline] + #[hal_macro_helper] fn disable(&mut self) { - self.dfllctrla().modify(|_, w| w.enable().clear_bit()); + self.dfllctrl().write(|w| w.enable().clear_bit()); + #[hal_cfg("clock-d5x")] self.wait_sync_enable(); } + #[hal_cfg("clock-d5x")] + #[inline] + fn is_ready(&self) -> bool { + self.reg_block().status().read().dfllrdy().bit() + } + + #[hal_cfg(any("clock-d11", "clock-d21"))] #[inline] fn is_ready(&self) -> bool { - self.oscctrl().status().read().dfllrdy().bit() + self.reg_block().pclksr().read().dfllrdy().bit() } } @@ -401,7 +459,10 @@ impl DfllToken { type MultFactor = u16; type CoarseMaxStep = u8; +#[hal_cfg("clock-d5x")] type FineMaxStep = u8; +#[hal_cfg(any("clock-d11", "clock-d21"))] +type FineMaxStep = u16; //============================================================================== // DfllId @@ -1122,8 +1183,7 @@ impl Dfll { /// [`Source`] for other clocks. #[inline] pub fn enable(mut self) -> EnabledDfll { - self.token.configure(self.settings.all()); - self.token.enable(); + self.token.enable(self.settings.all()); Enabled::new(self) } } diff --git a/hal/src/peripherals/clock/d5x/v2/dpll.rs b/hal/src/clock/v2/dpll.rs similarity index 87% rename from hal/src/peripherals/clock/d5x/v2/dpll.rs rename to hal/src/clock/v2/dpll.rs index 7f20db8a820c..f1c35227aeb3 100644 --- a/hal/src/peripherals/clock/d5x/v2/dpll.rs +++ b/hal/src/clock/v2/dpll.rs @@ -1,9 +1,10 @@ -//! # Digital Phase-Locked Loop +//! # Fractional Digital Phase-Locked Loop //! //! ## Overview //! -//! The `dpll` module provides access to the two digital phase-locked loops -//! (DPLLs) within the `OSCCTRL` peripheral. +//! The `dpll` module provides access to the digital phase-locked loops (DPLLs) +//! within the `OSCCTRL` or `SYSCTRL` peripheral, datasheets refer to these as +//! FDPLL200M and FDPLL96M, respectively. //! //! A DPLL is used to multiply clock frequencies. It takes a lower-frequency //! input clock and produces a higher-frequency output clock. It works by taking @@ -237,22 +238,41 @@ //! [`GclkDivider`]: super::gclk::GclkDivider //! [`Pclk`]: super::pclk::Pclk +use atsamd_hal_macros::{hal_cfg, hal_macro_helper}; use core::marker::PhantomData; use fugit::RateExtU32; use typenum::U0; -use crate::pac::oscctrl; -use crate::pac::oscctrl::dpll::{Dpllctrla, Dpllctrlb, Dpllratio, dpllstatus, dpllsyncbusy}; - -use crate::pac::oscctrl::dpll::dpllctrlb::Refclkselect; +#[hal_cfg("oscctrl")] +use crate::pac::{ + Oscctrl as Peripheral, + oscctrl::{ + Dpll as PacDpll, + dpll::dpllctrlb::Refclkselect, + dpll::{Dpllctrla, Dpllctrlb, Dpllratio, dpllstatus, dpllsyncbusy}, + }, +}; + +#[hal_cfg("sysctrl")] +use crate::pac::{ + Sysctrl as Peripheral, + sysctrl::{ + RegisterBlock as PacDpll, + dpllctrlb::Refclkselect, + {Dpllctrla, Dpllctrlb, Dpllratio, dpllstatus}, + }, +}; use crate::time::Hertz; use crate::typelevel::{Decrement, Increment, Sealed}; use super::gclk::GclkId; use super::pclk::{Pclk, PclkId}; -use super::xosc::{Xosc0Id, Xosc1Id, XoscId}; +#[hal_cfg("xosc1")] +use super::xosc::Xosc1Id; +use super::xosc::{Xosc0Id, XoscId}; +#[hal_cfg("xosc32k")] use super::xosc32k::Xosc32kId; use super::{Enabled, Source}; @@ -292,12 +312,20 @@ impl DpllToken { /// Access the corresponding PAC `DPLL` struct #[inline] - fn dpll(&self) -> &oscctrl::Dpll { + #[hal_macro_helper] + fn dpll(&self) -> &PacDpll { // Safety: Each `DpllToken` only has access to a mutually exclusive set // of registers for the corresponding `DpllId`, and we use a shared // reference to the register block. See the notes on `Token` types and // memory safety in the root of the `clock` module for more details. - unsafe { (*crate::pac::Oscctrl::PTR).dpll(D::NUM) } + #[hal_cfg("oscctrl")] + unsafe { + (*Peripheral::PTR).dpll(D::NUM) + } + #[hal_cfg("sysctrl")] + unsafe { + &(*Peripheral::PTR) + } } /// Access the corresponding Dpllctrla register @@ -318,6 +346,7 @@ impl DpllToken { self.dpll().dpllratio() } + #[hal_cfg("oscctrl")] /// Access the corresponding DPLLSYNCBUSY register for reading only #[inline] fn syncbusy(&self) -> dpllsyncbusy::R { @@ -331,13 +360,17 @@ impl DpllToken { } #[inline] - fn configure(&mut self, id: DynDpllSourceId, settings: Settings, prediv: u16) { + #[hal_macro_helper] + fn enable(&mut self, id: DynDpllSourceId, settings: Settings, prediv: u16) { // Convert the actual predivider to the `div` register field value let div = match id { - DynDpllSourceId::Xosc0 | DynDpllSourceId::Xosc1 => prediv / 2 - 1, + DynDpllSourceId::Xosc0 => prediv / 2 - 1, + #[hal_cfg("xosc1")] + DynDpllSourceId::Xosc1 => prediv / 2 - 1, _ => 0, }; - self.ctrlb().modify(|_, w| { + + self.ctrlb().write(|w| { // Safety: The value is masked to the correct bit width by the PAC. // An invalid value could produce an invalid clock frequency, but // that does not break memory safety. @@ -345,6 +378,7 @@ impl DpllToken { w.refclk().variant(id.into()); w.lbypass().bit(settings.lock_bypass); w.wuf().bit(settings.wake_up_fast); + #[hal_cfg("oscctrl")] if let Some(cap) = settings.dco_filter { w.dcoen().bit(true); unsafe { @@ -362,25 +396,40 @@ impl DpllToken { w.ldr().bits(settings.mult - 1); w.ldrfrac().bits(settings.frac) }); + #[hal_cfg("oscctrl")] while self.syncbusy().dpllratio().bit_is_set() {} self.ctrla().modify(|_, w| { w.ondemand().bit(settings.on_demand); - w.runstdby().bit(settings.run_standby) + w.runstdby().bit(settings.run_standby); + w.enable().set_bit() }); + self.wait_enabled(); + } + + /// Disable the [`Dpll`] + #[inline] + fn disable(&mut self) { + self.ctrla().modify(|_, w| w.enable().clear_bit()); + self.wait_disabled(); } - /// Enable the [`Dpll`] + /// Waits for the enable bit to synchronize in the enabled state #[inline] - fn enable(&mut self) { - self.ctrla().modify(|_, w| w.enable().set_bit()); + #[hal_macro_helper] + fn wait_enabled(&self) { + #[hal_cfg("oscctrl")] while self.syncbusy().enable().bit_is_set() {} + #[hal_cfg("sysctrl")] + while self.status().enable().bit_is_set() {} } - /// Disable the [`Dpll`] #[inline] - fn disable(&mut self) { - self.ctrla().modify(|_, w| w.enable().clear_bit()); + #[hal_macro_helper] + fn wait_disabled(&self) { + #[hal_cfg("oscctrl")] while self.syncbusy().enable().bit_is_set() {} + #[hal_cfg("sysctrl")] + while self.status().enable().bit_is_set() {} } /// Check the STATUS register to see if the clock is locked @@ -415,15 +464,14 @@ pub enum DynDpllId { // DpllId //============================================================================== -/// Type-level enum identifying one of two possible [`Dpll`]s +/// Type-level enum identifying a [`Dpll`] /// -/// The types implementing this trait, i.e. [`Dpll0Id`] and [`Dpll1Id`], are -/// type-level variants of `DpllId`, and they identify one of the two possible -/// digital phase-locked loops. +/// The types implementing this trait, i.e. [`Dpll0Id`] (and `Dpll1Id`, on +/// targets with two DPLLs), identify a specific digital phase-locked loop. /// /// `DpllId` is the type-level equivalent of [`DynDpllId`]. See the -/// documentation on [type-level programming] and specifically -/// [type-level enums] for more details. +/// documentation on [type-level programming] and specifically [type-level +/// enums] for more details. /// /// [type-level programming]: crate::typelevel /// [type-level enums]: crate::typelevel#type-level-enums @@ -457,10 +505,13 @@ impl DpllId for Dpll0Id { /// /// [type-level programming]: crate::typelevel /// [type-level enums]: crate::typelevel#type-level-enums +#[hal_cfg("oscctrl")] pub enum Dpll1Id {} +#[hal_cfg("oscctrl")] impl Sealed for Dpll1Id {} +#[hal_cfg("oscctrl")] impl DpllId for Dpll1Id { const DYN: DynDpllId = DynDpllId::Dpll1; const NUM: usize = 1; @@ -476,18 +527,21 @@ impl DpllId for Dpll1Id { /// a given [`Dpll`]. /// /// `DynDpllSourceId` is the value-level equivalent of [`DpllSourceId`]. +#[hal_macro_helper] #[derive(Copy, Clone, PartialEq, Eq, Debug)] pub enum DynDpllSourceId { /// The DPLL is driven by a [`Pclk`] Pclk, /// The DPLL is driven by [`Xosc0`](super::xosc::Xosc0) Xosc0, + #[hal_cfg("xosc1")] /// The DPLL is driven by [`Xosc1`](super::xosc::Xosc1) Xosc1, /// The DPLL is driven by [`Xosc32k`](super::xosc32k::Xosc32k) Xosc32k, } +#[hal_cfg("oscctrl")] impl From for Refclkselect { fn from(source: DynDpllSourceId) -> Self { match source { @@ -499,6 +553,16 @@ impl From for Refclkselect { } } +#[hal_cfg("sysctrl")] +impl From for Refclkselect { + fn from(source: DynDpllSourceId) -> Self { + match source { + DynDpllSourceId::Pclk => Refclkselect::Gclk, + DynDpllSourceId::Xosc0 => Refclkselect::Ref1, + DynDpllSourceId::Xosc32k => Refclkselect::Ref0, + } + } +} //============================================================================== // DpllSourceId //============================================================================== @@ -533,10 +597,12 @@ impl DpllSourceId for Xosc0Id { const DYN: DynDpllSourceId = DynDpllSourceId::Xosc0; type Reference = settings::Xosc; } +#[hal_cfg("xosc1")] impl DpllSourceId for Xosc1Id { const DYN: DynDpllSourceId = DynDpllSourceId::Xosc1; type Reference = settings::Xosc; } +#[hal_cfg("xosc32k")] impl DpllSourceId for Xosc32kId { const DYN: DynDpllSourceId = DynDpllSourceId::Xosc32k; type Reference = settings::Xosc32k; @@ -546,6 +612,12 @@ impl DpllSourceId for Xosc32kId { // Settings //============================================================================== +/// Fractional divider for the `LDRFRAC` field +#[hal_cfg("sysctrl")] +pub const FRAC_DIV: u8 = 16; +#[hal_cfg("oscctrl")] +pub const FRAC_DIV: u8 = 32; + /// [`Dpll`] Proportional Integral Filter /// /// Filter settings affect PLL stability and jitter. The datasheet suggests a @@ -610,6 +682,7 @@ pub enum DcoFilter { /// [`Dpll`] settings relevant to all reference clocks #[derive(Copy, Clone)] +#[hal_macro_helper] struct Settings { mult: u16, frac: u8, @@ -618,13 +691,16 @@ struct Settings { on_demand: bool, run_standby: bool, filter: PiFilter, + #[hal_cfg("oscctrl")] dco_filter: Option, } /// Store and retrieve [`Dpll`] settings for different reference clocks mod settings { use super::super::pclk; + #[hal_cfg("xosc32k")] use super::RateExtU32; + use super::hal_cfg; use super::{DpllId, GclkId, Hertz}; /// [`Dpll`] settings when referenced to a [`Pclk`] @@ -648,6 +724,7 @@ mod settings { /// /// [`Dpll`]: super::Dpll /// [`Xosc32k`]: super::super::xosc32k::Xosc32k + #[hal_cfg("xosc32k")] pub struct Xosc32k; /// Generic interface for the frequency and predivider of a reference clock @@ -678,6 +755,7 @@ mod settings { } } + #[hal_cfg("xosc32k")] impl Reference for Xosc32k { #[inline] fn freq(&self) -> Hertz { @@ -699,12 +777,12 @@ mod settings { /// A DPLL is used to multiply clock frequencies, taking a lower-frequency input /// clock and producing a higher-frequency output clock. /// -/// The type parameter `D` is a [`DpllId`] that determines which of the two -/// instances this `Dpll` represents ([`Dpll0`] or [`Dpll1`]). The type -/// parameter `I` represents the `Id` type for the clock [`Source`] driving this -/// `Dpll`. It must be one of the valid [`DpllSourceId`]s. See the -/// [`clock` module documentation](super) for more detail on -/// [`Id` types](super#id-types). +/// The type parameter `D` is a [`DpllId`] that determines which of the one or +/// two (depending on the target) instances this `Dpll` represents ([`Dpll0`] or +/// `Dpll1`). The type parameter `I` represents the `Id` type for the clock +/// [`Source`] driving this `Dpll`. It must be one of the valid +/// [`DpllSourceId`]s. See the [`clock` module documentation](super) for more +/// detail on [`Id` types](super#id-types). /// /// On its own, an instance of `Dpll` does not represent an enabled DPLL. /// Instead, it must first be wrapped with [`Enabled`], which implements @@ -733,6 +811,7 @@ where pub type Dpll0 = Dpll; /// Type alias for the corresponding [`Dpll`] +#[hal_cfg("oscctrl")] pub type Dpll1 = Dpll; impl Dpll @@ -740,6 +819,7 @@ where D: DpllId, I: DpllSourceId, { + #[hal_macro_helper] fn new(token: DpllToken, reference: I::Reference) -> Self { let settings = Settings { mult: 1, @@ -749,6 +829,7 @@ where on_demand: true, run_standby: false, filter: PiFilter::Bw92p7kHzDf0p76, + #[hal_cfg("oscctrl")] dco_filter: None, }; Self { @@ -859,6 +940,7 @@ where } } +#[hal_cfg("xosc32k")] impl Dpll { /// Create a [`Dpll`] from an [`Xosc32k`] /// @@ -908,7 +990,7 @@ where /// parts of the division factor, i.e. the division factor is: /// /// ```text - /// int + frac / 32 + /// int + frac / FRAC_DIV /// ``` /// /// This function will confirm that the `int` and `frac` values convert to @@ -918,7 +1000,7 @@ where if int < 1 || int > 0x2000 { panic!("Invalid integer part of the DPLL loop divider") } - if frac > 31 { + if frac > FRAC_DIV - 1 { panic!("Invalid fractional part of the DPLL loop divider") } self.settings.mult = int; @@ -959,6 +1041,7 @@ where /// Enable sigma-delta DAC low pass filter #[inline] + #[hal_cfg("oscctrl")] pub fn dco_filter(mut self, capacitor: DcoFilter) -> Self { self.settings.dco_filter = Some(capacitor); self @@ -991,15 +1074,15 @@ where #[inline] fn output_freq(&self) -> Hertz { // The actual formula is: - // y = x * (mult + frac / 32) + // y = x * (mult + frac / FRAC_DIV) // To avoid integer precision loss, the formula is restructured: - // y = x * (mult + frac / 32) * 32 / 32 - // y = x * (32 * mult + 32 * frac / 32) / 32 - // y = x * (32 * mult + frac) / 32 + // y = x * (mult + frac / FRAC_DIV) * FRAC_DIV / FRAC_DIV + // y = x * (FRAC_DIV * mult + FRAC_DIV * frac / FRAC_DIV) / FRAC_DIV + // y = x * (FRAC_DIV * mult + frac) / FRAC_DIV let input = self.input_freq().to_Hz() as u64; - let multiplier_times_32 = - (32 * self.settings.mult as u32 + self.settings.frac as u32) as u64; - let output = (input * multiplier_times_32 / 32) as u32; + let multiplier_scaled = + (FRAC_DIV as u32 * self.settings.mult as u32 + self.settings.frac as u32) as u64; + let output = (input * multiplier_scaled / FRAC_DIV as u64) as u32; output.Hz() } @@ -1028,12 +1111,29 @@ where /// frequency is invalid, this call will panic. #[inline] pub fn enable(self) -> EnabledDpll { + const INPUT_MIN: u32 = 32_000; + + #[hal_cfg("sysctrl")] + const INPUT_MAX: u32 = 2_000_000; + #[hal_cfg("oscctrl")] + const INPUT_MAX: u32 = 3_200_000; + + #[hal_cfg("sysctrl")] + const OUTPUT_MIN: u32 = 48_000_000; + #[hal_cfg("oscctrl")] + const OUTPUT_MIN: u32 = 96_000_000; + + #[hal_cfg("sysctrl")] + const OUTPUT_MAX: u32 = 96_000_000; + #[hal_cfg("oscctrl")] + const OUTPUT_MAX: u32 = 200_000_000; + let input_freq = self.input_freq().to_Hz(); let output_freq = self.output_freq().to_Hz(); - if input_freq < 32_000 || input_freq > 3_200_000 { + if input_freq < INPUT_MIN || input_freq > INPUT_MAX { panic!("Invalid DPLL input frequency"); } - if output_freq < 96_000_000 || output_freq > 200_000_000 { + if output_freq < OUTPUT_MIN || output_freq > OUTPUT_MAX { panic!("Invalid DPLL output frequency"); } self.enable_unchecked() @@ -1049,8 +1149,7 @@ where pub fn enable_unchecked(mut self) -> EnabledDpll { use settings::Reference; let prediv = self.reference.prediv(); - self.token.configure(I::DYN, self.settings, prediv); - self.token.enable(); + self.token.enable(I::DYN, self.settings, prediv); Enabled::new(self) } } @@ -1074,6 +1173,7 @@ pub type EnabledDpll = Enabled, N>; pub type EnabledDpll0 = EnabledDpll; /// Type alias for the corresponding [`EnabledDpll`] +#[hal_cfg("oscctrl")] pub type EnabledDpll1 = EnabledDpll; impl EnabledDpll diff --git a/hal/src/peripherals/clock/d5x/v2/gclk.rs b/hal/src/clock/v2/gclk.rs similarity index 71% rename from hal/src/peripherals/clock/d5x/v2/gclk.rs rename to hal/src/clock/v2/gclk.rs index 18a28eddf99e..af07992cd9b6 100644 --- a/hal/src/peripherals/clock/d5x/v2/gclk.rs +++ b/hal/src/clock/v2/gclk.rs @@ -83,10 +83,10 @@ //! //! At this point, notice that [`Gclk`] takes two type parameters. `G` is //! a [`GclkId`] identifying which of the 12 generators this `Gclk` represents. -//! [`Gclk1`] is simply an alias for `Gclk`. `I` is an -//! [`Id` type](super#id-types) identifying the input clock, which must be a -//! valid [`GclkSourceId`]. In this case, `I` is [`PB14`](gpio::PB14), which is -//! a `GclkSourceId` for `Gclk1`, because it implements [`GclkIo`] with +//! [`Gclk1`] is simply an alias for `Gclk`. `I` is an [`Id` +//! type](super#id-types) identifying the input clock, which must be a valid +//! [`GclkSourceId`]. In this case, `I` is `PB14`, which is a `GclkSourceId` for +//! `Gclk1` on this target, because it implements [`GclkIo`] with //! [`GclkIo::GclkId`]` = Gclk1Id`. //! //! ```no_run @@ -335,27 +335,34 @@ //! [`Pins`]: crate::gpio::Pins //! [`Sercom0`]: crate::sercom::Sercom0 +use atsamd_hal_macros::{hal_cfg, hal_macro_helper}; use core::cmp::max; use core::marker::PhantomData; use paste::paste; -use seq_macro::seq; use typenum::{U0, U1}; use crate::pac; -use crate::pac::Nvmctrl; -use crate::pac::gclk::genctrl::Divselselect; -use crate::gpio::{self, AlternateM, AnyPin, Pin, PinId}; +use crate::gpio::{self, AlternateH, AnyPin, Pin, PinId}; use crate::pac::gclk::Genctrl; +#[hal_cfg(any("clock-d11", "clock-d21"))] +use crate::pac::gclk::Gendiv; use crate::pac::gclk::genctrl::Srcselect; use crate::time::Hertz; use crate::typelevel::{Decrement, Increment, PrivateDecrement, PrivateIncrement, Sealed}; use super::dfll::DfllId; -use super::dpll::{Dpll0Id, Dpll1Id}; +use super::dpll::Dpll0Id; +#[hal_cfg("oscctrl")] +use super::dpll::Dpll1Id; +#[hal_cfg(any("clock-d11", "clock-d21"))] +use super::osc::OscId; use super::osculp32k::OscUlp32kId; -use super::xosc::{Xosc0Id, Xosc1Id}; +use super::xosc::Xosc0Id; +#[hal_cfg("xosc1")] +use super::xosc::Xosc1Id; +#[hal_cfg("xosc32k")] use super::xosc32k::Xosc32kId; use super::{Enabled, Source}; @@ -369,12 +376,13 @@ use super::{Enabled, Source}; /// various `Token` types can be exchanged for actual clock types. They /// typically represent clocks that are disabled at power-on reset. /// -/// [`GclkToken`]s are no different. All [`Gclk`]s other than [`Gclk0`] are -/// disabled at power-on reset. To use a [`Gclk`], you must first exchange the -/// token for an actual clock with [`Gclk::from_source`] or [`Gclk::from_pin`]. +/// [`GclkToken`]s are no different. [`Gclk`]s other than [`Gclk0`], and +/// [`Gclk2`] on SAMD21/SAMD11, are disabled at power-on reset. To use a +/// [`Gclk`], you must first exchange the token for an actual clock with +/// [`Gclk::from_source`] or [`Gclk::from_pin`]. /// /// [`GclkToken`] is generic over the [`GclkId`], where each corresponding token -/// represents one of the 12 respective [`Gclk`]s. +/// represents one of the [`Gclk`]s. pub struct GclkToken { generator: PhantomData, } @@ -395,16 +403,31 @@ impl GclkToken { } /// SYNCBUSY register mask for the corresponding GCLK + #[hal_cfg("clock-d5x")] const MASK: u16 = 1 << G::NUM; /// Provide a reference to the corresponding [`Genctrl`] register #[inline] + #[hal_macro_helper] fn genctrl(&self) -> &Genctrl { // Safety: Each `GclkToken` only has access to a mutually exclusive set // of registers for the corresponding `GclkId`, and we use a shared // reference to the register block. See the notes on `Token` types and // memory safety in the root of the `clock` module for more details. - unsafe { (*pac::Gclk::PTR).genctrl(G::NUM) } + #[hal_cfg("clock-d5x")] + unsafe { + (*pac::Gclk::PTR).genctrl(G::NUM) + } + #[hal_cfg(any("clock-d11", "clock-d21"))] + unsafe { + (*pac::Gclk::PTR).genctrl() + } + } + + #[hal_cfg(any("clock-d11", "clock-d21"))] + #[inline] + fn gendiv(&self) -> &Gendiv { + unsafe { (*pac::Gclk::PTR).gendiv() } } /// Block until synchronization has completed @@ -412,97 +435,21 @@ impl GclkToken { /// Reads or writes to synchronized fields must be accompanied by a check of /// the `SYNCBUSY` register. See the datasheet for more details. #[inline] + #[hal_macro_helper] fn wait_syncbusy(&self) { // Safety: We are only reading from the `SYNCBUSY` register, and we are // only observing the bit corresponding to this particular `GclkId`, so // there is no risk of memory corruption. - let syncbusy = unsafe { &(*pac::Gclk::PTR).syncbusy() }; - while syncbusy.read().genctrl().bits() & Self::MASK != 0 {} - } - - /// Set the clock source for this [`Gclk`] - #[inline] - fn set_source(&mut self, source: DynGclkSourceId) { - self.genctrl().modify(|_, w| w.src().variant(source.into())); - self.wait_syncbusy(); - } - - /// Set the [`GclkDivider`] value - /// - /// Use the internal interface of [`GclkDivider`] to set the `DIV` and - /// `DIVSEL` fields of the `GENCTRL` register. - #[inline] - fn set_div(&mut self, div: G::Divider) { - let (divsel, div) = div.divsel_div(); - // Safety: The `DIVSEL` and `DIV` values are derived from the - // `GclkDivider` type, so they are guaranteed to be valid. - self.genctrl().modify(|_, w| unsafe { - w.divsel().variant(divsel); - w.div().bits(div) - }); - self.wait_syncbusy(); - } - - /// Output a 50-50 duty-cycle clock when using an odd division factor - #[inline] - fn improve_duty_cycle(&mut self, flag: bool) { - self.genctrl().modify(|_, w| w.idc().bit(flag)); - self.wait_syncbusy(); - } - - /// Set the state of [`GclkOut`] pins when the GCLK_IO output is disabled - #[inline] - fn output_off_value(&mut self, high: bool) { - self.genctrl().modify(|_, w| w.oov().bit(high)); - self.wait_syncbusy(); - } - - /// Enable [`Gclk`] output to a GPIO [`Pin`] - #[inline] - fn enable_gclk_out(&mut self) { - self.genctrl().modify(|_, w| w.oe().set_bit()); - self.wait_syncbusy(); - } - - /// Disable [`Gclk`] output on a GPIO [`Pin`] - /// - /// If a corresponding [`Pin`] is in the [`AlternateM`] mode, it's logic - /// level will depend on the [`output_off_value`]. - #[inline] - fn disable_gclk_out(&mut self) { - self.genctrl().modify(|_, w| w.oe().clear_bit()); - self.wait_syncbusy(); - } - - #[inline] - fn configure(&mut self, id: DynGclkSourceId, settings: Settings) { - let (divsel, div) = settings.div.divsel_div(); - self.genctrl().modify(|_, w| { - // Safety: The `DIVSEL` and `DIV` values are derived from the - // `GclkDivider` type, so they are guaranteed to be valid. - unsafe { - w.divsel().variant(divsel); - w.div().bits(div); - }; - w.src().variant(id.into()); - w.idc().bit(settings.improve_duty_cycle); - w.oov().bit(settings.output_off_value) - }); - self.wait_syncbusy(); - } - - /// Enable the [`Gclk`] - #[inline] - fn enable(&mut self) { - self.genctrl().modify(|_, w| w.genen().set_bit()); - self.wait_syncbusy(); - } - - /// Disable the [`Gclk`] - #[inline] - fn disable(&mut self) { - self.genctrl().modify(|_, w| w.genen().clear_bit()); - self.wait_syncbusy(); + #[hal_cfg("clock-d5x")] + { + let syncbusy = unsafe { &(*pac::Gclk::PTR).syncbusy() }; + while syncbusy.read().genctrl().bits() & Self::MASK != 0 {} + } + #[hal_cfg(any("clock-d11", "clock-d21"))] + { + let status = unsafe { &(*pac::Gclk::PTR).status() }; + while status.read().syncbusy().bit() {} + } } } @@ -510,12 +457,13 @@ impl GclkToken { // DynGclkId //============================================================================== -/// Value-level enum identifying one of 12 possible [`Gclk`]s +/// Value-level enum identifying one of the possible [`Gclk`]s /// -/// The variants of this enum identify one of the 12 possible generic clock -/// generators. +/// The variants of this enum identify one generic clock generator. /// /// `DynGclkId` is the value-level equivalent of [`GclkId`]. +#[derive(Clone, Copy, PartialEq, Eq)] +#[hal_macro_helper] pub enum DynGclkId { Gclk0, Gclk1, @@ -523,27 +471,59 @@ pub enum DynGclkId { Gclk3, Gclk4, Gclk5, + #[hal_cfg("gclk6")] Gclk6, + #[hal_cfg("gclk7")] Gclk7, + #[hal_cfg("gclk8")] Gclk8, + #[hal_cfg("gclk9")] Gclk9, + #[hal_cfg("gclk10")] Gclk10, + #[hal_cfg("gclk11")] Gclk11, } +impl core::fmt::Debug for DynGclkId { + #[hal_macro_helper] + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + Self::Gclk0 => write!(f, "Gclk0"), + Self::Gclk1 => write!(f, "Gclk1"), + Self::Gclk2 => write!(f, "Gclk2"), + Self::Gclk3 => write!(f, "Gclk3"), + Self::Gclk4 => write!(f, "Gclk4"), + Self::Gclk5 => write!(f, "Gclk5"), + #[hal_cfg("gclk6")] + Self::Gclk6 => write!(f, "Gclk6"), + #[hal_cfg("gclk7")] + Self::Gclk7 => write!(f, "Gclk7"), + #[hal_cfg("gclk8")] + Self::Gclk8 => write!(f, "Gclk8"), + #[hal_cfg("gclk9")] + Self::Gclk9 => write!(f, "Gclk9"), + #[hal_cfg("gclk10")] + Self::Gclk10 => write!(f, "Gclk10"), + #[hal_cfg("gclk11")] + Self::Gclk11 => write!(f, "Gclk11"), + } + } +} + //============================================================================== // GclkId //============================================================================== -/// Type-level enum identifying one of 12 possible [`Gclk`]s +/// Type-level enum identifying one of possible [`Gclk`]s /// -/// The types implementing this trait, i.e. [`Gclk0Id`] - [`Gclk11Id`], are -/// type-level variants of `GclkId`, and they identify one of the 12 possible +/// The types implementing this trait, i.e. [`Gclk0Id`] - `Gclk11Id`, are +/// type-level variants of `GclkId`, and they identify one of the possible /// generic clock generators. /// /// `GclkId` is the type-level equivalent of [`DynGclkId`]. See the -/// documentation on [type-level programming] and specifically -/// [type-level enums] for more details. +/// documentation on [type-level programming] and specifically [type-level +/// enums] for more details. /// /// [type-level programming]: crate::typelevel /// [type-level enums]: crate::typelevel#type-level-enums @@ -589,25 +569,43 @@ impl GclkId for Gclk1Id { type Divider = GclkDiv16; } -seq!(N in 2..=11 { - paste! { - /// Type-level variant of [`GclkId`] representing the identity of - #[doc = "GCLK" N] - /// - /// See the documentation on [type-level programming] and specifically - /// [type-level enums] for more details. - /// - /// [type-level programming]: crate::typelevel - /// [type-level enums]: crate::typelevel#type-level-enums - pub enum [] {} - impl Sealed for [] {} - impl GclkId for [] { - const DYN: DynGclkId = DynGclkId::Gclk~N; - const NUM: usize = N; - type Divider = GclkDiv8; +macro_rules! make_gclk_id { + ($num:literal) => { + paste! { + #[doc = GCLK $num] + /// + /// See the documentation on [type-level programming] and specifically + /// [type-level enums] for more details. + /// + /// [type-level programming]: crate::typelevel + /// [type-level enums]: crate::typelevel#type-level-enums + pub enum [] {} + impl Sealed for [] {} + impl GclkId for [] { + const DYN: DynGclkId = DynGclkId::[]; + const NUM: usize = $num; + type Divider = GclkDiv8; + } } - } -}); + }; +} + +make_gclk_id!(2); +make_gclk_id!(3); +make_gclk_id!(4); +make_gclk_id!(5); +#[hal_cfg("gclk6")] +make_gclk_id!(6); +#[hal_cfg("gclk7")] +make_gclk_id!(7); +#[hal_cfg("gclk8")] +make_gclk_id!(8); +#[hal_cfg("gclk9")] +make_gclk_id!(9); +#[hal_cfg("gclk10")] +make_gclk_id!(10); +#[hal_cfg("gclk11")] +make_gclk_id!(11); //============================================================================== // GclkDivider @@ -631,7 +629,7 @@ pub trait GclkDivider: Sealed + Default + Copy { /// Returns the actual clock divider value as a `u32` fn divider(&self) -> u32; /// Return the corresponding `DIVSEL` and and `DIV` register fields - fn divsel_div(&self) -> (Divselselect, u16); + fn divsel_div(&self) -> (bool, u16); } //============================================================================== @@ -678,11 +676,11 @@ impl GclkDivider for GclkDiv8 { } #[inline] - fn divsel_div(&self) -> (Divselselect, u16) { + fn divsel_div(&self) -> (bool, u16) { match self { - GclkDiv8::Div(div) => (Divselselect::Div1, (*div).into()), - GclkDiv8::Div2Pow8 => (Divselselect::Div2, 7), - GclkDiv8::Div2Pow9 => (Divselselect::Div2, 8), + GclkDiv8::Div(div) => (false, (*div).into()), + GclkDiv8::Div2Pow8 => (true, 7), + GclkDiv8::Div2Pow9 => (true, 8), } } } @@ -730,11 +728,11 @@ impl GclkDivider for GclkDiv16 { } #[inline] - fn divsel_div(&self) -> (Divselselect, u16) { + fn divsel_div(&self) -> (bool, u16) { match self { - GclkDiv16::Div(div) => (Divselselect::Div1, *div), - GclkDiv16::Div2Pow16 => (Divselselect::Div2, 15), - GclkDiv16::Div2Pow17 => (Divselselect::Div2, 16), + GclkDiv16::Div(div) => (false, *div), + GclkDiv16::Div2Pow16 => (true, 15), + GclkDiv16::Div2Pow17 => (true, 16), } } } @@ -756,6 +754,7 @@ pub trait GclkIo: PinId { // These implementations are much easier to read with `#[rustfmt::skip]` #[rustfmt::skip] +#[hal_cfg("clock-d5x")] mod gclkio_impl { use atsamd_hal_macros::hal_cfg; @@ -807,6 +806,80 @@ mod gclkio_impl { impl GclkIo for gpio::PB23 { type GclkId = Gclk1Id; } } +#[rustfmt::skip] +#[hal_cfg("clock-d21")] +mod gclkio_impl { + use super::*; + + impl GclkIo for gpio::PA10 { type GclkId = Gclk4Id; } + impl GclkIo for gpio::PA11 { type GclkId = Gclk5Id; } + impl GclkIo for gpio::PA14 { type GclkId = Gclk0Id; } + impl GclkIo for gpio::PA15 { type GclkId = Gclk1Id; } + impl GclkIo for gpio::PA16 { type GclkId = Gclk2Id; } + impl GclkIo for gpio::PA17 { type GclkId = Gclk3Id; } + #[hal_cfg("pa20")] + impl GclkIo for gpio::PA20 { type GclkId = Gclk4Id; } + #[hal_cfg("pa21")] + impl GclkIo for gpio::PA21 { type GclkId = Gclk5Id; } + impl GclkIo for gpio::PA22 { type GclkId = Gclk6Id; } + impl GclkIo for gpio::PA23 { type GclkId = Gclk7Id; } + #[hal_cfg("pa27")] + impl GclkIo for gpio::PA27 { type GclkId = Gclk0Id; } + #[hal_cfg("pa28")] + impl GclkIo for gpio::PA28 { type GclkId = Gclk0Id; } + impl GclkIo for gpio::PA30 { type GclkId = Gclk0Id; } + + #[hal_cfg("pb10")] + impl GclkIo for gpio::PB10 { type GclkId = Gclk4Id; } + #[hal_cfg("pb11")] + impl GclkIo for gpio::PB11 { type GclkId = Gclk5Id; } + #[hal_cfg("pb12")] + impl GclkIo for gpio::PB12 { type GclkId = Gclk6Id; } + #[hal_cfg("pb13")] + impl GclkIo for gpio::PB13 { type GclkId = Gclk7Id; } + #[hal_cfg("pb14")] + impl GclkIo for gpio::PB14 { type GclkId = Gclk0Id; } + #[hal_cfg("pb15")] + impl GclkIo for gpio::PB15 { type GclkId = Gclk1Id; } + #[hal_cfg("pb16")] + impl GclkIo for gpio::PB16 { type GclkId = Gclk2Id; } + #[hal_cfg("pb17")] + impl GclkIo for gpio::PB17 { type GclkId = Gclk3Id; } + #[hal_cfg("pb22")] + impl GclkIo for gpio::PB22 { type GclkId = Gclk0Id; } + #[hal_cfg("pb23")] + impl GclkIo for gpio::PB23 { type GclkId = Gclk1Id; } +} + +#[rustfmt::skip] +#[hal_cfg("clock-d11")] +mod gclkio_impl { + use super::*; + + impl GclkIo for gpio::PA08 { type GclkId = Gclk0Id; } + impl GclkIo for gpio::PA09 { type GclkId = Gclk1Id; } + #[hal_cfg("pa10")] + impl GclkIo for gpio::PA10 { type GclkId = Gclk4Id; } + #[hal_cfg("pa11")] + impl GclkIo for gpio::PA11 { type GclkId = Gclk5Id; } + impl GclkIo for gpio::PA14 { type GclkId = Gclk4Id; } + impl GclkIo for gpio::PA15 { type GclkId = Gclk5Id; } + #[hal_cfg("pa16")] + impl GclkIo for gpio::PA16 { type GclkId = Gclk2Id; } + #[hal_cfg("pa17")] + impl GclkIo for gpio::PA17 { type GclkId = Gclk3Id; } + #[hal_cfg("pa22")] + impl GclkIo for gpio::PA22 { type GclkId = Gclk1Id; } + #[hal_cfg("pa23")] + impl GclkIo for gpio::PA23 { type GclkId = Gclk2Id; } + impl GclkIo for gpio::PA24 { type GclkId = Gclk0Id; } + impl GclkIo for gpio::PA25 { type GclkId = Gclk0Id; } + #[hal_cfg("pa27")] + impl GclkIo for gpio::PA27 { type GclkId = Gclk0Id; } + impl GclkIo for gpio::PA30 { type GclkId = Gclk0Id; } + impl GclkIo for gpio::PA31 { type GclkId = Gclk0Id; } +} + //============================================================================== // Gclk0Io //============================================================================== @@ -815,13 +888,13 @@ mod gclkio_impl { /// /// This is effectively a trait alias for [`PinId`]s that implement [`GclkIo`] /// with a `GclkId` associated type of [`Gclk0Id`], i.e. -/// `GclkIo`. The trait is useful to simply some function +/// `GclkIo`. The trait is useful to simplify some function /// signatures and to help type inference in a few cases. pub trait Gclk0Io where Self: Sized, Self: GclkIo, - Self: GclkSourceId>, + Self: GclkSourceId>, { } @@ -837,32 +910,54 @@ impl> Gclk0Io for I {} /// a given [`Gclk`]. /// /// `DynGclkSourceId` is the value-level equivalent of [`GclkSourceId`]. +#[hal_macro_helper] #[derive(Copy, Clone, PartialEq, Eq)] pub enum DynGclkSourceId { Dfll, Dpll0, + #[hal_cfg("clock-d5x")] Dpll1, Gclk1, GclkIn, + #[hal_cfg(any("clock-d11", "clock-d21"))] + Osc8M, + #[hal_cfg(any("clock-d11", "clock-d21"))] + Osc32k, OscUlp32k, Xosc0, + #[hal_cfg("clock-d5x")] Xosc1, Xosc32k, } impl From for Srcselect { + #[hal_cfg("clock-d5x")] + fn from(source: DynGclkSourceId) -> Self { + match source { + DynGclkSourceId::Dfll => Srcselect::Dfll, + DynGclkSourceId::Dpll0 => Srcselect::Dpll0, + DynGclkSourceId::Dpll1 => Srcselect::Dpll1, + DynGclkSourceId::Gclk1 => Srcselect::Gclkgen1, + DynGclkSourceId::GclkIn => Srcselect::Gclkin, + DynGclkSourceId::OscUlp32k => Srcselect::Osculp32k, + DynGclkSourceId::Xosc0 => Srcselect::Xosc0, + DynGclkSourceId::Xosc1 => Srcselect::Xosc1, + DynGclkSourceId::Xosc32k => Srcselect::Xosc32k, + } + } + + #[hal_cfg(any("clock-d11", "clock-d21"))] fn from(source: DynGclkSourceId) -> Self { - use DynGclkSourceId::*; match source { - Dfll => Self::Dfll, - Dpll0 => Self::Dpll0, - Dpll1 => Self::Dpll1, - Gclk1 => Self::Gclkgen1, - GclkIn => Self::Gclkin, - OscUlp32k => Self::Osculp32k, - Xosc0 => Self::Xosc0, - Xosc1 => Self::Xosc1, - Xosc32k => Self::Xosc32k, + DynGclkSourceId::Dfll => Srcselect::Dfll48m, + DynGclkSourceId::Dpll0 => Srcselect::Dpll96m, + DynGclkSourceId::Gclk1 => Srcselect::Gclkgen1, + DynGclkSourceId::GclkIn => Srcselect::Gclkin, + DynGclkSourceId::Osc8M => Srcselect::Osc8m, + DynGclkSourceId::Osc32k => Srcselect::Osc32k, + DynGclkSourceId::OscUlp32k => Srcselect::Osculp32k, + DynGclkSourceId::Xosc0 => Srcselect::Xosc, + DynGclkSourceId::Xosc32k => Srcselect::Xosc32k, } } } @@ -904,6 +999,7 @@ impl GclkSourceId for Dpll0Id { const DYN: DynGclkSourceId = DynGclkSourceId::Dpll0; type Resource = (); } +#[hal_cfg("oscctrl")] impl GclkSourceId for Dpll1Id { const DYN: DynGclkSourceId = DynGclkSourceId::Dpll1; type Resource = (); @@ -914,20 +1010,27 @@ impl GclkSourceId for Gclk1Id { } impl GclkSourceId for I { const DYN: DynGclkSourceId = DynGclkSourceId::GclkIn; - type Resource = Pin; + type Resource = Pin; } impl GclkSourceId for OscUlp32kId { const DYN: DynGclkSourceId = DynGclkSourceId::OscUlp32k; type Resource = (); } +#[hal_cfg(any("clock-d11", "clock-d21"))] +impl GclkSourceId for OscId { + const DYN: DynGclkSourceId = DynGclkSourceId::Osc8M; + type Resource = (); +} impl GclkSourceId for Xosc0Id { const DYN: DynGclkSourceId = DynGclkSourceId::Xosc0; type Resource = (); } +#[hal_cfg("xosc1")] impl GclkSourceId for Xosc1Id { const DYN: DynGclkSourceId = DynGclkSourceId::Xosc1; type Resource = (); } +#[hal_cfg("xosc32k")] impl GclkSourceId for Xosc32kId { const DYN: DynGclkSourceId = DynGclkSourceId::Xosc32k; type Resource = (); @@ -955,11 +1058,16 @@ impl> NotGclkIo for I {} // Settings //============================================================================== -/// Collection of [`Gclk`] settings to configure on enable +/// Collection of [`Gclk`] settings +/// +/// This structure is largely required due to the thumbv6m chips sharing one +/// GENCTRL register among all the clock generators. On these chips, all fields +/// in the register need to be updated by a 32b atomic write. struct Settings { div: G::Divider, output_off_value: bool, improve_duty_cycle: bool, + output_enable: bool, } impl Clone for Settings { @@ -976,6 +1084,7 @@ impl Default for Settings { div: G::Divider::default(), output_off_value: false, improve_duty_cycle: false, + output_enable: false, } } } @@ -990,13 +1099,13 @@ impl Default for Settings { /// a root or branch clock to other branch or leaf clocks. In particular, all /// peripheral [`Pclk`]s must be derived from a `Gclk`. /// -/// The type parameter `G` is a [`GclkId`] that determines which of the 12 -/// generators this [`Gclk`] represents ([`Gclk0`] - [`Gclk11`]). The type -/// parameter `I` represents the `Id` type for the clock [`Source`] driving this -/// `Gclk`. It must be one of the valid [`GclkSourceId`]s. Alternatively, if the -/// `Gclk` is driven by a [GPIO](gpio) [`Pin`], then `I` is a [`PinId`] -/// implementing [`GclkIo`]. See the [`clock` module documentation](super) for -/// more detail on `Id` types. +/// The type parameter `G` is a [`GclkId`] that determines which of the +/// generators this [`Gclk`] represents ([`Gclk0`] - `Gclk11` on the largest +/// targets). The type parameter `I` represents the `Id` type for the clock +/// [`Source`] driving this `Gclk`. It must be one of the valid +/// [`GclkSourceId`]s. Alternatively, if the `Gclk` is driven by a [GPIO](gpio) +/// [`Pin`], then `I` is a [`PinId`] implementing [`GclkIo`]. See the [`clock` +/// module documentation](super) for more detail on `Id` types. /// /// On its own, an instance of `Gclk` does not represent an enabled clock /// generator. Instead, it must first be wrapped with [`Enabled`], which @@ -1054,15 +1163,35 @@ pub type Gclk0 = Gclk; /// on [`EnabledGclk0`] to configure the `Gclk` while it is actively running. pub type EnabledGclk0 = EnabledGclk; -seq!(G in 1..=11 { - paste! { - /// Type alias for the corresponding [`Gclk`] - pub type Gclk~G = Gclk<[], I>; +macro_rules! make_gclk { + ($num:literal) => { + paste! { + /// Type alias for the corresponding [`Gclk`] + pub type [] = Gclk<[], I>; - /// Type alias for the corresponding [`EnabledGclk`] - pub type EnabledGclk~G = EnabledGclk<[], I, N>; - } -}); + /// Type alias for the corresponding [`EnabledGclk`] + pub type [] = EnabledGclk<[], I, N>; + } + }; +} + +make_gclk!(1); +make_gclk!(2); +make_gclk!(3); +make_gclk!(4); +make_gclk!(5); +#[hal_cfg("gclk6")] +make_gclk!(6); +#[hal_cfg("gclk7")] +make_gclk!(7); +#[hal_cfg("gclk8")] +make_gclk!(8); +#[hal_cfg("gclk9")] +make_gclk!(9); +#[hal_cfg("gclk10")] +make_gclk!(10); +#[hal_cfg("gclk11")] +make_gclk!(11); impl Gclk where @@ -1098,7 +1227,7 @@ where /// /// Freeing a [`Gclk`] returns the corresponding [`GclkToken`] and GPIO /// [`Pin`]. - pub fn free_pin(self) -> (GclkToken, Pin) { + pub fn free_pin(self) -> (GclkToken, Pin) { (self.token, self.resource) } } @@ -1153,21 +1282,72 @@ where G: GclkId, I: GclkSourceId, { + /// Updates the GENCTRL register based on self.settings + /// + /// The thumbv7em chips use one GENCTRL per clock generator and are capable + /// of read-modify-write operations, but the thumbv6 chips share one GENCTRL + /// register among the clock generators so only support writes. To + /// accommodate both, this implementation maintains a [`Settings`] struct + /// containing the GENCTRL settings, and this method is called after + /// updating it to apply them. + #[hal_macro_helper] + fn update_genctrl(&self, genen: bool) { + let (divsel, div) = self.settings.div.divsel_div(); + + #[hal_cfg("clock-d5x")] + self.token.genctrl().modify(|_, w| { + w.divsel().bit(divsel); + // Safety: The `DIVSEL` and `DIV` values are derived from the + // `GclkDivider` type, so they are guaranteed to be valid. + unsafe { + w.div().bits(div); + } + w.oe().bit(self.settings.output_enable); + w.oov().bit(self.settings.output_off_value); + w.idc().bit(self.settings.improve_duty_cycle); + w.genen().bit(genen); + w.src().variant(I::DYN.into()) + }); + + #[hal_cfg(any("clock-d11", "clock-d21"))] + { + // Suppress warning for thumbv7em builds + let _ = div; + + self.token.genctrl().write(|w| { + w.divsel().bit(divsel); + w.oe().bit(self.settings.output_enable); + w.oov().bit(self.settings.output_off_value); + w.idc().bit(self.settings.improve_duty_cycle); + w.genen().bit(genen); + w.src().variant(I::DYN.into()); + unsafe { w.id().bits(G::NUM as u8) } + }); + } + + self.token.wait_syncbusy(); + } + /// Modify the source of an existing clock /// /// This is a helper function for swapping Gclk0 to different clock sources. fn change_source( - mut self, + self, resource: N::Resource, freq: Hertz, ) -> (Gclk, I::Resource) { - self.token.set_source(N::DYN); let gclk = Gclk { token: self.token, resource, src_freq: freq, settings: self.settings, }; + + // Call update_genctrl() on object that has the correct source type + gclk.update_genctrl( + // This method is always called on Gclk0, which is always enabled + true, + ); (gclk, self.resource) } @@ -1212,33 +1392,43 @@ where /// When calling this function, the new OOV will take effect immediately. /// /// However, remember that the `Pin` is not controlled by the `Gclk` unless - /// the `Pin` is configured in [`AlternateM`] mode. `Pin`s are automatically - /// set to `AlternateM` mode when calling [`enable_gclk_out`], but by that + /// the `Pin` is configured in [`AlternateH`] mode. `Pin`s are automatically + /// set to `AlternateH` mode when calling [`enable_gclk_out`], but by that /// point, the OOV is irrelevant. If you need the `Pin` to be set to its - /// OOV, you must *manually* set it to `AlternateM` mode before constructing + /// OOV, you must *manually* set it to `AlternateH` mode before constructing /// the `GclkOut`. /// /// [`enable_gclk_out`]: EnabledGclk::enable_gclk_out #[inline] pub fn output_off_value(mut self, high: bool) -> Self { self.settings.output_off_value = high; - self.token.output_off_value(high); + self.update_genctrl( + // This method is only accessible via disabled GCLKs + false, + ); + self } /// Enable the [`Gclk`], so that it can be used as a clock [`Source`] /// - /// As mentioned in the [`Gclk`] documentation, no hardware registers are - /// actually modified until this call. Rather, the desired configuration is - /// stored internally, and the [`Gclk`] is initialized and configured here - /// according to the datasheet. - /// /// The returned value is an [`EnabledGclk`] that can be used as a clock /// [`Source`] for other clocks. #[inline] - pub fn enable(mut self) -> EnabledGclk { - self.token.configure(I::DYN, self.settings); - self.token.enable(); + #[hal_macro_helper] + pub fn enable(self) -> EnabledGclk { + #[hal_cfg(any("clock-d11", "clock-d21"))] + { + let (_divsel, div) = self.settings.div.divsel_div(); + self.token.gendiv().write(|w| unsafe { + w.id().bits(G::NUM as u8); + w.div().bits(div) + }); + self.token.wait_syncbusy(); + } + + self.update_genctrl(true); + Enabled::new(self) } } @@ -1253,8 +1443,8 @@ where /// This method is only implemented for `N = U0`, which means the clock can /// only be disabled when no other clocks consume this [`Gclk`]. #[inline] - pub fn disable(mut self) -> Gclk { - self.0.token.disable(); + pub fn disable(self) -> Gclk { + self.0.update_genctrl(false); self.0 } } @@ -1276,6 +1466,11 @@ impl EnabledGclk0 { /// Swap [`Gclk0`] from one clock [`Source`] to another /// /// `Gclk0` will remain fully enabled during the swap. + /// + /// Note for thumbv6m chips: Before switching the Generic Clock Generator 0 + /// (GCLKGEN0) from a clock source A to another clock source B, enable the + /// "ONDEMAND" feature of the clock source A to ensure a proper transition + /// from clock source A to clock source B. #[inline] pub fn swap_sources(self, old: O, new: N) -> (EnabledGclk0, O::Dec, N::Inc) where @@ -1291,12 +1486,13 @@ impl EnabledGclk0 { /// Swap [`Gclk0`] from one [`GclkIo`] [`Pin`] to another /// /// `Gclk0` will remain fully enabled during the swap. + /// TODO there's only one input IO pad per... #[inline] pub fn swap_pins

( self, pin: P, freq: impl Into, - ) -> (EnabledGclk0, Pin) + ) -> (EnabledGclk0, Pin) where I: Gclk0Io, P: AnyPin, @@ -1337,7 +1533,7 @@ impl EnabledGclk0 { pub fn swap_pin_for_source( self, source: S, - ) -> (EnabledGclk0, Pin, S::Inc) + ) -> (EnabledGclk0, Pin, S::Inc) where I: Gclk0Io, S: Source + Increment, @@ -1352,16 +1548,31 @@ impl EnabledGclk0 { /// /// See [`Gclk::div`] documentation for more details. #[inline] - pub fn div(&mut self, div: GclkDiv8) { - self.0.settings.div = div; - self.0.token.set_div(div); + #[hal_macro_helper] + pub fn div(&mut self, divider: GclkDiv8) { + self.0.settings.div = divider; + + // D5x div is in the GENCTRL register, smaller chips keep it separate + #[hal_cfg(any("clock-d11", "clock-d21"))] + { + let (_divsel, div) = divider.divsel_div(); + // Safety: The `DIVSEL` and `DIV` values are derived from the + // `GclkDivider` type, so they are guaranteed to be valid. + self.0.token.gendiv().write(|w| unsafe { + w.id().bits(0); + w.div().bits(div) + }); + self.0.token.wait_syncbusy(); + } + + self.0.update_genctrl(true); } /// Output a 50-50 duty cycle clock when using an odd [`GclkDivider`] #[inline] pub fn improve_duty_cycle(&mut self, flag: bool) { self.0.settings.improve_duty_cycle = flag; - self.0.token.improve_duty_cycle(flag); + self.0.update_genctrl(true); } /// Return the [`Gclk0`] frequency @@ -1378,7 +1589,7 @@ impl EnabledGclk0 { #[inline] pub fn output_off_value(&mut self, high: bool) { self.0.settings.output_off_value = high; - self.0.token.output_off_value(high); + self.0.update_genctrl(true); } } @@ -1403,38 +1614,62 @@ where // Tokens //============================================================================== -seq!(N in 1..=11 { - paste! { - /// Set of [`GclkToken`]s representing the disabled [`Gclk`]s at - /// power-on reset - pub struct GclkTokens { - #( - /// [`GclkToken`] for - #[doc = "[`Gclk" N "`]"] - pub gclk~N: GclkToken<[]>, - )* - } +/// Set of [`GclkToken`]s representing the disabled [`Gclk`]s at +/// power-on reset +#[hal_macro_helper] +pub struct GclkTokens { + pub gclk0: GclkToken, + pub gclk1: GclkToken, + pub gclk2: GclkToken, + pub gclk3: GclkToken, + pub gclk4: GclkToken, + pub gclk5: GclkToken, + #[hal_cfg("gclk6")] + pub gclk6: GclkToken, + #[hal_cfg("gclk7")] + pub gclk7: GclkToken, + #[hal_cfg("gclk8")] + pub gclk8: GclkToken, + #[hal_cfg("gclk9")] + pub gclk9: GclkToken, + #[hal_cfg("gclk10")] + pub gclk10: GclkToken, + #[hal_cfg("gclk11")] + pub gclk11: GclkToken, +} - impl GclkTokens { - /// Create the set of [`GclkToken`]s - /// - /// # Safety - /// - /// All of the invariants required by `GclkToken::new` must be - /// upheld here as well. - #[inline] - pub(super) unsafe fn new(nvmctrl: &mut Nvmctrl) -> Self { - unsafe { - // Use auto wait states - nvmctrl.ctrla().modify(|_, w| w.autows().set_bit()); - GclkTokens { - #( gclk~N: GclkToken::new(), )* - } - } - } +#[hal_macro_helper] +impl GclkTokens { + /// Create the set of [`GclkToken`]s + /// + /// # Safety + /// + /// All of the invariants required by `GclkToken::new` must be + /// upheld here as well. + #[inline] + pub(super) unsafe fn new() -> Self { + GclkTokens { + gclk0: unsafe { GclkToken::new() }, + gclk1: unsafe { GclkToken::new() }, + gclk2: unsafe { GclkToken::new() }, + gclk3: unsafe { GclkToken::new() }, + gclk4: unsafe { GclkToken::new() }, + gclk5: unsafe { GclkToken::new() }, + #[hal_cfg("gclk6")] + gclk6: unsafe { GclkToken::new() }, + #[hal_cfg("gclk7")] + gclk7: unsafe { GclkToken::new() }, + #[hal_cfg("gclk8")] + gclk8: unsafe { GclkToken::new() }, + #[hal_cfg("gclk9")] + gclk9: unsafe { GclkToken::new() }, + #[hal_cfg("gclk10")] + gclk10: unsafe { GclkToken::new() }, + #[hal_cfg("gclk11")] + gclk11: unsafe { GclkToken::new() }, } } -}); +} //============================================================================== // GclkOut @@ -1448,7 +1683,7 @@ seq!(N in 1..=11 { /// See the [module-level documentation](self) for an example of creating a /// [`GclkOut`] from an [`EnabledGclk`]. pub struct GclkOut { - pin: Pin, + pin: Pin, freq: Hertz, } @@ -1480,7 +1715,7 @@ where /// enforce this requirement. /// /// Finally, when a [`GclkOut`] is disabled, but the [`Pin`] is still in - /// [`AlternateM`] mode, it takes the "output off value" of the `Gclk`. See + /// [`AlternateH`] mode, it takes the "output off value" of the `Gclk`. See /// the [`Gclk::output_off_value`] documentation for more details. #[inline] pub fn enable_gclk_out

(mut self, pin: P) -> (EnabledGclk, GclkOut) @@ -1490,9 +1725,13 @@ where P::Id: GclkIo, { let pin = pin.into().into_mode(); - let freq = self.freq(); - self.0.token.enable_gclk_out(); - let gclk_out = GclkOut { pin, freq }; + self.0.settings.output_enable = true; + self.0.update_genctrl(true); + + let gclk_out = GclkOut { + pin, + freq: self.freq(), + }; (self.inc(), gclk_out) } @@ -1500,18 +1739,20 @@ where /// /// Disabling [`GclkIo`] output will [`Decrement`] the [`EnabledGclk`] /// counter. When a [`GclkOut`] is disabled, but the [`Pin`] is still in - /// [`AlternateM`] mode, it takes the "output off value" of the `Gclk`. See + /// [`AlternateH`] mode, it takes the "output off value" of the `Gclk`. See /// the [`Gclk::output_off_value`] documentation for more details. #[inline] pub fn disable_gclk_out( mut self, gclk_out: GclkOut, - ) -> (EnabledGclk, Pin) + ) -> (EnabledGclk, Pin) where N: Decrement, I: GclkIo, { - self.0.token.disable_gclk_out(); + self.0.settings.output_enable = false; + self.0.update_genctrl(true); + (self.dec(), gclk_out.pin) } } diff --git a/hal/src/clock/v2/osc.rs b/hal/src/clock/v2/osc.rs new file mode 100644 index 000000000000..d0ebbae9c7a8 --- /dev/null +++ b/hal/src/clock/v2/osc.rs @@ -0,0 +1,171 @@ +//! Open-loop 8MHz oscillator + +use crate::pac::sysctrl::osc8m::{Frangeselect, Prescselect}; +use crate::pac::sysctrl::Osc8m; +use crate::pac::Sysctrl; + +use fugit::RateExtU32; +use crate::time::Hertz; +use crate::typelevel::Sealed; + +use super::{Enabled, Source}; + +pub struct OscToken(()); + +impl OscToken { + pub(super) unsafe fn new() -> Self { + Self(()) + } + + fn osc8m(&self) -> &Osc8m { + unsafe { (*Sysctrl::PTR).osc8m() } + } + + fn enable(&mut self, settings: Settings) { + self.osc8m().modify(|_, w| { + if let Some(freq_range) = settings.freq_range { + w.frange().variant(freq_range.into()); + } + if let Some(calibration) = settings.calibration { + // Safety: The PAC will truncate the value to 12 bits, + // and all 12-bit values are valid + unsafe { w.calib().bits(calibration) }; + } + w.presc().variant(settings.prescaler.into()); + w.ondemand().bit(settings.on_demand); + w.runstdby().bit(settings.run_standby); + w.enable().set_bit() + }); + } + + fn disable(&mut self) { + self.osc8m().modify(|_, w| w.enable().clear_bit()) + } +} + +#[derive(Clone, Copy, PartialEq, Eq)] +struct Settings { + freq_range: Option, + calibration: Option, + prescaler: Prescaler, + on_demand: bool, + run_standby: bool, +} + +#[derive(Clone, Copy, PartialEq, Eq)] +/// Frequency ranges in Megahertz +pub enum FreqRange { + FourToSix, + SixToEight, + EightToEleven, + ElevenToFifteen, +} + +impl From for Frangeselect { + fn from(freq_range: FreqRange) -> Self { + match freq_range { + FreqRange::FourToSix => Self::_0, + FreqRange::SixToEight => Self::_1, + FreqRange::EightToEleven => Self::_2, + FreqRange::ElevenToFifteen => Self::_3, + } + } +} + +#[derive(Clone, Copy, PartialEq, Eq)] +pub enum Prescaler { + One, + Two, + Four, + Eight, +} + +impl From for Prescselect { + fn from(prescaler: Prescaler) -> Self { + match prescaler { + Prescaler::One => Self::_0, + Prescaler::Two => Self::_1, + Prescaler::Four => Self::_2, + Prescaler::Eight => Self::_3, + } + } +} + +pub enum OscId {} + +impl Sealed for OscId {} + +pub struct Osc { + token: OscToken, + settings: Settings, +} + +impl Osc { + pub fn new(token: OscToken) -> Self { + let settings = Settings { + freq_range: None, + calibration: None, + prescaler: Prescaler::Eight, + on_demand: true, + run_standby: false, + }; + Self { token, settings } + } + + pub fn freq_range(mut self, freq_range: FreqRange) -> Self { + self.settings.freq_range = Some(freq_range); + self + } + + pub fn calibration(mut self, calibration: u16) -> Self { + self.settings.calibration = Some(calibration); + self + } + + pub fn prescaler(mut self, prescaler: Prescaler) -> Self { + self.settings.prescaler = prescaler; + self + } + + pub fn on_demand(mut self, on_demand: bool) -> Self { + self.settings.on_demand = on_demand; + self + } + + pub fn run_standby(mut self, run_standby: bool) -> Self { + self.settings.run_standby = run_standby; + self + } + + pub fn freq(&self) -> Hertz { + let div = match self.settings.prescaler { + Prescaler::One => 1, + Prescaler::Two => 2, + Prescaler::Four => 4, + Prescaler::Eight => 8, + }; + (8_000_000u32 / div).Hz() + } + + pub fn enable(mut self) -> Enabled { + self.token.enable(self.settings); + Enabled::new(self) + } +} + +pub type EnabledOsc = Enabled; + +impl EnabledOsc { + pub fn disable(mut self) -> Osc { + self.0.token.disable(); + self.0 + } +} + +impl Source for EnabledOsc { + type Id = OscId; + + fn freq(&self) -> Hertz { + self.0.freq() + } +} diff --git a/hal/src/clock/v2/osc32k.rs b/hal/src/clock/v2/osc32k.rs new file mode 100644 index 000000000000..96f3bcfe4fa2 --- /dev/null +++ b/hal/src/clock/v2/osc32k.rs @@ -0,0 +1,587 @@ +//! # Tunable, low-speed and low-power clock source +//! +//! ## Overview +//! +//! The `osc32k` module provides access to the 32 kHz oscillator (OSC32K), +//! provided by the `SYSCTRL` peripheral. Depending on the target, it has one +//! or two outputs: one at 32 kHz and another divided down to 1 kHz. These +//! outputs can be disabled or enabled independently at any given time. +//! +//! We can see, that the `OSC32K` peripheral forms its own, miniature clock +//! tree. There is a 1:N producer clock; and there are one or two possible +//! consumer clocks that can be independently and optionally enabled. +//! +//! To represent this structure in the type system, we divide the `OSC32K` +//! peripheral into these three clocks. Users get access to the 1:N +//! [`EnabledOsc32kBase`] clock [`Source`], which can be consumed by both the +//! [`Osc32k`] and [`Osc1k`] clocks. Note that `Osc32k` and `Osc1k` are +//! themselves 1:N clocks as well. +//! +//! ## Write lock +//! +//! The `OSC32K` register has a dedicated write lock bit that will freeze its +//! configuration until the next power-on reset. We implement this by simply +//! dropping the [`Osc32kBase`] clock, which prevents any further access to the +//! `OSC32K` register. +//! +//! ## Example +//! +//! Creating and configuring the OSC32K clocks proceeds according to the +//! principles outlined in the [`clock` module documentation]. It is best shown +//! with an example. +//! +//! Let's start by using [`clock_system_at_reset`] to access the HAL clocking +//! structs. +//! +//! ```no_run +//! use atsamd_hal::{ +//! clock::v2::{ +//! clock_system_at_reset, +//! osc32k::{Osc1k, Osc32k}, +//! }, +//! pac::Peripherals, +//! }; +//! let mut pac = Peripherals::take().unwrap(); +//! let (buses, clocks, tokens) = clock_system_at_reset( +//! pac.OSCCTRL, +//! pac.OSC32KCTRL, +//! pac.GCLK, +//! pac.MCLK, +//! &mut pac.NVMCTRL, +//! ); +//! ``` +//! +//! Next, we can extract the [`EnabledOsc32kBase`] clock from the [`Clocks`] +//! struct and use it to enable the [`Osc1k`] and [`Osc32k`] clocks. +//! +//! ```no_run +//! # use atsamd_hal::{ +//! # clock::v2::{ +//! # clock_system_at_reset, +//! # osc32k::{Osc1k, Osc32k}, +//! # }, +//! # pac::Peripherals, +//! # }; +//! # let mut pac = Peripherals::take().unwrap(); +//! # let (buses, clocks, tokens) = clock_system_at_reset( +//! # pac.OSCCTRL, +//! # pac.OSC32KCTRL, +//! # pac.GCLK, +//! # pac.MCLK, +//! # &mut pac.NVMCTRL, +//! # ); +//! let base = clocks.osc32k_base; +//! let (osc1k, base) = Osc1k::enable(tokens.osc32k.osc1k, base); +//! let (osc32k, base) = Osc32k::enable(tokens.osc32k.osc32k, base); +//! ``` +//! +//! We can then override the calibration value read from flash at start up. +//! +//! ```no_run +//! # use atsamd_hal::{ +//! # clock::v2::{ +//! # clock_system_at_reset, +//! # osc32k::{Osc1k, Osc32k}, +//! # }, +//! # pac::Peripherals, +//! # }; +//! # let mut pac = Peripherals::take().unwrap(); +//! # let (buses, clocks, tokens) = clock_system_at_reset( +//! # pac.OSCCTRL, +//! # pac.OSC32KCTRL, +//! # pac.GCLK, +//! # pac.MCLK, +//! # &mut pac.NVMCTRL, +//! # ); +//! # let base = clocks.osc32k_base; +//! # let (osc1k, base) = Osc1k::enable(tokens.osc32k.osc1k, base); +//! # let (osc32k, mut base) = Osc32k::enable(tokens.osc32k.osc32k, base); +//! base.set_calibration(128); +//! ``` +//! +//! And finally, we can set the write lock bit to freeze the configuation until +//! the next power-on reset. Doing so also drops the `EnabledOsc32kBase` clock. +//! +//! ```no_run +//! # use atsamd_hal::{ +//! # clock::v2::{ +//! # clock_system_at_reset, +//! # osc32k::{Osc1k, Osc32k}, +//! # }, +//! # pac::Peripherals, +//! # }; +//! # let mut pac = Peripherals::take().unwrap(); +//! # let (buses, clocks, tokens) = clock_system_at_reset( +//! # pac.OSCCTRL, +//! # pac.OSC32KCTRL, +//! # pac.GCLK, +//! # pac.MCLK, +//! # &mut pac.NVMCTRL, +//! # ); +//! # let base = clocks.osc32k_base; +//! # let (osc1k, base) = Osc1k::enable(tokens.osc32k.osc1k, base); +//! # let (osc32k, mut base) = Osc32k::enable(tokens.osc32k.osc32k, base); +//! # base.set_calibration(128); +//! base.write_lock(); +//! ``` +//! +//! The complete example is shown below. +//! +//! ```no_run +//! use atsamd_hal::{ +//! clock::v2::{ +//! clock_system_at_reset, +//! osc32k::{Osc1k, Osc32k}, +//! }, +//! pac::Peripherals, +//! }; +//! let mut pac = Peripherals::take().unwrap(); +//! let (buses, clocks, tokens) = clock_system_at_reset( +//! pac.OSCCTRL, +//! pac.OSC32KCTRL, +//! pac.GCLK, +//! pac.MCLK, +//! &mut pac.NVMCTRL, +//! ); +//! let base = clocks.osc32k_base; +//! let (osc1k, base) = Osc1k::enable(tokens.osc32k.osc1k, base); +//! let (osc32k, mut base) = Osc32k::enable(tokens.osc32k.osc32k, base); +//! base.set_calibration(128); +//! base.write_lock(); +//! ``` +//! +//! [`clock` module documentation]: super +//! [`clock_system_at_reset`]: super::clock_system_at_reset +//! [`Clocks`]: super::Clocks + +use atsamd_hal_macros::{hal_cfg, hal_macro_helper}; + +use typenum::U0; + +use crate::pac::sysctrl::Osc32k as OSC32K; + +use crate::typelevel::{Decrement, Increment, PrivateDecrement, PrivateIncrement}; + +use fugit::RateExtU32; +use crate::time::Hertz; +use crate::typelevel::Sealed; + +use super::{Enabled, Source}; + +//============================================================================== +// Tokens +//============================================================================== + +/// Singleton token for the [`Osc32kBase`] clock +// +// There should never be more than one instance of `Osc32kBaseToken`, because +// it relies on that fact for memory safety. +// +// Users never see `Osc32kBaseToken`, because the OSC32K base oscillator +// is always enabled. Internally, however, it is used as a register interface. +// The token is zero-sized, so it can be carried by clock types without +// introducing any memory bloat. +// +// As part of that register interface, the `Osc32kBaseToken` can access the +// `OSC32K` register. That the token is a singleton guarantees the register +// is written from only one location. This allows the token to be `Sync`, even +// though the PAC `OSC32KCTRL` struct is not. +pub struct Osc32kBaseToken(()); + +/// Singleton token that can be exchanged for [`Osc1k`] +/// +/// As explained in the [`clock` module documentation](super), instances of +/// various `Token` types can be exchanged for actual clock types. They +/// typically represent clocks that are disabled at power-on reset. +/// +/// The [`Osc1k`] clock is disabled at power-on reset. To use it, you must +/// first exchange the token for an actual clock with [`Osc1k::enable`]. + +#[hal_cfg("sysctrl-d11")] +pub struct Osc1kToken(()); + +/// Singleton token that can be exchanged for [`Osc32k`] +/// +/// As explained in the [`clock` module documentation](super), instances of +/// various `Token` types can be exchanged for actual clock types. They +/// typically represent clocks that are disabled at power-on reset. +/// +/// The [`Osc32k`] clock is disabled at power-on reset. To use it, you must +/// first exchange the token for an actual clock with [`Osc32k::enable`]. +pub struct Osc32kToken(()); + +/// Set of tokens representing the disabled OSC32K clocks power-on reset +#[hal_macro_helper] +pub struct Osc32kTokens { + pub base: Osc32kBaseToken, + #[hal_cfg("sysctrl-d11")] + pub osc1k: Osc1kToken, + pub osc32k: Osc32kToken, +} + +impl Osc32kTokens { + /// Create the set of tokens + /// + /// # Safety + /// + /// There must never be more than one instance of each token at any given + /// time. See the notes on `Token` types and memory safety in the root of + /// the `clock` module for more details. + #[allow(unused)] + #[hal_macro_helper] + pub(super) unsafe fn new() -> Self { + Self { + base: Osc32kBaseToken(()), + #[hal_cfg("sysctrl-d11")] + osc1k: Osc1kToken(()), + osc32k: Osc32kToken(()), + } + } +} + +impl Osc32kBaseToken { + #[inline] + fn osc32k(&self) -> &OSC32K { + // Safety: The `Osc32kBaseToken` has exclusive access to the + // `OSC32K` register. See the notes on `Token` types and memory + // safety in the root of the `clock` module for more details. + unsafe { (*crate::pac::Sysctrl::PTR).osc32k() } + } + + /// Set the calibration + #[inline] + fn set_calibration(&mut self, calib: u8) { + // Safety: All bit patterns are valid for this field + self.osc32k() + .modify(|_, w| unsafe { w.calib().bits(calib) }); + } + + #[inline] + fn enable(&mut self, settings: Settings) { + self.osc32k().modify(|_, w| { + // Safety: The value comes from the `StartUpDelay` enum, + // so the value is guaranteed to be valid + unsafe { w.startup().bits(settings.start_up as u8) }; + w.ondemand().bit(settings.on_demand); + w.runstdby().bit(settings.run_standby); + w.enable().set_bit() + }); + } + + #[inline] + fn disable(&mut self) { + self.osc32k().modify(|_, w| w.enable().clear_bit()); + } + + /// Enable the 1 kHz output + #[hal_cfg("sysctrl-d11")] + #[inline] + fn enable_1k(&mut self) { + self.osc32k().modify(|_, w| w.en1k().set_bit()); + } + + /// Disable the 1 kHz output + #[hal_cfg("sysctrl-d11")] + #[inline] + fn disable_1k(&mut self) { + self.osc32k().modify(|_, w| w.en1k().clear_bit()); + } + + /// Enable the 32 kHz output + #[inline] + fn enable_32k(&mut self) { + self.osc32k().modify(|_, w| w.en32k().set_bit()); + } + + /// Disable the 32 kHz output + #[inline] + fn disable_32k(&mut self) { + self.osc32k().modify(|_, w| w.en32k().clear_bit()); + } + + /// Enable the write lock + #[inline] + fn write_lock(&mut self) { + self.osc32k().modify(|_, w| w.wrtlock().set_bit()); + } +} + +#[derive(Clone, Copy, PartialEq, Eq)] +struct Settings { + start_up: StartUpDelay, + on_demand: bool, + run_standby: bool, +} + +//============================================================================== +// StartUpDelay +//============================================================================== + +#[repr(u8)] +#[derive(Clone, Copy, Default, PartialEq, Eq)] +pub enum StartUpDelay { + #[default] + Delay92us, + Delay122us, + Delay183us, + Delay305us, + Delay549us, + Delay1ms, + Delay2ms, + Delay4ms, +} + +//============================================================================== +// OscBase +//============================================================================== + +/// OSC3ULP2K base clock, which feeds the [`Osc1k`] and [`Osc32k`] clocks +/// +/// The OSC32K peripheral has two possible clock outputs, one at 32 kHz and +/// another at 1 kHz. This structure is represented in the type system as a set +/// of three clocks forming a small clock tree. The [`Osc32kBase`] clock +/// represents the base oscillator that feeds the optional [`Osc1k`] and +/// [`Osc32k`] output clocks. See the [module-level documentation](super) for +/// details and examples. +pub struct Osc32kBase { + token: Osc32kBaseToken, + settings: Settings, +} + +/// The [`Enabled`] [`Osc32kBase`] clock +/// +/// As described in the [`clock` module documentation](super), the [`Enabled`] +/// wrapper implements compile-time clock tree safety by tracking the number of +/// clocks consuming the [`Osc32kBase`] clock and restricts access to the +/// underlying type to prevent misuse. +/// +/// **NOTE:** The `Osc32kBase` clock is internal and can never be disabled, +/// so we do not provide a `disable` method. +/// +/// As with [`Enabled`], the default value for `N` is `U0`; if left unspecified, +/// the counter is assumed to be zero. +pub type EnabledOsc32kBase = Enabled; + +impl Osc32kBase { + /// Create the ultra-low power base oscillator + /// + /// # Safety + /// + /// Because an `Osc32kBase` contains an `Osc32kBaseToken`, there must + /// never be more than one instance of this struct at any given time. See + /// the notes on `Token` types and memory safety in the root of the `clock` + /// module for more details. + #[inline] + pub fn new(token: Osc32kBaseToken) -> Self { + let settings = Settings { + start_up: StartUpDelay::Delay92us, + on_demand: true, + run_standby: false, + }; + Self { token, settings } + } + + pub fn start_up_delay(mut self, start_up: StartUpDelay) -> Self { + self.settings.start_up = start_up; + self + } + + pub fn on_demand(mut self, on_demand: bool) -> Self { + self.settings.on_demand = on_demand; + self + } + + pub fn run_standby(mut self, run_standby: bool) -> Self { + self.settings.run_standby = run_standby; + self + } + + #[inline] + pub fn enable(mut self) -> EnabledOsc32kBase { + self.token.enable(self.settings); + Enabled::new(self) + } +} + +impl EnabledOsc32kBase { + #[inline] + pub fn disable(mut self) -> Osc32kBase { + self.0.token.disable(); + self.0 + } + + /// Override the factory-default calibration value + #[inline] + pub fn set_calibration(&mut self, calib: u8) { + self.0.token.set_calibration(calib); + } + + /// Freeze the OSC32K configuration until power-on reset + /// + /// This function sets the write-lock bit, which freezes the OSC32K + /// configuration at the hardware level until power-on reset. At the API + /// level, it also consumes and drops the [`Osc32kBase`] clock, which + /// prevents any further modifications. + #[inline] + pub fn write_lock(mut self) { + self.0.token.write_lock(); + } +} + +//============================================================================== +// Ids +//============================================================================== + +/// Type representing the identity of the [`Osc1k`] clock +/// +/// See the discussion on [`Id` types](super#id-types) for more information. +#[hal_cfg("sysctrl-d11")] +pub enum Osc1kId {} + +#[hal_cfg("sysctrl-d11")] +impl Sealed for Osc1kId {} + +/// Type representing the identity of the [`Osc32k`] clock +/// +/// See the discussion on [`Id` types](super#id-types) for more information. +pub enum Osc32kId {} + +impl Sealed for Osc32kId {} + +//============================================================================== +// Osc1k +//============================================================================== + +/// Clock representing the 1 kHz output of the [`Osc32kBase`] clock +/// +/// The OSC32K peripheral has two possible clock outputs, one at 32 kHz and +/// another at 1 kHz. This structure is represented in the type system as a set +/// of three clocks forming a small clock tree. The [`Osc1k`] clock is +/// derived from the [`Osc32kBase`] clock. See the +/// [module-level documentation](super) for details and examples. +#[hal_cfg("sysctrl-d11")] +pub struct Osc1k { + #[allow(unused)] + token: Osc1kToken, +} + +/// OSC1K is not available on the currently-documented target +#[cfg(doc)] +#[hal_cfg(not("sysctrl-d11"))] +pub struct Osc1k {} + +/// The [`Enabled`] [`Osc1k`] clock +/// +/// As described in the [`clock` module documentation](super), the [`Enabled`] +/// wrapper implements compile-time clock tree safety by tracking the number of +/// clocks consuming the [`Osc1k`] clock and restricts access to the +/// underlying type to prevent misuse. +/// +/// As with [`Enabled`], the default value for `N` is `U0`; if left unspecified, +/// the counter is assumed to be zero. +#[hal_cfg("sysctrl-d11")] +pub type EnabledOsc1k = Enabled; + +#[hal_cfg("sysctrl-d11")] +impl Osc1k { + /// Enable 1 kHz output from the [`Osc32kBase`] clock + /// + /// This will [`Increment`] the [`EnabledOsc32kBase`] counter. + #[inline] + pub fn enable( + token: Osc1kToken, + mut base: EnabledOsc32kBase, + ) -> (EnabledOsc1k, EnabledOsc32kBase) { + base.0.token.enable_1k(); + (Enabled::new(Self { token }), base.inc()) + } +} + +#[hal_cfg("sysctrl-d11")] +impl EnabledOsc1k { + /// Disable 1 kHz output from the [`Osc32kBase`] clock + /// + /// This will [`Decrement`] the [`EnabledOsc32kBase`] counter. + #[inline] + pub fn disable( + self, + mut base: EnabledOsc32kBase, + ) -> (Osc1kToken, EnabledOsc32kBase) { + base.0.token.disable_1k(); + (self.0.token, base.dec()) + } +} + +#[hal_cfg("sysctrl-d11")] +impl Source for EnabledOsc1k { + type Id = Osc1kId; + + #[inline] + fn freq(&self) -> Hertz { + 1_024u32.Hz() + } +} + +//============================================================================== +// Osc32k +//============================================================================== + +/// Clock representing the 32 kHz output of the [`Osc32kBase`] clock +/// +/// The OSC32K peripheral has two possible clock outputs, one at 32 kHz and +/// another at 1 kHz. This structure is represented in the type system as a set +/// of three clocks forming a small clock tree. The [`Osc32k`] clock is +/// derived from the [`Osc32kBase`] clock. See the +/// [module-level documentation](super) for details and examples. +pub struct Osc32k { + #[allow(unused)] + token: Osc32kToken, +} + +/// The [`Enabled`] [`Osc32k`] clock +/// +/// As described in the [`clock` module documentation](super), the [`Enabled`] +/// wrapper implements compile-time clock tree safety by tracking the number of +/// clocks consuming the [`Osc32k`] clock and restricts access to the +/// underlying type to prevent misuse. +/// +/// As with [`Enabled`], the default value for `N` is `U0`; if left unspecified, +/// the counter is assumed to be zero. +pub type EnabledOsc32k = Enabled; + +impl Osc32k { + /// Enable 32 kHz output from the [`Osc32kBase`] clock + /// + /// This will [`Increment`] the [`EnabledOsc32kBase`] counter. + #[inline] + pub fn enable( + token: Osc32kToken, + mut base: EnabledOsc32kBase, + ) -> (EnabledOsc32k, EnabledOsc32kBase) { + base.0.token.enable_32k(); + (Enabled::new(Self { token }), base.inc()) + } +} + +impl EnabledOsc32k { + /// Disable 32 kHz output from the [`Osc32kBase`] clock + /// + /// This will [`Decrement`] the [`EnabledOsc32kBase`] counter. + #[inline] + pub fn disable( + self, + mut base: EnabledOsc32kBase, + ) -> (Osc32kToken, EnabledOsc32kBase) { + base.0.token.disable_32k(); + (self.0.token, base.dec()) + } +} + +impl Source for EnabledOsc32k { + type Id = Osc32kId; + + #[inline] + fn freq(&self) -> Hertz { + 32_768u32.Hz() + } +} diff --git a/hal/src/peripherals/clock/d5x/v2/osculp32k.rs b/hal/src/clock/v2/osculp32k.rs similarity index 85% rename from hal/src/peripherals/clock/d5x/v2/osculp32k.rs rename to hal/src/clock/v2/osculp32k.rs index 1b4e1c30d99b..e580bc18c561 100644 --- a/hal/src/peripherals/clock/d5x/v2/osculp32k.rs +++ b/hal/src/clock/v2/osculp32k.rs @@ -3,7 +3,8 @@ //! ## Overview //! //! The `osculp32k` module provides access to the 32 kHz ultra low power -//! internal oscillator (OSCULP32K) within the `OSC32KCTRL` peripheral. +//! internal oscillator (OSCULP32K) within the `OSC32KCTRL` or `SYSCTRL` +//! peripheral. //! //! The `OSCULP32K` clock is unlike most other clocks. First, it is an internal //! clock that is always enabled and can't be disabled. And second, it has two @@ -13,9 +14,9 @@ //! We can see, then, that the `OSCULP32K` peripheral forms its own, miniature //! clock tree. There is a 1:N producer clock that is always enabled; and there //! are two possible consumer clocks that can be independently and optionally -//! enabled. In fact, this structure is illustrated by the `OSCULP32K` -//! register, which has no regular `ENABLE` bit and two different enable bits -//! for clock output, `EN32K` and `EN1K`. +//! enabled. In fact, this structure is illustrated by the `OSCULP32K` register, +//! which has no regular `ENABLE` bit and two different enable bits for clock +//! output, `EN32K` and `EN1K`. //! //! To represent this structure in the type system, we divide the `OSCULP32K` //! peripheral into these three clocks. Users get access to the 1:N @@ -161,13 +162,20 @@ //! [`clock_system_at_reset`]: super::clock_system_at_reset //! [`Clocks`]: super::Clocks +use atsamd_hal_macros::{hal_cfg, hal_macro_helper}; use fugit::RateExtU32; use typenum::U0; +#[hal_cfg("osc32kctrl")] use crate::pac::osc32kctrl::Osculp32k; +#[hal_cfg("osc32kctrl")] +use crate::typelevel::{Decrement, Increment, PrivateDecrement, PrivateIncrement}; + +#[hal_cfg("sysctrl")] +use crate::pac::sysctrl::Osculp32k; use crate::time::Hertz; -use crate::typelevel::{Decrement, Increment, PrivateDecrement, PrivateIncrement, Sealed}; +use crate::typelevel::Sealed; use super::{Enabled, Source}; @@ -197,8 +205,9 @@ struct OscUlp32kBaseToken(()); /// various `Token` types can be exchanged for actual clock types. They /// typically represent clocks that are disabled at power-on reset. /// -/// The [`OscUlp1k`] clock is disabled at power-on reset. To use it, you must -/// first exchange the token for an actual clock with [`OscUlp1k::enable`]. +/// On some targets, the [`OscUlp1k`] clock is disabled at power-on reset, and +/// to use it the token must first be exchanged for an actual clock with +/// [`OscUlp1k::enable`]. pub struct OscUlp1kToken(()); /// Singleton token that can be exchanged for [`OscUlp32k`] @@ -207,59 +216,50 @@ pub struct OscUlp1kToken(()); /// various `Token` types can be exchanged for actual clock types. They /// typically represent clocks that are disabled at power-on reset. /// -/// The [`OscUlp32k`] clock is disabled at power-on reset. To use it, you must -/// first exchange the token for an actual clock with [`OscUlp32k::enable`]. +/// On some targets, the [`OscUlp32k`] clock is disabled at power-on reset, and +/// to use it the token must first be exchanged for an actual clock with +/// [`OscUlp32k::enable`]. pub struct OscUlp32kToken(()); -/// Set of tokens representing the disabled OSCULP32K clocks power-on reset -pub struct OscUlp32kTokens { - pub osculp1k: OscUlp1kToken, - pub osculp32k: OscUlp32kToken, -} - -impl OscUlp32kTokens { - /// Create the set of tokens - /// - /// # Safety - /// - /// There must never be more than one instance of each token at any given - /// time. See the notes on `Token` types and memory safety in the root of - /// the `clock` module for more details. - pub(super) unsafe fn new() -> Self { - Self { - osculp1k: OscUlp1kToken(()), - osculp32k: OscUlp32kToken(()), - } - } -} - impl OscUlp32kBaseToken { #[inline] + #[hal_macro_helper] fn osculp32k(&self) -> &Osculp32k { // Safety: The `OscUlp32kBaseToken` has exclusive access to the // `OSCULP32K` register. See the notes on `Token` types and memory // safety in the root of the `clock` module for more details. - unsafe { (*crate::pac::Osc32kctrl::PTR).osculp32k() } + #[hal_cfg("osc32kctrl")] + unsafe { + (*crate::pac::Osc32kctrl::PTR).osculp32k() + } + #[hal_cfg("sysctrl")] + unsafe { + (*crate::pac::Sysctrl::PTR).osculp32k() + } } + #[hal_cfg("clock-d5x")] /// Enable the 1 kHz output #[inline] fn enable_1k(&mut self) { self.osculp32k().modify(|_, w| w.en1k().set_bit()); } + #[hal_cfg("clock-d5x")] /// Disable the 1 kHz output #[inline] fn disable_1k(&mut self) { self.osculp32k().modify(|_, w| w.en1k().clear_bit()); } + #[hal_cfg("clock-d5x")] /// Enable the 32 kHz output #[inline] fn enable_32k(&mut self) { self.osculp32k().modify(|_, w| w.en32k().set_bit()); } + #[hal_cfg("clock-d5x")] /// Disable the 32 kHz output #[inline] fn disable_32k(&mut self) { @@ -313,9 +313,9 @@ impl OscUlp32kBase { /// the notes on `Token` types and memory safety in the root of the `clock` /// module for more details. #[inline] - pub(super) unsafe fn new() -> EnabledOscUlp32kBase { + pub(super) unsafe fn new() -> Self { let token = OscUlp32kBaseToken(()); - Enabled::new(Self { token }) + Self { token } } } @@ -362,6 +362,7 @@ impl Sealed for OscUlp32kId {} /// derived from the [`OscUlp32kBase`] clock. See the /// [module-level documentation](super) for details and examples. pub struct OscUlp1k { + #[allow(unused)] token: OscUlp1kToken, } @@ -377,9 +378,24 @@ pub struct OscUlp1k { pub type EnabledOscUlp1k = Enabled; impl OscUlp1k { + /// Create the ultra-low power base oscillator + /// + /// # Safety + /// + /// Because an `OscUlp1k` contains an `OscUlp1kToken`, there must never be + /// more than one instance of this struct at any given time. See the notes + /// on `Token` types and memory safety in the root of the `clock` module for + /// more details. + #[inline] + pub(super) unsafe fn new() -> Self { + let token = OscUlp1kToken(()); + Self { token } + } + /// Enable 1 kHz output from the [`OscUlp32kBase`] clock /// /// This will [`Increment`] the [`EnabledOscUlp32kBase`] counter. + #[hal_cfg("clock-d5x")] #[inline] pub fn enable( token: OscUlp1kToken, @@ -388,12 +404,18 @@ impl OscUlp1k { base.0.token.enable_1k(); (Enabled::new(Self { token }), base.inc()) } + + /// The OSCULP32K 1.024kHz output is always enabled on the documented target + #[cfg(doc)] + #[hal_cfg(not("clock-d5x"))] + pub fn enable(_token: OscUlp1kToken) {} } impl EnabledOscUlp1k { /// Disable 1 kHz output from the [`OscUlp32kBase`] clock /// /// This will [`Decrement`] the [`EnabledOscUlp32kBase`] counter. + #[hal_cfg("clock-d5x")] #[inline] pub fn disable( self, @@ -425,6 +447,7 @@ impl Source for EnabledOscUlp1k { /// derived from the [`OscUlp32kBase`] clock. See the /// [module-level documentation](super) for details and examples. pub struct OscUlp32k { + #[allow(unused)] token: OscUlp32kToken, } @@ -440,9 +463,24 @@ pub struct OscUlp32k { pub type EnabledOscUlp32k = Enabled; impl OscUlp32k { + /// Create the ultra-low power base oscillator + /// + /// # Safety + /// + /// Because an `OscUlp32k` contains an `OscUlp32kToken`, there must never be + /// more than one instance of this struct at any given time. See the notes + /// on `Token` types and memory safety in the root of the `clock` module for + /// more details. + #[inline] + pub(super) unsafe fn new() -> Self { + let token = OscUlp32kToken(()); + Self { token } + } + /// Enable 32 kHz output from the [`OscUlp32kBase`] clock /// /// This will [`Increment`] the [`EnabledOscUlp32kBase`] counter. + #[hal_cfg("clock-d5x")] #[inline] pub fn enable( token: OscUlp32kToken, @@ -451,12 +489,19 @@ impl OscUlp32k { base.0.token.enable_32k(); (Enabled::new(Self { token }), base.inc()) } + + /// The OSCULP32K 32.768kHz output is always enabled on the documented + /// target + #[cfg(doc)] + #[hal_cfg(not("clock-d5x"))] + pub fn enable(_token: OscUlp32kToken) {} } impl EnabledOscUlp32k { /// Disable 32 kHz output from the [`OscUlp32kBase`] clock /// /// This will [`Decrement`] the [`EnabledOscUlp32kBase`] counter. + #[hal_cfg("clock-d5x")] #[inline] pub fn disable( self, diff --git a/hal/src/peripherals/clock/d5x/v2/pclk.rs b/hal/src/clock/v2/pclk.rs similarity index 63% rename from hal/src/peripherals/clock/d5x/v2/pclk.rs rename to hal/src/clock/v2/pclk.rs index cd58db8f6cca..02a04286dd95 100644 --- a/hal/src/peripherals/clock/d5x/v2/pclk.rs +++ b/hal/src/clock/v2/pclk.rs @@ -62,15 +62,27 @@ //! [`clock::v2::types`]: super::types //! [`Sercom`]: crate::sercom::Sercom -use atsamd_hal_macros::hal_macro_helper; +use atsamd_hal_macros::{hal_cfg, hal_macro_helper}; use core::marker::PhantomData; use paste::paste; -use seq_macro::seq; use crate::pac; -use crate::pac::gclk::pchctrl::Genselect; + +#[hal_cfg("clock-d5x")] +mod imports { + pub use crate::pac::gclk::Pchctrl as Ctrl; + pub use crate::pac::gclk::pchctrl::Genselect; +} + +#[hal_cfg(any("clock-d11", "clock-d21"))] +mod imports { + pub use crate::pac::gclk::Clkctrl as Ctrl; + pub use crate::pac::gclk::clkctrl::Genselect; +} + +use imports::*; use crate::time::Hertz; use crate::typelevel::{Decrement, Increment, Sealed}; @@ -113,31 +125,51 @@ impl PclkToken

{ /// Access the corresponding `PCHCTRL` register #[inline] - fn pchctrl(&self) -> &pac::gclk::Pchctrl { + #[hal_macro_helper] + fn ctrl(&self) -> &Ctrl { // Safety: Each `PclkToken` only has access to a mutually exclusive set // of registers for the corresponding `PclkId`, and we use a shared // reference to the register block. See the notes on `Token` types and // memory safety in the root of the `clock` module for more details. - unsafe { (*pac::Gclk::PTR).pchctrl(P::DYN as usize) } + #[hal_cfg("clock-d5x")] + unsafe { + (*pac::Gclk::PTR).pchctrl(P::DYN as usize) + } + #[hal_cfg(any("clock-d11", "clock-d21"))] + unsafe { + (*pac::Gclk::PTR).clkctrl() + } } /// Set the [`Pclk`] source #[inline] - fn set_source(&mut self, source: DynPclkSourceId) { - self.pchctrl() - .modify(|_, w| w.r#gen().variant(source.into())); - } - - /// Enable the [`Pclk`] - #[inline] - fn enable(&mut self) { - self.pchctrl().modify(|_, w| w.chen().set_bit()); + #[hal_macro_helper] + fn enable(&mut self, source: DynPclkSourceId) { + self.ctrl().write(|w| { + w.r#gen().variant(source.into()); + #[hal_cfg(any("clock-d11", "clock-d21"))] + { + w.clken().set_bit(); + unsafe { w.id().bits(P::DYN as u8) } + } + #[hal_cfg("clock-d5x")] + w.chen().set_bit() + }); } /// Disable the [`Pclk`] #[inline] + #[hal_macro_helper] fn disable(&mut self) { - self.pchctrl().modify(|_, w| w.chen().clear_bit()); + self.ctrl().modify(|_, w| { + #[hal_cfg(any("clock-d11", "clock-d21"))] + { + w.clken().clear_bit(); + unsafe { w.id().bits(P::DYN as u8) } + } + #[hal_cfg("clock-d5x")] + w.chen().clear_bit() + }); } } @@ -157,22 +189,43 @@ impl PclkToken

{ pub mod ids { use atsamd_hal_macros::hal_cfg; - pub use crate::sercom::{Sercom0, Sercom1, Sercom2, Sercom3, Sercom4, Sercom5}; + pub use crate::sercom::{Sercom0, Sercom1}; + #[hal_cfg("sercom2")] + pub use crate::sercom::Sercom2; + #[hal_cfg("sercom3")] + pub use crate::sercom::Sercom3; + #[hal_cfg("sercom4")] + pub use crate::sercom::Sercom4; + #[hal_cfg("sercom5")] + pub use crate::sercom::Sercom5; #[hal_cfg("sercom6")] pub use crate::sercom::Sercom6; #[hal_cfg("sercom7")] pub use crate::sercom::Sercom7; pub use super::super::dfll::DfllId; - pub use super::super::dpll::{Dpll0Id, Dpll1Id}; + pub use super::super::dpll::Dpll0Id; + #[hal_cfg("clock-d5x")] + pub use super::super::dpll::Dpll1Id; + // TODO crude hack + #[hal_cfg("clock-d5x")] pub use super::super::types::{ Ac, Adc0, Adc1, CM4Trace, Ccl, Dac, Eic, EvSys0, EvSys1, EvSys2, EvSys3, EvSys4, EvSys5, EvSys6, EvSys7, EvSys8, EvSys9, EvSys10, EvSys11, FreqMMeasure, FreqMReference, PDec, - Sdhc0, SlowClk, Tc0Tc1, Tc2Tc3, Tcc0Tcc1, Tcc2Tcc3, Usb, + Sdhc0, SlowClk, Tc0Tc1, Tc2Tc3, Usb, }; + #[hal_cfg(any("clock-d11", "clock-d21"))] + pub use super::super::types::{ + Ac, AcAna, AcDig, Adc0, Dac, Eic, EvSys0, EvSys1, EvSys2, EvSys3, EvSys4, EvSys5, EvSys6, + EvSys7, EvSys8, EvSys9, EvSys10, EvSys11, Ptc, Rtc, SercomSlow, SlowClk, Usb, Wdt, + }; + + #[hal_cfg("clock-d11")] + pub use super::super::types::{Tc1Tc2, Tcc0}; + #[hal_cfg("can0")] pub use super::super::types::Can0; #[hal_cfg("can1")] @@ -183,12 +236,21 @@ pub mod ids { pub use super::super::types::Tc4Tc5; #[hal_cfg(all("tc6", "tc7"))] pub use super::super::types::Tc6Tc7; + #[hal_cfg(all("tcc0", "tcc1"))] + pub use super::super::types::Tcc0Tcc1; + #[hal_cfg(all("tcc2", "tcc3"))] + pub use super::super::types::Tcc2Tcc3; #[hal_cfg("tcc4")] pub use super::super::types::Tcc4; + + // TODO would it make sense to just pub use super::super::types::* and + // conditionally restrict what types we make there? + #[hal_cfg(all("tcc2", "tc3-d21"))] + pub use super::super::types::Tcc2Tc3; + #[hal_cfg("i2s")] pub use super::super::types::{I2S0, I2S1}; } - use ids::*; /// Append the list of all [`PclkId`] types and `snake_case` id names to the @@ -225,6 +287,7 @@ use ids::*; /// /// with_pclk_types_ids!(some_macro!(first, second)); /// ``` +#[hal_cfg("clock-d5x")] #[hal_macro_helper] macro_rules! with_pclk_types_ids { ( $some_macro:ident ! ( $( $args:tt )* ) ) => { @@ -261,6 +324,7 @@ macro_rules! with_pclk_types_ids { (Can0 = 27, can0) #[hal_cfg("can1")] (Can1 = 28, can1) + #[hal_cfg(all("tcc2", "tcc3"))] (Tcc2Tcc3 = 29, tcc2_tcc3) #[hal_cfg(all("tc4", "tc5"))] (Tc4Tc5 = 30, tc4_tc5) @@ -292,6 +356,95 @@ macro_rules! with_pclk_types_ids { }; } +#[hal_cfg("clock-d21")] +#[hal_macro_helper] +macro_rules! with_pclk_types_ids { + ( $some_macro:ident ! ( $( $args:tt )* ) ) => { + $some_macro!( + $( $args )* + (DfllId = 0, dfll) + (Dpll0Id = 1, dpll) + (SlowClk = 2, slow) + (Wdt = 3, wdt) + (Rtc = 4, rtc) + (Eic = 5, eic) + (Usb = 6, usb) + (EvSys0 = 7, ev_sys0) + (EvSys1 = 8, ev_sys1) + (EvSys2 = 9, ev_sys2) + (EvSys3 = 10, ev_sys3) + (EvSys4 = 11, ev_sys4) + (EvSys5 = 12, ev_sys5) + (EvSys6 = 13, ev_sys6) + (EvSys7 = 14, ev_sys7) + (EvSys8 = 15, ev_sys8) + (EvSys9 = 16, ev_sys9) + (EvSys10 = 17, ev_sys10) + (EvSys11 = 18, ev_sys11) + (SercomSlow = 19, sercom_slow) + (Sercom0 = 20, sercom0) + (Sercom1 = 21, sercom1) + #[hal_cfg("sercom2")] + (Sercom2 = 22, sercom2) + #[hal_cfg("sercom3")] + (Sercom3 = 23, sercom3) + #[hal_cfg("sercom4")] + (Sercom4 = 24, sercom4) + #[hal_cfg("sercom5")] + (Sercom5 = 25, sercom5) + (Tcc0Tcc1 = 26, tcc0_tcc1) + (Tcc2Tc3 = 27, tcc2_tc3) + (Tc4Tc5 = 28, tc4_tc5) + #[hal_cfg(all("tc6", "tc7"))] + (Tc6Tc7 = 29, tc6_tc7) + (Adc0 = 30, adc) + (AcDig = 31, ac_dig) + (AcAna = 32, ac_ana) + (Dac = 33, dac) + (Ptc = 34, ptc) + #[hal_cfg("i2s")] + (I2S0 = 35, i2s0) + #[hal_cfg("i2s")] + (I2S1 = 36, i2s1) + ); + }; +} + +#[hal_cfg("clock-d11")] +#[hal_macro_helper] +macro_rules! with_pclk_types_ids { + ( $some_macro:ident ! ( $( $args:tt )* ) ) => { + $some_macro!( + $( $args )* + (DfllId = 0, dfll) + (Dpll0Id = 1, dpll) + (SlowClk = 2, slow) + (Wdt = 3, wdt) + (Rtc = 4, rtc) + (Eic = 5, eic) + (Usb = 6, usb) + (EvSys0 = 7, ev_sys0) + (EvSys1 = 8, ev_sys1) + (EvSys2 = 9, ev_sys2) + (EvSys3 = 10, ev_sys3) + (EvSys4 = 11, ev_sys4) + (EvSys5 = 12, ev_sys5) + (SercomSlow = 13, sercom_slow) + (Sercom0 = 14, sercom0) + (Sercom1 = 15, sercom1) + #[hal_cfg("sercom2")] + (Sercom2 = 16, sercom2) + (Tcc0 = 17, tcc0) + (Tc1Tc2 = 18, tc1_tc2) + (Adc0 = 19, adc) + (AcDig = 20, ac_dig) + (AcAna = 21, ac_ana) + (Dac = 22, dac) + (Ptc = 23, ptc) + ); + }; +} + //============================================================================== // DynPclkId //============================================================================== @@ -368,18 +521,54 @@ pub trait PclkId: Sealed { pub type DynPclkSourceId = DynGclkId; /// Convert from [`DynPclkSourceId`] to the equivalent [PAC](crate::pac) type +#[hal_macro_helper] impl From for Genselect { fn from(source: DynPclkSourceId) -> Self { - seq!(N in 0..=11 { - match source { - #( - DynGclkId::Gclk~N => Genselect::Gclk~N, - )* - } - }) + match source { + DynPclkSourceId::Gclk0 => Genselect::Gclk0, + DynPclkSourceId::Gclk1 => Genselect::Gclk1, + DynPclkSourceId::Gclk2 => Genselect::Gclk2, + DynPclkSourceId::Gclk3 => Genselect::Gclk3, + DynPclkSourceId::Gclk4 => Genselect::Gclk4, + DynPclkSourceId::Gclk5 => Genselect::Gclk5, + #[hal_cfg("gclk6")] + DynPclkSourceId::Gclk6 => Genselect::Gclk6, + #[hal_cfg("gclk7")] + DynPclkSourceId::Gclk7 => Genselect::Gclk7, + #[hal_cfg("gclk8")] + DynPclkSourceId::Gclk8 => Genselect::Gclk8, + #[hal_cfg("gclk9")] + DynPclkSourceId::Gclk9 => Genselect::Gclk9, + #[hal_cfg("gclk10")] + DynPclkSourceId::Gclk10 => Genselect::Gclk10, + #[hal_cfg("gclk11")] + DynPclkSourceId::Gclk11 => Genselect::Gclk11, + } + } +} + +impl PclkSourceId for DynPclkSourceId { + fn source_id(&self) -> DynGclkId { + *self } } +impl Sealed for DynPclkSourceId {} + +//============================================================================== +// PclkSourceId +//============================================================================== + +pub struct PclkSource { + _src: PhantomData, +} + +impl GclkId for PclkSource { + const DYN: DynGclkId = G::DYN; + const NUM: usize = G::NUM; + type Divider = G::Divider; +} + //============================================================================== // PclkSourceId //============================================================================== @@ -399,9 +588,19 @@ impl From for Genselect { /// [`Gclk`]: super::gclk::Gclk /// [type-level programming]: crate::typelevel /// [type-level enums]: crate::typelevel#type-level-enums -pub trait PclkSourceId: GclkId {} +pub trait PclkSourceId: Sealed { + fn source_id(&self) -> DynGclkId; +} -impl PclkSourceId for G {} +// PclkSource implements PclkSourceId via its GclkId implementation +impl PclkSourceId for G { + #[inline] + fn source_id(&self) -> DynGclkId { + Self::DYN + } +} + +impl Sealed for PclkSource {} //============================================================================== // Pclk @@ -439,24 +638,41 @@ where I: PclkSourceId, { token: PclkToken

, - src: PhantomData, + src: I, freq: Hertz, } -impl Pclk +/// [`Pclk`] with a dynamic source ID ([`DynPclkSourceId`]). +pub type DynPclk

= Pclk; + +impl From>> for DynPclk

where P: PclkId, - I: PclkSourceId, + G: GclkId, +{ + fn from(value: Pclk>) -> Self { + Pclk { + token: value.token, + freq: value.freq, + src: G::DYN, + } + } +} + +impl Pclk> +where + P: PclkId, + G: GclkId, { pub(super) fn new(token: PclkToken

, freq: Hertz) -> Self { Self { token, - src: PhantomData, + src: PclkSource { _src: PhantomData }, freq, } } - /// Create and enable a [`Pclk`] + /// Create and enable a [`Pclk`] with a type-checked source ID. /// /// Creating a [`Pclk`] immediately enables the corresponding peripheral /// channel clock. It also [`Increment`]s the [`Source`]'s [`Enabled`] @@ -469,16 +685,15 @@ where #[inline] pub fn enable(mut token: PclkToken

, gclk: S) -> (Self, S::Inc) where - S: Source + Increment, + S: Source + Increment, { let freq = gclk.freq(); - token.set_source(I::DYN); - token.enable(); - let pclk = Pclk::new(token, freq); + token.enable(G::DYN); + let pclk = Self::new(token, freq); (pclk, gclk.inc()) } - /// Disable and destroy a [`Pclk`] + /// Disable and destroy a [`Pclk`]. /// /// Consume the [`Pclk`], release the [`PclkToken`], and [`Decrement`] the /// [`EnabledGclk`]'s counter @@ -488,12 +703,87 @@ where #[inline] pub fn disable(mut self, gclk: S) -> (PclkToken

, S::Dec) where - S: Source + Decrement, + S: Source + Decrement, + { + self.token.disable(); + (self.token, gclk.dec()) + } +} + +impl

DynPclk

+where + P: PclkId, +{ + pub(super) fn new(token: PclkToken

, freq: Hertz) -> Self { + Self { + token, + src: G::DYN, + freq, + } + } + + /// Create and enable a [`Pclk`] with an underlying [`DynPclkSourceId`] + /// source ID type. + /// + /// Some peripherals require a dynamic PCLK source ID type parameter; use + /// this method to create a [`Pclk`] where this type parameter is + /// type-erased. + /// + /// Creating a [`Pclk`] immediately enables the corresponding peripheral + /// channel clock. It also [`Increment`]s the [`Source`]'s [`Enabled`] + /// counter. + /// + /// Note that the [`Source`] will always be an [`EnabledGclk`]. + /// + /// [`Enabled`]: super::Enabled + /// [`EnabledGclk`]: super::gclk::EnabledGclk + #[inline] + pub fn enable_dyn(mut token: PclkToken

, gclk: S) -> (Self, S::Inc) + where + S: Source + Increment, + { + let freq = gclk.freq(); + token.enable(G::DYN); + let pclk = Self::new::(token, freq); + (pclk, gclk.inc()) + } + + /// Disable and destroy a [`Pclk`]. + /// + /// Consume the [`Pclk`], release the [`PclkToken`], and [`Decrement`] the + /// [`EnabledGclk`]'s counter. + /// + /// # Panics + /// + /// Panics if the [`Pclk`]'s underlying GCLK source ID does not match the ID + /// of the provided [`Source`]. + /// + /// [`Enabled`]: super::Enabled + /// [`EnabledGclk`]: super::gclk::EnabledGclk + #[inline] + pub fn disable(mut self, gclk: S) -> (PclkToken

, S::Dec) + where + S: Source + Decrement, { + // Make sure that we can only decrement the source we are actually using + assert_eq!( + G::DYN, + self.src, + "Expected GCLK ID {:?}, found {:?}", + G::DYN, + self.src + ); + self.token.disable(); (self.token, gclk.dec()) } +} +impl Pclk +where + P: PclkId, + I: PclkSourceId, +{ /// Return the [`Pclk`] frequency #[inline] pub fn freq(&self) -> Hertz { diff --git a/hal/src/clock/v2/reset_thumbv6m.rs b/hal/src/clock/v2/reset_thumbv6m.rs new file mode 100644 index 000000000000..a9831beadbcb --- /dev/null +++ b/hal/src/clock/v2/reset_thumbv6m.rs @@ -0,0 +1,190 @@ +//! This module is intentionally private. Its contents are publicly exported +//! from the `v2` module, which is where the corresponding documentation will +//! appear. + +use atsamd_hal_macros::hal_macro_helper; + +use typenum::U1; + +use crate::{ + clock::v2::pclk::PclkSource, + pac::{Gclk, Pm, Sysctrl}, +}; + +use super::*; + +/// Collection of low-level PAC structs +/// +/// This struct serves to guard access to the low-level PAC structs. It places +/// them behind an `unsafe` barrier. +/// +/// Normally, users trade the low-level PAC structs for the higher-level +/// `clock::v2` API. However, in some cases, the `clock::v2` API may not be +/// sufficient. In these cases, users can access the registers directly by +/// calling [`Pac::steal`] to recover the PAC structs. +pub struct Pac { + gclk: Gclk, + pm: Pm, + sysctrl: Sysctrl, +} + +impl Pac { + /// Escape hatch allowing to access low-level PAC structs + /// + /// Consume the [`Pac`] and return the low-level PAC structs. This is + /// useful when the `clock::v2` API does not provide a necessary feature, or + /// when dealing with the legacy `clock::v1` API. + /// + /// # Safety + /// + /// Directly configuring clocks through the PAC API can invalidate the + /// type-level guarantees of the `clock` module API. + pub unsafe fn steal(self) -> (Gclk, Pm, Sysctrl) { + (self.gclk, self.pm, self.sysctrl) + } +} + +/// Bus clock objects +/// +/// This type is constructed using the [`clock_system_at_reset`] function, which +/// consumes the PAC-level clocking structs and returns the HAL-level clocking +/// structs in their reset state. +/// +/// This type contains the [bus clocks](super#bus-clocks), which are a necessary +/// to implement memory safety for the [`AhbClk`]s and [`ApbClk`]s. +/// +/// [`AhbClk`]: super::ahb::AhbClk +/// [`ApbClk`]: super::apb::ApbClk +pub struct Buses { + pub ahb: ahb::Ahb, + pub apb: apb::Apb, +} + +pub struct OscUlpClocks { + pub base: osculp32k::EnabledOscUlp32kBase, + pub osculp1k: osculp32k::EnabledOscUlp1k, + pub osculp32k: osculp32k::EnabledOscUlp32k, +} + +/// Enabled clocks at power-on reset +/// +/// This type is constructed using the [`clock_system_at_reset`] function, which +/// consumes the PAC-level clocking structs and returns the HAL-level clocking +/// structs in their reset state. +/// +/// This type represents the clocks as they are configured at power-on reset. +/// The main clock, [`Gclk0`](gclk::Gclk0), runs at 48 MHz using the +/// [`Dfll`](dfll::Dfll) in open-loop mode. The ultra-low power +/// [base oscillator](osculp32k::OscUlp32kBase) is also enabled and running, as +/// it can never be disabled. +/// +/// As described in the [top-level](super::super) documentation for the `clock` +/// module, only [`Enabled`] clocks can be used as a [`Source`] for downstream +/// clocks. This struct contains all of the `Enabled` clocks at reset. +/// +/// This struct also contains the [`Pac`] wrapper struct, which provides +/// `unsafe` access to the low-level PAC structs. +pub struct Clocks { + /// Wrapper providing `unsafe` access to low-level PAC structs + pub pac: Pac, + /// Enabled AHB clocks + pub ahbs: ahb::AhbClks, + /// Enabled APB clocks + pub apbs: apb::ApbClks, + /// Main system clock, driven at 1 MHz by the OSC8M divided by 8 + pub gclk0: Enabled, U1>, + /// GCLK2, driven at 32 kHz by the OSCULP + pub gclk2: Enabled, U1>, + /// 8 MHz internal oscillator, divided by 8 for a 1 MHz output + pub osc: Enabled, + /// Always-enabled OSCULP oscillators + pub osculp: OscUlpClocks, + /// [`Pclk`](pclk::Pclk) for the watchdog timer, sourced from [`Gclk2`](gclk::Gclk2) + pub wdt: pclk::Pclk>, +} + +/// Type-level tokens for unused clocks at power-on reset +/// +/// This type is constructed using the [`clock_system_at_reset`] function, which +/// consumes the PAC-level clocking structs and returns the HAL-level clocking +/// structs in their reset state. +/// +/// As described in the [top-level](super::super) documentation for the `clock` +/// module, token types are used to guanrantee the uniqueness of each clock. To +/// configure or enable a clock, you must provide the corresponding token. +#[hal_macro_helper] +pub struct Tokens { + /// Tokens to create [`apb::ApbClk`]s + pub apbs: apb::ApbTokens, + /// Token to create [`dfll::Dfll`] + pub dfll: dfll::DfllToken, + /// Token to create [`dpll::Dpll0`] + pub dpll: dpll::DpllToken, + /// Tokens to create [`gclk::Gclk`] + pub gclks: gclk::GclkTokens, + /// Tokens to create [`pclk::Pclk`]s + pub pclks: pclk::PclkTokens, + /// Tokens to create the [`osc32k`] clocks + pub osc32k: osc32k::Osc32kTokens, + /// Tokens [`xosc::Xosc0`] + pub xosc: xosc::XoscToken, + /// Tokens to create [`xosc32k`] clocks + #[hal_cfg("xosc32k")] + pub xosc32k: xosc32k::Xosc32kTokens, +} + +/// Consume the PAC clocking structs and return a HAL-level representation of +/// the clocks at power-on reset +/// +/// This function consumes the [`Gclk`], [`Pm`], and [`Sysctrl``] PAC structs +/// and returns the [`Buses`], [`Clocks`] and [`Tokens`]. +/// +/// See the [module-level documentation](super) for more details. +#[inline] +#[hal_macro_helper] +pub fn clock_system_at_reset(gclk: Gclk, pm: Pm, sysctrl: Sysctrl) -> (Buses, Clocks, Tokens) { + // Safety: No bus, clock or token is instantiated more than once + unsafe { + let buses = Buses { + ahb: ahb::Ahb::new(), + apb: apb::Apb::new(), + }; + let pac = Pac { gclk, pm, sysctrl }; + let osc = Enabled::<_, U0>::new(osc::Osc::new(osc::OscToken::new())); + let (gclk0, osc) = gclk::Gclk0::from_source(gclk::GclkToken::new(), osc); + let gclk0 = Enabled::new(gclk0); + let base = Enabled::new(osculp32k::OscUlp32kBase::new()); + let osculp1k = Enabled::new(osculp32k::OscUlp1k::new()); + let osculp32k = Enabled::<_, U0>::new(osculp32k::OscUlp32k::new()); + let (gclk2, osculp32k) = gclk::Gclk2::from_source(gclk::GclkToken::new(), osculp32k); + let gclk2 = Enabled::new(gclk2); + let wdt = pclk::Pclk::<_, PclkSource<_>>::new(pclk::PclkToken::new(), gclk2.freq()); + let osculp = OscUlpClocks { + base, + osculp1k, + osculp32k, + }; + let clocks = Clocks { + pac, + ahbs: ahb::AhbClks::new(), + apbs: apb::ApbClks::new(), + gclk0, + gclk2, + osc, + wdt, + osculp, + }; + let tokens = Tokens { + apbs: apb::ApbTokens::new(), + dfll: dfll::DfllToken::new(), + dpll: dpll::DpllToken::new(), + gclks: gclk::GclkTokens::new(), + pclks: pclk::PclkTokens::new(), + osc32k: osc32k::Osc32kTokens::new(), + xosc: xosc::XoscToken::new(), + #[hal_cfg("xosc32k")] + xosc32k: xosc32k::Xosc32kTokens::new(), + }; + (buses, clocks, tokens) + } +} diff --git a/hal/src/peripherals/clock/d5x/v2/reset.rs b/hal/src/clock/v2/reset_thumbv7em.rs similarity index 91% rename from hal/src/peripherals/clock/d5x/v2/reset.rs rename to hal/src/clock/v2/reset_thumbv7em.rs index dda29b0e6479..acac05d7d86d 100644 --- a/hal/src/peripherals/clock/d5x/v2/reset.rs +++ b/hal/src/clock/v2/reset_thumbv7em.rs @@ -4,7 +4,7 @@ use typenum::U1; -use crate::pac::{Gclk, Mclk, Nvmctrl, Osc32kctrl, Oscctrl}; +use crate::pac::{Gclk, Mclk, Osc32kctrl, Oscctrl}; use super::*; @@ -57,6 +57,12 @@ pub struct Buses { pub apb: apb::Apb, } +pub struct OscUlpClocks { + pub base: osculp32k::EnabledOscUlp32kBase, + pub osculp1k: osculp32k::EnabledOscUlp1k, + pub osculp32k: osculp32k::EnabledOscUlp32k, +} + /// Enabled clocks at power-on reset /// /// This type is constructed using the [`clock_system_at_reset`] function, which @@ -88,7 +94,7 @@ pub struct Clocks { pub dfll: Enabled, /// Always-enabled base oscillator for the [`OscUlp1k`](osculp32k::OscUlp1k) /// and [`OscUlp32k`](osculp32k::OscUlp32k) clocks. - pub osculp32k_base: Enabled, + pub osculp: OscUlpClocks, } /// Type-level tokens for unused clocks at power-on reset @@ -120,8 +126,6 @@ pub struct Tokens { /// Tokens to create [`xosc32k::Xosc32kBase`], [`xosc32k::Xosc1k`] and /// [`xosc32k::Xosc32k`] pub xosc32k: xosc32k::Xosc32kTokens, - /// Tokens to create [`osculp32k::OscUlp1k`] and [`osculp32k::OscUlp32k`] - pub osculp32k: osculp32k::OscUlp32kTokens, } /// Consume the PAC clocking structs and return a HAL-level @@ -137,7 +141,6 @@ pub fn clock_system_at_reset( osc32kctrl: Osc32kctrl, gclk: Gclk, mclk: Mclk, - nvmctrl: &mut Nvmctrl, ) -> (Buses, Clocks, Tokens) { // Safety: No bus, clock or token is instantiated more than once unsafe { @@ -154,25 +157,32 @@ pub fn clock_system_at_reset( let dfll = Enabled::<_>::new(dfll::Dfll::open_loop(dfll::DfllToken::new())); let (gclk0, dfll) = gclk::Gclk0::from_source(gclk::GclkToken::new(), dfll); let gclk0 = Enabled::new(gclk0); + let base = Enabled::new(osculp32k::OscUlp32kBase::new()); + let osculp1k = Enabled::new(osculp32k::OscUlp1k::new()); + let osculp32k = Enabled::new(osculp32k::OscUlp32k::new()); + let osculp = OscUlpClocks { + base, + osculp1k, + osculp32k, + }; let clocks = Clocks { pac, ahbs: ahb::AhbClks::new(), apbs: apb::ApbClks::new(), gclk0, dfll, - osculp32k_base: osculp32k::OscUlp32kBase::new(), + osculp, }; let tokens = Tokens { apbs: apb::ApbTokens::new(), dpll0: dpll::DpllToken::new(), dpll1: dpll::DpllToken::new(), - gclks: gclk::GclkTokens::new(nvmctrl), + gclks: gclk::GclkTokens::new(), pclks: pclk::PclkTokens::new(), rtcosc: rtcosc::RtcOscToken::new(), xosc0: xosc::XoscToken::new(), xosc1: xosc::XoscToken::new(), xosc32k: xosc32k::Xosc32kTokens::new(), - osculp32k: osculp32k::OscUlp32kTokens::new(), }; (buses, clocks, tokens) } diff --git a/hal/src/peripherals/clock/d5x/v2/rtcosc.rs b/hal/src/clock/v2/rtcosc.rs similarity index 100% rename from hal/src/peripherals/clock/d5x/v2/rtcosc.rs rename to hal/src/clock/v2/rtcosc.rs index 7edd596ea319..a13b7376b678 100644 --- a/hal/src/peripherals/clock/d5x/v2/rtcosc.rs +++ b/hal/src/clock/v2/rtcosc.rs @@ -93,16 +93,16 @@ use core::marker::PhantomData; -use crate::pac::osc32kctrl::rtcctrl::Rtcselselect; -use crate::pac::osc32kctrl::Rtcctrl; use crate::pac::Osc32kctrl; +use crate::pac::osc32kctrl::Rtcctrl; +use crate::pac::osc32kctrl::rtcctrl::Rtcselselect; use crate::time::Hertz; use crate::typelevel::{Decrement, Increment}; +use super::Source; use super::osculp32k::{OscUlp1kId, OscUlp32kId}; use super::xosc32k::{Xosc1kId, Xosc32kId}; -use super::Source; //============================================================================== // RtcOscToken diff --git a/hal/src/clock/v2/types.rs b/hal/src/clock/v2/types.rs new file mode 100644 index 000000000000..93b8dc81e7eb --- /dev/null +++ b/hal/src/clock/v2/types.rs @@ -0,0 +1,213 @@ +//! Module defining or exporting peripheral types for the ['ahb'], ['apb'] and +//! ['pclk'] modules +//! +//! The `ahb`, `apb` and `pclk` modules each define structs that are +//! generic over a type parameter representing a peripheral. Some peripheral +//! modules already define suitable types for this purpose. For example, +//! [`sercom`] defines the [`Sercom0`], [`Sercom1`], etc. types. But other +//! peripherals are either not yet implemented in the HAL or do not define a +//! suitable type. This module defines a type for such peripherals. If/when a +//! suitable type is added for a given peripheral, the type defined here should +//! be deprecated or removed. +//! +//! [`ahb`]: super::ahb +//! [`apb`]: super::apb +//! [`pclk`]: super::pclk +//! [`sercom`]: crate::sercom +//! [`Sercom0`]: crate::sercom::Sercom0 +//! [`Sercom1`]: crate::sercom::Sercom1 + +use atsamd_hal_macros::hal_cfg; + +use crate::typelevel::Sealed; + +macro_rules! create_types { + ( + $( + $Type:ident + ),+ + ) => { + $( + /// Marker type representing the corresponding peripheral + /// + /// This type is defined by and used within the + /// [`clock`](super::super) module. See the the [`types`](super) + /// module documentation for more details. + pub enum $Type {} + impl Sealed for $Type {} + )+ + }; +} + +// AHB types +create_types!(Dmac); +create_types!(Dsu); +create_types!(Hpb0, Hpb1, Hpb2); +create_types!(NvmCtrl); +create_types!(Usb); + +// APB types +create_types!(Ac); +create_types!(Adc0); +create_types!(Dac); +create_types!(Eic); +create_types!(EvSys); +create_types!(Gclk); +#[hal_cfg("i2s")] +create_types!(I2S); +create_types!(Pac0); +create_types!(Pm); +create_types!(Port); +create_types!(Rtc); +#[hal_cfg("sercom0")] +pub use crate::sercom::Sercom0; +#[hal_cfg("sercom1")] +pub use crate::sercom::Sercom1; +#[hal_cfg("sercom2")] +pub use crate::sercom::Sercom2; +#[hal_cfg("sercom3")] +pub use crate::sercom::Sercom3; +#[hal_cfg("sercom4")] +pub use crate::sercom::Sercom4; +#[hal_cfg("sercom5")] +pub use crate::sercom::Sercom5; +#[hal_cfg("sercom6")] +pub use crate::sercom::Sercom6; +#[hal_cfg("sercom7")] +pub use crate::sercom::Sercom7; + +#[hal_cfg("tc0")] +create_types!(Tc0); +#[hal_cfg("tc1")] +create_types!(Tc1); +#[hal_cfg("tc2")] +create_types!(Tc2); +#[hal_cfg("tc3")] +create_types!(Tc3); +#[hal_cfg("tc4")] +create_types!(Tc4); +#[hal_cfg("tc5")] +create_types!(Tc5); +#[hal_cfg("tc6")] +create_types!(Tc6); +#[hal_cfg("tc7")] +create_types!(Tc7); + +#[hal_cfg("tcc0")] +create_types!(Tcc0); +#[hal_cfg("tcc1")] +create_types!(Tcc1); +#[hal_cfg("tcc2")] +create_types!(Tcc2); +#[hal_cfg("tcc3")] +create_types!(Tcc3); +#[hal_cfg("tcc4")] +create_types!(Tcc4); + +// TODO +#[hal_cfg(all("tc0", "tc1"))] +create_types!(Tc0Tc1); +#[hal_cfg(all("tc2", "tc3"))] +create_types!(Tc2Tc3); +#[hal_cfg(all("tc4", "tc5"))] +create_types!(Tc4Tc5); +#[hal_cfg(all("tc6", "tc7"))] +create_types!(Tc6Tc7); + +#[hal_cfg(all("tcc0", "tcc1"))] +create_types!(Tcc0Tcc1); +#[hal_cfg(all("tcc2", "tcc3"))] +create_types!(Tcc2Tcc3); + +#[hal_cfg(all("tc1-d11", "tc2-d11"))] +create_types!(Tc1Tc2); + +#[hal_cfg(all("tcc2", "tc3-d21"))] +create_types!(Tcc2Tc3); + +create_types!(Wdt); + +// PCLK types +// TODO set channel numbers in devices.yaml +create_types!( + EvSys0, EvSys1, EvSys2, EvSys3, EvSys4, EvSys5, EvSys6, EvSys7, EvSys8, EvSys9, EvSys10, + EvSys11 +); +#[hal_cfg("i2s")] +create_types!(I2S0, I2S1); +create_types!(SlowClk); + +#[hal_cfg("clock-d5x")] +mod variant_ahb_types { + use super::{Sealed, hal_cfg}; + + #[hal_cfg("can0")] + create_types!(Can0); + #[hal_cfg("can1")] + create_types!(Can1); + create_types!(Cmcc); + #[hal_cfg("gmac")] + create_types!(Gmac); + create_types!(Hpb3); + create_types!(Icm); + create_types!(NvmCtrlSmeeProm, NvmCtrlCache); + create_types!(Pukcc); + create_types!(Qspi, Qspi2x); + #[hal_cfg("sdhc0")] + create_types!(Sdhc0); + #[hal_cfg("sdhc1")] + create_types!(Sdhc1); +} + +#[hal_cfg("clock-d5x")] +mod variant_apb_types { + use super::Sealed; + + create_types!(Adc1); + create_types!(Aes); + create_types!(Ccl); + create_types!(FreqM); + create_types!(Mclk); + create_types!(OscCtrl); + create_types!(Osc32kCtrl); + create_types!(Pcc); + create_types!(PDec); + create_types!(RamEcc); + create_types!(RstC); + create_types!(SupC); + create_types!(Trng); +} + +#[hal_cfg("clock-d5x")] +mod variant_pclk_types { + use super::Sealed; + + create_types!(CM4Trace); + create_types!(FreqMMeasure); + create_types!(FreqMReference); +} + +#[hal_cfg(any("clock-d11", "clock-d21"))] +mod variant_apb_types { + use super::{Sealed, hal_cfg}; + + #[hal_cfg("clock-d21")] + create_types!(Ac1); + create_types!(Pac1, Pac2); + create_types!(Ptc); + create_types!(SysCtrl); +} + +#[hal_cfg(any("clock-d11", "clock-d21"))] +mod variant_pclk_types { + use super::Sealed; + + create_types!(AcDig); + create_types!(AcAna); + create_types!(SercomSlow); +} + +#[hal_cfg("clock-d5x")] +pub use variant_ahb_types::*; +pub use variant_apb_types::*; +pub use variant_pclk_types::*; diff --git a/hal/src/peripherals/clock/d5x/v2/xosc.rs b/hal/src/clock/v2/xosc.rs similarity index 81% rename from hal/src/peripherals/clock/d5x/v2/xosc.rs rename to hal/src/clock/v2/xosc.rs index 2169636ca691..49af41688882 100644 --- a/hal/src/peripherals/clock/d5x/v2/xosc.rs +++ b/hal/src/clock/v2/xosc.rs @@ -11,8 +11,8 @@ //! //! When used with an external clock, only one GPIO [`Pin`] is required, but //! when used with a crystal oscillator, two GPIO `Pin`s are required. The -//! [`XIn`] `Pin` is used in both `Mode`s, while the [`XOut`] `Pin` is only -//! used in [`CrystalMode`]. +//! [`XIn`] `Pin` is used in both `Mode`s, while the [`XOut`] `Pin` is only used +//! in [`CrystalMode`]. //! //! When operating in [`CrystalMode`], the XOSC peripheral provides several //! configuration options to increase stability or reduce power consumption of @@ -86,14 +86,15 @@ //! //! We start by calling [`Xosc::from_crystal`], and we provide the corresponding //! [`XIn`] and [`XOut`] [`Pin`]s, as well as the nominal crystal frequency. We -//! then set the [`CrystalCurrent`] level to `Medium`. The default current level -//! for a 20 MHz signal is actually `High`, but we opt for a lower current under -//! the assumption that our crystal's capacitive load is small. Next, we turn on -//! automatic loop control, which should save power, but we also set -//! `LOWBUFGAIN` to `1`. Counterintuitively, this actually _increases_ the -//! crystal amplitude, which increases power consumption, but it also improves -//! stability. We then apply a 488 μs start up delay, to allow the clock to -//! stabilize before it is applied to any logic. Finally, we enable the `Xosc`. +//! then set the `CrystalCurrent` level to `Medium` (supported only on some +//! targets). The default current level for a 20 MHz signal is actually `High`, +//! but we opt for a lower current under the assumption that our crystal's +//! capacitive load is small. Next, we turn on automatic loop control, which +//! should save power, but we also set `LOWBUFGAIN` to `1`. Counterintuitively, +//! this actually _increases_ the crystal amplitude, which increases power +//! consumption, but it also improves stability. We then apply a 488 μs start up +//! delay, to allow the clock to stabilize before it is applied to any logic. +//! Finally, we enable the `Xosc`. //! //! Next, we wait until the `Xosc` is stable and ready to be used as a clock //! [`Source`]. @@ -126,11 +127,11 @@ //! while !xosc.is_ready() {} //! ``` //! -//! Once the clock is stable, we can also enable failure detection. To do so, we -//! must provide the [`EnabledDfll`] to act as the backup safe clock. We can -//! also select a divider for the safe clock, so that it loosely matches the -//! `Xosc` frequency. In thise case, we divide the 48 MHz [`Dfll`] down to -//! 24 MHz, which is the closest option to 20 MHz. +//! Once the clock is stable, we can also enable failure detection on targets +//! that support it. To do so, we must provide the [`EnabledDfll`] to act as the +//! backup safe clock. We can also select a divider for the safe clock, so that +//! it loosely matches the `Xosc` frequency. In thise case, we divide the 48 MHz +//! [`Dfll`] down to 24 MHz, which is the closest option to 20 MHz. //! //! ```no_run //! # use atsamd_hal::{ @@ -204,17 +205,38 @@ //! [`Dfll`]: super::dfll::Dfll //! [`EnabledDfll`]: super::dfll::EnabledDfll +use atsamd_hal_macros::{hal_cfg, hal_macro_helper}; use core::marker::PhantomData; use typenum::U0; -use crate::pac::oscctrl::{self, Xoscctrl}; +#[hal_cfg("clock-d5x")] +mod imports { + pub use super::super::dfll::DfllId; + pub use crate::pac::Oscctrl as Peripheral; + pub use crate::pac::oscctrl::{Xoscctrl, status::R as STATUS_R}; + pub use crate::typelevel::{Decrement, Increment}; +} + +#[hal_cfg(any("clock-d11", "clock-d21"))] +mod imports { + pub use crate::pac::Sysctrl as Peripheral; + pub use crate::pac::sysctrl::{Xosc as Xoscctrl, pclksr::R as STATUS_R, xosc::Gainselect}; +} + +use imports::*; + +#[hal_cfg("clock-d11")] +pub use crate::gpio::{PA08 as XIn0Id, PA09 as XOut0Id}; +#[hal_cfg("clock-d21")] +pub use crate::gpio::{PA14 as XIn0Id, PA15 as XOut0Id}; +#[hal_cfg("clock-d5x")] +pub use crate::gpio::{PA14 as XIn0Id, PA15 as XOut0Id, PB22 as XIn1Id, PB23 as XOut1Id}; -use crate::gpio::{FloatingDisabled, Pin, PinId, PA14, PA15, PB22, PB23}; +use crate::gpio::{FloatingDisabled, Pin, PinId}; use crate::time::Hertz; -use crate::typelevel::{Decrement, Increment, Sealed}; +use crate::typelevel::Sealed; -use super::dfll::DfllId; use super::{Enabled, Source}; //============================================================================== @@ -252,20 +274,36 @@ impl XoscToken { /// Return a reference to the corresponding XOSCCTRL register #[inline] + #[hal_macro_helper] fn xoscctrl(&self) -> &Xoscctrl { // Safety: Each `XoscToken` only has access to a mutually exclusive set // of registers for the corresponding `XoscId`, and we use a shared // reference to the register block. See the notes on `Token` types and // memory safety in the root of the `clock` module for more details. - unsafe { (*crate::pac::Oscctrl::PTR).xoscctrl(X::NUM) } + #[hal_cfg("clock-d5x")] + unsafe { + (*Peripheral::PTR).xoscctrl(X::NUM) + } + #[hal_cfg(any("clock-d11", "clock-d21"))] + unsafe { + (*Peripheral::PTR).xosc() + } } /// Read the STATUS register #[inline] - fn status(&self) -> oscctrl::status::R { + #[hal_macro_helper] + fn status(&self) -> STATUS_R { // Safety: We are only reading from the `STATUS` register, so there is // no risk of memory corruption. - unsafe { (*crate::pac::Oscctrl::PTR).status().read() } + #[hal_cfg("clock-d5x")] + unsafe { + (*Peripheral::PTR).status().read() + } + #[hal_cfg(any("clock-d11", "clock-d21"))] + unsafe { + (*Peripheral::PTR).pclksr().read() + } } /// Check whether the XOSC is stable and ready @@ -276,6 +314,7 @@ impl XoscToken { } /// Check whether the XOSC has triggered failure detection + #[hal_cfg("clock-d5x")] #[inline] fn has_failed(&self) -> bool { let mask = 1 << (X::NUM + 2); @@ -283,27 +322,24 @@ impl XoscToken { } /// Check whether the XOSC has been switched to the safe clock + #[hal_cfg("clock-d5x")] #[inline] fn is_switched(&self) -> bool { let mask = 1 << (X::NUM + 4); self.status().bits() & mask != 0 } - /// Reset the XOSCCTRL register - #[inline] - fn reset(&self) { - self.xoscctrl().reset(); - } - /// Switch from the safe clock back to the XOSC clock/oscillator /// /// This bit is cleared by the hardware after successfully switching back + #[hal_cfg("clock-d5x")] #[inline] fn switch_back(&mut self) { self.xoscctrl().modify(|_, w| w.swben().set_bit()); } /// Enable clock failure detection and set the safe clock divider + #[hal_cfg("clock-d5x")] #[inline] fn enable_failure_detection(&mut self, div: SafeClockDiv) { // Safety: The divider is guaranteed to be in the valid range 0..16. @@ -317,6 +353,7 @@ impl XoscToken { } /// Disable clock failure detection + #[hal_cfg("clock-d5x")] #[inline] fn disable_failure_detection(&mut self) { self.xoscctrl().modify(|_, w| w.cfden().clear_bit()); @@ -324,32 +361,36 @@ impl XoscToken { /// Set most of the fields in the XOSCCTRL register #[inline] - fn set_xoscctrl(&mut self, settings: Settings) { - let xtalen = settings.mode == DynMode::CrystalMode; + #[hal_macro_helper] + fn enable(&mut self, mode: DynMode, settings: Settings) { + let xtalen = mode == DynMode::CrystalMode; // Safety: The `IMULT` and `IPTAT` values come from the // `CrystalCurrent`, so they are guaranteed to be valid. - self.xoscctrl().modify(|_, w| unsafe { + self.xoscctrl().write(|w| unsafe { w.startup().bits(settings.start_up as u8); - w.enalc().bit(settings.loop_control); - w.imult().bits(settings.current.imult()); - w.iptat().bits(settings.current.iptat()); - w.lowbufgain().bit(settings.low_buf_gain); + #[hal_cfg(any("clock-d11", "clock-d21"))] + { + w.ampgc().bit(settings.ampgc); + w.gain().variant(settings.gain.into()) + }; + #[hal_cfg("clock-d5x")] + { + w.enalc().bit(settings.loop_control); + w.imult().bits(settings.current.imult()); + w.iptat().bits(settings.current.iptat()); + w.lowbufgain().bit(settings.low_buf_gain); + }; w.ondemand().bit(settings.on_demand); w.runstdby().bit(settings.run_standby); - w.xtalen().bit(xtalen) + w.xtalen().bit(xtalen); + w.enable().set_bit() }); } - /// Enable the XOSC - #[inline] - fn enable(&mut self) { - self.xoscctrl().modify(|_, w| w.enable().set_bit()); - } - /// Disable the XOSC #[inline] fn disable(&mut self) { - self.xoscctrl().modify(|_, w| w.enable().clear_bit()); + self.xoscctrl().write(|w| w.enable().clear_bit()); } } @@ -362,6 +403,7 @@ impl XoscToken { // All of these fields are set in a single write to XOSCCTRL during the call to // [`Xosc::enable`]. The remaining fields are only modified after it has been // enabled. +#[hal_cfg("clock-d5x")] #[derive(Clone, Copy)] struct Settings { start_up: StartUpDelay, @@ -370,18 +412,54 @@ struct Settings { low_buf_gain: bool, on_demand: bool, run_standby: bool, - mode: DynMode, +} + +#[hal_cfg("clock-d5x")] +impl Default for Settings { + fn default() -> Self { + Settings { + start_up: StartUpDelay::default(), + loop_control: false, + current: CrystalCurrent::default(), + low_buf_gain: false, + on_demand: true, + run_standby: false, + } + } +} + +#[hal_cfg(any("clock-d11", "clock-d21"))] +#[derive(Clone, Copy)] +struct Settings { + start_up: StartUpDelay, + ampgc: bool, + gain: Gain, + on_demand: bool, + run_standby: bool, +} + +#[hal_cfg(any("clock-d11", "clock-d21"))] +impl Default for Settings { + fn default() -> Self { + Settings { + start_up: StartUpDelay::default(), + ampgc: false, + gain: Gain::default(), + on_demand: true, + run_standby: false, + } + } } //============================================================================== // XoscId //============================================================================== -/// Type-level enum identifying one of two possible [`Xosc`]s +/// Type-level enum identifying [`Xosc`]s /// -/// The types implementing this trait, i.e. [`Xosc0Id`] and [`Xosc1Id`], are -/// type-level variants of `XoscId`, and they identify one of two possible -/// external crystal oscillators. +/// The types implementing this trait, i.e. [`Xosc0Id`] and `Xosc1Id` on +/// supporting targets, are type-level variants of `XoscId`, and they identify +/// one of two possible external crystal oscillators. /// /// See the documentation on [type-level programming] and specifically /// [type-level enums] for more details. @@ -410,8 +488,8 @@ impl Sealed for Xosc0Id {} impl XoscId for Xosc0Id { const NUM: usize = 0; - type XIn = PA14; - type XOut = PA15; + type XIn = XIn0Id; + type XOut = XOut0Id; } /// Type-level variant of [`XoscId`] representing the identity of XOSC1 @@ -421,14 +499,17 @@ impl XoscId for Xosc0Id { /// /// [type-level programming]: crate::typelevel /// [type-level enums]: crate::typelevel#type-level-enums +#[hal_cfg("clock-d5x")] pub enum Xosc1Id {} +#[hal_cfg("clock-d5x")] impl Sealed for Xosc1Id {} +#[hal_cfg("clock-d5x")] impl XoscId for Xosc1Id { const NUM: usize = 1; - type XIn = PB22; - type XOut = PB23; + type XIn = XIn1Id; + type XOut = XOut1Id; } //============================================================================== @@ -526,6 +607,7 @@ pub enum StartUpDelay { /// each frequency range, it also acknowledges some flexibility in that choice. /// Specifically, it notes that users can save power by selecting the next-lower /// frequency range if the capacitive load is small. +#[hal_cfg("clock-d5x")] #[derive(Clone, Copy, Default, PartialEq, Eq)] pub enum CrystalCurrent { /// Used only in [`ClockMode`] to represent the default register values @@ -541,6 +623,7 @@ pub enum CrystalCurrent { ExtraHigh, } +#[hal_cfg("clock-d5x")] impl CrystalCurrent { #[inline] fn imult(&self) -> u8 { @@ -565,6 +648,34 @@ impl CrystalCurrent { } } +//============================================================================== +// Gain +//============================================================================== + +#[hal_cfg(any("clock-d11", "clock-d21"))] +#[derive(Clone, Copy, Default, PartialEq, Eq)] +pub enum Gain { + #[default] + Zero, + One, + Two, + Three, + Four, +} + +#[hal_cfg(any("clock-d11", "clock-d21"))] +impl From for Gainselect { + fn from(gain: Gain) -> Self { + match gain { + Gain::Zero => Gainselect::_0, + Gain::One => Gainselect::_1, + Gain::Two => Gainselect::_2, + Gain::Three => Gainselect::_3, + Gain::Four => Gainselect::_4, + } + } +} + //============================================================================== // DynMode //============================================================================== @@ -660,8 +771,8 @@ impl Mode for CrystalMode { /// oscillator and delivers the resulting clock to the rest of the clock system. /// /// The type parameter `X` is a [`XoscId`] that determines which of the two -/// instances this `Xosc` represents ([`Xosc0`] or [`Xosc1`]). The type -/// parameter `M` represents the operating [`Mode`], either [`ClockMode`] or +/// instances this `Xosc` represents ([`Xosc0`] or `Xosc1`). The type parameter +/// `M` represents the operating [`Mode`], either [`ClockMode`] or /// [`CrystalMode`]. /// /// On its own, an instance of `Xosc` does not represent an enabled XOSC. @@ -692,6 +803,7 @@ where pub type Xosc0 = Xosc; /// Type alias for the corresponding [`Xosc`] +#[hal_cfg("clock-d5x")] pub type Xosc1 = Xosc; /// An [`Enabled`] [`Xosc`] @@ -709,6 +821,7 @@ pub type EnabledXosc = Enabled, N>; pub type EnabledXosc0 = EnabledXosc; /// Type alias for the corresponding [`EnabledXosc`] +#[hal_cfg("clock-d5x")] pub type EnabledXosc1 = EnabledXosc; impl Xosc { @@ -768,6 +881,7 @@ impl Xosc { } /// Set the [`CrystalCurrent`] drive strength + #[hal_cfg("clock-d5x")] #[inline] pub fn current(mut self, current: CrystalCurrent) -> Self { self.settings.current = current; @@ -778,6 +892,7 @@ impl Xosc { /// /// If enabled, the hardware will automatically adjust the oscillator /// amplitude. In most cases, this will lower power consumption. + #[hal_cfg("clock-d5x")] #[inline] pub fn loop_control(mut self, loop_control: bool) -> Self { self.settings.loop_control = loop_control; @@ -790,11 +905,26 @@ impl Xosc { /// loop control is enabled, setting the `LOWBUFGAIN` field to `1` will /// _increase_ the oscillator amplitude by a factor of appoximately 2. This /// can help solve stability issues. + #[hal_cfg("clock-d5x")] #[inline] pub fn low_buf_gain(mut self, low_buf_gain: bool) -> Self { self.settings.low_buf_gain = low_buf_gain; self } + + #[hal_cfg(any("clock-d11", "clock-d21"))] + #[inline] + pub fn amplitude_gain_control(mut self, enabled: bool) -> Self { + self.settings.ampgc = enabled; + self + } + + #[hal_cfg(any("clock-d11", "clock-d21"))] + #[inline] + pub fn gain(mut self, gain: Gain) -> Self { + self.settings.gain = gain; + self + } } impl Xosc @@ -802,28 +932,19 @@ where X: XoscId, M: Mode, { + #[hal_cfg("clock-d5x")] #[inline] fn new(token: XoscToken, pins: M::Pins, freq: Hertz) -> Self { - let current = match freq.to_Hz() { - 8_000_000 => CrystalCurrent::Low, - 8_000_001..=16_000_000 => CrystalCurrent::Medium, - 16_000_001..=24_000_000 => CrystalCurrent::High, - 24_000_001..=48_000_000 => CrystalCurrent::ExtraHigh, - _ => panic!("The XOSC input frequency must be 8-48 MHz"), - }; - let current = match M::DYN { - DynMode::ClockMode => CrystalCurrent::Zero, - DynMode::CrystalMode => current, - }; - let settings = Settings { - start_up: StartUpDelay::Delay31us, - loop_control: false, - current, - low_buf_gain: false, - on_demand: true, - run_standby: false, - mode: M::DYN, - }; + let mut settings = Settings::default(); + if M::DYN == DynMode::CrystalMode { + settings.current = match freq.to_Hz() { + 8_000_000 => CrystalCurrent::Low, + 8_000_001..=16_000_000 => CrystalCurrent::Medium, + 16_000_001..=24_000_000 => CrystalCurrent::High, + 24_000_001..=48_000_000 => CrystalCurrent::ExtraHigh, + _ => panic!("The XOSC input frequency must be 8-48 MHz"), + }; + } Self { token, pins, @@ -832,6 +953,17 @@ where } } + #[hal_cfg(any("clock-d11", "clock-d21"))] + #[inline] + fn new(token: XoscToken, pins: M::Pins, freq: Hertz) -> Self { + Self { + token, + pins, + freq, + settings: Settings::default(), + } + } + /// Return the clock or crystal frequency #[inline] pub fn freq(&self) -> Hertz { @@ -885,9 +1017,7 @@ where /// [`Source`] for other clocks. #[inline] pub fn enable(mut self) -> EnabledXosc { - self.token.reset(); - self.token.set_xoscctrl(self.settings); - self.token.enable(); + self.token.enable(M::DYN, self.settings); Enabled::new(self) } } @@ -908,6 +1038,7 @@ where } } +#[hal_macro_helper] impl EnabledXosc where X: XoscId, @@ -920,6 +1051,25 @@ where self.0.token.is_ready() } + /// XOSC failure detection is not supported by the currently-documented + /// target + #[cfg(doc)] + #[hal_cfg(not("clock-d5x"))] + pub fn has_failed(&self) {} + + /// XOSC failure detection is not supported by the currently-documented + /// target + #[cfg(doc)] + #[hal_cfg(not("clock-d5x"))] + pub fn switch_back(&self) {} +} + +#[hal_cfg("clock-d5x")] +impl EnabledXosc +where + X: XoscId, + M: Mode, +{ /// Enable continuous monitoring of the [`Xosc`] for clock failure /// /// Failure detection will continuously monitor the [`Xosc`] to verify it is diff --git a/hal/src/peripherals/clock/d5x/v2/xosc32k.rs b/hal/src/clock/v2/xosc32k.rs similarity index 89% rename from hal/src/peripherals/clock/d5x/v2/xosc32k.rs rename to hal/src/clock/v2/xosc32k.rs index 838d02b1cbf8..4b574121cd7a 100644 --- a/hal/src/peripherals/clock/d5x/v2/xosc32k.rs +++ b/hal/src/clock/v2/xosc32k.rs @@ -33,20 +33,20 @@ //! `ENABLE` bit. The call to [`Xosc32kBase::enable`] returns a 1:N [`Enabled`] //! clock [`Source`], which can be consumed by both the [`Xosc32k`] and //! [`Xosc1k`] clocks. Enabling either of these two clocks will [`Increment`] -//! the [`EnabledXosc32kBase`] counter, preventing it from being disabled. -//! Note that `Xosc32k` and `Xosc1k` are themselves 1:N clocks as well. +//! the [`EnabledXosc32kBase`] counter, preventing it from being disabled. Note +//! that `Xosc32k` and `Xosc1k` are themselves 1:N clocks as well. //! //! ## Clock failure detection and write lock //! -//! Like the [`Xosc`] clocks, the XOSC32K peripheral also has clock failure -//! detection. However, unlike the `XOSCCTRL` registers, the `XOSC32K` register -//! has a dedicated write lock bit that will freeze its configuration until the -//! next power-on reset. +//! The XOSC32K peripheral on some microcontrollers supports Clock Failure +//! Detection (CFD), like the [`Xosc`] clocks. However, unlike the `XOSCCTRL` +//! registers, the `XOSC32K` register has a dedicated write lock bit that will +//! freeze its configuration until the next power-on reset. //! //! While `Xosc` clock failure detection is configured directly in the -//! `XOSCCTRL` register, the XOSC32K peripheral has a separate, dedicated -//! clock failure detection register (`Cfdctrl`). This difference likely exists -//! to provide control of clock failure detection *after* write lock has been +//! `XOSCCTRL` register, the XOSC32K peripheral has a separate, dedicated clock +//! failure detection register (`Cfdctrl`). This difference likely exists to +//! provide control of clock failure detection *after* write lock has been //! enabled. //! //! In this module, write lock is implemented by simply dropping the @@ -71,7 +71,6 @@ //! osculp32k::OscUlp32k, //! xosc32k::{ //! ControlGainMode, SafeClockDiv, StartUpDelay, Xosc1k, Xosc32k, Xosc32kBase, -//! Xosc32kCfd, //! }, //! }, //! gpio::Pins, @@ -91,8 +90,8 @@ //! Next, we create the [`Xosc32kBase`] clock from a 32 kHz oscillator using its //! corresponding [`Xosc32kBaseToken`] and the [`XIn32`] and [`XOut32`] `Pin`s. //! We then set the delay before the clock is unmasked by providing a desired -//! [`StartUpDelay`]. Finally, we select a [`ControlGainMode`] for the crystal -//! before enabling it. +//! [`StartUpDelay`]. Finally, on supported targets, we select a +//! `ControlGainMode` for the crystal before enabling it. //! //! ```no_run //! # use atsamd_hal::{ @@ -101,7 +100,6 @@ //! # osculp32k::OscUlp32k, //! # xosc32k::{ //! # ControlGainMode, SafeClockDiv, StartUpDelay, Xosc1k, Xosc32k, Xosc32kBase, -//! # Xosc32kCfd, //! # }, //! # }, //! # gpio::Pins, @@ -118,6 +116,7 @@ //! # ); //! let xosc32k_base = Xosc32kBase::from_crystal(tokens.xosc32k.base, pins.pa00, pins.pa01) //! .start_up_delay(StartUpDelay::Delay1s) +//! // n.b. only some targets support this setting //! .control_gain_mode(ControlGainMode::HighSpeed) //! .enable(); //! ``` @@ -132,7 +131,6 @@ //! # osculp32k::OscUlp32k, //! # xosc32k::{ //! # ControlGainMode, SafeClockDiv, StartUpDelay, Xosc1k, Xosc32k, Xosc32kBase, -//! # Xosc32kCfd, //! # }, //! # }, //! # gpio::Pins, @@ -155,10 +153,10 @@ //! ``` //! //! With the [`EnabledXosc32kBase`] clock in hand, we can enable the [`Xosc1k`] -//! and [`Xosc32k`], each of which [`Increment`]s the [`Enabled`] counter. -//! Once we are satisfied with the configuration, we can call `write_lock` to -//! lock the XOSC32K configuration at the hardware level. Doing so also consumes -//! the `EnabledXosc32kBase` clock, which eliminates any ability to change the +//! and [`Xosc32k`], each of which [`Increment`]s the [`Enabled`] counter. Once +//! we are satisfied with the configuration, we can call `write_lock` to lock +//! the XOSC32K configuration at the hardware level. Doing so also consumes the +//! `EnabledXosc32kBase` clock, which eliminates any ability to change the //! configuration at the API level. //! //! ```no_run @@ -168,7 +166,6 @@ //! # osculp32k::OscUlp32k, //! # xosc32k::{ //! # ControlGainMode, SafeClockDiv, StartUpDelay, Xosc1k, Xosc32k, Xosc32kBase, -//! # Xosc32kCfd, //! # }, //! # }, //! # gpio::Pins, @@ -341,17 +338,34 @@ //! [`is_switched`]: Xosc32kCfd::is_switched //! [`switch_back`]: Xosc32kCfd::switch_back +use atsamd_hal_macros::{hal_cfg, hal_macro_helper}; use fugit::RateExtU32; use typenum::U0; -use crate::pac::osc32kctrl::xosc32k::{Cgmselect, Startupselect}; -use crate::pac::osc32kctrl::{self, status, Cfdctrl}; - -use crate::gpio::{FloatingDisabled, Pin, PA00, PA01}; +#[hal_cfg("osc32kctrl")] +use crate::{ + clock::v2::osculp32k::OscUlp32kId, + pac::{ + Osc32kctrl as PERIPHERAL, + osc32kctrl::{Cfdctrl, Xosc32k as PacXosc32k, status::R as STATUS_R, xosc32k::Cgmselect}, + }, +}; + +#[hal_cfg("sysctrl")] +use crate::pac::{ + Sysctrl as PERIPHERAL, + sysctrl::{Xosc32k as PacXosc32k, pclksr::R as STATUS_R}, +}; + +#[hal_cfg(any("port-d21", "port-d5x"))] +pub use crate::gpio::{PA00 as XIn32Id, PA01 as XOut32Id}; +#[hal_cfg("port-d11")] +pub use crate::gpio::{PA08 as XIn32Id, PA09 as XOut32Id}; + +use crate::gpio::{FloatingDisabled, Pin}; use crate::time::Hertz; use crate::typelevel::{Decrement, Increment, PrivateDecrement, PrivateIncrement, Sealed}; -use super::osculp32k::OscUlp32kId; use super::{Enabled, Source}; //============================================================================== @@ -377,6 +391,7 @@ pub struct Xosc32kBaseToken(()); /// /// The [`Xosc1k`] clock is disabled at power-on reset. To use it, you must /// first exchange the token for an actual clock with [`Xosc1k::enable`]. +#[hal_cfg(any("sysctrl-d11", "osc32kctrl"))] pub struct Xosc1kToken(()); /// Singleton token that can be exchanged for [`Xosc32k`] @@ -398,16 +413,21 @@ pub struct Xosc32kToken(()); /// /// Clock failure detection is disabled at power-on reset. To use it, you must /// first enable it by exchanging the token with [`Xosc32kCfd::enable`]. +#[hal_cfg("osc32kctrl")] pub struct Xosc32kCfdToken(()); +#[hal_macro_helper] /// Set of tokens representing the disabled XOSC32K clocks power-on reset pub struct Xosc32kTokens { pub base: Xosc32kBaseToken, + #[hal_cfg(any("sysctrl-d11", "osc32kctrl"))] pub xosc1k: Xosc1kToken, pub xosc32k: Xosc32kToken, + #[hal_cfg("osc32kctrl")] pub cfd: Xosc32kCfdToken, } +#[hal_macro_helper] impl Xosc32kTokens { /// Create the set of tokens /// @@ -419,8 +439,10 @@ impl Xosc32kTokens { pub(super) unsafe fn new() -> Self { Self { base: Xosc32kBaseToken(()), + #[hal_cfg(any("sysctrl-d11", "osc32kctrl"))] xosc1k: Xosc1kToken(()), xosc32k: Xosc32kToken(()), + #[hal_cfg("osc32kctrl")] cfd: Xosc32kCfdToken(()), } } @@ -428,10 +450,18 @@ impl Xosc32kTokens { impl Xosc32kBaseToken { #[inline] - fn status(&self) -> status::R { + #[hal_macro_helper] + fn status(&self) -> STATUS_R { // Safety: We are only reading from the `STATUS` register, so there is // no risk of memory corruption. - unsafe { (*crate::pac::Osc32kctrl::PTR).status().read() } + #[hal_cfg("osc32kctrl")] + unsafe { + (*PERIPHERAL::PTR).status().read() + } + #[hal_cfg("sysctrl")] + unsafe { + (*PERIPHERAL::PTR).pclksr().read() + } } /// Check whether the XOSC32K is stable and ready @@ -441,11 +471,11 @@ impl Xosc32kBaseToken { } #[inline] - fn xosc32k(&self) -> &osc32kctrl::Xosc32k { + fn xosc32k(&self) -> &PacXosc32k { // Safety: The `Xosc32kBaseToken` has exclusive access to the `XOSC32K` // register. See the notes on `Token` types and memory safety in the // root of the `clock` module for more details. - unsafe { (*crate::pac::Osc32kctrl::PTR).xosc32k() } + unsafe { (*PERIPHERAL::PTR).xosc32k() } } /// Reset the XOSC32K register @@ -456,23 +486,22 @@ impl Xosc32kBaseToken { /// Set most of the fields in the XOSC32K register #[inline] - fn set_xosc32k(&mut self, settings: Settings) { - let xtalen = settings.mode == DynMode::CrystalMode; - self.xosc32k().modify(|_, w| { + #[hal_macro_helper] + fn enable(&mut self, mode: DynMode, settings: Settings) { + let xtalen = mode == DynMode::CrystalMode; + self.xosc32k().write(|w| { + #[hal_cfg("osc32kctrl")] w.cgm().variant(settings.cgm.into()); - w.startup().variant(settings.start_up.into()); + #[hal_cfg("sysctrl")] + w.aampen().bit(settings.aampen); + unsafe { w.startup().bits(settings.start_up as u8) }; w.ondemand().bit(settings.on_demand); w.runstdby().bit(settings.run_standby); - w.xtalen().bit(xtalen) + w.xtalen().bit(xtalen); + w.enable().set_bit() }); } - /// Disable the XOSC32K - #[inline] - fn enable(&mut self) { - self.xosc32k().modify(|_, w| w.enable().set_bit()); - } - /// Disable the XOSC32K #[inline] fn disable(&mut self) { @@ -481,12 +510,14 @@ impl Xosc32kBaseToken { /// Enable the 1 kHz output #[inline] + #[hal_cfg(any("sysctrl-d11", "osc32kctrl"))] fn enable_1k(&mut self) { self.xosc32k().modify(|_, w| w.en1k().set_bit()); } /// Disable the 1 kHz output #[inline] + #[hal_cfg(any("sysctrl-d11", "osc32kctrl"))] fn disable_1k(&mut self) { self.xosc32k().modify(|_, w| w.en1k().clear_bit()); } @@ -510,12 +541,13 @@ impl Xosc32kBaseToken { } } +#[hal_cfg("osc32kctrl")] impl Xosc32kCfdToken { #[inline] - fn status(&self) -> status::R { + fn status(&self) -> STATUS_R { // Safety: We are only reading from the `STATUS` register, so there is // no risk of memory corruption. - unsafe { (*crate::pac::Osc32kctrl::PTR).status().read() } + unsafe { (*PERIPHERAL::PTR).status().read() } } /// Check whether the XOSC32K has triggered failure detection @@ -535,7 +567,7 @@ impl Xosc32kCfdToken { // Safety: The `Xosc32kCfdToken` has exclusive access to the `Cfdctrl` // register. See the notes on `Token` types and memory safety in the // root of the `clock` module for more details. - unsafe { (*crate::pac::Osc32kctrl::PTR).cfdctrl() } + unsafe { (*PERIPHERAL::PTR).cfdctrl() } } /// Enable clock failure detection and set the safe clock divider @@ -571,13 +603,46 @@ impl Xosc32kCfdToken { // All of these fields are set in a single write to XOSC32K during the call to // [`Xosc32kBase::enable`]. The remaining fields are only modified after it has // been enabled. +#[hal_cfg("clock-d5x")] #[derive(Clone, Copy)] struct Settings { start_up: StartUpDelay, cgm: ControlGainMode, on_demand: bool, run_standby: bool, - mode: DynMode, +} + +#[hal_cfg("clock-d5x")] +impl Default for Settings { + fn default() -> Self { + Settings { + start_up: StartUpDelay::Delay63ms, + cgm: ControlGainMode::Standard, + on_demand: true, + run_standby: false, + } + } +} + +#[hal_cfg(any("clock-d11", "clock-d21"))] +#[derive(Clone, Copy)] +struct Settings { + start_up: StartUpDelay, + aampen: bool, + on_demand: bool, + run_standby: bool, +} + +#[hal_cfg(any("clock-d11", "clock-d21"))] +impl Default for Settings { + fn default() -> Self { + Settings { + start_up: StartUpDelay::Delay63ms, + aampen: false, + on_demand: true, + run_standby: false, + } + } } //============================================================================== @@ -585,10 +650,10 @@ struct Settings { //============================================================================== /// Type alias for the XOSC32K input [`Pin`] -pub type XIn32 = Pin; +pub type XIn32 = Pin; /// Type alias for the XOSC32K output [`Pin`] -pub type XOut32 = Pin; +pub type XOut32 = Pin; //============================================================================== // SafeClockDiv @@ -631,6 +696,7 @@ impl From for bool { /// The start up delay is counted using the [`OscUlp32k`] clock. /// /// [`OscUlp32k`]: super::osculp32k::OscUlp32k +#[hal_cfg("clock-d5x")] #[repr(u8)] #[derive(Clone, Copy, Default, PartialEq, Eq)] pub enum StartUpDelay { @@ -644,18 +710,19 @@ pub enum StartUpDelay { Delay8s, } -impl From for Startupselect { - fn from(delay: StartUpDelay) -> Self { - match delay { - StartUpDelay::Delay63ms => Startupselect::Cycle2048, - StartUpDelay::Delay125ms => Startupselect::Cycle4096, - StartUpDelay::Delay500ms => Startupselect::Cycle16384, - StartUpDelay::Delay1s => Startupselect::Cycle32768, - StartUpDelay::Delay2s => Startupselect::Cycle65536, - StartUpDelay::Delay4s => Startupselect::Cycle131072, - StartUpDelay::Delay8s => Startupselect::Cycle262144, - } - } +#[hal_cfg(any("clock-d11", "clock-d21"))] +#[repr(u8)] +#[derive(Clone, Copy, Default, PartialEq, Eq)] +pub enum StartUpDelay { + #[default] + Delay122us, + Delay1ms, + Delay63ms, + Delay125ms, + Delay500ms, + Delay1s, + Delay2s, + Delay4s, } //============================================================================== @@ -666,6 +733,7 @@ impl From for Startupselect { /// /// The XOSC32K crystal oscillator control loop has a configurable gain to allow /// users to trade power for speed and stability. +#[hal_cfg("clock-d5x")] #[derive(Copy, Clone, Default, PartialEq, Eq)] pub enum ControlGainMode { #[default] @@ -673,6 +741,7 @@ pub enum ControlGainMode { HighSpeed, } +#[hal_cfg("clock-d5x")] impl From for Cgmselect { fn from(cgm: ControlGainMode) -> Self { match cgm { @@ -843,6 +912,7 @@ impl Xosc32kBase { /// Set the crystal oscillator [`ControlGainMode`] #[inline] + #[hal_cfg("clock-d5x")] pub fn control_gain_mode(mut self, cgm: ControlGainMode) -> Self { self.settings.cgm = cgm; self @@ -852,13 +922,7 @@ impl Xosc32kBase { impl Xosc32kBase { #[inline] fn new(token: Xosc32kBaseToken, pins: M::Pins) -> Self { - let settings = Settings { - start_up: StartUpDelay::Delay63ms, - cgm: ControlGainMode::Standard, - on_demand: true, - run_standby: false, - mode: M::DYN, - }; + let settings = Settings::default(); Self { token, pins, @@ -932,8 +996,7 @@ impl Xosc32kBase { #[inline] pub fn enable(mut self) -> EnabledXosc32kBase { self.token.reset(); - self.token.set_xosc32k(self.settings); - self.token.enable(); + self.token.enable(M::DYN, self.settings); Enabled::new(self) } } @@ -997,10 +1060,12 @@ impl EnabledXosc32kBase { /// /// [`OscUlp32k`]: super::osculp32k::OscUlp32k /// [`EnabledOscUlp32k`]: super::osculp32k::EnabledOscUlp32k +#[hal_cfg("osc32kctrl")] pub struct Xosc32kCfd { token: Xosc32kCfdToken, } +#[hal_cfg("osc32kctrl")] impl Xosc32kCfd { /// Enable continuous monitoring of the XOSC32K for clock failure /// @@ -1071,6 +1136,22 @@ impl Xosc32kCfd { } } +/// Clock failure detection is not supported by the currently-documented target +#[cfg(doc)] +#[hal_cfg(not("osc32kctrl"))] +pub struct Xosc32kCfd; + +#[cfg(doc)] +#[hal_cfg(not("osc32kctrl"))] +impl Xosc32kCfd { + /// Clock failure detection is not supported by the currently-documented target + pub fn has_failed() {} + /// Clock failure detection is not supported by the currently-documented target + pub fn is_switched() {} + /// Clock failure detection is not supported by the currently-documented target + pub fn switch_back() {} +} + //============================================================================== // Ids //============================================================================== @@ -1100,10 +1181,18 @@ impl Sealed for Xosc32kId {} /// of three clocks forming a small clock tree. The [`Xosc1k`] clock is derived /// from the [`Xosc32kBase`] clock. See the [module-level documentation](super) /// for details and examples. +#[hal_cfg(any("sysctrl-d11", "osc32kctrl"))] pub struct Xosc1k { token: Xosc1kToken, } + +/// XOSC1K is not available on the currently-documented target +#[cfg(doc)] +#[hal_cfg(not(any("sysctrl-d11", "osc32kctrl")))] +pub struct Xosc1k; + + /// The [`Enabled`] [`Xosc1k`] clock /// /// As described in the [`clock` module documentation](super), the [`Enabled`] @@ -1113,8 +1202,10 @@ pub struct Xosc1k { /// /// As with [`Enabled`], the default value for `N` is `U0`; if left unspecified, /// the counter is assumed to be zero. +#[hal_cfg(any("sysctrl-d11", "osc32kctrl"))] pub type EnabledXosc1k = Enabled; +#[hal_cfg(any("sysctrl-d11", "osc32kctrl"))] impl Xosc1k { /// Enable 1 kHz output from the [`Xosc32kBase`] clock /// @@ -1133,6 +1224,7 @@ impl Xosc1k { } } +#[hal_cfg(any("sysctrl-d11", "osc32kctrl"))] impl EnabledXosc1k { /// Disable 1 kHz output from the [`Xosc32kBase`] clock /// @@ -1151,6 +1243,7 @@ impl EnabledXosc1k { } } +#[hal_cfg(any("sysctrl-d11", "osc32kctrl"))] impl Source for EnabledXosc1k { type Id = Xosc1kId; diff --git a/hal/src/delay.rs b/hal/src/delay.rs index 3ea6aa60bc8a..aad0273f3447 100644 --- a/hal/src/delay.rs +++ b/hal/src/delay.rs @@ -1,6 +1,5 @@ //! Delays -use atsamd_hal_macros::hal_cfg; use cortex_m::peripheral::SYST; use cortex_m::peripheral::syst::SystClkSource; @@ -9,10 +8,8 @@ use crate::ehal::delay::DelayNs; use crate::ehal_02; use crate::time::Hertz; -#[hal_cfg("rtc-d5x")] use crate::typelevel::Increment; -#[hal_cfg("rtc-d5x")] use crate::clock::v2::{Source, gclk::Gclk0Id}; /// System timer (SysTick) as a delay provider @@ -32,7 +29,6 @@ impl Delay { } } - #[hal_cfg("rtc-d5x")] /// Configures the system timer (SysTick) as a delay provide, compatible /// with the V2 clocking API pub fn new_with_source(mut syst: SYST, gclk0: S) -> (Self, S::Inc) diff --git a/hal/src/dmac/channel/mod.rs b/hal/src/dmac/channel/mod.rs index 26bcaf822b86..68d0da72e818 100644 --- a/hal/src/dmac/channel/mod.rs +++ b/hal/src/dmac/channel/mod.rs @@ -543,6 +543,8 @@ impl Channel { /// compatible lengths. You must guarantee that: /// - Either `source` or `dest` has a buffer length of 1, or /// - Both buffers have the same length. + /// * The source and destination buffers must have a length smaller or equal + /// to `u16::MAX`. /// * You must ensure that the transfer is completed or stopped before /// returning the [`Channel`]. Doing otherwise breaks type safety, because /// a [`Ready`] channel would still be in the middle of a transfer. @@ -676,10 +678,6 @@ impl Channel { /// /// # Safety /// - /// * This method does not check that the two provided buffers have - /// compatible lengths. You must guarantee that: - /// - Either `source` or `dest` has a buffer length of 1, or - /// - Both buffers have the same length. /// * You must ensure that the transfer is completed or stopped before /// returning the [`Channel`]. Doing otherwise breaks type safety, because /// a [`ReadyFuture`] channel would still be in the middle of a transfer. @@ -841,6 +839,13 @@ impl Default for InterruptFlags { /// location, or be null. They must not be circular (ie, points to itself). /// Any linked transfer must strictly be a read transaction (destination /// pointer is a byte buffer, source pointer is the SERCOM DATA register). +/// +/// * The length of both the source and destination buffers must be smaller or +/// equal to `u16::MAX`. +/// +/// * Either: +/// - `source` or `dest` has a buffer length of 1, or +/// - Both buffers have the same length. #[inline] pub(crate) unsafe fn write_descriptor>( descriptor: &mut DmacDescriptor, @@ -856,6 +861,8 @@ pub(crate) unsafe fn write_descriptor let dst_inc = destination.incrementing(); let dst_len = destination.buffer_len(); + // This is sufficient since buffers of unequal lengths breaks the safety + // contract if neither buffer has a length of 1. let length = core::cmp::max(src_len, dst_len); // Channel::xfer_complete() tests the channel enable bit, which indicates diff --git a/hal/src/dmac/mod.rs b/hal/src/dmac/mod.rs index c1d4980a9daf..f64c35bfe80f 100644 --- a/hal/src/dmac/mod.rs +++ b/hal/src/dmac/mod.rs @@ -120,20 +120,20 @@ //! # Example //! ``` //! let mut peripherals = Peripherals::take().unwrap(); -//! let mut dmac = DmaController::init(peripherals.DMAC, &mut peripherals.PM); +//! let mut dmac = DmaController::init(peripherals.dmac, &mut peripherals.pm); //! // Get individual handles to DMA channels //! let channels = dmac.split(); //! //! // Initialize DMA Channel 0 -//! let chan0 = channels.0.init(PriorityLevel::LVL0, false, &mut dmac); +//! let chan0 = channels.0.init(PriorityLevel::Lvl0); //! //! // Setup a DMA transfer (memory-to-memory -> incrementing source, incrementing destination) //! // NOTE: buf_src and buf_dest should be either: //! // &'static mut T, &'static mut [T], or &'static mut [T; N] where T: BeatSize //! let xfer = Transfer::new(chan0, buf_src, buf_dest, false).begin( //! &mut dmac, -//! TriggerSource::DISABLE, -//! TriggerAction::BLOCK, +//! TriggerSource::Disable, +//! TriggerAction::Block, //! ); //! //! // Wait for transfer to complete and grab resulting buffers @@ -141,7 +141,7 @@ //! //! // (Optional) free the [`DmaController`] struct and return the underlying PAC struct //! channels.0 = chan0.into(); -//! let dmac = dmac.free(channels, &mut peripherals.PM); +//! let dmac = dmac.free(channels, &mut peripherals.pm); //! ``` //! //! # [`Transfer`] recycling @@ -264,12 +264,16 @@ pub enum Error { /// Buffers need to either have the same length in beats, or one should have /// length == 1. In cases where one buffer is length 1, that buffer will be /// the source or destination of each beat in the transfer. If both buffers - /// had length >1, but not equal to each other, then it would not be clear + /// had length > 1, but not equal to each other, then it would not be clear /// how to structure the transfer. LengthMismatch, + /// The DMAC only supports up to `u16::MAX` beats in a single transfer. + TooManyBeats, + /// Operation is not valid in the current state of the object. InvalidState, + /// Chip reported an error during transfer TransferError, } diff --git a/hal/src/dmac/transfer.rs b/hal/src/dmac/transfer.rs index 34d09e5a54bc..7d9012327f5e 100644 --- a/hal/src/dmac/transfer.rs +++ b/hal/src/dmac/transfer.rs @@ -329,8 +329,10 @@ where /// /// # Errors /// - /// Returns [`Error::LengthMismatch`] if both - /// buffers have a length > 1 and are not of equal length. + /// * Returns [`Error::LengthMismatch`] if both buffers have a length > 1 + /// and are not of equal length. + /// * Returns [`Error::TooManyBeats`] if the number of beats are greater + /// than `u16::MAX``. #[allow(clippy::new_ret_no_self)] #[inline] pub fn new( @@ -360,6 +362,8 @@ where if src_len > 1 && dst_len > 1 && src_len != dst_len { Err(Error::LengthMismatch) + } else if src_len.max(dst_len) > u16::MAX.into() { + Err(Error::TooManyBeats) } else { Ok(()) } @@ -387,6 +391,9 @@ where /// exacly the same, unless one or both buffers are of length 1. The /// transfer length will be set to the longest of both buffers if they are /// not of equal size. + /// + /// * The source and destination buffers should have a length smaller or + /// equal to `u16::MAX`. #[inline] pub unsafe fn new_unchecked( mut chan: C, diff --git a/hal/src/gpio/dynpin.rs b/hal/src/gpio/dynpin.rs index 913193b81e3a..1c6d5e290a24 100644 --- a/hal/src/gpio/dynpin.rs +++ b/hal/src/gpio/dynpin.rs @@ -111,7 +111,6 @@ pub enum DynAlternate { E, F, G, - #[hal_cfg(any("port-d21", "port-d5x"))] H, #[hal_cfg("port-d5x")] I, @@ -182,9 +181,7 @@ macro_rules! dyn_alternate { }; } -dyn_alternate!(B, C, D, E, F, G); -#[hal_cfg(any("port-d21", "port-d5x"))] -dyn_alternate!(H); +dyn_alternate!(B, C, D, E, F, G, H); #[hal_cfg("port-d5x")] dyn_alternate!(I, J, K, L, M, N); diff --git a/hal/src/gpio/pin.rs b/hal/src/gpio/pin.rs index 040c8473e8bd..8979f0b63d4c 100644 --- a/hal/src/gpio/pin.rs +++ b/hal/src/gpio/pin.rs @@ -341,10 +341,7 @@ macro_rules! alternate { }; } -alternate!(B, C, D, E, F, G); - -#[hal_cfg(any("port-d21", "port-d5x"))] -alternate!(H); +alternate!(B, C, D, E, F, G, H); #[hal_cfg("port-d5x")] alternate!(I, J, K, L, M, N); @@ -752,7 +749,6 @@ impl_core_convert_from!( AlternateE, AlternateF, AlternateG, - #[hal_cfg(any("port-d21", "port-d5x"))] AlternateH, #[hal_cfg("port-d5x")] AlternateI, @@ -1088,8 +1084,7 @@ macro_rules! pins{ )+ } impl Pins { - /// Take ownership of the PAC - /// [`Port`] and split it into + /// Take ownership of the PAC [`Port`] and split it into /// discrete [`Pin`]s #[inline] pub fn new(port: Port) -> Pins { diff --git a/hal/src/gpio/reg.rs b/hal/src/gpio/reg.rs index e1229fa425ae..1eb865fe8da8 100644 --- a/hal/src/gpio/reg.rs +++ b/hal/src/gpio/reg.rs @@ -125,7 +125,6 @@ impl From for ModeFields { G => { fields.pmux = 6; } - #[hal_cfg(any("port-d21", "port-d5x"))] H => { fields.pmux = 7; } diff --git a/hal/src/lib.rs b/hal/src/lib.rs index e20f6b5025d1..4bf3008b7bba 100644 --- a/hal/src/lib.rs +++ b/hal/src/lib.rs @@ -74,6 +74,8 @@ macro_rules! dbgprint { #[cfg(feature = "async")] pub mod async_hal; +#[cfg(feature = "device")] +pub mod clock; #[cfg(feature = "device")] pub mod delay; #[cfg(feature = "device")] diff --git a/hal/src/peripherals/adc/builder.rs b/hal/src/peripherals/adc/builder.rs index baaa62e74b83..a42ee1a8e7fa 100644 --- a/hal/src/peripherals/adc/builder.rs +++ b/hal/src/peripherals/adc/builder.rs @@ -1,5 +1,7 @@ use atsamd_hal_macros::hal_cfg; +use crate::clock::v2::{apb::ApbClk, pclk::DynPclk}; + #[hal_cfg("adc-d5x")] use crate::pac::adc0; @@ -13,9 +15,7 @@ pub use adc0::ctrlb::Prescalerselect as Prescaler; pub use adc0::ctrla::Prescalerselect as Prescaler; pub use adc0::avgctrl::Samplenumselect as SampleCount; - pub use adc0::ctrlb::Resselselect as Resolution; - pub use adc0::refctrl::Refselselect as Reference; use super::{Adc, AdcInstance}; @@ -244,28 +244,18 @@ impl AdcBuilder { Ok(adc_clk_freq / clocks_per_sample) } - /// Turn the builder into an ADC - #[hal_cfg("adc-d5x")] - #[inline] - pub fn enable( - self, - adc: I::Instance, - clk: crate::clock::v2::apb::ApbClk, - pclk: &crate::clock::v2::pclk::Pclk, - ) -> Result, BuilderError> { - let settings = self.to_settings()?; - Adc::new(adc, settings, clk, pclk).map_err(|e| e.into()) - } - - #[hal_cfg(any("adc-d11", "adc-d21"))] + /// Turn the builder into an ADC. + /// + /// This function will convert the provided + /// [`Pclk`](crate::clock::v2::pclk::Pclk) into a [`DynPclk`](crate::clock::v2::pclk::DynPclk). #[inline] pub fn enable( self, adc: I::Instance, - pm: &mut crate::pac::Pm, - clock: &crate::clock::AdcClock, + clk: ApbClk, + pclk: impl Into>, ) -> Result, BuilderError> { let settings = self.to_settings()?; - Adc::new(adc, settings, pm, clock).map_err(|e| e.into()) + Adc::new(adc, settings, clk, pclk.into()).map_err(|e| e.into()) } } diff --git a/hal/src/peripherals/adc/d11/mod.rs b/hal/src/peripherals/adc/d11/mod.rs index d43d76012144..be5753932679 100644 --- a/hal/src/peripherals/adc/d11/mod.rs +++ b/hal/src/peripherals/adc/d11/mod.rs @@ -22,6 +22,8 @@ impl PrimaryAdc for Adc0 {} impl AdcInstance for Adc0 { type Instance = pac::Adc; + type ClockId = crate::clock::v2::pclk::ids::Adc0; + #[cfg(feature = "async")] type Interrupt = crate::async_hal::interrupts::ADC; @@ -30,11 +32,6 @@ impl AdcInstance for Adc0 { &p.adc } - #[inline] - fn enable_pm(pm: &mut pac::Pm) { - pm.apbcmask().modify(|_, w| w.adc_().set_bit()); - } - #[inline] fn calibrate(instance: &Self::Instance) { instance.calib().write(|w| unsafe { diff --git a/hal/src/peripherals/adc/mod.rs b/hal/src/peripherals/adc/mod.rs index c34b560ad113..099819737488 100644 --- a/hal/src/peripherals/adc/mod.rs +++ b/hal/src/peripherals/adc/mod.rs @@ -27,7 +27,12 @@ use core::ops::Deref; use atsamd_hal_macros::{hal_cfg, hal_module}; use pac::Peripherals; -use crate::{gpio::AnyPin, pac, typelevel::Sealed}; +use crate::{ + clock::v2::{apb::ApbClk, pclk::DynPclk}, + gpio::AnyPin, + pac, + typelevel::Sealed, +}; #[hal_module( any("adc-d11", "adc-d21") => "d11/mod.rs", @@ -140,14 +145,10 @@ pub trait AdcInstance { // The Adc0 and Adc1 PAC types implement Deref type Instance: Deref; - #[hal_cfg("adc-d5x")] type ClockId: crate::clock::v2::apb::ApbId + crate::clock::v2::pclk::PclkId; fn peripheral_reg_block(p: &mut Peripherals) -> &adc0::RegisterBlock; - #[hal_cfg(any("adc-d11", "adc-d21"))] - fn enable_pm(pm: &mut pac::Pm); - fn calibrate(instance: &Self::Instance); #[cfg(feature = "async")] @@ -162,19 +163,10 @@ where const CHANNEL: u8; } -/// ADC Instance -#[hal_cfg(any("adc-d11", "adc-d21"))] -pub struct Adc { - adc: I::Instance, - cfg: AdcSettings, - discard: bool, -} - -/// ADC Instance -#[hal_cfg("adc-d5x")] pub struct Adc { adc: I::Instance, _apbclk: crate::clock::v2::apb::ApbClk, + _pclk: crate::clock::v2::pclk::DynPclk, cfg: AdcSettings, discard: bool, } @@ -191,23 +183,23 @@ impl Adc { /// ## Important /// /// This function will return `Err` if the clock source provided - /// is faster than 100 MHz, since this is the maximum frequency for - /// GCLK_ADCx as per the datasheet. + /// is faster than 100 MHz (D5x) or 48Mhz (D21/D11), since this + /// is the maximum frequency for GCLK_ADCx as per the datasheet. /// /// The [`new`](Self::new) function currently takes an `&` reference to a /// [`Pclk`](crate::clock::v2::pclk::Pclk). In the future this will likely /// change to taking full ownership of it; in the meantime, you must ensure /// that the PCLK is enabled for the `Adc` struct's lifetime. /// - /// NOTE: If you plan to run the chip above 100°C, then the maximum GCLK + /// NOTE (D5x specific): If you plan to run the chip above 100°C, then the maximum GCLK /// frequency for the ADC is restricted to 90Mhz for stable performance. - #[hal_cfg("adc-d5x")] #[inline] - pub(crate) fn new( + #[atsamd_hal_macros::hal_macro_helper] + pub(crate) fn new( adc: I::Instance, settings: AdcSettings, clk: crate::clock::v2::apb::ApbClk, - pclk: &crate::clock::v2::pclk::Pclk, + pclk: crate::clock::v2::pclk::DynPclk, ) -> Result { // TODO: Ideally, the ADC struct would take ownership of the Pclk type here. // However, since clock::v2 is not implemented for all chips yet, the @@ -218,44 +210,21 @@ impl Adc { // at the time of creation of the Adc struct; however we can't guarantee // that the clock will stay enabled for the duration of its lifetime. + #[hal_cfg("adc-d5x")] if pclk.freq() > fugit::HertzU32::from_raw(100_000_000) { // Clock source is too fast return Err(Error::ClockTooFast); } - - let mut new_adc = Self { - adc, - _apbclk: clk, - cfg: settings, - discard: true, - }; - new_adc.configure(settings); - Ok(new_adc) - } - - /// Construct a new ADC instance - /// - /// ## Important - /// - /// This function will return [Error::ClockTooFast] if the clock source - /// provided is faster than 48 MHz, since this is the maximum frequency - /// for the ADC as per the datasheet. - #[hal_cfg(any("adc-d11", "adc-d21"))] - #[inline] - pub(crate) fn new( - adc: I::Instance, - settings: AdcSettings, - pm: &mut pac::Pm, - clock: &crate::clock::AdcClock, - ) -> Result { - if (clock.freq() as crate::time::Hertz).to_Hz() > 48_000_000 { + #[hal_cfg(any("adc-d21", "adc-d11"))] + if pclk.freq() > fugit::HertzU32::from_raw(48_000_000) { // Clock source is too fast return Err(Error::ClockTooFast); } - I::enable_pm(pm); let mut new_adc = Self { adc, + _apbclk: clk, + _pclk: pclk, cfg: settings, discard: true, }; @@ -352,12 +321,13 @@ impl Adc { // If the ADC has to discard the next value, then we try to read it // and then discard it #[inline] - pub fn check_read_discard(&mut self) { + fn check_read_discard(&mut self) { if self.discard { self.start_conversion(); while !self.read_flags().contains(Flags::RESRDY) { core::hint::spin_loop(); } + self.clear_all_flags(); self.discard = false; } } @@ -381,15 +351,7 @@ impl Adc { self.mux(ch); self.enable_freerunning(); self.start_conversion(); - if self.discard { - // Discard first result - while !self.read_flags().contains(Flags::RESRDY) { - core::hint::spin_loop(); - } - self.clear_all_flags(); - self.discard = false; - } - + self.check_read_discard(); for result in dst.iter_mut() { while !self.read_flags().contains(Flags::RESRDY) { core::hint::spin_loop(); @@ -412,20 +374,11 @@ impl Adc { Ok(()) } - /// Return the underlying ADC PAC object. - #[hal_cfg(any("adc-d11", "adc-d21"))] - #[inline] - pub fn free(mut self) -> I::Instance { - self.software_reset(); - self.adc - } - /// Return the underlying ADC PAC object and the enabled APB ADC clock. - #[hal_cfg("adc-d5x")] #[inline] - pub fn free(mut self) -> (I::Instance, crate::clock::v2::apb::ApbClk) { + pub fn free(mut self) -> (I::Instance, ApbClk, DynPclk) { self.software_reset(); - (self.adc, self._apbclk) + (self.adc, self._apbclk, self._pclk) } /// Reset the peripheral. @@ -468,7 +421,7 @@ where self.inner.start_conversion(); let _ = self.wait_flags(Flags::RESRDY).await; self.inner.discard = false; - let _ = self.inner.conversion_result(); + self.inner.clear_all_flags(); } self.inner.start_conversion(); // Here we explicitly ignore the result, because we know that diff --git a/hal/src/peripherals/aes/mod.rs b/hal/src/peripherals/aes/mod.rs index 23deaec40df8..e30fd6efc6d0 100644 --- a/hal/src/peripherals/aes/mod.rs +++ b/hal/src/peripherals/aes/mod.rs @@ -119,11 +119,6 @@ //! assert_eq!(block, block_copy); //! ``` -// The cipher crate has an outdated dependency on the generic-array crate -// (by way of the crypto_common crate) -// Nothing we can do about this until the dependency chain is up to date -#![allow(deprecated)] - // Re-exports pub use pac::aes::ctrla::{ Aesmodeselect, Cfbsselect, Cipherselect, Keysizeselect, Lodselect, Startmodeselect, diff --git a/hal/src/peripherals/clock/d5x/mod.rs b/hal/src/peripherals/clock/d5x/mod.rs deleted file mode 100644 index ce090b2fd4b5..000000000000 --- a/hal/src/peripherals/clock/d5x/mod.rs +++ /dev/null @@ -1,9 +0,0 @@ -//! # Clocking API -//! -//! Users are encouraged to use [`v2`] variant of an API because of the richer -//! feature set and safety. - -pub mod v1; -pub use v1::*; - -pub mod v2; diff --git a/hal/src/peripherals/clock/d5x/v2/types.rs b/hal/src/peripherals/clock/d5x/v2/types.rs deleted file mode 100644 index 88bfc43ee225..000000000000 --- a/hal/src/peripherals/clock/d5x/v2/types.rs +++ /dev/null @@ -1,127 +0,0 @@ -//! Module defining or exporting peripheral types for the ['ahb'], ['apb'] and -//! ['pclk'] modules -//! -//! The `ahb`, `apb` and `pclk` modules each define structs that are -//! generic over a type parameter representing a peripheral. Some peripheral -//! modules already define suitable types for this purpose. For example, -//! [`sercom`] defines the [`Sercom0`], [`Sercom1`], etc. types. But other -//! peripherals are either not yet implemented in the HAL or do not define a -//! suitable type. This module defines a type for such peripherals. If/when a -//! suitable type is added for a given peripheral, the type defined here should -//! be deprecated or removed. -//! -//! [`ahb`]: super::ahb -//! [`apb`]: super::apb -//! [`pclk`]: super::pclk -//! [`sercom`]: crate::sercom -//! [`Sercom0`]: crate::sercom::Sercom0 -//! [`Sercom1`]: crate::sercom::Sercom1 - -use atsamd_hal_macros::hal_cfg; - -use crate::typelevel::Sealed; - -#[hal_cfg("sercom0")] -pub use crate::sercom::Sercom0; - -#[hal_cfg("sercom1")] -pub use crate::sercom::Sercom1; - -#[hal_cfg("sercom2")] -pub use crate::sercom::Sercom2; - -#[hal_cfg("sercom3")] -pub use crate::sercom::Sercom3; - -#[hal_cfg("sercom4")] -pub use crate::sercom::Sercom4; - -#[hal_cfg("sercom5")] -pub use crate::sercom::Sercom5; - -#[hal_cfg("sercom6")] -pub use crate::sercom::Sercom6; - -#[hal_cfg("sercom7")] -pub use crate::sercom::Sercom7; - -macro_rules! create_types { - ( - $( - $Type:ident - ),+ - ) => { - $( - /// Marker type representing the corresponding peripheral - /// - /// This type is defined by and used within the [`clock`](super) - /// module. See the the [`types`](self) module documentation for - /// more details. - pub enum $Type {} - impl Sealed for $Type {} - )+ - }; -} - -create_types!(Ac); -create_types!(Adc0, Adc1); -create_types!(Aes); -#[hal_cfg("can0")] -create_types!(Can0); -#[hal_cfg("can1")] -create_types!(Can1); -create_types!(Ccl); -create_types!(Cmcc); -create_types!(CM4Trace); -create_types!(Dac); -create_types!(Dmac); -create_types!(Dsu); -create_types!(Eic); -create_types!( - EvSys, EvSys0, EvSys1, EvSys2, EvSys3, EvSys4, EvSys5, EvSys6, EvSys7, EvSys8, EvSys9, EvSys10, - EvSys11 -); -create_types!(FreqM); -create_types!(FreqMMeasure); -create_types!(FreqMReference); -create_types!(Gclk); -#[hal_cfg("gmac")] -create_types!(Gmac); -create_types!(Hpb0, Hpb1, Hpb2, Hpb3); -create_types!(Icm); -create_types!(Mclk); -create_types!(NvmCtrl, NvmCtrlSmeeProm, NvmCtrlCache); -#[hal_cfg("i2s")] -create_types!(I2S, I2S0, I2S1); -create_types!(OscCtrl); -create_types!(Osc32kCtrl); -create_types!(Pac); -create_types!(Pcc); -create_types!(PDec); -create_types!(Pm); -create_types!(Port); -create_types!(Pukcc); -create_types!(Qspi, Qspi2x); -create_types!(RamEcc); -create_types!(RstC); -create_types!(Rtc); -create_types!(Sdhc0); -#[hal_cfg("sdhc1")] -create_types!(Sdhc1); -create_types!(SlowClk); -create_types!(SupC); -create_types!(Tc0Tc1, Tc0, Tc1); -create_types!(Tc2Tc3, Tc2, Tc3); -#[hal_cfg(all("tc4", "tc5"))] -create_types!(Tc4Tc5, Tc4, Tc5); -#[hal_cfg(all("tc6", "tc7"))] -create_types!(Tc6Tc7, Tc6, Tc7); -create_types!(Tcc0Tcc1, Tcc0, Tcc1); -create_types!(Tcc2Tcc3, Tcc2); -#[hal_cfg("tcc3")] -create_types!(Tcc3); -#[hal_cfg("tcc4")] -create_types!(Tcc4); -create_types!(Trng); -create_types!(Usb); -create_types!(Wdt); diff --git a/hal/src/peripherals/eic.rs b/hal/src/peripherals/eic.rs index e3d4f2fe829d..b959d9e74c38 100644 --- a/hal/src/peripherals/eic.rs +++ b/hal/src/peripherals/eic.rs @@ -83,7 +83,7 @@ mod impls {} pub use impls::async_api::*; #[hal_cfg("eic-d5x")] -use super::clock::v2::{self, gclk::GclkId, osculp32k::OscUlp32kId, pclk::Pclk, rtcosc::RtcOsc}; +use crate::clock::v2::{self, gclk::GclkId, osculp32k::OscUlp32kId, pclk::Pclk, rtcosc::RtcOsc}; pub type Sense = pac::eic::config::Sense0select; diff --git a/hal/src/peripherals/eic/d11/pin.rs b/hal/src/peripherals/eic/d11/pin.rs index e1b3ec8e3ae4..8de32744bbb2 100644 --- a/hal/src/peripherals/eic/d11/pin.rs +++ b/hal/src/peripherals/eic/d11/pin.rs @@ -4,7 +4,7 @@ use crate::ehal::digital::{ErrorType, InputPin}; use crate::ehal_02::digital::v2::InputPin as InputPin_02; use crate::eic::*; use crate::gpio::{ - self, pin::*, AnyPin, FloatingInterrupt, PinMode, PullDownInterrupt, PullUpInterrupt, + self, AnyPin, FloatingInterrupt, PinMode, PullDownInterrupt, PullUpInterrupt, pin::*, }; use core::convert::Infallible; @@ -213,16 +213,9 @@ mod async_impls { self.disable_interrupt(); match sense { - Sense::High => { - if self.is_high().unwrap() { - return; - } - } - Sense::Low => { - if self.is_low().unwrap() { - return; - } - } + Sense::High if self.is_high().unwrap() => return, + + Sense::Low if self.is_low().unwrap() => return, _ => (), } diff --git a/hal/src/peripherals/mod.rs b/hal/src/peripherals/mod.rs index 78eb4048a08c..45259be902ac 100644 --- a/hal/src/peripherals/mod.rs +++ b/hal/src/peripherals/mod.rs @@ -31,12 +31,6 @@ pub mod usb {} )] pub mod pwm {} -#[hal_module( - any("clock-d11", "clock-d21") => "clock/d11.rs", - "clock-d5x" => "clock/d5x/mod.rs", -)] -pub mod clock {} - #[hal_module("aes")] pub mod aes {} diff --git a/hal/src/peripherals/nvm/mod.rs b/hal/src/peripherals/nvm/mod.rs index e4082db3f19f..b41284b5739a 100644 --- a/hal/src/peripherals/nvm/mod.rs +++ b/hal/src/peripherals/nvm/mod.rs @@ -176,6 +176,12 @@ impl Nvm { Self { nvm } } + /// Releases the NvmCtrl resource + #[inline] + pub fn free(self) -> Nvmctrl { + self.nvm + } + /// Raw access to the registers. /// /// # Safety diff --git a/hal/src/peripherals/pwm/d11.rs b/hal/src/peripherals/pwm/d11.rs index a640912a34ce..49ef1c285475 100644 --- a/hal/src/peripherals/pwm/d11.rs +++ b/hal/src/peripherals/pwm/d11.rs @@ -57,6 +57,15 @@ impl $TYPE { } } + #[inline] + // Disables the TC, then releases it + pub fn free(self) -> crate::pac::$TC { + let count = self.tc.count16(); + count.ctrla().write(|w| w.swrst().set_bit()); + while count.ctrla().read().bits() & 1 != 0 {} + self.tc + } + pub fn set_period(&mut self, period: Hertz) { let params = TimerParams::new(period, self.clock_freq); @@ -97,7 +106,7 @@ impl $crate::ehal::pwm::SetDutyCycle for $TYPE { fn max_duty_cycle(&self) -> u16 { let count = self.tc.count16(); let top = count.cc(0).read().cc().bits(); - top + top.saturating_add(1) } fn set_duty_cycle(&mut self, duty: u16) -> Result<(), Self::Error> { @@ -252,7 +261,7 @@ impl $crate::ehal_02::Pwm for $TYPE { fn get_max_duty(&self) -> Self::Duty { let top = self.tcc.per().read().bits(); - top + top + 1 } fn set_duty(&mut self, channel: Self::Channel, duty: Self::Duty) { diff --git a/hal/src/peripherals/pwm/d5x.rs b/hal/src/peripherals/pwm/d5x.rs index a9094fac4ac6..10b8a547f0c5 100644 --- a/hal/src/peripherals/pwm/d5x.rs +++ b/hal/src/peripherals/pwm/d5x.rs @@ -227,7 +227,7 @@ impl $crate::ehal::pwm::SetDutyCycle for $TYPE { fn max_duty_cycle(&self) -> u16 { let count = self.tc.count16(); let top = count.cc(0).read().cc().bits(); - top + top.saturating_add(1) } fn set_duty_cycle(&mut self, duty: u16) -> Result<(), Self::Error> { @@ -606,6 +606,14 @@ impl $TYPE { pinout, } } + + #[inline] + // Disables the TCC, then releases it + pub fn free(self) -> crate::pac::$TCC { + self.tcc.ctrla().write(|w| w.swrst().set_bit()); + while self.tcc.syncbusy().read().swrst().bit_is_set() {} + self.tcc + } } impl $crate::ehal_02::Pwm for $TYPE { @@ -637,7 +645,7 @@ impl $crate::ehal_02::Pwm for $TYPE { fn get_max_duty(&self) -> Self::Duty { let top = self.tcc.per().read().bits(); - top + top + 1 } fn set_duty(&mut self, channel: Self::Channel, duty: Self::Duty) { @@ -680,9 +688,9 @@ impl $crate::ehal_02::Pwm for $TYPE { pwm_tcc! { Tcc0Pwm: (Tcc0, TCC0Pinout, Tcc0Tcc1Clock, apbbmask, tcc0_, TccPwm0Wrapper) } #[hal_cfg("tcc1")] pwm_tcc! { Tcc1Pwm: (Tcc1, TCC1Pinout, Tcc0Tcc1Clock, apbbmask, tcc1_, TccPwm1Wrapper) } -#[hal_cfg("tcc2")] +#[hal_cfg(all("tcc2", "tcc3"))] pwm_tcc! { Tcc2Pwm: (Tcc2, TCC2Pinout, Tcc2Tcc3Clock, apbcmask, tcc2_, TccPwm2Wrapper) } -#[hal_cfg("tcc3")] +#[hal_cfg(all("tcc2", "tcc3"))] pwm_tcc! { Tcc3Pwm: (Tcc3, TCC3Pinout, Tcc2Tcc3Clock, apbcmask, tcc3_, TccPwm3Wrapper) } #[hal_cfg("tcc4")] pwm_tcc! { Tcc4Pwm: (Tcc4, TCC4Pinout, Tcc4Clock, apbdmask, tcc4_, TccPwm4Wrapper) } diff --git a/hal/src/peripherals/timer/d11.rs b/hal/src/peripherals/timer/d11.rs index b484eabf417c..dd79f19f6477 100644 --- a/hal/src/peripherals/timer/d11.rs +++ b/hal/src/peripherals/timer/d11.rs @@ -57,11 +57,11 @@ where // need to manually read the bit here while count.ctrla().read().bits() & 1 != 0 {} - count.ctrlbset().write(|w| { + count.ctrlbclr().write(|w| { // Count up when the direction bit is zero - w.dir().clear_bit(); + w.dir().set_bit(); // Periodic - w.oneshot().clear_bit() + w.oneshot().set_bit() }); // Set TOP value for mfrq mode @@ -129,6 +129,15 @@ impl TimerCounter<$TC> tc, } } + + #[inline] + // Disables the TC, then releases it + pub fn free(self) -> $TC { + let count = self.tc.count16(); + count.ctrla().write(|w| w.swrst().set_bit()); + while count.ctrla().read().bits() & 1 != 0 {} + self.tc + } } )+ } diff --git a/hal/src/peripherals/timer/d5x.rs b/hal/src/peripherals/timer/d5x.rs index 260f7e2f7b6a..31b457166d98 100644 --- a/hal/src/peripherals/timer/d5x.rs +++ b/hal/src/peripherals/timer/d5x.rs @@ -55,11 +55,11 @@ where count.ctrla().write(|w| w.swrst().set_bit()); while count.syncbusy().read().swrst().bit_is_set() {} - count.ctrlbset().write(|w| { + count.ctrlbclr().write(|w| { // Count up when the direction bit is zero - w.dir().clear_bit(); + w.dir().set_bit(); // Periodic - w.oneshot().clear_bit() + w.oneshot().set_bit() }); // Set TOP value for mfrq mode @@ -128,6 +128,15 @@ impl TimerCounter<$TC> tc, } } + + #[inline] + // Disables the TC, then releases it + pub fn free(self) -> $TC { + let count = self.tc.count_16(); + count.ctrla().write(|w| w.swrst().set_bit()); + while count.syncbusy().read().swrst().bit_is_set() {} + self.tc + } } )+ } diff --git a/hal/src/peripherals/trng.rs b/hal/src/peripherals/trng.rs index e6f76e14b32d..f4e7706d8037 100644 --- a/hal/src/peripherals/trng.rs +++ b/hal/src/peripherals/trng.rs @@ -13,6 +13,11 @@ impl Trng { Self(trng) } + /// Releases the Trng resource + pub fn free(self) -> pac::Trng { + self.0 + } + pub fn random(&self, buf: &mut [u8]) { for chunk in buf.chunks_mut(4) { chunk.copy_from_slice(&self.random_u32().to_le_bytes()[..chunk.len()]); diff --git a/hal/src/peripherals/usb/d11/buffer.rs b/hal/src/peripherals/usb/d11/buffer.rs new file mode 100644 index 000000000000..35c1ae0951c5 --- /dev/null +++ b/hal/src/peripherals/usb/d11/buffer.rs @@ -0,0 +1,96 @@ +use core::ops::{Deref, DerefMut}; +use cortex_m::singleton; +use usb_device::{Result as UsbResult, UsbError}; + +/// Size of the buffer that holds ALL USB endpoint buffers +pub const BUFFER_SIZE: usize = { + #[cfg(feature = "usb-buffer-1k")] + { + 1024 + } + #[cfg(feature = "usb-buffer-4k")] + { + 4098 + } + #[cfg(feature = "usb-buffer-8k")] + { + 8192 + } + #[cfg(feature = "usb-buffer-16k")] + { + 16384 + } + #[cfg(not(any( + feature = "usb-buffer-1k", + feature = "usb-buffer-4k", + feature = "usb-buffer-8k", + feature = "usb-buffer-16k" + )))] + { + 2048 // Default + } +}; + +/// Size of each USB endpoints buffer (Always guaranteed to be divisible by 4) +pub const ALLOC_SIZE_MAX_PER_EP: usize = BUFFER_SIZE / 16; + +/// USB endpoint storage buffer, aligned to 4 bytes +#[repr(C, align(4))] +pub struct Buffer { + inner: [u8; BUFFER_SIZE], +} + +impl Default for Buffer { + fn default() -> Self { + Self { + inner: [0; BUFFER_SIZE], + } + } +} + +impl Deref for Buffer { + type Target = [u8; BUFFER_SIZE]; + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +impl DerefMut for Buffer { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.inner + } +} + +fn buffer() -> &'static mut Buffer { + singleton!(: Buffer = Buffer::default() ).unwrap() +} + +pub struct BufferAllocator { + buffers: &'static mut Buffer, + next_buf: usize, +} + +impl Default for BufferAllocator { + fn default() -> Self { + Self { + next_buf: 0, + buffers: buffer(), + } + } +} + +impl BufferAllocator { + /// Allocates a fixed buffer of [`ALLOC_SIZE_MAX_PER_EP`] + pub fn allocate_buffer(&mut self) -> UsbResult<*mut u8> { + // Do all range checks first + if self.next_buf >= self.buffers.len() + || self.next_buf + ALLOC_SIZE_MAX_PER_EP > self.buffers.len() + { + return Err(UsbError::EndpointMemoryOverflow); + } + + let start_addr = &mut self.buffers[self.next_buf] as *mut u8; + self.next_buf += ALLOC_SIZE_MAX_PER_EP; + Ok(start_addr) + } +} diff --git a/hal/src/peripherals/usb/d11/bus.rs b/hal/src/peripherals/usb/d11/bus.rs index 45315e94bbd3..2695ea36f1f8 100644 --- a/hal/src/peripherals/usb/d11/bus.rs +++ b/hal/src/peripherals/usb/d11/bus.rs @@ -8,16 +8,15 @@ use super::Descriptors; use crate::calibration::{usb_transn_cal, usb_transp_cal, usb_trim_cal}; use crate::clock; -use crate::gpio::{AlternateG, AnyPin, Pin, PA24, PA25}; +use crate::gpio::{AlternateG, AnyPin, PA24, PA25, Pin}; use crate::pac::usb::Device; use crate::pac::{Pm, Usb}; +use crate::usb::buffer::*; use crate::usb::devicedesc::DeviceDescBank; use atsamd_hal_macros::{hal_cfg, hal_macro_helper}; use core::cell::{Ref, RefCell, RefMut}; use core::marker::PhantomData; -use core::mem; -use cortex_m::singleton; -use critical_section::{with as disable_interrupts, Mutex}; +use critical_section::{Mutex, with as disable_interrupts}; use usb_device::bus::PollResult; use usb_device::endpoint::{EndpointAddress, EndpointType}; use usb_device::{Result as UsbResult, UsbDirection, UsbError}; @@ -51,22 +50,17 @@ impl From for EndpointTypeBits { #[derive(Default, Clone, Copy)] struct EPConfig { ep_type: EndpointTypeBits, - allocated_size: u16, max_packet_size: u16, + multi_packet_size: u16, addr: usize, } impl EPConfig { - fn new( - ep_type: EndpointType, - allocated_size: u16, - max_packet_size: u16, - buffer_addr: *mut u8, - ) -> Self { + fn new(ep_type: EndpointType, max_packet_size: u16, buffer_addr: *mut u8) -> Self { Self { ep_type: ep_type.into(), - allocated_size, max_packet_size, + multi_packet_size: 0, addr: buffer_addr as usize, } } @@ -127,7 +121,6 @@ impl AllEndpoints { dir: UsbDirection, idx: usize, ep_type: EndpointType, - allocated_size: u16, max_packet_size: u16, _interval: u8, buffer_addr: *mut u8, @@ -140,57 +133,12 @@ impl AllEndpoints { return Err(UsbError::EndpointOverflow); } - *bank = EPConfig::new(ep_type, allocated_size, max_packet_size, buffer_addr); + *bank = EPConfig::new(ep_type, max_packet_size, buffer_addr); Ok(EndpointAddress::from_parts(idx, dir)) } } -// FIXME: replace with more general heap? -const BUFFER_SIZE: usize = 2048; -fn buffer() -> &'static mut [u8; BUFFER_SIZE] { - singleton!(: [u8; BUFFER_SIZE] = [0; BUFFER_SIZE] ).unwrap() -} - -struct BufferAllocator { - buffers: &'static mut [u8; BUFFER_SIZE], - next_buf: u16, -} - -impl BufferAllocator { - fn new() -> Self { - Self { - next_buf: 0, - buffers: buffer(), - } - } - - fn allocate_buffer(&mut self, size: u16) -> UsbResult<*mut u8> { - debug_assert!(size & 1 == 0); - - let start_addr = &mut self.buffers[self.next_buf as usize] as *mut u8; - let buf_end = unsafe { start_addr.add(BUFFER_SIZE) }; - - // The address must be 32-bit aligned, so allow for that here - // by offsetting by an appropriate alignment. - let offset = start_addr.align_offset(mem::align_of::()); - let start_addr = unsafe { start_addr.add(offset) }; - - if start_addr >= buf_end { - return Err(UsbError::EndpointMemoryOverflow); - } - - let end_addr = unsafe { start_addr.offset(size as isize) }; - if end_addr > buf_end { - return Err(UsbError::EndpointMemoryOverflow); - } - - self.next_buf = unsafe { end_addr.sub(self.buffers.as_ptr() as usize) as u16 }; - - Ok(start_addr) - } -} - struct Inner { desc: RefCell, _dm_pad: Pin, @@ -303,7 +251,7 @@ impl Bank<'_, InBank> { /// bank1 buffer. The caller must call set_ready() to finalize the /// transfer. pub fn write(&mut self, buf: &[u8]) -> UsbResult { - let size = buf.len().min(self.config().allocated_size as usize); + let size = buf.len().min(ALLOC_SIZE_MAX_PER_EP); let desc = self.desc_bank(); unsafe { @@ -410,6 +358,7 @@ impl Bank<'_, OutBank> { /// must call set_ready to indicate the buffer is free for the next /// transfer. pub fn read(&mut self, buf: &mut [u8]) -> UsbResult { + let mp_size = self.config().multi_packet_size; let desc = self.desc_bank(); let size = desc.get_byte_count() as usize; @@ -422,7 +371,7 @@ impl Bank<'_, OutBank> { } desc.set_byte_count(0); - desc.set_multi_packet_size(0); + desc.set_multi_packet_size(mp_size); Ok(size) } @@ -498,7 +447,7 @@ impl UsbBus { _dm_pad: dm_pad.into().into_mode::(), _dp_pad: dp_pad.into().into_mode::(), desc, - buffers: RefCell::new(BufferAllocator::new()), + buffers: RefCell::new(BufferAllocator::default()), endpoints: RefCell::new(AllEndpoints::new()), }; @@ -680,22 +629,12 @@ impl Inner { max_packet_size: u16, interval: u8, ) -> UsbResult { - // The USB hardware encodes the maximum packet size in 3 bits, so - // reserve enough buffer that the hardware won't overwrite it even if - // the other side issues an overly-long transfer. - let allocated_size = match max_packet_size { - 1..=8 => 8, - 9..=16 => 16, - 17..=32 => 32, - 33..=64 => 64, - 65..=128 => 128, - 129..=256 => 256, - 257..=512 => 512, - 513..=1023 => 1024, - _ => return Err(UsbError::Unsupported), - }; + // packet size is too big to fit into an endpoint buffer + if max_packet_size > ALLOC_SIZE_MAX_PER_EP as u16 { + return Err(UsbError::EndpointMemoryOverflow); + } - let buffer = self.buffers.borrow_mut().allocate_buffer(allocated_size)?; + let buffer = self.buffers.borrow_mut().allocate_buffer()?; let mut endpoints = self.endpoints.borrow_mut(); @@ -704,15 +643,8 @@ impl Inner { Some(addr) => addr.index(), }; - let addr = endpoints.allocate_endpoint( - dir, - idx, - ep_type, - allocated_size, - max_packet_size, - interval, - buffer, - )?; + let addr = + endpoints.allocate_endpoint(dir, idx, ep_type, max_packet_size, interval, buffer)?; Ok(addr) } @@ -731,6 +663,26 @@ impl Inner { false } + /// Configure the multi-packet reception of an OUT endpoint + fn set_out_ep_multi_packet_size( + &mut self, + ep: EndpointAddress, + size: u16, + ) -> Result<(), UsbError> { + { + let config = &mut self.endpoints.borrow_mut().endpoints[ep.index()].bank0; + if size > ALLOC_SIZE_MAX_PER_EP as u16 { + return Err(UsbError::EndpointMemoryOverflow); + } else if size % config.max_packet_size != 0 { + return Err(UsbError::Unsupported); + } else { + config.multi_packet_size = size; + } + } + self.bank0(ep)?.flush_config(); + Ok(()) + } + fn poll(&self) -> PollResult { let intflags = self.usb().intflag().read(); if intflags.eorst().bit() { @@ -872,6 +824,52 @@ impl UsbBus { pub fn check_sof_interrupt(&self) -> bool { disable_interrupts(|cs| self.inner.borrow(cs).borrow_mut().check_sof_interrupt()) } + + /// Configures the Multi-Packet-Rx feature of the USB peripheral. + /// + /// This allows for the USB Peripheral to ACK multiple incomming packets in hardware, and then + /// only fire an interrupt once the buffer is full. This will reduce the number of USB interrupts, + /// especially when dealing with BULK endpoints. + /// + /// The default behaviour of the endpoint is to trigger an interrupt as soon as any amount of + /// data is received (`size = 0`). + /// + /// The Buffer size can be configured using the HAL's feature flags: + /// + /// |Feature flag|Endpoint buffer size|Max number of hardware ACK packets| + /// |:-:|:-:|:-:| + /// |`usb-buffer-1k`|64|1| + /// |`usb-buffer-2k`|128|2| + /// |`usb-buffer-4k`|256|4| + /// |`usb-buffer-8k`|512|8| + /// |`usb-buffer-16k`|1024|16| + /// + /// **NOTE**: Above table assumes a 64 byte packet size (USB FS Standard) + /// + /// ## Requirements + /// 1. `size` is less than the allocated buffer of the endpoint. + /// 2. `size` is a multiple of the endpoints packet size. + /// 3. The provided `ep` is an OUT endpoint. + /// + /// ## Notes + /// * For IN endpoints, multi-packet transfer is automatically handled without + /// any user input. + /// * If less than `size` bytes are received by the endpoint, then it will NOT + /// fire an interrupt. + /// * ZLP packets still result in an interrupt being fired, regardless + /// of the endpoints received data length + pub fn configure_out_endpoint_multipacket_rx( + &self, + ep: EndpointAddress, + size: u16, + ) -> Result<(), UsbError> { + disable_interrupts(|cs| { + self.inner + .borrow(cs) + .borrow_mut() + .set_out_ep_multi_packet_size(ep, size) + }) + } } impl usb_device::bus::UsbBus for UsbBus { diff --git a/hal/src/peripherals/usb/d11/devicedesc.rs b/hal/src/peripherals/usb/d11/devicedesc.rs index fa6e579d6d0f..c2519a64ed2e 100644 --- a/hal/src/peripherals/usb/d11/devicedesc.rs +++ b/hal/src/peripherals/usb/d11/devicedesc.rs @@ -205,7 +205,7 @@ pub struct Descriptors { impl Debug for Descriptors { fn fmt(&self, fmt: &mut Formatter<'_>) -> FmtResult { for ep in 0..8 { - write!(fmt, "\nep{}: {:?}", ep, &self.desc[ep])?; + write!(fmt, "\nep{}: {:?}", ep, self.desc[ep])?; } Ok(()) } diff --git a/hal/src/peripherals/usb/d11/mod.rs b/hal/src/peripherals/usb/d11/mod.rs index 73fd05566fe4..4afd47d2a380 100644 --- a/hal/src/peripherals/usb/d11/mod.rs +++ b/hal/src/peripherals/usb/d11/mod.rs @@ -7,7 +7,9 @@ use crate::gpio::{ pub use usb_device; +mod buffer; mod bus; +pub use self::buffer::*; pub use self::bus::UsbBus; mod devicedesc; diff --git a/hal/src/peripherals/usb/d5x/buffer.rs b/hal/src/peripherals/usb/d5x/buffer.rs new file mode 100644 index 000000000000..179a12e4ea2c --- /dev/null +++ b/hal/src/peripherals/usb/d5x/buffer.rs @@ -0,0 +1,97 @@ +use core::ops::{Deref, DerefMut}; +use cortex_m::singleton; +use usb_device::{Result as UsbResult, UsbError}; + +/// Size of the buffer that holds ALL USB endpoint buffers +pub const BUFFER_SIZE: usize = { + #[cfg(feature = "usb-buffer-1k")] + { + 1024 + } + #[cfg(feature = "usb-buffer-4k")] + { + 4098 + } + #[cfg(feature = "usb-buffer-8k")] + { + 8192 + } + #[cfg(feature = "usb-buffer-16k")] + { + 16384 + } + #[cfg(not(any( + feature = "usb-buffer-1k", + feature = "usb-buffer-4k", + feature = "usb-buffer-8k", + feature = "usb-buffer-16k" + )))] + { + 2048 // Default + } +}; + +/// Size of each USB endpoints buffer (Always guaranteed to be divisible by 4) +pub const ALLOC_SIZE_MAX_PER_EP: usize = BUFFER_SIZE / 16; + +/// USB endpoint storage buffer, aligned to 4 bytes +#[repr(C, align(4))] +pub struct Buffer { + inner: [u8; BUFFER_SIZE], +} + +impl Default for Buffer { + fn default() -> Self { + Self { + inner: [0; BUFFER_SIZE], + } + } +} + +impl Deref for Buffer { + type Target = [u8; BUFFER_SIZE]; + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +impl DerefMut for Buffer { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.inner + } +} + +fn buffer() -> &'static mut Buffer { + singleton!(: Buffer = Buffer::default() ).unwrap() +} + +pub struct BufferAllocator { + buffers: &'static mut Buffer, + next_buf: usize, +} + +impl Default for BufferAllocator { + fn default() -> Self { + Self { + next_buf: 0, + buffers: buffer(), + } + } +} + +impl BufferAllocator { + + /// Allocates a fixed buffer of [`ALLOC_SIZE_MAX_PER_EP`] + pub fn allocate_buffer(&mut self) -> UsbResult<*mut u8> { + // Do all range checks first + if self.next_buf >= self.buffers.len() + || self.next_buf + ALLOC_SIZE_MAX_PER_EP > self.buffers.len() + { + return Err(UsbError::EndpointMemoryOverflow); + } + + let start_addr = &mut self.buffers[self.next_buf] as *mut u8; + self.next_buf += ALLOC_SIZE_MAX_PER_EP; + Ok(start_addr) + } +} diff --git a/hal/src/peripherals/usb/d5x/bus.rs b/hal/src/peripherals/usb/d5x/bus.rs index 1441ff780dc2..7a8aa70e6000 100644 --- a/hal/src/peripherals/usb/d5x/bus.rs +++ b/hal/src/peripherals/usb/d5x/bus.rs @@ -8,16 +8,15 @@ use super::Descriptors; use crate::calibration::{usb_transn_cal, usb_transp_cal, usb_trim_cal}; use crate::clock; -use crate::gpio::{AlternateH, AnyPin, Pin, PA24, PA25}; +use crate::gpio::{AlternateH, AnyPin, PA24, PA25, Pin}; use crate::pac; use crate::pac::usb::Device; use crate::pac::{Mclk, Usb}; +use crate::usb::buffer::*; use crate::usb::devicedesc::DeviceDescBank; use core::cell::{Ref, RefCell, RefMut}; use core::marker::PhantomData; -use core::mem; -use cortex_m::singleton; -use critical_section::{with as disable_interrupts, Mutex}; +use critical_section::{Mutex, with as disable_interrupts}; use usb_device::bus::PollResult; use usb_device::endpoint::{EndpointAddress, EndpointType}; use usb_device::{Result as UsbResult, UsbDirection, UsbError}; @@ -51,22 +50,17 @@ impl From for EndpointTypeBits { #[derive(Default, Clone, Copy)] struct EPConfig { ep_type: EndpointTypeBits, - allocated_size: u16, max_packet_size: u16, + multi_packet_size: u16, addr: usize, } impl EPConfig { - fn new( - ep_type: EndpointType, - allocated_size: u16, - max_packet_size: u16, - buffer_addr: *mut u8, - ) -> Self { + fn new(ep_type: EndpointType, max_packet_size: u16, buffer_addr: *mut u8) -> Self { Self { ep_type: ep_type.into(), - allocated_size, max_packet_size, + multi_packet_size: 0, addr: buffer_addr as usize, } } @@ -127,7 +121,6 @@ impl AllEndpoints { dir: UsbDirection, idx: usize, ep_type: EndpointType, - allocated_size: u16, max_packet_size: u16, _interval: u8, buffer_addr: *mut u8, @@ -140,57 +133,12 @@ impl AllEndpoints { return Err(UsbError::EndpointOverflow); } - *bank = EPConfig::new(ep_type, allocated_size, max_packet_size, buffer_addr); + *bank = EPConfig::new(ep_type, max_packet_size, buffer_addr); Ok(EndpointAddress::from_parts(idx, dir)) } } -// FIXME: replace with more general heap? -const BUFFER_SIZE: usize = 2048; -fn buffer() -> &'static mut [u8; BUFFER_SIZE] { - singleton!(: [u8; BUFFER_SIZE] = [0; BUFFER_SIZE] ).unwrap() -} - -struct BufferAllocator { - buffers: &'static mut [u8; BUFFER_SIZE], - next_buf: u16, -} - -impl BufferAllocator { - fn new() -> Self { - Self { - next_buf: 0, - buffers: buffer(), - } - } - - fn allocate_buffer(&mut self, size: u16) -> UsbResult<*mut u8> { - debug_assert!(size & 1 == 0); - - let start_addr = &mut self.buffers[self.next_buf as usize] as *mut u8; - let buf_end = unsafe { start_addr.add(BUFFER_SIZE) }; - - // The address must be 32-bit aligned, so allow for that here - // by offsetting by an appropriate alignment. - let offset = start_addr.align_offset(mem::align_of::()); - let start_addr = unsafe { start_addr.add(offset) }; - - if start_addr >= buf_end { - return Err(UsbError::EndpointMemoryOverflow); - } - - let end_addr = unsafe { start_addr.offset(size as isize) }; - if end_addr > buf_end { - return Err(UsbError::EndpointMemoryOverflow); - } - - self.next_buf = unsafe { end_addr.sub(self.buffers.as_ptr() as usize) as u16 }; - - Ok(start_addr) - } -} - struct Inner { desc: RefCell, _dm_pad: Pin, @@ -299,7 +247,7 @@ impl Bank<'_, InBank> { /// bank1 buffer. The caller must call set_ready() to finalize the /// transfer. pub fn write(&mut self, buf: &[u8]) -> UsbResult { - let size = buf.len().min(self.config().allocated_size as usize); + let size = buf.len().min(ALLOC_SIZE_MAX_PER_EP); let desc = self.desc_bank(); unsafe { @@ -383,7 +331,7 @@ impl Bank<'_, OutBank> { let desc = self.desc_bank(); desc.set_address(config.addr as *mut u8); desc.set_endpoint_size(config.max_packet_size); - desc.set_multi_packet_size(0); + desc.set_multi_packet_size(config.multi_packet_size); desc.set_byte_count(0); } } @@ -398,6 +346,7 @@ impl Bank<'_, OutBank> { /// must call set_ready to indicate the buffer is free for the next /// transfer. pub fn read(&mut self, buf: &mut [u8]) -> UsbResult { + let mp_size = self.config().multi_packet_size; let desc = self.desc_bank(); let size = desc.get_byte_count() as usize; @@ -410,7 +359,7 @@ impl Bank<'_, OutBank> { } desc.set_byte_count(0); - desc.set_multi_packet_size(0); + desc.set_multi_packet_size(mp_size); Ok(size) } @@ -541,7 +490,7 @@ impl UsbBus { _dm_pad: dm_pad.into().into_mode::(), _dp_pad: dp_pad.into().into_mode::(), desc, - buffers: RefCell::new(BufferAllocator::new()), + buffers: RefCell::new(BufferAllocator::default()), endpoints: RefCell::new(AllEndpoints::new()), }; @@ -707,22 +656,12 @@ impl Inner { max_packet_size: u16, interval: u8, ) -> UsbResult { - // The USB hardware encodes the maximum packet size in 3 bits, so - // reserve enough buffer that the hardware won't overwrite it even if - // the other side issues an overly-long transfer. - let allocated_size = match max_packet_size { - 1..=8 => 8, - 9..=16 => 16, - 17..=32 => 32, - 33..=64 => 64, - 65..=128 => 128, - 129..=256 => 256, - 257..=512 => 512, - 513..=1023 => 1024, - _ => return Err(UsbError::Unsupported), - }; + // packet size is too big to fit into an endpoint buffer + if max_packet_size > ALLOC_SIZE_MAX_PER_EP as u16 { + return Err(UsbError::EndpointMemoryOverflow); + } - let buffer = self.buffers.borrow_mut().allocate_buffer(allocated_size)?; + let buffer = self.buffers.borrow_mut().allocate_buffer()?; let mut endpoints = self.endpoints.borrow_mut(); @@ -731,15 +670,8 @@ impl Inner { Some(addr) => addr.index(), }; - let addr = endpoints.allocate_endpoint( - dir, - idx, - ep_type, - allocated_size, - max_packet_size, - interval, - buffer, - )?; + let addr = + endpoints.allocate_endpoint(dir, idx, ep_type, max_packet_size, interval, buffer)?; Ok(addr) } @@ -758,6 +690,26 @@ impl Inner { false } + /// Configure the multi-packet reception of an OUT endpoint + fn set_out_ep_multi_packet_size( + &mut self, + ep: EndpointAddress, + size: u16, + ) -> Result<(), UsbError> { + { + let config = &mut self.endpoints.borrow_mut().endpoints[ep.index()].bank0; + if size > ALLOC_SIZE_MAX_PER_EP as u16 { + return Err(UsbError::EndpointMemoryOverflow); + } else if size % config.max_packet_size != 0 { + return Err(UsbError::Unsupported); + } else { + config.multi_packet_size = size; + } + } + self.bank0(ep)?.flush_config(); + Ok(()) + } + fn poll(&self) -> PollResult { let intflags = self.usb().intflag().read(); if intflags.eorst().bit() { @@ -899,6 +851,52 @@ impl UsbBus { pub fn check_sof_interrupt(&self) -> bool { disable_interrupts(|cs| self.inner.borrow(cs).borrow_mut().check_sof_interrupt()) } + + /// Configures the Multi-Packet-Rx feature of the USB peripheral. + /// + /// This allows for the USB Peripheral to ACK multiple incomming packets in hardware, and then + /// only fire an interrupt once the buffer is full. This will reduce the number of USB interrupts, + /// especially when dealing with BULK endpoints. + /// + /// The default behaviour of the endpoint is to trigger an interrupt as soon as any amount of + /// data is received (`size = 0`). + /// + /// The Buffer size can be configured using the HAL's feature flags: + /// + /// |Feature flag|Endpoint buffer size|Max number of hardware ACK packets| + /// |:-:|:-:|:-:| + /// |`usb-buffer-1k`|64|1| + /// |`usb-buffer-2k`|128|2| + /// |`usb-buffer-4k`|256|4| + /// |`usb-buffer-8k`|512|8| + /// |`usb-buffer-16k`|1024|16| + /// + /// **NOTE**: Above table assumes a 64 byte packet size (USB FS Standard) + /// + /// ## Requirements + /// 1. `size` is less than the allocated buffer of the endpoint. + /// 2. `size` is a multiple of the endpoints packet size. + /// 3. The provided `ep` is an OUT endpoint. + /// + /// ## Notes + /// * For IN endpoints, multi-packet transfer is automatically handled without + /// any user input. + /// * If less than `size` bytes are received by the endpoint, then it will NOT + /// fire an interrupt. + /// * ZLP packets still result in an interrupt being fired, regardless + /// of the endpoints received data length + pub fn configure_out_endpoint_multipacket_rx( + &self, + ep: EndpointAddress, + size: u16, + ) -> Result<(), UsbError> { + disable_interrupts(|cs| { + self.inner + .borrow(cs) + .borrow_mut() + .set_out_ep_multi_packet_size(ep, size) + }) + } } impl usb_device::bus::UsbBus for UsbBus { diff --git a/hal/src/peripherals/usb/d5x/devicedesc.rs b/hal/src/peripherals/usb/d5x/devicedesc.rs index f4752cb195b0..e9427268a783 100644 --- a/hal/src/peripherals/usb/d5x/devicedesc.rs +++ b/hal/src/peripherals/usb/d5x/devicedesc.rs @@ -206,7 +206,7 @@ pub struct Descriptors { impl Debug for Descriptors { fn fmt(&self, fmt: &mut Formatter<'_>) -> FmtResult { for ep in 0..8 { - write!(fmt, "\nep{}: {:?}", ep, &self.desc[ep])?; + write!(fmt, "\nep{}: {:?}", ep, self.desc[ep])?; } Ok(()) } diff --git a/hal/src/peripherals/usb/d5x/mod.rs b/hal/src/peripherals/usb/d5x/mod.rs index 10d22657214e..82cc5be5dec5 100644 --- a/hal/src/peripherals/usb/d5x/mod.rs +++ b/hal/src/peripherals/usb/d5x/mod.rs @@ -7,7 +7,9 @@ use crate::gpio::{ pub use usb_device; +mod buffer; mod bus; +pub use self::buffer::*; pub use self::bus::UsbBus; mod devicedesc; diff --git a/hal/src/rtc/rtic/backends.rs b/hal/src/rtc/rtic/backends.rs index 29bab219defc..20223b73a2d6 100644 --- a/hal/src/rtc/rtic/backends.rs +++ b/hal/src/rtc/rtic/backends.rs @@ -132,11 +132,11 @@ macro_rules! __internal_basic_backend { } fn enable_timer() { - <$mode>::enable(unsafe { &pac::Rtc::steal() }); + <$mode>::enable_interrupt::<$rtic_int>(unsafe { &pac::Rtc::steal() }); } fn disable_timer() { - <$mode>::disable(unsafe { &pac::Rtc::steal() }); + <$mode>::disable_interrupt::<$rtic_int>(unsafe { &pac::Rtc::steal() }); } fn on_interrupt() { @@ -243,12 +243,12 @@ macro_rules! __internal_half_period_counting_backend { fn enable_timer() { let rtc = unsafe { pac::Rtc::steal() }; - <$mode>::enable(&rtc); + <$mode>::enable_interrupt::<$rtic_int>(&rtc); } fn disable_timer() { let rtc = unsafe { pac::Rtc::steal() }; - <$mode>::disable(&rtc); + <$mode>::disable_interrupt::<$rtic_int>(&rtc); } fn on_interrupt() { diff --git a/hal/src/sercom/dma.rs b/hal/src/sercom/dma.rs index 02e1c0a337d1..703b8f0b0d10 100644 --- a/hal/src/sercom/dma.rs +++ b/hal/src/sercom/dma.rs @@ -176,10 +176,9 @@ where /// the provided buffer. /// /// In order to be (safely) non-blocking, his method has to take a `'static` - /// buffer. If you'd rather use DMA with the blocking - /// [`embedded_io::Read`] trait, and avoid having - /// to use static buffers, - /// use [`Uart::with_rx_channel`](Self::with_tx_channel) instead. + /// buffer. If you'd rather use DMA with the blocking [`embedded_io::Read`] + /// trait, and avoid having to use static buffers, use + /// [`Uart::with_rx_channel`](Self::with_tx_channel) instead. #[inline] #[hal_macro_helper] pub fn receive_with_dma( @@ -218,10 +217,9 @@ where /// provided buffer. /// /// In order to be (safely) non-blocking, his method takes a `'static` - /// buffer. If you'd rather use DMA with the blocking - /// [`embedded_io::Write`] trait, and avoid - /// having to use static buffers, - /// use[`Uart::with_tx_channel`](Self::with_tx_channel) instead. + /// buffer. If you'd rather use DMA with the blocking [`embedded_io::Write`] + /// trait, and avoid having to use static buffers, use + /// [`Uart::with_tx_channel`](Self::with_tx_channel) instead. #[inline] #[hal_macro_helper] pub fn send_with_dma( diff --git a/hal/src/sercom/mod.rs b/hal/src/sercom/mod.rs index e606b4b64fe2..1c678fa9438c 100644 --- a/hal/src/sercom/mod.rs +++ b/hal/src/sercom/mod.rs @@ -2,25 +2,41 @@ //! //! The SERCOM module is used to configure the SERCOM peripherals as USART, SPI //! or I2C interfaces. +//! //! # Undocumented features //! //! The ATSAMx5x chips contain certain features that aren't documented in the //! datasheet. These features are implemented in the HAL based on //! experimentation with certain boards which have verifiably demonstrated that -//! those features work as intended. +//! those features work as intended. These undocumented features are disabled by +//! default, and can be enabled by enabling the `undoc-features` Cargo feature +//! +//! ## SAMD21: +//! * `PA00` is I2C-capable according to `circuit_playground_express`. As such, +//! `PA00` implements [`IsI2cPad`]. +//! +//! * `PA01` is I2C-capable according to `circuit_playground_express`. As such, +//! PA01 implements [`IsI2cPad`]. +//! +//! * `PB02` is I2C-capable according to `circuit_playground_express`. As such, +//! PB02 implements [`IsI2cPad`]. +//! +//! * `PB03` is I2C-capable according to `circuit_playground_express`. As such, +//! PB03 implements [`IsI2cPad`]. //! -//! * [`UndocIoSet1`]: Implement an undocumented `IoSet` for PA16, PA17, PB22 & -//! PB23 configured for [`Sercom1`]. The pygamer & feather_m4 use this +//! ## SAMx5x devices: +//! * `UndocIoSet1`: Implement an undocumented `IoSet` for PA16, PA17, PB22 & +//! PB23 configured for [`Sercom1`]. The `pygamer` & `feather_m4` use this //! combination, but it is not listed as valid in the datasheet. //! -//! * [`UndocIoSet2`]: Implement an undocumented `IoSet` for PA00, PA01, PB22 & -//! PB23 configured for [`Sercom1`]. The itsybitsy_m4 uses this combination, +//! * `UndocIoSet2`: Implement an undocumented `IoSet` for PA00, PA01, PB22 & +//! PB23 configured for [`Sercom1`]. The `itsybitsy_m4` uses this combination, //! but it is not listed as valid in the datasheet. //! -//! * [`PB02`] is I2C-capable according to metro_m4. As such, [`PB02`] +//! * [`PB02`] is I2C-capable according to `metro_m4`. As such, [`PB02`] //! implements [`IsI2cPad`]. //! -//! * [`PB03`] is I2C-capable according to metro_m4. As such, [`PB03`] +//! * [`PB03`] is I2C-capable according to `metro_m4`. As such, [`PB03`] //! implements [`IsI2cPad`]. //! //! [`PB02`]: crate::gpio::pin::PB02 diff --git a/hal/src/sercom/pad.rs b/hal/src/sercom/pad.rs index 6f0a76d55fd5..38ff057a8f14 100644 --- a/hal/src/sercom/pad.rs +++ b/hal/src/sercom/pad.rs @@ -309,6 +309,14 @@ mod ioset { }); // Implement IoSets for NoneT, making it act as a wildcard. + #[cfg(not(feature = "undoc-features"))] + seq!(N in 1..=5 { + impl IoSets for NoneT { + type SetList = mk_hlist! ( #(::Order, )* ::Order ); + } + }); + + #[cfg(feature = "undoc-features")] seq!(N in 1..=6 { impl IoSets for NoneT { type SetList = mk_hlist! ( #(::Order, )* ::Order, ::Order ); @@ -335,8 +343,11 @@ mod ioset { /// variants. /// /// [type-level enum]: crate::typelevel#type-level-enum + #[cfg(feature = "undoc-features")] pub enum UndocIoSet1 {} + #[cfg(feature = "undoc-features")] impl Sealed for UndocIoSet1 {} + #[cfg(feature = "undoc-features")] impl IoSet for UndocIoSet1 { type Order = typenum::U8; } @@ -361,8 +372,11 @@ mod ioset { /// variants. /// /// [type-level enum]: crate::typelevel#type-level-enum + #[cfg(feature = "undoc-features")] pub enum UndocIoSet2 {} + #[cfg(feature = "undoc-features")] impl Sealed for UndocIoSet2 {} + #[cfg(feature = "undoc-features")] impl IoSet for UndocIoSet2 { type Order = typenum::U9; } diff --git a/hal/src/sercom/pad/impl_pad_thumbv6m.rs b/hal/src/sercom/pad/impl_pad_thumbv6m.rs index 72e162a6223e..9a0424a606ea 100644 --- a/hal/src/sercom/pad/impl_pad_thumbv6m.rs +++ b/hal/src/sercom/pad/impl_pad_thumbv6m.rs @@ -63,12 +63,13 @@ macro_rules! pad_table { #[$id_cfg:meta] $PinId:ident { $( - $( #[$sercom_cfg:meta] )? + $( #[$sercom_cfg:meta] )* $Cfg:ident: ( $Sercom:ident, $PadNum:ident ) $( + $I2C:ident )?, )+ } ) => { $( + #[$id_cfg] $( #[$sercom_cfg] )? pad_info!( $PinId, $Cfg, $Sercom, $PadNum $( + $I2C )? ); @@ -78,13 +79,13 @@ macro_rules! pad_table { ( $PinId:ident { $( - $( #[$sercom_cfg:meta] )? + $( #[$sercom_cfg:meta] )* $Cfg:ident: ( $Sercom:ident, $PadNum:ident ) $( + $I2C:ident )?, )+ } ) => { $( - $( #[$sercom_cfg] )? + $( #[$sercom_cfg] )* pad_info!( $PinId, $Cfg, $Sercom, $PadNum $( + $I2C )? ); )+ }; @@ -94,7 +95,7 @@ macro_rules! pad_table { $( #[$id_cfg:meta] )? $PinId:ident { $( - $( #[$sercom_cfg:meta] )? + $( #[$sercom_cfg:meta] )* $Cfg:ident: ( $Sercom:ident, $PadNum:ident ) $( + $I2C:ident )?, )+ } @@ -105,7 +106,7 @@ macro_rules! pad_table { $( #[$id_cfg] )? $PinId { $( - $( #[$sercom_cfg] )? + $( #[$sercom_cfg] )* $Cfg: ( $Sercom, $PadNum ) $( + $I2C )?, )+ } @@ -250,13 +251,23 @@ pad_table!( pad_table!( #[hal_cfg("pa00")] PA00 { + #[cfg(not(feature = "undoc-features"))] #[hal_cfg("sercom1")] D: (Sercom1, Pad0), + + #[cfg(feature = "undoc-features")] + #[hal_cfg("sercom1")] + D: (Sercom1, Pad0) + I2C, } #[hal_cfg("pa01")] PA01 { + #[cfg(not(feature = "undoc-features"))] #[hal_cfg("sercom1")] D: (Sercom1, Pad1), + + #[cfg(feature = "undoc-features")] + #[hal_cfg("sercom1")] + D: (Sercom1, Pad1) + I2C, } #[hal_cfg("pa04")] PA04 { @@ -426,23 +437,43 @@ pad_table!( } #[hal_cfg("pb02")] PB02 { + #[cfg(not(feature = "undoc-features"))] #[hal_cfg("sercom5")] D: (Sercom5, Pad0), + + #[cfg(feature = "undoc-features")] + #[hal_cfg("sercom5")] + D: (Sercom5, Pad0) + I2C, } #[hal_cfg("pb03")] PB03 { + #[cfg(not(feature = "undoc-features"))] #[hal_cfg("sercom5")] D: (Sercom5, Pad1), + + #[cfg(feature = "undoc-features")] + #[hal_cfg("sercom5")] + D: (Sercom5, Pad1) + I2C, } #[hal_cfg("pb08")] PB08 { + #[cfg(not(feature = "undoc-features"))] #[hal_cfg("sercom4")] D: (Sercom4, Pad0), + + #[cfg(feature = "undoc-features")] + #[hal_cfg("sercom4")] + D: (Sercom4, Pad0) + I2C, } #[hal_cfg("pb09")] PB09 { + #[cfg(not(feature = "undoc-features"))] #[hal_cfg("sercom4")] D: (Sercom4, Pad1), + + #[cfg(feature = "undoc-features")] + #[hal_cfg("sercom4")] + D: (Sercom4, Pad1) + I2C, } #[hal_cfg("pb10")] PB10 { diff --git a/hal/src/sercom/pad/impl_pad_thumbv7em.rs b/hal/src/sercom/pad/impl_pad_thumbv7em.rs index 53d8314bf496..386b65072de7 100644 --- a/hal/src/sercom/pad/impl_pad_thumbv7em.rs +++ b/hal/src/sercom/pad/impl_pad_thumbv7em.rs @@ -63,27 +63,27 @@ macro_rules! pad_table { #[$id_cfg:meta] $PinId:ident { $( - $( #[$sercom_cfg:meta] )? + $( #[$sercom_cfg:meta] )* $Cfg:ident: ( $Sercom:ident, $PadNum:ident, $( $IoSet:ident ),+ ) $( + $I2C:ident )?, )+ } ) => { $( #[$id_cfg] - $( #[$sercom_cfg] )? + $( #[$sercom_cfg] )* pad_info!( $PinId, $Cfg, $Sercom, $PadNum, $( $IoSet ),+ $( + $I2C )?); )+ }; ( $PinId:ident { $( - $( #[$sercom_cfg:meta] )? + $( #[$sercom_cfg:meta] )* $Cfg:ident: ( $Sercom:ident, $PadNum:ident, $( $IoSet:ident ),+ ) $( + $I2C:ident )?, )+ } ) => { $( - $( #[$sercom_cfg] )? + $( #[$sercom_cfg] )* pad_info!( $PinId, $Cfg, $Sercom, $PadNum, $( $IoSet ),+ $( + $I2C )?); )+ }; @@ -92,7 +92,7 @@ macro_rules! pad_table { $( #[$id_cfg:meta] )? $PinId:ident { $( - $( #[$sercom_cfg:meta] )? + $( #[$sercom_cfg:meta] )* $Cfg:ident: ( $Sercom:ident, $PadNum:ident, $( $IoSet:ident ),+ ) $( + $I2C:ident )?, )+ } @@ -103,7 +103,7 @@ macro_rules! pad_table { $( #[$id_cfg] )? $PinId{ $( - $( #[$sercom_cfg] )? + $( #[$sercom_cfg] )* $Cfg: ( $Sercom, $PadNum, $( $IoSet),+ ) $( + $I2C )?, )+ } @@ -123,11 +123,21 @@ macro_rules! pad_table { pad_table!( #[hal_cfg("pa00")] PA00 { + #[cfg(not(feature = "undoc-features"))] + #[hal_cfg("sercom1")] + D: (Sercom1, Pad0, IoSet4), + + #[cfg(feature = "undoc-features")] #[hal_cfg("sercom1")] D: (Sercom1, Pad0, IoSet4, UndocIoSet2), } #[hal_cfg("pa01")] PA01 { + #[cfg(not(feature = "undoc-features"))] + #[hal_cfg("sercom1")] + D: (Sercom1, Pad1, IoSet4), + + #[cfg(feature = "undoc-features")] #[hal_cfg("sercom1")] D: (Sercom1, Pad1, IoSet4, UndocIoSet2), } @@ -209,6 +219,11 @@ pad_table!( } #[hal_cfg("pa16")] PA16 { + #[hal_cfg("sercom1")] + #[cfg(not(feature = "undoc-features"))] + C: (Sercom1, Pad0, IoSet1) + I2C, + + #[cfg(feature = "undoc-features")] #[hal_cfg("sercom1")] C: (Sercom1, Pad0, IoSet1, UndocIoSet1) + I2C, #[hal_cfg("sercom3")] @@ -216,6 +231,11 @@ pad_table!( } #[hal_cfg("pa17")] PA17 { + #[cfg(not(feature = "undoc-features"))] + #[hal_cfg("sercom1")] + C: (Sercom1, Pad1, IoSet1) + I2C, + + #[cfg(feature = "undoc-features")] #[hal_cfg("sercom1")] C: (Sercom1, Pad1, IoSet1, UndocIoSet1) + I2C, #[hal_cfg("sercom3")] @@ -305,13 +325,21 @@ pad_table!( } #[hal_cfg("pb02")] PB02 { - // According to Metro M4, PB02 is I2C-capable. This disagrees with datasheet table 6-8. + #[cfg(not(feature = "undoc-features"))] + #[hal_cfg("sercom5")] + D: (Sercom5, Pad0, IoSet6), + + #[cfg(feature = "undoc-features")] #[hal_cfg("sercom5")] D: (Sercom5, Pad0, IoSet6) + I2C, } #[hal_cfg("pb03")] PB03 { - // According to Metro M4, PB03 is I2C-capable. This disagrees with datasheet table 6-8. + #[cfg(not(feature = "undoc-features"))] + #[hal_cfg("sercom5")] + D: (Sercom5, Pad1, IoSet6), + + #[cfg(feature = "undoc-features")] #[hal_cfg("sercom5")] D: (Sercom5, Pad1, IoSet6) + I2C, } @@ -399,6 +427,11 @@ pad_table!( } #[hal_cfg("pb22")] PB22 { + #[cfg(not(feature = "undoc-features"))] + #[hal_cfg("sercom1")] + C: (Sercom1, Pad2, IoSet3), + + #[cfg(feature = "undoc-features")] #[hal_cfg("sercom1")] C: (Sercom1, Pad2, IoSet3, UndocIoSet1, UndocIoSet2), #[hal_cfg("sercom5")] @@ -406,6 +439,11 @@ pad_table!( } #[hal_cfg("pb23")] PB23 { + #[cfg(not(feature = "undoc-features"))] + #[hal_cfg("sercom1")] + C: (Sercom1, Pad3, IoSet3), + + #[cfg(feature = "undoc-features")] #[hal_cfg("sercom1")] C: (Sercom1, Pad3, IoSet3, UndocIoSet1, UndocIoSet2), #[hal_cfg("sercom5")] diff --git a/hal/src/sercom/uart/impl_ehal.rs b/hal/src/sercom/uart/impl_ehal.rs index 54df1068cf49..cf439f08fd79 100644 --- a/hal/src/sercom/uart/impl_ehal.rs +++ b/hal/src/sercom/uart/impl_ehal.rs @@ -107,12 +107,36 @@ where return Ok(0); } + while !self.read_flags_errors()?.contains(Flags::RXC) { + core::hint::spin_loop(); + } + + let mut bytes_read = 0; + + while self.read_flags_errors()?.contains(Flags::RXC) { + let w = nb::block!(>::read(self))?; + buf[bytes_read] = w; + bytes_read += 1; + } + + Ok(bytes_read) + } + + #[inline] + fn read_exact( + &mut self, + buf: &mut [u8], + ) -> Result<(), embedded_io::ReadExactError> { + if buf.is_empty() { + return Ok(()); + } + for byte in buf.iter_mut() { let w = nb::block!(>::read(self))?; *byte = w; } - Ok(buf.len()) + Ok(()) } } diff --git a/rustfmt.sh b/rustfmt.sh index 83555c8b3b6e..12a1d195bf45 100755 --- a/rustfmt.sh +++ b/rustfmt.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + RET=0 # first, all boards & their examples