mirror of
https://github.com/embassy-rs/embassy.git
synced 2024-11-21 22:32:29 +00:00
Merge branch 'embassy-rs:main' into add-miso-pullup
This commit is contained in:
commit
0a5820e3ed
17
.github/ci/book.sh
vendored
Executable file
17
.github/ci/book.sh
vendored
Executable file
@ -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
|
9
.vscode/settings.json
vendored
9
.vscode/settings.json
vendored
@ -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",
|
||||
|
@ -18,6 +18,7 @@ Rust's <a href="https://rust-lang.github.io/async-book/">async/await</a> allows
|
||||
- <a href="https://github.com/esp-rs">esp-rs</a>, 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.
|
||||
- <a href="https://github.com/ch32-rs/ch32-hal">ch32-hal</a>, for the WCH 32-bit RISC-V(CH32V) series of chips.
|
||||
|
||||
- **Time that Just Works** -
|
||||
No more messing with hardware timers. <a href="https://docs.embassy.dev/embassy-time">embassy_time</a> 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 <https://probe.rs>.
|
||||
- Change directory to the sample's base directory. For example:
|
||||
|
||||
```bash
|
||||
|
11
ci.sh
11
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 \
|
||||
|
@ -23,7 +23,7 @@ TODO:
|
||||
|
||||
## Running the examples
|
||||
|
||||
- `cargo install probe-rs --features cli`
|
||||
- Install `probe-rs` following the instructions at <https://probe.rs>.
|
||||
- `cd examples/rp`
|
||||
### Example 1: Scan the wifi stations
|
||||
- `cargo run --release --bin wifi_scan`
|
||||
|
@ -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 => {
|
||||
|
8
docs/Makefile
Normal file
8
docs/Makefile
Normal file
@ -0,0 +1,8 @@
|
||||
all:
|
||||
asciidoctor -d book -D book/ index.adoc
|
||||
cp -r images book
|
||||
|
||||
clean:
|
||||
rm -rf book
|
||||
|
||||
.PHONY: all clean
|
@ -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`.
|
||||
|
@ -1,5 +0,0 @@
|
||||
name: ROOT
|
||||
title: Embassy
|
||||
version: dev
|
||||
nav:
|
||||
- modules/ROOT/nav.adoc
|
18
docs/examples/basic/Cargo.toml
Normal file
18
docs/examples/basic/Cargo.toml
Normal file
@ -0,0 +1,18 @@
|
||||
[package]
|
||||
authors = ["Dario Nieuwenhuis <dirbaio@dirbaio.net>"]
|
||||
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"] }
|
1
docs/examples/examples
Symbolic link
1
docs/examples/examples
Symbolic link
@ -0,0 +1 @@
|
||||
../../examples
|
@ -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
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 31 KiB |
Before Width: | Height: | Size: 118 KiB After Width: | Height: | Size: 118 KiB |
Before Width: | Height: | Size: 131 KiB After Width: | Height: | Size: 131 KiB |
16
docs/index.adoc
Normal file
16
docs/index.adoc
Normal file
@ -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]
|
@ -1,18 +0,0 @@
|
||||
[package]
|
||||
authors = ["Dario Nieuwenhuis <dirbaio@dirbaio.net>"]
|
||||
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"] }
|
@ -1 +0,0 @@
|
||||
../../../../examples
|
@ -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]
|
@ -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).
|
11
docs/pages/beginners.adoc
Normal file
11
docs/pages/beginners.adoc
Normal file
@ -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]
|
@ -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:
|
@ -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[]
|
||||
----
|
@ -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.
|
@ -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].
|
@ -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.
|
@ -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:
|
@ -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:
|
||||
|
@ -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:
|
@ -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.
|
@ -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.
|
13
docs/pages/system.adoc
Normal file
13
docs/pages/system.adoc
Normal file
@ -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]
|
@ -55,13 +55,14 @@ where
|
||||
type Error = SpiDeviceError<BUS::Error, CS::Error>;
|
||||
}
|
||||
|
||||
impl<M, BUS, CS> spi::SpiDevice for SpiDevice<'_, M, BUS, CS>
|
||||
impl<M, BUS, CS, Word> spi::SpiDevice<Word> for SpiDevice<'_, M, BUS, CS>
|
||||
where
|
||||
M: RawMutex,
|
||||
BUS: spi::SpiBus,
|
||||
BUS: spi::SpiBus<Word>,
|
||||
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<BUS::Error, CS::Error>;
|
||||
}
|
||||
|
||||
impl<M, BUS, CS> spi::SpiDevice for SpiDeviceWithConfig<'_, M, BUS, CS>
|
||||
impl<M, BUS, CS, Word> spi::SpiDevice<Word> for SpiDeviceWithConfig<'_, M, BUS, CS>
|
||||
where
|
||||
M: RawMutex,
|
||||
BUS: spi::SpiBus + SetConfig,
|
||||
BUS: spi::SpiBus<Word> + 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);
|
||||
}
|
||||
|
@ -48,13 +48,14 @@ where
|
||||
type Error = SpiDeviceError<BUS::Error, CS::Error>;
|
||||
}
|
||||
|
||||
impl<BUS, M, CS> embedded_hal_1::spi::SpiDevice for SpiDevice<'_, M, BUS, CS>
|
||||
impl<BUS, M, CS, Word> embedded_hal_1::spi::SpiDevice<Word> for SpiDevice<'_, M, BUS, CS>
|
||||
where
|
||||
M: RawMutex,
|
||||
BUS: SpiBus,
|
||||
BUS: SpiBus<Word>,
|
||||
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<u8> for SpiDevice<'_, M, BUS, CS>
|
||||
where
|
||||
M: RawMutex,
|
||||
BUS: embedded_hal_02::blocking::spi::Transfer<u8, Error = BusErr>,
|
||||
CS: OutputPin<Error = CsErr>,
|
||||
{
|
||||
type Error = SpiDeviceError<BusErr, CsErr>;
|
||||
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<u8> for SpiDevice<'_, M, BUS, CS>
|
||||
where
|
||||
M: RawMutex,
|
||||
BUS: embedded_hal_02::blocking::spi::Write<u8, Error = BusErr>,
|
||||
CS: OutputPin<Error = CsErr>,
|
||||
{
|
||||
type Error = SpiDeviceError<BusErr, CsErr>;
|
||||
|
||||
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<BUS::Error, CS::Error>;
|
||||
}
|
||||
|
||||
impl<BUS, M, CS> embedded_hal_1::spi::SpiDevice for SpiDeviceWithConfig<'_, M, BUS, CS>
|
||||
impl<BUS, M, CS, Word> embedded_hal_1::spi::SpiDevice<Word> for SpiDeviceWithConfig<'_, M, BUS, CS>
|
||||
where
|
||||
M: RawMutex,
|
||||
BUS: SpiBus + SetConfig,
|
||||
BUS: SpiBus<Word> + 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);
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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 }
|
||||
|
@ -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;
|
||||
|
||||
|
@ -81,24 +81,22 @@ impl From<InputMode> for Divmode {
|
||||
}
|
||||
|
||||
/// PWM driver.
|
||||
pub struct Pwm<'d, T: Slice> {
|
||||
inner: PeripheralRef<'d, T>,
|
||||
pub struct Pwm<'d> {
|
||||
pin_a: Option<PeripheralRef<'d, AnyPin>>,
|
||||
pin_b: Option<PeripheralRef<'d, AnyPin>>,
|
||||
slice: usize,
|
||||
}
|
||||
|
||||
impl<'d, T: Slice> Pwm<'d, T> {
|
||||
impl<'d> Pwm<'d> {
|
||||
fn new_inner(
|
||||
inner: impl Peripheral<P = T> + 'd,
|
||||
slice: usize,
|
||||
a: Option<PeripheralRef<'d, AnyPin>>,
|
||||
b: Option<PeripheralRef<'d, AnyPin>>,
|
||||
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<P = T> + 'd, config: Config) -> Self {
|
||||
Self::new_inner(inner, None, None, Pull::None, config, Divmode::DIV)
|
||||
pub fn new_free<T: Slice>(slice: impl Peripheral<P = T> + '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<P = T> + 'd,
|
||||
pub fn new_output_a<T: Slice>(
|
||||
slice: impl Peripheral<P = T> + 'd,
|
||||
a: impl Peripheral<P = impl ChannelAPin<T>> + '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<P = T> + 'd,
|
||||
pub fn new_output_b<T: Slice>(
|
||||
slice: impl Peripheral<P = T> + 'd,
|
||||
b: impl Peripheral<P = impl ChannelBPin<T>> + '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<P = T> + 'd,
|
||||
pub fn new_output_ab<T: Slice>(
|
||||
slice: impl Peripheral<P = T> + 'd,
|
||||
a: impl Peripheral<P = impl ChannelAPin<T>> + 'd,
|
||||
b: impl Peripheral<P = impl ChannelBPin<T>> + '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<P = T> + 'd,
|
||||
pub fn new_input<T: Slice>(
|
||||
slice: impl Peripheral<P = T> + 'd,
|
||||
b: impl Peripheral<P = impl ChannelBPin<T>> + '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<P = T> + 'd,
|
||||
pub fn new_output_input<T: Slice>(
|
||||
slice: impl Peripheral<P = T> + 'd,
|
||||
a: impl Peripheral<P = impl ChannelAPin<T>> + 'd,
|
||||
b: impl Peripheral<P = impl ChannelBPin<T>> + '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<P = Self> + 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
|
||||
}
|
||||
}
|
||||
|
@ -51,14 +51,20 @@ pub struct BufferedUartTx<'d, T: Instance> {
|
||||
|
||||
pub(crate) fn init_buffers<'d, T: Instance + 'd>(
|
||||
_irq: impl Binding<T::Interrupt, BufferedInterruptHandler<T>>,
|
||||
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::<T>(irq, tx_buffer, rx_buffer);
|
||||
init_buffers::<T>(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::<T>(irq, tx_buffer, rx_buffer);
|
||||
init_buffers::<T>(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::<T>(irq, &mut [], rx_buffer);
|
||||
init_buffers::<T>(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::<T>(irq, &mut [], rx_buffer);
|
||||
init_buffers::<T>(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::<T>(irq, tx_buffer, &mut []);
|
||||
init_buffers::<T>(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::<T>(irq, tx_buffer, &mut []);
|
||||
init_buffers::<T>(irq, Some(tx_buffer), None);
|
||||
|
||||
Self { phantom: PhantomData }
|
||||
}
|
||||
|
||||
fn write<'a>(buf: &'a [u8]) -> impl Future<Output = Result<usize, Error>> + 'a {
|
||||
fn write(buf: &[u8]) -> impl Future<Output = Result<usize, Error>> + '_ {
|
||||
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<T: Instance> interrupt::typelevel::Handler<T::Interrupt> 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.
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -231,7 +231,7 @@ impl<'d, T: Instance> UartTx<'d, T, Blocking> {
|
||||
irq: impl Binding<T::Interrupt, BufferedInterruptHandler<T>>,
|
||||
tx_buffer: &'d mut [u8],
|
||||
) -> BufferedUartTx<'d, T> {
|
||||
buffered::init_buffers::<T>(irq, tx_buffer, &mut []);
|
||||
buffered::init_buffers::<T>(irq, Some(tx_buffer), None);
|
||||
|
||||
BufferedUartTx { phantom: PhantomData }
|
||||
}
|
||||
@ -352,7 +352,7 @@ impl<'d, T: Instance> UartRx<'d, T, Blocking> {
|
||||
irq: impl Binding<T::Interrupt, BufferedInterruptHandler<T>>,
|
||||
rx_buffer: &'d mut [u8],
|
||||
) -> BufferedUartRx<'d, T> {
|
||||
buffered::init_buffers::<T>(irq, &mut [], rx_buffer);
|
||||
buffered::init_buffers::<T>(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::<T>(irq, tx_buffer, rx_buffer);
|
||||
buffered::init_buffers::<T>(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);
|
||||
}
|
||||
|
@ -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"]
|
||||
|
@ -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 {
|
||||
|
@ -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<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl
|
||||
}
|
||||
|
||||
pub struct Vref;
|
||||
impl<T: Instance> AdcPin<T> for Vref {}
|
||||
impl<T: Instance> super::SealedAdcPin<T> for Vref {
|
||||
impl<T: Instance> AdcChannel<T> for Vref {}
|
||||
impl<T: Instance> super::SealedAdcChannel<T> for Vref {
|
||||
fn channel(&self) -> u8 {
|
||||
17
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Temperature;
|
||||
impl<T: Instance> AdcPin<T> for Temperature {}
|
||||
impl<T: Instance> super::SealedAdcPin<T> for Temperature {
|
||||
impl<T: Instance> AdcChannel<T> for Temperature {}
|
||||
impl<T: Instance> super::SealedAdcChannel<T> 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<T>) -> u16 {
|
||||
Self::set_channel_sample_time(pin.channel(), self.sample_time);
|
||||
pub async fn read(&mut self, channel: &mut impl AdcChannel<T>) -> 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
|
||||
}
|
||||
|
||||
|
@ -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<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl
|
||||
}
|
||||
|
||||
pub struct Vref;
|
||||
impl<T: Instance> AdcPin<T> for Vref {}
|
||||
impl<T: Instance> super::SealedAdcPin<T> for Vref {
|
||||
impl<T: Instance> AdcChannel<T> for Vref {}
|
||||
impl<T: Instance> super::SealedAdcChannel<T> for Vref {
|
||||
fn channel(&self) -> u8 {
|
||||
18
|
||||
}
|
||||
@ -47,8 +47,8 @@ impl Vref {
|
||||
}
|
||||
|
||||
pub struct Temperature;
|
||||
impl<T: Instance> AdcPin<T> for Temperature {}
|
||||
impl<T: Instance> super::SealedAdcPin<T> for Temperature {
|
||||
impl<T: Instance> AdcChannel<T> for Temperature {}
|
||||
impl<T: Instance> super::SealedAdcChannel<T> 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<T>) -> u16 {
|
||||
Self::set_channel_sample_time(pin.channel(), self.sample_time);
|
||||
pub async fn read(&mut self, channel: &mut impl AdcChannel<T>) -> 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
|
||||
}
|
||||
|
||||
|
@ -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<T: Instance>(op: i8) {
|
||||
}
|
||||
|
||||
pub struct Vref<T: Instance>(core::marker::PhantomData<T>);
|
||||
impl<T: Instance> AdcPin<T> for Vref<T> {}
|
||||
impl<T: Instance> super::SealedAdcPin<T> for Vref<T> {
|
||||
impl<T: Instance> AdcChannel<T> for Vref<T> {}
|
||||
impl<T: Instance> super::SealedAdcChannel<T> for Vref<T> {
|
||||
fn channel(&self) -> u8 {
|
||||
17
|
||||
}
|
||||
@ -123,8 +123,8 @@ impl<T: Instance> Drop for Vref<T> {
|
||||
}
|
||||
|
||||
pub struct Temperature<T: Instance>(core::marker::PhantomData<T>);
|
||||
impl<T: Instance> AdcPin<T> for Temperature<T> {}
|
||||
impl<T: Instance> super::SealedAdcPin<T> for Temperature<T> {
|
||||
impl<T: Instance> AdcChannel<T> for Temperature<T> {}
|
||||
impl<T: Instance> super::SealedAdcChannel<T> for Temperature<T> {
|
||||
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<T>) -> u16 {
|
||||
self.set_sample_sequence(&[pin.channel()]).await;
|
||||
pub async fn read(&mut self, channel: &mut impl AdcChannel<T>) -> 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<T>, 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<T>, 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<T>) -> SampleTime {
|
||||
Self::get_channel_sample_time(pin.channel())
|
||||
pub fn get_sample_time(&self, channel: &impl AdcChannel<T>) -> SampleTime {
|
||||
Self::get_channel_sample_time(channel.channel())
|
||||
}
|
||||
|
||||
/// Sets the channel sample time
|
||||
|
@ -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<T: Instance> InternalChannel<T> for VrefInt {}
|
||||
impl<T: Instance> super::SealedInternalChannel<T> for VrefInt {
|
||||
impl<T: Instance> AdcChannel<T> for VrefInt {}
|
||||
impl<T: Instance> super::SealedAdcChannel<T> for VrefInt {
|
||||
fn channel(&self) -> u8 {
|
||||
VREF_CHANNEL
|
||||
}
|
||||
@ -42,8 +42,8 @@ impl<T: Instance> super::SealedInternalChannel<T> for VrefInt {
|
||||
|
||||
/// Internal temperature channel.
|
||||
pub struct Temperature;
|
||||
impl<T: Instance> InternalChannel<T> for Temperature {}
|
||||
impl<T: Instance> super::SealedInternalChannel<T> for Temperature {
|
||||
impl<T: Instance> AdcChannel<T> for Temperature {}
|
||||
impl<T: Instance> super::SealedAdcChannel<T> for Temperature {
|
||||
fn channel(&self) -> u8 {
|
||||
TEMP_CHANNEL
|
||||
}
|
||||
@ -51,8 +51,8 @@ impl<T: Instance> super::SealedInternalChannel<T> for Temperature {
|
||||
|
||||
/// Internal battery voltage channel.
|
||||
pub struct Vbat;
|
||||
impl<T: Instance> InternalChannel<T> for Vbat {}
|
||||
impl<T: Instance> super::SealedInternalChannel<T> for Vbat {
|
||||
impl<T: Instance> AdcChannel<T> for Vbat {}
|
||||
impl<T: Instance> super::SealedAdcChannel<T> 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<P>(&mut self, pin: &mut P) -> u16
|
||||
where
|
||||
P: AdcPin<T>,
|
||||
P: crate::gpio::Pin,
|
||||
{
|
||||
pin.set_as_analog();
|
||||
pub fn read(&mut self, channel: &mut impl AdcChannel<T>) -> u16 {
|
||||
channel.setup();
|
||||
|
||||
self.read_channel(pin.channel())
|
||||
}
|
||||
|
||||
/// Read an ADC internal channel.
|
||||
pub fn read_internal(&mut self, channel: &mut impl InternalChannel<T>) -> u16 {
|
||||
self.read_channel(channel.channel())
|
||||
}
|
||||
|
||||
|
@ -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<T: Instance> {
|
||||
#[cfg(any(adc_v1, adc_l0, adc_v2))]
|
||||
fn set_as_analog(&mut self) {}
|
||||
pub(crate) trait SealedAdcChannel<T> {
|
||||
#[cfg(any(adc_v1, adc_l0, adc_v2, adc_g4, adc_v4))]
|
||||
fn setup(&mut self) {}
|
||||
|
||||
#[allow(unused)]
|
||||
fn channel(&self) -> u8;
|
||||
}
|
||||
|
||||
trait SealedInternalChannel<T> {
|
||||
#[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<P = Self> + crate::rcc::R
|
||||
type Interrupt: crate::interrupt::typelevel::Interrupt;
|
||||
}
|
||||
|
||||
/// ADC pin.
|
||||
/// ADC channel.
|
||||
#[allow(private_bounds)]
|
||||
pub trait AdcPin<T: Instance>: SealedAdcPin<T> {}
|
||||
/// ADC internal channel.
|
||||
#[allow(private_bounds)]
|
||||
pub trait InternalChannel<T>: SealedInternalChannel<T> {}
|
||||
pub trait AdcChannel<T>: SealedAdcChannel<T> + Sized {
|
||||
#[allow(unused_mut)]
|
||||
fn degrade_adc(mut self) -> AnyAdcChannel<T> {
|
||||
#[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<T> {
|
||||
channel: u8,
|
||||
_phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T: Instance> AdcChannel<T> for AnyAdcChannel<T> {}
|
||||
impl<T: Instance> SealedAdcChannel<T> for AnyAdcChannel<T> {
|
||||
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<peripherals::$inst> for crate::peripherals::$pin {}
|
||||
|
||||
impl crate::adc::SealedAdcPin<peripherals::$inst> for crate::peripherals::$pin {
|
||||
#[cfg(any(adc_v1, adc_l0, adc_v2))]
|
||||
fn set_as_analog(&mut self) {
|
||||
impl crate::adc::AdcChannel<peripherals::$inst> for crate::peripherals::$pin {}
|
||||
impl crate::adc::SealedAdcChannel<peripherals::$inst> for crate::peripherals::$pin {
|
||||
#[cfg(any(adc_v1, adc_l0, adc_v2, adc_g4, adc_v4))]
|
||||
fn setup(&mut self) {
|
||||
<Self as crate::gpio::SealedPin>::set_as_analog(self);
|
||||
}
|
||||
|
||||
|
@ -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<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl
|
||||
pub struct Vbat;
|
||||
|
||||
#[cfg(not(adc_l0))]
|
||||
impl AdcPin<ADC1> for Vbat {}
|
||||
impl AdcChannel<ADC1> for Vbat {}
|
||||
|
||||
#[cfg(not(adc_l0))]
|
||||
impl super::SealedAdcPin<ADC1> for Vbat {
|
||||
impl super::SealedAdcChannel<ADC1> for Vbat {
|
||||
fn channel(&self) -> u8 {
|
||||
18
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Vref;
|
||||
impl AdcPin<ADC1> for Vref {}
|
||||
impl super::SealedAdcPin<ADC1> for Vref {
|
||||
impl AdcChannel<ADC1> for Vref {}
|
||||
impl super::SealedAdcChannel<ADC1> for Vref {
|
||||
fn channel(&self) -> u8 {
|
||||
17
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Temperature;
|
||||
impl AdcPin<ADC1> for Temperature {}
|
||||
impl super::SealedAdcPin<ADC1> for Temperature {
|
||||
impl AdcChannel<ADC1> for Temperature {}
|
||||
impl super::SealedAdcChannel<ADC1> 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<T>) -> u16 {
|
||||
let channel = pin.channel();
|
||||
pin.set_as_analog();
|
||||
pub async fn read(&mut self, channel: &mut impl AdcChannel<T>) -> 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
|
||||
}
|
||||
|
@ -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<ADC1> for VrefInt {}
|
||||
impl super::SealedAdcPin<ADC1> for VrefInt {
|
||||
impl AdcChannel<ADC1> for VrefInt {}
|
||||
impl super::SealedAdcChannel<ADC1> for VrefInt {
|
||||
fn channel(&self) -> u8 {
|
||||
17
|
||||
}
|
||||
@ -27,8 +27,8 @@ impl VrefInt {
|
||||
}
|
||||
|
||||
pub struct Temperature;
|
||||
impl AdcPin<ADC1> for Temperature {}
|
||||
impl super::SealedAdcPin<ADC1> for Temperature {
|
||||
impl AdcChannel<ADC1> for Temperature {}
|
||||
impl super::SealedAdcChannel<ADC1> 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<ADC1> for Vbat {}
|
||||
impl super::SealedAdcPin<ADC1> for Vbat {
|
||||
impl AdcChannel<ADC1> for Vbat {}
|
||||
impl super::SealedAdcChannel<ADC1> 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<T>) -> u16 {
|
||||
pin.set_as_analog();
|
||||
pub fn read(&mut self, channel: &mut impl AdcChannel<T>) -> 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));
|
||||
|
@ -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<T: Instance> AdcPin<T> for VrefInt {}
|
||||
impl<T: Instance> super::SealedAdcPin<T> for VrefInt {
|
||||
impl<T: Instance> AdcChannel<T> for VrefInt {}
|
||||
impl<T: Instance> super::SealedAdcChannel<T> for VrefInt {
|
||||
fn channel(&self) -> u8 {
|
||||
cfg_if! {
|
||||
if #[cfg(adc_g0)] {
|
||||
@ -30,8 +30,8 @@ impl<T: Instance> super::SealedAdcPin<T> for VrefInt {
|
||||
}
|
||||
|
||||
pub struct Temperature;
|
||||
impl<T: Instance> AdcPin<T> for Temperature {}
|
||||
impl<T: Instance> super::SealedAdcPin<T> for Temperature {
|
||||
impl<T: Instance> AdcChannel<T> for Temperature {}
|
||||
impl<T: Instance> super::SealedAdcChannel<T> for Temperature {
|
||||
fn channel(&self) -> u8 {
|
||||
cfg_if! {
|
||||
if #[cfg(adc_g0)] {
|
||||
@ -49,8 +49,8 @@ impl<T: Instance> super::SealedAdcPin<T> for Temperature {
|
||||
}
|
||||
|
||||
pub struct Vbat;
|
||||
impl<T: Instance> AdcPin<T> for Vbat {}
|
||||
impl<T: Instance> super::SealedAdcPin<T> for Vbat {
|
||||
impl<T: Instance> AdcChannel<T> for Vbat {}
|
||||
impl<T: Instance> super::SealedAdcChannel<T> for Vbat {
|
||||
fn channel(&self) -> u8 {
|
||||
cfg_if! {
|
||||
if #[cfg(adc_g0)] {
|
||||
@ -70,8 +70,8 @@ impl<T: Instance> super::SealedAdcPin<T> for Vbat {
|
||||
cfg_if! {
|
||||
if #[cfg(adc_h5)] {
|
||||
pub struct VddCore;
|
||||
impl<T: Instance> AdcPin<T> for VddCore {}
|
||||
impl<T: Instance> super::SealedAdcPin<T> for VddCore {
|
||||
impl<T: Instance> AdcChannel<T> for VddCore {}
|
||||
impl<T: Instance> super::SealedAdcChannel<T> for VddCore {
|
||||
fn channel(&self) -> u8 {
|
||||
6
|
||||
}
|
||||
@ -82,8 +82,8 @@ cfg_if! {
|
||||
cfg_if! {
|
||||
if #[cfg(adc_u0)] {
|
||||
pub struct DacOut;
|
||||
impl<T: Instance> AdcPin<T> for DacOut {}
|
||||
impl<T: Instance> super::SealedAdcPin<T> for DacOut {
|
||||
impl<T: Instance> AdcChannel<T> for DacOut {}
|
||||
impl<T: Instance> super::SealedAdcChannel<T> 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<T>) -> u16 {
|
||||
pub fn read(&mut self, channel: &mut impl AdcChannel<T>) -> 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));
|
||||
}
|
||||
|
||||
|
@ -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<T: Instance> InternalChannel<T> for VrefInt {}
|
||||
impl<T: Instance> super::SealedInternalChannel<T> for VrefInt {
|
||||
impl<T: Instance> AdcChannel<T> for VrefInt {}
|
||||
impl<T: Instance> super::SealedAdcChannel<T> for VrefInt {
|
||||
fn channel(&self) -> u8 {
|
||||
VREF_CHANNEL
|
||||
}
|
||||
@ -42,8 +42,8 @@ impl<T: Instance> super::SealedInternalChannel<T> for VrefInt {
|
||||
|
||||
/// Internal temperature channel.
|
||||
pub struct Temperature;
|
||||
impl<T: Instance> InternalChannel<T> for Temperature {}
|
||||
impl<T: Instance> super::SealedInternalChannel<T> for Temperature {
|
||||
impl<T: Instance> AdcChannel<T> for Temperature {}
|
||||
impl<T: Instance> super::SealedAdcChannel<T> for Temperature {
|
||||
fn channel(&self) -> u8 {
|
||||
TEMP_CHANNEL
|
||||
}
|
||||
@ -51,8 +51,8 @@ impl<T: Instance> super::SealedInternalChannel<T> for Temperature {
|
||||
|
||||
/// Internal battery voltage channel.
|
||||
pub struct Vbat;
|
||||
impl<T: Instance> InternalChannel<T> for Vbat {}
|
||||
impl<T: Instance> super::SealedInternalChannel<T> for Vbat {
|
||||
impl<T: Instance> AdcChannel<T> for Vbat {}
|
||||
impl<T: Instance> super::SealedAdcChannel<T> 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<P>(&mut self, pin: &mut P) -> u16
|
||||
where
|
||||
P: AdcPin<T>,
|
||||
P: crate::gpio::Pin,
|
||||
{
|
||||
pin.set_as_analog();
|
||||
/// Read an ADC channel.
|
||||
pub fn read(&mut self, channel: &mut impl AdcChannel<T>) -> u16 {
|
||||
channel.setup();
|
||||
|
||||
self.read_channel(pin.channel())
|
||||
}
|
||||
|
||||
/// Read an ADC internal channel.
|
||||
pub fn read_internal(&mut self, channel: &mut impl InternalChannel<T>) -> u16 {
|
||||
self.read_channel(channel.channel())
|
||||
}
|
||||
|
||||
|
429
embassy-stm32/src/dsihost.rs
Normal file
429
embassy-stm32/src/dsihost.rs
Normal file
@ -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<PacketType> 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<P = T> + 'd, te: impl Peripheral<P = impl TePin<T>> + '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 {}
|
||||
};
|
||||
);
|
182
embassy-stm32/src/hsem/mod.rs
Normal file
182
embassy-stm32/src/hsem/mod.rs
Normal file
@ -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<P = T> + '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 {}
|
@ -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<ChannelAndRequest<'d>>,
|
||||
rx_dma: Option<ChannelAndRequest<'d>>,
|
||||
#[cfg(feature = "time")]
|
||||
@ -81,9 +84,9 @@ pub struct I2c<'d, T: Instance, M: Mode> {
|
||||
_phantom: PhantomData<M>,
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> I2c<'d, T, Async> {
|
||||
impl<'d> I2c<'d, Async> {
|
||||
/// Create a new I2C driver.
|
||||
pub fn new(
|
||||
pub fn new<T: Instance>(
|
||||
peri: impl Peripheral<P = T> + 'd,
|
||||
scl: impl Peripheral<P = impl SclPin<T>> + 'd,
|
||||
sda: impl Peripheral<P = impl SdaPin<T>> + '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<T: Instance>(
|
||||
peri: impl Peripheral<P = T> + 'd,
|
||||
scl: impl Peripheral<P = impl SclPin<T>> + 'd,
|
||||
sda: impl Peripheral<P = impl SdaPin<T>> + '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<P = T> + 'd,
|
||||
fn new_inner<T: Instance>(
|
||||
_peri: impl Peripheral<P = T> + 'd,
|
||||
scl: impl Peripheral<P = impl SclPin<T>> + 'd,
|
||||
sda: impl Peripheral<P = impl SdaPin<T>> + 'd,
|
||||
tx_dma: Option<ChannelAndRequest<'d>>,
|
||||
@ -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<T: Instance> interrupt::typelevel::Handler<T::ErrorInterrupt> 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
|
||||
}
|
||||
|
@ -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<T: Instance>() {
|
||||
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<T: Instance>() {
|
||||
});
|
||||
}
|
||||
|
||||
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<i2c::regs::Sr1, Error> {
|
||||
fn check_and_clear_error_flags(info: &'static Info) -> Result<i2c::regs::Sr1, Error> {
|
||||
// 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<u8, Error> {
|
||||
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);
|
||||
});
|
||||
|
||||
|
@ -10,7 +10,7 @@ use super::*;
|
||||
use crate::pac::i2c;
|
||||
|
||||
pub(crate) unsafe fn on_interrupt<T: Instance>() {
|
||||
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<T: Instance>() {
|
||||
});
|
||||
}
|
||||
|
||||
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);
|
||||
|
@ -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<PeripheralRef<'d, AnyPin>>,
|
||||
ws: Option<PeripheralRef<'d, AnyPin>>,
|
||||
ck: Option<PeripheralRef<'d, AnyPin>>,
|
||||
mck: Option<PeripheralRef<'d, AnyPin>>,
|
||||
}
|
||||
|
||||
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<T: Instance>(
|
||||
peri: impl Peripheral<P = T> + 'd,
|
||||
sd: impl Peripheral<P = impl MosiPin<T>> + 'd,
|
||||
ws: impl Peripheral<P = impl WsPin<T>> + '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());
|
||||
|
@ -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)]
|
||||
|
142
embassy-stm32/src/ltdc.rs
Normal file
142
embassy-stm32/src/ltdc.rs
Normal file
@ -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<P = T> + 'd,
|
||||
/*
|
||||
clk: impl Peripheral<P = impl ClkPin<T>> + 'd,
|
||||
hsync: impl Peripheral<P = impl HsyncPin<T>> + 'd,
|
||||
vsync: impl Peripheral<P = impl VsyncPin<T>> + 'd,
|
||||
b0: impl Peripheral<P = impl B0Pin<T>> + 'd,
|
||||
b1: impl Peripheral<P = impl B1Pin<T>> + 'd,
|
||||
b2: impl Peripheral<P = impl B2Pin<T>> + 'd,
|
||||
b3: impl Peripheral<P = impl B3Pin<T>> + 'd,
|
||||
b4: impl Peripheral<P = impl B4Pin<T>> + 'd,
|
||||
b5: impl Peripheral<P = impl B5Pin<T>> + 'd,
|
||||
b6: impl Peripheral<P = impl B6Pin<T>> + 'd,
|
||||
b7: impl Peripheral<P = impl B7Pin<T>> + 'd,
|
||||
g0: impl Peripheral<P = impl G0Pin<T>> + 'd,
|
||||
g1: impl Peripheral<P = impl G1Pin<T>> + 'd,
|
||||
g2: impl Peripheral<P = impl G2Pin<T>> + 'd,
|
||||
g3: impl Peripheral<P = impl G3Pin<T>> + 'd,
|
||||
g4: impl Peripheral<P = impl G4Pin<T>> + 'd,
|
||||
g5: impl Peripheral<P = impl G5Pin<T>> + 'd,
|
||||
g6: impl Peripheral<P = impl G6Pin<T>> + 'd,
|
||||
g7: impl Peripheral<P = impl G7Pin<T>> + 'd,
|
||||
r0: impl Peripheral<P = impl R0Pin<T>> + 'd,
|
||||
r1: impl Peripheral<P = impl R1Pin<T>> + 'd,
|
||||
r2: impl Peripheral<P = impl R2Pin<T>> + 'd,
|
||||
r3: impl Peripheral<P = impl R3Pin<T>> + 'd,
|
||||
r4: impl Peripheral<P = impl R4Pin<T>> + 'd,
|
||||
r5: impl Peripheral<P = impl R5Pin<T>> + 'd,
|
||||
r6: impl Peripheral<P = impl R6Pin<T>> + 'd,
|
||||
r7: impl Peripheral<P = impl R7Pin<T>> + '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 {}
|
||||
};
|
||||
);
|
@ -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<P = Self> + 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")]
|
||||
|
@ -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<crate::peripherals::$adc>
|
||||
impl<'d> crate::adc::SealedAdcChannel<crate::peripherals::$adc>
|
||||
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<crate::peripherals::$adc>
|
||||
impl<'d> crate::adc::AdcChannel<crate::peripherals::$adc>
|
||||
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<crate::peripherals::$adc>
|
||||
impl<'d> crate::adc::SealedAdcChannel<crate::peripherals::$adc>
|
||||
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<crate::peripherals::$adc>
|
||||
impl<'d> crate::adc::AdcChannel<crate::peripherals::$adc>
|
||||
for OpAmpInternalOutput<'d, crate::peripherals::$inst>
|
||||
{
|
||||
}
|
||||
|
@ -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<PeripheralRef<'d, AnyPin>>,
|
||||
d0: Option<PeripheralRef<'d, AnyPin>>,
|
||||
@ -167,259 +170,13 @@ pub struct Ospi<'d, T: Instance, Dma> {
|
||||
d7: Option<PeripheralRef<'d, AnyPin>>,
|
||||
nss: Option<PeripheralRef<'d, AnyPin>>,
|
||||
dqs: Option<PeripheralRef<'d, AnyPin>>,
|
||||
dma: PeripheralRef<'d, Dma>,
|
||||
dma: Option<ChannelAndRequest<'d>>,
|
||||
_phantom: PhantomData<M>,
|
||||
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<P = T> + 'd,
|
||||
sck: impl Peripheral<P = impl SckPin<T>> + 'd,
|
||||
d0: impl Peripheral<P = impl D0Pin<T>> + 'd,
|
||||
d1: impl Peripheral<P = impl D1Pin<T>> + 'd,
|
||||
nss: impl Peripheral<P = impl NSSPin<T>> + 'd,
|
||||
dma: impl Peripheral<P = Dma> + '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<P = T> + 'd,
|
||||
sck: impl Peripheral<P = impl SckPin<T>> + 'd,
|
||||
d0: impl Peripheral<P = impl D0Pin<T>> + 'd,
|
||||
d1: impl Peripheral<P = impl D1Pin<T>> + 'd,
|
||||
nss: impl Peripheral<P = impl NSSPin<T>> + 'd,
|
||||
dma: impl Peripheral<P = Dma> + '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<P = T> + 'd,
|
||||
sck: impl Peripheral<P = impl SckPin<T>> + 'd,
|
||||
d0: impl Peripheral<P = impl D0Pin<T>> + 'd,
|
||||
d1: impl Peripheral<P = impl D1Pin<T>> + 'd,
|
||||
d2: impl Peripheral<P = impl D2Pin<T>> + 'd,
|
||||
d3: impl Peripheral<P = impl D3Pin<T>> + 'd,
|
||||
nss: impl Peripheral<P = impl NSSPin<T>> + 'd,
|
||||
dma: impl Peripheral<P = Dma> + '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<P = T> + 'd,
|
||||
sck: impl Peripheral<P = impl SckPin<T>> + 'd,
|
||||
d0: impl Peripheral<P = impl D0Pin<T>> + 'd,
|
||||
d1: impl Peripheral<P = impl D1Pin<T>> + 'd,
|
||||
d2: impl Peripheral<P = impl D2Pin<T>> + 'd,
|
||||
d3: impl Peripheral<P = impl D3Pin<T>> + 'd,
|
||||
d4: impl Peripheral<P = impl D4Pin<T>> + 'd,
|
||||
d5: impl Peripheral<P = impl D5Pin<T>> + 'd,
|
||||
d6: impl Peripheral<P = impl D6Pin<T>> + 'd,
|
||||
d7: impl Peripheral<P = impl D7Pin<T>> + 'd,
|
||||
nss: impl Peripheral<P = impl NSSPin<T>> + 'd,
|
||||
dma: impl Peripheral<P = Dma> + '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<P = T> + 'd,
|
||||
sck: impl Peripheral<P = impl SckPin<T>> + 'd,
|
||||
d0: impl Peripheral<P = impl D0Pin<T>> + 'd,
|
||||
d1: impl Peripheral<P = impl D1Pin<T>> + 'd,
|
||||
d2: impl Peripheral<P = impl D2Pin<T>> + 'd,
|
||||
d3: impl Peripheral<P = impl D3Pin<T>> + 'd,
|
||||
d4: impl Peripheral<P = impl D4Pin<T>> + 'd,
|
||||
d5: impl Peripheral<P = impl D5Pin<T>> + 'd,
|
||||
d6: impl Peripheral<P = impl D6Pin<T>> + 'd,
|
||||
d7: impl Peripheral<P = impl D7Pin<T>> + 'd,
|
||||
nss: impl Peripheral<P = impl NSSPin<T>> + 'd,
|
||||
dma: impl Peripheral<P = Dma> + '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<P = T> + 'd,
|
||||
d0: Option<PeripheralRef<'d, AnyPin>>,
|
||||
@ -433,12 +190,12 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> {
|
||||
sck: Option<PeripheralRef<'d, AnyPin>>,
|
||||
nss: Option<PeripheralRef<'d, AnyPin>>,
|
||||
dqs: Option<PeripheralRef<'d, AnyPin>>,
|
||||
dma: impl Peripheral<P = Dma> + 'd,
|
||||
dma: Option<ChannelAndRequest<'d>>,
|
||||
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<W: Word>(&mut self, buf: &mut [W], transaction: TransferConfig) -> Result<(), OspiError>
|
||||
where
|
||||
Dma: OctoDma<T>,
|
||||
{
|
||||
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<W: Word>(&mut self, buf: &[W], transaction: TransferConfig) -> Result<(), OspiError>
|
||||
where
|
||||
Dma: OctoDma<T>,
|
||||
{
|
||||
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<W: Word>(&mut self, buf: &mut [W], transaction: TransferConfig) -> Result<(), OspiError>
|
||||
where
|
||||
Dma: OctoDma<T>,
|
||||
{
|
||||
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<W: Word>(&mut self, buf: &[W], transaction: TransferConfig) -> Result<(), OspiError>
|
||||
where
|
||||
Dma: OctoDma<T>,
|
||||
{
|
||||
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<P = T> + 'd,
|
||||
sck: impl Peripheral<P = impl SckPin<T>> + 'd,
|
||||
d0: impl Peripheral<P = impl D0Pin<T>> + 'd,
|
||||
d1: impl Peripheral<P = impl D1Pin<T>> + 'd,
|
||||
nss: impl Peripheral<P = impl NSSPin<T>> + '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<P = T> + 'd,
|
||||
sck: impl Peripheral<P = impl SckPin<T>> + 'd,
|
||||
d0: impl Peripheral<P = impl D0Pin<T>> + 'd,
|
||||
d1: impl Peripheral<P = impl D1Pin<T>> + 'd,
|
||||
nss: impl Peripheral<P = impl NSSPin<T>> + '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<P = T> + 'd,
|
||||
sck: impl Peripheral<P = impl SckPin<T>> + 'd,
|
||||
d0: impl Peripheral<P = impl D0Pin<T>> + 'd,
|
||||
d1: impl Peripheral<P = impl D1Pin<T>> + 'd,
|
||||
d2: impl Peripheral<P = impl D2Pin<T>> + 'd,
|
||||
d3: impl Peripheral<P = impl D3Pin<T>> + 'd,
|
||||
nss: impl Peripheral<P = impl NSSPin<T>> + '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<P = T> + 'd,
|
||||
sck: impl Peripheral<P = impl SckPin<T>> + 'd,
|
||||
d0: impl Peripheral<P = impl D0Pin<T>> + 'd,
|
||||
d1: impl Peripheral<P = impl D1Pin<T>> + 'd,
|
||||
d2: impl Peripheral<P = impl D2Pin<T>> + 'd,
|
||||
d3: impl Peripheral<P = impl D3Pin<T>> + 'd,
|
||||
d4: impl Peripheral<P = impl D4Pin<T>> + 'd,
|
||||
d5: impl Peripheral<P = impl D5Pin<T>> + 'd,
|
||||
d6: impl Peripheral<P = impl D6Pin<T>> + 'd,
|
||||
d7: impl Peripheral<P = impl D7Pin<T>> + 'd,
|
||||
nss: impl Peripheral<P = impl NSSPin<T>> + '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<P = T> + 'd,
|
||||
sck: impl Peripheral<P = impl SckPin<T>> + 'd,
|
||||
d0: impl Peripheral<P = impl D0Pin<T>> + 'd,
|
||||
d1: impl Peripheral<P = impl D1Pin<T>> + 'd,
|
||||
d2: impl Peripheral<P = impl D2Pin<T>> + 'd,
|
||||
d3: impl Peripheral<P = impl D3Pin<T>> + 'd,
|
||||
d4: impl Peripheral<P = impl D4Pin<T>> + 'd,
|
||||
d5: impl Peripheral<P = impl D5Pin<T>> + 'd,
|
||||
d6: impl Peripheral<P = impl D6Pin<T>> + 'd,
|
||||
d7: impl Peripheral<P = impl D7Pin<T>> + 'd,
|
||||
nss: impl Peripheral<P = impl NSSPin<T>> + '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<P = T> + 'd,
|
||||
sck: impl Peripheral<P = impl SckPin<T>> + 'd,
|
||||
d0: impl Peripheral<P = impl D0Pin<T>> + 'd,
|
||||
d1: impl Peripheral<P = impl D1Pin<T>> + 'd,
|
||||
nss: impl Peripheral<P = impl NSSPin<T>> + 'd,
|
||||
dma: impl Peripheral<P = impl OctoDma<T>> + '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<P = T> + 'd,
|
||||
sck: impl Peripheral<P = impl SckPin<T>> + 'd,
|
||||
d0: impl Peripheral<P = impl D0Pin<T>> + 'd,
|
||||
d1: impl Peripheral<P = impl D1Pin<T>> + 'd,
|
||||
nss: impl Peripheral<P = impl NSSPin<T>> + 'd,
|
||||
dma: impl Peripheral<P = impl OctoDma<T>> + '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<P = T> + 'd,
|
||||
sck: impl Peripheral<P = impl SckPin<T>> + 'd,
|
||||
d0: impl Peripheral<P = impl D0Pin<T>> + 'd,
|
||||
d1: impl Peripheral<P = impl D1Pin<T>> + 'd,
|
||||
d2: impl Peripheral<P = impl D2Pin<T>> + 'd,
|
||||
d3: impl Peripheral<P = impl D3Pin<T>> + 'd,
|
||||
nss: impl Peripheral<P = impl NSSPin<T>> + 'd,
|
||||
dma: impl Peripheral<P = impl OctoDma<T>> + '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<P = T> + 'd,
|
||||
sck: impl Peripheral<P = impl SckPin<T>> + 'd,
|
||||
d0: impl Peripheral<P = impl D0Pin<T>> + 'd,
|
||||
d1: impl Peripheral<P = impl D1Pin<T>> + 'd,
|
||||
d2: impl Peripheral<P = impl D2Pin<T>> + 'd,
|
||||
d3: impl Peripheral<P = impl D3Pin<T>> + 'd,
|
||||
d4: impl Peripheral<P = impl D4Pin<T>> + 'd,
|
||||
d5: impl Peripheral<P = impl D5Pin<T>> + 'd,
|
||||
d6: impl Peripheral<P = impl D6Pin<T>> + 'd,
|
||||
d7: impl Peripheral<P = impl D7Pin<T>> + 'd,
|
||||
nss: impl Peripheral<P = impl NSSPin<T>> + 'd,
|
||||
dma: impl Peripheral<P = impl OctoDma<T>> + '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<P = T> + 'd,
|
||||
sck: impl Peripheral<P = impl SckPin<T>> + 'd,
|
||||
d0: impl Peripheral<P = impl D0Pin<T>> + 'd,
|
||||
d1: impl Peripheral<P = impl D1Pin<T>> + 'd,
|
||||
d2: impl Peripheral<P = impl D2Pin<T>> + 'd,
|
||||
d3: impl Peripheral<P = impl D3Pin<T>> + 'd,
|
||||
d4: impl Peripheral<P = impl D4Pin<T>> + 'd,
|
||||
d5: impl Peripheral<P = impl D5Pin<T>> + 'd,
|
||||
d6: impl Peripheral<P = impl D6Pin<T>> + 'd,
|
||||
d7: impl Peripheral<P = impl D7Pin<T>> + 'd,
|
||||
nss: impl Peripheral<P = impl NSSPin<T>> + 'd,
|
||||
dma: impl Peripheral<P = impl OctoDma<T>> + '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<W: Word>(&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<W: Word>(&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<W: Word>(&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<W: Word>(&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()
|
||||
|
@ -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<PeripheralRef<'d, AnyPin>>,
|
||||
d0: Option<PeripheralRef<'d, AnyPin>>,
|
||||
@ -79,93 +82,12 @@ pub struct Qspi<'d, T: Instance, Dma> {
|
||||
d2: Option<PeripheralRef<'d, AnyPin>>,
|
||||
d3: Option<PeripheralRef<'d, AnyPin>>,
|
||||
nss: Option<PeripheralRef<'d, AnyPin>>,
|
||||
dma: PeripheralRef<'d, Dma>,
|
||||
dma: Option<ChannelAndRequest<'d>>,
|
||||
_phantom: PhantomData<M>,
|
||||
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<P = T> + 'd,
|
||||
d0: impl Peripheral<P = impl BK1D0Pin<T>> + 'd,
|
||||
d1: impl Peripheral<P = impl BK1D1Pin<T>> + 'd,
|
||||
d2: impl Peripheral<P = impl BK1D2Pin<T>> + 'd,
|
||||
d3: impl Peripheral<P = impl BK1D3Pin<T>> + 'd,
|
||||
sck: impl Peripheral<P = impl SckPin<T>> + 'd,
|
||||
nss: impl Peripheral<P = impl BK1NSSPin<T>> + 'd,
|
||||
dma: impl Peripheral<P = Dma> + '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<P = T> + 'd,
|
||||
d0: impl Peripheral<P = impl BK2D0Pin<T>> + 'd,
|
||||
d1: impl Peripheral<P = impl BK2D1Pin<T>> + 'd,
|
||||
d2: impl Peripheral<P = impl BK2D2Pin<T>> + 'd,
|
||||
d3: impl Peripheral<P = impl BK2D3Pin<T>> + 'd,
|
||||
sck: impl Peripheral<P = impl SckPin<T>> + 'd,
|
||||
nss: impl Peripheral<P = impl BK2NSSPin<T>> + 'd,
|
||||
dma: impl Peripheral<P = Dma> + '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<P = T> + 'd,
|
||||
d0: Option<PeripheralRef<'d, AnyPin>>,
|
||||
@ -174,11 +96,11 @@ impl<'d, T: Instance, Dma> Qspi<'d, T, Dma> {
|
||||
d3: Option<PeripheralRef<'d, AnyPin>>,
|
||||
sck: Option<PeripheralRef<'d, AnyPin>>,
|
||||
nss: Option<PeripheralRef<'d, AnyPin>>,
|
||||
dma: impl Peripheral<P = Dma> + 'd,
|
||||
dma: Option<ChannelAndRequest<'d>>,
|
||||
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<T>,
|
||||
{
|
||||
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<T>,
|
||||
{
|
||||
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<usize>) {
|
||||
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<P = T> + 'd,
|
||||
d0: impl Peripheral<P = impl BK1D0Pin<T>> + 'd,
|
||||
d1: impl Peripheral<P = impl BK1D1Pin<T>> + 'd,
|
||||
d2: impl Peripheral<P = impl BK1D2Pin<T>> + 'd,
|
||||
d3: impl Peripheral<P = impl BK1D3Pin<T>> + 'd,
|
||||
sck: impl Peripheral<P = impl SckPin<T>> + 'd,
|
||||
nss: impl Peripheral<P = impl BK1NSSPin<T>> + '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<P = T> + 'd,
|
||||
d0: impl Peripheral<P = impl BK2D0Pin<T>> + 'd,
|
||||
d1: impl Peripheral<P = impl BK2D1Pin<T>> + 'd,
|
||||
d2: impl Peripheral<P = impl BK2D2Pin<T>> + 'd,
|
||||
d3: impl Peripheral<P = impl BK2D3Pin<T>> + 'd,
|
||||
sck: impl Peripheral<P = impl SckPin<T>> + 'd,
|
||||
nss: impl Peripheral<P = impl BK2NSSPin<T>> + '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<P = T> + 'd,
|
||||
d0: impl Peripheral<P = impl BK1D0Pin<T>> + 'd,
|
||||
d1: impl Peripheral<P = impl BK1D1Pin<T>> + 'd,
|
||||
d2: impl Peripheral<P = impl BK1D2Pin<T>> + 'd,
|
||||
d3: impl Peripheral<P = impl BK1D3Pin<T>> + 'd,
|
||||
sck: impl Peripheral<P = impl SckPin<T>> + 'd,
|
||||
nss: impl Peripheral<P = impl BK1NSSPin<T>> + 'd,
|
||||
dma: impl Peripheral<P = impl QuadDma<T>> + '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<P = T> + 'd,
|
||||
d0: impl Peripheral<P = impl BK2D0Pin<T>> + 'd,
|
||||
d1: impl Peripheral<P = impl BK2D1Pin<T>> + 'd,
|
||||
d2: impl Peripheral<P = impl BK2D2Pin<T>> + 'd,
|
||||
d3: impl Peripheral<P = impl BK2D3Pin<T>> + 'd,
|
||||
sck: impl Peripheral<P = impl SckPin<T>> + 'd,
|
||||
nss: impl Peripheral<P = impl BK2NSSPin<T>> + 'd,
|
||||
dma: impl Peripheral<P = impl QuadDma<T>> + '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;
|
||||
}
|
||||
|
@ -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
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -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)]
|
||||
|
@ -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,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -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<T: RccPeripheral>() {
|
||||
pub unsafe fn disable<T: RccPeripheral>() {
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -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<PeripheralRef<'d, AnyPin>>,
|
||||
mosi: Option<PeripheralRef<'d, AnyPin>>,
|
||||
miso: Option<PeripheralRef<'d, AnyPin>>,
|
||||
@ -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<P = T> + 'd,
|
||||
impl<'d, M: PeriMode> Spi<'d, M> {
|
||||
fn new_inner<T: Instance>(
|
||||
_peri: impl Peripheral<P = T> + 'd,
|
||||
sck: Option<PeripheralRef<'d, AnyPin>>,
|
||||
mosi: Option<PeripheralRef<'d, AnyPin>>,
|
||||
miso: Option<PeripheralRef<'d, AnyPin>>,
|
||||
@ -119,11 +120,9 @@ impl<'d, T: Instance, M: PeriMode> Spi<'d, T, M> {
|
||||
rx_dma: Option<ChannelAndRequest<'d>>,
|
||||
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) = <u8 as SealedWord>::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(<u8 as SealedWord>::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<W: Word>(&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<W: Word>(&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<W: Word>(&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<W: Word>(&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<T: Instance>(
|
||||
peri: impl Peripheral<P = T> + 'd,
|
||||
sck: impl Peripheral<P = impl SckPin<T>> + 'd,
|
||||
mosi: impl Peripheral<P = impl MosiPin<T>> + '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<T: Instance>(
|
||||
peri: impl Peripheral<P = T> + 'd,
|
||||
sck: impl Peripheral<P = impl SckPin<T>> + 'd,
|
||||
miso: impl Peripheral<P = impl MisoPin<T>> + '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<T: Instance>(
|
||||
peri: impl Peripheral<P = T> + 'd,
|
||||
sck: impl Peripheral<P = impl SckPin<T>> + 'd,
|
||||
mosi: impl Peripheral<P = impl MosiPin<T>> + '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<T: Instance>(
|
||||
peri: impl Peripheral<P = T> + 'd,
|
||||
mosi: impl Peripheral<P = impl MosiPin<T>> + '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<T: Instance>(
|
||||
peri: impl Peripheral<P = T> + 'd,
|
||||
sck: impl Peripheral<P = impl SckPin<T>> + 'd,
|
||||
mosi: impl Peripheral<P = impl MosiPin<T>> + '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<T: Instance>(
|
||||
peri: impl Peripheral<P = T> + 'd,
|
||||
sck: impl Peripheral<P = impl SckPin<T>> + 'd,
|
||||
miso: impl Peripheral<P = impl MisoPin<T>> + '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<T: Instance>(
|
||||
peri: impl Peripheral<P = T> + 'd,
|
||||
sck: impl Peripheral<P = impl SckPin<T>> + 'd,
|
||||
mosi: impl Peripheral<P = impl MosiPin<T>> + '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<T: Instance>(
|
||||
peri: impl Peripheral<P = T> + 'd,
|
||||
mosi: impl Peripheral<P = impl MosiPin<T>> + 'd,
|
||||
tx_dma: impl Peripheral<P = impl TxDma<T>> + '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<T: Instance>(
|
||||
peri: impl Peripheral<P = T> + 'd,
|
||||
tx_dma: impl Peripheral<P = impl TxDma<T>> + 'd,
|
||||
rx_dma: impl Peripheral<P = impl RxDma<T>> + '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 = <peripherals::SUBGHZSPI as crate::rcc::SealedRccPeripheral>::frequency().0;
|
||||
let pclk3_freq = <crate::peripherals::SUBGHZSPI as crate::rcc::SealedRccPeripheral>::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<T: Instance>(
|
||||
peri: impl Peripheral<P = T> + 'd,
|
||||
tx_dma: impl Peripheral<P = impl TxDma<T>> + 'd,
|
||||
rx_dma: impl Peripheral<P = impl RxDma<T>> + '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<W: Word>(regs: Regs, tx_word: W) -> Result<W, Error> {
|
||||
// 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<W> for Spi<'d, T, M> {
|
||||
impl<'d, W: Word, M: PeriMode> embedded_hal_1::spi::SpiBus<W> 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<W> for Spi<'d, T, Async> {
|
||||
impl<'d, W: Word> embedded_hal_async::spi::SpiBus<W> 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<W> 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<P = Self> + 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<(), ()> {
|
||||
|
233
embassy-stm32/src/timer/input_capture.rs
Normal file
233
embassy-stm32/src/timer/input_capture.rs
Normal file
@ -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<P = impl $pin_trait<T>> + '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<P = T> + 'd,
|
||||
_ch1: Option<CapturePin<'d, T, Ch1>>,
|
||||
_ch2: Option<CapturePin<'d, T, Ch2>>,
|
||||
_ch3: Option<CapturePin<'d, T, Ch3>>,
|
||||
_ch4: Option<CapturePin<'d, T, Ch4>>,
|
||||
_irq: impl Binding<T::CaptureCompareInterrupt, CaptureCompareInterruptHandler<T>> + 'd,
|
||||
freq: Hertz,
|
||||
counting_mode: CountingMode,
|
||||
) -> Self {
|
||||
Self::new_inner(tim, freq, counting_mode)
|
||||
}
|
||||
|
||||
fn new_inner(tim: impl Peripheral<P = T> + '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<T> {
|
||||
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<T: GeneralInstance4Channel> {
|
||||
channel: Channel,
|
||||
phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T: GeneralInstance4Channel> Drop for InputCaptureFuture<T> {
|
||||
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<T: GeneralInstance4Channel> Future for InputCaptureFuture<T> {
|
||||
type Output = u32;
|
||||
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
@ -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));
|
||||
|
@ -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<T: CoreInstance> {
|
||||
_phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T: CoreInstance> interrupt::typelevel::Handler<T::UpdateInterrupt> for UpdateInterruptHandler<T> {
|
||||
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<T: GeneralInstance1Channel> {
|
||||
_phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T: GeneralInstance1Channel> interrupt::typelevel::Handler<T::CaptureCompareInterrupt>
|
||||
for CaptureCompareInterruptHandler<T>
|
||||
{
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
238
embassy-stm32/src/tsc/enums.rs
Normal file
238
embassy-stm32/src/tsc/enums.rs
Normal file
@ -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<TscIOPin> for u32 {
|
||||
type Output = u32;
|
||||
fn bitor(self, rhs: TscIOPin) -> Self::Output {
|
||||
let rhs: u32 = rhs.into();
|
||||
self | rhs
|
||||
}
|
||||
}
|
||||
|
||||
impl BitOr<u32> 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<u32> 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<Self, ()> {
|
||||
if val == 0 || val > 128 {
|
||||
return Err(());
|
||||
}
|
||||
Ok(Self(val - 1))
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<u8> 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<u8> 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<u8> 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<u8> 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,
|
||||
}
|
||||
}
|
||||
}
|
936
embassy-stm32/src/tsc/mod.rs
Normal file
936
embassy-stm32/src/tsc/mod.rs
Normal file
@ -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<embassy_stm32::peripherals::TSC, G1> = PinGroup::new();
|
||||
//! g1.set_io2(context.PB13, PinType::Sample);
|
||||
//! g1.set_io3(context.PB14, PinType::Shield);
|
||||
//!
|
||||
//! let mut g2: PinGroup<embassy_stm32::peripherals::TSC, G2> = PinGroup::new();
|
||||
//! g2.set_io1(context.PB4, PinType::Sample);
|
||||
//! g2.set_io2(context.PB5, PinType::Channel);
|
||||
//!
|
||||
//! let mut g7: PinGroup<embassy_stm32::peripherals::TSC, G7> = 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<usize> 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<TscPin<'d, T, C>>,
|
||||
d2: Option<TscPin<'d, T, C>>,
|
||||
d3: Option<TscPin<'d, T, C>>,
|
||||
d4: Option<TscPin<'d, T, C>>,
|
||||
}
|
||||
|
||||
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<P = impl $trait1<T>> + '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<P = impl $trait2<T>> + '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<P = impl $trait3<T>> + '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<P = impl $trait4<T>> + '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<PinGroup<'d, T, G1>>,
|
||||
_g2: Option<PinGroup<'d, T, G2>>,
|
||||
_g3: Option<PinGroup<'d, T, G3>>,
|
||||
_g4: Option<PinGroup<'d, T, G4>>,
|
||||
_g5: Option<PinGroup<'d, T, G5>>,
|
||||
_g6: Option<PinGroup<'d, T, G6>>,
|
||||
#[cfg(any(tsc_v2, tsc_v3))]
|
||||
_g7: Option<PinGroup<'d, T, G7>>,
|
||||
#[cfg(tsc_v3)]
|
||||
_g8: Option<PinGroup<'d, T, G8>>,
|
||||
state: State,
|
||||
config: Config,
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> Tsc<'d, T> {
|
||||
/// Create new TSC driver
|
||||
pub fn new(
|
||||
peri: impl Peripheral<P = T> + 'd,
|
||||
g1: Option<PinGroup<'d, T, G1>>,
|
||||
g2: Option<PinGroup<'d, T, G2>>,
|
||||
g3: Option<PinGroup<'d, T, G3>>,
|
||||
g4: Option<PinGroup<'d, T, G4>>,
|
||||
g5: Option<PinGroup<'d, T, G5>>,
|
||||
g6: Option<PinGroup<'d, T, G6>>,
|
||||
#[cfg(any(tsc_v2, tsc_v3))] g7: Option<PinGroup<'d, T, G7>>,
|
||||
#[cfg(tsc_v3)] g8: Option<PinGroup<'d, T, G8>>,
|
||||
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<PinGroup<'d, T, G1>>,
|
||||
g2: &Option<PinGroup<'d, T, G2>>,
|
||||
g3: &Option<PinGroup<'d, T, G3>>,
|
||||
g4: &Option<PinGroup<'d, T, G4>>,
|
||||
g5: &Option<PinGroup<'d, T, G5>>,
|
||||
g6: &Option<PinGroup<'d, T, G6>>,
|
||||
#[cfg(any(tsc_v2, tsc_v3))] g7: &Option<PinGroup<'d, T, G7>>,
|
||||
#[cfg(tsc_v3)] g8: &Option<PinGroup<'d, T, G8>>,
|
||||
) -> 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<P = T> + 'd,
|
||||
g1: Option<PinGroup<'d, T, G1>>,
|
||||
g2: Option<PinGroup<'d, T, G2>>,
|
||||
g3: Option<PinGroup<'d, T, G3>>,
|
||||
g4: Option<PinGroup<'d, T, G4>>,
|
||||
g5: Option<PinGroup<'d, T, G5>>,
|
||||
g6: Option<PinGroup<'d, T, G6>>,
|
||||
#[cfg(any(tsc_v2, tsc_v3))] g7: Option<PinGroup<'d, T, G7>>,
|
||||
#[cfg(tsc_v3)] g8: Option<PinGroup<'d, T, G8>>,
|
||||
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<P = Self> + 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);
|
@ -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<ReadCompletionEvent, 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, 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() }
|
||||
|
@ -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::<T>(config)
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user