diff --git a/.github/ci/book.sh b/.github/ci/book.sh new file mode 100755 index 000000000..285cdc8fa --- /dev/null +++ b/.github/ci/book.sh @@ -0,0 +1,17 @@ +#!/bin/bash +## on push branch=main + +set -euxo pipefail + +make -C docs + +export KUBECONFIG=/ci/secrets/kubeconfig.yml +POD=$(kubectl -n embassy get po -l app=website -o jsonpath={.items[0].metadata.name}) + +mkdir -p build +mv docs/book build/book +tar -C build -cf book.tar book +kubectl exec $POD -- mkdir -p /usr/share/nginx/html +kubectl cp book.tar $POD:/usr/share/nginx/html/ +kubectl exec $POD -- find /usr/share/nginx/html +kubectl exec $POD -- tar -C /usr/share/nginx/html -xvf /usr/share/nginx/html/book.tar diff --git a/.vscode/settings.json b/.vscode/settings.json index 220d25914..48d0957e6 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -9,12 +9,14 @@ "rust-analyzer.check.noDefaultFeatures": true, "rust-analyzer.cargo.noDefaultFeatures": true, "rust-analyzer.showUnlinkedFileNotification": false, - // uncomment the target of your chip. + // Uncomment the target of your chip. //"rust-analyzer.cargo.target": "thumbv6m-none-eabi", //"rust-analyzer.cargo.target": "thumbv7m-none-eabi", "rust-analyzer.cargo.target": "thumbv7em-none-eabi", + //"rust-analyzer.cargo.target": "thumbv7em-none-eabihf", //"rust-analyzer.cargo.target": "thumbv8m.main-none-eabihf", "rust-analyzer.cargo.features": [ + // Comment out these features when working on the examples. Most example crates do not have any cargo features. "stm32f446re", "time-driver-any", "unstable-pac", @@ -22,9 +24,10 @@ "rt", ], "rust-analyzer.linkedProjects": [ - // Uncomment ONE line for the chip you want to work on. - // This makes rust-analyzer work on the example crate and all its dependencies. "embassy-stm32/Cargo.toml", + // To work on the examples, comment the line above and all of the cargo.features lines, + // then uncomment ONE line below to select the chip you want to work on. + // This makes rust-analyzer work on the example crate and all its dependencies. // "examples/nrf52840-rtic/Cargo.toml", // "examples/nrf5340/Cargo.toml", // "examples/nrf-rtos-trace/Cargo.toml", diff --git a/README.md b/README.md index 0bd1ac594..65ccaa967 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,7 @@ Rust's async/await allows - esp-rs, for the Espressif Systems ESP32 series of chips. - Embassy HAL support for Espressif chips is being developed in the [esp-rs/esp-hal](https://github.com/esp-rs/esp-hal) repository. - Async WiFi, Bluetooth and ESP-NOW is being developed in the [esp-rs/esp-wifi](https://github.com/esp-rs/esp-wifi) repository. + - ch32-hal, for the WCH 32-bit RISC-V(CH32V) series of chips. - **Time that Just Works** - No more messing with hardware timers. embassy_time provides Instant, Duration and Timer types that are globally available and never overflow. @@ -99,12 +100,7 @@ Examples are found in the `examples/` folder separated by the chip manufacturer ### Running examples -- Install `probe-rs`. - -```bash -cargo install probe-rs --features cli -``` - +- Install `probe-rs` following the instructions at . - Change directory to the sample's base directory. For example: ```bash diff --git a/ci.sh b/ci.sh index 6fff1e68c..0b5308153 100755 --- a/ci.sh +++ b/ci.sh @@ -171,11 +171,11 @@ cargo batch \ --- build --release --manifest-path embassy-boot-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9160-ns \ --- build --release --manifest-path embassy-boot-rp/Cargo.toml --target thumbv6m-none-eabi \ --- build --release --manifest-path embassy-boot-stm32/Cargo.toml --target thumbv7em-none-eabi --features embassy-stm32/stm32wl55jc-cm4 \ - --- build --release --manifest-path docs/modules/ROOT/examples/basic/Cargo.toml --target thumbv7em-none-eabi \ - --- build --release --manifest-path docs/modules/ROOT/examples/layer-by-layer/blinky-pac/Cargo.toml --target thumbv7em-none-eabi \ - --- build --release --manifest-path docs/modules/ROOT/examples/layer-by-layer/blinky-hal/Cargo.toml --target thumbv7em-none-eabi \ - --- build --release --manifest-path docs/modules/ROOT/examples/layer-by-layer/blinky-irq/Cargo.toml --target thumbv7em-none-eabi \ - --- build --release --manifest-path docs/modules/ROOT/examples/layer-by-layer/blinky-async/Cargo.toml --target thumbv7em-none-eabi \ + --- build --release --manifest-path docs/examples/basic/Cargo.toml --target thumbv7em-none-eabi \ + --- build --release --manifest-path docs/examples/layer-by-layer/blinky-pac/Cargo.toml --target thumbv7em-none-eabi \ + --- build --release --manifest-path docs/examples/layer-by-layer/blinky-hal/Cargo.toml --target thumbv7em-none-eabi \ + --- build --release --manifest-path docs/examples/layer-by-layer/blinky-irq/Cargo.toml --target thumbv7em-none-eabi \ + --- build --release --manifest-path docs/examples/layer-by-layer/blinky-async/Cargo.toml --target thumbv7em-none-eabi \ --- build --release --manifest-path examples/nrf52840/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/nrf52840 \ --- build --release --manifest-path examples/nrf5340/Cargo.toml --target thumbv8m.main-none-eabihf --out-dir out/examples/nrf5340 \ --- build --release --manifest-path examples/nrf9160/Cargo.toml --target thumbv8m.main-none-eabihf --out-dir out/examples/nrf9160 \ @@ -187,6 +187,7 @@ cargo batch \ --- build --release --manifest-path examples/stm32f3/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/stm32f3 \ --- build --release --manifest-path examples/stm32f334/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/stm32f334 \ --- build --release --manifest-path examples/stm32f4/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/stm32f4 \ + --- build --release --manifest-path examples/stm32f469/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/stm32f469 \ --- build --release --manifest-path examples/stm32f7/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/stm32f7 \ --- build --release --manifest-path examples/stm32c0/Cargo.toml --target thumbv6m-none-eabi --out-dir out/examples/stm32c0 \ --- build --release --manifest-path examples/stm32g0/Cargo.toml --target thumbv6m-none-eabi --out-dir out/examples/stm32g0 \ diff --git a/cyw43/README.md b/cyw43/README.md index dabdf0471..5b4a5d789 100644 --- a/cyw43/README.md +++ b/cyw43/README.md @@ -23,7 +23,7 @@ TODO: ## Running the examples -- `cargo install probe-rs --features cli` +- Install `probe-rs` following the instructions at . - `cd examples/rp` ### Example 1: Scan the wifi stations - `cargo run --release --bin wifi_scan` diff --git a/cyw43/src/runner.rs b/cyw43/src/runner.rs index c72cf0def..e90316302 100644 --- a/cyw43/src/runner.rs +++ b/cyw43/src/runner.rs @@ -1,6 +1,5 @@ use embassy_futures::select::{select3, Either3}; use embassy_net_driver_channel as ch; -use embassy_sync::pubsub::PubSubBehavior; use embassy_time::{block_for, Duration, Timer}; use embedded_hal_1::digital::OutputPin; @@ -438,13 +437,16 @@ where // publish() is a deadlock risk in the current design as awaiting here prevents ioctls // The `Runner` always yields when accessing the device, so consumers always have a chance to receive the event // (if they are actively awaiting the queue) - self.events.queue.publish_immediate(events::Message::new( - Status { - event_type: evt_type, - status, - }, - event_payload, - )); + self.events + .queue + .immediate_publisher() + .publish_immediate(events::Message::new( + Status { + event_type: evt_type, + status, + }, + event_payload, + )); } } CHANNEL_TYPE_DATA => { diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 000000000..834802d3b --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,8 @@ +all: + asciidoctor -d book -D book/ index.adoc + cp -r images book + +clean: + rm -rf book + +.PHONY: all clean diff --git a/docs/README.md b/docs/README.md index 0bf3a6c89..94ebd7a05 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,4 +1,9 @@ # embassy docs -The documentation hosted at [https://embassy.dev/book](https://embassy.dev/book). Building the documentation requires -cloning the [embassy-book](https://github.com/embassy-rs/embassy-book) repository and following the instructions. +The documentation hosted at [https://embassy.dev/book](https://embassy.dev/book). Building the documentation requires the [asciidoctor](https://asciidoctor.org/) tool, and can built running `make` in this folder: + +``` +make +``` + +Then open the generated file `thebook/index.html`. diff --git a/docs/antora.yml b/docs/antora.yml deleted file mode 100644 index 9a00fa820..000000000 --- a/docs/antora.yml +++ /dev/null @@ -1,5 +0,0 @@ -name: ROOT -title: Embassy -version: dev -nav: - - modules/ROOT/nav.adoc diff --git a/docs/modules/ROOT/examples/basic/.cargo/config.toml b/docs/examples/basic/.cargo/config.toml similarity index 100% rename from docs/modules/ROOT/examples/basic/.cargo/config.toml rename to docs/examples/basic/.cargo/config.toml diff --git a/docs/examples/basic/Cargo.toml b/docs/examples/basic/Cargo.toml new file mode 100644 index 000000000..d86d4a809 --- /dev/null +++ b/docs/examples/basic/Cargo.toml @@ -0,0 +1,18 @@ +[package] +authors = ["Dario Nieuwenhuis "] +edition = "2018" +name = "embassy-basic-example" +version = "0.1.0" +license = "MIT OR Apache-2.0" + +[dependencies] +embassy-executor = { version = "0.5.0", path = "../../../embassy-executor", features = ["defmt", "integrated-timers", "arch-cortex-m", "executor-thread"] } +embassy-time = { version = "0.3.0", path = "../../../embassy-time", features = ["defmt"] } +embassy-nrf = { version = "0.1.0", path = "../../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote"] } + +defmt = "0.3" +defmt-rtt = "0.3" + +cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } +cortex-m-rt = "0.7.0" +panic-probe = { version = "0.3", features = ["print-defmt"] } diff --git a/docs/modules/ROOT/examples/basic/build.rs b/docs/examples/basic/build.rs similarity index 100% rename from docs/modules/ROOT/examples/basic/build.rs rename to docs/examples/basic/build.rs diff --git a/docs/modules/ROOT/examples/basic/memory.x b/docs/examples/basic/memory.x similarity index 100% rename from docs/modules/ROOT/examples/basic/memory.x rename to docs/examples/basic/memory.x diff --git a/docs/modules/ROOT/examples/basic/src/main.rs b/docs/examples/basic/src/main.rs similarity index 100% rename from docs/modules/ROOT/examples/basic/src/main.rs rename to docs/examples/basic/src/main.rs diff --git a/docs/examples/examples b/docs/examples/examples new file mode 120000 index 000000000..d15735c1d --- /dev/null +++ b/docs/examples/examples @@ -0,0 +1 @@ +../../examples \ No newline at end of file diff --git a/docs/modules/ROOT/examples/layer-by-layer/.cargo/config.toml b/docs/examples/layer-by-layer/.cargo/config.toml similarity index 100% rename from docs/modules/ROOT/examples/layer-by-layer/.cargo/config.toml rename to docs/examples/layer-by-layer/.cargo/config.toml diff --git a/docs/modules/ROOT/examples/layer-by-layer/Cargo.toml b/docs/examples/layer-by-layer/Cargo.toml similarity index 69% rename from docs/modules/ROOT/examples/layer-by-layer/Cargo.toml rename to docs/examples/layer-by-layer/Cargo.toml index 943249a17..0f233eae5 100644 --- a/docs/modules/ROOT/examples/layer-by-layer/Cargo.toml +++ b/docs/examples/layer-by-layer/Cargo.toml @@ -8,8 +8,8 @@ members = [ ] [patch.crates-io] -embassy-executor = { path = "../../../../../embassy-executor" } -embassy-stm32 = { path = "../../../../../embassy-stm32" } +embassy-executor = { path = "../../../embassy-executor" } +embassy-stm32 = { path = "../../../embassy-stm32" } [profile.release] codegen-units = 1 diff --git a/docs/modules/ROOT/examples/layer-by-layer/blinky-async/Cargo.toml b/docs/examples/layer-by-layer/blinky-async/Cargo.toml similarity index 100% rename from docs/modules/ROOT/examples/layer-by-layer/blinky-async/Cargo.toml rename to docs/examples/layer-by-layer/blinky-async/Cargo.toml diff --git a/docs/modules/ROOT/examples/layer-by-layer/blinky-async/src/main.rs b/docs/examples/layer-by-layer/blinky-async/src/main.rs similarity index 100% rename from docs/modules/ROOT/examples/layer-by-layer/blinky-async/src/main.rs rename to docs/examples/layer-by-layer/blinky-async/src/main.rs diff --git a/docs/modules/ROOT/examples/layer-by-layer/blinky-hal/Cargo.toml b/docs/examples/layer-by-layer/blinky-hal/Cargo.toml similarity index 100% rename from docs/modules/ROOT/examples/layer-by-layer/blinky-hal/Cargo.toml rename to docs/examples/layer-by-layer/blinky-hal/Cargo.toml diff --git a/docs/modules/ROOT/examples/layer-by-layer/blinky-hal/src/main.rs b/docs/examples/layer-by-layer/blinky-hal/src/main.rs similarity index 100% rename from docs/modules/ROOT/examples/layer-by-layer/blinky-hal/src/main.rs rename to docs/examples/layer-by-layer/blinky-hal/src/main.rs diff --git a/docs/modules/ROOT/examples/layer-by-layer/blinky-irq/Cargo.toml b/docs/examples/layer-by-layer/blinky-irq/Cargo.toml similarity index 100% rename from docs/modules/ROOT/examples/layer-by-layer/blinky-irq/Cargo.toml rename to docs/examples/layer-by-layer/blinky-irq/Cargo.toml diff --git a/docs/modules/ROOT/examples/layer-by-layer/blinky-irq/src/main.rs b/docs/examples/layer-by-layer/blinky-irq/src/main.rs similarity index 100% rename from docs/modules/ROOT/examples/layer-by-layer/blinky-irq/src/main.rs rename to docs/examples/layer-by-layer/blinky-irq/src/main.rs diff --git a/docs/modules/ROOT/examples/layer-by-layer/blinky-pac/Cargo.toml b/docs/examples/layer-by-layer/blinky-pac/Cargo.toml similarity index 100% rename from docs/modules/ROOT/examples/layer-by-layer/blinky-pac/Cargo.toml rename to docs/examples/layer-by-layer/blinky-pac/Cargo.toml diff --git a/docs/modules/ROOT/examples/layer-by-layer/blinky-pac/src/main.rs b/docs/examples/layer-by-layer/blinky-pac/src/main.rs similarity index 100% rename from docs/modules/ROOT/examples/layer-by-layer/blinky-pac/src/main.rs rename to docs/examples/layer-by-layer/blinky-pac/src/main.rs diff --git a/docs/modules/ROOT/images/bootloader_flash.png b/docs/images/bootloader_flash.png similarity index 100% rename from docs/modules/ROOT/images/bootloader_flash.png rename to docs/images/bootloader_flash.png diff --git a/docs/modules/ROOT/images/embassy_executor.drawio b/docs/images/embassy_executor.drawio similarity index 100% rename from docs/modules/ROOT/images/embassy_executor.drawio rename to docs/images/embassy_executor.drawio diff --git a/docs/modules/ROOT/images/embassy_executor.png b/docs/images/embassy_executor.png similarity index 100% rename from docs/modules/ROOT/images/embassy_executor.png rename to docs/images/embassy_executor.png diff --git a/docs/modules/ROOT/images/embassy_irq.drawio b/docs/images/embassy_irq.drawio similarity index 100% rename from docs/modules/ROOT/images/embassy_irq.drawio rename to docs/images/embassy_irq.drawio diff --git a/docs/modules/ROOT/images/embassy_irq.png b/docs/images/embassy_irq.png similarity index 100% rename from docs/modules/ROOT/images/embassy_irq.png rename to docs/images/embassy_irq.png diff --git a/docs/index.adoc b/docs/index.adoc new file mode 100644 index 000000000..9c6150196 --- /dev/null +++ b/docs/index.adoc @@ -0,0 +1,16 @@ +:description: Embassy Book +:sectanchors: +:doctype: book +:toc: +:toc-placement: left +:toclevels: 2 +:imagesdir: images + +# Embassy Book + +Welcome to the Embassy Book. The Embassy Book is for everyone who wants to use Embassy and understand how Embassy works. + +include::pages/overview.adoc[leveloffset = 1] +include::pages/beginners.adoc[leveloffset = 1] +include::pages/system.adoc[leveloffset = 1] +include::pages/faq.adoc[leveloffset = 1] diff --git a/docs/modules/ROOT/examples/basic/Cargo.toml b/docs/modules/ROOT/examples/basic/Cargo.toml deleted file mode 100644 index 2c282145d..000000000 --- a/docs/modules/ROOT/examples/basic/Cargo.toml +++ /dev/null @@ -1,18 +0,0 @@ -[package] -authors = ["Dario Nieuwenhuis "] -edition = "2018" -name = "embassy-basic-example" -version = "0.1.0" -license = "MIT OR Apache-2.0" - -[dependencies] -embassy-executor = { version = "0.5.0", path = "../../../../../embassy-executor", features = ["defmt", "integrated-timers", "arch-cortex-m", "executor-thread"] } -embassy-time = { version = "0.3.0", path = "../../../../../embassy-time", features = ["defmt"] } -embassy-nrf = { version = "0.1.0", path = "../../../../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote"] } - -defmt = "0.3" -defmt-rtt = "0.3" - -cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } -cortex-m-rt = "0.7.0" -panic-probe = { version = "0.3", features = ["print-defmt"] } diff --git a/docs/modules/ROOT/examples/examples b/docs/modules/ROOT/examples/examples deleted file mode 120000 index 1929330b0..000000000 --- a/docs/modules/ROOT/examples/examples +++ /dev/null @@ -1 +0,0 @@ -../../../../examples \ No newline at end of file diff --git a/docs/modules/ROOT/nav.adoc b/docs/modules/ROOT/nav.adoc deleted file mode 100644 index 44b0eddb9..000000000 --- a/docs/modules/ROOT/nav.adoc +++ /dev/null @@ -1,19 +0,0 @@ -* xref:getting_started.adoc[Getting started] -** xref:basic_application.adoc[Basic application] -** xref:project_structure.adoc[Project Structure] -** xref:new_project.adoc[Starting a new Embassy project] -** xref:best_practices.adoc[Best Practices] -* xref:runtime.adoc[Executor] -* xref::time_keeping.adoc[Time-keeping] -* xref:sharing_peripherals.adoc[Sharing peripherals] -* xref:hal.adoc[HAL] -** xref:layer_by_layer.adoc[Anatomy of an async HAL] -** xref:nrf.adoc[nRF] -** xref:stm32.adoc[STM32] -* xref:bootloader.adoc[Bootloader] - -* xref:examples.adoc[Examples] -* xref:developer.adoc[Developer Docs] -** xref:developer_stm32.adoc[Developer Docs: STM32] -* xref:embassy_in_the_wild.adoc[Embassy in the wild] -* xref:faq.adoc[Frequently Asked Questions] diff --git a/docs/modules/ROOT/pages/basic_application.adoc b/docs/pages/basic_application.adoc similarity index 85% rename from docs/modules/ROOT/pages/basic_application.adoc rename to docs/pages/basic_application.adoc index d5aad806d..7b4ebda4f 100644 --- a/docs/modules/ROOT/pages/basic_application.adoc +++ b/docs/pages/basic_application.adoc @@ -1,10 +1,10 @@ = A basic Embassy application -So you've got one of the xref:examples.adoc[examples] running, but what now? Let's go through a simple Embassy application for the nRF52 DK to understand it better. +So you've got one of the examples running, but what now? Let's go through a simple Embassy application for the nRF52 DK to understand it better. == Main -The full example can be found link:https://github.com/embassy-rs/embassy/tree/master/docs/modules/ROOT/examples/basic[here]. +The full example can be found link:https://github.com/embassy-rs/embassy/tree/master/docs/examples/basic[here]. NOTE: If you’re using VS Code and rust-analyzer to view and edit the examples, you may need to make some changes to `.vscode/settings.json` to tell it which project we’re working on. Follow the instructions commented in that file to get rust-analyzer working correctly. @@ -14,7 +14,7 @@ The first thing you’ll notice are two attributes at the top of the file. These [source,rust] ---- -include::example$basic/src/main.rs[lines="1..2"] +include::../examples/basic/src/main.rs[lines="1..2"] ---- === Dealing with errors @@ -23,7 +23,7 @@ Then, what follows are some declarations on how to deal with panics and faults. [source,rust] ---- -include::example$basic/src/main.rs[lines="8"] +include::../examples/basic/src/main.rs[lines="8"] ---- === Task declaration @@ -32,7 +32,7 @@ After a bit of import declaration, the tasks run by the application should be de [source,rust] ---- -include::example$basic/src/main.rs[lines="10..18"] +include::../examples/basic/src/main.rs[lines="10..18"] ---- An embassy task must be declared `async`, and may NOT take generic arguments. In this case, we are handed the LED that should be blinked and the interval of the blinking. @@ -47,7 +47,7 @@ We then initialize the HAL with a default config, which gives us a `Peripherals` [source,rust] ---- -include::example$basic/src/main.rs[lines="20..-1"] +include::../examples/basic/src/main.rs[lines="20..-1"] ---- What happens when the `blinker` task has been spawned and main returns? Well, the main entry point is actually just like any other task, except that you can only have one and it takes some specific type arguments. The magic lies within the `#[embassy_executor::main]` macro. The macro does the following: @@ -64,7 +64,7 @@ The project definition needs to contain the embassy dependencies: [source,toml] ---- -include::example$basic/Cargo.toml[lines="9..11"] +include::../examples/basic/Cargo.toml[lines="9..11"] ---- Depending on your microcontroller, you may need to replace `embassy-nrf` with something else (`embassy-stm32` for STM32. Remember to update feature flags as well). diff --git a/docs/pages/beginners.adoc b/docs/pages/beginners.adoc new file mode 100644 index 000000000..48c9f4541 --- /dev/null +++ b/docs/pages/beginners.adoc @@ -0,0 +1,11 @@ += For beginners + +The articles in this section are primarily aimed at users new to Embassy, +showing how to get started, how to structure your project and other best practices. + +include::getting_started.adoc[leveloffset = 2] +include::basic_application.adoc[leveloffset = 2] +include::project_structure.adoc[leveloffset = 2] +include::new_project.adoc[leveloffset = 2] +include::best_practices.adoc[leveloffset = 2] +include::layer_by_layer.adoc[leveloffset = 2] diff --git a/docs/modules/ROOT/pages/best_practices.adoc b/docs/pages/best_practices.adoc similarity index 100% rename from docs/modules/ROOT/pages/best_practices.adoc rename to docs/pages/best_practices.adoc diff --git a/docs/modules/ROOT/pages/bootloader.adoc b/docs/pages/bootloader.adoc similarity index 100% rename from docs/modules/ROOT/pages/bootloader.adoc rename to docs/pages/bootloader.adoc diff --git a/docs/modules/ROOT/pages/developer.adoc b/docs/pages/developer.adoc similarity index 100% rename from docs/modules/ROOT/pages/developer.adoc rename to docs/pages/developer.adoc diff --git a/docs/modules/ROOT/pages/developer_stm32.adoc b/docs/pages/developer_stm32.adoc similarity index 100% rename from docs/modules/ROOT/pages/developer_stm32.adoc rename to docs/pages/developer_stm32.adoc diff --git a/docs/modules/ROOT/pages/embassy_in_the_wild.adoc b/docs/pages/embassy_in_the_wild.adoc similarity index 86% rename from docs/modules/ROOT/pages/embassy_in_the_wild.adoc rename to docs/pages/embassy_in_the_wild.adoc index a7d63f990..76b1169bd 100644 --- a/docs/modules/ROOT/pages/embassy_in_the_wild.adoc +++ b/docs/pages/embassy_in_the_wild.adoc @@ -1,6 +1,6 @@ = Embassy in the wild! -Here are known examples of real-world projects which make use of Embassy. Feel free to link:https://github.com/embassy-rs/embassy/blob/main/docs/modules/ROOT/pages/embassy_in_the_wild.adoc[add more]! +Here are known examples of real-world projects which make use of Embassy. Feel free to link:https://github.com/embassy-rs/embassy/blob/main/docs/pages/embassy_in_the_wild.adoc[add more]! * link:https://github.com/haobogu/rmk/[RMK: A feature-rich Rust keyboard firmware] ** RMK has built-in layer support, wireless(BLE) support, real-time key editing support using vial, and more! @@ -10,8 +10,8 @@ Here are known examples of real-world projects which make use of Embassy. Feel f * link:https://github.com/card-io-ecg/card-io-fw[Card/IO firmware] - firmware for an open source ECG device ** Targets the ESP32-S3 or ESP32-C6 MCU * The link:https://github.com/lora-rs/lora-rs[lora-rs] project includes link:https://github.com/lora-rs/lora-rs/tree/main/examples/stm32l0/src/bin[various standalone examples] for NRF52840, RP2040, STM32L0 and STM32WL -** link:https://github.com/matoushybl/air-force-one[Air force one: A simple air quality monitoring system] -*** Targets nRF52 and uses nrf-softdevice +* link:https://github.com/matoushybl/air-force-one[Air force one: A simple air quality monitoring system] +** Targets nRF52 and uses nrf-softdevice * link:https://github.com/schmettow/ylab-edge-go[YLab Edge Go] and link:https://github.com/schmettow/ylab-edge-pro[YLab Edge Pro] projects develop firmware (RP2040, STM32) for capturing physiological data in behavioural science research. Included so far are: diff --git a/docs/modules/ROOT/pages/examples.adoc b/docs/pages/examples.adoc similarity index 74% rename from docs/modules/ROOT/pages/examples.adoc rename to docs/pages/examples.adoc index c852f5205..82381a2c5 100644 --- a/docs/modules/ROOT/pages/examples.adoc +++ b/docs/pages/examples.adoc @@ -7,5 +7,5 @@ Main loop example [source,rust] ---- -include::example$examples/std/src/bin/tick.rs[] +include::../examples/examples/std/src/bin/tick.rs[] ---- diff --git a/docs/modules/ROOT/pages/faq.adoc b/docs/pages/faq.adoc similarity index 82% rename from docs/modules/ROOT/pages/faq.adoc rename to docs/pages/faq.adoc index 6c7126cd6..5f50951d8 100644 --- a/docs/modules/ROOT/pages/faq.adoc +++ b/docs/pages/faq.adoc @@ -2,7 +2,7 @@ These are a list of unsorted, commonly asked questions and answers. -Please feel free to add items to link:https://github.com/embassy-rs/embassy/edit/main/docs/modules/ROOT/pages/faq.adoc[this page], especially if someone in the chat answered a question for you! +Please feel free to add items to link:https://github.com/embassy-rs/embassy/edit/main/docs/pages/faq.adoc[this page], especially if someone in the chat answered a question for you! == How to deploy to RP2040 without a debugging probe. @@ -291,7 +291,7 @@ General steps: See link:/examples/stm32h7/src/bin/spi_bdma.rs[this example] for more details. -=== How do I switch to the `main` branch? +== How do I switch to the `main` branch? Sometimes to test new changes or fixes, you'll want to switch your project to using a version from GitHub. @@ -314,3 +314,22 @@ embassy-time = { git = "https://github.com/embassy-rs/embassy", rev embassy-usb = { git = "https://github.com/embassy-rs/embassy", rev = "4cade64ebd34bf93458f17cfe85c5f710d0ff13c" } embassy-usb-driver = { git = "https://github.com/embassy-rs/embassy", rev = "4cade64ebd34bf93458f17cfe85c5f710d0ff13c" } ---- + +== How do I add support for a new microcontroller to embassy? + +This is particularly for cortex-m, and potentially risc-v, where there is already support for basics like interrupt handling, or even already embassy-executor support for your architecture. + +This is a *much harder path* than just using Embassy on an already supported chip. If you are a beginner, consider using embassy on an existing, well supported chip for a while, before you decide to write drivers from scratch. It's also worth reading the existing source of supported Embassy HALs, to get a feel for how drivers are implemented for various chips. You should already be comfortable reading and writing unsafe code, and understanding the responsibilities of writing safe abstractions for users of your HAL. + +This is not the only possible approach, but if you are looking for where to start, this is a reasonable way to tackle the task: + +1. First, drop by the Matrix room or search around to see if someone has already started writing drivers, either in Embassy or otherwise in Rust. You might not have to start from scratch! +2. Make sure the target is supported in probe-rs, it likely is, and if not, there is likely a cmsis-pack you can use to add support so that flashing and debugging is possible. You will definitely appreciate being able to debug with SWD or JTAG when writing drivers! +3. See if there is an SVD (or SVDs, if it's a family) available, if it is, run it through chiptool to create a PAC for low level register access. If not, there are other ways (like scraping the PDF datasheets or existing C header files), but these are more work than starting from the SVD file to define peripheral memory locations necessary for writing drivers. +4. Either make a fork of embassy repo, and add your target there, or make a repo that just contains the PAC and an empty HAL. It doesn't necessarily have to live in the embassy repo at first. +5. Get a hello world binary working on your chip, either with minimal HAL or just PAC access, use delays and blink a light or send some raw data on some interface, make sure it works and you can flash, debug with defmt + RTT, write a proper linker script, etc. +6. Get basic timer operations and timer interrupts working, upgrade your blinking application to use hardware timers and interrupts, and ensure they are accurate (with a logic analyzer or oscilloscope, if possible). +7. Implement the embassy-time driver API with your timer and timer interrupt code, so that you can use embassy-time operations in your drivers and applications. +8. Then start implementing whatever peripherals you need, like GPIOs, UART, SPI, I2C, etc. This is the largest part of the work, and will likely continue for a while! Don't feel like you need 100% coverage of all peripherals at first, this is likely to be an ongoing process over time. +9. Start implementing the embedded-hal, embedded-io, and embedded-hal-async traits on top of your HAL drivers, once you start having more features completed. This will allow users to use standard external device drivers (e.g. sensors, actuators, displays, etc.) with your HAL. +10. Discuss upstreaming the PAC/HAL for embassy support, or make sure your drivers are added to the awesome-embedded-rust list so that people can find it. diff --git a/docs/modules/ROOT/pages/getting_started.adoc b/docs/pages/getting_started.adoc similarity index 96% rename from docs/modules/ROOT/pages/getting_started.adoc rename to docs/pages/getting_started.adoc index 73cb5530d..465059922 100644 --- a/docs/modules/ROOT/pages/getting_started.adoc +++ b/docs/pages/getting_started.adoc @@ -137,7 +137,7 @@ If you’re still having problems, check the link:https://embassy.dev/book/dev/f Congratulations, you have your first Embassy application running! Here are some suggestions for where to go from here: -* Read more about the xref:runtime.adoc[executor]. -* Read more about the xref:hal.adoc[HAL]. -* Start xref:basic_application.adoc[writing your application]. -* Learn how to xref:new_project.adoc[start a new embassy project by adapting an example]. +* Read more about the xref:_embassy_executor[executor]. +* Read more about the xref:_hardware_abstraction_layer_hal[HAL]. +* Start xref:_a_basic_embassy_application[writing your application]. +* Learn how to xref:_starting_a_new_project[start a new embassy project by adapting an example]. diff --git a/docs/modules/ROOT/pages/hal.adoc b/docs/pages/hal.adoc similarity index 83% rename from docs/modules/ROOT/pages/hal.adoc rename to docs/pages/hal.adoc index b1382e8e5..14b85e1f1 100644 --- a/docs/modules/ROOT/pages/hal.adoc +++ b/docs/pages/hal.adoc @@ -10,3 +10,5 @@ These HALs implement async/await functionality for most peripherals while also i async traits in `embedded-hal` and `embedded-hal-async`. You can also use these HALs with another executor. For the ESP32 series, there is an link:https://github.com/esp-rs/esp-hal[esp-hal] which you can use. + +For the WCH 32-bit RISC-V series, there is an link:https://github.com/ch32-rs/ch32-hal[ch32-hal], which you can use. diff --git a/docs/modules/ROOT/pages/layer_by_layer.adoc b/docs/pages/layer_by_layer.adoc similarity index 95% rename from docs/modules/ROOT/pages/layer_by_layer.adoc rename to docs/pages/layer_by_layer.adoc index fa419f75e..f87291c20 100644 --- a/docs/modules/ROOT/pages/layer_by_layer.adoc +++ b/docs/pages/layer_by_layer.adoc @@ -16,7 +16,7 @@ The blinky app using PAC is shown below: [source,rust] ---- -include::example$layer-by-layer/blinky-pac/src/main.rs[] +include::../examples/layer-by-layer/blinky-pac/src/main.rs[] ---- As you can see, a lot of code is needed to enable the peripheral clocks and to configure the input pins and the output pins of the application. @@ -35,7 +35,7 @@ The HAL example is shown below: [source,rust] ---- -include::example$layer-by-layer/blinky-hal/src/main.rs[] +include::../examples/layer-by-layer/blinky-hal/src/main.rs[] ---- As you can see, the application becomes a lot simpler, even without using any async code. The `Input` and `Output` types hide all the details of accessing the GPIO registers and allow you to use a much simpler API for querying the state of the button and toggling the LED output. @@ -52,7 +52,7 @@ Given Embassy focus on async Rust (which we'll come back to after this example), [source,rust] ---- -include::example$layer-by-layer/blinky-irq/src/main.rs[lines="1..57"] +include::../examples/layer-by-layer/blinky-irq/src/main.rs[lines="1..57"] ---- The simple application is now more complex again, primarily because of the need to keep the button and LED states in the global scope where it is accessible by the main application loop, as well as the interrupt handler. @@ -67,7 +67,7 @@ It's time to use the Embassy capabilities to its fullest. At the core, Embassy h [source,rust] ---- -include::example$layer-by-layer/blinky-async/src/main.rs[] +include::../examples/layer-by-layer/blinky-async/src/main.rs[] ---- The async version looks very similar to the HAL version, apart from a few minor details: diff --git a/docs/modules/ROOT/pages/new_project.adoc b/docs/pages/new_project.adoc similarity index 96% rename from docs/modules/ROOT/pages/new_project.adoc rename to docs/pages/new_project.adoc index 320966bb6..346d9f0f8 100644 --- a/docs/modules/ROOT/pages/new_project.adoc +++ b/docs/pages/new_project.adoc @@ -1,17 +1,18 @@ -= Starting a new Embassy project += Starting a new project Once you’ve successfully xref:getting_started.adoc[run some example projects], the next step is to make a standalone Embassy project. -There are some tools for generating Embassy projects: (WIP) +== Tools for generating Embassy projects -==== CLI +=== CLI - link:https://github.com/adinack/cargo-embassy[cargo-embassy] (STM32 and NRF) -==== cargo-generate +=== cargo-generate - link:https://github.com/lulf/embassy-template[embassy-template] (STM32, NRF, and RP) - link:https://github.com/bentwire/embassy-rp2040-template[embassy-rp2040-template] (RP) -But if you want to start from scratch: + +== Starting a project from scratch As an example, let’s create a new embassy project from scratch for a STM32G474. The same instructions are applicable for any supported chip with some minor changes. @@ -35,7 +36,7 @@ stm32g474-example Looking in link:https://github.com/embassy-rs/embassy/tree/main/examples[the Embassy examples], we can see there’s a `stm32g4` folder. Find `src/blinky.rs` and copy its contents into our `src/main.rs`. -== .cargo/config.toml +=== The .cargo/config.toml Currently, we’d need to provide cargo with a target triple every time we run `cargo build` or `cargo run`. Let’s spare ourselves that work by copying `.cargo/config.toml` from `examples/stm32g4` into our project. @@ -66,7 +67,7 @@ and copying `STM32G474RETx` into `.cargo/config.toml` as so: runner = "probe-rs run --chip STM32G474RETx" ---- -== Cargo.toml +=== Cargo.toml Now that cargo knows what target to compile for (and probe-rs knows what chip to run it on), we’re ready to add some dependencies. @@ -117,7 +118,7 @@ Finally, copy the `[profile.release]` section from the example `Cargo.toml` into debug = 2 ---- -== rust-toolchain.toml +=== rust-toolchain.toml Before we can build our project, we need to add an additional file to tell cargo to use the nightly toolchain. Copy the `rust-toolchain.toml` from the embassy repo to ours, and trim the list of targets down to only the target triple relevent for our project — in this case, `thumbv7em-none-eabi`: @@ -142,7 +143,7 @@ components = [ "rust-src", "rustfmt", "llvm-tools", "miri" ] targets = ["thumbv7em-none-eabi"] ---- -== build.rs +=== build.rs In order to produce a working binary for our target, cargo requires a custom build script. Copy `build.rs` from the example to our project: @@ -158,7 +159,7 @@ stm32g474-example └── main.rs ---- -== Building and running +=== Building and running At this point, we‘re finally ready to build and run our project! Connect your board via a debug probe and run: diff --git a/docs/modules/ROOT/pages/nrf.adoc b/docs/pages/nrf.adoc similarity index 100% rename from docs/modules/ROOT/pages/nrf.adoc rename to docs/pages/nrf.adoc diff --git a/docs/modules/ROOT/pages/index.adoc b/docs/pages/overview.adoc similarity index 94% rename from docs/modules/ROOT/pages/index.adoc rename to docs/pages/overview.adoc index e17adbbd7..1b9381bfe 100644 --- a/docs/modules/ROOT/pages/index.adoc +++ b/docs/pages/overview.adoc @@ -1,4 +1,4 @@ -= Embassy += Introduction Embassy is a project to make async/await a first-class option for embedded development. @@ -30,6 +30,7 @@ The Embassy project maintains HALs for select hardware, but you can still use HA * link:https://docs.embassy.dev/embassy-nrf/[embassy-nrf], for the Nordic Semiconductor nRF52, nRF53, nRF91 series. * link:https://docs.embassy.dev/embassy-rp/[embassy-rp], for the Raspberry Pi RP2040 microcontroller. * link:https://github.com/esp-rs[esp-rs], for the Espressif Systems ESP32 series of chips. +* link:https://github.com/ch32-rs/ch32-hal[ch32-hal], for the WCH 32-bit RISC-V(CH32V) series of chips. NOTE: A common question is if one can use the Embassy HALs standalone. Yes, it is possible! There are no dependency on the executor within the HALs. You can even use them without async, as they implement both the link:https://github.com/rust-embedded/embedded-hal[Embedded HAL] blocking and async traits. @@ -55,6 +56,20 @@ For most I/O in embedded devices, the peripheral doesn't directly support the tr The Direct Memory Access controller (DMA) is a controller that is present in MCUs that Embassy supports, including stm32 and nrf. The DMA allows the MCU to set up a transfer, either send or receive, and then wait for the transfer to complete. With DMA, once started, no MCU intervention is required until the transfer is complete, meaning that the MCU can perform other computation, or set up other I/O while the transfer is in progress. For high I/O rates, DMA can cut the time that the MCU spends handling I/O by over half. However, because DMA is more complex to set-up, it is less widely used in the embedded community. Embassy aims to change that by making DMA the first choice rather than the last. Using Embassy, there's no additional tuning required once I/O rates increase because your application is already set-up to handle them. +== Examples + +Embassy provides examples for all HALs supported. You can find them in the `examples/` folder. + + +Main loop example + +[source,rust] +---- +include::../examples/examples/std/src/bin/tick.rs[] +---- + +include::embassy_in_the_wild.adoc[leveloffset = 2] + == Resources For more reading material on async Rust and Embassy: diff --git a/docs/modules/ROOT/pages/project_structure.adoc b/docs/pages/project_structure.adoc similarity index 96% rename from docs/modules/ROOT/pages/project_structure.adoc rename to docs/pages/project_structure.adoc index 2adfcc1df..722ec8d9d 100644 --- a/docs/modules/ROOT/pages/project_structure.adoc +++ b/docs/pages/project_structure.adoc @@ -18,6 +18,7 @@ my-project |- rust-toolchain.toml ---- +[discrete] == .cargo/config.toml This directory/file describes what platform you're on, and configures link:https://github.com/probe-rs/probe-rs[probe-rs] to deploy to your device. @@ -36,21 +37,27 @@ target = "thumbv6m-none-eabi" # <-change for your platform DEFMT_LOG = "trace" # <- can change to info, warn, or error ---- +[discrete] == build.rs This is the build script for your project. It links defmt (what is link:https://defmt.ferrous-systems.com[defmt]?) and the `memory.x` file if needed. This file is pretty specific for each chipset, just copy and paste from the corresponding link:https://github.com/embassy-rs/embassy/tree/main/examples[example]. +[discrete] == Cargo.toml This is your manifest file, where you can configure all of the embassy components to use the features you need. -==== Features -===== Time +[discrete] +=== Features + +[discrete] +==== Time - tick-hz-x: Configures the tick rate of `embassy-time`. Higher tick rate means higher precision, and higher CPU wakes. - defmt-timestamp-uptime: defmt log entries will display the uptime in seconds. ...more to come +[discrete] == memory.x This file outlines the flash/ram usage of your program. It is especially useful when using link:https://github.com/embassy-rs/nrf-softdevice[nrf-softdevice] on an nRF5x. @@ -68,6 +75,7 @@ MEMORY } ---- +[discrete] == rust-toolchain.toml This file configures the rust version and configuration to use. diff --git a/docs/modules/ROOT/pages/runtime.adoc b/docs/pages/runtime.adoc similarity index 100% rename from docs/modules/ROOT/pages/runtime.adoc rename to docs/pages/runtime.adoc diff --git a/docs/modules/ROOT/pages/sharing_peripherals.adoc b/docs/pages/sharing_peripherals.adoc similarity index 98% rename from docs/modules/ROOT/pages/sharing_peripherals.adoc rename to docs/pages/sharing_peripherals.adoc index 784239fb1..6bcd56b01 100644 --- a/docs/modules/ROOT/pages/sharing_peripherals.adoc +++ b/docs/pages/sharing_peripherals.adoc @@ -125,4 +125,4 @@ async fn toggle_led(control: Sender<'static, ThreadModeRawMutex, LedState, 64>, ---- This example replaces the Mutex with a Channel, and uses another task (the main loop) to drive the LED. The advantage of this approach is that only a single task references the peripheral, separating concerns. However, using a Mutex has a lower overhead and might be necessary if you need to ensure -that the operation is ecompleted before continuing to do other work in your task. +that the operation is completed before continuing to do other work in your task. diff --git a/docs/modules/ROOT/pages/stm32.adoc b/docs/pages/stm32.adoc similarity index 100% rename from docs/modules/ROOT/pages/stm32.adoc rename to docs/pages/stm32.adoc diff --git a/docs/pages/system.adoc b/docs/pages/system.adoc new file mode 100644 index 000000000..985f92b18 --- /dev/null +++ b/docs/pages/system.adoc @@ -0,0 +1,13 @@ += System description + +This section describes different parts of Embassy in more detail. + +include::runtime.adoc[leveloffset = 2] +include::bootloader.adoc[leveloffset = 2] +include::time_keeping.adoc[leveloffset = 2] +include::hal.adoc[leveloffset = 2] +include::nrf.adoc[leveloffset = 2] +include::stm32.adoc[leveloffset = 2] +include::sharing_peripherals.adoc[leveloffset = 2] +include::developer.adoc[leveloffset = 2] +include::developer_stm32.adoc[leveloffset = 2] diff --git a/docs/modules/ROOT/pages/time_keeping.adoc b/docs/pages/time_keeping.adoc similarity index 100% rename from docs/modules/ROOT/pages/time_keeping.adoc rename to docs/pages/time_keeping.adoc diff --git a/embassy-embedded-hal/src/shared_bus/asynch/spi.rs b/embassy-embedded-hal/src/shared_bus/asynch/spi.rs index 9890f218d..30d4ecc36 100644 --- a/embassy-embedded-hal/src/shared_bus/asynch/spi.rs +++ b/embassy-embedded-hal/src/shared_bus/asynch/spi.rs @@ -55,13 +55,14 @@ where type Error = SpiDeviceError; } -impl spi::SpiDevice for SpiDevice<'_, M, BUS, CS> +impl spi::SpiDevice for SpiDevice<'_, M, BUS, CS> where M: RawMutex, - BUS: spi::SpiBus, + BUS: spi::SpiBus, CS: OutputPin, + Word: Copy + 'static, { - async fn transaction(&mut self, operations: &mut [spi::Operation<'_, u8>]) -> Result<(), Self::Error> { + async fn transaction(&mut self, operations: &mut [spi::Operation<'_, Word>]) -> Result<(), Self::Error> { if cfg!(not(feature = "time")) && operations.iter().any(|op| matches!(op, Operation::DelayNs(_))) { return Err(SpiDeviceError::DelayNotSupported); } @@ -138,13 +139,14 @@ where type Error = SpiDeviceError; } -impl spi::SpiDevice for SpiDeviceWithConfig<'_, M, BUS, CS> +impl spi::SpiDevice for SpiDeviceWithConfig<'_, M, BUS, CS> where M: RawMutex, - BUS: spi::SpiBus + SetConfig, + BUS: spi::SpiBus + SetConfig, CS: OutputPin, + Word: Copy + 'static, { - async fn transaction(&mut self, operations: &mut [spi::Operation<'_, u8>]) -> Result<(), Self::Error> { + async fn transaction(&mut self, operations: &mut [spi::Operation<'_, Word>]) -> Result<(), Self::Error> { if cfg!(not(feature = "time")) && operations.iter().any(|op| matches!(op, Operation::DelayNs(_))) { return Err(SpiDeviceError::DelayNotSupported); } diff --git a/embassy-embedded-hal/src/shared_bus/blocking/spi.rs b/embassy-embedded-hal/src/shared_bus/blocking/spi.rs index 801899f9f..eb9c0c4f4 100644 --- a/embassy-embedded-hal/src/shared_bus/blocking/spi.rs +++ b/embassy-embedded-hal/src/shared_bus/blocking/spi.rs @@ -48,13 +48,14 @@ where type Error = SpiDeviceError; } -impl embedded_hal_1::spi::SpiDevice for SpiDevice<'_, M, BUS, CS> +impl embedded_hal_1::spi::SpiDevice for SpiDevice<'_, M, BUS, CS> where M: RawMutex, - BUS: SpiBus, + BUS: SpiBus, CS: OutputPin, + Word: Copy + 'static, { - fn transaction(&mut self, operations: &mut [Operation<'_, u8>]) -> Result<(), Self::Error> { + fn transaction(&mut self, operations: &mut [embedded_hal_1::spi::Operation<'_, Word>]) -> Result<(), Self::Error> { if cfg!(not(feature = "time")) && operations.iter().any(|op| matches!(op, Operation::DelayNs(_))) { return Err(SpiDeviceError::DelayNotSupported); } @@ -90,47 +91,6 @@ where } } -impl<'d, M, BUS, CS, BusErr, CsErr> embedded_hal_02::blocking::spi::Transfer for SpiDevice<'_, M, BUS, CS> -where - M: RawMutex, - BUS: embedded_hal_02::blocking::spi::Transfer, - CS: OutputPin, -{ - type Error = SpiDeviceError; - fn transfer<'w>(&mut self, words: &'w mut [u8]) -> Result<&'w [u8], Self::Error> { - self.bus.lock(|bus| { - let mut bus = bus.borrow_mut(); - self.cs.set_low().map_err(SpiDeviceError::Cs)?; - let op_res = bus.transfer(words); - let cs_res = self.cs.set_high(); - let op_res = op_res.map_err(SpiDeviceError::Spi)?; - cs_res.map_err(SpiDeviceError::Cs)?; - Ok(op_res) - }) - } -} - -impl<'d, M, BUS, CS, BusErr, CsErr> embedded_hal_02::blocking::spi::Write for SpiDevice<'_, M, BUS, CS> -where - M: RawMutex, - BUS: embedded_hal_02::blocking::spi::Write, - CS: OutputPin, -{ - type Error = SpiDeviceError; - - fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> { - self.bus.lock(|bus| { - let mut bus = bus.borrow_mut(); - self.cs.set_low().map_err(SpiDeviceError::Cs)?; - let op_res = bus.write(words); - let cs_res = self.cs.set_high(); - let op_res = op_res.map_err(SpiDeviceError::Spi)?; - cs_res.map_err(SpiDeviceError::Cs)?; - Ok(op_res) - }) - } -} - /// SPI device on a shared bus, with its own configuration. /// /// This is like [`SpiDevice`], with an additional bus configuration that's applied @@ -163,13 +123,14 @@ where type Error = SpiDeviceError; } -impl embedded_hal_1::spi::SpiDevice for SpiDeviceWithConfig<'_, M, BUS, CS> +impl embedded_hal_1::spi::SpiDevice for SpiDeviceWithConfig<'_, M, BUS, CS> where M: RawMutex, - BUS: SpiBus + SetConfig, + BUS: SpiBus + SetConfig, CS: OutputPin, + Word: Copy + 'static, { - fn transaction(&mut self, operations: &mut [Operation<'_, u8>]) -> Result<(), Self::Error> { + fn transaction(&mut self, operations: &mut [embedded_hal_1::spi::Operation<'_, Word>]) -> Result<(), Self::Error> { if cfg!(not(feature = "time")) && operations.iter().any(|op| matches!(op, Operation::DelayNs(_))) { return Err(SpiDeviceError::DelayNotSupported); } diff --git a/embassy-hal-internal/src/atomic_ring_buffer.rs b/embassy-hal-internal/src/atomic_ring_buffer.rs index 40ad9dc7a..00b7a1249 100644 --- a/embassy-hal-internal/src/atomic_ring_buffer.rs +++ b/embassy-hal-internal/src/atomic_ring_buffer.rs @@ -123,6 +123,11 @@ impl RingBuffer { Some(Writer(self)) } + /// Return if buffer is available. + pub fn is_available(&self) -> bool { + !self.buf.load(Ordering::Relaxed).is_null() && self.len.load(Ordering::Relaxed) != 0 + } + /// Return length of buffer. pub fn len(&self) -> usize { self.len.load(Ordering::Relaxed) diff --git a/embassy-net-esp-hosted/Cargo.toml b/embassy-net-esp-hosted/Cargo.toml index dda65dbf9..96f6c8b00 100644 --- a/embassy-net-esp-hosted/Cargo.toml +++ b/embassy-net-esp-hosted/Cargo.toml @@ -9,6 +9,10 @@ license = "MIT OR Apache-2.0" repository = "https://github.com/embassy-rs/embassy" documentation = "https://docs.embassy.dev/embassy-net-esp-hosted" +[features] +defmt = [ "dep:defmt", "heapless/defmt-03" ] +log = [ "dep:log" ] + [dependencies] defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } diff --git a/embassy-rp/src/flash.rs b/embassy-rp/src/flash.rs index 45b385cb4..6e2a823d8 100644 --- a/embassy-rp/src/flash.rs +++ b/embassy-rp/src/flash.rs @@ -374,6 +374,11 @@ impl<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> ReadNorFlash for Flash<' impl<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> MultiwriteNorFlash for Flash<'d, T, M, FLASH_SIZE> {} +impl<'d, T: Instance, const FLASH_SIZE: usize> embedded_storage_async::nor_flash::MultiwriteNorFlash + for Flash<'d, T, Async, FLASH_SIZE> +{ +} + impl<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> NorFlash for Flash<'d, T, M, FLASH_SIZE> { const WRITE_SIZE: usize = WRITE_SIZE; diff --git a/embassy-rp/src/pwm.rs b/embassy-rp/src/pwm.rs index a1f400cfb..20b5c4d58 100644 --- a/embassy-rp/src/pwm.rs +++ b/embassy-rp/src/pwm.rs @@ -81,24 +81,22 @@ impl From for Divmode { } /// PWM driver. -pub struct Pwm<'d, T: Slice> { - inner: PeripheralRef<'d, T>, +pub struct Pwm<'d> { pin_a: Option>, pin_b: Option>, + slice: usize, } -impl<'d, T: Slice> Pwm<'d, T> { +impl<'d> Pwm<'d> { fn new_inner( - inner: impl Peripheral

+ 'd, + slice: usize, a: Option>, b: Option>, b_pull: Pull, config: Config, divmode: Divmode, ) -> Self { - into_ref!(inner); - - let p = inner.regs(); + let p = pac::PWM.ch(slice); p.csr().modify(|w| { w.set_divmode(divmode); w.set_en(false); @@ -117,51 +115,67 @@ impl<'d, T: Slice> Pwm<'d, T> { }); } Self { - inner, + // inner: p.into(), pin_a: a, pin_b: b, + slice, } } /// Create PWM driver without any configured pins. #[inline] - pub fn new_free(inner: impl Peripheral

+ 'd, config: Config) -> Self { - Self::new_inner(inner, None, None, Pull::None, config, Divmode::DIV) + pub fn new_free(slice: impl Peripheral

+ 'd, config: Config) -> Self { + into_ref!(slice); + Self::new_inner(slice.number(), None, None, Pull::None, config, Divmode::DIV) } /// Create PWM driver with a single 'a' as output. #[inline] - pub fn new_output_a( - inner: impl Peripheral

+ 'd, + pub fn new_output_a( + slice: impl Peripheral

+ 'd, a: impl Peripheral

> + 'd, config: Config, ) -> Self { - into_ref!(a); - Self::new_inner(inner, Some(a.map_into()), None, Pull::None, config, Divmode::DIV) + into_ref!(slice, a); + Self::new_inner( + slice.number(), + Some(a.map_into()), + None, + Pull::None, + config, + Divmode::DIV, + ) } /// Create PWM driver with a single 'b' pin as output. #[inline] - pub fn new_output_b( - inner: impl Peripheral

+ 'd, + pub fn new_output_b( + slice: impl Peripheral

+ 'd, b: impl Peripheral

> + 'd, config: Config, ) -> Self { - into_ref!(b); - Self::new_inner(inner, None, Some(b.map_into()), Pull::None, config, Divmode::DIV) + into_ref!(slice, b); + Self::new_inner( + slice.number(), + None, + Some(b.map_into()), + Pull::None, + config, + Divmode::DIV, + ) } /// Create PWM driver with a 'a' and 'b' pins as output. #[inline] - pub fn new_output_ab( - inner: impl Peripheral

+ 'd, + pub fn new_output_ab( + slice: impl Peripheral

+ 'd, a: impl Peripheral

> + 'd, b: impl Peripheral

> + 'd, config: Config, ) -> Self { - into_ref!(a, b); + into_ref!(slice, a, b); Self::new_inner( - inner, + slice.number(), Some(a.map_into()), Some(b.map_into()), Pull::None, @@ -172,30 +186,30 @@ impl<'d, T: Slice> Pwm<'d, T> { /// Create PWM driver with a single 'b' as input pin. #[inline] - pub fn new_input( - inner: impl Peripheral

+ 'd, + pub fn new_input( + slice: impl Peripheral

+ 'd, b: impl Peripheral

> + 'd, b_pull: Pull, mode: InputMode, config: Config, ) -> Self { - into_ref!(b); - Self::new_inner(inner, None, Some(b.map_into()), b_pull, config, mode.into()) + into_ref!(slice, b); + Self::new_inner(slice.number(), None, Some(b.map_into()), b_pull, config, mode.into()) } /// Create PWM driver with a 'a' and 'b' pins in the desired input mode. #[inline] - pub fn new_output_input( - inner: impl Peripheral

+ 'd, + pub fn new_output_input( + slice: impl Peripheral

+ 'd, a: impl Peripheral

> + 'd, b: impl Peripheral

> + 'd, b_pull: Pull, mode: InputMode, config: Config, ) -> Self { - into_ref!(a, b); + into_ref!(slice, a, b); Self::new_inner( - inner, + slice.number(), Some(a.map_into()), Some(b.map_into()), b_pull, @@ -206,7 +220,7 @@ impl<'d, T: Slice> Pwm<'d, T> { /// Set the PWM config. pub fn set_config(&mut self, config: &Config) { - Self::configure(self.inner.regs(), config); + Self::configure(pac::PWM.ch(self.slice), config); } fn configure(p: pac::pwm::Channel, config: &Config) { @@ -228,22 +242,22 @@ impl<'d, T: Slice> Pwm<'d, T> { }); } - /// Advances a slice’s output phase by one count while it is running + /// Advances a slice's output phase by one count while it is running /// by inserting a pulse into the clock enable. The counter /// will not count faster than once per cycle. #[inline] pub fn phase_advance(&mut self) { - let p = self.inner.regs(); + let p = pac::PWM.ch(self.slice); p.csr().write_set(|w| w.set_ph_adv(true)); while p.csr().read().ph_adv() {} } - /// Retards a slice’s output phase by one count while it is running + /// Retards a slice's output phase by one count while it is running /// by deleting a pulse from the clock enable. The counter will not - /// count backward when clock enable is permenantly low. + /// count backward when clock enable is permanently low. #[inline] pub fn phase_retard(&mut self) { - let p = self.inner.regs(); + let p = pac::PWM.ch(self.slice); p.csr().write_set(|w| w.set_ph_ret(true)); while p.csr().read().ph_ret() {} } @@ -251,13 +265,13 @@ impl<'d, T: Slice> Pwm<'d, T> { /// Read PWM counter. #[inline] pub fn counter(&self) -> u16 { - self.inner.regs().ctr().read().ctr() + pac::PWM.ch(self.slice).ctr().read().ctr() } /// Write PWM counter. #[inline] pub fn set_counter(&self, ctr: u16) { - self.inner.regs().ctr().write(|w| w.set_ctr(ctr)) + pac::PWM.ch(self.slice).ctr().write(|w| w.set_ctr(ctr)) } /// Wait for channel interrupt. @@ -281,7 +295,7 @@ impl<'d, T: Slice> Pwm<'d, T> { #[inline] fn bit(&self) -> u32 { - 1 << self.inner.number() as usize + 1 << self.slice as usize } } @@ -291,7 +305,7 @@ pub struct PwmBatch(u32); impl PwmBatch { #[inline] /// Enable a PWM slice in this batch. - pub fn enable(&mut self, pwm: &Pwm<'_, impl Slice>) { + pub fn enable(&mut self, pwm: &Pwm<'_>) { self.0 |= pwm.bit(); } @@ -308,9 +322,9 @@ impl PwmBatch { } } -impl<'d, T: Slice> Drop for Pwm<'d, T> { +impl<'d> Drop for Pwm<'d> { fn drop(&mut self) { - self.inner.regs().csr().write_clear(|w| w.set_en(false)); + pac::PWM.ch(self.slice).csr().write_clear(|w| w.set_en(false)); if let Some(pin) = &self.pin_a { pin.gpio().ctrl().write(|w| w.set_funcsel(31)); } @@ -326,19 +340,14 @@ trait SealedSlice {} #[allow(private_bounds)] pub trait Slice: Peripheral

+ SealedSlice + Sized + 'static { /// Slice number. - fn number(&self) -> u8; - - /// Slice register block. - fn regs(&self) -> pac::pwm::Channel { - pac::PWM.ch(self.number() as _) - } + fn number(&self) -> usize; } macro_rules! slice { ($name:ident, $num:expr) => { impl SealedSlice for peripherals::$name {} impl Slice for peripherals::$name { - fn number(&self) -> u8 { + fn number(&self) -> usize { $num } } diff --git a/embassy-rp/src/uart/buffered.rs b/embassy-rp/src/uart/buffered.rs index da1157984..c94164040 100644 --- a/embassy-rp/src/uart/buffered.rs +++ b/embassy-rp/src/uart/buffered.rs @@ -51,14 +51,20 @@ pub struct BufferedUartTx<'d, T: Instance> { pub(crate) fn init_buffers<'d, T: Instance + 'd>( _irq: impl Binding>, - tx_buffer: &'d mut [u8], - rx_buffer: &'d mut [u8], + tx_buffer: Option<&'d mut [u8]>, + rx_buffer: Option<&'d mut [u8]>, ) { let state = T::buffered_state(); - let len = tx_buffer.len(); - unsafe { state.tx_buf.init(tx_buffer.as_mut_ptr(), len) }; - let len = rx_buffer.len(); - unsafe { state.rx_buf.init(rx_buffer.as_mut_ptr(), len) }; + + if let Some(tx_buffer) = tx_buffer { + let len = tx_buffer.len(); + unsafe { state.tx_buf.init(tx_buffer.as_mut_ptr(), len) }; + } + + if let Some(rx_buffer) = rx_buffer { + let len = rx_buffer.len(); + unsafe { state.rx_buf.init(rx_buffer.as_mut_ptr(), len) }; + } // From the datasheet: // "The transmit interrupt is based on a transition through a level, rather @@ -95,7 +101,7 @@ impl<'d, T: Instance> BufferedUart<'d, T> { into_ref!(tx, rx); super::Uart::<'d, T, Async>::init(Some(tx.map_into()), Some(rx.map_into()), None, None, config); - init_buffers::(irq, tx_buffer, rx_buffer); + init_buffers::(irq, Some(tx_buffer), Some(rx_buffer)); Self { rx: BufferedUartRx { phantom: PhantomData }, @@ -124,7 +130,7 @@ impl<'d, T: Instance> BufferedUart<'d, T> { Some(cts.map_into()), config, ); - init_buffers::(irq, tx_buffer, rx_buffer); + init_buffers::(irq, Some(tx_buffer), Some(rx_buffer)); Self { rx: BufferedUartRx { phantom: PhantomData }, @@ -175,7 +181,7 @@ impl<'d, T: Instance> BufferedUartRx<'d, T> { into_ref!(rx); super::Uart::<'d, T, Async>::init(None, Some(rx.map_into()), None, None, config); - init_buffers::(irq, &mut [], rx_buffer); + init_buffers::(irq, None, Some(rx_buffer)); Self { phantom: PhantomData } } @@ -192,7 +198,7 @@ impl<'d, T: Instance> BufferedUartRx<'d, T> { into_ref!(rx, rts); super::Uart::<'d, T, Async>::init(None, Some(rx.map_into()), Some(rts.map_into()), None, config); - init_buffers::(irq, &mut [], rx_buffer); + init_buffers::(irq, None, Some(rx_buffer)); Self { phantom: PhantomData } } @@ -323,7 +329,7 @@ impl<'d, T: Instance> BufferedUartTx<'d, T> { into_ref!(tx); super::Uart::<'d, T, Async>::init(Some(tx.map_into()), None, None, None, config); - init_buffers::(irq, tx_buffer, &mut []); + init_buffers::(irq, Some(tx_buffer), None); Self { phantom: PhantomData } } @@ -340,12 +346,12 @@ impl<'d, T: Instance> BufferedUartTx<'d, T> { into_ref!(tx, cts); super::Uart::<'d, T, Async>::init(Some(tx.map_into()), None, None, Some(cts.map_into()), config); - init_buffers::(irq, tx_buffer, &mut []); + init_buffers::(irq, Some(tx_buffer), None); Self { phantom: PhantomData } } - fn write<'a>(buf: &'a [u8]) -> impl Future> + 'a { + fn write(buf: &[u8]) -> impl Future> + '_ { poll_fn(move |cx| { if buf.is_empty() { return Poll::Ready(Ok(0)); @@ -459,9 +465,9 @@ impl<'d, T: Instance> Drop for BufferedUartRx<'d, T> { let state = T::buffered_state(); unsafe { state.rx_buf.deinit() } - // TX is inactive if the the buffer is not available. + // TX is inactive if the buffer is not available. // We can now unregister the interrupt handler - if state.tx_buf.is_empty() { + if !state.tx_buf.is_available() { T::Interrupt::disable(); } } @@ -472,9 +478,9 @@ impl<'d, T: Instance> Drop for BufferedUartTx<'d, T> { let state = T::buffered_state(); unsafe { state.tx_buf.deinit() } - // RX is inactive if the the buffer is not available. + // RX is inactive if the buffer is not available. // We can now unregister the interrupt handler - if state.rx_buf.is_empty() { + if !state.rx_buf.is_available() { T::Interrupt::disable(); } } @@ -520,64 +526,68 @@ impl interrupt::typelevel::Handler for BufferedInterr } // RX - let mut rx_writer = unsafe { s.rx_buf.writer() }; - let rx_buf = rx_writer.push_slice(); - let mut n_read = 0; - let mut error = false; - for rx_byte in rx_buf { - if r.uartfr().read().rxfe() { - break; + if s.rx_buf.is_available() { + let mut rx_writer = unsafe { s.rx_buf.writer() }; + let rx_buf = rx_writer.push_slice(); + let mut n_read = 0; + let mut error = false; + for rx_byte in rx_buf { + if r.uartfr().read().rxfe() { + break; + } + let dr = r.uartdr().read(); + if (dr.0 >> 8) != 0 { + s.rx_error.fetch_or((dr.0 >> 8) as u8, Ordering::Relaxed); + error = true; + // only fill the buffer with valid characters. the current character is fine + // if the error is an overrun, but if we add it to the buffer we'll report + // the overrun one character too late. drop it instead and pretend we were + // a bit slower at draining the rx fifo than we actually were. + // this is consistent with blocking uart error reporting. + break; + } + *rx_byte = dr.data(); + n_read += 1; } - let dr = r.uartdr().read(); - if (dr.0 >> 8) != 0 { - s.rx_error.fetch_or((dr.0 >> 8) as u8, Ordering::Relaxed); - error = true; - // only fill the buffer with valid characters. the current character is fine - // if the error is an overrun, but if we add it to the buffer we'll report - // the overrun one character too late. drop it instead and pretend we were - // a bit slower at draining the rx fifo than we actually were. - // this is consistent with blocking uart error reporting. - break; + if n_read > 0 { + rx_writer.push_done(n_read); + s.rx_waker.wake(); + } else if error { + s.rx_waker.wake(); + } + // Disable any further RX interrupts when the buffer becomes full or + // errors have occurred. This lets us buffer additional errors in the + // fifo without needing more error storage locations, and most applications + // will want to do a full reset of their uart state anyway once an error + // has happened. + if s.rx_buf.is_full() || error { + r.uartimsc().write_clear(|w| { + w.set_rxim(true); + w.set_rtim(true); + }); } - *rx_byte = dr.data(); - n_read += 1; - } - if n_read > 0 { - rx_writer.push_done(n_read); - s.rx_waker.wake(); - } else if error { - s.rx_waker.wake(); - } - // Disable any further RX interrupts when the buffer becomes full or - // errors have occurred. This lets us buffer additional errors in the - // fifo without needing more error storage locations, and most applications - // will want to do a full reset of their uart state anyway once an error - // has happened. - if s.rx_buf.is_full() || error { - r.uartimsc().write_clear(|w| { - w.set_rxim(true); - w.set_rtim(true); - }); } // TX - let mut tx_reader = unsafe { s.tx_buf.reader() }; - let tx_buf = tx_reader.pop_slice(); - let mut n_written = 0; - for tx_byte in tx_buf.iter_mut() { - if r.uartfr().read().txff() { - break; + if s.tx_buf.is_available() { + let mut tx_reader = unsafe { s.tx_buf.reader() }; + let tx_buf = tx_reader.pop_slice(); + let mut n_written = 0; + for tx_byte in tx_buf.iter_mut() { + if r.uartfr().read().txff() { + break; + } + r.uartdr().write(|w| w.set_data(*tx_byte)); + n_written += 1; } - r.uartdr().write(|w| w.set_data(*tx_byte)); - n_written += 1; + if n_written > 0 { + tx_reader.pop_done(n_written); + s.tx_waker.wake(); + } + // The TX interrupt only triggers once when the FIFO threshold is + // crossed. No need to disable it when the buffer becomes empty + // as it does re-trigger anymore once we have cleared it. } - if n_written > 0 { - tx_reader.pop_done(n_written); - s.tx_waker.wake(); - } - // The TX interrupt only triggers once when the FIFO threshold is - // crossed. No need to disable it when the buffer becomes empty - // as it does re-trigger anymore once we have cleared it. } } diff --git a/embassy-rp/src/uart/mod.rs b/embassy-rp/src/uart/mod.rs index ee2dcb27d..30ece15bd 100644 --- a/embassy-rp/src/uart/mod.rs +++ b/embassy-rp/src/uart/mod.rs @@ -231,7 +231,7 @@ impl<'d, T: Instance> UartTx<'d, T, Blocking> { irq: impl Binding>, tx_buffer: &'d mut [u8], ) -> BufferedUartTx<'d, T> { - buffered::init_buffers::(irq, tx_buffer, &mut []); + buffered::init_buffers::(irq, Some(tx_buffer), None); BufferedUartTx { phantom: PhantomData } } @@ -352,7 +352,7 @@ impl<'d, T: Instance> UartRx<'d, T, Blocking> { irq: impl Binding>, rx_buffer: &'d mut [u8], ) -> BufferedUartRx<'d, T> { - buffered::init_buffers::(irq, &mut [], rx_buffer); + buffered::init_buffers::(irq, None, Some(rx_buffer)); BufferedUartRx { phantom: PhantomData } } @@ -690,7 +690,7 @@ impl<'d, T: Instance> Uart<'d, T, Blocking> { tx_buffer: &'d mut [u8], rx_buffer: &'d mut [u8], ) -> BufferedUart<'d, T> { - buffered::init_buffers::(irq, tx_buffer, rx_buffer); + buffered::init_buffers::(irq, Some(tx_buffer), Some(rx_buffer)); BufferedUart { rx: BufferedUartRx { phantom: PhantomData }, @@ -860,7 +860,7 @@ impl<'d, T: Instance + 'd, M: Mode> Uart<'d, T, M> { }); } - /// sets baudrate on runtime + /// sets baudrate on runtime pub fn set_baudrate(&mut self, baudrate: u32) { Self::set_baudrate_inner(baudrate); } diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index a50da1a9e..5ef2366d9 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -72,7 +72,7 @@ rand_core = "0.6.3" sdio-host = "0.5.0" critical-section = "1.1" #stm32-metapac = { version = "15" } -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-f1297385e91f061fcb6134bb25f51e12d8abff93" } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-34c0188a682b32c32ff147d377e0629b1ebe8318" } vcell = "0.1.3" nb = "1.0.0" @@ -89,7 +89,6 @@ volatile-register = { version = "0.2.1" } bitflags = "2.4.2" - [dev-dependencies] critical-section = { version = "1.1", features = ["std"] } @@ -98,7 +97,7 @@ proc-macro2 = "1.0.36" quote = "1.0.15" #stm32-metapac = { version = "15", default-features = false, features = ["metadata"]} -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-f1297385e91f061fcb6134bb25f51e12d8abff93", default-features = false, features = ["metadata"]} +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-34c0188a682b32c32ff147d377e0629b1ebe8318", default-features = false, features = ["metadata"]} [features] default = ["rt"] diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index ba118f338..4eed6fe7d 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -7,6 +7,7 @@ use std::{env, fs}; use proc_macro2::{Ident, TokenStream}; use quote::{format_ident, quote}; +use stm32_metapac::metadata::ir::BitOffset; use stm32_metapac::metadata::{ MemoryRegionKind, PeripheralRccKernelClock, PeripheralRccRegister, PeripheralRegisters, StopMode, METADATA, }; @@ -359,12 +360,17 @@ fn main() { // ======== // Extract the rcc registers + let rcc_registers = METADATA .peripherals .iter() .filter_map(|p| p.registers.as_ref()) .find(|r| r.kind == "rcc") .unwrap(); + for b in rcc_registers.ir.blocks { + eprintln!("{}", b.name); + } + let rcc_block = rcc_registers.ir.blocks.iter().find(|b| b.name == "Rcc").unwrap(); // ======== // Generate RccPeripheral impls @@ -540,6 +546,29 @@ fn main() { let pname = format_ident!("{}", p.name); let en_reg = format_ident!("{}", en.register.to_ascii_lowercase()); let set_en_field = format_ident!("set_{}", en.field.to_ascii_lowercase()); + let en_reg_offs = rcc_block + .items + .iter() + .find(|i| i.name.eq_ignore_ascii_case(en.register)) + .unwrap() + .byte_offset; + let en_reg_offs: u8 = (en_reg_offs / 4).try_into().unwrap(); + + let en_bit_offs = &rcc_registers + .ir + .fieldsets + .iter() + .find(|i| i.name.eq_ignore_ascii_case(en.register)) + .unwrap() + .fields + .iter() + .find(|i| i.name.eq_ignore_ascii_case(en.field)) + .unwrap() + .bit_offset; + let BitOffset::Regular(en_bit_offs) = en_bit_offs else { + panic!("cursed bit offset") + }; + let en_bit_offs: u8 = en_bit_offs.offset.try_into().unwrap(); let refcount = clock_gen.force_refcount.contains(ptype) || *rcc_field_count.get(&(en.register, en.field)).unwrap() > 1; @@ -624,6 +653,9 @@ fn main() { crate::pac::RCC.#en_reg().modify(|w| w.#set_en_field(false)); #decr_stop_refcount } + + const ENABLE_BIT: crate::rcc::ClockEnableBit = + unsafe { crate::rcc::ClockEnableBit::new(#en_reg_offs, #en_bit_offs) }; } impl crate::rcc::RccPeripheral for peripherals::#pname {} @@ -836,6 +868,35 @@ fn main() { (("dcmi", "HSYNC"), quote!(crate::dcmi::HSyncPin)), (("dcmi", "VSYNC"), quote!(crate::dcmi::VSyncPin)), (("dcmi", "PIXCLK"), quote!(crate::dcmi::PixClkPin)), + (("dsihost", "TE"), quote!(crate::dsihost::TePin)), + (("ltdc", "CLK"), quote!(crate::ltdc::ClkPin)), + (("ltdc", "HSYNC"), quote!(crate::ltdc::HsyncPin)), + (("ltdc", "VSYNC"), quote!(crate::ltdc::VsyncPin)), + (("ltdc", "DE"), quote!(crate::ltdc::DePin)), + (("ltdc", "R0"), quote!(crate::ltdc::R0Pin)), + (("ltdc", "R1"), quote!(crate::ltdc::R1Pin)), + (("ltdc", "R2"), quote!(crate::ltdc::R2Pin)), + (("ltdc", "R3"), quote!(crate::ltdc::R3Pin)), + (("ltdc", "R4"), quote!(crate::ltdc::R4Pin)), + (("ltdc", "R5"), quote!(crate::ltdc::R5Pin)), + (("ltdc", "R6"), quote!(crate::ltdc::R6Pin)), + (("ltdc", "R7"), quote!(crate::ltdc::R7Pin)), + (("ltdc", "G0"), quote!(crate::ltdc::G0Pin)), + (("ltdc", "G1"), quote!(crate::ltdc::G1Pin)), + (("ltdc", "G2"), quote!(crate::ltdc::G2Pin)), + (("ltdc", "G3"), quote!(crate::ltdc::G3Pin)), + (("ltdc", "G4"), quote!(crate::ltdc::G4Pin)), + (("ltdc", "G5"), quote!(crate::ltdc::G5Pin)), + (("ltdc", "G6"), quote!(crate::ltdc::G6Pin)), + (("ltdc", "G7"), quote!(crate::ltdc::G7Pin)), + (("ltdc", "B0"), quote!(crate::ltdc::B0Pin)), + (("ltdc", "B1"), quote!(crate::ltdc::B1Pin)), + (("ltdc", "B2"), quote!(crate::ltdc::B2Pin)), + (("ltdc", "B3"), quote!(crate::ltdc::B3Pin)), + (("ltdc", "B4"), quote!(crate::ltdc::B4Pin)), + (("ltdc", "B5"), quote!(crate::ltdc::B5Pin)), + (("ltdc", "B6"), quote!(crate::ltdc::B6Pin)), + (("ltdc", "B7"), quote!(crate::ltdc::B7Pin)), (("usb", "DP"), quote!(crate::usb::DpPin)), (("usb", "DM"), quote!(crate::usb::DmPin)), (("otg", "DP"), quote!(crate::usb::DpPin)), @@ -1030,6 +1091,38 @@ fn main() { (("octospi", "NCS"), quote!(crate::ospi::NSSPin)), (("octospi", "CLK"), quote!(crate::ospi::SckPin)), (("octospi", "NCLK"), quote!(crate::ospi::NckPin)), + (("tsc", "G1_IO1"), quote!(crate::tsc::G1IO1Pin)), + (("tsc", "G1_IO2"), quote!(crate::tsc::G1IO2Pin)), + (("tsc", "G1_IO3"), quote!(crate::tsc::G1IO3Pin)), + (("tsc", "G1_IO4"), quote!(crate::tsc::G1IO4Pin)), + (("tsc", "G2_IO1"), quote!(crate::tsc::G2IO1Pin)), + (("tsc", "G2_IO2"), quote!(crate::tsc::G2IO2Pin)), + (("tsc", "G2_IO3"), quote!(crate::tsc::G2IO3Pin)), + (("tsc", "G2_IO4"), quote!(crate::tsc::G2IO4Pin)), + (("tsc", "G3_IO1"), quote!(crate::tsc::G3IO1Pin)), + (("tsc", "G3_IO2"), quote!(crate::tsc::G3IO2Pin)), + (("tsc", "G3_IO3"), quote!(crate::tsc::G3IO3Pin)), + (("tsc", "G3_IO4"), quote!(crate::tsc::G3IO4Pin)), + (("tsc", "G4_IO1"), quote!(crate::tsc::G4IO1Pin)), + (("tsc", "G4_IO2"), quote!(crate::tsc::G4IO2Pin)), + (("tsc", "G4_IO3"), quote!(crate::tsc::G4IO3Pin)), + (("tsc", "G4_IO4"), quote!(crate::tsc::G4IO4Pin)), + (("tsc", "G5_IO1"), quote!(crate::tsc::G5IO1Pin)), + (("tsc", "G5_IO2"), quote!(crate::tsc::G5IO2Pin)), + (("tsc", "G5_IO3"), quote!(crate::tsc::G5IO3Pin)), + (("tsc", "G5_IO4"), quote!(crate::tsc::G5IO4Pin)), + (("tsc", "G6_IO1"), quote!(crate::tsc::G6IO1Pin)), + (("tsc", "G6_IO2"), quote!(crate::tsc::G6IO2Pin)), + (("tsc", "G6_IO3"), quote!(crate::tsc::G6IO3Pin)), + (("tsc", "G6_IO4"), quote!(crate::tsc::G6IO4Pin)), + (("tsc", "G7_IO1"), quote!(crate::tsc::G7IO1Pin)), + (("tsc", "G7_IO2"), quote!(crate::tsc::G7IO2Pin)), + (("tsc", "G7_IO3"), quote!(crate::tsc::G7IO3Pin)), + (("tsc", "G7_IO4"), quote!(crate::tsc::G7IO4Pin)), + (("tsc", "G8_IO1"), quote!(crate::tsc::G8IO1Pin)), + (("tsc", "G8_IO2"), quote!(crate::tsc::G8IO2Pin)), + (("tsc", "G8_IO3"), quote!(crate::tsc::G8IO3Pin)), + (("tsc", "G8_IO4"), quote!(crate::tsc::G8IO4Pin)), ].into(); for p in METADATA.peripherals { diff --git a/embassy-stm32/src/adc/f1.rs b/embassy-stm32/src/adc/f1.rs index 80eaecc14..3822d5032 100644 --- a/embassy-stm32/src/adc/f1.rs +++ b/embassy-stm32/src/adc/f1.rs @@ -5,7 +5,7 @@ use core::task::Poll; use embassy_hal_internal::into_ref; use super::blocking_delay_us; -use crate::adc::{Adc, AdcPin, Instance, SampleTime}; +use crate::adc::{Adc, AdcChannel, Instance, SampleTime}; use crate::time::Hertz; use crate::{interrupt, Peripheral}; @@ -32,16 +32,16 @@ impl interrupt::typelevel::Handler for InterruptHandl } pub struct Vref; -impl AdcPin for Vref {} -impl super::SealedAdcPin for Vref { +impl AdcChannel for Vref {} +impl super::SealedAdcChannel for Vref { fn channel(&self) -> u8 { 17 } } pub struct Temperature; -impl AdcPin for Temperature {} -impl super::SealedAdcPin for Temperature { +impl AdcChannel for Temperature {} +impl super::SealedAdcChannel for Temperature { fn channel(&self) -> u8 { 16 } @@ -135,8 +135,8 @@ impl<'d, T: Instance> Adc<'d, T> { T::regs().dr().read().0 as u16 } - pub async fn read(&mut self, pin: &mut impl AdcPin) -> u16 { - Self::set_channel_sample_time(pin.channel(), self.sample_time); + pub async fn read(&mut self, channel: &mut impl AdcChannel) -> u16 { + Self::set_channel_sample_time(channel.channel(), self.sample_time); T::regs().cr1().modify(|reg| { reg.set_scan(false); reg.set_discen(false); @@ -151,7 +151,7 @@ impl<'d, T: Instance> Adc<'d, T> { }); // Configure the channel to sample - T::regs().sqr3().write(|reg| reg.set_sq(0, pin.channel())); + T::regs().sqr3().write(|reg| reg.set_sq(0, channel.channel())); self.convert().await } diff --git a/embassy-stm32/src/adc/f3.rs b/embassy-stm32/src/adc/f3.rs index c22a3fe4a..3f076d64b 100644 --- a/embassy-stm32/src/adc/f3.rs +++ b/embassy-stm32/src/adc/f3.rs @@ -5,7 +5,7 @@ use core::task::Poll; use embassy_hal_internal::into_ref; use super::blocking_delay_us; -use crate::adc::{Adc, AdcPin, Instance, SampleTime}; +use crate::adc::{Adc, AdcChannel, Instance, SampleTime}; use crate::interrupt::typelevel::Interrupt; use crate::time::Hertz; use crate::{interrupt, Peripheral}; @@ -32,8 +32,8 @@ impl interrupt::typelevel::Handler for InterruptHandl } pub struct Vref; -impl AdcPin for Vref {} -impl super::SealedAdcPin for Vref { +impl AdcChannel for Vref {} +impl super::SealedAdcChannel for Vref { fn channel(&self) -> u8 { 18 } @@ -47,8 +47,8 @@ impl Vref { } pub struct Temperature; -impl AdcPin for Temperature {} -impl super::SealedAdcPin for Temperature { +impl AdcChannel for Temperature {} +impl super::SealedAdcChannel for Temperature { fn channel(&self) -> u8 { 16 } @@ -154,11 +154,11 @@ impl<'d, T: Instance> Adc<'d, T> { T::regs().dr().read().rdata() } - pub async fn read(&mut self, pin: &mut impl AdcPin) -> u16 { - Self::set_channel_sample_time(pin.channel(), self.sample_time); + pub async fn read(&mut self, channel: &mut impl AdcChannel) -> u16 { + Self::set_channel_sample_time(channel.channel(), self.sample_time); // Configure the channel to sample - T::regs().sqr1().write(|w| w.set_sq(0, pin.channel())); + T::regs().sqr1().write(|w| w.set_sq(0, channel.channel())); self.convert().await } diff --git a/embassy-stm32/src/adc/f3_v1_1.rs b/embassy-stm32/src/adc/f3_v1_1.rs index 672ace04f..106956989 100644 --- a/embassy-stm32/src/adc/f3_v1_1.rs +++ b/embassy-stm32/src/adc/f3_v1_1.rs @@ -7,7 +7,7 @@ use embassy_hal_internal::into_ref; use embassy_time::Instant; use super::Resolution; -use crate::adc::{Adc, AdcPin, Instance, SampleTime}; +use crate::adc::{Adc, AdcChannel, Instance, SampleTime}; use crate::interrupt::typelevel::Interrupt; use crate::time::Hertz; use crate::{interrupt, Peripheral}; @@ -64,8 +64,8 @@ fn update_vref(op: i8) { } pub struct Vref(core::marker::PhantomData); -impl AdcPin for Vref {} -impl super::SealedAdcPin for Vref { +impl AdcChannel for Vref {} +impl super::SealedAdcChannel for Vref { fn channel(&self) -> u8 { 17 } @@ -123,8 +123,8 @@ impl Drop for Vref { } pub struct Temperature(core::marker::PhantomData); -impl AdcPin for Temperature {} -impl super::SealedAdcPin for Temperature { +impl AdcChannel for Temperature {} +impl super::SealedAdcChannel for Temperature { fn channel(&self) -> u8 { 16 } @@ -271,8 +271,8 @@ impl<'d, T: Instance> Adc<'d, T> { } } - pub async fn read(&mut self, pin: &mut impl AdcPin) -> u16 { - self.set_sample_sequence(&[pin.channel()]).await; + pub async fn read(&mut self, channel: &mut impl AdcChannel) -> u16 { + self.set_sample_sequence(&[channel.channel()]).await; self.convert().await } @@ -283,18 +283,18 @@ impl<'d, T: Instance> Adc<'d, T> { } } - pub async fn set_sample_time(&mut self, pin: &mut impl AdcPin, sample_time: SampleTime) { - if Self::get_channel_sample_time(pin.channel()) != sample_time { + pub async fn set_sample_time(&mut self, channel: &mut impl AdcChannel, sample_time: SampleTime) { + if Self::get_channel_sample_time(channel.channel()) != sample_time { self.stop_adc().await; unsafe { - Self::set_channel_sample_time(pin.channel(), sample_time); + Self::set_channel_sample_time(channel.channel(), sample_time); } self.start_adc().await; } } - pub fn get_sample_time(&self, pin: &impl AdcPin) -> SampleTime { - Self::get_channel_sample_time(pin.channel()) + pub fn get_sample_time(&self, channel: &impl AdcChannel) -> SampleTime { + Self::get_channel_sample_time(channel.channel()) } /// Sets the channel sample time diff --git a/embassy-stm32/src/adc/g4.rs b/embassy-stm32/src/adc/g4.rs index 221cc2a40..ce7f5db70 100644 --- a/embassy-stm32/src/adc/g4.rs +++ b/embassy-stm32/src/adc/g4.rs @@ -2,7 +2,7 @@ use pac::adc::vals::{Adcaldif, Difsel, Exten}; use pac::adccommon::vals::Presc; -use super::{blocking_delay_us, Adc, AdcPin, Instance, InternalChannel, Resolution, SampleTime}; +use super::{blocking_delay_us, Adc, AdcChannel, Instance, Resolution, SampleTime}; use crate::time::Hertz; use crate::{pac, Peripheral}; @@ -33,8 +33,8 @@ const VBAT_CHANNEL: u8 = 17; // NOTE: Vrefint/Temperature/Vbat are not available on all ADCs, this currently cannot be modeled with stm32-data, so these are available from the software on all ADCs /// Internal voltage reference channel. pub struct VrefInt; -impl InternalChannel for VrefInt {} -impl super::SealedInternalChannel for VrefInt { +impl AdcChannel for VrefInt {} +impl super::SealedAdcChannel for VrefInt { fn channel(&self) -> u8 { VREF_CHANNEL } @@ -42,8 +42,8 @@ impl super::SealedInternalChannel for VrefInt { /// Internal temperature channel. pub struct Temperature; -impl InternalChannel for Temperature {} -impl super::SealedInternalChannel for Temperature { +impl AdcChannel for Temperature {} +impl super::SealedAdcChannel for Temperature { fn channel(&self) -> u8 { TEMP_CHANNEL } @@ -51,8 +51,8 @@ impl super::SealedInternalChannel for Temperature { /// Internal battery voltage channel. pub struct Vbat; -impl InternalChannel for Vbat {} -impl super::SealedInternalChannel for Vbat { +impl AdcChannel for Vbat {} +impl super::SealedAdcChannel for Vbat { fn channel(&self) -> u8 { VBAT_CHANNEL } @@ -258,18 +258,9 @@ impl<'d, T: Instance> Adc<'d, T> { } /// Read an ADC pin. - pub fn read

(&mut self, pin: &mut P) -> u16 - where - P: AdcPin, - P: crate::gpio::Pin, - { - pin.set_as_analog(); + pub fn read(&mut self, channel: &mut impl AdcChannel) -> u16 { + channel.setup(); - self.read_channel(pin.channel()) - } - - /// Read an ADC internal channel. - pub fn read_internal(&mut self, channel: &mut impl InternalChannel) -> u16 { self.read_channel(channel.channel()) } diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs index c753d046c..040ee9c53 100644 --- a/embassy-stm32/src/adc/mod.rs +++ b/embassy-stm32/src/adc/mod.rs @@ -15,6 +15,8 @@ #[cfg_attr(adc_g4, path = "g4.rs")] mod _version; +use core::marker::PhantomData; + #[allow(unused)] #[cfg(not(adc_f3_v2))] pub use _version::*; @@ -58,19 +60,14 @@ trait SealedInstance { fn state() -> &'static State; } -pub(crate) trait SealedAdcPin { - #[cfg(any(adc_v1, adc_l0, adc_v2))] - fn set_as_analog(&mut self) {} +pub(crate) trait SealedAdcChannel { + #[cfg(any(adc_v1, adc_l0, adc_v2, adc_g4, adc_v4))] + fn setup(&mut self) {} #[allow(unused)] fn channel(&self) -> u8; } -trait SealedInternalChannel { - #[allow(unused)] - fn channel(&self) -> u8; -} - /// Performs a busy-wait delay for a specified number of microseconds. #[allow(unused)] pub(crate) fn blocking_delay_us(us: u32) { @@ -124,12 +121,36 @@ pub trait Instance: SealedInstance + crate::Peripheral

+ crate::rcc::R type Interrupt: crate::interrupt::typelevel::Interrupt; } -/// ADC pin. +/// ADC channel. #[allow(private_bounds)] -pub trait AdcPin: SealedAdcPin {} -/// ADC internal channel. -#[allow(private_bounds)] -pub trait InternalChannel: SealedInternalChannel {} +pub trait AdcChannel: SealedAdcChannel + Sized { + #[allow(unused_mut)] + fn degrade_adc(mut self) -> AnyAdcChannel { + #[cfg(any(adc_v1, adc_l0, adc_v2, adc_g4, adc_v4))] + self.setup(); + + AnyAdcChannel { + channel: self.channel(), + _phantom: PhantomData, + } + } +} + +/// A type-erased channel for a given ADC instance. +/// +/// This is useful in scenarios where you need the ADC channels to have the same type, such as +/// storing them in an array. +pub struct AnyAdcChannel { + channel: u8, + _phantom: PhantomData, +} + +impl AdcChannel for AnyAdcChannel {} +impl SealedAdcChannel for AnyAdcChannel { + fn channel(&self) -> u8 { + self.channel + } +} foreach_adc!( ($inst:ident, $common_inst:ident, $clock:ident) => { @@ -158,11 +179,10 @@ foreach_adc!( macro_rules! impl_adc_pin { ($inst:ident, $pin:ident, $ch:expr) => { - impl crate::adc::AdcPin for crate::peripherals::$pin {} - - impl crate::adc::SealedAdcPin for crate::peripherals::$pin { - #[cfg(any(adc_v1, adc_l0, adc_v2))] - fn set_as_analog(&mut self) { + impl crate::adc::AdcChannel for crate::peripherals::$pin {} + impl crate::adc::SealedAdcChannel for crate::peripherals::$pin { + #[cfg(any(adc_v1, adc_l0, adc_v2, adc_g4, adc_v4))] + fn setup(&mut self) { ::set_as_analog(self); } diff --git a/embassy-stm32/src/adc/v1.rs b/embassy-stm32/src/adc/v1.rs index f17522076..090790c39 100644 --- a/embassy-stm32/src/adc/v1.rs +++ b/embassy-stm32/src/adc/v1.rs @@ -7,7 +7,7 @@ use embassy_hal_internal::into_ref; use stm32_metapac::adc::vals::Ckmode; use super::blocking_delay_us; -use crate::adc::{Adc, AdcPin, Instance, Resolution, SampleTime}; +use crate::adc::{Adc, AdcChannel, Instance, Resolution, SampleTime}; use crate::interrupt::typelevel::Interrupt; use crate::peripherals::ADC1; use crate::{interrupt, Peripheral}; @@ -36,26 +36,26 @@ impl interrupt::typelevel::Handler for InterruptHandl pub struct Vbat; #[cfg(not(adc_l0))] -impl AdcPin for Vbat {} +impl AdcChannel for Vbat {} #[cfg(not(adc_l0))] -impl super::SealedAdcPin for Vbat { +impl super::SealedAdcChannel for Vbat { fn channel(&self) -> u8 { 18 } } pub struct Vref; -impl AdcPin for Vref {} -impl super::SealedAdcPin for Vref { +impl AdcChannel for Vref {} +impl super::SealedAdcChannel for Vref { fn channel(&self) -> u8 { 17 } } pub struct Temperature; -impl AdcPin for Temperature {} -impl super::SealedAdcPin for Temperature { +impl AdcChannel for Temperature {} +impl super::SealedAdcChannel for Temperature { fn channel(&self) -> u8 { 16 } @@ -155,12 +155,12 @@ impl<'d, T: Instance> Adc<'d, T> { T::regs().cfgr2().modify(|reg| reg.set_ckmode(ckmode)); } - pub async fn read(&mut self, pin: &mut impl AdcPin) -> u16 { - let channel = pin.channel(); - pin.set_as_analog(); + pub async fn read(&mut self, channel: &mut impl AdcChannel) -> u16 { + let ch_num = channel.channel(); + channel.setup(); // A.7.5 Single conversion sequence code example - Software trigger - T::regs().chselr().write(|reg| reg.set_chselx(channel as usize, true)); + T::regs().chselr().write(|reg| reg.set_chselx(ch_num as usize, true)); self.convert().await } diff --git a/embassy-stm32/src/adc/v2.rs b/embassy-stm32/src/adc/v2.rs index 7771cf768..033108195 100644 --- a/embassy-stm32/src/adc/v2.rs +++ b/embassy-stm32/src/adc/v2.rs @@ -1,7 +1,7 @@ use embassy_hal_internal::into_ref; use super::blocking_delay_us; -use crate::adc::{Adc, AdcPin, Instance, Resolution, SampleTime}; +use crate::adc::{Adc, AdcChannel, Instance, Resolution, SampleTime}; use crate::peripherals::ADC1; use crate::time::Hertz; use crate::Peripheral; @@ -12,8 +12,8 @@ pub const VREF_DEFAULT_MV: u32 = 3300; pub const VREF_CALIB_MV: u32 = 3300; pub struct VrefInt; -impl AdcPin for VrefInt {} -impl super::SealedAdcPin for VrefInt { +impl AdcChannel for VrefInt {} +impl super::SealedAdcChannel for VrefInt { fn channel(&self) -> u8 { 17 } @@ -27,8 +27,8 @@ impl VrefInt { } pub struct Temperature; -impl AdcPin for Temperature {} -impl super::SealedAdcPin for Temperature { +impl AdcChannel for Temperature {} +impl super::SealedAdcChannel for Temperature { fn channel(&self) -> u8 { cfg_if::cfg_if! { if #[cfg(any(stm32f2, stm32f40, stm32f41))] { @@ -48,8 +48,8 @@ impl Temperature { } pub struct Vbat; -impl AdcPin for Vbat {} -impl super::SealedAdcPin for Vbat { +impl AdcChannel for Vbat {} +impl super::SealedAdcChannel for Vbat { fn channel(&self) -> u8 { 18 } @@ -175,11 +175,11 @@ where T::regs().dr().read().0 as u16 } - pub fn read(&mut self, pin: &mut impl AdcPin) -> u16 { - pin.set_as_analog(); + pub fn read(&mut self, channel: &mut impl AdcChannel) -> u16 { + channel.setup(); // Configure ADC - let channel = pin.channel(); + let channel = channel.channel(); // Select channel T::regs().sqr3().write(|reg| reg.set_sq(0, channel)); diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs index dc418297e..be857f4dd 100644 --- a/embassy-stm32/src/adc/v3.rs +++ b/embassy-stm32/src/adc/v3.rs @@ -2,7 +2,7 @@ use cfg_if::cfg_if; use embassy_hal_internal::into_ref; use super::blocking_delay_us; -use crate::adc::{Adc, AdcPin, Instance, Resolution, SampleTime}; +use crate::adc::{Adc, AdcChannel, Instance, Resolution, SampleTime}; use crate::Peripheral; /// Default VREF voltage used for sample conversion to millivolts. @@ -11,8 +11,8 @@ pub const VREF_DEFAULT_MV: u32 = 3300; pub const VREF_CALIB_MV: u32 = 3000; pub struct VrefInt; -impl AdcPin for VrefInt {} -impl super::SealedAdcPin for VrefInt { +impl AdcChannel for VrefInt {} +impl super::SealedAdcChannel for VrefInt { fn channel(&self) -> u8 { cfg_if! { if #[cfg(adc_g0)] { @@ -30,8 +30,8 @@ impl super::SealedAdcPin for VrefInt { } pub struct Temperature; -impl AdcPin for Temperature {} -impl super::SealedAdcPin for Temperature { +impl AdcChannel for Temperature {} +impl super::SealedAdcChannel for Temperature { fn channel(&self) -> u8 { cfg_if! { if #[cfg(adc_g0)] { @@ -49,8 +49,8 @@ impl super::SealedAdcPin for Temperature { } pub struct Vbat; -impl AdcPin for Vbat {} -impl super::SealedAdcPin for Vbat { +impl AdcChannel for Vbat {} +impl super::SealedAdcChannel for Vbat { fn channel(&self) -> u8 { cfg_if! { if #[cfg(adc_g0)] { @@ -70,8 +70,8 @@ impl super::SealedAdcPin for Vbat { cfg_if! { if #[cfg(adc_h5)] { pub struct VddCore; - impl AdcPin for VddCore {} - impl super::SealedAdcPin for VddCore { + impl AdcChannel for VddCore {} + impl super::SealedAdcChannel for VddCore { fn channel(&self) -> u8 { 6 } @@ -82,8 +82,8 @@ cfg_if! { cfg_if! { if #[cfg(adc_u0)] { pub struct DacOut; - impl AdcPin for DacOut {} - impl super::SealedAdcPin for DacOut { + impl AdcChannel for DacOut {} + impl super::SealedAdcChannel for DacOut { fn channel(&self) -> u8 { 19 } @@ -220,7 +220,7 @@ impl<'d, T: Instance> Adc<'d, T> { T::regs().dr().read().0 as u16 } - pub fn read(&mut self, pin: &mut impl AdcPin) -> u16 { + pub fn read(&mut self, channel: &mut impl AdcChannel) -> u16 { // Make sure bits are off while T::regs().cr().read().addis() { // spin @@ -241,18 +241,18 @@ impl<'d, T: Instance> Adc<'d, T> { // RM0492, RM0481, etc. // "This option bit must be set to 1 when ADCx_INP0 or ADCx_INN1 channel is selected." #[cfg(adc_h5)] - if pin.channel() == 0 { + if channel.channel() == 0 { T::regs().or().modify(|reg| reg.set_op0(true)); } // Configure channel - Self::set_channel_sample_time(pin.channel(), self.sample_time); + Self::set_channel_sample_time(channel.channel(), self.sample_time); // Select channel #[cfg(not(any(adc_g0, adc_u0)))] - T::regs().sqr1().write(|reg| reg.set_sq(0, pin.channel())); + T::regs().sqr1().write(|reg| reg.set_sq(0, channel.channel())); #[cfg(any(adc_g0, adc_u0))] - T::regs().chselr().write(|reg| reg.set_chsel(1 << pin.channel())); + T::regs().chselr().write(|reg| reg.set_chsel(1 << channel.channel())); // Some models are affected by an erratum: // If we perform conversions slower than 1 kHz, the first read ADC value can be @@ -270,7 +270,7 @@ impl<'d, T: Instance> Adc<'d, T> { // RM0492, RM0481, etc. // "This option bit must be set to 1 when ADCx_INP0 or ADCx_INN1 channel is selected." #[cfg(adc_h5)] - if pin.channel() == 0 { + if channel.channel() == 0 { T::regs().or().modify(|reg| reg.set_op0(false)); } diff --git a/embassy-stm32/src/adc/v4.rs b/embassy-stm32/src/adc/v4.rs index ca87b41ee..f564114c2 100644 --- a/embassy-stm32/src/adc/v4.rs +++ b/embassy-stm32/src/adc/v4.rs @@ -2,7 +2,7 @@ use pac::adc::vals::{Adcaldif, Boost, Difsel, Exten, Pcsel}; use pac::adccommon::vals::Presc; -use super::{blocking_delay_us, Adc, AdcPin, Instance, InternalChannel, Resolution, SampleTime}; +use super::{blocking_delay_us, Adc, AdcChannel, Instance, Resolution, SampleTime}; use crate::time::Hertz; use crate::{pac, Peripheral}; @@ -33,8 +33,8 @@ const VBAT_CHANNEL: u8 = 17; // NOTE: Vrefint/Temperature/Vbat are not available on all ADCs, this currently cannot be modeled with stm32-data, so these are available from the software on all ADCs /// Internal voltage reference channel. pub struct VrefInt; -impl InternalChannel for VrefInt {} -impl super::SealedInternalChannel for VrefInt { +impl AdcChannel for VrefInt {} +impl super::SealedAdcChannel for VrefInt { fn channel(&self) -> u8 { VREF_CHANNEL } @@ -42,8 +42,8 @@ impl super::SealedInternalChannel for VrefInt { /// Internal temperature channel. pub struct Temperature; -impl InternalChannel for Temperature {} -impl super::SealedInternalChannel for Temperature { +impl AdcChannel for Temperature {} +impl super::SealedAdcChannel for Temperature { fn channel(&self) -> u8 { TEMP_CHANNEL } @@ -51,8 +51,8 @@ impl super::SealedInternalChannel for Temperature { /// Internal battery voltage channel. pub struct Vbat; -impl InternalChannel for Vbat {} -impl super::SealedInternalChannel for Vbat { +impl AdcChannel for Vbat {} +impl super::SealedAdcChannel for Vbat { fn channel(&self) -> u8 { VBAT_CHANNEL } @@ -271,19 +271,10 @@ impl<'d, T: Instance> Adc<'d, T> { T::regs().dr().read().0 as u16 } - /// Read an ADC pin. - pub fn read

(&mut self, pin: &mut P) -> u16 - where - P: AdcPin, - P: crate::gpio::Pin, - { - pin.set_as_analog(); + /// Read an ADC channel. + pub fn read(&mut self, channel: &mut impl AdcChannel) -> u16 { + channel.setup(); - self.read_channel(pin.channel()) - } - - /// Read an ADC internal channel. - pub fn read_internal(&mut self, channel: &mut impl InternalChannel) -> u16 { self.read_channel(channel.channel()) } diff --git a/embassy-stm32/src/dsihost.rs b/embassy-stm32/src/dsihost.rs new file mode 100644 index 000000000..f1c737fdc --- /dev/null +++ b/embassy-stm32/src/dsihost.rs @@ -0,0 +1,429 @@ +//! DSI HOST + +use core::marker::PhantomData; + +use embassy_hal_internal::{into_ref, PeripheralRef}; + +//use crate::gpio::{AnyPin, SealedPin}; +use crate::gpio::{AFType, AnyPin, Pull, Speed}; +use crate::rcc::RccPeripheral; +use crate::{peripherals, Peripheral}; + +/// Performs a busy-wait delay for a specified number of microseconds. +pub fn blocking_delay_ms(ms: u32) { + #[cfg(time)] + embassy_time::block_for(embassy_time::Duration::from_millis(ms)); + #[cfg(not(time))] + cortex_m::asm::delay(unsafe { crate::rcc::get_freqs() }.sys.unwrap().0 / 1_000 * ms); +} + +/// PacketTypes extracted from CubeMX +#[repr(u8)] +#[allow(dead_code)] +pub enum PacketType { + /// DCS short write, no parameters + DcsShortPktWriteP0, + /// DCS short write, one parameter + DcsShortPktWriteP1, + /// Generic short write, no parameters + GenShortPktWriteP0, + /// Generic short write, one parameter + GenShortPktWriteP1, + /// Generic short write, two parameters + GenShortPktWriteP2, + /// DCS long write + DcsLongPktWrite, + /// Generic long write + GenLongPktWrite, + /// DCS short read + DcsShortPktRead(u8), + /// Generic short read, no parameters + GenShortPktReadP0, + /// Generic short read, one parameter + GenShortPktReadP1(u8), + /// Generic short read, two parameters + GenShortPktReadP2(u8, u8), + /// Used to set the maximum return packet size for reading data + MaxReturnPktSize, +} + +impl From for u8 { + fn from(packet_type: PacketType) -> u8 { + match packet_type { + PacketType::DcsShortPktWriteP0 => 0x05, + PacketType::DcsShortPktWriteP1 => 0x15, + PacketType::GenShortPktWriteP0 => 0x03, + PacketType::GenShortPktWriteP1 => 0x13, + PacketType::GenShortPktWriteP2 => 0x23, + PacketType::DcsLongPktWrite => 0x39, + PacketType::GenLongPktWrite => 0x29, + PacketType::DcsShortPktRead(_) => 0x06, + PacketType::GenShortPktReadP0 => 0x04, + PacketType::GenShortPktReadP1(_) => 0x14, + PacketType::GenShortPktReadP2(_, _) => 0x24, + PacketType::MaxReturnPktSize => 0x37, + } + } +} + +/// DSIHOST driver. +pub struct DsiHost<'d, T: Instance> { + _peri: PhantomData<&'d mut T>, + _te: PeripheralRef<'d, AnyPin>, +} + +impl<'d, T: Instance> DsiHost<'d, T> { + /// Note: Full-Duplex modes are not supported at this time + pub fn new(_peri: impl Peripheral

+ 'd, te: impl Peripheral

> + 'd) -> Self { + into_ref!(te); + + T::enable_and_reset(); + + // Set Tearing Enable pin according to CubeMx example + te.set_as_af_pull(te.af_num(), AFType::OutputPushPull, Pull::None); + te.set_speed(Speed::Low); + /* + T::regs().wcr().modify(|w| { + w.set_dsien(true); + }); + */ + Self { + _peri: PhantomData, + _te: te.map_into(), + } + } + + /// Get the DSIHOST hardware version. Found in the reference manual for comparison. + pub fn get_version(&self) -> u32 { + T::regs().vr().read().version() + } + + /// Set the enable bit in the control register and assert that it has been enabled + pub fn enable(&mut self) { + T::regs().cr().modify(|w| w.set_en(true)); + assert!(T::regs().cr().read().en()) + } + + /// Unset the enable bit in the control register and assert that it has been disabled + pub fn disable(&mut self) { + T::regs().cr().modify(|w| w.set_en(false)); + assert!(!T::regs().cr().read().en()) + } + + /// Set the DSI enable bit in the wrapper control register and assert that it has been enabled + pub fn enable_wrapper_dsi(&mut self) { + T::regs().wcr().modify(|w| w.set_dsien(true)); + assert!(T::regs().wcr().read().dsien()) + } + + /// Unset the DSI enable bit in the wrapper control register and assert that it has been disabled + pub fn disable_wrapper_dsi(&mut self) { + T::regs().wcr().modify(|w| w.set_dsien(false)); + assert!(!T::regs().wcr().read().dsien()) + } + + /// DCS or Generic short/long write command + pub fn write_cmd(&mut self, channel_id: u8, address: u8, data: &[u8]) -> Result<(), Error> { + assert!(data.len() > 0); + + if data.len() == 1 { + self.short_write(channel_id, PacketType::DcsShortPktWriteP1, address, data[0]) + } else { + self.long_write( + channel_id, + PacketType::DcsLongPktWrite, // FIXME: This might be a generic long packet, as well... + address, + data, + ) + } + } + + fn short_write(&mut self, channel_id: u8, packet_type: PacketType, param1: u8, param2: u8) -> Result<(), Error> { + #[cfg(feature = "defmt")] + defmt::debug!("short_write: BEGIN wait for command fifo empty"); + + // Wait for Command FIFO empty + self.wait_command_fifo_empty()?; + #[cfg(feature = "defmt")] + defmt::debug!("short_write: END wait for command fifo empty"); + + // Configure the packet to send a short DCS command with 0 or 1 parameters + // Update the DSI packet header with new information + self.config_packet_header(channel_id, packet_type, param1, param2); + + self.wait_command_fifo_empty()?; + + let status = T::regs().isr1().read().0; + if status != 0 { + error!("ISR1 after short_write(): {:b}", status); + } + Ok(()) + } + + fn config_packet_header(&mut self, channel_id: u8, packet_type: PacketType, param1: u8, param2: u8) { + T::regs().ghcr().write(|w| { + w.set_dt(packet_type.into()); + w.set_vcid(channel_id); + w.set_wclsb(param1); + w.set_wcmsb(param2); + }); + } + + /// Write long DCS or long Generic command. + /// + /// `params` is expected to contain at least 2 elements. Use [`short_write`] for a single element. + fn long_write(&mut self, channel_id: u8, packet_type: PacketType, address: u8, data: &[u8]) -> Result<(), Error> { + // Must be a long packet if we do the long write, obviously. + assert!(matches!( + packet_type, + PacketType::DcsLongPktWrite | PacketType::GenLongPktWrite + )); + + // params needs to have at least 2 elements, otherwise short_write should be used + assert!(data.len() >= 2); + + #[cfg(feature = "defmt")] + defmt::debug!("long_write: BEGIN wait for command fifo empty"); + + self.wait_command_fifo_empty()?; + + #[cfg(feature = "defmt")] + defmt::debug!("long_write: DONE wait for command fifo empty"); + + // Note: CubeMX example "NbParams" is always one LESS than params.len() + // DCS code (last element of params) must be on payload byte 1 and if we have only 2 more params, + // then they must go into data2 and data3 + T::regs().gpdr().write(|w| { + // data[2] may or may not exist. + if let Some(x) = data.get(2) { + w.set_data4(*x); + } + // data[0] and [1] have to exist if long_write is called. + w.set_data3(data[1]); + w.set_data2(data[0]); + + // DCS Code + w.set_data1(address); + }); + + self.wait_command_fifo_empty()?; + + // These steps are only necessary if more than 1x 4 bytes need to go into the FIFO + if data.len() >= 4 { + // Generate an iterator that iterates over chunks of exactly 4 bytes + let iter = data[3..data.len()].chunks_exact(4); + // Obtain remainder before consuming iter + let remainder = iter.remainder(); + + // Keep filling the buffer with remaining data + for param in iter { + self.wait_command_fifo_not_full()?; + T::regs().gpdr().write(|w| { + w.set_data4(param[3]); + w.set_data3(param[2]); + w.set_data2(param[1]); + w.set_data1(param[0]); + }); + + self.wait_command_fifo_empty().unwrap(); + } + + // If the remaining data was not devisible by 4 we get a remainder + if remainder.len() >= 1 { + self.wait_command_fifo_not_full()?; + T::regs().gpdr().write(|w| { + if let Some(x) = remainder.get(2) { + w.set_data3(*x); + } + if let Some(x) = remainder.get(1) { + w.set_data2(*x); + } + w.set_data1(remainder[0]); + }); + self.wait_command_fifo_empty().unwrap(); + } + } + // Configure the packet to send a long DCS command + self.config_packet_header( + channel_id, + packet_type, + ((data.len() + 1) & 0x00FF) as u8, // +1 to account for address byte + (((data.len() + 1) & 0xFF00) >> 8) as u8, // +1 to account for address byte + ); + + self.wait_command_fifo_empty()?; + + let status = T::regs().isr1().read().0; + if status != 0 { + error!("ISR1 after long_write(): {:b}", status); + } + Ok(()) + } + + /// Read DSI Register + pub fn read( + &mut self, + channel_id: u8, + packet_type: PacketType, + read_size: u16, + data: &mut [u8], + ) -> Result<(), Error> { + if data.len() != read_size as usize { + return Err(Error::InvalidReadSize); + } + + // Set the maximum return packet size + self.short_write( + channel_id, + PacketType::MaxReturnPktSize, + (read_size & 0xFF) as u8, + ((read_size & 0xFF00) >> 8) as u8, + )?; + + // Set the packet header according to the packet_type + use PacketType::*; + match packet_type { + DcsShortPktRead(cmd) => self.config_packet_header(channel_id, packet_type, cmd, 0), + GenShortPktReadP0 => self.config_packet_header(channel_id, packet_type, 0, 0), + GenShortPktReadP1(param1) => self.config_packet_header(channel_id, packet_type, param1, 0), + GenShortPktReadP2(param1, param2) => self.config_packet_header(channel_id, packet_type, param1, param2), + _ => return Err(Error::InvalidPacketType), + } + + self.wait_read_not_busy()?; + + // Obtain chunks of 32-bit so the entire FIFO data register can be read + for bytes in data.chunks_exact_mut(4) { + self.wait_payload_read_fifo_not_empty()?; + + // Only perform a single read on the entire register to avoid unintended side-effects + let gpdr = T::regs().gpdr().read(); + bytes[0] = gpdr.data1(); + bytes[1] = gpdr.data2(); + bytes[2] = gpdr.data3(); + bytes[3] = gpdr.data4(); + } + + // Collect the remaining chunks and read the corresponding number of bytes from the FIFO + let remainder = data.chunks_exact_mut(4).into_remainder(); + if !remainder.is_empty() { + self.wait_payload_read_fifo_not_empty()?; + // Only perform a single read on the entire register to avoid unintended side-effects + let gpdr = T::regs().gpdr().read(); + if let Some(x) = remainder.get_mut(0) { + *x = gpdr.data1() + } + if let Some(x) = remainder.get_mut(1) { + *x = gpdr.data2() + } + if let Some(x) = remainder.get_mut(2) { + *x = gpdr.data3() + } + } + + /* + // Used this to check whether there are read errors. Does not seem like it. + if !self.read_busy() { + defmt::debug!("Read not busy!"); + if self.packet_size_error() { + return Err(Error::ReadError); + } + } + */ + Ok(()) + } + + fn wait_command_fifo_empty(&self) -> Result<(), Error> { + for _ in 1..1000 { + // Wait for Command FIFO empty + if T::regs().gpsr().read().cmdfe() { + return Ok(()); + } + blocking_delay_ms(1); + } + Err(Error::FifoTimeout) + } + + fn wait_command_fifo_not_full(&self) -> Result<(), Error> { + for _ in 1..1000 { + // Wait for Command FIFO not empty + if !T::regs().gpsr().read().cmdff() { + return Ok(()); + } + blocking_delay_ms(1); + } + Err(Error::FifoTimeout) + } + + fn wait_read_not_busy(&self) -> Result<(), Error> { + for _ in 1..1000 { + // Wait for read not busy + if !self.read_busy() { + return Ok(()); + } + blocking_delay_ms(1); + } + Err(Error::ReadTimeout) + } + + fn wait_payload_read_fifo_not_empty(&self) -> Result<(), Error> { + for _ in 1..1000 { + // Wait for payload read FIFO not empty + if !T::regs().gpsr().read().prdfe() { + return Ok(()); + } + blocking_delay_ms(1); + } + Err(Error::FifoTimeout) + } + + fn _packet_size_error(&self) -> bool { + T::regs().isr1().read().pse() + } + + fn read_busy(&self) -> bool { + T::regs().gpsr().read().rcb() + } +} + +/// Possible Error Types for DSI HOST +#[non_exhaustive] +#[derive(Debug)] +pub enum Error { + /// Waiting for FIFO empty flag timed out + FifoTimeout, + /// The specified `PacketType` is invalid for the selected operation + InvalidPacketType, + /// Provided read size does not match the read buffer length + InvalidReadSize, + /// Error during read + ReadError, + /// Read operation timed out + ReadTimeout, +} + +impl<'d, T: Instance> Drop for DsiHost<'d, T> { + fn drop(&mut self) {} +} + +trait SealedInstance: crate::rcc::SealedRccPeripheral { + fn regs() -> &'static crate::pac::dsihost::Dsihost; +} + +/// DSI instance trait. +#[allow(private_bounds)] +pub trait Instance: SealedInstance + RccPeripheral + 'static {} + +pin_trait!(TePin, Instance); + +foreach_peripheral!( + (dsihost, $inst:ident) => { + impl crate::dsihost::SealedInstance for peripherals::$inst { + fn regs() -> &'static crate::pac::dsihost::Dsihost { + &crate::pac::$inst + } + } + + impl crate::dsihost::Instance for peripherals::$inst {} + }; +); diff --git a/embassy-stm32/src/hsem/mod.rs b/embassy-stm32/src/hsem/mod.rs new file mode 100644 index 000000000..b77a3415b --- /dev/null +++ b/embassy-stm32/src/hsem/mod.rs @@ -0,0 +1,182 @@ +//! Hardware Semaphore (HSEM) + +// TODO: This code works for all HSEM implemenations except for the STM32WBA52/4/5xx MCUs. +// Those MCUs have a different HSEM implementation (Secure semaphore lock support, +// Privileged / unprivileged semaphore lock support, Semaphore lock protection via semaphore attribute), +// which is not yet supported by this code. +use embassy_hal_internal::{into_ref, PeripheralRef}; + +use crate::rcc::RccPeripheral; +use crate::{pac, Peripheral}; + +/// HSEM error. +#[derive(Debug)] +pub enum HsemError { + /// Locking the semaphore failed. + LockFailed, +} + +/// CPU core. +/// The enum values are identical to the bus master IDs / core Ids defined for each +/// chip family (i.e. stm32h747 see rm0399 table 95) +#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)] +#[repr(u8)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum CoreId { + #[cfg(any(stm32h745, stm32h747, stm32h755, stm32h757))] + /// Cortex-M7, core 1. + Core0 = 0x3, + + #[cfg(any(stm32h745, stm32h747, stm32h755, stm32h757))] + /// Cortex-M4, core 2. + Core1 = 0x1, + + #[cfg(not(any(stm32h745, stm32h747, stm32h755, stm32h757)))] + /// Cortex-M4, core 1 + Core0 = 0x4, + + #[cfg(any(stm32wb, stm32wl))] + /// Cortex-M0+, core 2. + Core1 = 0x8, +} + +/// Get the current core id +/// This code assume that it is only executed on a Cortex-M M0+, M4 or M7 core. +#[inline(always)] +pub fn get_current_coreid() -> CoreId { + let cpuid = unsafe { cortex_m::peripheral::CPUID::PTR.read_volatile().base.read() }; + match cpuid & 0x000000F0 { + #[cfg(any(stm32wb, stm32wl))] + 0x0 => CoreId::Core1, + + #[cfg(not(any(stm32h745, stm32h747, stm32h755, stm32h757)))] + 0x4 => CoreId::Core0, + + #[cfg(any(stm32h745, stm32h747, stm32h755, stm32h757))] + 0x4 => CoreId::Core1, + + #[cfg(any(stm32h745, stm32h747, stm32h755, stm32h757))] + 0x7 => CoreId::Core0, + _ => panic!("Unknown Cortex-M core"), + } +} + +/// Translates the core ID to an index into the interrupt registers. +#[inline(always)] +fn core_id_to_index(core: CoreId) -> usize { + match core { + CoreId::Core0 => 0, + #[cfg(any(stm32h745, stm32h747, stm32h755, stm32h757, stm32wb, stm32wl))] + CoreId::Core1 => 1, + } +} + +/// HSEM driver +pub struct HardwareSemaphore<'d, T: Instance> { + _peri: PeripheralRef<'d, T>, +} + +impl<'d, T: Instance> HardwareSemaphore<'d, T> { + /// Creates a new HardwareSemaphore instance. + pub fn new(peripheral: impl Peripheral

+ 'd) -> Self { + into_ref!(peripheral); + HardwareSemaphore { _peri: peripheral } + } + + /// Locks the semaphore. + /// The 2-step lock procedure consists in a write to lock the semaphore, followed by a read to + /// check if the lock has been successful, carried out from the HSEM_Rx register. + pub fn two_step_lock(&mut self, sem_id: u8, process_id: u8) -> Result<(), HsemError> { + T::regs().r(sem_id as usize).write(|w| { + w.set_procid(process_id); + w.set_coreid(get_current_coreid() as u8); + w.set_lock(true); + }); + let reg = T::regs().r(sem_id as usize).read(); + match ( + reg.lock(), + reg.coreid() == get_current_coreid() as u8, + reg.procid() == process_id, + ) { + (true, true, true) => Ok(()), + _ => Err(HsemError::LockFailed), + } + } + + /// Locks the semaphore. + /// The 1-step procedure consists in a read to lock and check the semaphore in a single step, + /// carried out from the HSEM_RLRx register. + pub fn one_step_lock(&mut self, sem_id: u8) -> Result<(), HsemError> { + let reg = T::regs().rlr(sem_id as usize).read(); + match (reg.lock(), reg.coreid() == get_current_coreid() as u8, reg.procid()) { + (false, true, 0) => Ok(()), + _ => Err(HsemError::LockFailed), + } + } + + /// Unlocks the semaphore. + /// Unlocking a semaphore is a protected process, to prevent accidental clearing by a AHB bus + /// core ID or by a process not having the semaphore lock right. + pub fn unlock(&mut self, sem_id: u8, process_id: u8) { + T::regs().r(sem_id as usize).write(|w| { + w.set_procid(process_id); + w.set_coreid(get_current_coreid() as u8); + w.set_lock(false); + }); + } + + /// Unlocks all semaphores. + /// All semaphores locked by a COREID can be unlocked at once by using the HSEM_CR + /// register. Write COREID and correct KEY value in HSEM_CR. All locked semaphores with a + /// matching COREID are unlocked, and may generate an interrupt when enabled. + pub fn unlock_all(&mut self, key: u16, core_id: u8) { + T::regs().cr().write(|w| { + w.set_key(key); + w.set_coreid(core_id); + }); + } + + /// Checks if the semaphore is locked. + pub fn is_semaphore_locked(&self, sem_id: u8) -> bool { + T::regs().r(sem_id as usize).read().lock() + } + + /// Sets the clear (unlock) key + pub fn set_clear_key(&mut self, key: u16) { + T::regs().keyr().modify(|w| w.set_key(key)); + } + + /// Gets the clear (unlock) key + pub fn get_clear_key(&mut self) -> u16 { + T::regs().keyr().read().key() + } + + /// Sets the interrupt enable bit for the semaphore. + pub fn enable_interrupt(&mut self, core_id: CoreId, sem_x: usize, enable: bool) { + T::regs() + .ier(core_id_to_index(core_id)) + .modify(|w| w.set_ise(sem_x, enable)); + } + + /// Clears the interrupt flag for the semaphore. + pub fn clear_interrupt(&mut self, core_id: CoreId, sem_x: usize) { + T::regs() + .icr(core_id_to_index(core_id)) + .write(|w| w.set_isc(sem_x, false)); + } +} + +trait SealedInstance { + fn regs() -> pac::hsem::Hsem; +} + +/// HSEM instance trait. +#[allow(private_bounds)] +pub trait Instance: SealedInstance + RccPeripheral + Send + 'static {} + +impl SealedInstance for crate::peripherals::HSEM { + fn regs() -> crate::pac::hsem::Hsem { + crate::pac::HSEM + } +} +impl Instance for crate::peripherals::HSEM {} diff --git a/embassy-stm32/src/i2c/mod.rs b/embassy-stm32/src/i2c/mod.rs index 0b2a56305..ef5fd0972 100644 --- a/embassy-stm32/src/i2c/mod.rs +++ b/embassy-stm32/src/i2c/mod.rs @@ -9,7 +9,7 @@ use core::future::Future; use core::iter; use core::marker::PhantomData; -use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; +use embassy_hal_internal::{into_ref, Peripheral}; use embassy_sync::waitqueue::AtomicWaker; #[cfg(feature = "time")] use embassy_time::{Duration, Instant}; @@ -18,6 +18,7 @@ use crate::dma::ChannelAndRequest; use crate::gpio::{AFType, Pull}; use crate::interrupt::typelevel::Interrupt; use crate::mode::{Async, Blocking, Mode}; +use crate::rcc::{ClockEnableBit, SealedRccPeripheral}; use crate::time::Hertz; use crate::{interrupt, peripherals}; @@ -72,8 +73,10 @@ impl Default for Config { } /// I2C driver. -pub struct I2c<'d, T: Instance, M: Mode> { - _peri: PeripheralRef<'d, T>, +pub struct I2c<'d, M: Mode> { + info: &'static Info, + state: &'static State, + kernel_clock: Hertz, tx_dma: Option>, rx_dma: Option>, #[cfg(feature = "time")] @@ -81,9 +84,9 @@ pub struct I2c<'d, T: Instance, M: Mode> { _phantom: PhantomData, } -impl<'d, T: Instance> I2c<'d, T, Async> { +impl<'d> I2c<'d, Async> { /// Create a new I2C driver. - pub fn new( + pub fn new( peri: impl Peripheral

+ 'd, scl: impl Peripheral

> + 'd, sda: impl Peripheral

> + 'd, @@ -99,9 +102,9 @@ impl<'d, T: Instance> I2c<'d, T, Async> { } } -impl<'d, T: Instance> I2c<'d, T, Blocking> { +impl<'d> I2c<'d, Blocking> { /// Create a new blocking I2C driver. - pub fn new_blocking( + pub fn new_blocking( peri: impl Peripheral

+ 'd, scl: impl Peripheral

> + 'd, sda: impl Peripheral

> + 'd, @@ -112,10 +115,10 @@ impl<'d, T: Instance> I2c<'d, T, Blocking> { } } -impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { +impl<'d, M: Mode> I2c<'d, M> { /// Create a new I2C driver. - fn new_inner( - peri: impl Peripheral

+ 'd, + fn new_inner( + _peri: impl Peripheral

+ 'd, scl: impl Peripheral

> + 'd, sda: impl Peripheral

> + 'd, tx_dma: Option>, @@ -123,7 +126,7 @@ impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { freq: Hertz, config: Config, ) -> Self { - into_ref!(peri, scl, sda); + into_ref!(scl, sda); T::enable_and_reset(); @@ -148,7 +151,9 @@ impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { unsafe { T::ErrorInterrupt::enable() }; let mut this = Self { - _peri: peri, + info: T::info(), + state: T::state(), + kernel_clock: T::frequency(), tx_dma, rx_dma, #[cfg(feature = "time")] @@ -217,19 +222,14 @@ impl State { } } -trait SealedInstance: crate::rcc::RccPeripheral { - fn regs() -> crate::pac::i2c::I2c; - fn state() -> &'static State; +struct Info { + regs: crate::pac::i2c::I2c, + pub(crate) enable_bit: ClockEnableBit, } -/// I2C peripheral instance -#[allow(private_bounds)] -pub trait Instance: SealedInstance + 'static { - /// Event interrupt for this instance - type EventInterrupt: interrupt::typelevel::Interrupt; - /// Error interrupt for this instance - type ErrorInterrupt: interrupt::typelevel::Interrupt; -} +peri_trait!( + irqs: [EventInterrupt, ErrorInterrupt], +); pin_trait!(SclPin, Instance); pin_trait!(SdaPin, Instance); @@ -260,11 +260,15 @@ impl interrupt::typelevel::Handler for ErrorInte foreach_peripheral!( (i2c, $inst:ident) => { + #[allow(private_interfaces)] impl SealedInstance for peripherals::$inst { - fn regs() -> crate::pac::i2c::I2c { - crate::pac::$inst + fn info() -> &'static Info { + static INFO: Info = Info{ + regs: crate::pac::$inst, + enable_bit: crate::peripherals::$inst::ENABLE_BIT, + }; + &INFO } - fn state() -> &'static State { static STATE: State = State::new(); &STATE @@ -278,7 +282,7 @@ foreach_peripheral!( }; ); -impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::i2c::Read for I2c<'d, T, M> { +impl<'d, M: Mode> embedded_hal_02::blocking::i2c::Read for I2c<'d, M> { type Error = Error; fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { @@ -286,7 +290,7 @@ impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::i2c::Read for I2c<'d, } } -impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::i2c::Write for I2c<'d, T, M> { +impl<'d, M: Mode> embedded_hal_02::blocking::i2c::Write for I2c<'d, M> { type Error = Error; fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> { @@ -294,7 +298,7 @@ impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::i2c::Write for I2c<'d, } } -impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::i2c::WriteRead for I2c<'d, T, M> { +impl<'d, M: Mode> embedded_hal_02::blocking::i2c::WriteRead for I2c<'d, M> { type Error = Error; fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> { @@ -318,11 +322,11 @@ impl embedded_hal_1::i2c::Error for Error { } } -impl<'d, T: Instance, M: Mode> embedded_hal_1::i2c::ErrorType for I2c<'d, T, M> { +impl<'d, M: Mode> embedded_hal_1::i2c::ErrorType for I2c<'d, M> { type Error = Error; } -impl<'d, T: Instance, M: Mode> embedded_hal_1::i2c::I2c for I2c<'d, T, M> { +impl<'d, M: Mode> embedded_hal_1::i2c::I2c for I2c<'d, M> { fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> { self.blocking_read(address, read) } @@ -344,7 +348,7 @@ impl<'d, T: Instance, M: Mode> embedded_hal_1::i2c::I2c for I2c<'d, T, M> { } } -impl<'d, T: Instance> embedded_hal_async::i2c::I2c for I2c<'d, T, Async> { +impl<'d> embedded_hal_async::i2c::I2c for I2c<'d, Async> { async fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> { self.read(address, read).await } diff --git a/embassy-stm32/src/i2c/v1.rs b/embassy-stm32/src/i2c/v1.rs index ac4fa1b9e..0269e53aa 100644 --- a/embassy-stm32/src/i2c/v1.rs +++ b/embassy-stm32/src/i2c/v1.rs @@ -28,7 +28,7 @@ use crate::pac::i2c; // There's some more details there, and we might have a fix for you. But please let us know if you // hit a case like this! pub unsafe fn on_interrupt() { - let regs = T::regs(); + let regs = T::info().regs; // i2c v2 only woke the task on transfer complete interrupts. v1 uses interrupts for a bunch of // other stuff, so we wake the task on every interrupt. T::state().waker.wake(); @@ -41,9 +41,9 @@ pub unsafe fn on_interrupt() { }); } -impl<'d, T: Instance, M: PeriMode> I2c<'d, T, M> { +impl<'d, M: PeriMode> I2c<'d, M> { pub(crate) fn init(&mut self, freq: Hertz, _config: Config) { - T::regs().cr1().modify(|reg| { + self.info.regs.cr1().modify(|reg| { reg.set_pe(false); //reg.set_anfoff(false); }); @@ -67,39 +67,39 @@ impl<'d, T: Instance, M: PeriMode> I2c<'d, T, M> { // // This presents as an ~infinite hang on read or write, as the START condition // is never generated, meaning the start event is never generated. - T::regs().cr1().modify(|reg| { + self.info.regs.cr1().modify(|reg| { reg.set_swrst(true); }); - T::regs().cr1().modify(|reg| { + self.info.regs.cr1().modify(|reg| { reg.set_swrst(false); }); - let timings = Timings::new(T::frequency(), freq); + let timings = Timings::new(self.kernel_clock, freq); - T::regs().cr2().modify(|reg| { + self.info.regs.cr2().modify(|reg| { reg.set_freq(timings.freq); }); - T::regs().ccr().modify(|reg| { + self.info.regs.ccr().modify(|reg| { reg.set_f_s(timings.mode.f_s()); reg.set_duty(timings.duty.duty()); reg.set_ccr(timings.ccr); }); - T::regs().trise().modify(|reg| { + self.info.regs.trise().modify(|reg| { reg.set_trise(timings.trise); }); - T::regs().cr1().modify(|reg| { + self.info.regs.cr1().modify(|reg| { reg.set_pe(true); }); } - fn check_and_clear_error_flags() -> Result { + fn check_and_clear_error_flags(info: &'static Info) -> Result { // Note that flags should only be cleared once they have been registered. If flags are // cleared otherwise, there may be an inherent race condition and flags may be missed. - let sr1 = T::regs().sr1().read(); + let sr1 = info.regs.sr1().read(); if sr1.timeout() { - T::regs().sr1().write(|reg| { + info.regs.sr1().write(|reg| { reg.0 = !0; reg.set_timeout(false); }); @@ -107,7 +107,7 @@ impl<'d, T: Instance, M: PeriMode> I2c<'d, T, M> { } if sr1.pecerr() { - T::regs().sr1().write(|reg| { + info.regs.sr1().write(|reg| { reg.0 = !0; reg.set_pecerr(false); }); @@ -115,7 +115,7 @@ impl<'d, T: Instance, M: PeriMode> I2c<'d, T, M> { } if sr1.ovr() { - T::regs().sr1().write(|reg| { + info.regs.sr1().write(|reg| { reg.0 = !0; reg.set_ovr(false); }); @@ -123,7 +123,7 @@ impl<'d, T: Instance, M: PeriMode> I2c<'d, T, M> { } if sr1.af() { - T::regs().sr1().write(|reg| { + info.regs.sr1().write(|reg| { reg.0 = !0; reg.set_af(false); }); @@ -131,7 +131,7 @@ impl<'d, T: Instance, M: PeriMode> I2c<'d, T, M> { } if sr1.arlo() { - T::regs().sr1().write(|reg| { + info.regs.sr1().write(|reg| { reg.0 = !0; reg.set_arlo(false); }); @@ -141,7 +141,7 @@ impl<'d, T: Instance, M: PeriMode> I2c<'d, T, M> { // The errata indicates that BERR may be incorrectly detected. It recommends ignoring and // clearing the BERR bit instead. if sr1.berr() { - T::regs().sr1().write(|reg| { + info.regs.sr1().write(|reg| { reg.0 = !0; reg.set_berr(false); }); @@ -154,32 +154,32 @@ impl<'d, T: Instance, M: PeriMode> I2c<'d, T, M> { if frame.send_start() { // Send a START condition - T::regs().cr1().modify(|reg| { + self.info.regs.cr1().modify(|reg| { reg.set_start(true); }); // Wait until START condition was generated - while !Self::check_and_clear_error_flags()?.start() { + while !Self::check_and_clear_error_flags(self.info)?.start() { timeout.check()?; } // Check if we were the ones to generate START - if T::regs().cr1().read().start() || !T::regs().sr2().read().msl() { + if self.info.regs.cr1().read().start() || !self.info.regs.sr2().read().msl() { return Err(Error::Arbitration); } // Set up current address we're trying to talk to - T::regs().dr().write(|reg| reg.set_dr(addr << 1)); + self.info.regs.dr().write(|reg| reg.set_dr(addr << 1)); // Wait until address was sent // Wait for the address to be acknowledged // Check for any I2C errors. If a NACK occurs, the ADDR bit will never be set. - while !Self::check_and_clear_error_flags()?.addr() { + while !Self::check_and_clear_error_flags(self.info)?.addr() { timeout.check()?; } // Clear condition by reading SR2 - let _ = T::regs().sr2().read(); + let _ = self.info.regs.sr2().read(); } // Send bytes @@ -189,7 +189,7 @@ impl<'d, T: Instance, M: PeriMode> I2c<'d, T, M> { if frame.send_stop() { // Send a STOP condition - T::regs().cr1().modify(|reg| reg.set_stop(true)); + self.info.regs.cr1().modify(|reg| reg.set_stop(true)); } // Fallthrough is success @@ -200,18 +200,18 @@ impl<'d, T: Instance, M: PeriMode> I2c<'d, T, M> { // Wait until we're ready for sending while { // Check for any I2C errors. If a NACK occurs, the ADDR bit will never be set. - !Self::check_and_clear_error_flags()?.txe() + !Self::check_and_clear_error_flags(self.info)?.txe() } { timeout.check()?; } // Push out a byte of data - T::regs().dr().write(|reg| reg.set_dr(byte)); + self.info.regs.dr().write(|reg| reg.set_dr(byte)); // Wait until byte is transferred while { // Check for any potential error conditions. - !Self::check_and_clear_error_flags()?.btf() + !Self::check_and_clear_error_flags(self.info)?.btf() } { timeout.check()?; } @@ -222,14 +222,14 @@ impl<'d, T: Instance, M: PeriMode> I2c<'d, T, M> { fn recv_byte(&self, timeout: Timeout) -> Result { while { // Check for any potential error conditions. - Self::check_and_clear_error_flags()?; + Self::check_and_clear_error_flags(self.info)?; - !T::regs().sr1().read().rxne() + !self.info.regs.sr1().read().rxne() } { timeout.check()?; } - let value = T::regs().dr().read().dr(); + let value = self.info.regs.dr().read().dr(); Ok(value) } @@ -246,32 +246,32 @@ impl<'d, T: Instance, M: PeriMode> I2c<'d, T, M> { if frame.send_start() { // Send a START condition and set ACK bit - T::regs().cr1().modify(|reg| { + self.info.regs.cr1().modify(|reg| { reg.set_start(true); reg.set_ack(true); }); // Wait until START condition was generated - while !Self::check_and_clear_error_flags()?.start() { + while !Self::check_and_clear_error_flags(self.info)?.start() { timeout.check()?; } // Check if we were the ones to generate START - if T::regs().cr1().read().start() || !T::regs().sr2().read().msl() { + if self.info.regs.cr1().read().start() || !self.info.regs.sr2().read().msl() { return Err(Error::Arbitration); } // Set up current address we're trying to talk to - T::regs().dr().write(|reg| reg.set_dr((addr << 1) + 1)); + self.info.regs.dr().write(|reg| reg.set_dr((addr << 1) + 1)); // Wait until address was sent // Wait for the address to be acknowledged - while !Self::check_and_clear_error_flags()?.addr() { + while !Self::check_and_clear_error_flags(self.info)?.addr() { timeout.check()?; } // Clear condition by reading SR2 - let _ = T::regs().sr2().read(); + let _ = self.info.regs.sr2().read(); } // Receive bytes into buffer @@ -280,7 +280,7 @@ impl<'d, T: Instance, M: PeriMode> I2c<'d, T, M> { } // Prepare to send NACK then STOP after next byte - T::regs().cr1().modify(|reg| { + self.info.regs.cr1().modify(|reg| { if frame.send_nack() { reg.set_ack(false); } @@ -346,17 +346,17 @@ impl<'d, T: Instance, M: PeriMode> I2c<'d, T, M> { // Async #[inline] // pretty sure this should always be inlined - fn enable_interrupts() -> () { - T::regs().cr2().modify(|w| { + fn enable_interrupts(info: &'static Info) -> () { + info.regs.cr2().modify(|w| { w.set_iterren(true); w.set_itevten(true); }); } } -impl<'d, T: Instance> I2c<'d, T, Async> { +impl<'d> I2c<'d, Async> { async fn write_frame(&mut self, address: u8, write: &[u8], frame: FrameOptions) -> Result<(), Error> { - T::regs().cr2().modify(|w| { + self.info.regs.cr2().modify(|w| { // Note: Do not enable the ITBUFEN bit in the I2C_CR2 register if DMA is used for // reception. w.set_itbufen(false); @@ -370,33 +370,31 @@ impl<'d, T: Instance> I2c<'d, T, Async> { // Sentinel to disable transfer when an error occurs or future is canceled. // TODO: Generate STOP condition on cancel? let on_drop = OnDrop::new(|| { - T::regs().cr2().modify(|w| { + self.info.regs.cr2().modify(|w| { w.set_dmaen(false); w.set_iterren(false); w.set_itevten(false); }) }); - let state = T::state(); - if frame.send_start() { // Send a START condition - T::regs().cr1().modify(|reg| { + self.info.regs.cr1().modify(|reg| { reg.set_start(true); }); // Wait until START condition was generated poll_fn(|cx| { - state.waker.register(cx.waker()); + self.state.waker.register(cx.waker()); - match Self::check_and_clear_error_flags() { + match Self::check_and_clear_error_flags(self.info) { Err(e) => Poll::Ready(Err(e)), Ok(sr1) => { if sr1.start() { Poll::Ready(Ok(())) } else { // When pending, (re-)enable interrupts to wake us up. - Self::enable_interrupts(); + Self::enable_interrupts(self.info); Poll::Pending } } @@ -405,25 +403,25 @@ impl<'d, T: Instance> I2c<'d, T, Async> { .await?; // Check if we were the ones to generate START - if T::regs().cr1().read().start() || !T::regs().sr2().read().msl() { + if self.info.regs.cr1().read().start() || !self.info.regs.sr2().read().msl() { return Err(Error::Arbitration); } // Set up current address we're trying to talk to - T::regs().dr().write(|reg| reg.set_dr(address << 1)); + self.info.regs.dr().write(|reg| reg.set_dr(address << 1)); // Wait for the address to be acknowledged poll_fn(|cx| { - state.waker.register(cx.waker()); + self.state.waker.register(cx.waker()); - match Self::check_and_clear_error_flags() { + match Self::check_and_clear_error_flags(self.info) { Err(e) => Poll::Ready(Err(e)), Ok(sr1) => { if sr1.addr() { Poll::Ready(Ok(())) } else { // When pending, (re-)enable interrupts to wake us up. - Self::enable_interrupts(); + Self::enable_interrupts(self.info); Poll::Pending } } @@ -432,26 +430,26 @@ impl<'d, T: Instance> I2c<'d, T, Async> { .await?; // Clear condition by reading SR2 - T::regs().sr2().read(); + self.info.regs.sr2().read(); } let dma_transfer = unsafe { // Set the I2C_DR register address in the DMA_SxPAR register. The data will be moved to // this address from the memory after each TxE event. - let dst = T::regs().dr().as_ptr() as *mut u8; + let dst = self.info.regs.dr().as_ptr() as *mut u8; self.tx_dma.as_mut().unwrap().write(write, dst, Default::default()) }; // Wait for bytes to be sent, or an error to occur. let poll_error = poll_fn(|cx| { - state.waker.register(cx.waker()); + self.state.waker.register(cx.waker()); - match Self::check_and_clear_error_flags() { + match Self::check_and_clear_error_flags(self.info) { Err(e) => Poll::Ready(Err::<(), Error>(e)), Ok(_) => { // When pending, (re-)enable interrupts to wake us up. - Self::enable_interrupts(); + Self::enable_interrupts(self.info); Poll::Pending } } @@ -463,7 +461,7 @@ impl<'d, T: Instance> I2c<'d, T, Async> { _ => Ok(()), }?; - T::regs().cr2().modify(|w| { + self.info.regs.cr2().modify(|w| { w.set_dmaen(false); }); @@ -473,16 +471,16 @@ impl<'d, T: Instance> I2c<'d, T, Async> { // 18.3.8 “Master transmitter: In the interrupt routine after the EOT interrupt, disable DMA // requests then wait for a BTF event before programming the Stop condition.” poll_fn(|cx| { - state.waker.register(cx.waker()); + self.state.waker.register(cx.waker()); - match Self::check_and_clear_error_flags() { + match Self::check_and_clear_error_flags(self.info) { Err(e) => Poll::Ready(Err(e)), Ok(sr1) => { if sr1.btf() { Poll::Ready(Ok(())) } else { // When pending, (re-)enable interrupts to wake us up. - Self::enable_interrupts(); + Self::enable_interrupts(self.info); Poll::Pending } } @@ -490,7 +488,7 @@ impl<'d, T: Instance> I2c<'d, T, Async> { }) .await?; - T::regs().cr1().modify(|w| { + self.info.regs.cr1().modify(|w| { w.set_stop(true); }); } @@ -525,7 +523,7 @@ impl<'d, T: Instance> I2c<'d, T, Async> { // Some branches below depend on whether the buffer contains only a single byte. let single_byte = buffer.len() == 1; - T::regs().cr2().modify(|w| { + self.info.regs.cr2().modify(|w| { // Note: Do not enable the ITBUFEN bit in the I2C_CR2 register if DMA is used for // reception. w.set_itbufen(false); @@ -541,34 +539,32 @@ impl<'d, T: Instance> I2c<'d, T, Async> { // Sentinel to disable transfer when an error occurs or future is canceled. // TODO: Generate STOP condition on cancel? let on_drop = OnDrop::new(|| { - T::regs().cr2().modify(|w| { + self.info.regs.cr2().modify(|w| { w.set_dmaen(false); w.set_iterren(false); w.set_itevten(false); }) }); - let state = T::state(); - if frame.send_start() { // Send a START condition and set ACK bit - T::regs().cr1().modify(|reg| { + self.info.regs.cr1().modify(|reg| { reg.set_start(true); reg.set_ack(true); }); // Wait until START condition was generated poll_fn(|cx| { - state.waker.register(cx.waker()); + self.state.waker.register(cx.waker()); - match Self::check_and_clear_error_flags() { + match Self::check_and_clear_error_flags(self.info) { Err(e) => Poll::Ready(Err(e)), Ok(sr1) => { if sr1.start() { Poll::Ready(Ok(())) } else { // When pending, (re-)enable interrupts to wake us up. - Self::enable_interrupts(); + Self::enable_interrupts(self.info); Poll::Pending } } @@ -577,25 +573,25 @@ impl<'d, T: Instance> I2c<'d, T, Async> { .await?; // Check if we were the ones to generate START - if T::regs().cr1().read().start() || !T::regs().sr2().read().msl() { + if self.info.regs.cr1().read().start() || !self.info.regs.sr2().read().msl() { return Err(Error::Arbitration); } // Set up current address we're trying to talk to - T::regs().dr().write(|reg| reg.set_dr((address << 1) + 1)); + self.info.regs.dr().write(|reg| reg.set_dr((address << 1) + 1)); // Wait for the address to be acknowledged poll_fn(|cx| { - state.waker.register(cx.waker()); + self.state.waker.register(cx.waker()); - match Self::check_and_clear_error_flags() { + match Self::check_and_clear_error_flags(self.info) { Err(e) => Poll::Ready(Err(e)), Ok(sr1) => { if sr1.addr() { Poll::Ready(Ok(())) } else { // When pending, (re-)enable interrupts to wake us up. - Self::enable_interrupts(); + Self::enable_interrupts(self.info); Poll::Pending } } @@ -606,18 +602,18 @@ impl<'d, T: Instance> I2c<'d, T, Async> { // 18.3.8: When a single byte must be received: the NACK must be programmed during EV6 // event, i.e. program ACK=0 when ADDR=1, before clearing ADDR flag. if frame.send_nack() && single_byte { - T::regs().cr1().modify(|w| { + self.info.regs.cr1().modify(|w| { w.set_ack(false); }); } // Clear condition by reading SR2 - T::regs().sr2().read(); + self.info.regs.sr2().read(); } else { // Before starting reception of single byte (but without START condition, i.e. in case // of continued frame), program NACK to emit at end of this byte. if frame.send_nack() && single_byte { - T::regs().cr1().modify(|w| { + self.info.regs.cr1().modify(|w| { w.set_ack(false); }); } @@ -627,7 +623,7 @@ impl<'d, T: Instance> I2c<'d, T, Async> { // condition either after clearing ADDR flag, or in the DMA Transfer Complete interrupt // routine. if frame.send_stop() && single_byte { - T::regs().cr1().modify(|w| { + self.info.regs.cr1().modify(|w| { w.set_stop(true); }); } @@ -635,20 +631,20 @@ impl<'d, T: Instance> I2c<'d, T, Async> { let dma_transfer = unsafe { // Set the I2C_DR register address in the DMA_SxPAR register. The data will be moved // from this address from the memory after each RxE event. - let src = T::regs().dr().as_ptr() as *mut u8; + let src = self.info.regs.dr().as_ptr() as *mut u8; self.rx_dma.as_mut().unwrap().read(src, buffer, Default::default()) }; // Wait for bytes to be received, or an error to occur. let poll_error = poll_fn(|cx| { - state.waker.register(cx.waker()); + self.state.waker.register(cx.waker()); - match Self::check_and_clear_error_flags() { + match Self::check_and_clear_error_flags(self.info) { Err(e) => Poll::Ready(Err::<(), Error>(e)), _ => { // When pending, (re-)enable interrupts to wake us up. - Self::enable_interrupts(); + Self::enable_interrupts(self.info); Poll::Pending } } @@ -659,12 +655,12 @@ impl<'d, T: Instance> I2c<'d, T, Async> { _ => Ok(()), }?; - T::regs().cr2().modify(|w| { + self.info.regs.cr2().modify(|w| { w.set_dmaen(false); }); if frame.send_stop() && !single_byte { - T::regs().cr1().modify(|w| { + self.info.regs.cr1().modify(|w| { w.set_stop(true); }); } @@ -704,9 +700,9 @@ impl<'d, T: Instance> I2c<'d, T, Async> { } } -impl<'d, T: Instance, M: PeriMode> Drop for I2c<'d, T, M> { +impl<'d, M: PeriMode> Drop for I2c<'d, M> { fn drop(&mut self) { - T::disable(); + self.info.enable_bit.disable() } } @@ -810,20 +806,20 @@ impl Timings { } } -impl<'d, T: Instance, M: PeriMode> SetConfig for I2c<'d, T, M> { +impl<'d, M: PeriMode> SetConfig for I2c<'d, M> { type Config = Hertz; type ConfigError = (); fn set_config(&mut self, config: &Self::Config) -> Result<(), ()> { - let timings = Timings::new(T::frequency(), *config); - T::regs().cr2().modify(|reg| { + let timings = Timings::new(self.kernel_clock, *config); + self.info.regs.cr2().modify(|reg| { reg.set_freq(timings.freq); }); - T::regs().ccr().modify(|reg| { + self.info.regs.ccr().modify(|reg| { reg.set_f_s(timings.mode.f_s()); reg.set_duty(timings.duty.duty()); reg.set_ccr(timings.ccr); }); - T::regs().trise().modify(|reg| { + self.info.regs.trise().modify(|reg| { reg.set_trise(timings.trise); }); diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index 12df98534..aa6daf786 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs @@ -10,7 +10,7 @@ use super::*; use crate::pac::i2c; pub(crate) unsafe fn on_interrupt() { - let regs = T::regs(); + let regs = T::info().regs; let isr = regs.isr().read(); if isr.tcr() || isr.tc() { @@ -23,16 +23,16 @@ pub(crate) unsafe fn on_interrupt() { }); } -impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { +impl<'d, M: Mode> I2c<'d, M> { pub(crate) fn init(&mut self, freq: Hertz, _config: Config) { - T::regs().cr1().modify(|reg| { + self.info.regs.cr1().modify(|reg| { reg.set_pe(false); reg.set_anfoff(false); }); - let timings = Timings::new(T::frequency(), freq.into()); + let timings = Timings::new(self.kernel_clock, freq.into()); - T::regs().timingr().write(|reg| { + self.info.regs.timingr().write(|reg| { reg.set_presc(timings.prescale); reg.set_scll(timings.scll); reg.set_sclh(timings.sclh); @@ -40,16 +40,17 @@ impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { reg.set_scldel(timings.scldel); }); - T::regs().cr1().modify(|reg| { + self.info.regs.cr1().modify(|reg| { reg.set_pe(true); }); } fn master_stop(&mut self) { - T::regs().cr2().write(|w| w.set_stop(true)); + self.info.regs.cr2().write(|w| w.set_stop(true)); } fn master_read( + info: &'static Info, address: u8, length: usize, stop: Stop, @@ -63,7 +64,7 @@ impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { // Wait for any previous address sequence to end // automatically. This could be up to 50% of a bus // cycle (ie. up to 0.5/freq) - while T::regs().cr2().read().start() { + while info.regs.cr2().read().start() { timeout.check()?; } } @@ -78,7 +79,7 @@ impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { i2c::vals::Reload::COMPLETED }; - T::regs().cr2().modify(|w| { + info.regs.cr2().modify(|w| { w.set_sadd((address << 1 | 0) as u16); w.set_add10(i2c::vals::Addmode::BIT7); w.set_dir(i2c::vals::Dir::READ); @@ -91,13 +92,20 @@ impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { Ok(()) } - fn master_write(address: u8, length: usize, stop: Stop, reload: bool, timeout: Timeout) -> Result<(), Error> { + fn master_write( + info: &'static Info, + address: u8, + length: usize, + stop: Stop, + reload: bool, + timeout: Timeout, + ) -> Result<(), Error> { assert!(length < 256); // Wait for any previous address sequence to end // automatically. This could be up to 50% of a bus // cycle (ie. up to 0.5/freq) - while T::regs().cr2().read().start() { + while info.regs.cr2().read().start() { timeout.check()?; } @@ -110,7 +118,7 @@ impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { // Set START and prepare to send `bytes`. The // START bit can be set even if the bus is BUSY or // I2C is in slave mode. - T::regs().cr2().modify(|w| { + info.regs.cr2().modify(|w| { w.set_sadd((address << 1 | 0) as u16); w.set_add10(i2c::vals::Addmode::BIT7); w.set_dir(i2c::vals::Dir::WRITE); @@ -123,10 +131,10 @@ impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { Ok(()) } - fn master_continue(length: usize, reload: bool, timeout: Timeout) -> Result<(), Error> { + fn master_continue(info: &'static Info, length: usize, reload: bool, timeout: Timeout) -> Result<(), Error> { assert!(length < 256 && length > 0); - while !T::regs().isr().read().tcr() { + while !info.regs.isr().read().tcr() { timeout.check()?; } @@ -136,7 +144,7 @@ impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { i2c::vals::Reload::COMPLETED }; - T::regs().cr2().modify(|w| { + info.regs.cr2().modify(|w| { w.set_nbytes(length as u8); w.set_reload(reload); }); @@ -145,27 +153,27 @@ impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { } fn flush_txdr(&self) { - if T::regs().isr().read().txis() { - T::regs().txdr().write(|w| w.set_txdata(0)); + if self.info.regs.isr().read().txis() { + self.info.regs.txdr().write(|w| w.set_txdata(0)); } - if !T::regs().isr().read().txe() { - T::regs().isr().modify(|w| w.set_txe(true)) + if !self.info.regs.isr().read().txe() { + self.info.regs.isr().modify(|w| w.set_txe(true)) } } fn wait_txe(&self, timeout: Timeout) -> Result<(), Error> { loop { - let isr = T::regs().isr().read(); + let isr = self.info.regs.isr().read(); if isr.txe() { return Ok(()); } else if isr.berr() { - T::regs().icr().write(|reg| reg.set_berrcf(true)); + self.info.regs.icr().write(|reg| reg.set_berrcf(true)); return Err(Error::Bus); } else if isr.arlo() { - T::regs().icr().write(|reg| reg.set_arlocf(true)); + self.info.regs.icr().write(|reg| reg.set_arlocf(true)); return Err(Error::Arbitration); } else if isr.nackf() { - T::regs().icr().write(|reg| reg.set_nackcf(true)); + self.info.regs.icr().write(|reg| reg.set_nackcf(true)); self.flush_txdr(); return Err(Error::Nack); } @@ -176,17 +184,17 @@ impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { fn wait_rxne(&self, timeout: Timeout) -> Result<(), Error> { loop { - let isr = T::regs().isr().read(); + let isr = self.info.regs.isr().read(); if isr.rxne() { return Ok(()); } else if isr.berr() { - T::regs().icr().write(|reg| reg.set_berrcf(true)); + self.info.regs.icr().write(|reg| reg.set_berrcf(true)); return Err(Error::Bus); } else if isr.arlo() { - T::regs().icr().write(|reg| reg.set_arlocf(true)); + self.info.regs.icr().write(|reg| reg.set_arlocf(true)); return Err(Error::Arbitration); } else if isr.nackf() { - T::regs().icr().write(|reg| reg.set_nackcf(true)); + self.info.regs.icr().write(|reg| reg.set_nackcf(true)); self.flush_txdr(); return Err(Error::Nack); } @@ -197,17 +205,17 @@ impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { fn wait_tc(&self, timeout: Timeout) -> Result<(), Error> { loop { - let isr = T::regs().isr().read(); + let isr = self.info.regs.isr().read(); if isr.tc() { return Ok(()); } else if isr.berr() { - T::regs().icr().write(|reg| reg.set_berrcf(true)); + self.info.regs.icr().write(|reg| reg.set_berrcf(true)); return Err(Error::Bus); } else if isr.arlo() { - T::regs().icr().write(|reg| reg.set_arlocf(true)); + self.info.regs.icr().write(|reg| reg.set_arlocf(true)); return Err(Error::Arbitration); } else if isr.nackf() { - T::regs().icr().write(|reg| reg.set_nackcf(true)); + self.info.regs.icr().write(|reg| reg.set_nackcf(true)); self.flush_txdr(); return Err(Error::Nack); } @@ -226,6 +234,7 @@ impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { let last_chunk_idx = total_chunks.saturating_sub(1); Self::master_read( + self.info, address, read.len().min(255), Stop::Automatic, @@ -236,14 +245,14 @@ impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { for (number, chunk) in read.chunks_mut(255).enumerate() { if number != 0 { - Self::master_continue(chunk.len(), number != last_chunk_idx, timeout)?; + Self::master_continue(self.info, chunk.len(), number != last_chunk_idx, timeout)?; } for byte in chunk { // Wait until we have received something self.wait_rxne(timeout)?; - *byte = T::regs().rxdr().read().rxdata(); + *byte = self.info.regs.rxdr().read().rxdata(); } } Ok(()) @@ -262,6 +271,7 @@ impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { // // ST SAD+W if let Err(err) = Self::master_write( + self.info, address, write.len().min(255), Stop::Software, @@ -276,7 +286,7 @@ impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { for (number, chunk) in write.chunks(255).enumerate() { if number != 0 { - Self::master_continue(chunk.len(), number != last_chunk_idx, timeout)?; + Self::master_continue(self.info, chunk.len(), number != last_chunk_idx, timeout)?; } for byte in chunk { @@ -290,7 +300,7 @@ impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { return Err(err); } - T::regs().txdr().write(|w| w.set_txdata(*byte)); + self.info.regs.txdr().write(|w| w.set_txdata(*byte)); } } // Wait until the write finishes @@ -348,6 +358,7 @@ impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { let last_slice_index = write.len() - 1; if let Err(err) = Self::master_write( + self.info, address, first_length.min(255), Stop::Software, @@ -370,6 +381,7 @@ impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { if idx != 0 { if let Err(err) = Self::master_continue( + self.info, slice_len.min(255), (idx != last_slice_index) || (slice_len > 255), timeout, @@ -382,6 +394,7 @@ impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { for (number, chunk) in slice.chunks(255).enumerate() { if number != 0 { if let Err(err) = Self::master_continue( + self.info, chunk.len(), (number != last_chunk_idx) || (idx != last_slice_index), timeout, @@ -402,7 +415,7 @@ impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { // Put byte on the wire //self.i2c.txdr.write(|w| w.txdata().bits(*byte)); - T::regs().txdr().write(|w| w.set_txdata(*byte)); + self.info.regs.txdr().write(|w| w.set_txdata(*byte)); } } } @@ -413,7 +426,7 @@ impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { } } -impl<'d, T: Instance> I2c<'d, T, Async> { +impl<'d> I2c<'d, Async> { async fn write_dma_internal( &mut self, address: u8, @@ -425,7 +438,7 @@ impl<'d, T: Instance> I2c<'d, T, Async> { let total_len = write.len(); let dma_transfer = unsafe { - let regs = T::regs(); + let regs = self.info.regs; regs.cr1().modify(|w| { w.set_txdmaen(true); if first_slice { @@ -437,11 +450,10 @@ impl<'d, T: Instance> I2c<'d, T, Async> { self.tx_dma.as_mut().unwrap().write(write, dst, Default::default()) }; - let state = T::state(); let mut remaining_len = total_len; let on_drop = OnDrop::new(|| { - let regs = T::regs(); + let regs = self.info.regs; regs.cr1().modify(|w| { if last_slice { w.set_txdmaen(false); @@ -451,12 +463,13 @@ impl<'d, T: Instance> I2c<'d, T, Async> { }); poll_fn(|cx| { - state.waker.register(cx.waker()); + self.state.waker.register(cx.waker()); - let isr = T::regs().isr().read(); + let isr = self.info.regs.isr().read(); if remaining_len == total_len { if first_slice { Self::master_write( + self.info, address, total_len.min(255), Stop::Software, @@ -464,8 +477,8 @@ impl<'d, T: Instance> I2c<'d, T, Async> { timeout, )?; } else { - Self::master_continue(total_len.min(255), (total_len > 255) || !last_slice, timeout)?; - T::regs().cr1().modify(|w| w.set_tcie(true)); + Self::master_continue(self.info, total_len.min(255), (total_len > 255) || !last_slice, timeout)?; + self.info.regs.cr1().modify(|w| w.set_tcie(true)); } } else if !(isr.tcr() || isr.tc()) { // poll_fn was woken without an interrupt present @@ -475,10 +488,10 @@ impl<'d, T: Instance> I2c<'d, T, Async> { } else { let last_piece = (remaining_len <= 255) && last_slice; - if let Err(e) = Self::master_continue(remaining_len.min(255), !last_piece, timeout) { + if let Err(e) = Self::master_continue(self.info, remaining_len.min(255), !last_piece, timeout) { return Poll::Ready(Err(e)); } - T::regs().cr1().modify(|w| w.set_tcie(true)); + self.info.regs.cr1().modify(|w| w.set_tcie(true)); } remaining_len = remaining_len.saturating_sub(255); @@ -509,7 +522,7 @@ impl<'d, T: Instance> I2c<'d, T, Async> { let total_len = buffer.len(); let dma_transfer = unsafe { - let regs = T::regs(); + let regs = self.info.regs; regs.cr1().modify(|w| { w.set_rxdmaen(true); w.set_tcie(true); @@ -519,11 +532,10 @@ impl<'d, T: Instance> I2c<'d, T, Async> { self.rx_dma.as_mut().unwrap().read(src, buffer, Default::default()) }; - let state = T::state(); let mut remaining_len = total_len; let on_drop = OnDrop::new(|| { - let regs = T::regs(); + let regs = self.info.regs; regs.cr1().modify(|w| { w.set_rxdmaen(false); w.set_tcie(false); @@ -531,11 +543,12 @@ impl<'d, T: Instance> I2c<'d, T, Async> { }); poll_fn(|cx| { - state.waker.register(cx.waker()); + self.state.waker.register(cx.waker()); - let isr = T::regs().isr().read(); + let isr = self.info.regs.isr().read(); if remaining_len == total_len { Self::master_read( + self.info, address, total_len.min(255), Stop::Software, @@ -551,10 +564,10 @@ impl<'d, T: Instance> I2c<'d, T, Async> { } else { let last_piece = remaining_len <= 255; - if let Err(e) = Self::master_continue(remaining_len.min(255), !last_piece, timeout) { + if let Err(e) = Self::master_continue(self.info, remaining_len.min(255), !last_piece, timeout) { return Poll::Ready(Err(e)); } - T::regs().cr1().modify(|w| w.set_tcie(true)); + self.info.regs.cr1().modify(|w| w.set_tcie(true)); } remaining_len = remaining_len.saturating_sub(255); @@ -658,9 +671,9 @@ impl<'d, T: Instance> I2c<'d, T, Async> { } } -impl<'d, T: Instance, M: Mode> Drop for I2c<'d, T, M> { +impl<'d, M: Mode> Drop for I2c<'d, M> { fn drop(&mut self) { - T::disable(); + self.info.enable_bit.disable(); } } @@ -788,12 +801,12 @@ impl Timings { } } -impl<'d, T: Instance, M: Mode> SetConfig for I2c<'d, T, M> { +impl<'d, M: Mode> SetConfig for I2c<'d, M> { type Config = Hertz; type ConfigError = (); fn set_config(&mut self, config: &Self::Config) -> Result<(), ()> { - let timings = Timings::new(T::frequency(), *config); - T::regs().timingr().write(|reg| { + let timings = Timings::new(self.kernel_clock, *config); + self.info.regs.timingr().write(|reg| { reg.set_presc(timings.prescale); reg.set_scll(timings.scll); reg.set_sclh(timings.sclh); diff --git a/embassy-stm32/src/i2s.rs b/embassy-stm32/src/i2s.rs index 9b80dc1d0..c78810a38 100644 --- a/embassy-stm32/src/i2s.rs +++ b/embassy-stm32/src/i2s.rs @@ -153,17 +153,17 @@ impl Default for Config { } /// I2S driver. -pub struct I2S<'d, T: Instance> { - _peri: Spi<'d, T, Async>, +pub struct I2S<'d> { + _peri: Spi<'d, Async>, sd: Option>, ws: Option>, ck: Option>, mck: Option>, } -impl<'d, T: Instance> I2S<'d, T> { +impl<'d> I2S<'d> { /// Note: Full-Duplex modes are not supported at this time - pub fn new( + pub fn new( peri: impl Peripheral

+ 'd, sd: impl Peripheral

> + 'd, ws: impl Peripheral

> + 'd, @@ -208,7 +208,7 @@ impl<'d, T: Instance> I2S<'d, T> { // rate to reach the proper audio sample frequency. The ODD bit in the SPI_I2SPR // register also has to be defined. - T::REGS.i2spr().modify(|w| { + spi.info.regs.i2spr().modify(|w| { w.set_i2sdiv(div); w.set_odd(match odd { true => Odd::ODD, @@ -235,7 +235,7 @@ impl<'d, T: Instance> I2S<'d, T> { // 5. The I2SE bit in SPI_I2SCFGR register must be set. - T::REGS.i2scfgr().modify(|w| { + spi.info.regs.i2scfgr().modify(|w| { w.set_ckpol(config.clock_polarity.ckpol()); w.set_i2smod(true); @@ -276,7 +276,7 @@ impl<'d, T: Instance> I2S<'d, T> { } } -impl<'d, T: Instance> Drop for I2S<'d, T> { +impl<'d> Drop for I2S<'d> { fn drop(&mut self) { self.sd.as_ref().map(|x| x.set_as_disconnected()); self.ws.as_ref().map(|x| x.set_as_disconnected()); diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 1f4e9ab1e..81ee60c1c 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -66,6 +66,8 @@ pub mod cryp; pub mod dac; #[cfg(dcmi)] pub mod dcmi; +#[cfg(dsihost)] +pub mod dsihost; #[cfg(eth)] pub mod eth; #[cfg(feature = "exti")] @@ -77,6 +79,8 @@ pub mod fmc; pub mod hash; #[cfg(hrtim)] pub mod hrtim; +#[cfg(hsem)] +pub mod hsem; #[cfg(i2c)] pub mod i2c; #[cfg(all(spi_v1, rcc_f4))] @@ -85,6 +89,8 @@ pub mod i2s; pub mod ipcc; #[cfg(feature = "low-power")] pub mod low_power; +#[cfg(ltdc)] +pub mod ltdc; #[cfg(opamp)] pub mod opamp; #[cfg(octospi)] @@ -101,6 +107,8 @@ pub mod sai; pub mod sdmmc; #[cfg(spi)] pub mod spi; +#[cfg(tsc)] +pub mod tsc; #[cfg(ucpd)] pub mod ucpd; #[cfg(uid)] diff --git a/embassy-stm32/src/ltdc.rs b/embassy-stm32/src/ltdc.rs new file mode 100644 index 000000000..0cc8a0557 --- /dev/null +++ b/embassy-stm32/src/ltdc.rs @@ -0,0 +1,142 @@ +//! LTDC +use core::marker::PhantomData; + +use crate::rcc::RccPeripheral; +use crate::{peripherals, Peripheral}; + +/// LTDC driver. +pub struct Ltdc<'d, T: Instance> { + _peri: PhantomData<&'d mut T>, +} + +impl<'d, T: Instance> Ltdc<'d, T> { + /// Note: Full-Duplex modes are not supported at this time + pub fn new( + _peri: impl Peripheral

+ 'd, + /* + clk: impl Peripheral

> + 'd, + hsync: impl Peripheral

> + 'd, + vsync: impl Peripheral

> + 'd, + b0: impl Peripheral

> + 'd, + b1: impl Peripheral

> + 'd, + b2: impl Peripheral

> + 'd, + b3: impl Peripheral

> + 'd, + b4: impl Peripheral

> + 'd, + b5: impl Peripheral

> + 'd, + b6: impl Peripheral

> + 'd, + b7: impl Peripheral

> + 'd, + g0: impl Peripheral

> + 'd, + g1: impl Peripheral

> + 'd, + g2: impl Peripheral

> + 'd, + g3: impl Peripheral

> + 'd, + g4: impl Peripheral

> + 'd, + g5: impl Peripheral

> + 'd, + g6: impl Peripheral

> + 'd, + g7: impl Peripheral

> + 'd, + r0: impl Peripheral

> + 'd, + r1: impl Peripheral

> + 'd, + r2: impl Peripheral

> + 'd, + r3: impl Peripheral

> + 'd, + r4: impl Peripheral

> + 'd, + r5: impl Peripheral

> + 'd, + r6: impl Peripheral

> + 'd, + r7: impl Peripheral

> + 'd, + */ + ) -> Self { + //into_ref!(clk); + + critical_section::with(|_cs| { + // RM says the pllsaidivr should only be changed when pllsai is off. But this could have other unintended side effects. So let's just give it a try like this. + // According to the debugger, this bit gets set, anyway. + #[cfg(stm32f7)] + stm32_metapac::RCC + .dckcfgr1() + .modify(|w| w.set_pllsaidivr(stm32_metapac::rcc::vals::Pllsaidivr::DIV2)); + + // It is set to RCC_PLLSAIDIVR_2 in ST's BSP example for the STM32469I-DISCO. + #[cfg(not(any(stm32f7, stm32u5)))] + stm32_metapac::RCC + .dckcfgr() + .modify(|w| w.set_pllsaidivr(stm32_metapac::rcc::vals::Pllsaidivr::DIV2)); + }); + + T::enable_and_reset(); + + //new_pin!(clk, AFType::OutputPushPull, Speed::VeryHigh, Pull::None); + + // Set Tearing Enable pin according to CubeMx example + //te.set_as_af_pull(te.af_num(), AFType::OutputPushPull, Pull::None); + //te.set_speed(Speed::Low); + /* + T::regs().wcr().modify(|w| { + w.set_dsien(true); + }); + */ + Self { _peri: PhantomData } + } + + /// Set the enable bit in the control register and assert that it has been enabled + pub fn enable(&mut self) { + T::regs().gcr().modify(|w| w.set_ltdcen(true)); + assert!(T::regs().gcr().read().ltdcen()) + } + + /// Unset the enable bit in the control register and assert that it has been disabled + pub fn disable(&mut self) { + T::regs().gcr().modify(|w| w.set_ltdcen(false)); + assert!(!T::regs().gcr().read().ltdcen()) + } +} + +impl<'d, T: Instance> Drop for Ltdc<'d, T> { + fn drop(&mut self) {} +} + +trait SealedInstance: crate::rcc::SealedRccPeripheral { + fn regs() -> &'static crate::pac::ltdc::Ltdc; +} + +/// DSI instance trait. +#[allow(private_bounds)] +pub trait Instance: SealedInstance + RccPeripheral + 'static {} + +pin_trait!(ClkPin, Instance); +pin_trait!(HsyncPin, Instance); +pin_trait!(VsyncPin, Instance); +pin_trait!(DePin, Instance); +pin_trait!(R0Pin, Instance); +pin_trait!(R1Pin, Instance); +pin_trait!(R2Pin, Instance); +pin_trait!(R3Pin, Instance); +pin_trait!(R4Pin, Instance); +pin_trait!(R5Pin, Instance); +pin_trait!(R6Pin, Instance); +pin_trait!(R7Pin, Instance); +pin_trait!(G0Pin, Instance); +pin_trait!(G1Pin, Instance); +pin_trait!(G2Pin, Instance); +pin_trait!(G3Pin, Instance); +pin_trait!(G4Pin, Instance); +pin_trait!(G5Pin, Instance); +pin_trait!(G6Pin, Instance); +pin_trait!(G7Pin, Instance); +pin_trait!(B0Pin, Instance); +pin_trait!(B1Pin, Instance); +pin_trait!(B2Pin, Instance); +pin_trait!(B3Pin, Instance); +pin_trait!(B4Pin, Instance); +pin_trait!(B5Pin, Instance); +pin_trait!(B6Pin, Instance); +pin_trait!(B7Pin, Instance); + +foreach_peripheral!( + (ltdc, $inst:ident) => { + impl crate::ltdc::SealedInstance for peripherals::$inst { + fn regs() -> &'static crate::pac::ltdc::Ltdc { + &crate::pac::$inst + } + } + + impl crate::ltdc::Instance for peripherals::$inst {} + }; +); diff --git a/embassy-stm32/src/macros.rs b/embassy-stm32/src/macros.rs index 02dce1266..7f8076043 100644 --- a/embassy-stm32/src/macros.rs +++ b/embassy-stm32/src/macros.rs @@ -1,5 +1,45 @@ #![macro_use] +macro_rules! peri_trait { + ( + $(irqs: [$($irq:ident),*],)? + ) => { + #[allow(private_interfaces)] + pub(crate) trait SealedInstance { + #[allow(unused)] + fn info() -> &'static Info; + #[allow(unused)] + fn state() -> &'static State; + } + + /// Peripheral instance trait. + #[allow(private_bounds)] + pub trait Instance: crate::Peripheral

+ SealedInstance + crate::rcc::RccPeripheral { + $($( + /// Interrupt for this peripheral. + type $irq: crate::interrupt::typelevel::Interrupt; + )*)? + } + }; +} + +macro_rules! peri_trait_impl { + ($instance:ident, $info:expr) => { + #[allow(private_interfaces)] + impl SealedInstance for crate::peripherals::$instance { + fn info() -> &'static Info { + static INFO: Info = $info; + &INFO + } + fn state() -> &'static State { + static STATE: State = State::new(); + &STATE + } + } + impl Instance for crate::peripherals::$instance {} + }; +} + macro_rules! pin_trait { ($signal:ident, $instance:path $(, $mode:path)?) => { #[doc = concat!(stringify!($signal), " pin trait")] diff --git a/embassy-stm32/src/opamp.rs b/embassy-stm32/src/opamp.rs index a3b4352c0..487902959 100644 --- a/embassy-stm32/src/opamp.rs +++ b/embassy-stm32/src/opamp.rs @@ -198,7 +198,7 @@ macro_rules! impl_opamp_external_output { ($inst:ident, $adc:ident, $ch:expr) => { foreach_adc!( ($adc, $common_inst:ident, $adc_clock:ident) => { - impl<'d> crate::adc::SealedAdcPin + impl<'d> crate::adc::SealedAdcChannel for OpAmpOutput<'d, crate::peripherals::$inst> { fn channel(&self) -> u8 { @@ -206,7 +206,7 @@ macro_rules! impl_opamp_external_output { } } - impl<'d> crate::adc::AdcPin + impl<'d> crate::adc::AdcChannel for OpAmpOutput<'d, crate::peripherals::$inst> { } @@ -244,7 +244,7 @@ macro_rules! impl_opamp_internal_output { ($inst:ident, $adc:ident, $ch:expr) => { foreach_adc!( ($adc, $common_inst:ident, $adc_clock:ident) => { - impl<'d> crate::adc::SealedAdcPin + impl<'d> crate::adc::SealedAdcChannel for OpAmpInternalOutput<'d, crate::peripherals::$inst> { fn channel(&self) -> u8 { @@ -252,7 +252,7 @@ macro_rules! impl_opamp_internal_output { } } - impl<'d> crate::adc::AdcPin + impl<'d> crate::adc::AdcChannel for OpAmpInternalOutput<'d, crate::peripherals::$inst> { } diff --git a/embassy-stm32/src/ospi/mod.rs b/embassy-stm32/src/ospi/mod.rs index c25ac4d24..536da4ca0 100644 --- a/embassy-stm32/src/ospi/mod.rs +++ b/embassy-stm32/src/ospi/mod.rs @@ -5,13 +5,16 @@ pub mod enums; +use core::marker::PhantomData; + use embassy_embedded_hal::{GetConfig, SetConfig}; use embassy_hal_internal::{into_ref, PeripheralRef}; pub use enums::*; use stm32_metapac::octospi::vals::{PhaseMode, SizeInBits}; -use crate::dma::{word, Transfer}; -use crate::gpio::{AFType, AnyPin, Pull, SealedPin as _}; +use crate::dma::{word, ChannelAndRequest}; +use crate::gpio::{AFType, AnyPin, Pull, SealedPin as _, Speed}; +use crate::mode::{Async, Blocking, Mode as PeriMode}; use crate::pac::octospi::{vals, Octospi as Regs}; use crate::rcc::RccPeripheral; use crate::{peripherals, Peripheral}; @@ -154,7 +157,7 @@ pub enum OspiError { } /// OSPI driver. -pub struct Ospi<'d, T: Instance, Dma> { +pub struct Ospi<'d, T: Instance, M: PeriMode> { _peri: PeripheralRef<'d, T>, sck: Option>, d0: Option>, @@ -167,259 +170,13 @@ pub struct Ospi<'d, T: Instance, Dma> { d7: Option>, nss: Option>, dqs: Option>, - dma: PeripheralRef<'d, Dma>, + dma: Option>, + _phantom: PhantomData, config: Config, width: OspiWidth, } -impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { - /// Create new OSPI driver for a single spi external chip - pub fn new_singlespi( - peri: impl Peripheral

+ 'd, - sck: impl Peripheral

> + 'd, - d0: impl Peripheral

> + 'd, - d1: impl Peripheral

> + 'd, - nss: impl Peripheral

> + 'd, - dma: impl Peripheral

+ 'd, - config: Config, - ) -> Self { - into_ref!(peri, sck, d0, d1, nss); - - sck.set_as_af_pull(sck.af_num(), AFType::OutputPushPull, Pull::None); - sck.set_speed(crate::gpio::Speed::VeryHigh); - nss.set_as_af_pull(nss.af_num(), AFType::OutputPushPull, Pull::Up); - nss.set_speed(crate::gpio::Speed::VeryHigh); - d0.set_as_af_pull(d0.af_num(), AFType::OutputPushPull, Pull::None); - d0.set_speed(crate::gpio::Speed::VeryHigh); - d1.set_as_af_pull(d1.af_num(), AFType::Input, Pull::None); - d1.set_speed(crate::gpio::Speed::VeryHigh); - - Self::new_inner( - peri, - Some(d0.map_into()), - Some(d1.map_into()), - None, - None, - None, - None, - None, - None, - Some(sck.map_into()), - Some(nss.map_into()), - None, - dma, - config, - OspiWidth::SING, - false, - ) - } - - /// Create new OSPI driver for a dualspi external chip - pub fn new_dualspi( - peri: impl Peripheral

+ 'd, - sck: impl Peripheral

> + 'd, - d0: impl Peripheral

> + 'd, - d1: impl Peripheral

> + 'd, - nss: impl Peripheral

> + 'd, - dma: impl Peripheral

+ 'd, - config: Config, - ) -> Self { - into_ref!(peri, sck, d0, d1, nss); - - sck.set_as_af_pull(sck.af_num(), AFType::OutputPushPull, Pull::None); - sck.set_speed(crate::gpio::Speed::VeryHigh); - nss.set_as_af_pull(nss.af_num(), AFType::OutputPushPull, Pull::Up); - nss.set_speed(crate::gpio::Speed::VeryHigh); - d0.set_as_af_pull(d0.af_num(), AFType::OutputPushPull, Pull::None); - d0.set_speed(crate::gpio::Speed::VeryHigh); - d1.set_as_af_pull(d1.af_num(), AFType::OutputPushPull, Pull::None); - d1.set_speed(crate::gpio::Speed::VeryHigh); - - Self::new_inner( - peri, - Some(d0.map_into()), - Some(d1.map_into()), - None, - None, - None, - None, - None, - None, - Some(sck.map_into()), - Some(nss.map_into()), - None, - dma, - config, - OspiWidth::DUAL, - false, - ) - } - - /// Create new OSPI driver for a quadspi external chip - pub fn new_quadspi( - peri: impl Peripheral

+ 'd, - sck: impl Peripheral

> + 'd, - d0: impl Peripheral

> + 'd, - d1: impl Peripheral

> + 'd, - d2: impl Peripheral

> + 'd, - d3: impl Peripheral

> + 'd, - nss: impl Peripheral

> + 'd, - dma: impl Peripheral

+ 'd, - config: Config, - ) -> Self { - into_ref!(peri, sck, d0, d1, d2, d3, nss); - - sck.set_as_af_pull(sck.af_num(), AFType::OutputPushPull, Pull::None); - sck.set_speed(crate::gpio::Speed::VeryHigh); - nss.set_as_af_pull(nss.af_num(), AFType::OutputPushPull, Pull::Up); - nss.set_speed(crate::gpio::Speed::VeryHigh); - d0.set_as_af_pull(d0.af_num(), AFType::OutputPushPull, Pull::None); - d0.set_speed(crate::gpio::Speed::VeryHigh); - d1.set_as_af_pull(d1.af_num(), AFType::OutputPushPull, Pull::None); - d1.set_speed(crate::gpio::Speed::VeryHigh); - d2.set_as_af_pull(d2.af_num(), AFType::OutputPushPull, Pull::None); - d2.set_speed(crate::gpio::Speed::VeryHigh); - d3.set_as_af_pull(d3.af_num(), AFType::OutputPushPull, Pull::None); - d3.set_speed(crate::gpio::Speed::VeryHigh); - - Self::new_inner( - peri, - Some(d0.map_into()), - Some(d1.map_into()), - Some(d2.map_into()), - Some(d3.map_into()), - None, - None, - None, - None, - Some(sck.map_into()), - Some(nss.map_into()), - None, - dma, - config, - OspiWidth::QUAD, - false, - ) - } - - /// Create new OSPI driver for two quadspi external chips - pub fn new_dualquadspi( - peri: impl Peripheral

+ 'd, - sck: impl Peripheral

> + 'd, - d0: impl Peripheral

> + 'd, - d1: impl Peripheral

> + 'd, - d2: impl Peripheral

> + 'd, - d3: impl Peripheral

> + 'd, - d4: impl Peripheral

> + 'd, - d5: impl Peripheral

> + 'd, - d6: impl Peripheral

> + 'd, - d7: impl Peripheral

> + 'd, - nss: impl Peripheral

> + 'd, - dma: impl Peripheral

+ 'd, - config: Config, - ) -> Self { - into_ref!(peri, sck, d0, d1, d2, d3, d4, d5, d6, d7, nss); - - sck.set_as_af_pull(sck.af_num(), AFType::OutputPushPull, Pull::None); - sck.set_speed(crate::gpio::Speed::VeryHigh); - nss.set_as_af_pull(nss.af_num(), AFType::OutputPushPull, Pull::Up); - nss.set_speed(crate::gpio::Speed::VeryHigh); - d0.set_as_af_pull(d0.af_num(), AFType::OutputPushPull, Pull::None); - d0.set_speed(crate::gpio::Speed::VeryHigh); - d1.set_as_af_pull(d1.af_num(), AFType::OutputPushPull, Pull::None); - d1.set_speed(crate::gpio::Speed::VeryHigh); - d2.set_as_af_pull(d2.af_num(), AFType::OutputPushPull, Pull::None); - d2.set_speed(crate::gpio::Speed::VeryHigh); - d3.set_as_af_pull(d3.af_num(), AFType::OutputPushPull, Pull::None); - d3.set_speed(crate::gpio::Speed::VeryHigh); - d4.set_as_af_pull(d4.af_num(), AFType::OutputPushPull, Pull::None); - d4.set_speed(crate::gpio::Speed::VeryHigh); - d5.set_as_af_pull(d5.af_num(), AFType::OutputPushPull, Pull::None); - d5.set_speed(crate::gpio::Speed::VeryHigh); - d6.set_as_af_pull(d6.af_num(), AFType::OutputPushPull, Pull::None); - d6.set_speed(crate::gpio::Speed::VeryHigh); - d7.set_as_af_pull(d7.af_num(), AFType::OutputPushPull, Pull::None); - d7.set_speed(crate::gpio::Speed::VeryHigh); - - Self::new_inner( - peri, - Some(d0.map_into()), - Some(d1.map_into()), - Some(d2.map_into()), - Some(d3.map_into()), - Some(d4.map_into()), - Some(d5.map_into()), - Some(d6.map_into()), - Some(d7.map_into()), - Some(sck.map_into()), - Some(nss.map_into()), - None, - dma, - config, - OspiWidth::QUAD, - true, - ) - } - - /// Create new OSPI driver for octospi external chips - pub fn new_octospi( - peri: impl Peripheral

+ 'd, - sck: impl Peripheral

> + 'd, - d0: impl Peripheral

> + 'd, - d1: impl Peripheral

> + 'd, - d2: impl Peripheral

> + 'd, - d3: impl Peripheral

> + 'd, - d4: impl Peripheral

> + 'd, - d5: impl Peripheral

> + 'd, - d6: impl Peripheral

> + 'd, - d7: impl Peripheral

> + 'd, - nss: impl Peripheral

> + 'd, - dma: impl Peripheral

+ 'd, - config: Config, - ) -> Self { - into_ref!(peri, sck, d0, d1, d2, d3, d4, d5, d6, d7, nss); - - sck.set_as_af_pull(sck.af_num(), AFType::OutputPushPull, Pull::None); - sck.set_speed(crate::gpio::Speed::VeryHigh); - nss.set_as_af_pull(nss.af_num(), AFType::OutputPushPull, Pull::Up); - nss.set_speed(crate::gpio::Speed::VeryHigh); - d0.set_as_af_pull(d0.af_num(), AFType::OutputPushPull, Pull::None); - d0.set_speed(crate::gpio::Speed::VeryHigh); - d1.set_as_af_pull(d1.af_num(), AFType::OutputPushPull, Pull::None); - d1.set_speed(crate::gpio::Speed::VeryHigh); - d2.set_as_af_pull(d2.af_num(), AFType::OutputPushPull, Pull::None); - d2.set_speed(crate::gpio::Speed::VeryHigh); - d3.set_as_af_pull(d3.af_num(), AFType::OutputPushPull, Pull::None); - d3.set_speed(crate::gpio::Speed::VeryHigh); - d4.set_as_af_pull(d4.af_num(), AFType::OutputPushPull, Pull::None); - d4.set_speed(crate::gpio::Speed::VeryHigh); - d5.set_as_af_pull(d5.af_num(), AFType::OutputPushPull, Pull::None); - d5.set_speed(crate::gpio::Speed::VeryHigh); - d6.set_as_af_pull(d6.af_num(), AFType::OutputPushPull, Pull::None); - d6.set_speed(crate::gpio::Speed::VeryHigh); - d7.set_as_af_pull(d7.af_num(), AFType::OutputPushPull, Pull::None); - d7.set_speed(crate::gpio::Speed::VeryHigh); - - Self::new_inner( - peri, - Some(d0.map_into()), - Some(d1.map_into()), - Some(d2.map_into()), - Some(d3.map_into()), - Some(d4.map_into()), - Some(d5.map_into()), - Some(d6.map_into()), - Some(d7.map_into()), - Some(sck.map_into()), - Some(nss.map_into()), - None, - dma, - config, - OspiWidth::OCTO, - false, - ) - } - +impl<'d, T: Instance, M: PeriMode> Ospi<'d, T, M> { fn new_inner( peri: impl Peripheral

+ 'd, d0: Option>, @@ -433,12 +190,12 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { sck: Option>, nss: Option>, dqs: Option>, - dma: impl Peripheral

+ 'd, + dma: Option>, config: Config, width: OspiWidth, dual_quad: bool, ) -> Self { - into_ref!(peri, dma); + into_ref!(peri); // System configuration T::enable_and_reset(); @@ -519,6 +276,7 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { nss, dqs, dma, + _phantom: PhantomData, config, width, } @@ -702,170 +460,6 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { Ok(()) } - /// Blocking read with DMA transfer - pub fn blocking_read_dma(&mut self, buf: &mut [W], transaction: TransferConfig) -> Result<(), OspiError> - where - Dma: OctoDma, - { - if buf.is_empty() { - return Err(OspiError::EmptyBuffer); - } - - // Wait for peripheral to be free - while T::REGS.sr().read().busy() {} - - self.configure_command(&transaction, Some(buf.len()))?; - - let current_address = T::REGS.ar().read().address(); - let current_instruction = T::REGS.ir().read().instruction(); - - // For a indirect read transaction, the transaction begins when the instruction/address is set - T::REGS.cr().modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECTREAD)); - if T::REGS.ccr().read().admode() == vals::PhaseMode::NONE { - T::REGS.ir().write(|v| v.set_instruction(current_instruction)); - } else { - T::REGS.ar().write(|v| v.set_address(current_address)); - } - - let request = self.dma.request(); - let transfer = unsafe { - Transfer::new_read( - &mut self.dma, - request, - T::REGS.dr().as_ptr() as *mut W, - buf, - Default::default(), - ) - }; - - T::REGS.cr().modify(|w| w.set_dmaen(true)); - - transfer.blocking_wait(); - - finish_dma(T::REGS); - - Ok(()) - } - - /// Blocking write with DMA transfer - pub fn blocking_write_dma(&mut self, buf: &[W], transaction: TransferConfig) -> Result<(), OspiError> - where - Dma: OctoDma, - { - if buf.is_empty() { - return Err(OspiError::EmptyBuffer); - } - - // Wait for peripheral to be free - while T::REGS.sr().read().busy() {} - - self.configure_command(&transaction, Some(buf.len()))?; - T::REGS - .cr() - .modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECTWRITE)); - - let request = self.dma.request(); - let transfer = unsafe { - Transfer::new_write( - &mut self.dma, - request, - buf, - T::REGS.dr().as_ptr() as *mut W, - Default::default(), - ) - }; - - T::REGS.cr().modify(|w| w.set_dmaen(true)); - - transfer.blocking_wait(); - - finish_dma(T::REGS); - - Ok(()) - } - - /// Asynchronous read from external device - pub async fn read(&mut self, buf: &mut [W], transaction: TransferConfig) -> Result<(), OspiError> - where - Dma: OctoDma, - { - if buf.is_empty() { - return Err(OspiError::EmptyBuffer); - } - - // Wait for peripheral to be free - while T::REGS.sr().read().busy() {} - - self.configure_command(&transaction, Some(buf.len()))?; - - let current_address = T::REGS.ar().read().address(); - let current_instruction = T::REGS.ir().read().instruction(); - - // For a indirect read transaction, the transaction begins when the instruction/address is set - T::REGS.cr().modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECTREAD)); - if T::REGS.ccr().read().admode() == vals::PhaseMode::NONE { - T::REGS.ir().write(|v| v.set_instruction(current_instruction)); - } else { - T::REGS.ar().write(|v| v.set_address(current_address)); - } - - let request = self.dma.request(); - let transfer = unsafe { - Transfer::new_read( - &mut self.dma, - request, - T::REGS.dr().as_ptr() as *mut W, - buf, - Default::default(), - ) - }; - - T::REGS.cr().modify(|w| w.set_dmaen(true)); - - transfer.await; - - finish_dma(T::REGS); - - Ok(()) - } - - /// Asynchronous write to external device - pub async fn write(&mut self, buf: &[W], transaction: TransferConfig) -> Result<(), OspiError> - where - Dma: OctoDma, - { - if buf.is_empty() { - return Err(OspiError::EmptyBuffer); - } - - // Wait for peripheral to be free - while T::REGS.sr().read().busy() {} - - self.configure_command(&transaction, Some(buf.len()))?; - T::REGS - .cr() - .modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECTWRITE)); - - let request = self.dma.request(); - let transfer = unsafe { - Transfer::new_write( - &mut self.dma, - request, - buf, - T::REGS.dr().as_ptr() as *mut W, - Default::default(), - ) - }; - - T::REGS.cr().modify(|w| w.set_dmaen(true)); - - transfer.await; - - finish_dma(T::REGS); - - Ok(()) - } - /// Set new bus configuration pub fn set_config(&mut self, config: &Config) { // Wait for busy flag to clear @@ -942,7 +536,470 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { } } -impl<'d, T: Instance, Dma> Drop for Ospi<'d, T, Dma> { +impl<'d, T: Instance> Ospi<'d, T, Blocking> { + /// Create new blocking OSPI driver for a single spi external chip + pub fn new_blocking_singlespi( + peri: impl Peripheral

+ 'd, + sck: impl Peripheral

> + 'd, + d0: impl Peripheral

> + 'd, + d1: impl Peripheral

> + 'd, + nss: impl Peripheral

> + 'd, + config: Config, + ) -> Self { + Self::new_inner( + peri, + new_pin!(d0, AFType::OutputPushPull, Speed::VeryHigh), + new_pin!(d1, AFType::Input, Speed::VeryHigh), + None, + None, + None, + None, + None, + None, + new_pin!(sck, AFType::OutputPushPull, Speed::VeryHigh), + new_pin!(nss, AFType::OutputPushPull, Speed::VeryHigh, Pull::Up), + None, + None, + config, + OspiWidth::SING, + false, + ) + } + + /// Create new blocking OSPI driver for a dualspi external chip + pub fn new_blocking_dualspi( + peri: impl Peripheral

+ 'd, + sck: impl Peripheral

> + 'd, + d0: impl Peripheral

> + 'd, + d1: impl Peripheral

> + 'd, + nss: impl Peripheral

> + 'd, + config: Config, + ) -> Self { + Self::new_inner( + peri, + new_pin!(d0, AFType::OutputPushPull, Speed::VeryHigh), + new_pin!(d1, AFType::OutputPushPull, Speed::VeryHigh), + None, + None, + None, + None, + None, + None, + new_pin!(sck, AFType::OutputPushPull, Speed::VeryHigh), + new_pin!(nss, AFType::OutputPushPull, Speed::VeryHigh, Pull::Up), + None, + None, + config, + OspiWidth::DUAL, + false, + ) + } + + /// Create new blocking OSPI driver for a quadspi external chip + pub fn new_blocking_quadspi( + peri: impl Peripheral

+ 'd, + sck: impl Peripheral

> + 'd, + d0: impl Peripheral

> + 'd, + d1: impl Peripheral

> + 'd, + d2: impl Peripheral

> + 'd, + d3: impl Peripheral

> + 'd, + nss: impl Peripheral

> + 'd, + config: Config, + ) -> Self { + Self::new_inner( + peri, + new_pin!(d0, AFType::OutputPushPull, Speed::VeryHigh), + new_pin!(d1, AFType::OutputPushPull, Speed::VeryHigh), + new_pin!(d2, AFType::OutputPushPull, Speed::VeryHigh), + new_pin!(d3, AFType::OutputPushPull, Speed::VeryHigh), + None, + None, + None, + None, + new_pin!(sck, AFType::OutputPushPull, Speed::VeryHigh), + new_pin!(nss, AFType::OutputPushPull, Speed::VeryHigh, Pull::Up), + None, + None, + config, + OspiWidth::QUAD, + false, + ) + } + + /// Create new blocking OSPI driver for two quadspi external chips + pub fn new_blocking_dualquadspi( + peri: impl Peripheral

+ 'd, + sck: impl Peripheral

> + 'd, + d0: impl Peripheral

> + 'd, + d1: impl Peripheral

> + 'd, + d2: impl Peripheral

> + 'd, + d3: impl Peripheral

> + 'd, + d4: impl Peripheral

> + 'd, + d5: impl Peripheral

> + 'd, + d6: impl Peripheral

> + 'd, + d7: impl Peripheral

> + 'd, + nss: impl Peripheral

> + 'd, + config: Config, + ) -> Self { + Self::new_inner( + peri, + new_pin!(d0, AFType::OutputPushPull, Speed::VeryHigh), + new_pin!(d1, AFType::OutputPushPull, Speed::VeryHigh), + new_pin!(d2, AFType::OutputPushPull, Speed::VeryHigh), + new_pin!(d3, AFType::OutputPushPull, Speed::VeryHigh), + new_pin!(d4, AFType::OutputPushPull, Speed::VeryHigh), + new_pin!(d5, AFType::OutputPushPull, Speed::VeryHigh), + new_pin!(d6, AFType::OutputPushPull, Speed::VeryHigh), + new_pin!(d7, AFType::OutputPushPull, Speed::VeryHigh), + new_pin!(sck, AFType::OutputPushPull, Speed::VeryHigh), + new_pin!(nss, AFType::OutputPushPull, Speed::VeryHigh, Pull::Up), + None, + None, + config, + OspiWidth::QUAD, + true, + ) + } + + /// Create new blocking OSPI driver for octospi external chips + pub fn new_blocking_octospi( + peri: impl Peripheral

+ 'd, + sck: impl Peripheral

> + 'd, + d0: impl Peripheral

> + 'd, + d1: impl Peripheral

> + 'd, + d2: impl Peripheral

> + 'd, + d3: impl Peripheral

> + 'd, + d4: impl Peripheral

> + 'd, + d5: impl Peripheral

> + 'd, + d6: impl Peripheral

> + 'd, + d7: impl Peripheral

> + 'd, + nss: impl Peripheral

> + 'd, + config: Config, + ) -> Self { + Self::new_inner( + peri, + new_pin!(d0, AFType::OutputPushPull, Speed::VeryHigh), + new_pin!(d1, AFType::OutputPushPull, Speed::VeryHigh), + new_pin!(d2, AFType::OutputPushPull, Speed::VeryHigh), + new_pin!(d3, AFType::OutputPushPull, Speed::VeryHigh), + new_pin!(d4, AFType::OutputPushPull, Speed::VeryHigh), + new_pin!(d5, AFType::OutputPushPull, Speed::VeryHigh), + new_pin!(d6, AFType::OutputPushPull, Speed::VeryHigh), + new_pin!(d7, AFType::OutputPushPull, Speed::VeryHigh), + new_pin!(sck, AFType::OutputPushPull, Speed::VeryHigh), + new_pin!(nss, AFType::OutputPushPull, Speed::VeryHigh, Pull::Up), + None, + None, + config, + OspiWidth::OCTO, + false, + ) + } +} + +impl<'d, T: Instance> Ospi<'d, T, Async> { + /// Create new blocking OSPI driver for a single spi external chip + pub fn new_singlespi( + peri: impl Peripheral

+ 'd, + sck: impl Peripheral

> + 'd, + d0: impl Peripheral

> + 'd, + d1: impl Peripheral

> + 'd, + nss: impl Peripheral

> + 'd, + dma: impl Peripheral

> + 'd, + config: Config, + ) -> Self { + Self::new_inner( + peri, + new_pin!(d0, AFType::OutputPushPull, Speed::VeryHigh), + new_pin!(d1, AFType::Input, Speed::VeryHigh), + None, + None, + None, + None, + None, + None, + new_pin!(sck, AFType::OutputPushPull, Speed::VeryHigh), + new_pin!(nss, AFType::OutputPushPull, Speed::VeryHigh, Pull::Up), + None, + new_dma!(dma), + config, + OspiWidth::SING, + false, + ) + } + + /// Create new blocking OSPI driver for a dualspi external chip + pub fn new_dualspi( + peri: impl Peripheral

+ 'd, + sck: impl Peripheral

> + 'd, + d0: impl Peripheral

> + 'd, + d1: impl Peripheral

> + 'd, + nss: impl Peripheral

> + 'd, + dma: impl Peripheral

> + 'd, + config: Config, + ) -> Self { + Self::new_inner( + peri, + new_pin!(d0, AFType::OutputPushPull, Speed::VeryHigh), + new_pin!(d1, AFType::OutputPushPull, Speed::VeryHigh), + None, + None, + None, + None, + None, + None, + new_pin!(sck, AFType::OutputPushPull, Speed::VeryHigh), + new_pin!(nss, AFType::OutputPushPull, Speed::VeryHigh, Pull::Up), + None, + new_dma!(dma), + config, + OspiWidth::DUAL, + false, + ) + } + + /// Create new blocking OSPI driver for a quadspi external chip + pub fn new_quadspi( + peri: impl Peripheral

+ 'd, + sck: impl Peripheral

> + 'd, + d0: impl Peripheral

> + 'd, + d1: impl Peripheral

> + 'd, + d2: impl Peripheral

> + 'd, + d3: impl Peripheral

> + 'd, + nss: impl Peripheral

> + 'd, + dma: impl Peripheral

> + 'd, + config: Config, + ) -> Self { + Self::new_inner( + peri, + new_pin!(d0, AFType::OutputPushPull, Speed::VeryHigh), + new_pin!(d1, AFType::OutputPushPull, Speed::VeryHigh), + new_pin!(d2, AFType::OutputPushPull, Speed::VeryHigh), + new_pin!(d3, AFType::OutputPushPull, Speed::VeryHigh), + None, + None, + None, + None, + new_pin!(sck, AFType::OutputPushPull, Speed::VeryHigh), + new_pin!(nss, AFType::OutputPushPull, Speed::VeryHigh, Pull::Up), + None, + new_dma!(dma), + config, + OspiWidth::QUAD, + false, + ) + } + + /// Create new blocking OSPI driver for two quadspi external chips + pub fn new_dualquadspi( + peri: impl Peripheral

+ 'd, + sck: impl Peripheral

> + 'd, + d0: impl Peripheral

> + 'd, + d1: impl Peripheral

> + 'd, + d2: impl Peripheral

> + 'd, + d3: impl Peripheral

> + 'd, + d4: impl Peripheral

> + 'd, + d5: impl Peripheral

> + 'd, + d6: impl Peripheral

> + 'd, + d7: impl Peripheral

> + 'd, + nss: impl Peripheral

> + 'd, + dma: impl Peripheral

> + 'd, + config: Config, + ) -> Self { + Self::new_inner( + peri, + new_pin!(d0, AFType::OutputPushPull, Speed::VeryHigh), + new_pin!(d1, AFType::OutputPushPull, Speed::VeryHigh), + new_pin!(d2, AFType::OutputPushPull, Speed::VeryHigh), + new_pin!(d3, AFType::OutputPushPull, Speed::VeryHigh), + new_pin!(d4, AFType::OutputPushPull, Speed::VeryHigh), + new_pin!(d5, AFType::OutputPushPull, Speed::VeryHigh), + new_pin!(d6, AFType::OutputPushPull, Speed::VeryHigh), + new_pin!(d7, AFType::OutputPushPull, Speed::VeryHigh), + new_pin!(sck, AFType::OutputPushPull, Speed::VeryHigh), + new_pin!(nss, AFType::OutputPushPull, Speed::VeryHigh, Pull::Up), + None, + new_dma!(dma), + config, + OspiWidth::QUAD, + true, + ) + } + + /// Create new blocking OSPI driver for octospi external chips + pub fn new_octospi( + peri: impl Peripheral

+ 'd, + sck: impl Peripheral

> + 'd, + d0: impl Peripheral

> + 'd, + d1: impl Peripheral

> + 'd, + d2: impl Peripheral

> + 'd, + d3: impl Peripheral

> + 'd, + d4: impl Peripheral

> + 'd, + d5: impl Peripheral

> + 'd, + d6: impl Peripheral

> + 'd, + d7: impl Peripheral

> + 'd, + nss: impl Peripheral

> + 'd, + dma: impl Peripheral

> + 'd, + config: Config, + ) -> Self { + Self::new_inner( + peri, + new_pin!(d0, AFType::OutputPushPull, Speed::VeryHigh), + new_pin!(d1, AFType::OutputPushPull, Speed::VeryHigh), + new_pin!(d2, AFType::OutputPushPull, Speed::VeryHigh), + new_pin!(d3, AFType::OutputPushPull, Speed::VeryHigh), + new_pin!(d4, AFType::OutputPushPull, Speed::VeryHigh), + new_pin!(d5, AFType::OutputPushPull, Speed::VeryHigh), + new_pin!(d6, AFType::OutputPushPull, Speed::VeryHigh), + new_pin!(d7, AFType::OutputPushPull, Speed::VeryHigh), + new_pin!(sck, AFType::OutputPushPull, Speed::VeryHigh), + new_pin!(nss, AFType::OutputPushPull, Speed::VeryHigh, Pull::Up), + None, + new_dma!(dma), + config, + OspiWidth::OCTO, + false, + ) + } + + /// Blocking read with DMA transfer + pub fn blocking_read_dma(&mut self, buf: &mut [W], transaction: TransferConfig) -> Result<(), OspiError> { + if buf.is_empty() { + return Err(OspiError::EmptyBuffer); + } + + // Wait for peripheral to be free + while T::REGS.sr().read().busy() {} + + self.configure_command(&transaction, Some(buf.len()))?; + + let current_address = T::REGS.ar().read().address(); + let current_instruction = T::REGS.ir().read().instruction(); + + // For a indirect read transaction, the transaction begins when the instruction/address is set + T::REGS.cr().modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECTREAD)); + if T::REGS.ccr().read().admode() == vals::PhaseMode::NONE { + T::REGS.ir().write(|v| v.set_instruction(current_instruction)); + } else { + T::REGS.ar().write(|v| v.set_address(current_address)); + } + + let transfer = unsafe { + self.dma + .as_mut() + .unwrap() + .read(T::REGS.dr().as_ptr() as *mut W, buf, Default::default()) + }; + + T::REGS.cr().modify(|w| w.set_dmaen(true)); + + transfer.blocking_wait(); + + finish_dma(T::REGS); + + Ok(()) + } + + /// Blocking write with DMA transfer + pub fn blocking_write_dma(&mut self, buf: &[W], transaction: TransferConfig) -> Result<(), OspiError> { + if buf.is_empty() { + return Err(OspiError::EmptyBuffer); + } + + // Wait for peripheral to be free + while T::REGS.sr().read().busy() {} + + self.configure_command(&transaction, Some(buf.len()))?; + T::REGS + .cr() + .modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECTWRITE)); + + let transfer = unsafe { + self.dma + .as_mut() + .unwrap() + .write(buf, T::REGS.dr().as_ptr() as *mut W, Default::default()) + }; + + T::REGS.cr().modify(|w| w.set_dmaen(true)); + + transfer.blocking_wait(); + + finish_dma(T::REGS); + + Ok(()) + } + + /// Asynchronous read from external device + pub async fn read(&mut self, buf: &mut [W], transaction: TransferConfig) -> Result<(), OspiError> { + if buf.is_empty() { + return Err(OspiError::EmptyBuffer); + } + + // Wait for peripheral to be free + while T::REGS.sr().read().busy() {} + + self.configure_command(&transaction, Some(buf.len()))?; + + let current_address = T::REGS.ar().read().address(); + let current_instruction = T::REGS.ir().read().instruction(); + + // For a indirect read transaction, the transaction begins when the instruction/address is set + T::REGS.cr().modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECTREAD)); + if T::REGS.ccr().read().admode() == vals::PhaseMode::NONE { + T::REGS.ir().write(|v| v.set_instruction(current_instruction)); + } else { + T::REGS.ar().write(|v| v.set_address(current_address)); + } + + let transfer = unsafe { + self.dma + .as_mut() + .unwrap() + .read(T::REGS.dr().as_ptr() as *mut W, buf, Default::default()) + }; + + T::REGS.cr().modify(|w| w.set_dmaen(true)); + + transfer.await; + + finish_dma(T::REGS); + + Ok(()) + } + + /// Asynchronous write to external device + pub async fn write(&mut self, buf: &[W], transaction: TransferConfig) -> Result<(), OspiError> { + if buf.is_empty() { + return Err(OspiError::EmptyBuffer); + } + + // Wait for peripheral to be free + while T::REGS.sr().read().busy() {} + + self.configure_command(&transaction, Some(buf.len()))?; + T::REGS + .cr() + .modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECTWRITE)); + + let transfer = unsafe { + self.dma + .as_mut() + .unwrap() + .write(buf, T::REGS.dr().as_ptr() as *mut W, Default::default()) + }; + + T::REGS.cr().modify(|w| w.set_dmaen(true)); + + transfer.await; + + finish_dma(T::REGS); + + Ok(()) + } +} + +impl<'d, T: Instance, M: PeriMode> Drop for Ospi<'d, T, M> { fn drop(&mut self) { self.sck.as_ref().map(|x| x.set_as_disconnected()); self.d0.as_ref().map(|x| x.set_as_disconnected()); @@ -1005,7 +1062,7 @@ foreach_peripheral!( }; ); -impl<'d, T: Instance, Dma> SetConfig for Ospi<'d, T, Dma> { +impl<'d, T: Instance, M: PeriMode> SetConfig for Ospi<'d, T, M> { type Config = Config; type ConfigError = (); fn set_config(&mut self, config: &Self::Config) -> Result<(), ()> { @@ -1014,7 +1071,7 @@ impl<'d, T: Instance, Dma> SetConfig for Ospi<'d, T, Dma> { } } -impl<'d, T: Instance, Dma> GetConfig for Ospi<'d, T, Dma> { +impl<'d, T: Instance, M: PeriMode> GetConfig for Ospi<'d, T, M> { type Config = Config; fn get_config(&self) -> Self::Config { self.get_config() diff --git a/embassy-stm32/src/qspi/mod.rs b/embassy-stm32/src/qspi/mod.rs index 0a4b4f074..a82e93b5b 100644 --- a/embassy-stm32/src/qspi/mod.rs +++ b/embassy-stm32/src/qspi/mod.rs @@ -4,11 +4,14 @@ pub mod enums; +use core::marker::PhantomData; + use embassy_hal_internal::{into_ref, PeripheralRef}; use enums::*; -use crate::dma::Transfer; -use crate::gpio::{AFType, AnyPin, Pull}; +use crate::dma::ChannelAndRequest; +use crate::gpio::{AFType, AnyPin, Pull, Speed}; +use crate::mode::{Async, Blocking, Mode as PeriMode}; use crate::pac::quadspi::Quadspi as Regs; use crate::rcc::RccPeripheral; use crate::{peripherals, Peripheral}; @@ -71,7 +74,7 @@ impl Default for Config { /// QSPI driver. #[allow(dead_code)] -pub struct Qspi<'d, T: Instance, Dma> { +pub struct Qspi<'d, T: Instance, M: PeriMode> { _peri: PeripheralRef<'d, T>, sck: Option>, d0: Option>, @@ -79,93 +82,12 @@ pub struct Qspi<'d, T: Instance, Dma> { d2: Option>, d3: Option>, nss: Option>, - dma: PeripheralRef<'d, Dma>, + dma: Option>, + _phantom: PhantomData, config: Config, } -impl<'d, T: Instance, Dma> Qspi<'d, T, Dma> { - /// Create a new QSPI driver for bank 1. - pub fn new_bk1( - peri: impl Peripheral

+ 'd, - d0: impl Peripheral

> + 'd, - d1: impl Peripheral

> + 'd, - d2: impl Peripheral

> + 'd, - d3: impl Peripheral

> + 'd, - sck: impl Peripheral

> + 'd, - nss: impl Peripheral

> + 'd, - dma: impl Peripheral

+ 'd, - config: Config, - ) -> Self { - into_ref!(peri, d0, d1, d2, d3, sck, nss); - - sck.set_as_af_pull(sck.af_num(), AFType::OutputPushPull, Pull::None); - sck.set_speed(crate::gpio::Speed::VeryHigh); - nss.set_as_af_pull(nss.af_num(), AFType::OutputPushPull, Pull::Up); - nss.set_speed(crate::gpio::Speed::VeryHigh); - d0.set_as_af_pull(d0.af_num(), AFType::OutputPushPull, Pull::None); - d0.set_speed(crate::gpio::Speed::VeryHigh); - d1.set_as_af_pull(d1.af_num(), AFType::OutputPushPull, Pull::None); - d1.set_speed(crate::gpio::Speed::VeryHigh); - d2.set_as_af_pull(d2.af_num(), AFType::OutputPushPull, Pull::None); - d2.set_speed(crate::gpio::Speed::VeryHigh); - d3.set_as_af_pull(d3.af_num(), AFType::OutputPushPull, Pull::None); - d3.set_speed(crate::gpio::Speed::VeryHigh); - - Self::new_inner( - peri, - Some(d0.map_into()), - Some(d1.map_into()), - Some(d2.map_into()), - Some(d3.map_into()), - Some(sck.map_into()), - Some(nss.map_into()), - dma, - config, - FlashSelection::Flash1, - ) - } - - /// Create a new QSPI driver for bank 2. - pub fn new_bk2( - peri: impl Peripheral

+ 'd, - d0: impl Peripheral

> + 'd, - d1: impl Peripheral

> + 'd, - d2: impl Peripheral

> + 'd, - d3: impl Peripheral

> + 'd, - sck: impl Peripheral

> + 'd, - nss: impl Peripheral

> + 'd, - dma: impl Peripheral

+ 'd, - config: Config, - ) -> Self { - into_ref!(peri, d0, d1, d2, d3, sck, nss); - - sck.set_as_af_pull(sck.af_num(), AFType::OutputPushPull, Pull::None); - sck.set_speed(crate::gpio::Speed::VeryHigh); - nss.set_as_af_pull(nss.af_num(), AFType::OutputPushPull, Pull::Up); - nss.set_speed(crate::gpio::Speed::VeryHigh); - d0.set_as_af_pull(d0.af_num(), AFType::OutputPushPull, Pull::None); - d0.set_speed(crate::gpio::Speed::VeryHigh); - d1.set_as_af_pull(d1.af_num(), AFType::OutputPushPull, Pull::None); - d1.set_speed(crate::gpio::Speed::VeryHigh); - d2.set_as_af_pull(d2.af_num(), AFType::OutputPushPull, Pull::None); - d2.set_speed(crate::gpio::Speed::VeryHigh); - d3.set_as_af_pull(d3.af_num(), AFType::OutputPushPull, Pull::None); - d3.set_speed(crate::gpio::Speed::VeryHigh); - - Self::new_inner( - peri, - Some(d0.map_into()), - Some(d1.map_into()), - Some(d2.map_into()), - Some(d3.map_into()), - Some(sck.map_into()), - Some(nss.map_into()), - dma, - config, - FlashSelection::Flash2, - ) - } - +impl<'d, T: Instance, M: PeriMode> Qspi<'d, T, M> { fn new_inner( peri: impl Peripheral

+ 'd, d0: Option>, @@ -174,11 +96,11 @@ impl<'d, T: Instance, Dma> Qspi<'d, T, Dma> { d3: Option>, sck: Option>, nss: Option>, - dma: impl Peripheral

+ 'd, + dma: Option>, config: Config, fsel: FlashSelection, ) -> Self { - into_ref!(peri, dma); + into_ref!(peri); T::enable_and_reset(); @@ -220,6 +142,7 @@ impl<'d, T: Instance, Dma> Qspi<'d, T, Dma> { d3, nss, dma, + _phantom: PhantomData, config, } } @@ -278,68 +201,6 @@ impl<'d, T: Instance, Dma> Qspi<'d, T, Dma> { T::REGS.fcr().modify(|v| v.set_ctcf(true)); } - /// Blocking read data, using DMA. - pub fn blocking_read_dma(&mut self, buf: &mut [u8], transaction: TransferConfig) - where - Dma: QuadDma, - { - self.setup_transaction(QspiMode::IndirectWrite, &transaction, Some(buf.len())); - - T::REGS.ccr().modify(|v| { - v.set_fmode(QspiMode::IndirectRead.into()); - }); - let current_ar = T::REGS.ar().read().address(); - T::REGS.ar().write(|v| { - v.set_address(current_ar); - }); - - let request = self.dma.request(); - let transfer = unsafe { - Transfer::new_read( - &mut self.dma, - request, - T::REGS.dr().as_ptr() as *mut u8, - buf, - Default::default(), - ) - }; - - // STM32H7 does not have dmaen - #[cfg(not(stm32h7))] - T::REGS.cr().modify(|v| v.set_dmaen(true)); - - transfer.blocking_wait(); - } - - /// Blocking write data, using DMA. - pub fn blocking_write_dma(&mut self, buf: &[u8], transaction: TransferConfig) - where - Dma: QuadDma, - { - self.setup_transaction(QspiMode::IndirectWrite, &transaction, Some(buf.len())); - - T::REGS.ccr().modify(|v| { - v.set_fmode(QspiMode::IndirectWrite.into()); - }); - - let request = self.dma.request(); - let transfer = unsafe { - Transfer::new_write( - &mut self.dma, - request, - buf, - T::REGS.dr().as_ptr() as *mut u8, - Default::default(), - ) - }; - - // STM32H7 does not have dmaen - #[cfg(not(stm32h7))] - T::REGS.cr().modify(|v| v.set_dmaen(true)); - - transfer.blocking_wait(); - } - fn setup_transaction(&mut self, fmode: QspiMode, transaction: &TransferConfig, data_len: Option) { T::REGS.fcr().modify(|v| { v.set_csmf(true); @@ -373,6 +234,160 @@ impl<'d, T: Instance, Dma> Qspi<'d, T, Dma> { } } +impl<'d, T: Instance> Qspi<'d, T, Blocking> { + /// Create a new QSPI driver for bank 1, in blocking mode. + pub fn new_blocking_bank1( + peri: impl Peripheral

+ 'd, + d0: impl Peripheral

> + 'd, + d1: impl Peripheral

> + 'd, + d2: impl Peripheral

> + 'd, + d3: impl Peripheral

> + 'd, + sck: impl Peripheral

> + 'd, + nss: impl Peripheral

> + 'd, + config: Config, + ) -> Self { + Self::new_inner( + peri, + new_pin!(d0, AFType::OutputPushPull, Speed::VeryHigh), + new_pin!(d1, AFType::OutputPushPull, Speed::VeryHigh), + new_pin!(d2, AFType::OutputPushPull, Speed::VeryHigh), + new_pin!(d3, AFType::OutputPushPull, Speed::VeryHigh), + new_pin!(sck, AFType::OutputPushPull, Speed::VeryHigh), + new_pin!(nss, AFType::OutputPushPull, Speed::VeryHigh, Pull::Up), + None, + config, + FlashSelection::Flash1, + ) + } + + /// Create a new QSPI driver for bank 2, in blocking mode. + pub fn new_blocking_bank2( + peri: impl Peripheral

+ 'd, + d0: impl Peripheral

> + 'd, + d1: impl Peripheral

> + 'd, + d2: impl Peripheral

> + 'd, + d3: impl Peripheral

> + 'd, + sck: impl Peripheral

> + 'd, + nss: impl Peripheral

> + 'd, + config: Config, + ) -> Self { + Self::new_inner( + peri, + new_pin!(d0, AFType::OutputPushPull, Speed::VeryHigh), + new_pin!(d1, AFType::OutputPushPull, Speed::VeryHigh), + new_pin!(d2, AFType::OutputPushPull, Speed::VeryHigh), + new_pin!(d3, AFType::OutputPushPull, Speed::VeryHigh), + new_pin!(sck, AFType::OutputPushPull, Speed::VeryHigh), + new_pin!(nss, AFType::OutputPushPull, Speed::VeryHigh, Pull::Up), + None, + config, + FlashSelection::Flash2, + ) + } +} + +impl<'d, T: Instance> Qspi<'d, T, Async> { + /// Create a new QSPI driver for bank 1. + pub fn new_bank1( + peri: impl Peripheral

+ 'd, + d0: impl Peripheral

> + 'd, + d1: impl Peripheral

> + 'd, + d2: impl Peripheral

> + 'd, + d3: impl Peripheral

> + 'd, + sck: impl Peripheral

> + 'd, + nss: impl Peripheral

> + 'd, + dma: impl Peripheral

> + 'd, + config: Config, + ) -> Self { + Self::new_inner( + peri, + new_pin!(d0, AFType::OutputPushPull, Speed::VeryHigh), + new_pin!(d1, AFType::OutputPushPull, Speed::VeryHigh), + new_pin!(d2, AFType::OutputPushPull, Speed::VeryHigh), + new_pin!(d3, AFType::OutputPushPull, Speed::VeryHigh), + new_pin!(sck, AFType::OutputPushPull, Speed::VeryHigh), + new_pin!(nss, AFType::OutputPushPull, Speed::VeryHigh, Pull::Up), + new_dma!(dma), + config, + FlashSelection::Flash1, + ) + } + + /// Create a new QSPI driver for bank 2. + pub fn new_bank2( + peri: impl Peripheral

+ 'd, + d0: impl Peripheral

> + 'd, + d1: impl Peripheral

> + 'd, + d2: impl Peripheral

> + 'd, + d3: impl Peripheral

> + 'd, + sck: impl Peripheral

> + 'd, + nss: impl Peripheral

> + 'd, + dma: impl Peripheral

> + 'd, + config: Config, + ) -> Self { + Self::new_inner( + peri, + new_pin!(d0, AFType::OutputPushPull, Speed::VeryHigh), + new_pin!(d1, AFType::OutputPushPull, Speed::VeryHigh), + new_pin!(d2, AFType::OutputPushPull, Speed::VeryHigh), + new_pin!(d3, AFType::OutputPushPull, Speed::VeryHigh), + new_pin!(sck, AFType::OutputPushPull, Speed::VeryHigh), + new_pin!(nss, AFType::OutputPushPull, Speed::VeryHigh, Pull::Up), + new_dma!(dma), + config, + FlashSelection::Flash2, + ) + } + + /// Blocking read data, using DMA. + pub fn blocking_read_dma(&mut self, buf: &mut [u8], transaction: TransferConfig) { + self.setup_transaction(QspiMode::IndirectWrite, &transaction, Some(buf.len())); + + T::REGS.ccr().modify(|v| { + v.set_fmode(QspiMode::IndirectRead.into()); + }); + let current_ar = T::REGS.ar().read().address(); + T::REGS.ar().write(|v| { + v.set_address(current_ar); + }); + + let transfer = unsafe { + self.dma + .as_mut() + .unwrap() + .read(T::REGS.dr().as_ptr() as *mut u8, buf, Default::default()) + }; + + // STM32H7 does not have dmaen + #[cfg(not(stm32h7))] + T::REGS.cr().modify(|v| v.set_dmaen(true)); + + transfer.blocking_wait(); + } + + /// Blocking write data, using DMA. + pub fn blocking_write_dma(&mut self, buf: &[u8], transaction: TransferConfig) { + self.setup_transaction(QspiMode::IndirectWrite, &transaction, Some(buf.len())); + + T::REGS.ccr().modify(|v| { + v.set_fmode(QspiMode::IndirectWrite.into()); + }); + + let transfer = unsafe { + self.dma + .as_mut() + .unwrap() + .write(buf, T::REGS.dr().as_ptr() as *mut u8, Default::default()) + }; + + // STM32H7 does not have dmaen + #[cfg(not(stm32h7))] + T::REGS.cr().modify(|v| v.set_dmaen(true)); + + transfer.blocking_wait(); + } +} + trait SealedInstance { const REGS: Regs; } diff --git a/embassy-stm32/src/rcc/f247.rs b/embassy-stm32/src/rcc/f247.rs index e3ee9a039..61f687d30 100644 --- a/embassy-stm32/src/rcc/f247.rs +++ b/embassy-stm32/src/rcc/f247.rs @@ -146,17 +146,18 @@ pub(crate) unsafe fn init(config: Config) { while !PWR.csr1().read().odswrdy() {} } + // Turn on the HSI + RCC.cr().modify(|w| w.set_hsion(true)); + while !RCC.cr().read().hsirdy() {} + + // Use the HSI clock as system clock during the actual clock setup + RCC.cfgr().modify(|w| w.set_sw(Sysclk::HSI)); + while RCC.cfgr().read().sws() != Sysclk::HSI {} + // Configure HSI let hsi = match config.hsi { - false => { - RCC.cr().modify(|w| w.set_hsion(false)); - None - } - true => { - RCC.cr().modify(|w| w.set_hsion(true)); - while !RCC.cr().read().hsirdy() {} - Some(HSI_FREQ) - } + false => None, + true => Some(HSI_FREQ), }; // Configure HSE @@ -260,6 +261,11 @@ pub(crate) unsafe fn init(config: Config) { }); while RCC.cfgr().read().sws() != config.sys {} + // Disable HSI if not used + if !config.hsi { + RCC.cr().modify(|w| w.set_hsion(false)); + } + config.mux.init(); set_clocks!( @@ -277,7 +283,7 @@ pub(crate) unsafe fn init(config: Config) { pclk2_tim: Some(pclk2_tim), rtc: rtc, pll1_q: pll.q, - pll1_r: None, // TODO + pll1_r: pll.r, #[cfg(any(stm32f2, all(stm32f4, not(stm32f410)), stm32f7))] plli2s1_p: plli2s.p, @@ -297,11 +303,12 @@ pub(crate) unsafe fn init(config: Config) { #[cfg(not(any(stm32f446, stm32f427, stm32f437, stm32f4x9, stm32f7)))] pllsai1_q: None, + #[cfg(dsihost)] + dsi_phy: None, // DSI PLL clock not supported, don't call `RccPeripheral::frequency()` in the drivers + hsi_div488: hsi.map(|hsi| hsi/488u32), hsi_hse: None, afif: None, - #[cfg(any(stm32f4, stm32f7))] - dsi_phy: None, // TODO ); } diff --git a/embassy-stm32/src/rcc/h.rs b/embassy-stm32/src/rcc/h.rs index 5d47d400c..4d7004872 100644 --- a/embassy-stm32/src/rcc/h.rs +++ b/embassy-stm32/src/rcc/h.rs @@ -677,11 +677,12 @@ pub(crate) unsafe fn init(config: Config) { #[cfg(rcc_h50)] pll3_r: None, + #[cfg(dsihost)] + dsi_phy: None, // DSI PLL clock not supported, don't call `RccPeripheral::frequency()` in the drivers + #[cfg(stm32h5)] audioclk: None, i2s_ckin: None, - #[cfg(any(stm32h7, stm32h7rs))] - dsi_phy: None, // TODO #[cfg(stm32h7rs)] spdifrx_symb: None, // TODO #[cfg(stm32h7rs)] diff --git a/embassy-stm32/src/rcc/l.rs b/embassy-stm32/src/rcc/l.rs index c625948fb..e9266c65b 100644 --- a/embassy-stm32/src/rcc/l.rs +++ b/embassy-stm32/src/rcc/l.rs @@ -159,6 +159,13 @@ pub(crate) unsafe fn init(config: Config) { while RCC.cfgr().read().sws() != Sysclk::MSI {} } + #[cfg(stm32wl)] + { + // Set max latency + FLASH.acr().modify(|w| w.set_prften(true)); + FLASH.acr().modify(|w| w.set_latency(2)); + } + // Set voltage scale #[cfg(any(stm32l0, stm32l1))] { @@ -413,6 +420,9 @@ pub(crate) unsafe fn init(config: Config) { #[cfg(any(stm32l47x, stm32l48x, stm32l49x, stm32l4ax, rcc_l4plus, stm32l5))] pllsai2_r: pllsai2.r, + #[cfg(dsihost)] + dsi_phy: None, // DSI PLL clock not supported, don't call `RccPeripheral::frequency()` in the drivers + rtc: rtc, // TODO @@ -420,8 +430,6 @@ pub(crate) unsafe fn init(config: Config) { sai2_extclk: None, lsi: None, lse: None, - #[cfg(stm32l4)] - dsi_phy: None, ); } diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs index a585940bc..28816256c 100644 --- a/embassy-stm32/src/rcc/mod.rs +++ b/embassy-stm32/src/rcc/mod.rs @@ -31,6 +31,7 @@ pub use hsi48::*; mod _version; pub use _version::*; +use stm32_metapac::RCC; pub use crate::_generated::{mux, Clocks}; use crate::time::Hertz; @@ -66,7 +67,9 @@ pub(crate) unsafe fn get_freqs() -> &'static Clocks { } pub(crate) trait SealedRccPeripheral { - fn frequency() -> crate::time::Hertz; + const ENABLE_BIT: ClockEnableBit; + + fn frequency() -> Hertz; fn enable_and_reset_with_cs(cs: CriticalSection); fn disable_with_cs(cs: CriticalSection); @@ -137,3 +140,49 @@ pub unsafe fn enable_and_reset() { pub unsafe fn disable() { T::disable(); } + +/// Struct representing some clock enable bit (xxxENR.xxEN), only known at runtime. +#[derive(Clone, Copy)] +pub(crate) struct ClockEnableBit { + /// offset in 32bit words of the xxxENR register into the RCC register block. + offset: u8, + /// bit within the register (0..=31) + bit: u8, +} + +impl ClockEnableBit { + /// Safety: offset+bit must correspond to a valid xxxEN bit. + pub(crate) const unsafe fn new(offset: u8, bit: u8) -> Self { + Self { offset, bit } + } + + fn ptr(self) -> *mut u32 { + unsafe { (RCC.as_ptr() as *mut u32).add(self.offset as _) } + } + + #[allow(unused)] + pub(crate) fn enable_with_cs(self, _cs: CriticalSection) { + let p = self.ptr(); + unsafe { + let val = p.read_volatile(); + p.write_volatile(val | 1u32 << self.bit); + } + } + + pub(crate) fn disable_with_cs(self, _cs: CriticalSection) { + let p = self.ptr(); + unsafe { + let val = p.read_volatile(); + p.write_volatile(val & !(1u32 << self.bit)); + } + } + + #[allow(unused)] + pub(crate) fn enable(self) { + critical_section::with(|cs| self.enable_with_cs(cs)) + } + + pub(crate) fn disable(self) { + critical_section::with(|cs| self.disable_with_cs(cs)) + } +} diff --git a/embassy-stm32/src/rcc/u5.rs b/embassy-stm32/src/rcc/u5.rs index 4013d0a46..d6331f512 100644 --- a/embassy-stm32/src/rcc/u5.rs +++ b/embassy-stm32/src/rcc/u5.rs @@ -289,6 +289,9 @@ pub(crate) unsafe fn init(config: Config) { pll3_q: pll3.q, pll3_r: pll3.r, + #[cfg(dsihost)] + dsi_phy: None, // DSI PLL clock not supported, don't call `RccPeripheral::frequency()` in the drivers + // TODO audioclk: None, hsi48_div_2: None, @@ -297,7 +300,6 @@ pub(crate) unsafe fn init(config: Config) { msik: None, shsi: None, shsi_div_2: None, - dsi_phy: None, ); } diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs index 24159adce..be8cfcecf 100644 --- a/embassy-stm32/src/spi/mod.rs +++ b/embassy-stm32/src/spi/mod.rs @@ -6,16 +6,16 @@ use core::ptr; use embassy_embedded_hal::SetConfig; use embassy_futures::join::join; -use embassy_hal_internal::{into_ref, PeripheralRef}; +use embassy_hal_internal::PeripheralRef; pub use embedded_hal_02::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3}; use crate::dma::{slice_ptr_parts, word, ChannelAndRequest}; use crate::gpio::{AFType, AnyPin, Pull, SealedPin as _, Speed}; use crate::mode::{Async, Blocking, Mode as PeriMode}; use crate::pac::spi::{regs, vals, Spi as Regs}; -use crate::rcc::RccPeripheral; +use crate::rcc::{ClockEnableBit, SealedRccPeripheral}; use crate::time::Hertz; -use crate::{peripherals, Peripheral}; +use crate::Peripheral; /// SPI error. #[derive(Debug, PartialEq, Eq)] @@ -98,8 +98,9 @@ impl Config { } } /// SPI driver. -pub struct Spi<'d, T: Instance, M: PeriMode> { - _peri: PeripheralRef<'d, T>, +pub struct Spi<'d, M: PeriMode> { + pub(crate) info: &'static Info, + kernel_clock: Hertz, sck: Option>, mosi: Option>, miso: Option>, @@ -109,9 +110,9 @@ pub struct Spi<'d, T: Instance, M: PeriMode> { current_word_size: word_impl::Config, } -impl<'d, T: Instance, M: PeriMode> Spi<'d, T, M> { - fn new_inner( - peri: impl Peripheral

+ 'd, +impl<'d, M: PeriMode> Spi<'d, M> { + fn new_inner( + _peri: impl Peripheral

+ 'd, sck: Option>, mosi: Option>, miso: Option>, @@ -119,11 +120,9 @@ impl<'d, T: Instance, M: PeriMode> Spi<'d, T, M> { rx_dma: Option>, config: Config, ) -> Self { - into_ref!(peri); - - let pclk = T::frequency(); - let freq = config.frequency; - let br = compute_baud_rate(pclk, freq); + let regs = T::info().regs; + let kernel_clock = T::frequency(); + let br = compute_baud_rate(kernel_clock, config.frequency); let cpha = config.raw_phase(); let cpol = config.raw_polarity(); @@ -134,10 +133,10 @@ impl<'d, T: Instance, M: PeriMode> Spi<'d, T, M> { #[cfg(any(spi_v1, spi_f1))] { - T::REGS.cr2().modify(|w| { + regs.cr2().modify(|w| { w.set_ssoe(false); }); - T::REGS.cr1().modify(|w| { + regs.cr1().modify(|w| { w.set_cpha(cpha); w.set_cpol(cpol); @@ -157,13 +156,13 @@ impl<'d, T: Instance, M: PeriMode> Spi<'d, T, M> { } #[cfg(spi_v2)] { - T::REGS.cr2().modify(|w| { + regs.cr2().modify(|w| { let (ds, frxth) = ::CONFIG; w.set_frxth(frxth); w.set_ds(ds); w.set_ssoe(false); }); - T::REGS.cr1().modify(|w| { + regs.cr1().modify(|w| { w.set_cpha(cpha); w.set_cpol(cpol); @@ -179,8 +178,8 @@ impl<'d, T: Instance, M: PeriMode> Spi<'d, T, M> { } #[cfg(any(spi_v3, spi_v4, spi_v5))] { - T::REGS.ifcr().write(|w| w.0 = 0xffff_ffff); - T::REGS.cfg2().modify(|w| { + regs.ifcr().write(|w| w.0 = 0xffff_ffff); + regs.cfg2().modify(|w| { //w.set_ssoe(true); w.set_ssoe(false); w.set_cpha(cpha); @@ -195,23 +194,24 @@ impl<'d, T: Instance, M: PeriMode> Spi<'d, T, M> { w.set_afcntr(true); w.set_ssiop(vals::Ssiop::ACTIVEHIGH); }); - T::REGS.cfg1().modify(|w| { + regs.cfg1().modify(|w| { w.set_crcen(false); w.set_mbr(br); w.set_dsize(::CONFIG); w.set_fthlv(vals::Fthlv::ONEFRAME); }); - T::REGS.cr2().modify(|w| { + regs.cr2().modify(|w| { w.set_tsize(0); }); - T::REGS.cr1().modify(|w| { + regs.cr1().modify(|w| { w.set_ssi(false); w.set_spe(true); }); } Self { - _peri: peri, + info: T::info(), + kernel_clock, sck, mosi, miso, @@ -229,12 +229,10 @@ impl<'d, T: Instance, M: PeriMode> Spi<'d, T, M> { let lsbfirst = config.raw_byte_order(); - let pclk = T::frequency(); - let freq = config.frequency; - let br = compute_baud_rate(pclk, freq); + let br = compute_baud_rate(self.kernel_clock, config.frequency); #[cfg(any(spi_v1, spi_f1, spi_v2))] - T::REGS.cr1().modify(|w| { + self.info.regs.cr1().modify(|w| { w.set_cpha(cpha); w.set_cpol(cpol); w.set_br(br); @@ -243,12 +241,12 @@ impl<'d, T: Instance, M: PeriMode> Spi<'d, T, M> { #[cfg(any(spi_v3, spi_v4, spi_v5))] { - T::REGS.cfg2().modify(|w| { + self.info.regs.cfg2().modify(|w| { w.set_cpha(cpha); w.set_cpol(cpol); w.set_lsbfirst(lsbfirst); }); - T::REGS.cfg1().modify(|w| { + self.info.regs.cfg1().modify(|w| { w.set_mbr(br); }); } @@ -258,11 +256,11 @@ impl<'d, T: Instance, M: PeriMode> Spi<'d, T, M> { /// Get current SPI configuration. pub fn get_current_config(&self) -> Config { #[cfg(any(spi_v1, spi_f1, spi_v2))] - let cfg = T::REGS.cr1().read(); + let cfg = self.info.regs.cr1().read(); #[cfg(any(spi_v3, spi_v4, spi_v5))] - let cfg = T::REGS.cfg2().read(); + let cfg = self.info.regs.cfg2().read(); #[cfg(any(spi_v3, spi_v4, spi_v5))] - let cfg1 = T::REGS.cfg1().read(); + let cfg1 = self.info.regs.cfg1().read(); let polarity = if cfg.cpol() == vals::Cpol::IDLELOW { Polarity::IdleLow @@ -297,8 +295,7 @@ impl<'d, T: Instance, M: PeriMode> Spi<'d, T, M> { #[cfg(any(spi_v3, spi_v4, spi_v5))] let br = cfg1.mbr(); - let pclk = T::frequency(); - let frequency = compute_frequency(pclk, br); + let frequency = compute_frequency(self.kernel_clock, br); Config { mode: Mode { polarity, phase }, @@ -315,40 +312,40 @@ impl<'d, T: Instance, M: PeriMode> Spi<'d, T, M> { #[cfg(any(spi_v1, spi_f1))] { - T::REGS.cr1().modify(|reg| { + self.info.regs.cr1().modify(|reg| { reg.set_spe(false); reg.set_dff(word_size) }); - T::REGS.cr1().modify(|reg| { + self.info.regs.cr1().modify(|reg| { reg.set_spe(true); }); } #[cfg(spi_v2)] { - T::REGS.cr1().modify(|w| { + self.info.regs.cr1().modify(|w| { w.set_spe(false); }); - T::REGS.cr2().modify(|w| { + self.info.regs.cr2().modify(|w| { w.set_frxth(word_size.1); w.set_ds(word_size.0); }); - T::REGS.cr1().modify(|w| { + self.info.regs.cr1().modify(|w| { w.set_spe(true); }); } #[cfg(any(spi_v3, spi_v4, spi_v5))] { - T::REGS.cr1().modify(|w| { + self.info.regs.cr1().modify(|w| { w.set_csusp(true); }); - while T::REGS.sr().read().eot() {} - T::REGS.cr1().modify(|w| { + while self.info.regs.sr().read().eot() {} + self.info.regs.cr1().modify(|w| { w.set_spe(false); }); - T::REGS.cfg1().modify(|w| { + self.info.regs.cfg1().modify(|w| { w.set_dsize(word_size); }); - T::REGS.cr1().modify(|w| { + self.info.regs.cr1().modify(|w| { w.set_csusp(false); w.set_spe(true); }); @@ -359,22 +356,22 @@ impl<'d, T: Instance, M: PeriMode> Spi<'d, T, M> { /// Blocking write. pub fn blocking_write(&mut self, words: &[W]) -> Result<(), Error> { - T::REGS.cr1().modify(|w| w.set_spe(true)); - flush_rx_fifo(T::REGS); + self.info.regs.cr1().modify(|w| w.set_spe(true)); + flush_rx_fifo(self.info.regs); self.set_word_size(W::CONFIG); for word in words.iter() { - let _ = transfer_word(T::REGS, *word)?; + let _ = transfer_word(self.info.regs, *word)?; } Ok(()) } /// Blocking read. pub fn blocking_read(&mut self, words: &mut [W]) -> Result<(), Error> { - T::REGS.cr1().modify(|w| w.set_spe(true)); - flush_rx_fifo(T::REGS); + self.info.regs.cr1().modify(|w| w.set_spe(true)); + flush_rx_fifo(self.info.regs); self.set_word_size(W::CONFIG); for word in words.iter_mut() { - *word = transfer_word(T::REGS, W::default())?; + *word = transfer_word(self.info.regs, W::default())?; } Ok(()) } @@ -383,11 +380,11 @@ impl<'d, T: Instance, M: PeriMode> Spi<'d, T, M> { /// /// This writes the contents of `data` on MOSI, and puts the received data on MISO in `data`, at the same time. pub fn blocking_transfer_in_place(&mut self, words: &mut [W]) -> Result<(), Error> { - T::REGS.cr1().modify(|w| w.set_spe(true)); - flush_rx_fifo(T::REGS); + self.info.regs.cr1().modify(|w| w.set_spe(true)); + flush_rx_fifo(self.info.regs); self.set_word_size(W::CONFIG); for word in words.iter_mut() { - *word = transfer_word(T::REGS, *word)?; + *word = transfer_word(self.info.regs, *word)?; } Ok(()) } @@ -399,13 +396,13 @@ impl<'d, T: Instance, M: PeriMode> Spi<'d, T, M> { /// The transfer runs for `max(read.len(), write.len())` bytes. If `read` is shorter extra bytes are ignored. /// If `write` is shorter it is padded with zero bytes. pub fn blocking_transfer(&mut self, read: &mut [W], write: &[W]) -> Result<(), Error> { - T::REGS.cr1().modify(|w| w.set_spe(true)); - flush_rx_fifo(T::REGS); + self.info.regs.cr1().modify(|w| w.set_spe(true)); + flush_rx_fifo(self.info.regs); self.set_word_size(W::CONFIG); let len = read.len().max(write.len()); for i in 0..len { let wb = write.get(i).copied().unwrap_or_default(); - let rb = transfer_word(T::REGS, wb)?; + let rb = transfer_word(self.info.regs, wb)?; if let Some(r) = read.get_mut(i) { *r = rb; } @@ -414,9 +411,9 @@ impl<'d, T: Instance, M: PeriMode> Spi<'d, T, M> { } } -impl<'d, T: Instance> Spi<'d, T, Blocking> { +impl<'d> Spi<'d, Blocking> { /// Create a new blocking SPI driver. - pub fn new_blocking( + pub fn new_blocking( peri: impl Peripheral

+ 'd, sck: impl Peripheral

> + 'd, mosi: impl Peripheral

> + 'd, @@ -443,7 +440,7 @@ impl<'d, T: Instance> Spi<'d, T, Blocking> { } /// Create a new blocking SPI driver, in RX-only mode (only MISO pin, no MOSI). - pub fn new_blocking_rxonly( + pub fn new_blocking_rxonly( peri: impl Peripheral

+ 'd, sck: impl Peripheral

> + 'd, miso: impl Peripheral

> + 'd, @@ -469,7 +466,7 @@ impl<'d, T: Instance> Spi<'d, T, Blocking> { } /// Create a new blocking SPI driver, in TX-only mode (only MOSI pin, no MISO). - pub fn new_blocking_txonly( + pub fn new_blocking_txonly( peri: impl Peripheral

+ 'd, sck: impl Peripheral

> + 'd, mosi: impl Peripheral

> + 'd, @@ -489,7 +486,7 @@ impl<'d, T: Instance> Spi<'d, T, Blocking> { /// Create a new SPI driver, in TX-only mode, without SCK pin. /// /// This can be useful for bit-banging non-SPI protocols. - pub fn new_blocking_txonly_nosck( + pub fn new_blocking_txonly_nosck( peri: impl Peripheral

+ 'd, mosi: impl Peripheral

> + 'd, config: Config, @@ -506,9 +503,9 @@ impl<'d, T: Instance> Spi<'d, T, Blocking> { } } -impl<'d, T: Instance> Spi<'d, T, Async> { +impl<'d> Spi<'d, Async> { /// Create a new SPI driver. - pub fn new( + pub fn new( peri: impl Peripheral

+ 'd, sck: impl Peripheral

> + 'd, mosi: impl Peripheral

> + 'd, @@ -529,7 +526,7 @@ impl<'d, T: Instance> Spi<'d, T, Async> { } /// Create a new SPI driver, in RX-only mode (only MISO pin, no MOSI). - pub fn new_rxonly( + pub fn new_rxonly( peri: impl Peripheral

+ 'd, sck: impl Peripheral

> + 'd, miso: impl Peripheral

> + 'd, @@ -548,7 +545,7 @@ impl<'d, T: Instance> Spi<'d, T, Async> { } /// Create a new SPI driver, in TX-only mode (only MOSI pin, no MISO). - pub fn new_txonly( + pub fn new_txonly( peri: impl Peripheral

+ 'd, sck: impl Peripheral

> + 'd, mosi: impl Peripheral

> + 'd, @@ -569,7 +566,7 @@ impl<'d, T: Instance> Spi<'d, T, Async> { /// Create a new SPI driver, in TX-only mode, without SCK pin. /// /// This can be useful for bit-banging non-SPI protocols. - pub fn new_txonly_nosck( + pub fn new_txonly_nosck( peri: impl Peripheral

+ 'd, mosi: impl Peripheral

> + 'd, tx_dma: impl Peripheral

> + 'd, @@ -588,7 +585,7 @@ impl<'d, T: Instance> Spi<'d, T, Async> { #[cfg(stm32wl)] /// Useful for on chip peripherals like SUBGHZ which are hardwired. - pub fn new_subghz( + pub fn new_subghz( peri: impl Peripheral

+ 'd, tx_dma: impl Peripheral

> + 'd, rx_dma: impl Peripheral

> + 'd, @@ -596,7 +593,7 @@ impl<'d, T: Instance> Spi<'d, T, Async> { // see RM0453 rev 1 section 7.2.13 page 291 // The SUBGHZSPI_SCK frequency is obtained by PCLK3 divided by two. // The SUBGHZSPI_SCK clock maximum speed must not exceed 16 MHz. - let pclk3_freq = ::frequency().0; + let pclk3_freq = ::frequency().0; let freq = Hertz(core::cmp::min(pclk3_freq / 2, 16_000_000)); let mut config = Config::default(); config.mode = MODE_0; @@ -607,7 +604,7 @@ impl<'d, T: Instance> Spi<'d, T, Async> { } #[allow(dead_code)] - pub(crate) fn new_internal( + pub(crate) fn new_internal( peri: impl Peripheral

+ 'd, tx_dma: impl Peripheral

> + 'd, rx_dma: impl Peripheral

> + 'd, @@ -623,25 +620,25 @@ impl<'d, T: Instance> Spi<'d, T, Async> { } self.set_word_size(W::CONFIG); - T::REGS.cr1().modify(|w| { + self.info.regs.cr1().modify(|w| { w.set_spe(false); }); - let tx_dst = T::REGS.tx_ptr(); + let tx_dst = self.info.regs.tx_ptr(); let tx_f = unsafe { self.tx_dma.as_mut().unwrap().write(data, tx_dst, Default::default()) }; - set_txdmaen(T::REGS, true); - T::REGS.cr1().modify(|w| { + set_txdmaen(self.info.regs, true); + self.info.regs.cr1().modify(|w| { w.set_spe(true); }); #[cfg(any(spi_v3, spi_v4, spi_v5))] - T::REGS.cr1().modify(|w| { + self.info.regs.cr1().modify(|w| { w.set_cstart(true); }); tx_f.await; - finish_dma(T::REGS); + finish_dma(self.info.regs); Ok(()) } @@ -653,22 +650,22 @@ impl<'d, T: Instance> Spi<'d, T, Async> { } self.set_word_size(W::CONFIG); - T::REGS.cr1().modify(|w| { + self.info.regs.cr1().modify(|w| { w.set_spe(false); }); // SPIv3 clears rxfifo on SPE=0 #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] - flush_rx_fifo(T::REGS); + flush_rx_fifo(self.info.regs); - set_rxdmaen(T::REGS, true); + set_rxdmaen(self.info.regs, true); let clock_byte_count = data.len(); - let rx_src = T::REGS.rx_ptr(); + let rx_src = self.info.regs.rx_ptr(); let rx_f = unsafe { self.rx_dma.as_mut().unwrap().read(rx_src, data, Default::default()) }; - let tx_dst = T::REGS.tx_ptr(); + let tx_dst = self.info.regs.tx_ptr(); let clock_byte = 0x00u8; let tx_f = unsafe { self.tx_dma @@ -677,18 +674,18 @@ impl<'d, T: Instance> Spi<'d, T, Async> { .write_repeated(&clock_byte, clock_byte_count, tx_dst, Default::default()) }; - set_txdmaen(T::REGS, true); - T::REGS.cr1().modify(|w| { + set_txdmaen(self.info.regs, true); + self.info.regs.cr1().modify(|w| { w.set_spe(true); }); #[cfg(any(spi_v3, spi_v4, spi_v5))] - T::REGS.cr1().modify(|w| { + self.info.regs.cr1().modify(|w| { w.set_cstart(true); }); join(tx_f, rx_f).await; - finish_dma(T::REGS); + finish_dma(self.info.regs); Ok(()) } @@ -702,20 +699,20 @@ impl<'d, T: Instance> Spi<'d, T, Async> { } self.set_word_size(W::CONFIG); - T::REGS.cr1().modify(|w| { + self.info.regs.cr1().modify(|w| { w.set_spe(false); }); // SPIv3 clears rxfifo on SPE=0 #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] - flush_rx_fifo(T::REGS); + flush_rx_fifo(self.info.regs); - set_rxdmaen(T::REGS, true); + set_rxdmaen(self.info.regs, true); - let rx_src = T::REGS.rx_ptr(); + let rx_src = self.info.regs.rx_ptr(); let rx_f = unsafe { self.rx_dma.as_mut().unwrap().read_raw(rx_src, read, Default::default()) }; - let tx_dst = T::REGS.tx_ptr(); + let tx_dst = self.info.regs.tx_ptr(); let tx_f = unsafe { self.tx_dma .as_mut() @@ -723,18 +720,18 @@ impl<'d, T: Instance> Spi<'d, T, Async> { .write_raw(write, tx_dst, Default::default()) }; - set_txdmaen(T::REGS, true); - T::REGS.cr1().modify(|w| { + set_txdmaen(self.info.regs, true); + self.info.regs.cr1().modify(|w| { w.set_spe(true); }); #[cfg(any(spi_v3, spi_v4, spi_v5))] - T::REGS.cr1().modify(|w| { + self.info.regs.cr1().modify(|w| { w.set_cstart(true); }); join(tx_f, rx_f).await; - finish_dma(T::REGS); + finish_dma(self.info.regs); Ok(()) } @@ -757,13 +754,13 @@ impl<'d, T: Instance> Spi<'d, T, Async> { } } -impl<'d, T: Instance, M: PeriMode> Drop for Spi<'d, T, M> { +impl<'d, M: PeriMode> Drop for Spi<'d, M> { fn drop(&mut self) { self.sck.as_ref().map(|x| x.set_as_disconnected()); self.mosi.as_ref().map(|x| x.set_as_disconnected()); self.miso.as_ref().map(|x| x.set_as_disconnected()); - T::disable(); + self.info.enable_bit.disable(); } } @@ -772,8 +769,8 @@ use vals::Br; #[cfg(any(spi_v3, spi_v4, spi_v5))] use vals::Mbr as Br; -fn compute_baud_rate(clocks: Hertz, freq: Hertz) -> Br { - let val = match clocks.0 / freq.0 { +fn compute_baud_rate(kernel_clock: Hertz, freq: Hertz) -> Br { + let val = match kernel_clock.0 / freq.0 { 0 => panic!("You are trying to reach a frequency higher than the clock"), 1..=2 => 0b000, 3..=5 => 0b001, @@ -788,7 +785,7 @@ fn compute_baud_rate(clocks: Hertz, freq: Hertz) -> Br { Br::from_bits(val) } -fn compute_frequency(clocks: Hertz, br: Br) -> Hertz { +fn compute_frequency(kernel_clock: Hertz, br: Br) -> Hertz { let div: u16 = match br { Br::DIV2 => 2, Br::DIV4 => 4, @@ -800,7 +797,7 @@ fn compute_frequency(clocks: Hertz, br: Br) -> Hertz { Br::DIV256 => 256, }; - clocks / div + kernel_clock / div } trait RegsExt { @@ -975,7 +972,7 @@ fn transfer_word(regs: Regs, tx_word: W) -> Result { // some marker traits. For details, see https://github.com/rust-embedded/embedded-hal/pull/289 macro_rules! impl_blocking { ($w:ident) => { - impl<'d, T: Instance, M: PeriMode> embedded_hal_02::blocking::spi::Write<$w> for Spi<'d, T, M> { + impl<'d, M: PeriMode> embedded_hal_02::blocking::spi::Write<$w> for Spi<'d, M> { type Error = Error; fn write(&mut self, words: &[$w]) -> Result<(), Self::Error> { @@ -983,7 +980,7 @@ macro_rules! impl_blocking { } } - impl<'d, T: Instance, M: PeriMode> embedded_hal_02::blocking::spi::Transfer<$w> for Spi<'d, T, M> { + impl<'d, M: PeriMode> embedded_hal_02::blocking::spi::Transfer<$w> for Spi<'d, M> { type Error = Error; fn transfer<'w>(&mut self, words: &'w mut [$w]) -> Result<&'w [$w], Self::Error> { @@ -997,11 +994,11 @@ macro_rules! impl_blocking { impl_blocking!(u8); impl_blocking!(u16); -impl<'d, T: Instance, M: PeriMode> embedded_hal_1::spi::ErrorType for Spi<'d, T, M> { +impl<'d, M: PeriMode> embedded_hal_1::spi::ErrorType for Spi<'d, M> { type Error = Error; } -impl<'d, T: Instance, W: Word, M: PeriMode> embedded_hal_1::spi::SpiBus for Spi<'d, T, M> { +impl<'d, W: Word, M: PeriMode> embedded_hal_1::spi::SpiBus for Spi<'d, M> { fn flush(&mut self) -> Result<(), Self::Error> { Ok(()) } @@ -1034,7 +1031,7 @@ impl embedded_hal_1::spi::Error for Error { } } -impl<'d, T: Instance, W: Word> embedded_hal_async::spi::SpiBus for Spi<'d, T, Async> { +impl<'d, W: Word> embedded_hal_async::spi::SpiBus for Spi<'d, Async> { async fn flush(&mut self) -> Result<(), Self::Error> { Ok(()) } @@ -1056,10 +1053,6 @@ impl<'d, T: Instance, W: Word> embedded_hal_async::spi::SpiBus for Spi<'d, T, } } -pub(crate) trait SealedInstance { - const REGS: Regs; -} - trait SealedWord { const CONFIG: word_impl::Config; } @@ -1145,9 +1138,20 @@ mod word_impl { impl_word!(u32, 32 - 1); } -/// SPI instance trait. -#[allow(private_bounds)] -pub trait Instance: Peripheral

+ SealedInstance + RccPeripheral {} +pub(crate) struct Info { + pub(crate) regs: Regs, + pub(crate) enable_bit: ClockEnableBit, +} + +struct State {} + +impl State { + const fn new() -> Self { + Self {} + } +} + +peri_trait!(); pin_trait!(SckPin, Instance); pin_trait!(MosiPin, Instance); @@ -1161,15 +1165,14 @@ dma_trait!(TxDma, Instance); foreach_peripheral!( (spi, $inst:ident) => { - impl SealedInstance for peripherals::$inst { - const REGS: Regs = crate::pac::$inst; - } - - impl Instance for peripherals::$inst {} + peri_trait_impl!($inst, Info { + regs: crate::pac::$inst, + enable_bit: crate::peripherals::$inst::ENABLE_BIT, + }); }; ); -impl<'d, T: Instance, M: PeriMode> SetConfig for Spi<'d, T, M> { +impl<'d, M: PeriMode> SetConfig for Spi<'d, M> { type Config = Config; type ConfigError = (); fn set_config(&mut self, config: &Self::Config) -> Result<(), ()> { diff --git a/embassy-stm32/src/timer/input_capture.rs b/embassy-stm32/src/timer/input_capture.rs new file mode 100644 index 000000000..a1c1486f9 --- /dev/null +++ b/embassy-stm32/src/timer/input_capture.rs @@ -0,0 +1,233 @@ +//! Input capture driver. + +use core::future::Future; +use core::marker::PhantomData; +use core::pin::Pin; +use core::task::{Context, Poll}; + +use embassy_hal_internal::{into_ref, PeripheralRef}; + +use super::low_level::{CountingMode, InputCaptureMode, InputTISelection, Timer}; +use super::{ + CaptureCompareInterruptHandler, Channel, Channel1Pin, Channel2Pin, Channel3Pin, Channel4Pin, + GeneralInstance4Channel, +}; +use crate::gpio::{AFType, AnyPin, Pull}; +use crate::interrupt::typelevel::{Binding, Interrupt}; +use crate::time::Hertz; +use crate::Peripheral; + +/// Channel 1 marker type. +pub enum Ch1 {} +/// Channel 2 marker type. +pub enum Ch2 {} +/// Channel 3 marker type. +pub enum Ch3 {} +/// Channel 4 marker type. +pub enum Ch4 {} + +/// Capture pin wrapper. +/// +/// This wraps a pin to make it usable with capture. +pub struct CapturePin<'d, T, C> { + _pin: PeripheralRef<'d, AnyPin>, + phantom: PhantomData<(T, C)>, +} + +macro_rules! channel_impl { + ($new_chx:ident, $channel:ident, $pin_trait:ident) => { + impl<'d, T: GeneralInstance4Channel> CapturePin<'d, T, $channel> { + #[doc = concat!("Create a new ", stringify!($channel), " capture pin instance.")] + pub fn $new_chx(pin: impl Peripheral

> + 'd, pull_type: Pull) -> Self { + into_ref!(pin); + critical_section::with(|_| { + pin.set_as_af_pull(pin.af_num(), AFType::Input, pull_type); + #[cfg(gpio_v2)] + pin.set_speed(crate::gpio::Speed::VeryHigh); + }); + CapturePin { + _pin: pin.map_into(), + phantom: PhantomData, + } + } + } + }; +} + +channel_impl!(new_ch1, Ch1, Channel1Pin); +channel_impl!(new_ch2, Ch2, Channel2Pin); +channel_impl!(new_ch3, Ch3, Channel3Pin); +channel_impl!(new_ch4, Ch4, Channel4Pin); + +/// Input capture driver. +pub struct InputCapture<'d, T: GeneralInstance4Channel> { + inner: Timer<'d, T>, +} + +impl<'d, T: GeneralInstance4Channel> InputCapture<'d, T> { + /// Create a new input capture driver. + pub fn new( + tim: impl Peripheral

+ 'd, + _ch1: Option>, + _ch2: Option>, + _ch3: Option>, + _ch4: Option>, + _irq: impl Binding> + 'd, + freq: Hertz, + counting_mode: CountingMode, + ) -> Self { + Self::new_inner(tim, freq, counting_mode) + } + + fn new_inner(tim: impl Peripheral

+ 'd, freq: Hertz, counting_mode: CountingMode) -> Self { + let mut this = Self { inner: Timer::new(tim) }; + + this.inner.set_counting_mode(counting_mode); + this.set_tick_freq(freq); + this.inner.enable_outputs(); // Required for advanced timers, see GeneralInstance4Channel for details + this.inner.start(); + + // enable NVIC interrupt + T::CaptureCompareInterrupt::unpend(); + unsafe { T::CaptureCompareInterrupt::enable() }; + + this + } + + /// Enable the given channel. + pub fn enable(&mut self, channel: Channel) { + self.inner.enable_channel(channel, true); + } + + /// Disable the given channel. + pub fn disable(&mut self, channel: Channel) { + self.inner.enable_channel(channel, false); + } + + /// Check whether given channel is enabled + pub fn is_enabled(&self, channel: Channel) -> bool { + self.inner.get_channel_enable_state(channel) + } + + /// Set tick frequency. + /// + /// Note: when you call this, the max period value changes + pub fn set_tick_freq(&mut self, freq: Hertz) { + let f = freq; + assert!(f.0 > 0); + let timer_f = self.inner.get_clock_frequency(); + + let pclk_ticks_per_timer_period = timer_f / f; + let psc: u16 = unwrap!((pclk_ticks_per_timer_period - 1).try_into()); + + let regs = self.inner.regs_core(); + regs.psc().write_value(psc); + + // Generate an Update Request + regs.egr().write(|r| r.set_ug(true)); + } + + /// Set the input capture mode for a given channel. + pub fn set_input_capture_mode(&mut self, channel: Channel, mode: InputCaptureMode) { + self.inner.set_input_capture_mode(channel, mode); + } + + /// Set input TI selection. + pub fn set_input_ti_selection(&mut self, channel: Channel, tisel: InputTISelection) { + self.inner.set_input_ti_selection(channel, tisel) + } + + /// Get capture value for a channel. + pub fn get_capture_value(&self, channel: Channel) -> u32 { + self.inner.get_capture_value(channel) + } + + /// Get input interrupt. + pub fn get_input_interrupt(&self, channel: Channel) -> bool { + self.inner.get_input_interrupt(channel) + } + + fn new_future(&self, channel: Channel, mode: InputCaptureMode, tisel: InputTISelection) -> InputCaptureFuture { + self.inner.enable_channel(channel, true); + self.inner.set_input_capture_mode(channel, mode); + self.inner.set_input_ti_selection(channel, tisel); + self.inner.clear_input_interrupt(channel); + self.inner.enable_input_interrupt(channel, true); + + InputCaptureFuture { + channel, + phantom: PhantomData, + } + } + + /// Asynchronously wait until the pin sees a rising edge. + pub async fn wait_for_rising_edge(&mut self, channel: Channel) -> u32 { + self.new_future(channel, InputCaptureMode::Rising, InputTISelection::Normal) + .await + } + + /// Asynchronously wait until the pin sees a falling edge. + pub async fn wait_for_falling_edge(&mut self, channel: Channel) -> u32 { + self.new_future(channel, InputCaptureMode::Falling, InputTISelection::Normal) + .await + } + + /// Asynchronously wait until the pin sees any edge. + pub async fn wait_for_any_edge(&mut self, channel: Channel) -> u32 { + self.new_future(channel, InputCaptureMode::BothEdges, InputTISelection::Normal) + .await + } + + /// Asynchronously wait until the (alternate) pin sees a rising edge. + pub async fn wait_for_rising_edge_alternate(&mut self, channel: Channel) -> u32 { + self.new_future(channel, InputCaptureMode::Rising, InputTISelection::Alternate) + .await + } + + /// Asynchronously wait until the (alternate) pin sees a falling edge. + pub async fn wait_for_falling_edge_alternate(&mut self, channel: Channel) -> u32 { + self.new_future(channel, InputCaptureMode::Falling, InputTISelection::Alternate) + .await + } + + /// Asynchronously wait until the (alternate) pin sees any edge. + pub async fn wait_for_any_edge_alternate(&mut self, channel: Channel) -> u32 { + self.new_future(channel, InputCaptureMode::BothEdges, InputTISelection::Alternate) + .await + } +} + +#[must_use = "futures do nothing unless you `.await` or poll them"] +struct InputCaptureFuture { + channel: Channel, + phantom: PhantomData, +} + +impl Drop for InputCaptureFuture { + fn drop(&mut self) { + critical_section::with(|_| { + let regs = unsafe { crate::pac::timer::TimGp16::from_ptr(T::regs()) }; + + // disable interrupt enable + regs.dier().modify(|w| w.set_ccie(self.channel.index(), false)); + }); + } +} + +impl Future for InputCaptureFuture { + type Output = u32; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + T::state().cc_waker[self.channel.index()].register(cx.waker()); + + let regs = unsafe { crate::pac::timer::TimGp16::from_ptr(T::regs()) }; + + let dier = regs.dier().read(); + if !dier.ccie(self.channel.index()) { + let val = regs.ccr(self.channel.index()).read().0; + Poll::Ready(val) + } else { + Poll::Pending + } + } +} diff --git a/embassy-stm32/src/timer/low_level.rs b/embassy-stm32/src/timer/low_level.rs index aa73986ea..7f533b75c 100644 --- a/embassy-stm32/src/timer/low_level.rs +++ b/embassy-stm32/src/timer/low_level.rs @@ -448,6 +448,11 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> { self.regs_gp16().sr().modify(|r| r.set_ccif(channel.index(), false)); } + /// Get input interrupt. + pub fn get_input_interrupt(&self, channel: Channel) -> bool { + self.regs_gp16().sr().read().ccif(channel.index()) + } + /// Enable input interrupt. pub fn enable_input_interrupt(&self, channel: Channel, enable: bool) { self.regs_gp16().dier().modify(|r| r.set_ccie(channel.index(), enable)); diff --git a/embassy-stm32/src/timer/mod.rs b/embassy-stm32/src/timer/mod.rs index 346127005..314b6006b 100644 --- a/embassy-stm32/src/timer/mod.rs +++ b/embassy-stm32/src/timer/mod.rs @@ -1,7 +1,12 @@ //! Timers, PWM, quadrature decoder. +use core::marker::PhantomData; + +use embassy_sync::waitqueue::AtomicWaker; + #[cfg(not(stm32l0))] pub mod complementary_pwm; +pub mod input_capture; pub mod low_level; pub mod qei; pub mod simple_pwm; @@ -45,8 +50,29 @@ pub enum TimerBits { Bits32, } +struct State { + up_waker: AtomicWaker, + cc_waker: [AtomicWaker; 4], +} + +impl State { + const fn new() -> Self { + const NEW_AW: AtomicWaker = AtomicWaker::new(); + Self { + up_waker: NEW_AW, + cc_waker: [NEW_AW; 4], + } + } +} + +trait SealedInstance: RccPeripheral { + /// Async state for this timer + fn state() -> &'static State; +} + /// Core timer instance. -pub trait CoreInstance: RccPeripheral + 'static { +#[allow(private_bounds)] +pub trait CoreInstance: SealedInstance + 'static { /// Update Interrupt for this timer. type UpdateInterrupt: interrupt::typelevel::Interrupt; @@ -143,6 +169,13 @@ dma_trait!(Ch4Dma, GeneralInstance4Channel); #[allow(unused)] macro_rules! impl_core_timer { ($inst:ident, $bits:expr) => { + impl SealedInstance for crate::peripherals::$inst { + fn state() -> &'static State { + static STATE: State = State::new(); + &STATE + } + } + impl CoreInstance for crate::peripherals::$inst { type UpdateInterrupt = crate::_generated::peripheral_interrupts::$inst::UP; @@ -285,3 +318,63 @@ foreach_interrupt! { impl AdvancedInstance4Channel for crate::peripherals::$inst {} }; } + +/// Update interrupt handler. +pub struct UpdateInterruptHandler { + _phantom: PhantomData, +} + +impl interrupt::typelevel::Handler for UpdateInterruptHandler { + unsafe fn on_interrupt() { + #[cfg(feature = "low-power")] + crate::low_power::on_wakeup_irq(); + + let regs = crate::pac::timer::TimCore::from_ptr(T::regs()); + + // Read TIM interrupt flags. + let sr = regs.sr().read(); + + // Mask relevant interrupts (UIE). + let bits = sr.0 & 0x00000001; + + // Mask all the channels that fired. + regs.dier().modify(|w| w.0 &= !bits); + + // Wake the tasks + if sr.uif() { + T::state().up_waker.wake(); + } + } +} + +/// Capture/Compare interrupt handler. +pub struct CaptureCompareInterruptHandler { + _phantom: PhantomData, +} + +impl interrupt::typelevel::Handler + for CaptureCompareInterruptHandler +{ + unsafe fn on_interrupt() { + #[cfg(feature = "low-power")] + crate::low_power::on_wakeup_irq(); + + let regs = crate::pac::timer::TimGp16::from_ptr(T::regs()); + + // Read TIM interrupt flags. + let sr = regs.sr().read(); + + // Mask relevant interrupts (CCIE). + let bits = sr.0 & 0x0000001E; + + // Mask all the channels that fired. + regs.dier().modify(|w| w.0 &= !bits); + + // Wake the tasks + for ch in 0..4 { + if sr.ccif(ch) { + T::state().cc_waker[ch].wake(); + } + } + } +} diff --git a/embassy-stm32/src/tsc/enums.rs b/embassy-stm32/src/tsc/enums.rs new file mode 100644 index 000000000..0d34a43ec --- /dev/null +++ b/embassy-stm32/src/tsc/enums.rs @@ -0,0 +1,238 @@ +use core::ops::BitOr; + +/// Pin defines +#[allow(missing_docs)] +pub enum TscIOPin { + Group1Io1, + Group1Io2, + Group1Io3, + Group1Io4, + Group2Io1, + Group2Io2, + Group2Io3, + Group2Io4, + Group3Io1, + Group3Io2, + Group3Io3, + Group3Io4, + Group4Io1, + Group4Io2, + Group4Io3, + Group4Io4, + Group5Io1, + Group5Io2, + Group5Io3, + Group5Io4, + Group6Io1, + Group6Io2, + Group6Io3, + Group6Io4, + #[cfg(any(tsc_v2, tsc_v3))] + Group7Io1, + #[cfg(any(tsc_v2, tsc_v3))] + Group7Io2, + #[cfg(any(tsc_v2, tsc_v3))] + Group7Io3, + #[cfg(any(tsc_v2, tsc_v3))] + Group7Io4, + #[cfg(tsc_v3)] + Group8Io1, + #[cfg(tsc_v3)] + Group8Io2, + #[cfg(tsc_v3)] + Group8Io3, + #[cfg(tsc_v3)] + Group8Io4, +} + +impl BitOr for u32 { + type Output = u32; + fn bitor(self, rhs: TscIOPin) -> Self::Output { + let rhs: u32 = rhs.into(); + self | rhs + } +} + +impl BitOr for TscIOPin { + type Output = u32; + fn bitor(self, rhs: u32) -> Self::Output { + let val: u32 = self.into(); + val | rhs + } +} + +impl BitOr for TscIOPin { + type Output = u32; + fn bitor(self, rhs: Self) -> Self::Output { + let val: u32 = self.into(); + let rhs: u32 = rhs.into(); + val | rhs + } +} + +impl Into for TscIOPin { + fn into(self) -> u32 { + match self { + TscIOPin::Group1Io1 => 0x00000001, + TscIOPin::Group1Io2 => 0x00000002, + TscIOPin::Group1Io3 => 0x00000004, + TscIOPin::Group1Io4 => 0x00000008, + TscIOPin::Group2Io1 => 0x00000010, + TscIOPin::Group2Io2 => 0x00000020, + TscIOPin::Group2Io3 => 0x00000040, + TscIOPin::Group2Io4 => 0x00000080, + TscIOPin::Group3Io1 => 0x00000100, + TscIOPin::Group3Io2 => 0x00000200, + TscIOPin::Group3Io3 => 0x00000400, + TscIOPin::Group3Io4 => 0x00000800, + TscIOPin::Group4Io1 => 0x00001000, + TscIOPin::Group4Io2 => 0x00002000, + TscIOPin::Group4Io3 => 0x00004000, + TscIOPin::Group4Io4 => 0x00008000, + TscIOPin::Group5Io1 => 0x00010000, + TscIOPin::Group5Io2 => 0x00020000, + TscIOPin::Group5Io3 => 0x00040000, + TscIOPin::Group5Io4 => 0x00080000, + TscIOPin::Group6Io1 => 0x00100000, + TscIOPin::Group6Io2 => 0x00200000, + TscIOPin::Group6Io3 => 0x00400000, + TscIOPin::Group6Io4 => 0x00800000, + #[cfg(any(tsc_v2, tsc_v3))] + TscIOPin::Group7Io1 => 0x01000000, + #[cfg(any(tsc_v2, tsc_v3))] + TscIOPin::Group7Io2 => 0x02000000, + #[cfg(any(tsc_v2, tsc_v3))] + TscIOPin::Group7Io3 => 0x04000000, + #[cfg(any(tsc_v2, tsc_v3))] + TscIOPin::Group7Io4 => 0x08000000, + #[cfg(tsc_v3)] + TscIOPin::Group8Io1 => 0x10000000, + #[cfg(tsc_v3)] + TscIOPin::Group8Io2 => 0x20000000, + #[cfg(tsc_v3)] + TscIOPin::Group8Io3 => 0x40000000, + #[cfg(tsc_v3)] + TscIOPin::Group8Io4 => 0x80000000, + } + } +} + +/// Spread Spectrum Deviation +#[derive(Copy, Clone)] +pub struct SSDeviation(u8); +impl SSDeviation { + /// Create new deviation value, acceptable inputs are 1-128 + pub fn new(val: u8) -> Result { + if val == 0 || val > 128 { + return Err(()); + } + Ok(Self(val - 1)) + } +} + +impl Into for SSDeviation { + fn into(self) -> u8 { + self.0 + } +} + +/// Charge transfer pulse cycles +#[allow(missing_docs)] +#[derive(Copy, Clone, PartialEq)] +pub enum ChargeTransferPulseCycle { + _1, + _2, + _3, + _4, + _5, + _6, + _7, + _8, + _9, + _10, + _11, + _12, + _13, + _14, + _15, + _16, +} + +impl Into for ChargeTransferPulseCycle { + fn into(self) -> u8 { + match self { + ChargeTransferPulseCycle::_1 => 0, + ChargeTransferPulseCycle::_2 => 1, + ChargeTransferPulseCycle::_3 => 2, + ChargeTransferPulseCycle::_4 => 3, + ChargeTransferPulseCycle::_5 => 4, + ChargeTransferPulseCycle::_6 => 5, + ChargeTransferPulseCycle::_7 => 6, + ChargeTransferPulseCycle::_8 => 7, + ChargeTransferPulseCycle::_9 => 8, + ChargeTransferPulseCycle::_10 => 9, + ChargeTransferPulseCycle::_11 => 10, + ChargeTransferPulseCycle::_12 => 11, + ChargeTransferPulseCycle::_13 => 12, + ChargeTransferPulseCycle::_14 => 13, + ChargeTransferPulseCycle::_15 => 14, + ChargeTransferPulseCycle::_16 => 15, + } + } +} + +/// Prescaler divider +#[allow(missing_docs)] +#[derive(Copy, Clone, PartialEq)] +pub enum PGPrescalerDivider { + _1, + _2, + _4, + _8, + _16, + _32, + _64, + _128, +} + +impl Into for PGPrescalerDivider { + fn into(self) -> u8 { + match self { + PGPrescalerDivider::_1 => 0, + PGPrescalerDivider::_2 => 1, + PGPrescalerDivider::_4 => 2, + PGPrescalerDivider::_8 => 3, + PGPrescalerDivider::_16 => 4, + PGPrescalerDivider::_32 => 5, + PGPrescalerDivider::_64 => 6, + PGPrescalerDivider::_128 => 7, + } + } +} + +/// Max count +#[allow(missing_docs)] +#[derive(Copy, Clone)] +pub enum MaxCount { + _255, + _511, + _1023, + _2047, + _4095, + _8191, + _16383, +} + +impl Into for MaxCount { + fn into(self) -> u8 { + match self { + MaxCount::_255 => 0, + MaxCount::_511 => 1, + MaxCount::_1023 => 2, + MaxCount::_2047 => 3, + MaxCount::_4095 => 4, + MaxCount::_8191 => 5, + MaxCount::_16383 => 6, + } + } +} diff --git a/embassy-stm32/src/tsc/mod.rs b/embassy-stm32/src/tsc/mod.rs new file mode 100644 index 000000000..bf583f04c --- /dev/null +++ b/embassy-stm32/src/tsc/mod.rs @@ -0,0 +1,936 @@ +//! TSC Peripheral Interface +//! +//! +//! # Example (stm32) +//! ``` rust, ignore +//! +//! let mut device_config = embassy_stm32::Config::default(); +//! { +//! device_config.rcc.mux = ClockSrc::MSI(Msirange::RANGE_4MHZ); +//! } +//! +//! let context = embassy_stm32::init(device_config); +//! +//! let config = tsc::Config { +//! ct_pulse_high_length: ChargeTransferPulseCycle::_2, +//! ct_pulse_low_length: ChargeTransferPulseCycle::_2, +//! spread_spectrum: false, +//! spread_spectrum_deviation: SSDeviation::new(2).unwrap(), +//! spread_spectrum_prescaler: false, +//! pulse_generator_prescaler: PGPrescalerDivider::_4, +//! max_count_value: MaxCount::_8191, +//! io_default_mode: false, +//! synchro_pin_polarity: false, +//! acquisition_mode: false, +//! max_count_interrupt: false, +//! channel_ios: TscIOPin::Group2Io2 | TscIOPin::Group7Io3, +//! shield_ios: TscIOPin::Group1Io3.into(), +//! sampling_ios: TscIOPin::Group1Io2 | TscIOPin::Group2Io1 | TscIOPin::Group7Io2, +//! }; +//! +//! let mut g1: PinGroup = PinGroup::new(); +//! g1.set_io2(context.PB13, PinType::Sample); +//! g1.set_io3(context.PB14, PinType::Shield); +//! +//! let mut g2: PinGroup = PinGroup::new(); +//! g2.set_io1(context.PB4, PinType::Sample); +//! g2.set_io2(context.PB5, PinType::Channel); +//! +//! let mut g7: PinGroup = PinGroup::new(); +//! g7.set_io2(context.PE3, PinType::Sample); +//! g7.set_io3(context.PE4, PinType::Channel); +//! +//! let mut touch_controller = tsc::Tsc::new( +//! context.TSC, +//! Some(g1), +//! Some(g2), +//! None, +//! None, +//! None, +//! None, +//! Some(g7), +//! None, +//! config, +//! ); +//! +//! touch_controller.discharge_io(true); +//! Timer::after_millis(1).await; +//! +//! touch_controller.start(); +//! +//! ``` + +#![macro_use] + +/// Enums defined for peripheral parameters +pub mod enums; + +use core::marker::PhantomData; + +use embassy_hal_internal::{into_ref, PeripheralRef}; +pub use enums::*; + +use crate::gpio::{AFType, AnyPin}; +use crate::pac::tsc::Tsc as Regs; +use crate::rcc::RccPeripheral; +use crate::{peripherals, Peripheral}; + +#[cfg(tsc_v1)] +const TSC_NUM_GROUPS: u32 = 6; +#[cfg(tsc_v2)] +const TSC_NUM_GROUPS: u32 = 7; +#[cfg(tsc_v3)] +const TSC_NUM_GROUPS: u32 = 8; + +/// Error type defined for TSC +#[derive(Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum Error { + /// Test error for TSC + Test, +} + +/// Pin type definition to control IO parameters +pub enum PinType { + /// Sensing channel pin connected to an electrode + Channel, + /// Sampling capacitor pin, one required for every pin group + Sample, + /// Shield pin connected to capacitive sensing shield + Shield, +} + +/// Peripheral state +#[derive(PartialEq, Clone, Copy)] +pub enum State { + /// Peripheral is being setup or reconfigured + Reset, + /// Ready to start acquisition + Ready, + /// In process of sensor acquisition + Busy, + /// Error occured during acquisition + Error, +} + +/// Individual group status checked after acquisition reported as complete +/// For groups with multiple channel pins, may take longer because acquisitions +/// are done sequentially. Check this status before pulling count for each +/// sampled channel +#[derive(PartialEq)] +pub enum GroupStatus { + /// Acquisition for channel still in progress + Ongoing, + /// Acquisition either not started or complete + Complete, +} + +/// Group identifier used to interrogate status +#[allow(missing_docs)] +pub enum Group { + One, + Two, + Three, + Four, + Five, + Six, + #[cfg(any(tsc_v2, tsc_v3))] + Seven, + #[cfg(tsc_v3)] + Eight, +} + +impl Into for Group { + fn into(self) -> usize { + match self { + Group::One => 0, + Group::Two => 1, + Group::Three => 2, + Group::Four => 3, + Group::Five => 4, + Group::Six => 5, + #[cfg(any(tsc_v2, tsc_v3))] + Group::Seven => 6, + #[cfg(tsc_v3)] + Group::Eight => 7, + } + } +} + +/// Peripheral configuration +#[derive(Clone, Copy)] +pub struct Config { + /// Duration of high state of the charge transfer pulse + pub ct_pulse_high_length: ChargeTransferPulseCycle, + /// Duration of the low state of the charge transfer pulse + pub ct_pulse_low_length: ChargeTransferPulseCycle, + /// Enable/disable of spread spectrum feature + pub spread_spectrum: bool, + /// Adds variable number of periods of the SS clk to pulse high state + pub spread_spectrum_deviation: SSDeviation, + /// Selects AHB clock divider used to generate SS clk + pub spread_spectrum_prescaler: bool, + /// Selects AHB clock divider used to generate pulse generator clk + pub pulse_generator_prescaler: PGPrescalerDivider, + /// Maximum number of charge tranfer pulses that can be generated before error + pub max_count_value: MaxCount, + /// Defines config of all IOs when no ongoing acquisition + pub io_default_mode: bool, + /// Polarity of sync input pin + pub synchro_pin_polarity: bool, + /// Acquisition starts when start bit is set or with sync pin input + pub acquisition_mode: bool, + /// Enable max count interrupt + pub max_count_interrupt: bool, + /// Channel IO mask + pub channel_ios: u32, + /// Shield IO mask + pub shield_ios: u32, + /// Sampling IO mask + pub sampling_ios: u32, +} + +impl Default for Config { + fn default() -> Self { + Self { + ct_pulse_high_length: ChargeTransferPulseCycle::_1, + ct_pulse_low_length: ChargeTransferPulseCycle::_1, + spread_spectrum: false, + spread_spectrum_deviation: SSDeviation::new(1).unwrap(), + spread_spectrum_prescaler: false, + pulse_generator_prescaler: PGPrescalerDivider::_1, + max_count_value: MaxCount::_255, + io_default_mode: false, + synchro_pin_polarity: false, + acquisition_mode: false, + max_count_interrupt: false, + channel_ios: 0, + shield_ios: 0, + sampling_ios: 0, + } + } +} + +/// Pin struct that maintains usage +#[allow(missing_docs)] +pub struct TscPin<'d, T, C> { + _pin: PeripheralRef<'d, AnyPin>, + role: PinType, + phantom: PhantomData<(T, C)>, +} + +enum GroupError { + NoSample, + ChannelShield, +} + +/// Pin group definition +/// Pins are organized into groups of four IOs, all groups with a +/// sampling channel must also have a sampling capacitor channel. +#[allow(missing_docs)] +#[derive(Default)] +pub struct PinGroup<'d, T, C> { + d1: Option>, + d2: Option>, + d3: Option>, + d4: Option>, +} + +impl<'d, T: Instance, C> PinGroup<'d, T, C> { + /// Create new sensing group + pub fn new() -> Self { + Self { + d1: None, + d2: None, + d3: None, + d4: None, + } + } + + fn contains_shield(&self) -> bool { + let mut shield_count = 0; + + if let Some(pin) = &self.d1 { + if let PinType::Shield = pin.role { + shield_count += 1; + } + } + + if let Some(pin) = &self.d2 { + if let PinType::Shield = pin.role { + shield_count += 1; + } + } + + if let Some(pin) = &self.d3 { + if let PinType::Shield = pin.role { + shield_count += 1; + } + } + + if let Some(pin) = &self.d4 { + if let PinType::Shield = pin.role { + shield_count += 1; + } + } + + shield_count == 1 + } + + fn check_group(&self) -> Result<(), GroupError> { + let mut channel_count = 0; + let mut shield_count = 0; + let mut sample_count = 0; + if let Some(pin) = &self.d1 { + match pin.role { + PinType::Channel => { + channel_count += 1; + } + PinType::Shield => { + shield_count += 1; + } + PinType::Sample => { + sample_count += 1; + } + } + } + + if let Some(pin) = &self.d2 { + match pin.role { + PinType::Channel => { + channel_count += 1; + } + PinType::Shield => { + shield_count += 1; + } + PinType::Sample => { + sample_count += 1; + } + } + } + + if let Some(pin) = &self.d3 { + match pin.role { + PinType::Channel => { + channel_count += 1; + } + PinType::Shield => { + shield_count += 1; + } + PinType::Sample => { + sample_count += 1; + } + } + } + + if let Some(pin) = &self.d4 { + match pin.role { + PinType::Channel => { + channel_count += 1; + } + PinType::Shield => { + shield_count += 1; + } + PinType::Sample => { + sample_count += 1; + } + } + } + + // Every group requires one sampling capacitor + if sample_count != 1 { + return Err(GroupError::NoSample); + } + + // Each group must have at least one shield or channel IO + if shield_count == 0 && channel_count == 0 { + return Err(GroupError::ChannelShield); + } + + // Any group can either contain channel ios or a shield IO + if shield_count != 0 && channel_count != 0 { + return Err(GroupError::ChannelShield); + } + + // No more than one shield IO is allow per group and amongst all groups + if shield_count > 1 { + return Err(GroupError::ChannelShield); + } + + Ok(()) + } +} + +macro_rules! group_impl { + ($group:ident, $trait1:ident, $trait2:ident, $trait3:ident, $trait4:ident) => { + impl<'d, T: Instance> PinGroup<'d, T, $group> { + #[doc = concat!("Create a new pin1 for ", stringify!($group), " TSC group instance.")] + pub fn set_io1(&mut self, pin: impl Peripheral

> + 'd, role: PinType) { + into_ref!(pin); + critical_section::with(|_| { + pin.set_low(); + pin.set_as_af( + pin.af_num(), + match role { + PinType::Channel => AFType::OutputPushPull, + PinType::Sample => AFType::OutputOpenDrain, + PinType::Shield => AFType::OutputPushPull, + }, + ); + self.d1 = Some(TscPin { + _pin: pin.map_into(), + role: role, + phantom: PhantomData, + }) + }) + } + + #[doc = concat!("Create a new pin2 for ", stringify!($group), " TSC group instance.")] + pub fn set_io2(&mut self, pin: impl Peripheral

> + 'd, role: PinType) { + into_ref!(pin); + critical_section::with(|_| { + pin.set_low(); + pin.set_as_af( + pin.af_num(), + match role { + PinType::Channel => AFType::OutputPushPull, + PinType::Sample => AFType::OutputOpenDrain, + PinType::Shield => AFType::OutputPushPull, + }, + ); + self.d2 = Some(TscPin { + _pin: pin.map_into(), + role: role, + phantom: PhantomData, + }) + }) + } + + #[doc = concat!("Create a new pin3 for ", stringify!($group), " TSC group instance.")] + pub fn set_io3(&mut self, pin: impl Peripheral

> + 'd, role: PinType) { + into_ref!(pin); + critical_section::with(|_| { + pin.set_low(); + pin.set_as_af( + pin.af_num(), + match role { + PinType::Channel => AFType::OutputPushPull, + PinType::Sample => AFType::OutputOpenDrain, + PinType::Shield => AFType::OutputPushPull, + }, + ); + self.d3 = Some(TscPin { + _pin: pin.map_into(), + role: role, + phantom: PhantomData, + }) + }) + } + + #[doc = concat!("Create a new pin4 for ", stringify!($group), " TSC group instance.")] + pub fn set_io4(&mut self, pin: impl Peripheral

> + 'd, role: PinType) { + into_ref!(pin); + critical_section::with(|_| { + pin.set_low(); + pin.set_as_af( + pin.af_num(), + match role { + PinType::Channel => AFType::OutputPushPull, + PinType::Sample => AFType::OutputOpenDrain, + PinType::Shield => AFType::OutputPushPull, + }, + ); + self.d4 = Some(TscPin { + _pin: pin.map_into(), + role: role, + phantom: PhantomData, + }) + }) + } + } + }; +} + +group_impl!(G1, G1IO1Pin, G1IO2Pin, G1IO3Pin, G1IO4Pin); +group_impl!(G2, G2IO1Pin, G2IO2Pin, G2IO3Pin, G2IO4Pin); +group_impl!(G3, G3IO1Pin, G3IO2Pin, G3IO3Pin, G3IO4Pin); +group_impl!(G4, G4IO1Pin, G4IO2Pin, G4IO3Pin, G4IO4Pin); +group_impl!(G5, G5IO1Pin, G5IO2Pin, G5IO3Pin, G5IO4Pin); +group_impl!(G6, G6IO1Pin, G6IO2Pin, G6IO3Pin, G6IO4Pin); +group_impl!(G7, G7IO1Pin, G7IO2Pin, G7IO3Pin, G7IO4Pin); +group_impl!(G8, G8IO1Pin, G8IO2Pin, G8IO3Pin, G8IO4Pin); + +/// Group 1 marker type. +pub enum G1 {} +/// Group 2 marker type. +pub enum G2 {} +/// Group 3 marker type. +pub enum G3 {} +/// Group 4 marker type. +pub enum G4 {} +/// Group 5 marker type. +pub enum G5 {} +/// Group 6 marker type. +pub enum G6 {} +/// Group 7 marker type. +pub enum G7 {} +/// Group 8 marker type. +pub enum G8 {} + +/// TSC driver +pub struct Tsc<'d, T: Instance> { + _peri: PeripheralRef<'d, T>, + _g1: Option>, + _g2: Option>, + _g3: Option>, + _g4: Option>, + _g5: Option>, + _g6: Option>, + #[cfg(any(tsc_v2, tsc_v3))] + _g7: Option>, + #[cfg(tsc_v3)] + _g8: Option>, + state: State, + config: Config, +} + +impl<'d, T: Instance> Tsc<'d, T> { + /// Create new TSC driver + pub fn new( + peri: impl Peripheral

+ 'd, + g1: Option>, + g2: Option>, + g3: Option>, + g4: Option>, + g5: Option>, + g6: Option>, + #[cfg(any(tsc_v2, tsc_v3))] g7: Option>, + #[cfg(tsc_v3)] g8: Option>, + config: Config, + ) -> Self { + // Need to check valid pin configuration input + let g1 = g1.filter(|b| b.check_group().is_ok()); + let g2 = g2.filter(|b| b.check_group().is_ok()); + let g3 = g3.filter(|b| b.check_group().is_ok()); + let g4 = g4.filter(|b| b.check_group().is_ok()); + let g5 = g5.filter(|b| b.check_group().is_ok()); + let g6 = g6.filter(|b| b.check_group().is_ok()); + #[cfg(any(tsc_v2, tsc_v3))] + let g7 = g7.filter(|b| b.check_group().is_ok()); + #[cfg(tsc_v3)] + let g8 = g8.filter(|b| b.check_group().is_ok()); + + match Self::check_shields( + &g1, + &g2, + &g3, + &g4, + &g5, + &g6, + #[cfg(any(tsc_v2, tsc_v3))] + &g7, + #[cfg(tsc_v3)] + &g8, + ) { + Ok(()) => Self::new_inner( + peri, + g1, + g2, + g3, + g4, + g5, + g6, + #[cfg(any(tsc_v2, tsc_v3))] + g7, + #[cfg(tsc_v3)] + g8, + config, + ), + Err(_) => Self::new_inner( + peri, + None, + None, + None, + None, + None, + None, + #[cfg(any(tsc_v2, tsc_v3))] + None, + #[cfg(tsc_v3)] + None, + config, + ), + } + } + + fn check_shields( + g1: &Option>, + g2: &Option>, + g3: &Option>, + g4: &Option>, + g5: &Option>, + g6: &Option>, + #[cfg(any(tsc_v2, tsc_v3))] g7: &Option>, + #[cfg(tsc_v3)] g8: &Option>, + ) -> Result<(), GroupError> { + let mut shield_count = 0; + + if let Some(pin_group) = g1 { + if pin_group.contains_shield() { + shield_count += 1; + } + }; + if let Some(pin_group) = g2 { + if pin_group.contains_shield() { + shield_count += 1; + } + }; + if let Some(pin_group) = g3 { + if pin_group.contains_shield() { + shield_count += 1; + } + }; + if let Some(pin_group) = g4 { + if pin_group.contains_shield() { + shield_count += 1; + } + }; + if let Some(pin_group) = g5 { + if pin_group.contains_shield() { + shield_count += 1; + } + }; + if let Some(pin_group) = g6 { + if pin_group.contains_shield() { + shield_count += 1; + } + }; + #[cfg(any(tsc_v2, tsc_v3))] + if let Some(pin_group) = g7 { + if pin_group.contains_shield() { + shield_count += 1; + } + }; + #[cfg(tsc_v3)] + if let Some(pin_group) = g8 { + if pin_group.contains_shield() { + shield_count += 1; + } + }; + + if shield_count > 1 { + return Err(GroupError::ChannelShield); + } + + Ok(()) + } + + fn extract_groups(io_mask: u32) -> u32 { + let mut groups: u32 = 0; + for idx in 0..TSC_NUM_GROUPS { + if io_mask & (0x0F << idx * 4) != 0 { + groups |= 1 << idx + } + } + groups + } + + fn new_inner( + peri: impl Peripheral

+ 'd, + g1: Option>, + g2: Option>, + g3: Option>, + g4: Option>, + g5: Option>, + g6: Option>, + #[cfg(any(tsc_v2, tsc_v3))] g7: Option>, + #[cfg(tsc_v3)] g8: Option>, + config: Config, + ) -> Self { + into_ref!(peri); + + T::enable_and_reset(); + + T::REGS.cr().modify(|w| { + w.set_tsce(true); + w.set_ctph(config.ct_pulse_high_length.into()); + w.set_ctpl(config.ct_pulse_low_length.into()); + w.set_sse(config.spread_spectrum); + // Prevent invalid configuration for pulse generator prescaler + if config.ct_pulse_low_length == ChargeTransferPulseCycle::_1 + && (config.pulse_generator_prescaler == PGPrescalerDivider::_1 + || config.pulse_generator_prescaler == PGPrescalerDivider::_2) + { + w.set_pgpsc(PGPrescalerDivider::_4.into()); + } else if config.ct_pulse_low_length == ChargeTransferPulseCycle::_2 + && config.pulse_generator_prescaler == PGPrescalerDivider::_1 + { + w.set_pgpsc(PGPrescalerDivider::_2.into()); + } else { + w.set_pgpsc(config.pulse_generator_prescaler.into()); + } + w.set_ssd(config.spread_spectrum_deviation.into()); + w.set_sspsc(config.spread_spectrum_prescaler); + + w.set_mcv(config.max_count_value.into()); + w.set_syncpol(config.synchro_pin_polarity); + w.set_am(config.acquisition_mode); + }); + + // Set IO configuration + // Disable Schmitt trigger hysteresis on all used TSC IOs + T::REGS + .iohcr() + .write(|w| w.0 = !(config.channel_ios | config.shield_ios | config.sampling_ios)); + + // Set channel and shield IOs + T::REGS.ioccr().write(|w| w.0 = config.channel_ios | config.shield_ios); + + // Set sampling IOs + T::REGS.ioscr().write(|w| w.0 = config.sampling_ios); + + // Set the groups to be acquired + T::REGS + .iogcsr() + .write(|w| w.0 = Self::extract_groups(config.channel_ios)); + + // Disable interrupts + T::REGS.ier().modify(|w| { + w.set_eoaie(false); + w.set_mceie(false); + }); + + // Clear flags + T::REGS.icr().modify(|w| { + w.set_eoaic(true); + w.set_mceic(true); + }); + + Self { + _peri: peri, + _g1: g1, + _g2: g2, + _g3: g3, + _g4: g4, + _g5: g5, + _g6: g6, + #[cfg(any(tsc_v2, tsc_v3))] + _g7: g7, + #[cfg(tsc_v3)] + _g8: g8, + state: State::Ready, + config, + } + } + + /// Start charge transfer acquisition + pub fn start(&mut self) { + self.state = State::Busy; + + // Disable interrupts + T::REGS.ier().modify(|w| { + w.set_eoaie(false); + w.set_mceie(false); + }); + + // Clear flags + T::REGS.icr().modify(|w| { + w.set_eoaic(true); + w.set_mceic(true); + }); + + // Set the touch sensing IOs not acquired to the default mode + T::REGS.cr().modify(|w| { + w.set_iodef(self.config.io_default_mode); + }); + + // Start the acquisition + T::REGS.cr().modify(|w| { + w.set_start(true); + }); + } + + /// Start charge transfer acquisition with interrupts enabled + pub fn start_it(&mut self) { + self.state = State::Busy; + + // Enable interrupts + T::REGS.ier().modify(|w| { + w.set_eoaie(true); + w.set_mceie(self.config.max_count_interrupt); + }); + + // Clear flags + T::REGS.icr().modify(|w| { + w.set_eoaic(true); + w.set_mceic(true); + }); + + // Set the touch sensing IOs not acquired to the default mode + T::REGS.cr().modify(|w| { + w.set_iodef(self.config.io_default_mode); + }); + + // Start the acquisition + T::REGS.cr().modify(|w| { + w.set_start(true); + }); + } + + /// Stop charge transfer acquisition + pub fn stop(&mut self) { + T::REGS.cr().modify(|w| { + w.set_start(false); + }); + + // Set the touch sensing IOs in low power mode + T::REGS.cr().modify(|w| { + w.set_iodef(false); + }); + + // Clear flags + T::REGS.icr().modify(|w| { + w.set_eoaic(true); + w.set_mceic(true); + }); + + self.state = State::Ready; + } + + /// Stop charge transfer acquisition and clear interrupts + pub fn stop_it(&mut self) { + T::REGS.cr().modify(|w| { + w.set_start(false); + }); + + // Set the touch sensing IOs in low power mode + T::REGS.cr().modify(|w| { + w.set_iodef(false); + }); + + // Disable interrupts + T::REGS.ier().modify(|w| { + w.set_eoaie(false); + w.set_mceie(false); + }); + + // Clear flags + T::REGS.icr().modify(|w| { + w.set_eoaic(true); + w.set_mceic(true); + }); + + self.state = State::Ready; + } + + /// Wait for end of acquisition + pub fn poll_for_acquisition(&mut self) { + while self.get_state() == State::Busy {} + } + + /// Get current state of acquisition + pub fn get_state(&mut self) -> State { + if self.state == State::Busy { + if T::REGS.isr().read().eoaf() { + if T::REGS.isr().read().mcef() { + self.state = State::Error + } else { + self.state = State::Ready + } + } + } + self.state + } + + /// Get the individual group status to check acquisition complete + pub fn group_get_status(&mut self, index: Group) -> GroupStatus { + // Status bits are set by hardware when the acquisition on the corresponding + // enabled analog IO group is complete, cleared when new acquisition is started + let status = match index { + Group::One => T::REGS.iogcsr().read().g1s(), + Group::Two => T::REGS.iogcsr().read().g2s(), + Group::Three => T::REGS.iogcsr().read().g3s(), + Group::Four => T::REGS.iogcsr().read().g4s(), + Group::Five => T::REGS.iogcsr().read().g5s(), + Group::Six => T::REGS.iogcsr().read().g6s(), + #[cfg(any(tsc_v2, tsc_v3))] + Group::Seven => T::REGS.iogcsr().read().g7s(), + #[cfg(tsc_v3)] + Group::Eight => T::REGS.iogcsr().read().g8s(), + }; + match status { + true => GroupStatus::Complete, + false => GroupStatus::Ongoing, + } + } + + /// Get the count for the acquisiton, valid once group status is set + pub fn group_get_value(&mut self, index: Group) -> u16 { + T::REGS.iogcr(index.into()).read().cnt() + } + + /// Discharge the IOs for subsequent acquisition + pub fn discharge_io(&mut self, status: bool) { + // Set the touch sensing IOs in low power mode + T::REGS.cr().modify(|w| { + w.set_iodef(!status); + }); + } +} + +impl<'d, T: Instance> Drop for Tsc<'d, T> { + fn drop(&mut self) { + T::disable(); + } +} + +pub(crate) trait SealedInstance { + const REGS: Regs; +} + +/// TSC instance trait +#[allow(private_bounds)] +pub trait Instance: Peripheral

+ SealedInstance + RccPeripheral {} + +foreach_peripheral!( + (tsc, $inst:ident) => { + impl SealedInstance for peripherals::$inst { + const REGS: Regs = crate::pac::$inst; + } + + impl Instance for peripherals::$inst {} + }; +); + +pin_trait!(G1IO1Pin, Instance); +pin_trait!(G1IO2Pin, Instance); +pin_trait!(G1IO3Pin, Instance); +pin_trait!(G1IO4Pin, Instance); +pin_trait!(G2IO1Pin, Instance); +pin_trait!(G2IO2Pin, Instance); +pin_trait!(G2IO3Pin, Instance); +pin_trait!(G2IO4Pin, Instance); +pin_trait!(G3IO1Pin, Instance); +pin_trait!(G3IO2Pin, Instance); +pin_trait!(G3IO3Pin, Instance); +pin_trait!(G3IO4Pin, Instance); +pin_trait!(G4IO1Pin, Instance); +pin_trait!(G4IO2Pin, Instance); +pin_trait!(G4IO3Pin, Instance); +pin_trait!(G4IO4Pin, Instance); +pin_trait!(G5IO1Pin, Instance); +pin_trait!(G5IO2Pin, Instance); +pin_trait!(G5IO3Pin, Instance); +pin_trait!(G5IO4Pin, Instance); +pin_trait!(G6IO1Pin, Instance); +pin_trait!(G6IO2Pin, Instance); +pin_trait!(G6IO3Pin, Instance); +pin_trait!(G6IO4Pin, Instance); +pin_trait!(G7IO1Pin, Instance); +pin_trait!(G7IO2Pin, Instance); +pin_trait!(G7IO3Pin, Instance); +pin_trait!(G7IO4Pin, Instance); +pin_trait!(G8IO1Pin, Instance); +pin_trait!(G8IO2Pin, Instance); +pin_trait!(G8IO3Pin, Instance); +pin_trait!(G8IO4Pin, Instance); diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index 68899bfff..a6dfbd482 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -359,16 +359,32 @@ impl<'d, T: BasicInstance> UartTx<'d, T, Async> { /// Initiate an asynchronous UART write pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> { + let r = T::regs(); + + // Disable Receiver for Half-Duplex mode + if r.cr3().read().hdsel() { + r.cr1().modify(|reg| reg.set_re(false)); + } + let ch = self.tx_dma.as_mut().unwrap(); - T::regs().cr3().modify(|reg| { + r.cr3().modify(|reg| { reg.set_dmat(true); }); // If we don't assign future to a variable, the data register pointer // is held across an await and makes the future non-Send. - let transfer = unsafe { ch.write(buffer, tdr(T::regs()), Default::default()) }; + let transfer = unsafe { ch.write(buffer, tdr(r), Default::default()) }; transfer.await; Ok(()) } + + async fn flush_inner() -> Result<(), Error> { + Self::blocking_flush_inner() + } + + /// Wait until transmission complete + pub async fn flush(&mut self) -> Result<(), Error> { + Self::flush_inner().await + } } impl<'d, T: BasicInstance> UartTx<'d, T, Blocking> { @@ -436,6 +452,12 @@ impl<'d, T: BasicInstance, M: Mode> UartTx<'d, T, M> { /// Perform a blocking UART write pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> { let r = T::regs(); + + // Disable Receiver for Half-Duplex mode + if r.cr3().read().hdsel() { + r.cr1().modify(|reg| reg.set_re(false)); + } + for &b in buffer { while !sr(r).read().txe() {} unsafe { tdr(r).write_volatile(b) }; @@ -443,12 +465,21 @@ impl<'d, T: BasicInstance, M: Mode> UartTx<'d, T, M> { Ok(()) } - /// Block until transmission complete - pub fn blocking_flush(&mut self) -> Result<(), Error> { + fn blocking_flush_inner() -> Result<(), Error> { let r = T::regs(); while !sr(r).read().tc() {} + + // Enable Receiver after transmission complete for Half-Duplex mode + if r.cr3().read().hdsel() { + r.cr1().modify(|reg| reg.set_re(true)); + } Ok(()) } + + /// Block until transmission complete + pub fn blocking_flush(&mut self) -> Result<(), Error> { + Self::blocking_flush_inner() + } } impl<'d, T: BasicInstance> UartRx<'d, T, Async> { @@ -502,6 +533,11 @@ impl<'d, T: BasicInstance> UartRx<'d, T, Async> { ) -> Result { let r = T::regs(); + // Call flush for Half-Duplex mode. It prevents reading of bytes which have just been written. + if r.cr3().read().hdsel() { + UartTx::<'d, T, Async>::flush_inner().await?; + } + // make sure USART state is restored to neutral state when this future is dropped let on_drop = OnDrop::new(move || { // defmt::trace!("Clear all USART interrupts and DMA Read Request"); @@ -825,6 +861,12 @@ impl<'d, T: BasicInstance, M: Mode> UartRx<'d, T, M> { /// Perform a blocking read into `buffer` pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { let r = T::regs(); + + // Call flush for Half-Duplex mode. It prevents reading of bytes which have just been written. + if r.cr3().read().hdsel() { + UartTx::<'d, T, M>::blocking_flush_inner()?; + } + for b in buffer { while !self.check_rx_flags()? {} unsafe { *b = rdr(r).read_volatile() } diff --git a/embassy-stm32/src/usart/ringbuffered.rs b/embassy-stm32/src/usart/ringbuffered.rs index 7ab6cbda7..0a6491bd5 100644 --- a/embassy-stm32/src/usart/ringbuffered.rs +++ b/embassy-stm32/src/usart/ringbuffered.rs @@ -72,9 +72,8 @@ impl<'d, T: BasicInstance> RingBufferedUartRx<'d, T> { Err(err) } - /// Cleanly stop and reconfigure the driver + /// Reconfigure the driver pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> { - self.teardown_uart(); reconfigure::(config) } diff --git a/embassy-sync/CHANGELOG.md b/embassy-sync/CHANGELOG.md index 3f6b39d8b..e5c453ce2 100644 --- a/embassy-sync/CHANGELOG.md +++ b/embassy-sync/CHANGELOG.md @@ -7,7 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased -- Add `len`, `is_empty` and `is_full` functions to `Channel`. +- Add `capacity`, `free_capacity`, `clear`, `len`, `is_empty` and `is_full` functions to `Channel`. +- Add `capacity`, `free_capacity`, `clear`, `len`, `is_empty` and `is_full` functions to `PriorityChannel`. +- Add `capacity`, `free_capacity`, `clear`, `len`, `is_empty` and `is_full` functions to `PubSubChannel`. +- Made `PubSubBehavior` sealed + - If you called `.publish_immediate(...)` on the queue directly before, then now call `.immediate_publisher().publish_immediate(...)` ## 0.5.0 - 2023-12-04 diff --git a/embassy-sync/src/channel.rs b/embassy-sync/src/channel.rs index c4267064c..55ac5fb66 100644 --- a/embassy-sync/src/channel.rs +++ b/embassy-sync/src/channel.rs @@ -42,7 +42,7 @@ where M: RawMutex, { fn clone(&self) -> Self { - Sender { channel: self.channel } + *self } } @@ -81,7 +81,7 @@ pub struct DynamicSender<'ch, T> { impl<'ch, T> Clone for DynamicSender<'ch, T> { fn clone(&self) -> Self { - DynamicSender { channel: self.channel } + *self } } @@ -135,7 +135,7 @@ where M: RawMutex, { fn clone(&self) -> Self { - Receiver { channel: self.channel } + *self } } @@ -188,7 +188,7 @@ pub struct DynamicReceiver<'ch, T> { impl<'ch, T> Clone for DynamicReceiver<'ch, T> { fn clone(&self) -> Self { - DynamicReceiver { channel: self.channel } + *self } } @@ -477,6 +477,10 @@ impl ChannelState { } } + fn clear(&mut self) { + self.queue.clear(); + } + fn len(&self) -> usize { self.queue.len() } @@ -620,6 +624,23 @@ where self.lock(|c| c.try_receive()) } + /// Returns the maximum number of elements the channel can hold. + pub const fn capacity(&self) -> usize { + N + } + + /// Returns the free capacity of the channel. + /// + /// This is equivalent to `capacity() - len()` + pub fn free_capacity(&self) -> usize { + N - self.len() + } + + /// Clears all elements in the channel. + pub fn clear(&self) { + self.lock(|c| c.clear()); + } + /// Returns the number of elements currently in the channel. pub fn len(&self) -> usize { self.lock(|c| c.len()) diff --git a/embassy-sync/src/once_lock.rs b/embassy-sync/src/once_lock.rs index 9332ecfaf..55608ba32 100644 --- a/embassy-sync/src/once_lock.rs +++ b/embassy-sync/src/once_lock.rs @@ -1,4 +1,4 @@ -//! Syncronization primitive for initializing a value once, allowing others to await a reference to the value. +//! Synchronization primitive for initializing a value once, allowing others to await a reference to the value. use core::cell::Cell; use core::future::poll_fn; @@ -78,7 +78,7 @@ impl OnceLock { /// Set the underlying value. If the value is already set, this will return an error with the given value. pub fn init(&self, value: T) -> Result<(), T> { // Critical section is required to ensure that the value is - // not simultaniously initialized elsewhere at the same time. + // not simultaneously initialized elsewhere at the same time. critical_section::with(|_| { // If the value is not set, set it and return Ok. if !self.init.load(Ordering::Relaxed) { @@ -99,7 +99,7 @@ impl OnceLock { F: FnOnce() -> T, { // Critical section is required to ensure that the value is - // not simultaniously initialized elsewhere at the same time. + // not simultaneously initialized elsewhere at the same time. critical_section::with(|_| { // If the value is not set, set it. if !self.init.load(Ordering::Relaxed) { diff --git a/embassy-sync/src/pipe.rs b/embassy-sync/src/pipe.rs index 42fe8ebd0..cd5b8ed75 100644 --- a/embassy-sync/src/pipe.rs +++ b/embassy-sync/src/pipe.rs @@ -25,7 +25,7 @@ where M: RawMutex, { fn clone(&self) -> Self { - Writer { pipe: self.pipe } + *self } } diff --git a/embassy-sync/src/priority_channel.rs b/embassy-sync/src/priority_channel.rs index e77678c24..24c6c5a7f 100644 --- a/embassy-sync/src/priority_channel.rs +++ b/embassy-sync/src/priority_channel.rs @@ -33,7 +33,7 @@ where M: RawMutex, { fn clone(&self) -> Self { - Sender { channel: self.channel } + *self } } @@ -101,7 +101,7 @@ where M: RawMutex, { fn clone(&self) -> Self { - Receiver { channel: self.channel } + *self } } @@ -314,6 +314,22 @@ where Poll::Pending } } + + fn clear(&mut self) { + self.queue.clear(); + } + + fn len(&self) -> usize { + self.queue.len() + } + + fn is_empty(&self) -> bool { + self.queue.is_empty() + } + + fn is_full(&self) -> bool { + self.queue.len() == self.queue.capacity() + } } /// A bounded channel for communicating between asynchronous tasks @@ -323,7 +339,7 @@ where /// buffer is full, attempts to `send` new messages will wait until a message is /// received from the channel. /// -/// Sent data may be reordered based on their priorty within the channel. +/// Sent data may be reordered based on their priority within the channel. /// For example, in a [`Max`](heapless::binary_heap::Max) [`PriorityChannel`] /// containing `u32`'s, data sent in the following order `[1, 2, 3]` will be received as `[3, 2, 1]`. pub struct PriorityChannel @@ -433,6 +449,38 @@ where pub fn try_receive(&self) -> Result { self.lock(|c| c.try_receive()) } + + /// Returns the maximum number of elements the channel can hold. + pub const fn capacity(&self) -> usize { + N + } + + /// Returns the free capacity of the channel. + /// + /// This is equivalent to `capacity() - len()` + pub fn free_capacity(&self) -> usize { + N - self.len() + } + + /// Clears all elements in the channel. + pub fn clear(&self) { + self.lock(|c| c.clear()); + } + + /// Returns the number of elements currently in the channel. + pub fn len(&self) -> usize { + self.lock(|c| c.len()) + } + + /// Returns whether the channel is empty. + pub fn is_empty(&self) -> bool { + self.lock(|c| c.is_empty()) + } + + /// Returns whether the channel is full. + pub fn is_full(&self) -> bool { + self.lock(|c| c.is_full()) + } } /// Implements the DynamicChannel to allow creating types that are unaware of the queue size with the diff --git a/embassy-sync/src/pubsub/mod.rs b/embassy-sync/src/pubsub/mod.rs index 6afd54af5..66c9b0017 100644 --- a/embassy-sync/src/pubsub/mod.rs +++ b/embassy-sync/src/pubsub/mod.rs @@ -160,9 +160,41 @@ impl DynImmediatePublisher { DynImmediatePublisher(ImmediatePub::new(self)) } + + /// Returns the maximum number of elements the channel can hold. + pub const fn capacity(&self) -> usize { + CAP + } + + /// Returns the free capacity of the channel. + /// + /// This is equivalent to `capacity() - len()` + pub fn free_capacity(&self) -> usize { + CAP - self.len() + } + + /// Clears all elements in the channel. + pub fn clear(&self) { + self.inner.lock(|inner| inner.borrow_mut().clear()); + } + + /// Returns the number of elements currently in the channel. + pub fn len(&self) -> usize { + self.inner.lock(|inner| inner.borrow().len()) + } + + /// Returns whether the channel is empty. + pub fn is_empty(&self) -> bool { + self.inner.lock(|inner| inner.borrow().is_empty()) + } + + /// Returns whether the channel is full. + pub fn is_full(&self) -> bool { + self.inner.lock(|inner| inner.borrow().is_full()) + } } -impl PubSubBehavior +impl SealedPubSubBehavior for PubSubChannel { fn get_message_with_context(&self, next_message_id: &mut u64, cx: Option<&mut Context<'_>>) -> Poll> { @@ -221,13 +253,6 @@ impl usize { - self.inner.lock(|s| { - let s = s.borrow(); - s.queue.capacity() - s.queue.len() - }) - } - fn unregister_subscriber(&self, subscriber_next_message_id: u64) { self.inner.lock(|s| { let mut s = s.borrow_mut(); @@ -241,6 +266,30 @@ impl usize { + self.capacity() + } + + fn free_capacity(&self) -> usize { + self.free_capacity() + } + + fn clear(&self) { + self.clear(); + } + + fn len(&self) -> usize { + self.len() + } + + fn is_empty(&self) -> bool { + self.is_empty() + } + + fn is_full(&self) -> bool { + self.is_full() + } } /// Internal state for the PubSub channel @@ -366,6 +415,22 @@ impl PubSubSta fn unregister_publisher(&mut self) { self.publisher_count -= 1; } + + fn clear(&mut self) { + self.queue.clear(); + } + + fn len(&self) -> usize { + self.queue.len() + } + + fn is_empty(&self) -> bool { + self.queue.is_empty() + } + + fn is_full(&self) -> bool { + self.queue.is_full() + } } /// Error type for the [PubSubChannel] @@ -382,10 +447,10 @@ pub enum Error { /// 'Middle level' behaviour of the pubsub channel. /// This trait is used so that Sub and Pub can be generic over the channel. -pub trait PubSubBehavior { +trait SealedPubSubBehavior { /// Try to get a message from the queue with the given message id. /// - /// If the message is not yet present and a context is given, then its waker is registered in the subsriber wakers. + /// If the message is not yet present and a context is given, then its waker is registered in the subscriber wakers. fn get_message_with_context(&self, next_message_id: &mut u64, cx: Option<&mut Context<'_>>) -> Poll>; /// Get the amount of messages that are between the given the next_message_id and the most recent message. @@ -400,8 +465,25 @@ pub trait PubSubBehavior { /// Publish a message immediately fn publish_immediate(&self, message: T); - /// The amount of messages that can still be published without having to wait or without having to lag the subscribers - fn space(&self) -> usize; + /// Returns the maximum number of elements the channel can hold. + fn capacity(&self) -> usize; + + /// Returns the free capacity of the channel. + /// + /// This is equivalent to `capacity() - len()` + fn free_capacity(&self) -> usize; + + /// Clears all elements in the channel. + fn clear(&self); + + /// Returns the number of elements currently in the channel. + fn len(&self) -> usize; + + /// Returns whether the channel is empty. + fn is_empty(&self) -> bool; + + /// Returns whether the channel is full. + fn is_full(&self) -> bool; /// Let the channel know that a subscriber has dropped fn unregister_subscriber(&self, subscriber_next_message_id: u64); @@ -410,6 +492,13 @@ pub trait PubSubBehavior { fn unregister_publisher(&self); } +/// 'Middle level' behaviour of the pubsub channel. +/// This trait is used so that Sub and Pub can be generic over the channel. +#[allow(private_bounds)] +pub trait PubSubBehavior: SealedPubSubBehavior {} + +impl> PubSubBehavior for C {} + /// The result of the subscriber wait procedure #[derive(Debug, Clone, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -542,6 +631,7 @@ mod tests { assert_eq!(pub0.try_publish(0), Ok(())); assert_eq!(pub0.try_publish(0), Ok(())); assert_eq!(pub0.try_publish(0), Ok(())); + assert!(pub0.is_full()); assert_eq!(pub0.try_publish(0), Err(0)); drop(sub0); @@ -574,32 +664,42 @@ mod tests { } #[futures_test::test] - async fn correct_space() { + async fn correct_len() { let channel = PubSubChannel::::new(); let mut sub0 = channel.subscriber().unwrap(); let mut sub1 = channel.subscriber().unwrap(); let pub0 = channel.publisher().unwrap(); - assert_eq!(pub0.space(), 4); + assert!(sub0.is_empty()); + assert!(sub1.is_empty()); + assert!(pub0.is_empty()); + assert_eq!(pub0.free_capacity(), 4); + assert_eq!(pub0.len(), 0); pub0.publish(42).await; - assert_eq!(pub0.space(), 3); + assert_eq!(pub0.free_capacity(), 3); + assert_eq!(pub0.len(), 1); pub0.publish(42).await; - assert_eq!(pub0.space(), 2); + assert_eq!(pub0.free_capacity(), 2); + assert_eq!(pub0.len(), 2); sub0.next_message().await; sub0.next_message().await; - assert_eq!(pub0.space(), 2); + assert_eq!(pub0.free_capacity(), 2); + assert_eq!(pub0.len(), 2); sub1.next_message().await; - assert_eq!(pub0.space(), 3); + assert_eq!(pub0.free_capacity(), 3); + assert_eq!(pub0.len(), 1); + sub1.next_message().await; - assert_eq!(pub0.space(), 4); + assert_eq!(pub0.free_capacity(), 4); + assert_eq!(pub0.len(), 0); } #[futures_test::test] @@ -610,29 +710,29 @@ mod tests { let mut sub0 = channel.subscriber().unwrap(); let mut sub1 = channel.subscriber().unwrap(); - assert_eq!(4, pub0.space()); + assert_eq!(4, pub0.free_capacity()); pub0.publish(1).await; pub0.publish(2).await; - assert_eq!(2, channel.space()); + assert_eq!(2, channel.free_capacity()); assert_eq!(1, sub0.try_next_message_pure().unwrap()); assert_eq!(2, sub0.try_next_message_pure().unwrap()); - assert_eq!(2, channel.space()); + assert_eq!(2, channel.free_capacity()); drop(sub0); - assert_eq!(2, channel.space()); + assert_eq!(2, channel.free_capacity()); assert_eq!(1, sub1.try_next_message_pure().unwrap()); - assert_eq!(3, channel.space()); + assert_eq!(3, channel.free_capacity()); drop(sub1); - assert_eq!(4, channel.space()); + assert_eq!(4, channel.free_capacity()); } struct CloneCallCounter(usize); diff --git a/embassy-sync/src/pubsub/publisher.rs b/embassy-sync/src/pubsub/publisher.rs index e1edc9eb9..e66b3b1db 100644 --- a/embassy-sync/src/pubsub/publisher.rs +++ b/embassy-sync/src/pubsub/publisher.rs @@ -43,12 +43,36 @@ impl<'a, PSB: PubSubBehavior + ?Sized, T: Clone> Pub<'a, PSB, T> { self.channel.publish_with_context(message, None) } - /// The amount of messages that can still be published without having to wait or without having to lag the subscribers + /// Returns the maximum number of elements the ***channel*** can hold. + pub fn capacity(&self) -> usize { + self.channel.capacity() + } + + /// Returns the free capacity of the ***channel***. /// - /// *Note: In the time between checking this and a publish action, other publishers may have had time to publish something. - /// So checking doesn't give any guarantees.* - pub fn space(&self) -> usize { - self.channel.space() + /// This is equivalent to `capacity() - len()` + pub fn free_capacity(&self) -> usize { + self.channel.free_capacity() + } + + /// Clears all elements in the ***channel***. + pub fn clear(&self) { + self.channel.clear(); + } + + /// Returns the number of elements currently in the ***channel***. + pub fn len(&self) -> usize { + self.channel.len() + } + + /// Returns whether the ***channel*** is empty. + pub fn is_empty(&self) -> bool { + self.channel.is_empty() + } + + /// Returns whether the ***channel*** is full. + pub fn is_full(&self) -> bool { + self.channel.is_full() } } @@ -124,12 +148,36 @@ impl<'a, PSB: PubSubBehavior + ?Sized, T: Clone> ImmediatePub<'a, PSB, T> { self.channel.publish_with_context(message, None) } - /// The amount of messages that can still be published without having to wait or without having to lag the subscribers + /// Returns the maximum number of elements the ***channel*** can hold. + pub fn capacity(&self) -> usize { + self.channel.capacity() + } + + /// Returns the free capacity of the ***channel***. /// - /// *Note: In the time between checking this and a publish action, other publishers may have had time to publish something. - /// So checking doesn't give any guarantees.* - pub fn space(&self) -> usize { - self.channel.space() + /// This is equivalent to `capacity() - len()` + pub fn free_capacity(&self) -> usize { + self.channel.free_capacity() + } + + /// Clears all elements in the ***channel***. + pub fn clear(&self) { + self.channel.clear(); + } + + /// Returns the number of elements currently in the ***channel***. + pub fn len(&self) -> usize { + self.channel.len() + } + + /// Returns whether the ***channel*** is empty. + pub fn is_empty(&self) -> bool { + self.channel.is_empty() + } + + /// Returns whether the ***channel*** is full. + pub fn is_full(&self) -> bool { + self.channel.is_full() } } diff --git a/embassy-sync/src/pubsub/subscriber.rs b/embassy-sync/src/pubsub/subscriber.rs index f420a75f0..6ad660cb3 100644 --- a/embassy-sync/src/pubsub/subscriber.rs +++ b/embassy-sync/src/pubsub/subscriber.rs @@ -65,10 +65,44 @@ impl<'a, PSB: PubSubBehavior + ?Sized, T: Clone> Sub<'a, PSB, T> { } } - /// The amount of messages this subscriber hasn't received yet + /// The amount of messages this subscriber hasn't received yet. This is like [Self::len] but specifically + /// for this subscriber. pub fn available(&self) -> u64 { self.channel.available(self.next_message_id) } + + /// Returns the maximum number of elements the ***channel*** can hold. + pub fn capacity(&self) -> usize { + self.channel.capacity() + } + + /// Returns the free capacity of the ***channel***. + /// + /// This is equivalent to `capacity() - len()` + pub fn free_capacity(&self) -> usize { + self.channel.free_capacity() + } + + /// Clears all elements in the ***channel***. + pub fn clear(&self) { + self.channel.clear(); + } + + /// Returns the number of elements currently in the ***channel***. + /// See [Self::available] for how many messages are available for this subscriber. + pub fn len(&self) -> usize { + self.channel.len() + } + + /// Returns whether the ***channel*** is empty. + pub fn is_empty(&self) -> bool { + self.channel.is_empty() + } + + /// Returns whether the ***channel*** is full. + pub fn is_full(&self) -> bool { + self.channel.is_full() + } } impl<'a, PSB: PubSubBehavior + ?Sized, T: Clone> Drop for Sub<'a, PSB, T> { diff --git a/embassy-sync/src/waitqueue/multi_waker.rs b/embassy-sync/src/waitqueue/multi_waker.rs index 824d192da..0e520bf40 100644 --- a/embassy-sync/src/waitqueue/multi_waker.rs +++ b/embassy-sync/src/waitqueue/multi_waker.rs @@ -14,7 +14,7 @@ impl MultiWakerRegistration { } /// Register a waker. If the buffer is full the function returns it in the error - pub fn register<'a>(&mut self, w: &'a Waker) { + pub fn register(&mut self, w: &Waker) { // If we already have some waker that wakes the same task as `w`, do nothing. // This avoids cloning wakers, and avoids unnecessary mass-wakes. for w2 in &self.wakers { diff --git a/embassy-usb-logger/CHANGELOG.md b/embassy-usb-logger/CHANGELOG.md new file mode 100644 index 000000000..4cd84b8be --- /dev/null +++ b/embassy-usb-logger/CHANGELOG.md @@ -0,0 +1,28 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## Unreleased + +## 0.2.0 - 2024-05-20 + +### Added + +- [#2414](https://github.com/embassy-rs/embassy/pull/2414) USB logger can now use an existing USB device (@JomerDev) + +### Changed + +- Update `embassy-usb` to 0.2.0 + +### Fixed + +- No more data loss at `Pipe` wraparound +- [#2414](https://github.com/embassy-rs/embassy/pull/2414) Messages that are exactly `MAX_PACKET_SIZE` long are no +longer delayed (@JomerDev) + +## 0.1.0 - 2024-01-14 + +- Initial Release diff --git a/embassy-usb-logger/Cargo.toml b/embassy-usb-logger/Cargo.toml index 81bcfc0da..d58fd7a34 100644 --- a/embassy-usb-logger/Cargo.toml +++ b/embassy-usb-logger/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "embassy-usb-logger" -version = "0.1.0" +version = "0.2.0" edition = "2021" license = "MIT OR Apache-2.0" description = "`log` implementation for USB serial using `embassy-usb`." diff --git a/embassy-usb-synopsys-otg/src/lib.rs b/embassy-usb-synopsys-otg/src/lib.rs index 42cdfb6e1..b90e059f6 100644 --- a/embassy-usb-synopsys-otg/src/lib.rs +++ b/embassy-usb-synopsys-otg/src/lib.rs @@ -221,6 +221,12 @@ struct EpState { out_size: AtomicU16, } +// SAFETY: The EndpointAllocator ensures that the buffer points to valid memory exclusive for each endpoint and is +// large enough to hold the maximum packet size. Access to the buffer is synchronized between the USB interrupt and the +// EndpointOut impl using the out_size atomic variable. +unsafe impl Send for EpState {} +unsafe impl Sync for EpState {} + struct ControlPipeSetupState { /// Holds received SETUP packets. Available if [Ep0State::setup_ready] is true. setup_data: UnsafeCell<[u8; 8]>, @@ -287,11 +293,22 @@ pub struct Config { /// If you set this to true, you must connect VBUS to PA9 for FS, PB13 for HS, possibly with a /// voltage divider. See ST application note AN4879 and the reference manual for more details. pub vbus_detection: bool, + + /// Enable transceiver delay. + /// + /// Some ULPI PHYs like the Microchip USB334x series require a delay between the ULPI register write that initiates + /// the HS Chirp and the subsequent transmit command, otherwise the HS Chirp does not get executed and the deivce + /// enumerates in FS mode. Some USB Link IP like those in the STM32H7 series support adding this delay to work with + /// the affected PHYs. + pub xcvrdly: bool, } impl Default for Config { fn default() -> Self { - Self { vbus_detection: false } + Self { + vbus_detection: false, + xcvrdly: false, + } } } @@ -575,6 +592,9 @@ impl<'d, const MAX_EP_COUNT: usize> Bus<'d, MAX_EP_COUNT> { r.dcfg().write(|w| { w.set_pfivl(vals::Pfivl::FRAME_INTERVAL_80); w.set_dspd(phy_type.to_dspd()); + if self.config.xcvrdly { + w.set_xcvrdly(true); + } }); // Unmask transfer complete EP interrupt @@ -1034,7 +1054,7 @@ impl<'d> embassy_usb_driver::EndpointOut for Endpoint<'d, Out> { return Poll::Ready(Err(EndpointError::BufferOverflow)); } - // SAFETY: exclusive access ensured by `ep_out_size` atomic variable + // SAFETY: exclusive access ensured by `out_size` atomic variable let data = unsafe { core::slice::from_raw_parts(*self.state.out_buffer.get(), len as usize) }; buf[..len as usize].copy_from_slice(data); diff --git a/embassy-usb/CHANGELOG.md b/embassy-usb/CHANGELOG.md index 00f3be06c..5f665ed25 100644 --- a/embassy-usb/CHANGELOG.md +++ b/embassy-usb/CHANGELOG.md @@ -7,8 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased -## 0.2.0 - 2024-04-23 +## 0.2.0 - 2024-05-20 +- [#2862](https://github.com/embassy-rs/embassy/pull/2862) WebUSB implementation by @chmanie - Removed dynamically sized `device_descriptor` fields ## 0.1.0 - 2024-01-11 diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index 73d19c28b..5178a690f 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -15,7 +15,7 @@ embassy-usb = { version = "0.2.0", path = "../../embassy-usb", features = ["defm embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "tcp", "udp", "raw", "dhcpv4", "medium-ethernet"] } embassy-net-wiznet = { version = "0.1.0", path = "../../embassy-net-wiznet", features = ["defmt"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } -embassy-usb-logger = { version = "0.1.0", path = "../../embassy-usb-logger" } +embassy-usb-logger = { version = "0.2.0", path = "../../embassy-usb-logger" } cyw43 = { version = "0.1.0", path = "../../cyw43", features = ["defmt", "firmware-logs"] } cyw43-pio = { version = "0.1.0", path = "../../cyw43-pio", features = ["defmt", "overclock"] } diff --git a/examples/rp/src/bin/interrupt.rs b/examples/rp/src/bin/interrupt.rs index d334d35d7..5b9d7027e 100644 --- a/examples/rp/src/bin/interrupt.rs +++ b/examples/rp/src/bin/interrupt.rs @@ -15,7 +15,6 @@ use embassy_executor::Spawner; use embassy_rp::adc::{self, Adc, Blocking}; use embassy_rp::gpio::Pull; use embassy_rp::interrupt; -use embassy_rp::peripherals::PWM_SLICE4; use embassy_rp::pwm::{Config, Pwm}; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::blocking_mutex::Mutex; @@ -26,7 +25,7 @@ use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; static COUNTER: AtomicU32 = AtomicU32::new(0); -static PWM: Mutex>>> = Mutex::new(RefCell::new(None)); +static PWM: Mutex>> = Mutex::new(RefCell::new(None)); static ADC: Mutex, adc::Channel)>>> = Mutex::new(RefCell::new(None)); static ADC_VALUES: Channel = Channel::new(); diff --git a/examples/rp/src/bin/uart_r503.rs b/examples/rp/src/bin/uart_r503.rs new file mode 100644 index 000000000..085be280b --- /dev/null +++ b/examples/rp/src/bin/uart_r503.rs @@ -0,0 +1,158 @@ +#![no_std] +#![no_main] + +use defmt::{debug, error, info}; +use embassy_executor::Spawner; +use embassy_rp::bind_interrupts; +use embassy_rp::peripherals::UART0; +use embassy_rp::uart::{Config, DataBits, InterruptHandler as UARTInterruptHandler, Parity, StopBits, Uart}; +use embassy_time::{with_timeout, Duration, Timer}; +use heapless::Vec; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(pub struct Irqs { + UART0_IRQ => UARTInterruptHandler; +}); + +const START: u16 = 0xEF01; +const ADDRESS: u32 = 0xFFFFFFFF; + +// ================================================================================ + +// Data package format +// Name Length Description +// ========================================================================================================== +// Start 2 bytes Fixed value of 0xEF01; High byte transferred first. +// Address 4 bytes Default value is 0xFFFFFFFF, which can be modified by command. +// High byte transferred first and at wrong adder value, module +// will reject to transfer. +// PID 1 byte 01H Command packet; +// 02H Data packet; Data packet shall not appear alone in executing +// processs, must follow command packet or acknowledge packet. +// 07H Acknowledge packet; +// 08H End of Data packet. +// LENGTH 2 bytes Refers to the length of package content (command packets and data packets) +// plus the length of Checksum (2 bytes). Unit is byte. Max length is 256 bytes. +// And high byte is transferred first. +// DATA - It can be commands, data, command’s parameters, acknowledge result, etc. +// (fingerprint character value, template are all deemed as data); +// SUM 2 bytes The arithmetic sum of package identifier, package length and all package +// contens. Overflowing bits are omitted. high byte is transferred first. + +// ================================================================================ + +// Checksum is calculated on 'length (2 bytes) + data (??)'. +fn compute_checksum(buf: Vec) -> u16 { + let mut checksum = 0u16; + + let check_end = buf.len(); + let checked_bytes = &buf[6..check_end]; + for byte in checked_bytes { + checksum += (*byte) as u16; + } + return checksum; +} + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + info!("Start"); + + let p = embassy_rp::init(Default::default()); + + // Initialize the fingerprint scanner. + let mut config = Config::default(); + config.baudrate = 57600; + config.stop_bits = StopBits::STOP1; + config.data_bits = DataBits::DataBits8; + config.parity = Parity::ParityNone; + + let (uart, tx_pin, tx_dma, rx_pin, rx_dma) = (p.UART0, p.PIN_16, p.DMA_CH0, p.PIN_17, p.DMA_CH1); + let uart = Uart::new(uart, tx_pin, rx_pin, Irqs, tx_dma, rx_dma, config); + let (mut tx, mut rx) = uart.split(); + + let mut vec_buf: Vec = heapless::Vec::new(); + let mut data: Vec = heapless::Vec::new(); + + let mut speeds: Vec = heapless::Vec::new(); + let _ = speeds.push(0xC8); // Slow + let _ = speeds.push(0x20); // Medium + let _ = speeds.push(0x02); // Fast + + // Cycle through the three colours Red, Blue and Purple forever. + loop { + for colour in 1..=3 { + for speed in &speeds { + // Set the data first, because the length is dependent on that. + // However, we write the length bits before we do the data. + data.clear(); + let _ = data.push(0x01); // ctrl=Breathing light + let _ = data.push(*speed); + let _ = data.push(colour as u8); // colour=Red, Blue, Purple + let _ = data.push(0x00); // times=Infinite + + // Clear buffers + vec_buf.clear(); + + // START + let _ = vec_buf.extend_from_slice(&START.to_be_bytes()[..]); + + // ADDRESS + let _ = vec_buf.extend_from_slice(&ADDRESS.to_be_bytes()[..]); + + // PID + let _ = vec_buf.extend_from_slice(&[0x01]); + + // LENGTH + let len: u16 = (1 + data.len() + 2).try_into().unwrap(); + let _ = vec_buf.extend_from_slice(&len.to_be_bytes()[..]); + + // COMMAND + let _ = vec_buf.push(0x35); // Command: AuraLedConfig + + // DATA + let _ = vec_buf.extend_from_slice(&data); + + // SUM + let chk = compute_checksum(vec_buf.clone()); + let _ = vec_buf.extend_from_slice(&chk.to_be_bytes()[..]); + + // ===== + + // Send command buffer. + let data_write: [u8; 16] = vec_buf.clone().into_array().unwrap(); + debug!(" write='{:?}'", data_write[..]); + match tx.write(&data_write).await { + Ok(..) => info!("Write successful."), + Err(e) => error!("Write error: {:?}", e), + } + + // ===== + + // Read command buffer. + let mut read_buf: [u8; 1] = [0; 1]; // Can only read one byte at a time! + let mut data_read: Vec = heapless::Vec::new(); // Save buffer. + + info!("Attempting read."); + loop { + // Some commands, like `Img2Tz()` needs longer, but we hard-code this to 200ms + // for this command. + match with_timeout(Duration::from_millis(200), rx.read(&mut read_buf)).await { + Ok(..) => { + // Extract and save read byte. + debug!(" r='{=u8:#04x}H' ({:03}D)", read_buf[0], read_buf[0]); + let _ = data_read.push(read_buf[0]).unwrap(); + } + Err(..) => break, // TimeoutError -> Ignore. + } + } + info!("Read successful"); + debug!(" read='{:?}'", data_read[..]); + + Timer::after_secs(3).await; + info!("Changing speed."); + } + + info!("Changing colour."); + } + } +} diff --git a/examples/stm32f4/Cargo.toml b/examples/stm32f4/Cargo.toml index 64ac50818..1eb1ae6db 100644 --- a/examples/stm32f4/Cargo.toml +++ b/examples/stm32f4/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] # Change stm32f429zi to your chip name, if necessary. -embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32f429zi", "unstable-pac", "memory-x", "time-driver-any", "exti", "chrono"] } +embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32f429zi", "unstable-pac", "memory-x", "time-driver-any", "exti", "chrono"] } embassy-sync = { version = "0.5.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.5.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } diff --git a/examples/stm32f4/src/bin/input_capture.rs b/examples/stm32f4/src/bin/input_capture.rs new file mode 100644 index 000000000..49de33d2b --- /dev/null +++ b/examples/stm32f4/src/bin/input_capture.rs @@ -0,0 +1,52 @@ +#![no_std] +#![no_main] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::gpio::{Level, Output, Pull, Speed}; +use embassy_stm32::time::khz; +use embassy_stm32::timer::input_capture::{CapturePin, InputCapture}; +use embassy_stm32::timer::{self, Channel}; +use embassy_stm32::{bind_interrupts, peripherals}; +use embassy_time::Timer; +use {defmt_rtt as _, panic_probe as _}; + +/// Connect PB2 and PB10 with a 1k Ohm resistor + +#[embassy_executor::task] +async fn blinky(led: peripherals::PB2) { + let mut led = Output::new(led, Level::High, Speed::Low); + + loop { + info!("high"); + led.set_high(); + Timer::after_millis(300).await; + + info!("low"); + led.set_low(); + Timer::after_millis(300).await; + } +} + +bind_interrupts!(struct Irqs { + TIM2 => timer::CaptureCompareInterruptHandler; +}); + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + let p = embassy_stm32::init(Default::default()); + info!("Hello World!"); + + unwrap!(spawner.spawn(blinky(p.PB2))); + + let ch3 = CapturePin::new_ch3(p.PB10, Pull::None); + let mut ic = InputCapture::new(p.TIM2, None, None, Some(ch3), None, Irqs, khz(1000), Default::default()); + + loop { + info!("wait for risign edge"); + ic.wait_for_rising_edge(Channel::Ch3).await; + + let capture_value = ic.get_capture_value(Channel::Ch3); + info!("new capture! {}", capture_value); + } +} diff --git a/examples/stm32f469/.cargo/config.toml b/examples/stm32f469/.cargo/config.toml new file mode 100644 index 000000000..05250954f --- /dev/null +++ b/examples/stm32f469/.cargo/config.toml @@ -0,0 +1,9 @@ +[target.'cfg(all(target_arch = "arm", target_os = "none"))'] +# replace STM32F429ZITx with your chip as listed in `probe-rs chip list` +runner = "probe-rs run --chip STM32F469NIHx" + +[build] +target = "thumbv7em-none-eabi" + +[env] +DEFMT_LOG = "trace" diff --git a/examples/stm32f469/Cargo.toml b/examples/stm32f469/Cargo.toml new file mode 100644 index 000000000..7718a46c1 --- /dev/null +++ b/examples/stm32f469/Cargo.toml @@ -0,0 +1,22 @@ +[package] +edition = "2021" +name = "embassy-stm32f469-examples" +version = "0.1.0" +license = "MIT OR Apache-2.0" + +[dependencies] +# Specific examples only for stm32f469 +embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32f469ni", "unstable-pac", "memory-x", "time-driver-any", "exti", "chrono"] } +embassy-executor = { version = "0.5.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } +embassy-time = { version = "0.3.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } + +defmt = "0.3" +defmt-rtt = "0.4" + +cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } +cortex-m-rt = "0.7.0" +embedded-hal = "1.0.0" +panic-probe = { version = "0.3", features = ["print-defmt"] } + +[profile.release] +debug = 2 diff --git a/examples/stm32f469/build.rs b/examples/stm32f469/build.rs new file mode 100644 index 000000000..8cd32d7ed --- /dev/null +++ b/examples/stm32f469/build.rs @@ -0,0 +1,5 @@ +fn main() { + println!("cargo:rustc-link-arg-bins=--nmagic"); + println!("cargo:rustc-link-arg-bins=-Tlink.x"); + println!("cargo:rustc-link-arg-bins=-Tdefmt.x"); +} diff --git a/examples/stm32f469/src/bin/dsi_bsp.rs b/examples/stm32f469/src/bin/dsi_bsp.rs new file mode 100644 index 000000000..e4e9e9c01 --- /dev/null +++ b/examples/stm32f469/src/bin/dsi_bsp.rs @@ -0,0 +1,694 @@ +#![no_std] +#![no_main] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::dsihost::{blocking_delay_ms, DsiHost, PacketType}; +use embassy_stm32::gpio::{Level, Output, Speed}; +use embassy_stm32::ltdc::Ltdc; +use embassy_stm32::pac::dsihost::regs::{Ier0, Ier1}; +use embassy_stm32::pac::ltdc::vals::{Bf1, Bf2, Depol, Hspol, Imr, Pcpol, Pf, Vspol}; +use embassy_stm32::pac::{DSIHOST, LTDC}; +use embassy_stm32::rcc::{ + AHBPrescaler, APBPrescaler, Hse, HseMode, Pll, PllMul, PllPDiv, PllPreDiv, PllQDiv, PllRDiv, PllSource, Sysclk, +}; +use embassy_stm32::time::mhz; +use embassy_time::Timer; +use {defmt_rtt as _, panic_probe as _}; + +enum _Orientation { + Landscape, + Portrait, +} + +const _LCD_ORIENTATION: _Orientation = _Orientation::Landscape; +const LCD_X_SIZE: u16 = 800; +const LCD_Y_SIZE: u16 = 480; + +static FERRIS_IMAGE: &[u8; 1536000] = include_bytes!("ferris.bin"); + +// This example allows to display an image on the STM32F469NI-DISCO boards +// with the Revision C, that is at least the boards marked DK32F469I$AU1. +// These boards have the NT35510 display driver. This example does not work +// for the older revisions with OTM8009A, though there are lots of C-examples +// available online where the correct config for the OTM8009A could be gotten from. +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let mut config = embassy_stm32::Config::default(); + config.rcc.sys = Sysclk::PLL1_P; + config.rcc.ahb_pre = AHBPrescaler::DIV1; + config.rcc.apb1_pre = APBPrescaler::DIV4; + config.rcc.apb2_pre = APBPrescaler::DIV2; + + // HSE is on and ready + config.rcc.hse = Some(Hse { + freq: mhz(8), + mode: HseMode::Oscillator, + }); + config.rcc.pll_src = PllSource::HSE; + + config.rcc.pll = Some(Pll { + prediv: PllPreDiv::DIV8, // PLLM + mul: PllMul::MUL360, // PLLN + divp: Some(PllPDiv::DIV2), + divq: Some(PllQDiv::DIV7), // was DIV4, but STM BSP example uses 7 + divr: Some(PllRDiv::DIV6), + }); + + // This seems to be working, the values in the RCC.PLLSAICFGR are correct according to the debugger. Also on and ready according to CR + config.rcc.pllsai = Some(Pll { + prediv: PllPreDiv::DIV8, // Actually ignored + mul: PllMul::MUL384, // PLLN + divp: None, // PLLP + divq: None, // PLLQ + divr: Some(PllRDiv::DIV7), // PLLR (Sai actually has special clockdiv register) + }); + + let p = embassy_stm32::init(config); + info!("Starting..."); + + let mut led = Output::new(p.PG6, Level::High, Speed::Low); + + // According to UM for the discovery kit, PH7 is an active-low reset for the LCD and touchsensor + let mut reset = Output::new(p.PH7, Level::Low, Speed::High); + + // CubeMX example waits 20 ms before de-asserting reset + embassy_time::block_for(embassy_time::Duration::from_millis(20)); + + // Disable the reset signal and wait 140ms as in the Linux driver (CubeMX waits only 20) + reset.set_high(); + embassy_time::block_for(embassy_time::Duration::from_millis(140)); + + let mut ltdc = Ltdc::new(p.LTDC); + let mut dsi = DsiHost::new(p.DSIHOST, p.PJ2); + let version = dsi.get_version(); + defmt::warn!("en: {:x}", version); + + // Disable the DSI wrapper + dsi.disable_wrapper_dsi(); + + // Disable the DSI host + dsi.disable(); + + // D-PHY clock and digital disable + DSIHOST.pctlr().modify(|w| { + w.set_cke(false); + w.set_den(false) + }); + + // Turn off the DSI PLL + DSIHOST.wrpcr().modify(|w| w.set_pllen(false)); + + // Disable the regulator + DSIHOST.wrpcr().write(|w| w.set_regen(false)); + + // Enable regulator + info!("DSIHOST: enabling regulator"); + DSIHOST.wrpcr().write(|w| w.set_regen(true)); + + for _ in 1..1000 { + // The regulator status (ready or not) can be monitored with the RRS flag in the DSI_WISR register. + // Once it is set, we stop waiting. + if DSIHOST.wisr().read().rrs() { + info!("DSIHOST Regulator ready"); + break; + } + embassy_time::block_for(embassy_time::Duration::from_millis(1)); + } + + if !DSIHOST.wisr().read().rrs() { + defmt::panic!("DSIHOST: enabling regulator FAILED"); + } + + // Set up PLL and enable it + DSIHOST.wrpcr().modify(|w| { + w.set_pllen(true); + w.set_ndiv(125); // PLL loop division factor set to 125 + w.set_idf(2); // PLL input divided by 2 + w.set_odf(0); // PLL output divided by 1 + }); + + /* 500 MHz / 8 = 62.5 MHz = 62500 kHz */ + const LANE_BYTE_CLK_K_HZ: u16 = 62500; // https://github.com/STMicroelectronics/32f469idiscovery-bsp/blob/ec051de2bff3e1b73a9ccd49c9b85abf7320add9/stm32469i_discovery_lcd.c#L224C21-L224C26 + + const _LCD_CLOCK: u16 = 27429; // https://github.com/STMicroelectronics/32f469idiscovery-bsp/blob/ec051de2bff3e1b73a9ccd49c9b85abf7320add9/stm32469i_discovery_lcd.c#L183 + + /* TX_ESCAPE_CKDIV = f(LaneByteClk)/15.62 = 4 */ + const TX_ESCAPE_CKDIV: u8 = (LANE_BYTE_CLK_K_HZ / 15620) as u8; // https://github.com/STMicroelectronics/32f469idiscovery-bsp/blob/ec051de2bff3e1b73a9ccd49c9b85abf7320add9/stm32469i_discovery_lcd.c#L230 + + for _ in 1..1000 { + embassy_time::block_for(embassy_time::Duration::from_millis(1)); + // The PLL status (lock or unlock) can be monitored with the PLLLS flag in the DSI_WISR register. + // Once it is set, we stop waiting. + if DSIHOST.wisr().read().pllls() { + info!("DSIHOST PLL locked"); + break; + } + } + + if !DSIHOST.wisr().read().pllls() { + defmt::panic!("DSIHOST: enabling PLL FAILED"); + } + + // Set the PHY parameters + + // D-PHY clock and digital enable + DSIHOST.pctlr().write(|w| { + w.set_cke(true); + w.set_den(true); + }); + + // Set Clock lane to high-speed mode and disable automatic clock lane control + DSIHOST.clcr().modify(|w| { + w.set_dpcc(true); + w.set_acr(false); + }); + + // Set number of active data lanes to two (lanes 0 and 1) + DSIHOST.pconfr().modify(|w| w.set_nl(1)); + + // Set the DSI clock parameters + + // Set the TX escape clock division factor to 4 + DSIHOST.ccr().modify(|w| w.set_txeckdiv(TX_ESCAPE_CKDIV)); + + // Calculate the bit period in high-speed mode in unit of 0.25 ns (UIX4) + // The equation is : UIX4 = IntegerPart( (1000/F_PHY_Mhz) * 4 ) + // Where : F_PHY_Mhz = (NDIV * HSE_Mhz) / (IDF * ODF) + // Set the bit period in high-speed mode + DSIHOST.wpcr0().modify(|w| w.set_uix4(8)); // 8 is set in the BSP example (confirmed with Debugger) + + // Disable all error interrupts and reset the Error Mask + DSIHOST.ier0().write_value(Ier0(0)); + DSIHOST.ier1().write_value(Ier1(0)); + + // Enable this to fix read timeout + DSIHOST.pcr().modify(|w| w.set_btae(true)); + + const DSI_PIXEL_FORMAT_RGB888: u8 = 0x05; + const _DSI_PIXEL_FORMAT_ARGB888: u8 = 0x00; + + const HACT: u16 = LCD_X_SIZE; + const VACT: u16 = LCD_Y_SIZE; + + const VSA: u16 = 120; + const VBP: u16 = 150; + const VFP: u16 = 150; + const HSA: u16 = 2; + const HBP: u16 = 34; + const HFP: u16 = 34; + + const VIRTUAL_CHANNEL_ID: u8 = 0; + + const COLOR_CODING: u8 = DSI_PIXEL_FORMAT_RGB888; + const VS_POLARITY: bool = false; // DSI_VSYNC_ACTIVE_HIGH == 0 + const HS_POLARITY: bool = false; // DSI_HSYNC_ACTIVE_HIGH == 0 + const DE_POLARITY: bool = false; // DSI_DATA_ENABLE_ACTIVE_HIGH == 0 + const MODE: u8 = 2; // DSI_VID_MODE_BURST; /* Mode Video burst ie : one LgP per line */ + const NULL_PACKET_SIZE: u16 = 0xFFF; + const NUMBER_OF_CHUNKS: u16 = 0; + const PACKET_SIZE: u16 = HACT; /* Value depending on display orientation choice portrait/landscape */ + const HORIZONTAL_SYNC_ACTIVE: u16 = 4; // ((HSA as u32 * LANE_BYTE_CLK_K_HZ as u32 ) / LCD_CLOCK as u32 ) as u16; + const HORIZONTAL_BACK_PORCH: u16 = 77; //((HBP as u32 * LANE_BYTE_CLK_K_HZ as u32 ) / LCD_CLOCK as u32) as u16; + const HORIZONTAL_LINE: u16 = 1982; //(((HACT + HSA + HBP + HFP) as u32 * LANE_BYTE_CLK_K_HZ as u32 ) / LCD_CLOCK as u32 ) as u16; /* Value depending on display orientation choice portrait/landscape */ + // FIXME: Make depend on orientation + const VERTICAL_SYNC_ACTIVE: u16 = VSA; + const VERTICAL_BACK_PORCH: u16 = VBP; + const VERTICAL_FRONT_PORCH: u16 = VFP; + const VERTICAL_ACTIVE: u16 = VACT; + const LP_COMMAND_ENABLE: bool = true; /* Enable sending commands in mode LP (Low Power) */ + + /* Largest packet size possible to transmit in LP mode in VSA, VBP, VFP regions */ + /* Only useful when sending LP packets is allowed while streaming is active in video mode */ + const LP_LARGEST_PACKET_SIZE: u8 = 16; + + /* Largest packet size possible to transmit in LP mode in HFP region during VACT period */ + /* Only useful when sending LP packets is allowed while streaming is active in video mode */ + const LPVACT_LARGEST_PACKET_SIZE: u8 = 0; + + const LPHORIZONTAL_FRONT_PORCH_ENABLE: bool = true; /* Allow sending LP commands during HFP period */ + const LPHORIZONTAL_BACK_PORCH_ENABLE: bool = true; /* Allow sending LP commands during HBP period */ + const LPVERTICAL_ACTIVE_ENABLE: bool = true; /* Allow sending LP commands during VACT period */ + const LPVERTICAL_FRONT_PORCH_ENABLE: bool = true; /* Allow sending LP commands during VFP period */ + const LPVERTICAL_BACK_PORCH_ENABLE: bool = true; /* Allow sending LP commands during VBP period */ + const LPVERTICAL_SYNC_ACTIVE_ENABLE: bool = true; /* Allow sending LP commands during VSync = VSA period */ + const FRAME_BTAACKNOWLEDGE_ENABLE: bool = false; /* Frame bus-turn-around acknowledge enable => false according to debugger */ + + /* Select video mode by resetting CMDM and DSIM bits */ + DSIHOST.mcr().modify(|w| w.set_cmdm(false)); + DSIHOST.wcfgr().modify(|w| w.set_dsim(false)); + + /* Configure the video mode transmission type */ + DSIHOST.vmcr().modify(|w| w.set_vmt(MODE)); + + /* Configure the video packet size */ + DSIHOST.vpcr().modify(|w| w.set_vpsize(PACKET_SIZE)); + + /* Set the chunks number to be transmitted through the DSI link */ + DSIHOST.vccr().modify(|w| w.set_numc(NUMBER_OF_CHUNKS)); + + /* Set the size of the null packet */ + DSIHOST.vnpcr().modify(|w| w.set_npsize(NULL_PACKET_SIZE)); + + /* Select the virtual channel for the LTDC interface traffic */ + DSIHOST.lvcidr().modify(|w| w.set_vcid(VIRTUAL_CHANNEL_ID)); + + /* Configure the polarity of control signals */ + DSIHOST.lpcr().modify(|w| { + w.set_dep(DE_POLARITY); + w.set_hsp(HS_POLARITY); + w.set_vsp(VS_POLARITY); + }); + + /* Select the color coding for the host */ + DSIHOST.lcolcr().modify(|w| w.set_colc(COLOR_CODING)); + + /* Select the color coding for the wrapper */ + DSIHOST.wcfgr().modify(|w| w.set_colmux(COLOR_CODING)); + + /* Set the Horizontal Synchronization Active (HSA) in lane byte clock cycles */ + DSIHOST.vhsacr().modify(|w| w.set_hsa(HORIZONTAL_SYNC_ACTIVE)); + + /* Set the Horizontal Back Porch (HBP) in lane byte clock cycles */ + DSIHOST.vhbpcr().modify(|w| w.set_hbp(HORIZONTAL_BACK_PORCH)); + + /* Set the total line time (HLINE=HSA+HBP+HACT+HFP) in lane byte clock cycles */ + DSIHOST.vlcr().modify(|w| w.set_hline(HORIZONTAL_LINE)); + + /* Set the Vertical Synchronization Active (VSA) */ + DSIHOST.vvsacr().modify(|w| w.set_vsa(VERTICAL_SYNC_ACTIVE)); + + /* Set the Vertical Back Porch (VBP)*/ + DSIHOST.vvbpcr().modify(|w| w.set_vbp(VERTICAL_BACK_PORCH)); + + /* Set the Vertical Front Porch (VFP)*/ + DSIHOST.vvfpcr().modify(|w| w.set_vfp(VERTICAL_FRONT_PORCH)); + + /* Set the Vertical Active period*/ + DSIHOST.vvacr().modify(|w| w.set_va(VERTICAL_ACTIVE)); + + /* Configure the command transmission mode */ + DSIHOST.vmcr().modify(|w| w.set_lpce(LP_COMMAND_ENABLE)); + + /* Low power largest packet size */ + DSIHOST.lpmcr().modify(|w| w.set_lpsize(LP_LARGEST_PACKET_SIZE)); + + /* Low power VACT largest packet size */ + DSIHOST.lpmcr().modify(|w| w.set_lpsize(LP_LARGEST_PACKET_SIZE)); + DSIHOST.lpmcr().modify(|w| w.set_vlpsize(LPVACT_LARGEST_PACKET_SIZE)); + + /* Enable LP transition in HFP period */ + DSIHOST.vmcr().modify(|w| w.set_lphfpe(LPHORIZONTAL_FRONT_PORCH_ENABLE)); + + /* Enable LP transition in HBP period */ + DSIHOST.vmcr().modify(|w| w.set_lphbpe(LPHORIZONTAL_BACK_PORCH_ENABLE)); + + /* Enable LP transition in VACT period */ + DSIHOST.vmcr().modify(|w| w.set_lpvae(LPVERTICAL_ACTIVE_ENABLE)); + + /* Enable LP transition in VFP period */ + DSIHOST.vmcr().modify(|w| w.set_lpvfpe(LPVERTICAL_FRONT_PORCH_ENABLE)); + + /* Enable LP transition in VBP period */ + DSIHOST.vmcr().modify(|w| w.set_lpvbpe(LPVERTICAL_BACK_PORCH_ENABLE)); + + /* Enable LP transition in vertical sync period */ + DSIHOST.vmcr().modify(|w| w.set_lpvsae(LPVERTICAL_SYNC_ACTIVE_ENABLE)); + + /* Enable the request for an acknowledge response at the end of a frame */ + DSIHOST.vmcr().modify(|w| w.set_fbtaae(FRAME_BTAACKNOWLEDGE_ENABLE)); + + /* Configure DSI PHY HS2LP and LP2HS timings */ + const CLOCK_LANE_HS2_LPTIME: u16 = 35; + const CLOCK_LANE_LP2_HSTIME: u16 = 35; + const DATA_LANE_HS2_LPTIME: u8 = 35; + const DATA_LANE_LP2_HSTIME: u8 = 35; + const DATA_LANE_MAX_READ_TIME: u16 = 0; + const STOP_WAIT_TIME: u8 = 10; + + const MAX_TIME: u16 = if CLOCK_LANE_HS2_LPTIME > CLOCK_LANE_LP2_HSTIME { + CLOCK_LANE_HS2_LPTIME + } else { + CLOCK_LANE_LP2_HSTIME + }; + + /* Clock lane timer configuration */ + + /* In Automatic Clock Lane control mode, the DSI Host can turn off the clock lane between two + High-Speed transmission. + To do so, the DSI Host calculates the time required for the clock lane to change from HighSpeed + to Low-Power and from Low-Power to High-Speed. + This timings are configured by the HS2LP_TIME and LP2HS_TIME in the DSI Host Clock Lane Timer Configuration + Register (DSI_CLTCR). + But the DSI Host is not calculating LP2HS_TIME + HS2LP_TIME but 2 x HS2LP_TIME. + + Workaround : Configure HS2LP_TIME and LP2HS_TIME with the same value being the max of HS2LP_TIME or LP2HS_TIME. + */ + + DSIHOST.cltcr().modify(|w| { + w.set_hs2lp_time(MAX_TIME); + w.set_lp2hs_time(MAX_TIME) + }); + + // Data lane timer configuration + DSIHOST.dltcr().modify(|w| { + w.set_hs2lp_time(DATA_LANE_HS2_LPTIME); + w.set_lp2hs_time(DATA_LANE_LP2_HSTIME); + w.set_mrd_time(DATA_LANE_MAX_READ_TIME); + }); + + // Configure the wait period to request HS transmission after a stop state + DSIHOST.pconfr().modify(|w| w.set_sw_time(STOP_WAIT_TIME)); + + const _PCPOLARITY: bool = false; // LTDC_PCPOLARITY_IPC == 0 + + const LTDC_DE_POLARITY: Depol = if !DE_POLARITY { + Depol::ACTIVELOW + } else { + Depol::ACTIVEHIGH + }; + const LTDC_VS_POLARITY: Vspol = if !VS_POLARITY { + Vspol::ACTIVEHIGH + } else { + Vspol::ACTIVELOW + }; + + const LTDC_HS_POLARITY: Hspol = if !HS_POLARITY { + Hspol::ACTIVEHIGH + } else { + Hspol::ACTIVELOW + }; + + /* Timing Configuration */ + const HORIZONTAL_SYNC: u16 = HSA - 1; + const VERTICAL_SYNC: u16 = VERTICAL_SYNC_ACTIVE - 1; + const ACCUMULATED_HBP: u16 = HSA + HBP - 1; + const ACCUMULATED_VBP: u16 = VERTICAL_SYNC_ACTIVE + VERTICAL_BACK_PORCH - 1; + const ACCUMULATED_ACTIVE_W: u16 = LCD_X_SIZE + HSA + HBP - 1; + const ACCUMULATED_ACTIVE_H: u16 = VERTICAL_SYNC_ACTIVE + VERTICAL_BACK_PORCH + VERTICAL_ACTIVE - 1; + const TOTAL_WIDTH: u16 = LCD_X_SIZE + HSA + HBP + HFP - 1; + const TOTAL_HEIGHT: u16 = VERTICAL_SYNC_ACTIVE + VERTICAL_BACK_PORCH + VERTICAL_ACTIVE + VERTICAL_FRONT_PORCH - 1; + + // DISABLE LTDC before making changes + ltdc.disable(); + + // Configure the HS, VS, DE and PC polarity + LTDC.gcr().modify(|w| { + w.set_hspol(LTDC_HS_POLARITY); + w.set_vspol(LTDC_VS_POLARITY); + w.set_depol(LTDC_DE_POLARITY); + w.set_pcpol(Pcpol::RISINGEDGE); + }); + + // Set Synchronization size + LTDC.sscr().modify(|w| { + w.set_hsw(HORIZONTAL_SYNC); + w.set_vsh(VERTICAL_SYNC) + }); + + // Set Accumulated Back porch + LTDC.bpcr().modify(|w| { + w.set_ahbp(ACCUMULATED_HBP); + w.set_avbp(ACCUMULATED_VBP); + }); + + // Set Accumulated Active Width + LTDC.awcr().modify(|w| { + w.set_aah(ACCUMULATED_ACTIVE_H); + w.set_aaw(ACCUMULATED_ACTIVE_W); + }); + + // Set Total Width + LTDC.twcr().modify(|w| { + w.set_totalh(TOTAL_HEIGHT); + w.set_totalw(TOTAL_WIDTH); + }); + + // Set the background color value + LTDC.bccr().modify(|w| { + w.set_bcred(0); + w.set_bcgreen(0); + w.set_bcblue(0) + }); + + // Enable the Transfer Error and FIFO underrun interrupts + LTDC.ier().modify(|w| { + w.set_terrie(true); + w.set_fuie(true); + }); + + // ENABLE LTDC after making changes + ltdc.enable(); + + dsi.enable(); + dsi.enable_wrapper_dsi(); + + // First, delay 120 ms (reason unknown, STM32 Cube Example does it) + blocking_delay_ms(120); + + // 1 to 26 + dsi.write_cmd(0, NT35510_WRITES_0[0], &NT35510_WRITES_0[1..]).unwrap(); + dsi.write_cmd(0, NT35510_WRITES_1[0], &NT35510_WRITES_1[1..]).unwrap(); + dsi.write_cmd(0, NT35510_WRITES_2[0], &NT35510_WRITES_2[1..]).unwrap(); + dsi.write_cmd(0, NT35510_WRITES_3[0], &NT35510_WRITES_3[1..]).unwrap(); + dsi.write_cmd(0, NT35510_WRITES_4[0], &NT35510_WRITES_4[1..]).unwrap(); + dsi.write_cmd(0, NT35510_WRITES_5[0], &NT35510_WRITES_5[1..]).unwrap(); + dsi.write_cmd(0, NT35510_WRITES_6[0], &NT35510_WRITES_6[1..]).unwrap(); + dsi.write_cmd(0, NT35510_WRITES_7[0], &NT35510_WRITES_7[1..]).unwrap(); + dsi.write_cmd(0, NT35510_WRITES_8[0], &NT35510_WRITES_8[1..]).unwrap(); + dsi.write_cmd(0, NT35510_WRITES_9[0], &NT35510_WRITES_9[1..]).unwrap(); + dsi.write_cmd(0, NT35510_WRITES_10[0], &NT35510_WRITES_10[1..]).unwrap(); + // 11 missing + dsi.write_cmd(0, NT35510_WRITES_12[0], &NT35510_WRITES_12[1..]).unwrap(); + dsi.write_cmd(0, NT35510_WRITES_13[0], &NT35510_WRITES_13[1..]).unwrap(); + dsi.write_cmd(0, NT35510_WRITES_14[0], &NT35510_WRITES_14[1..]).unwrap(); + dsi.write_cmd(0, NT35510_WRITES_15[0], &NT35510_WRITES_15[1..]).unwrap(); + dsi.write_cmd(0, NT35510_WRITES_16[0], &NT35510_WRITES_16[1..]).unwrap(); + dsi.write_cmd(0, NT35510_WRITES_17[0], &NT35510_WRITES_17[1..]).unwrap(); + dsi.write_cmd(0, NT35510_WRITES_18[0], &NT35510_WRITES_18[1..]).unwrap(); + dsi.write_cmd(0, NT35510_WRITES_19[0], &NT35510_WRITES_19[1..]).unwrap(); + dsi.write_cmd(0, NT35510_WRITES_20[0], &NT35510_WRITES_20[1..]).unwrap(); + dsi.write_cmd(0, NT35510_WRITES_21[0], &NT35510_WRITES_21[1..]).unwrap(); + dsi.write_cmd(0, NT35510_WRITES_22[0], &NT35510_WRITES_22[1..]).unwrap(); + dsi.write_cmd(0, NT35510_WRITES_23[0], &NT35510_WRITES_23[1..]).unwrap(); + dsi.write_cmd(0, NT35510_WRITES_24[0], &NT35510_WRITES_24[1..]).unwrap(); + + // Tear on + dsi.write_cmd(0, NT35510_WRITES_26[0], &NT35510_WRITES_26[1..]).unwrap(); + + // Set Pixel color format to RGB888 + dsi.write_cmd(0, NT35510_WRITES_37[0], &NT35510_WRITES_37[1..]).unwrap(); + + // Add a delay, otherwise MADCTL not taken + blocking_delay_ms(200); + + // Configure orientation as landscape + dsi.write_cmd(0, NT35510_MADCTL_LANDSCAPE[0], &NT35510_MADCTL_LANDSCAPE[1..]) + .unwrap(); + dsi.write_cmd(0, NT35510_CASET_LANDSCAPE[0], &NT35510_CASET_LANDSCAPE[1..]) + .unwrap(); + dsi.write_cmd(0, NT35510_RASET_LANDSCAPE[0], &NT35510_RASET_LANDSCAPE[1..]) + .unwrap(); + + // Sleep out + dsi.write_cmd(0, NT35510_WRITES_27[0], &NT35510_WRITES_27[1..]).unwrap(); + + // Wait for sleep out exit + blocking_delay_ms(120); + + // Configure COLOR_CODING + dsi.write_cmd(0, NT35510_WRITES_37[0], &NT35510_WRITES_37[1..]).unwrap(); + + /* CABC : Content Adaptive Backlight Control section start >> */ + /* Note : defaut is 0 (lowest Brightness), 0xFF is highest Brightness, try 0x7F : intermediate value */ + dsi.write_cmd(0, NT35510_WRITES_31[0], &NT35510_WRITES_31[1..]).unwrap(); + /* defaut is 0, try 0x2C - Brightness Control Block, Display Dimming & BackLight on */ + dsi.write_cmd(0, NT35510_WRITES_32[0], &NT35510_WRITES_32[1..]).unwrap(); + /* defaut is 0, try 0x02 - image Content based Adaptive Brightness [Still Picture] */ + dsi.write_cmd(0, NT35510_WRITES_33[0], &NT35510_WRITES_33[1..]).unwrap(); + /* defaut is 0 (lowest Brightness), 0xFF is highest Brightness */ + dsi.write_cmd(0, NT35510_WRITES_34[0], &NT35510_WRITES_34[1..]).unwrap(); + /* CABC : Content Adaptive Backlight Control section end << */ + /* Display on */ + dsi.write_cmd(0, NT35510_WRITES_30[0], &NT35510_WRITES_30[1..]).unwrap(); + + /* Send Command GRAM memory write (no parameters) : this initiates frame write via other DSI commands sent by */ + /* DSI host from LTDC incoming pixels in video mode */ + dsi.write_cmd(0, NT35510_WRITES_35[0], &NT35510_WRITES_35[1..]).unwrap(); + + /* Initialize the LCD pixel width and pixel height */ + const WINDOW_X0: u16 = 0; + const WINDOW_X1: u16 = LCD_X_SIZE; // 480 for ferris + const WINDOW_Y0: u16 = 0; + const WINDOW_Y1: u16 = LCD_Y_SIZE; // 800 for ferris + const PIXEL_FORMAT: Pf = Pf::ARGB8888; + //const FBStartAdress: u16 = FB_Address; + const ALPHA: u8 = 255; + const ALPHA0: u8 = 0; + const BACKCOLOR_BLUE: u8 = 0; + const BACKCOLOR_GREEN: u8 = 0; + const BACKCOLOR_RED: u8 = 0; + const IMAGE_WIDTH: u16 = LCD_X_SIZE; // 480 for ferris + const IMAGE_HEIGHT: u16 = LCD_Y_SIZE; // 800 for ferris + + const PIXEL_SIZE: u8 = match PIXEL_FORMAT { + Pf::ARGB8888 => 4, + Pf::RGB888 => 3, + Pf::ARGB4444 | Pf::RGB565 | Pf::ARGB1555 | Pf::AL88 => 2, + _ => 1, + }; + + // Configure the horizontal start and stop position + LTDC.layer(0).whpcr().write(|w| { + w.set_whstpos(LTDC.bpcr().read().ahbp() + 1 + WINDOW_X0); + w.set_whsppos(LTDC.bpcr().read().ahbp() + WINDOW_X1); + }); + + // Configures the vertical start and stop position + LTDC.layer(0).wvpcr().write(|w| { + w.set_wvstpos(LTDC.bpcr().read().avbp() + 1 + WINDOW_Y0); + w.set_wvsppos(LTDC.bpcr().read().avbp() + WINDOW_Y1); + }); + + // Specify the pixel format + LTDC.layer(0).pfcr().write(|w| w.set_pf(PIXEL_FORMAT)); + + // Configures the default color values as zero + LTDC.layer(0).dccr().modify(|w| { + w.set_dcblue(BACKCOLOR_BLUE); + w.set_dcgreen(BACKCOLOR_GREEN); + w.set_dcred(BACKCOLOR_RED); + w.set_dcalpha(ALPHA0); + }); + + // Specifies the constant ALPHA value + LTDC.layer(0).cacr().write(|w| w.set_consta(ALPHA)); + + // Specifies the blending factors + LTDC.layer(0).bfcr().write(|w| { + w.set_bf1(Bf1::CONSTANT); + w.set_bf2(Bf2::CONSTANT); + }); + + // Configure the color frame buffer start address + let fb_start_address: u32 = &FERRIS_IMAGE[0] as *const _ as u32; + info!("Setting Framebuffer Start Address: {:010x}", fb_start_address); + LTDC.layer(0).cfbar().write(|w| w.set_cfbadd(fb_start_address)); + + // Configures the color frame buffer pitch in byte + LTDC.layer(0).cfblr().write(|w| { + w.set_cfbp(IMAGE_WIDTH * PIXEL_SIZE as u16); + w.set_cfbll(((WINDOW_X1 - WINDOW_X0) * PIXEL_SIZE as u16) + 3); + }); + + // Configures the frame buffer line number + LTDC.layer(0).cfblnr().write(|w| w.set_cfblnbr(IMAGE_HEIGHT)); + + // Enable LTDC_Layer by setting LEN bit + LTDC.layer(0).cr().modify(|w| w.set_len(true)); + + //LTDC->SRCR = LTDC_SRCR_IMR; + LTDC.srcr().modify(|w| w.set_imr(Imr::RELOAD)); + + blocking_delay_ms(5000); + + const READ_SIZE: u16 = 1; + let mut data = [1u8; READ_SIZE as usize]; + dsi.read(0, PacketType::DcsShortPktRead(0xDA), READ_SIZE, &mut data) + .unwrap(); + info!("Display ID1: {:#04x}", data); + + dsi.read(0, PacketType::DcsShortPktRead(0xDB), READ_SIZE, &mut data) + .unwrap(); + info!("Display ID2: {:#04x}", data); + + dsi.read(0, PacketType::DcsShortPktRead(0xDC), READ_SIZE, &mut data) + .unwrap(); + info!("Display ID3: {:#04x}", data); + + blocking_delay_ms(500); + + info!("Config done, start blinking LED"); + loop { + led.set_high(); + Timer::after_millis(1000).await; + + // Increase screen brightness + dsi.write_cmd(0, NT35510_CMD_WRDISBV, &[0xFF]).unwrap(); + + led.set_low(); + Timer::after_millis(1000).await; + + // Reduce screen brightness + dsi.write_cmd(0, NT35510_CMD_WRDISBV, &[0x50]).unwrap(); + } +} + +const NT35510_WRITES_0: &[u8] = &[0xF0, 0x55, 0xAA, 0x52, 0x08, 0x01]; // LV2: Page 1 enable +const NT35510_WRITES_1: &[u8] = &[0xB0, 0x03, 0x03, 0x03]; // AVDD: 5.2V +const NT35510_WRITES_2: &[u8] = &[0xB6, 0x46, 0x46, 0x46]; // AVDD: Ratio +const NT35510_WRITES_3: &[u8] = &[0xB1, 0x03, 0x03, 0x03]; // AVEE: -5.2V +const NT35510_WRITES_4: &[u8] = &[0xB7, 0x36, 0x36, 0x36]; // AVEE: Ratio +const NT35510_WRITES_5: &[u8] = &[0xB2, 0x00, 0x00, 0x02]; // VCL: -2.5V +const NT35510_WRITES_6: &[u8] = &[0xB8, 0x26, 0x26, 0x26]; // VCL: Ratio +const NT35510_WRITES_7: &[u8] = &[0xBF, 0x01]; // VGH: 15V (Free Pump) +const NT35510_WRITES_8: &[u8] = &[0xB3, 0x09, 0x09, 0x09]; +const NT35510_WRITES_9: &[u8] = &[0xB9, 0x36, 0x36, 0x36]; // VGH: Ratio +const NT35510_WRITES_10: &[u8] = &[0xB5, 0x08, 0x08, 0x08]; // VGL_REG: -10V +const NT35510_WRITES_12: &[u8] = &[0xBA, 0x26, 0x26, 0x26]; // VGLX: Ratio +const NT35510_WRITES_13: &[u8] = &[0xBC, 0x00, 0x80, 0x00]; // VGMP/VGSP: 4.5V/0V +const NT35510_WRITES_14: &[u8] = &[0xBD, 0x00, 0x80, 0x00]; // VGMN/VGSN:-4.5V/0V +const NT35510_WRITES_15: &[u8] = &[0xBE, 0x00, 0x50]; // VCOM: -1.325V +const NT35510_WRITES_16: &[u8] = &[0xF0, 0x55, 0xAA, 0x52, 0x08, 0x00]; // LV2: Page 0 enable +const NT35510_WRITES_17: &[u8] = &[0xB1, 0xFC, 0x00]; // Display control +const NT35510_WRITES_18: &[u8] = &[0xB6, 0x03]; // Src hold time +const NT35510_WRITES_19: &[u8] = &[0xB5, 0x51]; +const NT35510_WRITES_20: &[u8] = &[0x00, 0x00, 0xB7]; // Gate EQ control +const NT35510_WRITES_21: &[u8] = &[0xB8, 0x01, 0x02, 0x02, 0x02]; // Src EQ control(Mode2) +const NT35510_WRITES_22: &[u8] = &[0xBC, 0x00, 0x00, 0x00]; // Inv. mode(2-dot) +const NT35510_WRITES_23: &[u8] = &[0xCC, 0x03, 0x00, 0x00]; +const NT35510_WRITES_24: &[u8] = &[0xBA, 0x01]; + +const _NT35510_MADCTL_PORTRAIT: &[u8] = &[NT35510_CMD_MADCTL, 0x00]; +const _NT35510_CASET_PORTRAIT: &[u8] = &[NT35510_CMD_CASET, 0x00, 0x00, 0x01, 0xDF]; +const _NT35510_RASET_PORTRAIT: &[u8] = &[NT35510_CMD_RASET, 0x00, 0x00, 0x03, 0x1F]; +const NT35510_MADCTL_LANDSCAPE: &[u8] = &[NT35510_CMD_MADCTL, 0x60]; +const NT35510_CASET_LANDSCAPE: &[u8] = &[NT35510_CMD_CASET, 0x00, 0x00, 0x03, 0x1F]; +const NT35510_RASET_LANDSCAPE: &[u8] = &[NT35510_CMD_RASET, 0x00, 0x00, 0x01, 0xDF]; + +const NT35510_WRITES_26: &[u8] = &[NT35510_CMD_TEEON, 0x00]; // Tear on +const NT35510_WRITES_27: &[u8] = &[NT35510_CMD_SLPOUT, 0x00]; // Sleep out + // 28,29 missing +const NT35510_WRITES_30: &[u8] = &[NT35510_CMD_DISPON, 0x00]; // Display on + +const NT35510_WRITES_31: &[u8] = &[NT35510_CMD_WRDISBV, 0x7F]; +const NT35510_WRITES_32: &[u8] = &[NT35510_CMD_WRCTRLD, 0x2C]; +const NT35510_WRITES_33: &[u8] = &[NT35510_CMD_WRCABC, 0x02]; +const NT35510_WRITES_34: &[u8] = &[NT35510_CMD_WRCABCMB, 0xFF]; +const NT35510_WRITES_35: &[u8] = &[NT35510_CMD_RAMWR, 0x00]; + +//const NT35510_WRITES_36: &[u8] = &[NT35510_CMD_COLMOD, NT35510_COLMOD_RGB565]; // FIXME: Example sets it to 888 but rest of the code seems to configure DSI for 565 +const NT35510_WRITES_37: &[u8] = &[NT35510_CMD_COLMOD, NT35510_COLMOD_RGB888]; + +// More of these: https://elixir.bootlin.com/linux/latest/source/include/video/mipi_display.h#L83 +const _NT35510_CMD_TEEON_GET_DISPLAY_ID: u8 = 0x04; + +const NT35510_CMD_TEEON: u8 = 0x35; +const NT35510_CMD_MADCTL: u8 = 0x36; + +const NT35510_CMD_SLPOUT: u8 = 0x11; +const NT35510_CMD_DISPON: u8 = 0x29; +const NT35510_CMD_CASET: u8 = 0x2A; +const NT35510_CMD_RASET: u8 = 0x2B; +const NT35510_CMD_RAMWR: u8 = 0x2C; /* Memory write */ +const NT35510_CMD_COLMOD: u8 = 0x3A; + +const NT35510_CMD_WRDISBV: u8 = 0x51; /* Write display brightness */ +const _NT35510_CMD_RDDISBV: u8 = 0x52; /* Read display brightness */ +const NT35510_CMD_WRCTRLD: u8 = 0x53; /* Write CTRL display */ +const _NT35510_CMD_RDCTRLD: u8 = 0x54; /* Read CTRL display value */ +const NT35510_CMD_WRCABC: u8 = 0x55; /* Write content adaptative brightness control */ +const NT35510_CMD_WRCABCMB: u8 = 0x5E; /* Write CABC minimum brightness */ + +const _NT35510_COLMOD_RGB565: u8 = 0x55; +const NT35510_COLMOD_RGB888: u8 = 0x77; diff --git a/examples/stm32f469/src/bin/ferris.bin b/examples/stm32f469/src/bin/ferris.bin new file mode 100644 index 000000000..ae1c466be --- /dev/null +++ b/examples/stm32f469/src/bin/ferris.bin @@ -0,0 +1,70 @@ +f2Hx$$ -'edV~-R%a_5}+lD '`EM ]hcdu+h40@N +7@ +RS -^XWs(9ye +=wFF@m { +I~ ;VAcnn +E |!e5;O +%.x +[K)/ +PGJagUgg0YT_{3b7)n|]C u;XJ:0\V ]n i )*hF_=[pv +2FpM uFc$5HP +T3C;vb;;f$] 0C-I-Vxm1HOk +2j)5eqJuTmLr/G 74q<tN0zNY"Q6Nt 1yP. +=cG(\DPvp+ Qqx}t)Zg_''XR\FQR~5`7d= 7hGVE.-yE8 !"x.n!0f2m3!e0j#_HYm#FKXF&Me |W & (,:GR[c#j#q'x'}~~~~~~~~~~*~~~~~~~~~~~~~~~~~~~*~~~~~~~~~~~~~~~~~~~~~~~~~~~.~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~.~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}0~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}1~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}3~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}3~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}|||||6~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}|||||||||||||6~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}|||||||||||||||||||||||||||||8~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}|||||||||||||||||||||||||||||||||||||8~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}|||||||||||||||||||||||||||||||||||||||||||||||{{{{{;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}||||||||||||||||||||||||||||||||||||||||||||||{{{{{{{{{{{{{{;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}||||||||||||||||||||||||||||||||||||||||||||||{{{{{{{{{{{{{{{{{{{{{{{{{{{{{=d02Fr%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}||||||||||||||||||||||||||||||||||||||||||||||{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{=~N9|zzz|A( Uz/~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}||||||||||||||||||||||||||||||||||||||||||||||{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{;^zzzzzzzzzzzzG2Kb~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}||||||||||||||||||||||||||||||||||||||||||||||{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{zzzzzzzzzzzzzz|~a@zzzyyyyyyyyyyyyy.-i~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}||||||||||||||||||||||||||||||||||||||||||||||{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{zzzzzzzzzzzzzzzzzzzzzzz {J zyyyyyyyyyyyyyyyyyyyyy:IHd}t~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}||||||||||||||||||||||||||||||||||||||||||||||{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzj4yyyyyyyyyyyyyyyyyyyyyyyyyy|%H|_~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}||||||||||||||||||||||||||||||||||||||||||||||{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzyyy z?*~{yyyyyyyyyyyyyyyyyyyyyyyyyyyyyza'\|S~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}|||||||||||||||||||||||||||||||||||||||{||||||{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzyyyyyyyyyyyyyth zyyyyyyyyyyyyyyyyyyyyyyyyyxxxxxxxxx y!1A-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}|||||||||||||||||||||||||||||||||||||||||||||||{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzyyyyyyyyyyyyyyyyyyyyyyyyyyyyy zEQ={yyyyyyyyyyyyyxxxxxxxxxxxxxxxxxxxxxxxxx~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}|||||||||||||||||||||||||||||||||||||||||||||||{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}||||||||||||||||||||||||||||||||||||||||||||||{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxwwwx ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}||||||||||||||||||||||||||||||||||||||||||||||{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxwwwwwwwwwwwJ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}|||||||||||||||||||||||||||||||||||||||||||||||{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxwwwwwwwwwwwwwwwwwwwwwwwww xw~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}|||||||||||||||||||||||||||||||||||||||||||||||{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww x~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}|||||||||||||||||||||||||||||||||||||||||||||||{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww${(~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}|||||||||||||||||||||||||||||||||||||||||||||||{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwvvvvvvvvvvpM~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}|||||||||||||||||||||||||||||||||||||||||||||||{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwvvvvvvvvvvvvvvvvvve~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}|||||||||||||||||||||||||||||||||||||||||||||||{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}||||||||||||||||||||||||||||||||||||||||||||||{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv>~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}||||||||||||||||||||||||||||||||||||||||||||||{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvuuuuuuuuuun$~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}|||||||||||||||||||||||||||||||||||||||||||||||{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvuuuuuuuuuuuuuuuuuuK~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}||||||||||||||||||||||||||||||||||||||||||||||{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvuuuuuuuuuuuuuuuuuuuuuuuuuwz~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}||||||||||||||||||||||||||||||||||||||||||||||{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu+{~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}||||||||||||||||||||||||||||||||||||||||||||||{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuttt9~&~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}||||||||||||||||||||||||||||||||||||||||||||||{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuttttttttttttttttttiV~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}||||||||||||||||||||||||||||||||||||||||||||||{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuutttttttttttttttttttttttttts}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}||||||||||||||||||||||||||||||||||||||||||||||{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuutttttttttttttttttttttttttttttttttttttttttt~}}}}}}}}}}}}}}}}}}}}}}}}}}}}||||||||||||||||||||||||||||||||||||||||||||||{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuutttttttttttttttttttttttttttttttttttttttttttttttss+y*}}}}}}}}}}}}}}}}}}}}||||||||||||||||||||||||||||||||||||||||||||||{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuutttttttttttttttttttttttttttttttttttttttttttttttssssssssssr8}}}}}||||||||||||||||||||||||||||||||||||||||||||||{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuutttttttttttttttttttttttttttttttttttttttttttttttssssssssssssssssssssssssssS|||||||||||||||||||||||||||||||||||||||||||{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuttttttttttttttttttttttttttttttttttttttttttttttsssssssssssssssssssssssssssssssssu~||||||||||||||||||||||||||||{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuutttttttttttttttttttttttttttttttttttttttttttttttsssssssssssssssssssssssssssssssssssssssssssssrrr9|||||||||||||||||||||{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuttttttttttttttttttttttttttttttttttttttttttttttsssssssssssssssssssssssssssssssssssssssssssssssrrrrrrrrrrQ!||||||||||{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{z{{{{{{{zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvuvvvvvvuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuttttttttttttttttttttttttttttttttttttttttsttttttssssssssssssssssssssssssssssssssssssssssssssssrrrrrrrrrrrrrrrrrrrlZ{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuttttttttttttttttttttttttttttttttttttttttttttttssssssssssssssssssssssssssssssssssssssssssssssrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr~{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuttttttttttttttttttttttttttttttttttttttttttttttsssssssssssssssssssssssssssssssssssssssssssssssrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr{{{{{{{{{{{{{{{{{zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuttttttttttttttttttttttttttttttttttttttttttttttsssssssssssssssssssssssssssssssssssssssssssssssrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrqqqqqqqqqq r4{{{{{{{{zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuttttttttttttttttttttttttttttttttttttttttttttttsssssssssssssssssssssssssssssssssssssssssssssssrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrqqqqqqqqqqqqqqqqqqkKzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuttttttttttttttttttttttttttttttttttttttttttttttsssssssssssssssssssssssssssssssssssssssssssssssrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqazzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuttttttttttttttttttttttttttttttttttttttttttttttsssssssssssssssssssssssssssssssssssssssssssssssrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqzzzzzzzzzzzzzzzzzzzzzzzyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuttttttttttttttttttttttttttttttttttttttttttttttsssssssssssssssssssssssssssssssssssssssssssssssrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqpp;{zzzzzzzzyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuutttttttttttttttttttttttttttttttttttttttttttttttssssssssssssssssssssssssssssssssssssssssssssssrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqppppppppppppppppppa#|yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuutttttttttttttttttttttttttttttttttttttttttttttttssssssssssssssssssssssssssssssssssssssssssssssrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqpppppppppppppppppppppppppp|^yyyyyyyyyyyyyyyyyyyyyyyyyyyyyxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuttttttttttttttttttttttttttttttttttttttttttttttsssssssssssssssssssssssssssssssssssssssssssssssrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqpppppppppppppppppppppppppppppppppppppppp qjyyyyyyyyyyyyyyyyyyyyyxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuttttttttttttttttttttttttttttttttttttttttttttttssssssssssssssssssssssssssssssssssssssssssssssrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqppppppppppppppppppppppppppppppppppppppppppppppooo pyyyyyyyxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuutttttttttttttttttttttttttttttttttttttttttttttttssssssssssssssssssssssssssssssssssssssssssssssrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqppppppppppppppppppppppppppppppppppppppppppppppooooooooooooooooooo;z+|xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuutttttttttttttttttttttttttttttttttttttttttttttttssssssssssssssssssssssssssssssssssssssssssssssrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqppppppppppppppppppppppppppppppppppppppppppppppooooooooooooooooooooooooooooG<xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuutttttttttttttttttttttttttttttttttttttttttttttttssssssssssssssssssssssssssssssssssssssssssssssrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqpppppppppppppppppppppppppppppppppppppppppppppppooooooooooooooooooooooooooooooooooooolUxxxxxxxxxxxxxxxxxxxxxxxwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuutttttttttttttttttttttttttttttttttttttttttttttttssssssssssssssssssssssssssssssssssssssssssssssrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqppppppppppppppppppppppppppppppppppppppppppppppooooooooooooooooooooooooooooooooooooooooooooooonnnnnnp}yxxxxxxxxxxxxxxxwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuutttttttttttttttttttttttttttttttttttttttttttttttssssssssssssssssssssssssssssssssssssssssssssssrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqppppppppppppppppppppppppppppppppppppppppppppppooooooooooooooooooooooooooooooooooooooooooooooonnnnnnnnnnnnnnnnd3~xxxxxxxxxy z,|2}9@EJNTW[_bfimry~%{xwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuutttttttttttttttttttttttttttttttttttttttttttttttsssssssssssssssssssssssssssssssssssssssssssssrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqpppppppppppppppppppppppppppppppppppppppppppppppoooooooooooooooooooooooooooooooooooooooooooooonnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn1v|zvspjec\VRJ~D|ypppppppppppppppppppppppppppppppppooooooooooooooooooooooooooooooooooooooooooooooonnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmlllllllllllllllllllllllllllllllllllllllllllllllkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiihhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhggggggggggggggggggggggggggggggggggggggggggggggffffffffffffffffffffffffffffffffffffffffffffffeeeeeeeeeeeeeeeeeeapppppppppppppppppppppppppoooooooooooooooooooooooooooooooooooooooooooooonnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmllllllllllllllllllllllllllllllllllllllllllllllkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiihhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhggggggggggggggggggggggggggggggggggggggggggggggffffffffffffffffffffffffffffffffffffffffffffffeeeeeeeeeeeeeeeeeeeeeeeee f6wpppppppppppppppooooooooooooooooooooooooooooooooooooooooooooooonnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmlllllllllllllllllllllllllllllllllllllllllllllllkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiihhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhggggggggggggggggggggggggggggggggggggggggggggggffffffffffffffffffffffffffffffffffffffffffffffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee;rwooooooooooooooooooooooooooooooooooooooooooooooonnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmllllllllllllllllllllllllllllllllllllllllllllllkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiihhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhggggggggggggggggggggggggggggggggggggggggggggggfffffffffffffffffffffffffffffffffffffffffffffffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeedd}.toooooooooooooooooooooooooooooooooooooonnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmllllllllllllllllllllllllllllllllllllllllllllllkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiihhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhggggggggggggggggggggggggggggggggggggggggggggggfffffffffffffffffffffffffffffffffffffffffffffffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeedddddddddd[oooooooooooooooooooooonnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmllllllllllllllllllllllllllllllllllllllllllllllkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiihhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhggggggggggggggggggggggggggggggggggggggggggggggfffffffffffffffffffffffffffffffffffffffffffffffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeddddddddddddddddddddddddduoooooooooooooonnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmllllllllllllllllllllllllllllllllllllllllllllllkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiihhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhggggggggggggggggggggggggggggggggggggggggggggggfffffffffffffffffffffffffffffffffffffffffffffffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeddddddddddddddddddddddddddddddddfRnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmllllllllllllllllllllllllllllllllllllllllllllllkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiihhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhggggggggggggggggggggggggggggggggggggggggggggggfffffffffffffffffffffffffffffffffffffffffffffffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeedddddddddddddddddddddddddddddddddddddddddddddddX~nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmllllllllllllllllllllllllllllllllllllllllllllllkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiihhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhggggggggggggggggggggggggggggggggggggggggggggggfffffffffffffffffffffffffffffffffffffffffffffffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeddddddddddddddddddddddddddddddddddddddddddddddcccccccccG{nnnnnnnnnnnnnnnnnnnnnnnnnnmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmllllllllllllllllllllllllllllllllllllllllllllllkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiihhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhggggggggggggggggggggggggggggggggggggggggggggggffffffffffffffffffffffffffffffffffffffffffffffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeedddddddddddddddddddddddddddddddddddddddddddddddcccccccccccccccc#hJ{nnnnnnnnnnnmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmllllllllllllllllllllllllllllllllllllllllllllllkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiihhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhgggggggggggggggggggggggggggggggggggggggggggggggffffffffffffffffffffffffffffffffffffffffffffffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeedddddddddddddddddddddddddddddddddddddddddddddddcccccccccccccccccccccccccccccccAs~nnnnmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmlllllllllllllllllllllllllllllllllllllllllllllllkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiihhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhgggggggggggggggggggggggggggggggggggggggggggggggffffffffffffffffffffffffffffffffffffffffffffffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeedddddddddddddddddddddddddddddddddddddddddddddddcccccccccccccccccccccccccccccccccccccccc'qmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmllllllllllllllllllllllllllllllllllllllllllllllkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiihhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhgggggggggggggggggggggggggggggggggggggggggggggggffffffffffffffffffffffffffffffffffffffffffffffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeddddddddddddddddddddddddddddddddddddddddddddddccccccccccccccccccccccccccccccccccccccccccccccbbbbbbbbbbkcmmmmmmmmmmmmmmmmmmmmmmmmmmmmllllllllllllllllllllllllllllllllllllllllllllllkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiihhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhgggggggggggggggggggggggggggggggggggggggggggggggffffffffffffffffffffffffffffffffffffffffffffffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeddddddddddddddddddddddddddddddddddddddddddddddccccccccccccccccccccccccccccccccccccccccccccccbbbbbbbbbbbbbbbbbb'h+rmmmmmmmmmmmmlllllllllllllllllllllllllllllllllllllllllllllllkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiihhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhgggggggggggggggggggggggggggggggggggggggggggggggffffffffffffffffffffffffffffffffffffffffffffffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeddddddddddddddddddddddddddddddddddddddddddddddcccccccccccccccccccccccccccccccccccccccccccccccbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbzkmmmmmlllllllllllllllllllllllllllllllllllllllllllllllkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiihhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhggggggggggggggggggggggggggggggggggggggggggggggffffffffffffffffffffffffffffffffffffffffffffffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeddddddddddddddddddddddddddddddddddddddddddddddcccccccccccccccccccccccccccccccccccccccccccccccbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbnllllllllllllllllllllllllllllllllllllllllllllkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiihhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhgggggggggggggggggggggggggggggggggggggggggggggggffffffffffffffffffffffffffffffffffffffffffffffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeddddddddddddddddddddddddddddddddddddddddddddddcccccccccccccccccccccccccccccccccccccccccccccccbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbaaaaaquGylllllllllllllllllllllllllllllkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiihhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhgggggggggggggggggggggggggggggggggggggggggggggggffffffffffffffffffffffffffffffffffffffffffffffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeddddddddddddddddddddddddddddddddddddddddddddddccccccccccccccccccccccccccccccccccccccccccccccbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaaaesX'plllllllllllllllllllllllllkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiihhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhgggggggggggggggggggggggggggggggggggggggggggggggffffffffffffffffffffffffffffffffffffffffffffffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeddddddddddddddddddddddddddddddddddddddddddddddcccccccccccccccccccccccccccccccccccccccccccccccbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaCr}m^N{8t mllllllllllllllkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiihhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhggggggggggggggggggggggggggggggggggggggggggggggffffffffffffffffffffffffffffffffffffffffffffffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeddddddddddddddddddddddddddddddddddddddddddddddcccccccccccccccccccccccccccccccccccccccccccccccbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa```````cNwl~d9tonmllllllllllkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiihhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhggggggggggggggggggggggggggggggggggggggggggggggffffffffffffffffffffffffffffffffffffffffffffffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeddddddddddddddddddddddddddddddddddddddddddddddcccccccccccccccccccccccccccccccccccccccccccccccbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa``````````````````` ac#f.iKu}o[Bwmllkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiihhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhgggggggggggggggggggggggggggggggggggggggggggggfffffffffffffffffffffffffffffffffffffffffffffffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeedddddddddddddddddddddddddddddddddddddddddddddddccccccccccccccccccccccccccccccccccccccccccccccbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa``````````````````````````````````````````bU{}iGy7t,q n lkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiihhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhggggggggggggggggggggggggggggggggggggggggggggggfffffffffffffffffffffffffffffffffffffffffffffffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeddddddddddddddddddddddddddddddddddddddddddddddcccccccccccccccccccccccccccccccccccccccccccccccbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa``````````````````````````````````````````````________ `+g@oMvdYmkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiihhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhggggggggggggggggggggggggggggggggggggggggggggggfffffffffffffffffffffffffffffffffffffffffffffffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeddddddddddddddddddddddddddddddddddddddddddddddcccccccccccccccccccccccccccccccccccccccccccccccbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa``````````````````````````````````````````````________________________DqW}mkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiihhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhggggggggggggggggggggggggggggggggggggggggggggggffffffffffffffffffffffffffffffffffffffffffffffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeedddddddddddddddddddddddddddddddddddddddddddddddccccccccccccccccccccccccccccccccccccccccccccccbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa``````````````````````````````````````````````__________________________________________HsHxkkkkkkkkkkkkkkkkkkkkkkkkjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiihhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhggggggggggggggggggggggggggggggggggggggggggggggfffffffffffffffffffffffffffffffffffffffffffffffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeedddddddddddddddddddddddddddddddddddddddddddddddccccccccccccccccccccccccccccccccccccccccccccccbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa``````````````````````````````````````````````_______________________________________________^^^^bckkkkkkkkkkjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiihhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhggggggggggggggggggggggggggggggggggggggggggggggfffffffffffffffffffffffffffffffffffffffffffffffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeddddddddddddddddddddddddddddddddddddddddddddddcccccccccccccccccccccccccccccccccccccccccccccccbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa`````````````````````````````````````````````_______________________________________________^^^^^^^^^^^^^^^^^^^^^bkkkjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiihhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhggggggggggggggggggggggggggggggggggggggggggggggfffffffffffffffffffffffffffffffffffffffffffffffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeddddddddddddddddddddddddddddddddddddddddddddddccccccccccccccccccccccccccccccccccccccccccccccbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa``````````````````````````````````````````````_______________________________________________^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^VzLyjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiihhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhgggggggggggggggggggggggggggggggggggggggggggggfffffffffffffffffffffffffffffffffffffffffffffffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeedddddddddddddddddddddddddddddddddddddddddddddddccccccccccccccccccccccccccccccccccccccccccccccbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa``````````````````````````````````````````````_______________________________________________^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^c-pjjjjjjjjjjjjjjjjjjjjjjjjjiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiihhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhggggggggggggggggggggggggggggggggggggggggggggggfffffffffffffffffffffffffffffffffffffffffffffffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeedddddddddddddddddddddddddddddddddddddddddddddddccccccccccccccccccccccccccccccccccccccccccccccbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa``````````````````````````````````````````````______________________________________________^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]!mjjjjjjjjjjjjjjjjjiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiihhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhggggggggggggggggggggggggggggggggggggggggggggggfffffffffffffffffffffffffffffffffffffffffffffffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeedddddddddddddddddddddddddddddddddddddddddddddddccccccccccccccccccccccccccccccccccccccccccccccbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa```````````````````````````````````````````````______________________________________________^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]`ljjiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiihhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhgggggggggggggggggggggggggggggggggggggggggggggggffffffffffffffffffffffffffffffffffffffffffffffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeedddddddddddddddddddddddddddddddddddddddddddddddccccccccccccccccccccccccccccccccccccccccccccccbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa``````````````````````````````````````````````_______________________________________________^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]].fkiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiihhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhgggggggggggggggggggggggggggggggggggggggggggggggffffffffffffffffffffffffffffffffffffffffffffffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeedddddddddddddddddddddddddddddddddddddddddddddddccccccccccccccccccccccccccccccccccccccccccccccbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa```````````````````````````````````````````````______________________________________________^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]kiiiiiiiiiiiiiiiiiiiiiiiiihhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhgggggggggggggggggggggggggggggggggggggggggggggggffffffffffffffffffffffffffffffffffffffffffffffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeddddddddddddddddddddddddddddddddddddddddddddddccccccccccccccccccccccccccccccccccccccccccccccbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa```````````````````````````````````````````````______________________________________________^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\7i%miiiiiiiiiiiiiiiiihhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhggggggggggggggggggggggggggggggggggggggggggggggffffffffffffffffffffffffffffffffffffffffffffffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeddddddddddddddddddddddddddddddddddddddddddddddccccccccccccccccccccccccccccccccccccccccccccccbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa```````````````````````````````````````````````______________________________________________^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\j5qiiiiiiiihhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhgggggggggggggggggggggggggggggggggggggggggggggggffffffffffffffffffffffffffffffffffffffffffffffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeddddddddddddddddddddddddddddddddddddddddddddddccccddddddddddddccccccccccccccccccccccccccccccbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabba[TQONOQU[aaa```````````````````````````````````````````````______________________________________________^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\khhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhgggggggggggggggggggggggggggggggggggggggggggggggffffffffffffffffffffffffffffffffffffffffffffffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeddddddddddddddddddddddddddddddddddddddddddddddccccccccccccccbbaUI?70{,s+q)o*p+r-v29DS`bbcccccccccccbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa`[C1'mYC4 ) ' -6E[)p3J]_`````````````````````````````______________________________________________^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\@m+nhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhgggggggggggggggggggggggggggggggggggggggggggggggffffffffffffffffffffffffffffffffffffffffffffffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeddddddddddddddddddddddddddddddddddddddddddddddccccccccccccccccccc_XTE.uBH8RXabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa```^TD U.yOX```````````````````______________________________________________^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\[~ihhhhhhhhhhhhhgggggggggggggggggggggggggggggggggggggggggggggggffffffffffffffffffffffffffffffffffffffffffffffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeddddddddddddddddddddddddddddddddddddddddddddddccccccccccccccccccccccccccccccc`K7&t =;0C^bbbbbbbbbbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa`````````````````]F.. [;W``______________________________________________^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[zX|hhhhhhggggggggggggggggggggggggggggggggggggggggggggggffffffffffffffffffffffffffffffffffffffffffffffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeddddddddddddddddddddddddddddddddddddddddddddddcccccccccccccccccccccccccccccccccccb]+_iv[[[3=Zabbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa``````````````````````bN{OOO +?Z______________________________________^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[Dn8pgggggggggggggggggggggggggggggggggggfffffffffffffffffffffffffffffffffffffffffffffffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeddddddddddddddddddddddddddddddddddddddddddddddcccccccccccccccccccccccccccccccccccccccccccccccbbb^;|RRR6@Zbaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa```````````````````````````````````_,q((( [F_____________________^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[iggggggggggggggggggggggggggfffffffffffffffffffffffffffffffffffffffffffffffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeddddddddddddddddddddddddddddddddddddddddddddddcccccccccccccccccccccccccccccccccccccccccccccccbbbbbbbbbbb1Q DK`aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa```````````````````````````````````````````]TTT#/}X___________^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[viggggggggggggggggggfffffffffffffffffffffffffffffffffffffffffffffffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeddddddddddddddddddddddddddddddddddddddddddddddcccccccccccccccccccccccccccccccccccccccccccccccbbbbbbbbbbbbbbbbbaHu + + +5Zaaaaaaaaaaaaaaaaaaaaaaaaaaaaa``````````````````````````````````````````````___]uuuVN__^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZMr?rggffffffffffffffffffffffffffffffffffffffffffffffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeedddddddddddddddddddddddddddddddddddddddddddddddccccccccccccccccccccccccccccccccccccccccccccccbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbC|#aSaaaaaaaaaaaa``````````````````````````````````````````````__________________Yo{fff4B\^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZ_!jfffffffffffffffffffffffffffffffffffffffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeedddddddddddddddddddddddddddddddddddddddddddddddccccccccccccccccccccccccccccccccccccccccccccccbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb'nBNaaa``````````````````````````````````````````````_________________________MPUpUUU=Z^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ{ugfffffffffffffffffffffffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeddddddddddddddddddddddddddddddddddddddddddddddcccccccccccccccccccccccccccccccccccccccccccccccbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbaaaaaaaa^qqq:Ha```````````````````````````````________________________________________B!>&&&4Z^^^^^]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZPtMvfffffffffffffffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeddddddddddddddddddddddddddddddddddddddddddddddccccccccccccccccccccccccccccccccccccccccccccccbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaj5556L```````````````````````______________________________________________\? .Z]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYY'`/leeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeedddddddddddddddddddddddddddddddddddddddddddddddccccccccccccccccccccccccccccccccccccccccccccccbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa*mKS```````_______________________________________________^^^^^^^^^^^^^[:,,,$$$2X]]]]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYgeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeedddddddddddddddddddddddddddddddddddddddddddddddccccccccccccccccccccccccccccccccccccccccccccccbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaap333TX____________________________________________^^^^^^^^^^^^^^^^^^^^^];8\]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYKp_}eeeeeeeeeeeeeeeeeeeeeeeeeeedddddddddddddddddddddddddddddddddddddddddddddddccccccccccccccccccccccccccccccccccccccccccccccbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_0\___________________________________^^^^^^^^^^^^^^^^^^^^^^^^^^^^^P %000S]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY.c5meeeeeeeeeedddddddddddddddddddddddddddddddddddddddddddddddccccccccccccccccccccccccccccccccccccccccccccccbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa``````````````_=E____________________^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^'kssshhh-{]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXX Yhedddddddddddddddddddddddddddddddddddddddddddddddccccccccccccccccccccccccccccccccccccccccccccccbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa``````````````````````^ccc U___________^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^]]]]]B :P\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXUum +edddddddddddddddddddddddddddddddccccccccccccccccccccccccccccccccccccccccccccccbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa``````````````````````````````````````^-|\^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]V\+++ ;[\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXX*aDqdddddddddddddddddddddddccccccccccccccccccccccccccccccccccccccccccccccbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa``````````````````````````````````````````````Ik K^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]]]]]EMMMKV\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX[(iddddddddddddddccccccccccccccccccccccccccccccccccccccccccccccbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa```````````````````````````````````````````````______^x}999$d\^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]Y@bbb[[[D[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX]zzeccccccccccccccccccccccccccccccccccccccccccccbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa```````````````````````````````````````````````______________________ITTTG^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]\\\]:uuuN[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWW[Uxccccccccccccccccccccccccccccccccccccbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa```````````````````````````````````````````````_____________________________])mbbbXZ]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\VNI[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWZ.jcccccccccccccccccccbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa```````````````````````````````````````````````____________________________________________N /mmm"G]]]]]]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\\B2[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWe~ecccccccccccbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa``````````````````````````````````````````````______________________________________________^^^^^^\7 vvv2[]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\X#dPRZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW0bdbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa``````````````````````````````````````````````______________________________________________^^^^^^^^^^^^^^^^^^^^^Z !}}} $V]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\[[NIZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVV8lbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa```````````````````````````````````````````````______________________________________________^^^^^^^^^^^^^^^^^^^^^^^^^^^^^?{{{=\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[)q-YZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVv#fbbbbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa```````````````````````````````````````````````______________________________________________^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\RtttE[\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[R ,VZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVV5crcbbbbbbaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa``````````````````````````````````````````````______________________________________________^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^]]]]]I & + + +hhh!L\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[<zzzAZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVKraaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa``````````````````````````````````````````````_______________________________________________^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]\8^^^9\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[X+}fffiii*xYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV Waaaaaaaaaaaaaaaaaaaaaaaaaaaaaa`````````````````````````````````````````````_______________________________________________^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]]]]]WVLLL)vX[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZP -SSS+++TRYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUU"[kaaaaaaaaaaaaaaaaaaaaa``````````````````````````````````````````````_______________________________________________^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]Rkkk000:Q[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZG444$GYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUX1iaaaaaa``````````````````````````````````````````````_______________________________________________^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]\\\\\8888N[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZY)t>XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU6j`````````````````````````````````````````````______________________________________________^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\?D[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZY #rrr*|VXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUr9k`````````````````````````````````````_______________________________________________^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\OFFF2ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYK###???2TXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTYCn```````````````````````_______________________________________________^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\]<  [YZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYZ7NXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTGp````````````````_______________________________________________^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\1eee1YZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYZ*{;;;EEE:WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTChOs``______________________________________________^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[V#iZZZ###YZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYW"j (uXWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTS-]Tu_________________________________________^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[O7 SYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXNL;;;888QXWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSVi\x__________________________^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[LFFFJYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXJ 2wwwxxx?SWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSJk_z___________________^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[IvvvBBBDYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXG ,FVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS*\h____________^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZY:ooo?ZYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXE@VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRig~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZY)u*** 9YXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWD;VVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRAfs +_^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZYSxxx6YXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWAYYYRRR8VVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRr +_^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZX? ggg6YXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWW>5VUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRbyya^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYW,<<UVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU$t +7SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOO=aAj\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWF%mUVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUV0;SSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOUKn\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWJ7UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTB ,DSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOWqQp[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWOTGUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTQAORRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNN8^Sq[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVU* +(JUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTS&s]SRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNQ|_w[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVW8 + fPUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSB3RRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN]t\v[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVO$/TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSKBJRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN3\g{ \[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV#i<TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSQ,^ORRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMM}j}\ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUG 1PTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSB!8PQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMPkp [ZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUQ!j(zTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS\@FQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONMMMMMMMMMMMMMMMMMMMMMMMMNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMv]ZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUU;HSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRC&zQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNMLLLLLLLLLLLLLLLLLLLLLLLMNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLo| [ZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUURXdOSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRO&{HQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLL6[]YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTUD;SSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRC /2PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLQ]YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTR.PNSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR*ZIPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL N^t]YYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTKN9SSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQMP=PPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMLLLLLLLLLLLLLLLLLLLLLLLLLMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL-X0aYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTB hORRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQ@(3OPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLQ*`YXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSR6 +.DRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ2'LPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKg:dXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSQ,0QRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPN+ kJOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL1YPQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOON5cBMNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLGe\sWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQFZPAQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOON4'V8MNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL N^uWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQK0 jGPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOON9B7GONNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLk~hz WVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQP@U  ,1NPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNMAGD0KMNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLJfj{VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQM4A nAPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNF-k6HMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLxm}XVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPOJ-#I>LPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNL@Z +-%|BIMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLv WVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUURJ>JQTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPG.Uk6MOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN:$ 8M&5IMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKJIIIIIJKLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLqtYVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTSF6,+,5BQTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPI9C ),BJOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMKD:$x ( , j5AELMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKGC>:765333333468:=AEKLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLR}YUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTSL5,+++++,.>NRTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPON?%xJ + 6^%{=MNOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMLLH:2,***.4>JLLMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLE5-,,++++++++++++++++,,,1:DHJLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLO| ZUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTN=,+++++++++*-:FQTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOF<-;C#t3<BLPOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMNNNNMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLD,+++++++++++++++++++++++++*-4;CJLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLO(\UUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTRC/++++++++++++++,0@ORSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOONLG2&x-6?HKLMNOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL9++++++++++++++++++++++++++++++-/:GKLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL N-]UUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTM9+++++++++++++++++++-<HOTSSSSSSSSSSSSSSSSSRNNPQRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOPOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLI1+++++++++++++++++++++++++++++++++,5AHLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLO1^TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSTH0++++++++++++++++++++++-2?NSSSSSSRRRRRRRRRRRQLLMMOPRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLD+++++++++++++++++ +.++++++++++++++++++,0<JLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLOBdTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSS1]{Y`++++++++++++++++++++++++++,9INRRRRRRRRRRRRRRRNLLLLLLNPQRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL7++++++++++++++++4C3++++++++++++++++++++/@ILLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL'Uo~TTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSHgx{/++++++++++++++++++++++++++++2=IRRRRRRRRRRRRRRMLLLLLLLMMNPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLG,+++++++++++++++8G+++++++++++++++++++++++3A\"SLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLh|%ZTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS TQl->++++++++++++++++++++++++++++++-3DNQRRRRRRRRRRPLLLLLLLLLLLMNPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL9++++++++++++++ +.Yb+++++++++++++++++++++++/@\sOLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL USSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRSSat]d++++++++++++++++++++++++++++++++*/;FNRQQQQQQQQNLLLLLLLLLLLLLMNOPQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLH0++++++++++++++U^+++++++++++++++++++++++fm +MLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL#Sq~SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRS`t1++++++++++++++++++++++++++++++++++-0;JPQQQQQQQMLLLLLLLLLLLLLLLLMNOQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL?+++++++++++++3C8G+++++++++++++++++++++++9]LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLqgxSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRR S`tin++++++++++++++++++++++++++++++++++++++3BJOQQQQNLLLLLLLLLLLLLLLLLLLMMNOPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLJ3++++++++++++el+++++++++++++++++++++++rx,WLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLOhySSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRNj5D+++++++++++++++++++++++++++++++++++++++.6AMQPPMLLLLLLLLLLLLLLLLLLLLLLMNOPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLC+++++++++++!7pv++++++++++++++++++++++/@TmLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL8]r~SSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRSrw,+++++++++++++++++++++++++++++++++++++++++-7ELNLLLLLLLLLLLLLLLLLLLLLLLLLMNOOPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLK1+++++++++ +.S] +.++++++++++++++++++++++}LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL SRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQ+YU]++++++++++++++++++++++++++++++++++++++++++++09ELLLLLLLLLLLLLLLLLLLLLLLLLLLLMNNOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLA,+++++++++++++++++++++++++++++++4DLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLEdURRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQ+Y6E+++++++++++++++++++++++++++++++++++++++++++++0HLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLMMNOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKJJJJJJKLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLJ5+++++++0ZbOY++++++++++++++++++++++LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL NNiRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQR|/+++++++++++++++++++++++++06E++++++++++++++++++9KLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLMMNOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLMMIB=:85.)&#y${'*/68:>DLMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLC+++++++:H+>+++++++++++++++++++++gnLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLOj|RRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ`f+++++++++++++++++++++++++4|sw4,+++++++++++++++,DLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLMNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLKKKKKKKKKKLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKJ?- q^A) 0Jf&6FKKLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL3+++++3tz +.++++++++++++++++++++%:h{LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL7^QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPo|BN+++++++++++++++++++++++++agZa0+++++++++++++++1JLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLMKEB@>7.("t lha l#u'/7>@BEJMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLIB:"t?.@ELLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL?+++++fmai+++++++++++++++++++++LhLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLk~evQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPIf%9++++++++++++++++++++++++%9?L+++++++++++++++;LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLC5,$V 0 +-T!y*3AKLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLD/!t /V(:LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLH1+++CO +.++++++++++++++++++++]dEdLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL5[&XQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP0Zuy +.++++++++++++++++++++++++quuy6E,++++++++++++.ELLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLMNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLJGA+A :&=FJLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLHA$y#$ 3 :@@= 6+@4EKLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL=++CO++++++++++++++++++++%:9]LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL NixQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQ~ci++++++++++++++++++++++++.?tw%9++++++++++++4KLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLMH8)D 5'5DMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLE4hQ'4:<>@@BBA@>=;8.h , 4,@MLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLG?CLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLG--?.?++++++++++++++++++++~%TLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLIf>`PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOO[oFQ+++++++++++++++++++++++ +.v{_f%9,++++++++++;LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLJD(G>"r@JLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKI1B =Z+AIJJKLLLLLLLLLLLLLKJIE3jF# +5$zDKLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKJB5.:KLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL2I +.+++++++++++++++++++AMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL{SPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOR';+++++++++++++++++++++++ISfk#8+++++++++.ELLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLMH;#x`8DLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLMF4=A+6DLMLLLLLLLLLLLLLLLLLLLLLLLMH:/T-CLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLIC;0++8JLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLl{jp+++++++++++++++++++!8LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL NYmPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOn{ .++++++++++++++++++++++/bh3++++++++4KLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLJ3`O,HLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLJ7Y`;FJKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKF>gI-JLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKE91,+++7IKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKFc3C+++++++++++++++++++sx]sLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL<_<_PPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO0Zdj,++++++++++++++++++++++T\]d+=+++++++=LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLH:E'1DLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKA`M)>KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKA+OY>KLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLIE>2++++++5HKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK +L+++++++++++++++++++:HLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL)WOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNQ}GR++++++++++++++++++++++,pt%9+++++-ELLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLK8n *Y&19?@@@@@@@?8.$xOI0HLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLF+ 5 15CKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKD2 "+GLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKC:3-+++++++6HKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKBN+++++++++++++++++++LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL~tROOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNTk2B++++++++++++++++++++++RZpt7E/+++5JLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKE!q&To+;HKKKLLLLLLLLLLLLKKKKF8(lM! >:JLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLK=KN.HKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKF*@>?KLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLJE:/,+++++++++4HKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKLg++++++++++++++++++ +.|LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLBbmzOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNR 7+++++++++++++++++++++&:~LU+++<LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLIFKLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLG/ +-o5<FLMLLLLLLLLLLLLLLLLLLLLLLLMLD<5i$~>MLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLE-1DKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKL@(.JLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLIC;2++++++++++++6HKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKdk++++++++++++++++++8F9]LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL_qPONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN]ppt +.+++++++++++++++++++++fkT]6-CLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLK808CJKLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLK=W/[8HJLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLIG4W/ 4.HLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKAM +!${DJKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKI;D aCLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKJA5/-+++++++++++++5HKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK5D++++++++++++++++++RLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLl~TjNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMO_e+++++++++++++++++++++4nsAQILLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLH/+*-5>DHLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLF0h1@LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL@1XU?KLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLI3b7JJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJE- +I;LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLJE@6-++++++++++++++++6HJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJ^r++++++++++++++++++{ +MLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL<_OgNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMN_qFQ+++++++++++++++++++++^epzNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLA,++++-04;CJLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL@^g?HLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLG<^ +3,KLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLG"u-FJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJI<G ,0KLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKC92.++++++++++++++++++8HJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJ*TJU+++++++++++++++++DPsLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLOCaNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM1X,=++++++++++++++++++++#8euLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLK8++++++++++3<EHJLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLJ5;'>KLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL?$ +-^BLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL@R\<JJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJD$,HLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLJGB8.++++++++++++++++++++,8IJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJ K++++++++++++++++++LhLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL]s<]MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLmz1+++++++++++++++++++,ioGbLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLI1++++++++++++/5:@GLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLG&'BLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLK@*B4LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL5;-DJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJH2$$}ELLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLJB:4.++++++++++++++++++++++,;IJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJ~W_+++++++++++++++++~LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL,W8[MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLRmr/+++++++++++++++++++2B%SLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLE-++++++++++++++,-19CIJKLLLLLLLLLLLLLLLLLLLLLLLLLLLLLAT)!sCKLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKB o"*GLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLJ1 @5IJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJ;C]ELLLLLLLLLLLLLLLLLLLLLLLLLLLKJH?4-,++++++++++++++++++++++++,;IJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJWn 7++++++++++++++++JULLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLO1XMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLetU]++++++++++++++++++++quvLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLK=,+++++++++++++++++*,29>CGLLLLLLLLLLLLLLLLLLLLLLLLM:Hk;KLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLJ:gnALLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLG*o>JIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII@aNALLLLLLLLLLLLLLLLLLLLLLLKF@:2,*++++++++++++++++++++++++++->IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII<\+++++++++++++++++=_LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLcw-WMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL/WAL+++++++++++++++++++#8]pLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLK4++++++++++++++++++++++-.28@IKKLLLLLLLLLLLLLLLLLK1, +02ILLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLI1 +2C=KLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLF s#yFIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIFlHBLLLLLLLLLLLLLLLLLLKJB70.,+++++++++++++++++++++++++++++-@IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII#QCO++++++++++++++++zLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL"S)ULLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLiw.?++++++++++++++++++,in:\LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLH0++++++++++++++++++++++++++*08?DGKLLLLLLLLLLLLI0&DLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLC$~7JLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLC` //IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIF(2ALLLLLLLLLLLLLJFC<3,+++++++++++++++++++++++++++++++++0BIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII++++++++++++++++OYLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLOQLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL%S,++++++++++++++++++3BMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLA-+++++++++++++++++++++++++++++,038>DKLLLLLLLH' 3;ILLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLI; 2-JLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL@T%:HIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIF-D@LLLLLLLLKF>72/++++++++++++++++++++++++++++++++++++2EIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIAM+++++++++++++++$9WoLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLh{}#SLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLbrns .++++++++++++++++++pto|MLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL:+++++++++++++++++++++++++++++++++++,28@GHJG% #xCLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLD#{$xHLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLA8J>IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIH/:BLLJHF@6.+++++++++++++++++++++++++++++++++++++++,6GIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII Jn}++++++++++++++++|.XLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL2Zy NLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLQY`,+++++++++++++++++#8SjLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLJ7+++++++++++++++++++++++++++++++++++++*,01n!3JLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLJ6 5 oGLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL> 7W@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHF/H9:50+*+++++++++++++++++++++++++++++++++++++++++:GHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHVldl+++++++++++++++V_LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLxOLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL\n`LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLynzLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLGbah+++++++++++++++++]d@^KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJ8+++++++++++++++++++++++++++++++)Y 3?KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKC[LALLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLAD(DGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGCVr+++++++++++++++++++++++++++++++++++-=FGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGK=J+++++++++++++ +.%TLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLBbo{OLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL M{IS++++++++++++++++6.VKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJ6+++++++++++++++++++++++++++++*iV@KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKF$}=BLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLDL&DGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGBN "++++++++++++++++++++++++++++++"8COU^^hJaGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG&P0+++++++++++++V_LLLLLLLLLLLLLLLLLLLLLLLLLLLLLL +MlxLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL7[0@+++++++++++++++,]dKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKH3++++++++++++++++++++++++++++nmBKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKG-TCLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLD]%DGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG<@ +6'++++++++++++++++++++++++++AMuzRhGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGNT]+++++++++++++2CVoLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLjw MLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLhv 7+++++++++++++++3p~JJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJ&QkrR[2B)<3-+++++++++++++++++++++v qDJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJH/EDLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLG s"vDGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG3B*++++++++++++++++++ -61BEQz~%PGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGN+++++++++++++3LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLRlgu MLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL&Twz/+++++++++++++++PXZnKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJMesx`gHR5+++++++++++++++++$%"zDJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJ3XELLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLJ)kBGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG,d*++++++++++++++*=[dx}vGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG.TQ[+++++++++++++gmLLLLLLLLLLLLLLLLLLLLLLLLLLLLL +MfuLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLMardj+++++++++++++++,:ZJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJNfrvKT31 .-+++++++++' +3$EJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJI7%bILLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLK+XCGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGD!v!+++++++-047FpvJcGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGL2++++++++++++-?.XLLLLLLLLLLLLLLLLLLLLLLLLLLLLLdsNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLOSZ,++++++++++++++DO3WJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJNfnsYaIS2B-++++)H!tEJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJ5 2kHLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL5 S@FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF>B'++4ANZbms"NFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF?\ms++++++++++++5 +MLLLLLLLLLLLLLLLLLLLLLLLLLLLLZqbrLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLC`9F++++++++++++++/~$QJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJMekpIS/g"yEJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJ: +/ 'HLLLLLLLLLLLLLLLLLLLLLLLLLLLLLM9 0=FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF97:XgwFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF?\#9++++++++++++rxnLLLLLLLLLLLLLLLLLLLLLLLLLLLL)Var NLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL Nt$9++++++++++++++8FyLIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII@]gCIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII7 +2o,-..0012344455555567755555444, 2 -:FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE, ]]]3UFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF GQg}++++++++++++;I:]LLLLLLLLLLLLLLLLLLLLLLLLLLLLbrLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL7[5++++++++++++++otq} +JIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII>\SAIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIJ8 +v+++++++++++++++++++++++++++( ?'4EFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAO\]]xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFct2+++++++++++5LLLLLLLLLLLLLLLLLLLLLLLLLLLLfzbrMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLhvko +.+++++++++++++0@]nJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII'RA@IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII7,"+++++++++++++++++++++++++*D+EFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF=\\\=Z GFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF Gu}+++++++++++ +.SlLLLLLLLLLLLLLLLLLLLLLLLLLLL9]cr MLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL MZa+++++++++++++,hmUiIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIOz:IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIJ4%+++++++++++++++++++++++*a%DEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEED-\\\IEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE>K+++++++++++LVLLLLLLLLLLLLLLLLLLLLLLLLLLLLbrLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKeIS+++++++++++++3KcHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHJXk[jHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH4 <'++++++++++++++++++++++!gAEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEAa\\\`qJb'OEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE"M+++++++++++0xLLLLLLLLLLLLLLLLLLLLLLLLLLLj}bsNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLO|1A+++++++++++++?K;ZHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH>[~LHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHG.W)++++++++++++++++++++%I>EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE6\]_RgHFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEG`5++++++++++0EdLLLLLLLLLLLLLLLLLLLLLLLLLL?``qLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL9[2++++++++++++,pu0UHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHLiw#PHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHG.c+++++++++++++++++++' 9 *9EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\dy]o3TEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEbspv+++++++++++T]PLLLLLLLLLLLLLLLLLLLLLLLLLObr MLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLgvwz0++++++++++++0@,SHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHI1U.THHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHE& v+++++++++++++++++*_/EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE.P\m3T$NIEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEJu*=++++++++++ 7yLLLLLLLLLLLLLLLLLLLLLLLLLLobrMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLR`g+++++++++++++_eNHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHMdVjHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHCi($++++++++++++++++v&CEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE+Qy++++++++++ .!SLLLLLLLLLLLLLLLLLLLLLLLLLPjbrMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLMWlNW++++++++++++/MGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGKMMNefests5KDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDG5++++++++++PLLLLLLLLLLLLLLLLLLLLLLLL\rbrNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLM^p}1+++++++++++5D{LGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGHxGDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDRgv{+++++++++ .w|uLLLLLLLLLLLLLLLLLLLLLLLL1YarLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL!Rmr/+++++++++++ko}HGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG N8UCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC D6E+++++++++8G-WLLLLLLLLLLLLLLLLLLLLLLLLcs MLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLB_T\+++++++++++)<}KFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"NRfCCCCCCCCCCG&M.P6T=XC\TgftvucqNbA[>Y:V5T/Q'MHDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC+++++++syRlLLLLLLLLLLLLLLLLLLLLL7\r|LLLLLLLLLLLLLLLLLLLLLLLLLLLL^q/++++++++diwIDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDE FGHII?ZZkmyGBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBCF^0++++++:H$TLLLLLLLLLLLLLLLLLLLL +Mq|OLLLLLLLLLLLLLLLLLLLLLLLLLLQv{1+++++++1A2RDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD K7UE]Re[lgtx(LAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA>Xdk++++++0}LLLLLLLLLLLLLLLLLLLLLl~r}LLLLLLLLLLLLLLLLLLLLLLLLLLL>^bh,++++++,uxF]DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD1RSfesxK`AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE|2++++++'ULLLLLLLLLLLLLLLLLLLLJfuPLLLLLLLLLLLLLLLLLLLLLLLLLLbrNW+++++++=J\lGDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD KzlyAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'Ldr++++++S\LLLLLLLLLLLLLLLLLLLLQt~ NLLLLLLLLLLLLLLLLLLLLLLLLLQBN+++++++zIDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD`oAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAap&:+++++ .4[LLLLLLLLLLLLLLLLLLLLxOLLLLLLLLLLLLLLLLLLLLLLLLLEa';++++++CNC[DCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCDH^FAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEF] .+++++OLLLLLLLLLLLLLLLLLLLRkwPLLLLLLLLLLLLLLLLLLLLLLLLLjx0+++++5fsFCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC-P.OAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUgkq+++++`hdwLLLLLLLLLLLLLLLLLLL-Wy NLLLLLLLLLLLLLLLLLLLLLLLLPjn+++++,ek5SCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC&L{4Q@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@AD[@M++++7PLLLLLLLLLLLLLLLLLLLyQLLLLLLLLLLLLLLLLLLLLLLLLB_CO+++++8F]lICCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCDlx9T@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@DUf+++++}bwLLLLLLLLLLLLLLLLLLL_u{OLLLLLLLLLLLLLLLLLLLLLLLLWk1+++++?YCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCReM`@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@drkq++++JULLLLLLLLLLLLLLLLLLL?`~ RLLLLLLLLLLLLLLLLLLLLLLLMkxx{,++++JTjv+NBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB.OCZ@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@.N]l++++2ZqLLLLLLLLLLLLLLLLLLO|PLLLLLLLLLLLLLLLLLLLLLLL!RRZ++++,_m DBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB Dp{Pb@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@=W++++\dQLLLLLLLLLLLLLLLLLLuPLLLLLLLLLLLLLLLLLLLLLLL2X$9+++,joE[EBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBCG]F[??????????????????????????????????????????????????????????????F]e+++ .WoLLLLLLLLLLLLLLLLLLIf}%SLLLLLLLLLLLLLLLLLLLLLLLNf1+++FPRdEBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBGH]??????????????????????????????????????????????????????????????F;I+++LLLLLLLLLLLLLLLLLL RPLLLLLLLLLLLLLLLLLLLLLL Np{ek,++6TeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA CQcCZ??????????????????????????????????????????????????????????????C4++LVNiLLLLLLLLLLLLLLLLLL*ULLLLLLLLLLLLLLLLLLLLLLQCN+++tx[i6RBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA H,L???????????????????????????????????????????????????????????????w++ .QLLLLLLLLLLLLLLLLL\s"RLLLLLLLLLLLLLLLLLLLLLL>]*=++HRSeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA[j.M???????????????????????????????????????????????????????????????vW_++oueyLLLLLLLLLLLLLLLLL9]'TLLLLLLLLLLLLLLLLLLLLLM_p}1++Pb.N"IDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA$Jt~F>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>cp++9GLLLLLLLLLLLLLLLLL +M(TLLLLLLLLLLLLLLLLLLLLLP_f++vzs|&KAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH]t~?>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>cp+0\rLLLLLLLLLLLLLLLLLv+ULLLLLLLLLLLLLLLLLLLLL-VJT+MWD@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ H_lRc>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>Xhns+R[PLLLLLLLLLLLLLLLLPj2XLLLLLLLLLLLLLLLLLLLLLVk;H6Ehs@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@'J~7Q>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>Oa2C0AsLLLLLLLLLLLLLLLLP/WLLLLLLLLLLLLLLLLLLLLO|2BUe@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@E[$H>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>Oa.?6[LLLLLLLLLLLLLLLL6ZLLLLLLLLLLLLLLLLLLLL4Y>V@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ BM``m=================================================================H\lrLLLLLLLLLLLLLLLLdw4YLLLLLLLLLLLLLLLLLLLLYmG@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@Edp>U=================================================================G[1YLLLLLLLLLLLLLLL2Y8[MLLLLLLLLLLLLLLLLLLP} A????????????????????????????????????????????????????????????????? G[jkvE=================================================================DYLLLLLLLLLLLLLLLL=]LLLLLLLLLLLLLLLLLLLC`qzB??????????????????????????????????????????????????????????????????CoxFZ==================================================================G[SlLLLLLLLLLLLLLLLz@^LLLLLLLLLLLLLLLLLLLmyYh????????????????????????????????????????????????????????????????????.M_lhtE==================================================================H\)VLLLLLLLLLLLLLLPjA_LLLLLLLLLLLLLLLLLLNI]?????????????????????????????????????????????????????????????????????@Te&H===================================================================K^LLLLLLLLLLLLLLQC`LLLLLLLLLLLLLLLLLLPg:S>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>BVfWf=<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>A,KvcoA<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>Dboy!E<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>D>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>^lxD<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<=w MLLLLLLLLLLL M}QhLLLLLLLLLLLLLLLMcsjt>==============================================================================9QXf`l.J;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AXoLLLLLLLLLLLLJfRhLLLLLLLLLLLLLLLOXg=================================================================================A\iN_<;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+HLLLLLLLLLLLLRWlLLLLLLLLLLLLLLLOgUd==================================================================================='HVes|s|EX;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1LLLLLLLLLLLLLXkMLLLLLLLLLLLLLPJ\====================================================================================>B#FM_}=SA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;(H8PBWZh{fqAU(F::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::S<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<@Uu|We)G> =;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;`mLLLLLLLLLLXkLLLLLLLLLLLQ8P;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;|[g[h[g[hVdP_IZ?T7O"C:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::B`uLLLLLLLLLeyKdMLLLLLLLLLL\n]LLLLLLOf`k>:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;:PzNhLLLL[r+ULLLLNfdn:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::dnK\:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::EX.WLLLL|QLLL N~(F:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;\h9P>:::::::::::::::::::::::::::::::::::::::::::::::::::::::::BK\(ULLL{"RLLL}Sa:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::=jt[h>:::::::::::::::::::::::::::::::::::::::::::::::::::::BVu~"SLLXo{QLL^py?:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: \hzM^9P'F =:::::::::::::::::::::::;!C4MAUbnZpfp=::::::::::::::::::::::::::::::::::::::::::::::::7NpyZh?T C:::::::::::::=;QVdjt|Uc@::::::::::::::::::::::::::::::::::::::::::::@\h`k'F:::::::::::::::::::::::::::::::::::::::::Q`t{)G:::::::::::::::::::::::::::::::::::::BUN],H:::::::::::::::::::::::::::::::"CN^Ye;::::::::::::::::::::::::::@Tv}[g4L)G@;:::::::::::::::@*G=Rjtyks[gM]> { - qspi: Qspi<'static, I, D>, +pub struct FlashMemory { + qspi: Qspi<'static, I, Async>, } -impl> FlashMemory { - pub fn new(qspi: Qspi<'static, I, D>) -> Self { +impl FlashMemory { + pub fn new(qspi: Qspi<'static, I, Async>) -> Self { let mut memory = Self { qspi }; memory.reset_memory(); @@ -279,7 +280,7 @@ async fn main(_spawner: Spawner) -> ! { cs_high_time: ChipSelectHighTime::_1Cycle, fifo_threshold: FIFOThresholdLevel::_16Bytes, }; - let driver = Qspi::new_bk1( + let driver = Qspi::new_bank1( p.QUADSPI, p.PF8, p.PF9, p.PE2, p.PF6, p.PF10, p.PB10, p.DMA2_CH7, config, ); let mut flash = FlashMemory::new(driver); diff --git a/examples/stm32g0/src/bin/i2c_async.rs b/examples/stm32g0/src/bin/i2c_async.rs new file mode 100644 index 000000000..7e3189b05 --- /dev/null +++ b/examples/stm32g0/src/bin/i2c_async.rs @@ -0,0 +1,48 @@ +#![no_std] +#![no_main] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::i2c::{self, I2c}; +use embassy_stm32::time::Hertz; +use embassy_stm32::{bind_interrupts, peripherals}; +use embassy_time::{Duration, Timer}; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct Irqs { + I2C1 => i2c::EventInterruptHandler, i2c::ErrorInterruptHandler; +}); + +const TMP117_ADDR: u8 = 0x48; +const TMP117_TEMP_RESULT: u8 = 0x00; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + info!("Hello world"); + + let p = embassy_stm32::init(Default::default()); + + let mut data = [0u8; 2]; + let mut i2c = I2c::new( + p.I2C1, + p.PB8, + p.PB9, + Irqs, + p.DMA1_CH1, + p.DMA1_CH2, + Hertz(100_000), + Default::default(), + ); + + loop { + match i2c.write_read(TMP117_ADDR, &[TMP117_TEMP_RESULT], &mut data).await { + Ok(()) => { + let temp = f32::from(i16::from_be_bytes(data)) * 7.8125 / 1000.0; + info!("Temperature {}", temp); + } + Err(_) => error!("I2C Error"), + } + + Timer::after(Duration::from_millis(1000)).await; + } +} diff --git a/examples/stm32h7/Cargo.toml b/examples/stm32h7/Cargo.toml index a09af4b97..c6c2d1354 100644 --- a/examples/stm32h7/Cargo.toml +++ b/examples/stm32h7/Cargo.toml @@ -8,6 +8,7 @@ license = "MIT OR Apache-2.0" # Change stm32h743bi to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32h743bi", "time-driver-tim2", "exti", "memory-x", "unstable-pac", "chrono"] } embassy-sync = { version = "0.5.0", path = "../../embassy-sync", features = ["defmt"] } +embassy-embedded-hal = { version = "0.1.0", path = "../../embassy-embedded-hal" } embassy-executor = { version = "0.5.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "proto-ipv6", "dns"] } diff --git a/examples/stm32h7/src/bin/adc.rs b/examples/stm32h7/src/bin/adc.rs index 0009103d1..e9a857a74 100644 --- a/examples/stm32h7/src/bin/adc.rs +++ b/examples/stm32h7/src/bin/adc.rs @@ -51,7 +51,7 @@ async fn main(_spawner: Spawner) { let mut vrefint_channel = adc.enable_vrefint(); loop { - let vrefint = adc.read_internal(&mut vrefint_channel); + let vrefint = adc.read(&mut vrefint_channel); info!("vrefint: {}", vrefint); let measured = adc.read(&mut p.PC0); info!("measured: {}", measured); diff --git a/examples/stm32h7/src/bin/i2c_shared.rs b/examples/stm32h7/src/bin/i2c_shared.rs new file mode 100644 index 000000000..6f4815582 --- /dev/null +++ b/examples/stm32h7/src/bin/i2c_shared.rs @@ -0,0 +1,111 @@ +#![no_std] +#![no_main] + +use core::cell::RefCell; + +use defmt::*; +use embassy_embedded_hal::shared_bus::blocking::i2c::I2cDevice; +use embassy_executor::Spawner; +use embassy_stm32::i2c::{self, I2c}; +use embassy_stm32::mode::Async; +use embassy_stm32::time::Hertz; +use embassy_stm32::{bind_interrupts, peripherals}; +use embassy_sync::blocking_mutex::NoopMutex; +use embassy_time::{Duration, Timer}; +use static_cell::StaticCell; +use {defmt_rtt as _, panic_probe as _}; + +const TMP117_ADDR: u8 = 0x48; +const TMP117_TEMP_RESULT: u8 = 0x00; + +const SHTC3_ADDR: u8 = 0x70; +const SHTC3_WAKEUP: [u8; 2] = [0x35, 0x17]; +const SHTC3_MEASURE_RH_FIRST: [u8; 2] = [0x5c, 0x24]; +const SHTC3_SLEEP: [u8; 2] = [0xb0, 0x98]; + +static I2C_BUS: StaticCell>>> = StaticCell::new(); + +bind_interrupts!(struct Irqs { + I2C1_EV => i2c::EventInterruptHandler; + I2C1_ER => i2c::ErrorInterruptHandler; +}); + +#[embassy_executor::task] +async fn temperature(mut i2c: impl embedded_hal_1::i2c::I2c + 'static) { + let mut data = [0u8; 2]; + + loop { + match i2c.write_read(TMP117_ADDR, &[TMP117_TEMP_RESULT], &mut data) { + Ok(()) => { + let temp = f32::from(i16::from_be_bytes(data)) * 7.8125 / 1000.0; + info!("Temperature {}", temp); + } + Err(_) => error!("I2C Error"), + } + + Timer::after(Duration::from_millis(1000)).await; + } +} + +#[embassy_executor::task] +async fn humidity(mut i2c: impl embedded_hal_1::i2c::I2c + 'static) { + let mut data = [0u8; 6]; + + loop { + // Wakeup + match i2c.write(SHTC3_ADDR, &SHTC3_WAKEUP) { + Ok(()) => Timer::after(Duration::from_millis(20)).await, + Err(_) => error!("I2C Error"), + } + + // Measurement + match i2c.write(SHTC3_ADDR, &SHTC3_MEASURE_RH_FIRST) { + Ok(()) => Timer::after(Duration::from_millis(5)).await, + Err(_) => error!("I2C Error"), + } + + // Result + match i2c.read(SHTC3_ADDR, &mut data) { + Ok(()) => Timer::after(Duration::from_millis(5)).await, + Err(_) => error!("I2C Error"), + } + + // Sleep + match i2c.write(SHTC3_ADDR, &SHTC3_SLEEP) { + Ok(()) => { + let (bytes, _) = data.split_at(core::mem::size_of::()); + let rh = f32::from(u16::from_be_bytes(bytes.try_into().unwrap())) * 100.0 / 65536.0; + info!("Humidity: {}", rh); + } + Err(_) => error!("I2C Error"), + } + + Timer::after(Duration::from_millis(1000)).await; + } +} + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + let p = embassy_stm32::init(Default::default()); + + let i2c = I2c::new( + p.I2C1, + p.PB8, + p.PB9, + Irqs, + p.DMA1_CH4, + p.DMA1_CH5, + Hertz(100_000), + Default::default(), + ); + let i2c_bus = NoopMutex::new(RefCell::new(i2c)); + let i2c_bus = I2C_BUS.init(i2c_bus); + + // Device 1, using embedded-hal-async compatible driver for TMP117 + let i2c_dev1 = I2cDevice::new(i2c_bus); + spawner.spawn(temperature(i2c_dev1)).unwrap(); + + // Device 2, using embedded-hal-async compatible driver for SHTC3 + let i2c_dev2 = I2cDevice::new(i2c_bus); + spawner.spawn(humidity(i2c_dev2)).unwrap(); +} diff --git a/examples/stm32h7/src/bin/spi.rs b/examples/stm32h7/src/bin/spi.rs index aaebdc346..ad4a8aaf7 100644 --- a/examples/stm32h7/src/bin/spi.rs +++ b/examples/stm32h7/src/bin/spi.rs @@ -8,7 +8,6 @@ use cortex_m_rt::entry; use defmt::*; use embassy_executor::Executor; use embassy_stm32::mode::Blocking; -use embassy_stm32::peripherals::SPI3; use embassy_stm32::time::mhz; use embassy_stm32::{spi, Config}; use heapless::String; @@ -16,7 +15,7 @@ use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::task] -async fn main_task(mut spi: spi::Spi<'static, SPI3, Blocking>) { +async fn main_task(mut spi: spi::Spi<'static, Blocking>) { for n in 0u32.. { let mut write: String<128> = String::new(); core::write!(&mut write, "Hello DMA World {}!\r\n", n).unwrap(); diff --git a/examples/stm32h7/src/bin/spi_bdma.rs b/examples/stm32h7/src/bin/spi_bdma.rs index f968df4a7..b2e941078 100644 --- a/examples/stm32h7/src/bin/spi_bdma.rs +++ b/examples/stm32h7/src/bin/spi_bdma.rs @@ -9,7 +9,7 @@ use defmt::*; use embassy_executor::Executor; use embassy_stm32::mode::Async; use embassy_stm32::time::mhz; -use embassy_stm32::{peripherals, spi, Config}; +use embassy_stm32::{spi, Config}; use heapless::String; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; @@ -19,7 +19,7 @@ use {defmt_rtt as _, panic_probe as _}; static mut RAM_D3: [u8; 64 * 1024] = [0u8; 64 * 1024]; #[embassy_executor::task] -async fn main_task(mut spi: spi::Spi<'static, peripherals::SPI6, Async>) { +async fn main_task(mut spi: spi::Spi<'static, Async>) { let read_buffer = unsafe { &mut RAM_D3[0..128] }; let write_buffer = unsafe { &mut RAM_D3[128..256] }; diff --git a/examples/stm32h7/src/bin/spi_dma.rs b/examples/stm32h7/src/bin/spi_dma.rs index 3d3c724eb..731c7fef5 100644 --- a/examples/stm32h7/src/bin/spi_dma.rs +++ b/examples/stm32h7/src/bin/spi_dma.rs @@ -9,13 +9,13 @@ use defmt::*; use embassy_executor::Executor; use embassy_stm32::mode::Async; use embassy_stm32::time::mhz; -use embassy_stm32::{peripherals, spi, Config}; +use embassy_stm32::{spi, Config}; use heapless::String; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::task] -async fn main_task(mut spi: spi::Spi<'static, peripherals::SPI3, Async>) { +async fn main_task(mut spi: spi::Spi<'static, Async>) { for n in 0u32.. { let mut write: String<128> = String::new(); let mut read = [0; 128]; diff --git a/examples/stm32h7rs/src/bin/spi.rs b/examples/stm32h7rs/src/bin/spi.rs index a7767876d..8d6ccc58b 100644 --- a/examples/stm32h7rs/src/bin/spi.rs +++ b/examples/stm32h7rs/src/bin/spi.rs @@ -8,7 +8,6 @@ use cortex_m_rt::entry; use defmt::*; use embassy_executor::Executor; use embassy_stm32::mode::Blocking; -use embassy_stm32::peripherals::SPI3; use embassy_stm32::spi; use embassy_stm32::time::mhz; use heapless::String; @@ -16,7 +15,7 @@ use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::task] -async fn main_task(mut spi: spi::Spi<'static, SPI3, Blocking>) { +async fn main_task(mut spi: spi::Spi<'static, Blocking>) { for n in 0u32.. { let mut write: String<128> = String::new(); core::write!(&mut write, "Hello DMA World {}!\r\n", n).unwrap(); diff --git a/examples/stm32h7rs/src/bin/spi_dma.rs b/examples/stm32h7rs/src/bin/spi_dma.rs index 26b5d6751..cb305351b 100644 --- a/examples/stm32h7rs/src/bin/spi_dma.rs +++ b/examples/stm32h7rs/src/bin/spi_dma.rs @@ -8,14 +8,14 @@ use cortex_m_rt::entry; use defmt::*; use embassy_executor::Executor; use embassy_stm32::mode::Async; +use embassy_stm32::spi; use embassy_stm32::time::mhz; -use embassy_stm32::{peripherals, spi}; use heapless::String; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::task] -async fn main_task(mut spi: spi::Spi<'static, peripherals::SPI3, Async>) { +async fn main_task(mut spi: spi::Spi<'static, Async>) { for n in 0u32.. { let mut write: String<128> = String::new(); let mut read = [0; 128]; diff --git a/examples/stm32l4/src/bin/can.rs b/examples/stm32l4/src/bin/can.rs new file mode 100644 index 000000000..3c4cdac24 --- /dev/null +++ b/examples/stm32l4/src/bin/can.rs @@ -0,0 +1,68 @@ +#![no_std] +#![no_main] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::can::filter::Mask32; +use embassy_stm32::can::{ + Can, Fifo, Frame, Rx0InterruptHandler, Rx1InterruptHandler, SceInterruptHandler, TxInterruptHandler, +}; +use embassy_stm32::peripherals::CAN1; +use embassy_stm32::{bind_interrupts, Config}; +use embassy_time::Timer; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct Irqs { + CAN1_RX0 => Rx0InterruptHandler; + CAN1_RX1 => Rx1InterruptHandler; + CAN1_SCE => SceInterruptHandler; + CAN1_TX => TxInterruptHandler; +}); + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_stm32::init(Config::default()); + + let mut can = Can::new(p.CAN1, p.PA11, p.PA12, Irqs); + + can.modify_filters().enable_bank(0, Fifo::Fifo0, Mask32::accept_all()); + + can.modify_config() + .set_loopback(true) // Receive own frames + .set_silent(true) + .set_bitrate(250_000); + + can.enable().await; + println!("CAN enabled"); + + let mut i = 0; + let mut last_read_ts = embassy_time::Instant::now(); + loop { + let frame = Frame::new_extended(0x123456F, &[i; 8]).unwrap(); + info!("Writing frame"); + + _ = can.write(&frame).await; + + match can.read().await { + Ok(envelope) => { + let (ts, rx_frame) = (envelope.ts, envelope.frame); + let delta = (ts - last_read_ts).as_millis(); + last_read_ts = ts; + info!( + "Rx: {} {:02x} --- {}ms", + rx_frame.header().len(), + rx_frame.data()[0..rx_frame.header().len() as usize], + delta, + ) + } + Err(err) => error!("Error in frame: {}", err), + } + + Timer::after_millis(250).await; + + i += 1; + if i > 2 { + break; + } + } +} diff --git a/examples/stm32l4/src/bin/spe_adin1110_http_server.rs b/examples/stm32l4/src/bin/spe_adin1110_http_server.rs index 694629ede..33149144c 100644 --- a/examples/stm32l4/src/bin/spe_adin1110_http_server.rs +++ b/examples/stm32l4/src/bin/spe_adin1110_http_server.rs @@ -55,12 +55,12 @@ const IP_ADDRESS: Ipv4Cidr = Ipv4Cidr::new(Ipv4Address([192, 168, 1, 5]), 24); // Listen port for the webserver const HTTP_LISTEN_PORT: u16 = 80; -pub type SpeSpi = Spi<'static, peripherals::SPI2, Async>; +pub type SpeSpi = Spi<'static, Async>; pub type SpeSpiCs = ExclusiveDevice, Delay>; pub type SpeInt = exti::ExtiInput<'static>; pub type SpeRst = Output<'static>; pub type Adin1110T = ADIN1110; -pub type TempSensI2c = I2c<'static, peripherals::I2C3, Async>; +pub type TempSensI2c = I2c<'static, Async>; static TEMP: AtomicI32 = AtomicI32::new(0); diff --git a/tests/stm32/src/bin/spi.rs b/tests/stm32/src/bin/spi.rs index 59cb0cfd3..c1576bfeb 100644 --- a/tests/stm32/src/bin/spi.rs +++ b/tests/stm32/src/bin/spi.rs @@ -58,6 +58,14 @@ async fn main(_spawner: Spawner) { spi.blocking_read::(&mut []).unwrap(); spi.blocking_write::(&[]).unwrap(); + // Assert the RCC bit gets disabled on drop. + #[cfg(feature = "stm32f429zi")] + { + defmt::assert!(embassy_stm32::pac::RCC.apb2enr().read().spi1en()); + drop(spi); + defmt::assert!(!embassy_stm32::pac::RCC.apb2enr().read().spi1en()); + } + info!("Test OK"); cortex_m::asm::bkpt(); }