Compare commits

...

389 Commits

Author SHA1 Message Date
a4f9e7cbcc Merge #1071
1071: refactor: autodetect macro variant r=Dirbaio a=lulf

Apply heuristics using target_arch, target_os and target_family to determine which variant of the entry point to use.

Co-authored-by: Ulf Lilleengen <lulf@redhat.com>
2022-11-23 13:21:59 +00:00
de95ab264d Merge pull request #1073 from embassy-rs/revert-riscv-race
fix: revert race condition introduced for riscv
2022-11-23 14:00:26 +01:00
04a7d97673 refactor: autodetect macro variant
Export all main macro per target architecture from embassy-macros,
and select the appropriate macro in embassy-executor.
2022-11-23 13:54:59 +01:00
50c5cc5db6 fix: revert race condition introduced for riscv 2022-11-23 13:17:05 +01:00
b76631bebe Merge #1069
1069: GPIOTE InputChannel with mutable reference. r=Dirbaio a=Ardelean-Calin

Adding these changes enables us to define a channel using a mutable reference to `GPIOTE_CH(n)`, similar to how we can do with other drivers. So instead of using:
```rust
let p = embassy_nrf::init(config);
let freq_in = InputChannel::new(
    p.GPIOTE_CH0,
    Input::new(&mut p.P0_19, embassy_nrf::gpio::Pull::Up),
    embassy_nrf::gpiote::InputChannelPolarity::HiToLo,
);
```
we can use:
```rust
let p = embassy_nrf::init(config);
let freq_in = InputChannel::new(
    &mut p.GPIOTE_CH0,
    Input::new(&mut p.P0_19, embassy_nrf::gpio::Pull::Up),
    embassy_nrf::gpiote::InputChannelPolarity::HiToLo,
);
```
therefore not giving ownership to GPIOTE_CH0.

Co-authored-by: Ardelean Călin Petru <ardelean.calin@outlook.com>
Co-authored-by: Ardelean Calin <ardelean.calin@proton.me>
2022-11-23 12:17:02 +00:00
eae67d0be8 Review comments. Corrected unused fields. 2022-11-23 14:16:18 +02:00
2fa2c1a6fe Merge #1054
1054: riscv fixes r=lulf a=swolix

With these changes I can run embassy on our RISC-V processor, please consider merging this, feedback is very welcome.

I don't fully understand the code in the executor, but I have implemented a critical section by globally disabling interrupts, which means the wfi inside the critical section will hang the whole thing.

Co-authored-by: Sijmen Woutersen <sijmen.woutersen@gmail.com>
2022-11-23 09:24:11 +00:00
83b199a874 Merge #1056
1056: embassy-nrf: Add TWIS module r=Dirbaio a=kalkyl

Verified to be working on nrf9160

Co-authored-by: kalkyl <henrik.alser@me.com>
Co-authored-by: Henrik Alsér <henrik.alser@me.com>
2022-11-22 21:50:42 +00:00
cf900a8a3f Rename write to respond_to_read 2022-11-22 22:10:04 +01:00
4f2f375777 Corrected order of use statements. 2022-11-22 17:45:05 +02:00
e7c876d744 Changed pin to private as it is for OutputChannel 2022-11-22 17:36:22 +02:00
64c2e1b9b6 Switched to PeripheralRef for channel. 2022-11-22 17:35:38 +02:00
61be0e75c8 Merge #1068
1068: Add Default to some types r=Dirbaio a=mkj

These are a couple of places I've found `Default` to be handy

Co-authored-by: Matt Johnston <matt@ucc.asn.au>
2022-11-22 15:04:24 +00:00
a074cd0625 Update gpiote.rs
Adding these changes enables us to define a channel using a mutable reference to `GPIOTE_CH(n)`, similar to how we can do with other drivers.
So instead of using:
```rust
let freq_in = InputChannel::new(
    p.GPIOTE_CH0,
    Input::new(&mut p.P0_19, embassy_nrf::gpio::Pull::Up),
    embassy_nrf::gpiote::InputChannelPolarity::HiToLo,
);
```
we can use:
```rust
let freq_in = InputChannel::new(
    &mut p.GPIOTE_CH0,
    Input::new(&mut p.P0_19, embassy_nrf::gpio::Pull::Up),
    embassy_nrf::gpiote::InputChannelPolarity::HiToLo,
);
```
2022-11-22 16:56:04 +02:00
ca4f615b25 Merge #1067
1067: doc: update cargo manifests with keywords r=lulf a=lulf



Co-authored-by: Ulf Lilleengen <lulf@redhat.com>
2022-11-22 13:59:28 +00:00
536b6a2de5 sync/signal: Implement Default for Signal 2022-11-22 21:55:42 +08:00
51233c0357 doc: update cargo manifests with keywords 2022-11-22 14:51:23 +01:00
5c52d6c217 Merge pull request #1066 from embassy-rs/embassy-macros-doc
doc: add README to embassy-macro
2022-11-22 14:31:56 +01:00
f474817872 doc: add README to embassy-macro
Documents the main and task macros.
2022-11-22 13:57:41 +01:00
97cb95bbf4 Merge #1042
1042: embassy-nrf: Add SPIS module r=Dirbaio a=kalkyl

Verified to be working on nrf9160

Co-authored-by: Henrik Alsér <henrik.alser@me.com>
Co-authored-by: Henrik Alsér <henrik.alser@ucsmindbite.se>
Co-authored-by: kalkyl <henrik.alser@me.com>
2022-11-22 11:20:14 +00:00
99c561a749 Merge #1065
1065: embassy-nrf: Default disable UARTE (nrf9160) r=Dirbaio a=kalkyl

Uarte is enabled by default on the nrf9160, which is both bad for power consumption and renders the other "shared" peripherals unusable. This might be an SPM bug, but had the same issue with all pre-compiled SPM:s available out there, so adding this fix until we figure out.

Co-authored-by: Henrik Alsér <henrik.alser@me.com>
2022-11-22 11:02:50 +00:00
f09745dfe1 embassy-nrf: Default disable UARTE (nrf9160) 2022-11-22 02:21:06 +01:00
da9f82f507 Fix pin refs 2022-11-22 02:13:03 +01:00
f13639e78c Merge #1059
1059: embassy-rp: Add basic ADC module r=kalkyl a=kalkyl

Oneshot ADC

Co-authored-by: Henrik Alsér <henrik.alser@me.com>
2022-11-22 01:06:25 +00:00
908eef2775 Change interrupt modify into write 2022-11-22 02:03:34 +01:00
633ffe46ae config write, docs, add address_match_index 2022-11-22 01:57:00 +01:00
e6b9722a31 Remove nrf9160 UARTE fix 2022-11-22 01:07:59 +01:00
b8f51c6496 Merge #1057
1057: stm32g0: Fix ADC for channels above 14 r=Dirbaio a=jaxter184

using the CHSELR register in sequence mode does not support ADC channels above 14. Also, it seems like the sequencer itself wasn't being used anyway, so I turned it off (maybe the whole block from L72..L76 could be removed?) and used a bit shift.

Co-authored-by: Jaxter Kim <jaxter.kim@elektron.se>
2022-11-21 23:58:28 +00:00
33ee48b9e8 Merge branch 'spis' of github.com:kalkyl/embassy into spis 2022-11-22 00:55:46 +01:00
a6d941fac3 Fix txonly/rxonly data pin dir, _from_ram and doc 2022-11-22 00:55:05 +01:00
15b4ed2c67 Merge #1060
1060: feat: embassy-usb-logger and example for rpi pico r=Dirbaio a=lulf

* Add embassy-usb-logger which allows logging over USB for any device implementing embassy-usb
* Add example using logger for rpi pico

Co-authored-by: Ulf Lilleengen <lulf@redhat.com>
2022-11-21 22:52:58 +00:00
bbfb786139 Merge #1064
1064: Fix LoRaWAN PHY settings for SX126x driver r=Dirbaio a=jbeaurivage

While working on #1023 / #1041, I noticed that the `lorawan_device::PhyTxRx` implementation does not conform to the LoRaWAN standard, and therefore devices using this driver could never communicate with a gateway. This PR backports the changes I've made to fix the offending parameters, and I can confirm that the driver now works with LoRaWAN networks.

* Set preamble length to 8 symbols
* Set polarity to inverted for received messages

Co-authored-by: Justin Beaurivage <justin@wearableavionics.com>
2022-11-21 22:08:46 +00:00
81dc532d2d Fix LoRaWAN PHY settings for SX126x driver
* Set preamble length to 8 symbols
* Set polarity to inverted for received messages
2022-11-21 12:08:44 -05:00
06fb3e4251 docs: add missing README for usb-logger 2022-11-21 11:24:53 +01:00
4943dec1a7 Merge remote-tracking branch 'upstream/master' 2022-11-20 20:04:23 +01:00
a444a65ebf feat: embassy-usb-logger and example for rpi pico
* Add embassy-usb-logger which allows logging over USB for any device
  implementing embassy-usb
* Add example using logger for rpi pico.
2022-11-18 11:22:58 +01:00
9f870a5edf Cleanup 2022-11-15 16:31:19 +01:00
eb149a0bd4 embassy-rp: Add basic ADC module 2022-11-15 16:12:07 +01:00
551b54ddcb stm32g0: Fix ADC for channels above 14 2022-11-15 12:56:47 +01:00
2528f45138 Merge #1058
1058: Fix some errors in the documentation r=lulf a=johannesneyer



Co-authored-by: Johannes Neyer <johannes.neyer@gmail.com>
2022-11-15 11:35:43 +00:00
9505a6f752 [doc] Remove obsolete code sample 2022-11-15 10:10:36 +01:00
ea61c19280 [doc] Fix some grammar 2022-11-15 10:10:36 +01:00
bcec55464f [doc] Fix line indices of basic example 2022-11-15 10:10:33 +01:00
0b066b22d1 Check events_acquired 2022-11-14 16:24:21 +01:00
3a1ddd66c6 Cleanup interrupts 2022-11-14 16:18:11 +01:00
8d2d5a30a5 Single waker 2022-11-14 11:39:55 +01:00
43c1afb6a6 Return number of bytes written, add address match getter 2022-11-14 11:22:14 +01:00
eba42cb5f4 embassy-nrf: Add TWIS module 2022-11-13 22:15:19 +01:00
5cfad3f853 Feature gate UARTE disable 2022-11-13 02:37:23 +01:00
dca11095e2 Disable UARTE in embassy-nrf::init 2022-11-13 01:49:55 +01:00
e70ae71ecc restore SIGNAL_WORK_THREAD_MODE 2022-11-12 10:58:37 +01:00
d05979c708 Merge #1052 #1053
1052: stm32: Fix watchdog division by zero for 256 prescaler, add watchdog … r=lulf a=matoushybl

…example for H7

The problem is that `2u8.powi(8) == 0`, which causes division by zero.

1053: Disable MMC interrupts r=lulf a=matoushybl

MMC interrupts can cause firmware hangup - refer to: https://github.com/stm32-rs/stm32h7xx-hal/issues/275 for more information

Fixes #594 

Co-authored-by: Matous Hybl <hyblmatous@gmail.com>
2022-11-11 08:04:16 +00:00
6e1120e17e riscv support 2022-11-10 17:39:41 +01:00
99682d313b Disable MMC interrupts
MMC interrupts can cause firmware hangup - refer to: https://github.com/stm32-rs/stm32h7xx-hal/issues/275 for more information
2022-11-10 17:21:42 +01:00
cbc97758e3 stm32: Fix watchdog division by zero for 256 prescaler, add watchdog example for H7 2022-11-10 15:56:28 +01:00
059610a8de Merge #1047
1047: Ensure embassy-lora stm32wl supports log crate r=lulf a=lulf



Co-authored-by: Ulf Lilleengen <ulf.lilleengen@gmail.com>
2022-11-09 09:57:36 +00:00
a3a58e8e4a Special handling for log and defmt 2022-11-09 10:04:37 +01:00
bd5ef80bec Ensure embassy-lora stm32wl supports log crate 2022-11-07 20:51:29 +01:00
c53614f057 Merge #1046
1046: embassy-stm32: Fix bug when Uart::read future is dropped and DMA request was not stopped r=lulf a=guillaume-michel

fixes #1045 

regression was introduced with PR #1031

Co-authored-by: Guillaume MICHEL <guillaume@squaremind.io>
2022-11-07 17:48:04 +00:00
1365ce6ab8 embassy-stm32: Fix bug when Uart::read future is dropped and DMA request was not stopped
fixes issue #1045

regression was introduced with PR #1031
2022-11-07 17:46:32 +01:00
14a2d15240 Derive Default for WakerRegistration
This simplifies creating arrays of WakerRegistrations
2022-11-06 11:33:38 +08:00
af34fc4ccc rustfmt 2022-11-05 01:40:20 +01:00
aecfce1159 rustfmt 2022-11-05 01:36:29 +01:00
207fa19551 Acquire semaphore on blocking 2022-11-05 01:34:52 +01:00
7da18e194a Add status checks 2022-11-05 01:12:25 +01:00
a3e8a6bc3a rustfmt 2022-11-05 00:19:52 +01:00
1920e90dcd embassy-nrf: Add SPIS module 2022-11-05 00:15:43 +01:00
b99533607c Merge #1039
1039: stm32-metapac-gen: Use `serde_json` to parse json files r=Dirbaio a=GrantM11235

This makes stm32-metapac-gen over twice as fast. A full run on my desktop goes from about six and a half seconds to about three seconds. Suprisingly, it also reduces the fresh compile time by almost a second.

Co-authored-by: Grant Miller <GrantM11235@gmail.com>
2022-11-01 20:58:17 +00:00
ea4d08b6cf stm32-metapac-gen: Use serde_json to parse json files 2022-11-01 14:52:43 -05:00
05968bf0f3 Merge #1037
1037: Add uart async task example r=miathedev a=miathedev

Dear Embassy Team,

here i propose an additional async uart pass-through example for the STM32WL.


Because im quite new to Rust, is there something like **interfaces**?

The code: 
```
mut usart1: Uart<
    'static,
    embassy_stm32::peripherals::USART1,
    embassy_stm32::peripherals::DMA1_CH3,
    embassy_stm32::peripherals::DMA1_CH4,
>,
mut usart2: Uart<
    'static,
    embassy_stm32::peripherals::LPUART1,
    embassy_stm32::peripherals::DMA1_CH5,
    embassy_stm32::peripherals::DMA1_CH6,
>,
```
is quite ugly in my opinion. I would like to allow any Type of DMA and USART/UART as argument. Is this possible somehow?
Im open to any feedback.

With love,
Mia

Co-authored-by: miathedev <mia@metzler.systems>
2022-11-01 08:48:54 +00:00
fc086fd4ba Add uart async example 2022-11-01 10:38:02 +01:00
ea702b3719 Merge #1038
1038: (embassy-boot): Move default initializer function to Default trait implementation r=lulf a=MathiasKoch



Co-authored-by: Mathias <mk@blackbird.online>
2022-11-01 08:05:37 +00:00
97d18c5ffb Move default initializer function to Default trait implementation 2022-11-01 07:54:43 +01:00
eed34f945c Merge #1036
1036: Fix ascii table in BootLoader doc comment r=lulf a=danbev

Signed-off-by: Daniel Bevenius <daniel.bevenius@gmail.com>

Co-authored-by: Daniel Bevenius <daniel.bevenius@gmail.com>
2022-10-29 13:28:54 +00:00
0b2d6996e8 Fix ascii table in BootLoader doc comment
Signed-off-by: Daniel Bevenius <daniel.bevenius@gmail.com>
2022-10-29 15:16:09 +02:00
e7fdd500d8 Merge #951
951: (embassy-rp): Implementation of generic flash mutation access r=Dirbaio a=MathiasKoch

I have attempted to utilize the work done in `rp2040-flash` by implementing `embedded-storage` traits on top, for RP2040.

Concerns:
1. ~~Should the DMA be paused where I have put a FIXME note? `DMA_CHx.ctrl_trig().write(|w| { w.set_en(false) })`? If so, how to properly do that without have control over the peripheral for the DMA channels? And if so, I assume we should only re-enable/unpause the ones that were enabled before?~~
2. ~~Should I make sure core2 is halted as part of this code? I am not sure if ea8ab1ac80/examples/flash_example.rs (L103-L109) is heavy/slow code to run?~~
3. ~~Any good way of making this configurable over `FLASH_SIZE`, `WRITE_SIZE` and `ERASE_SIZE` without doing it as generics or parameters, as those make it possible to do differing configs throughout the same program, which feels wrong? Preferably, a compile-time option?~~


**EDIT:**
I have implemented the flash API here under the assumption that all external QSPI nor flashes are infact `Multiwrite` capable, as this makes it possible to use the ROM function for writes of 1 bytes at a time.

I have also added a HIL test for this, but because HIL tests are running 100% from RAM and I wanted to make sure it still works when running from flash, I have also added an example testing erase/write cycles of entire sectors, as well as single bytes in multi-write style.

Ping `@Dirbaio` 

Co-authored-by: Mathias <mk@blackbird.online>
Co-authored-by: Vincent Stakenburg <v.stakenburg@sinewave.nl>
Co-authored-by: Joakim Hulthe <joakim@hulthe.net>
Co-authored-by: Alex Martens <alex@thinglab.org>
Co-authored-by: Ulf Lilleengen <ulf.lilleengen@gmail.com>
Co-authored-by: Dario Nieuwenhuis <dirbaio@dirbaio.net>
2022-10-28 12:19:56 +00:00
1f246d0e37 Merge #1034
1034: stm32/usart: Fix bug where USART idle flag could end a `read` prematuraly r=Dirbaio a=guillaume-michel

on STM32, when setting USART `detect_previous_overrun = true`, the idle flag is not cleared and could result in premature end of the `read` method.

This PR fixes that.

Co-authored-by: Guillaume MICHEL <guillaume@squaremind.io>
2022-10-28 11:22:53 +00:00
a7d5c87049 Merge #1033
1033: stm32/usart: Add missing constructor with hardware flow control r=Dirbaio a=guillaume-michel

This PR follows #1031 and #987 and add missing constructors with hardware flow control.

It also factor general UART configuration like word size, parity, ... used in `Uart`, `UartRx`, `UartTx` and `BufferedUart`.

Co-authored-by: Guillaume MICHEL <guillaume@squaremind.io>
2022-10-28 11:13:11 +00:00
49e1091309 embassy-stm32: Fix bug where USART idle flag could end a read prematuraly 2022-10-28 10:49:59 +02:00
79b49c6fae embassy-stm32: remove duplicated code for USART general configuration 2022-10-28 09:32:05 +02:00
f053bf742c embassy-stm32: Add support for hardware flow control for BufferedUart 2022-10-28 09:04:36 +02:00
9423987ac5 embassy-stm32: Add hardware flow control constructor for UartRx and UartTx 2022-10-28 09:04:36 +02:00
bc21b6efaf Add delay to flash test to allow time to parse RTT header 2022-10-27 12:49:20 +02:00
a7b90c7fb6 Remove unused imports from test 2022-10-27 11:36:46 +02:00
4e61d83555 Merge #1032
1032: stm32/adc: Misc refactoring r=Dirbaio a=GrantM11235

Noteworthy changes:

- Fixed a few typos in the `SampleTime`s
- `set_resolution` now writes directly to the configuration register. This also fixed a bug in v3 where the resolution was changed while `ADEN` is enabled, which the datasheet says isn't allowed.

Co-authored-by: Grant Miller <GrantM11235@gmail.com>
2022-10-27 08:45:12 +00:00
c871fe0848 Rebase on master 2022-10-27 07:12:34 +02:00
3c6c382465 Remove random delay from example, and move flash functions to allow using without embedded-storage in scope 2022-10-27 07:10:35 +02:00
171b764d82 Refactor: Use PeripheralRef 2022-10-26 18:36:04 -05:00
08c8022583 Refactor: Reorder _version cfgs 2022-10-26 18:04:52 -05:00
4f2dcca34b Refactor: Fix v4 RccPeripheral bounds 2022-10-26 17:59:44 -05:00
9c30d565b9 Refactor: Factor out Adc struct declaration 2022-10-26 17:51:12 -05:00
f363f6ce92 Refactor: Don't return references to pointers 2022-10-26 17:35:06 -05:00
6bf24b4d1a Refactor: Remove unused Common trait 2022-10-26 17:35:01 -05:00
88bbc238b7 Set resolution directly 2022-10-26 17:07:58 -05:00
2cfe2439c9 Refactor: Impl From for SampleTime and Resolution 2022-10-26 17:07:58 -05:00
7b38b95e10 Refactor: Factor out Resolution 2022-10-26 17:07:58 -05:00
5142674786 Fix pre-existing SampleTime typos 2022-10-26 17:07:50 -05:00
a5b1d2237f Refactor: Factor out SampleTime 2022-10-26 17:06:44 -05:00
61560e740d time: add missing cargo manifest fields. 2022-10-26 22:18:10 +02:00
d2246ae693 Release embassy-sync, embassy-time v0.1.0 2022-10-26 22:13:27 +02:00
7f499f3edc Merge #1024
1024: stm32/adc: Remove voltage and temperature conversions r=Dirbaio a=GrantM11235

The current conversion utilities are confusing and a bit of a footgun. (Two out of the three examples got it wrong! They didn't measure vref at all, so all the conversions are completely wrong if vcca isn't 3.3v)

I think we should eventually have some sort of conversion utilities in the HAL, but for now I think it is best to just remove it and let the users do their own math.

cc `@chemicstry` 

Co-authored-by: Grant Miller <GrantM11235@gmail.com>
2022-10-26 19:44:06 +00:00
01e23bf9dd Merge #1025
1025: Implement I2C timeouts, second attempt r=Dirbaio a=chemicstry

This is an alterrnative to #1022 as discussed there.

Timeouts are implemented using suggested `check_timeout: impl Fn() -> Result<(), Error>` function, which does not depend on `embassy-time` by default and is a noop for regular I2C.

This also adds `time` feature like in `embassy-nrf` to enable `embassy-time` dependencies. While at it, I also gated some other peripherals that depend on `embassy-time`, notably `usb` and (partially) `subghz`.

`TimeoutI2c` is currently only implemented for i2cv1, because i2cv2 has additional complications:
- Async methods still use a lot of busy waiting code in between DMA transfers, so simple `with_timeout()` will not work and it will have to use both types of timeouts. It could probably be rewritten to replace busy waits with IRQs, but that's outside the scope of this PR.
- I2C definition `I2c<'d, T, TXDMA, RXDMA>` is different from i2cv1 `I2c<'d, T>` making it hard to share single `TimeoutI2c` wrapper. A couple of options here:
  - Duplicate `TimeoutI2c` code
  - Add dummy `TXDMA`, `RXDMA` types to i2cv1 considering that in the future it should also support DMA

Co-authored-by: chemicstry <chemicstry@gmail.com>
2022-10-26 19:34:43 +00:00
e5097a8866 Merge #959
959: Generic, executor-agnostic queue implementation r=ivmarkov a=ivmarkov

Hopefully relatively well documented.

Implementation relies on a fixed-size `SortedLinkedList` from `heapless`. (By default, for up to 128 timer schedules, but we can lower this number to - say - 64.)

As discussed earlier, on queue overflow, the `WakerRegistration` approach is utilized, whereas the waker that is ordered first in the queue is awoken to make room for the incoming one (which might be the waker that would be awoken after all!). Wakers are compared with `Waker::will_wake`, so the queue should actually not fill up that easily, if at all.

I've left provisions for the user to manually instantiate the queue using a dedicated macro - `generic_queue!` so that users willing to adjust the queue size, or users (like me) who have to use the queue in a complex "on-top-of-RTOS-but-the-timer-driver-calling-back-from-ISR" scenario can customize the mutex that protects the queue.

The one thing I'm not completely happy with is the need to call `{ embassy_time::queue::initialize() }` early on before any futures using embassy-time are polled, which is currently on the shoulders of the user. I'm open to any ideas where we can get rid of this and do it on the first call to `_embassy_time_schedule_wake`, without introducing very complex combinations of critical sections, atomics and whatnot.




Co-authored-by: ivmarkov <ivan.markov@gmail.com>
Co-authored-by: Dario Nieuwenhuis <dirbaio@dirbaio.net>
2022-10-26 19:14:12 +00:00
f9da6271ce time/generic_queue: use Vec instead of SortedLinkedList 2022-10-26 21:00:50 +02:00
4976cbbe60 time/generic-queue: ensure queue goes in .bss instead of .data 2022-10-26 20:02:58 +02:00
9b86de770b Merge #1031
1031: stm32: Add support for read_until_idle on UART - rebase r=Dirbaio a=Dirbaio

`@guillaume-michel` I rebased #1011 for you and then noticed you don't have the "allowed maintainers to push" option so I had to open a new PR. 

bors r+

Co-authored-by: Guillaume MICHEL <guillaume@squaremind.io>
2022-10-26 17:16:15 +00:00
9cac649fcf stm32: Add support for read_until_idle on UART 2022-10-26 19:06:18 +02:00
ff76fde299 Merge pull request #1030 from embassy-rs/update-nightly
Update Rust nightly.
2022-10-26 18:41:23 +02:00
560eecdb73 Remove the _embassy_time_schedule_wake magic 2022-10-26 18:05:27 +03:00
ac6995f9e6 Fix a bug identified during code review 2022-10-26 17:48:22 +03:00
eeb072d9cb Update Rust nightly. 2022-10-26 16:47:29 +02:00
1669e39565 Buffer data to be written to flash in ram if it does not already reside in ram 2022-10-26 15:02:39 +02:00
80e58426fc Add flash example & flash HIL test 2022-10-26 12:24:04 +02:00
1b249ca72d Merge #987
987: (embassy-stm32): uart flowcontrol r=Dirbaio a=MathiasKoch

Add RTS & CTS flow control to stm32 UARTs

Co-authored-by: Mathias <mk@blackbird.online>
2022-10-26 10:18:44 +00:00
66611a80ca Introduce shared new_inner for uart instantiation 2022-10-26 11:51:37 +02:00
d1eee52625 Merge branch 'master' of https://github.com/embassy-rs/embassy into embassy-stm32/uart-flowcontrol 2022-10-26 11:47:00 +02:00
71cc6833e1 Merge #1029
1029: Update nrf pacs r=Dirbaio a=diondokter



Co-authored-by: Dion Dokter <dion@tweedegolf.com>
2022-10-26 09:35:44 +00:00
0c9ec8dc36 Update usb 2022-10-26 10:39:29 +02:00
ea868920e6 Update nrf pacs 2022-10-26 09:13:26 +02:00
7a6732adcf Improve examples 2022-10-24 15:27:12 -05:00
52c03cf0a4 Add more docs 2022-10-24 22:48:40 +03:00
ac61e0ee9f fmt 2022-10-24 22:39:13 +03:00
33f75419e5 Unify i2cv1 definition with i2cv2 2022-10-24 22:34:10 +03:00
6062978d58 Remove weird async timeouts 2022-10-24 22:22:20 +03:00
ca8afacfd0 Implement TimeoutI2c for i2cv2 2022-10-24 22:11:15 +03:00
9ad7e85288 Merge branch 'i2c_timeout2_v2' into i2c_timeout2 2022-10-24 21:50:29 +03:00
ad0eb3f4bd Implement flash padding to 256 under assumption that all QSPI NOR flashes are MultiwriteNorFlashes 2022-10-24 12:17:22 +02:00
8d809c96ec Merge branch 'master' of https://github.com/embassy-rs/embassy into embassy-rp/flash 2022-10-24 12:14:26 +02:00
9b209ffe1c Add docs 2022-10-24 12:39:47 +03:00
5f02bee388 Gate TimeoutI2c behind i2cv1 2022-10-24 12:34:55 +03:00
1bed02296c i2cv2 timeouts 2022-10-24 12:33:17 +03:00
516f4ce946 Fix embassy-time wasm build and fix a bug in wasm time driver 2022-10-24 12:15:53 +03:00
545cc9326b stm32/adc: Remove voltage and temperature conversions 2022-10-24 04:00:29 -05:00
d99841fea9 Implement time feature 2022-10-24 11:38:15 +03:00
e3cf4255c6 Help compiler with type inference 2022-10-24 11:31:54 +03:00
4ce4131f8b Implement i2cv1 timeout 2022-10-24 11:30:04 +03:00
f78c706b89 Address review feedback 2022-10-24 11:10:59 +03:00
4d5550070f Change time Driver contract to never fire the alarm synchronously 2022-10-24 09:17:43 +03:00
53608a87ac Address feedback after code review 2022-10-24 08:21:35 +03:00
ba6e452cc5 Documentation and initial testing framework
Add mock waker

First simple test

Tests & documentation
2022-10-24 08:21:31 +03:00
c2404ee8ca Initial generic timer queue impl 2022-10-24 08:20:29 +03:00
ce1cba761c Merge #855
855: PDM microphone support for nrf r=Dirbaio a=pbert519

PDM microphones have a long startup phase, therefore the driver samples continuously and only switches the target buffer if the user requests sampling.

Co-authored-by: pbert <pbert@posteo.net>
2022-10-21 21:03:51 +00:00
495ca6108c Merge #1020
1020: Fix mistaken EP_IN_WAKERS r=Dirbaio a=mkj

I'm not totally certain, but this looks like a typo?

Co-authored-by: Matt Johnston <matt@ucc.asn.au>
2022-10-21 20:43:14 +00:00
73d06dd67b Merge #1021
1021: rp usb: wait for accept() completion r=Dirbaio a=mkj

This fixes failures when `defmt` feature isn't enabled (timing related). 

Co-authored-by: Matt Johnston <matt@ucc.asn.au>
2022-10-21 19:48:49 +00:00
866a42f3ae rp usb: wait for accept() completion
This ensures that the current response has finished being sent
before the subsequent set_address() happens. Otherwise connecting
a device is intermittent, can fail depending on timing.
2022-10-21 22:02:13 +08:00
f45d34ce7c Fix mistaken EP_IN_WAKERS 2022-10-20 23:46:02 +08:00
bf0ad38640 Merge #1019
1019: Add missing files and features for basic example r=lulf a=lulf



Co-authored-by: Ulf Lilleengen <ulf.lilleengen@gmail.com>
2022-10-20 14:18:47 +00:00
de103a5f4f Add missing files and features for basic example 2022-10-20 16:12:59 +02:00
8c42b26fc6 Merge #1017
1017: Add missing examples to rust-analyzer linked projects r=lulf a=GrantM11235



Co-authored-by: Grant Miller <GrantM11235@gmail.com>
2022-10-19 10:37:22 +00:00
fa495b8e88 Add missing examples to rust-analyzer linked projects 2022-10-19 03:39:29 -05:00
d9c773f475 Merge #1014
1014: Add memory barriers to H7 flash driver to mitigate PGSERR errors r=lulf a=matoushybl

The stm32h7xx-hal uses only the ordering barrier, while the CubeMX uses the DSB and ISB instructions, to be on the safe side, both are used here.

Without the barrier, the PG bit is not set, when the writes are being done, resulting in an error.

Co-authored-by: Matous Hybl <hyblmatous@gmail.com>
2022-10-19 07:29:12 +00:00
a669f4cfd8 Merge #1015
1015: Enable defmt in embassy-hal-common when defmt is enabled in stm32 HAL r=Dirbaio a=matoushybl



Co-authored-by: Matous Hybl <hyblmatous@gmail.com>
2022-10-18 21:12:05 +00:00
9d2641f2f5 Enable defmt in embassy-hal-common 2022-10-18 22:48:43 +02:00
6c5d81ada5 Add memory barriers to H7 flash driver to mitigate PGSERR errors
The stm32h7xx-hal uses only the ordering barrier, while the CubeMX uses the DSB and ISB instructions, to be on the safe side, both are used here.
2022-10-18 22:42:02 +02:00
18453ee64c Merge #1012
1012: rp i2c: have separate wakers for each i2c unit r=Dirbaio a=jsgf

If they both share one waker, there's the possibility that some wakeups could get lost.

Co-authored-by: Jeremy Fitzhardinge <jeremy@goop.org>
2022-10-18 08:29:43 +00:00
02a3cdb507 Associate state with the instance rather than having a separate array 2022-10-17 21:50:40 -07:00
e4c2b2aa9a rp i2c: have separate wakers for each i2c unit
If they both share one waker, there's the possibility that some wakeups
could get lost.
2022-10-16 18:00:23 -07:00
a4afab4640 add support for pdm microphones in nrf driver 2022-10-13 18:37:53 +02:00
b7d0944265 Merge #984 #1006
984: rp pico async i2c implementation r=Dirbaio a=jsgf

This implements an interrupt-driven async i2c master. It is based on https://github.com/embassy-rs/embassy/pull/914, a bit of https://github.com/embassy-rs/embassy/pull/978 and `@ithinuel's` https://github.com/ithinuel/rp2040-async-i2c.git

This is still work-in-progress, and is currently untested.

1006: Removes some of the code duplication for UarteWithIdle r=Dirbaio a=huntc

This PR removes some of the code duplications for `UarteWithIdle` at the slight expense of requiring a split when using idle processing. As the nRF example illustrates though given the LoC removed, this expense seems worth the benefit in terms of maintenance, and the avoidance of copying over methods. My main motivation for this PR was actually due to the `event_endtx` method not having been copied across to the idle-related code.

Tested the uart_idle example on my nRF52840-dk, and from within my app. Both appear to work fine.

Co-authored-by: Jeremy Fitzhardinge <jeremy@goop.org>
Co-authored-by: huntc <huntchr@gmail.com>
2022-10-12 19:41:52 +00:00
1559374a19 Merge #1010
1010: (embassy-rp): correctly enable RTC_IRQ when scheduling an RTC alarm r=Dirbaio a=MathiasKoch



Co-authored-by: Mathias <mk@blackbird.online>
2022-10-12 19:15:48 +00:00
5846b4ff7d Correctly enable RTC_IRQ when scheduling an RTC alarm 2022-10-12 10:54:47 +02:00
83fcc360fe Merge #985
985: Create Sx126X LORA driver r=lulf a=ceekdee

Implementation features:

- update embassy-lora to support Semtech SX126X chips, specifically the RAK4631 chip (nrf52480 and sx1262).

- support additional SX126X packages by adding a feature (reference feature rak4631) and updating the board specific Rust file.  To enable feature rak4631, settings.json must currently enable "rust-analyzer.linkedProjects" for "examples/nrf/Cargo.toml".

- provide tx/rx examples in examples/nrf to show compatibility with the interface provided by the SX127X LORA implementation.

Only LORA P2P communication has been tested.  Implementation lines marked with ??? indicate areas for further investigation.  Furthermore, I question whether the DIO1 handler is adequate for catching all interrupt sequences.

This implementation is patterned after the C/C++ implementation provided by Semtech, available through the RAK-nRF52-RUI developers platform.

Co-authored-by: ceekdee <taigatensor@gmail.com>
Co-authored-by: Chuck Davis <91165799+ceekdee@users.noreply.github.com>
2022-10-11 08:48:55 +00:00
71a56292d6 Merge #1008
1008: Add note on partition sizes to bootloader docs r=lulf a=lulf

See #1007

Co-authored-by: Ulf Lilleengen <ulf.lilleengen@gmail.com>
2022-10-11 08:27:00 +00:00
4da6320e63 Add note on partition sizes to bootloader docs
See #1007
2022-10-11 10:20:31 +02:00
327d3cf0df Change rak4631 feature to sx126x, removing use in board-specific processing; simplify the P2P examples; correct RSSI computation. 2022-10-10 12:35:42 -05:00
79ba20d315 Merge branch 'embassy-rs:master' into master 2022-10-10 09:17:07 -05:00
9d5b524bb0 Merge #1005
1005: Expose Pin::pin() and Pin::bank() as public r=lulf a=mkj



Co-authored-by: Matt Johnston <matt@ucc.asn.au>
2022-10-10 06:49:34 +00:00
ef533e6df4 Merge #1004
1004: Fix internal channels for adc v2 r=lulf a=chemicstry

Internal channel reading was broken on adc_v2, because `Adc::read()` requires gpio pin trait, which was not implemented by `VrefInt`, `Temperature`, `Vbat`. The required configuration bits `tsvrefe`, `vbate` were not enabled either. This PR makes it a bit closer to how adc_v4 works.

While at it, I also changed adc_v2 to use `RccPeripheral` instead of permanently enabling all ADCs.

Co-authored-by: chemicstry <chemicstry@gmail.com>
2022-10-10 06:28:41 +00:00
e1faf88607 Removes some of the code duplication for UarteWithIdle
This commit removes some of the code duplication for UarteWithIdle at the expense of requiring a split. As the example illustrates though, this expense seems worth the benefit in terms of maintenance, and the avoidance of copying over methods. My main motivation for this commit was actually due to the `event_endtx` method not having been copied across.
2022-10-09 13:07:25 +11:00
3d0ba58b2d Merge branch 'embassy-rs:master' into master 2022-10-08 14:38:41 -05:00
f554962f54 Improve generics and consolidate antenna handling 2022-10-08 14:32:22 -05:00
aa8ba2115c Expose Pin::pin() and Pin::bank() as public 2022-10-08 11:44:06 +08:00
322cfafed3 Fix adc_v4 compilation 2022-10-07 14:53:03 +03:00
df7174ecb0 Fix internal channel reading on adc_v2 2022-10-07 14:31:55 +03:00
f8fd6ab208 Merge #1003
1003: all Cargo.toml: Add license to all crate Cargo.toml files r=lulf a=chrysn

This sets the license to "MIT OR Apache-2.0" in a machine readable form on all crates, as it was already in human readable form in the README.md file, and reaffirmed in #1002.

(The statements on all the individual examples might not be strictly essential for the `cargo deny` use case as they are leaf crates, but other tools might use that information).

Co-authored-by: chrysn <chrysn@fsfe.org>
2022-10-07 11:15:14 +00:00
6718ca3a94 all Cargo.toml: Add license to all crate Cargo.toml files
Closes: https://github.com/embassy-rs/embassy/issues/1002
2022-10-07 12:41:56 +02:00
9dca368c3d Use RccPeripheral for adc_v2 2022-10-07 13:29:56 +03:00
d49d1b6b1c ci/doc: build embassy-time too. 2022-10-05 17:08:02 +02:00
1b9479197d Merge #1000
1000: Forgot to add space function to immediate publisher r=lulf a=diondokter

Title says it all really. This function was added to the normal publisher, so now also to the immediate publisher

Co-authored-by: Dion Dokter <dion@tweedegolf.com>
2022-10-05 14:05:56 +00:00
530182d668 Forgot to add space function to immediate publisher 2022-10-05 15:15:03 +02:00
94606833aa Merge #996
996: Add required info to embassy-sync package r=Dirbaio a=lulf

Updates the README.md based on embassy-futures structure.

Co-authored-by: Ulf Lilleengen <lulf@redhat.com>
2022-10-04 14:40:49 +00:00
59765590e0 Add required info to embassy-sync package
Updates the README.md based on embassy-futures structure.
2022-10-04 16:38:11 +02:00
4fd831e4a8 rp async i2c: raise the tx_empty threshold
Assert "tx_empty" interrupt a little early so there's time to wake up
and start refilling the fifo before it drains. This avoids stalling the
i2c bus if the tx fifo completely drains.
2022-10-03 18:50:03 -07:00
cae8499179 rp i2c: clean up tx_abrt handling
Make sure we always wait for the stop bit if there's a reason to -
either because we sent one, or because there was a hardware tx abort.
2022-10-03 01:18:26 -07:00
f075e62444 Use 1 thread in ci doc building. 2022-10-03 01:59:44 +02:00
e8bb8faa23 rp i2c: allow blocking ops on async contexts 2022-10-02 15:09:14 -07:00
63f5602111 Merge pull request #994 from embassy-rs/ci-docs
Build docs in CI
2022-10-02 23:31:11 +02:00
753781a263 Build docs in CI 2022-10-02 23:30:12 +02:00
73208d5248 Merge #993
993: rp i2c: blocking example r=Dirbaio a=jsgf

i2c example talking to mcp23017 i2c gpio expander.

Co-authored-by: Jeremy Fitzhardinge <jeremy@goop.org>
2022-10-02 18:58:49 +00:00
09afece93d make I2c::write_async take an iterator
There's no other iterator async API right now.
2022-10-01 19:28:27 -07:00
1ee4bb22de embassy-rp i2c: async (non-blocking) example
Simple example exercising an mcp23017 GPIO expander, configured on
RP2040 GPIOs 14+15 (i2c1) with 8 inputs and 8 outputs. Input bit 0
controls whether to display a mcp23017 register dump.
2022-10-01 13:43:37 -07:00
5e2c52ee5b embassy-rp: async i2c implementation
This is an interrupt-driven async i2c master implementation. It makes as
best use of the RP2040's i2c block's fifos as possible to minimize
interrupts.

It implements embedded_hal_async::i2c for easy interop.
WIP async impl
2022-10-01 13:43:37 -07:00
72b645b0c9 rp i2c: make blocking only for Mode=Blocking 2022-10-01 13:26:13 -07:00
8d38eacae4 rp i2c: remove vestiges of DMA 2022-10-01 13:26:13 -07:00
90d392205f embassy-rp: inline I2c::regs
It just returns a literal constant, so there's no reason not to always inline it.
2022-10-01 13:26:13 -07:00
c96581879c update embedded-hal api
Also pin to alpha.9 since its a breaking change
2022-10-01 01:34:45 -07:00
d5abd32da2 rename to i2c_blocking 2022-10-01 01:29:10 -07:00
9f77dbf5ae rp i2c: blocking example
i2c example talking to mcp23017 i2c gpio expander.
2022-10-01 01:29:10 -07:00
aabc02506b Merge #992
992: (embassy-stm32): remove flash lock/unlock public API from stm32 flash r=lulf a=MathiasKoch

Instead, perform the unlocking and locking automatically on erase and write operations.

This makes the `embedded-storage` abstraction actually useable in libraries, while still keeping the flash peripheral locked the majority of the time.

Co-authored-by: Mathias <mk@blackbird.online>
2022-09-30 11:32:27 +00:00
99284b8304 Merge #981
981: (embassy-stm32): add E-H1 uart blocking & nb implementation r=MathiasKoch a=MathiasKoch



Co-authored-by: Mathias <mk@blackbird.online>
2022-09-30 04:14:52 +00:00
a283c47557 Implement embedded-hal-nb for uart 2022-09-30 06:04:19 +02:00
a7fdeac560 Remove flash lock/unlock public API from stm32 flash, and perform the unlocking and locking automatically on erase and write operations 2022-09-30 06:00:46 +02:00
88a3c360e8 Merge branch 'master' of https://github.com/embassy-rs/embassy into embassy-stm32/uart-eh1 2022-09-30 05:23:59 +02:00
1d6f5493e7 Merge #991
991: usb: remove all "Direction as u8" casts. r=Dirbaio a=Dirbaio

Alternative fix for #989 , see comment there for rationale.

bors r+

Co-authored-by: Dario Nieuwenhuis <dirbaio@dirbaio.net>
2022-09-29 13:53:22 +00:00
dab1762709 usb: remove all "Direction as u8" casts. 2022-09-29 15:52:23 +02:00
ebf5a92ab2 Merge #990
990: Small pubsub improvements r=Dirbaio a=diondokter

- Futures in pub & sub are now awaited instead of returned
- Added functions for reading how many messages are available

This helps people get better compiler diagnostics. For example, I forgot to call await on a future and the compiler didn't complain.
This also helps with making some decisions based on the state of the channels.

Co-authored-by: Dion Dokter <dion@tweedegolf.com>
2022-09-29 13:28:56 +00:00
874384826d Went back to named futures but now with must_use 2022-09-29 15:15:10 +02:00
f4ebc36b63 Futures in pub & sub are now awaited instead of returned for better user compiler diagnostics.
Added functions for reading how many messages are available
2022-09-29 14:24:42 +02:00
38faae26e5 Merge branch 'master' of https://github.com/embassy-rs/embassy into embassy-stm32/uart-flowcontrol 2022-09-29 12:58:38 +02:00
a77e2c3512 Merge branch 'master' of https://github.com/embassy-rs/embassy into embassy-stm32/uart-eh1 2022-09-29 12:58:01 +02:00
8b9f4ad259 Merge #988
988: Update embedded-hal versions and explicitly pin r=lulf a=lulf

Pinning to ensure CI don't accidentally break.

Co-authored-by: Ulf Lilleengen <lulf@redhat.com>
2022-09-29 09:29:33 +00:00
72c2e985bb Update embedded-hal versions and explicitly pin 2022-09-29 11:27:46 +02:00
7152031229 Add flash ram helpers 2022-09-29 10:03:49 +02:00
7ee7109508 Rebase on master 2022-09-29 10:00:13 +02:00
f9c62d4f1d Add flowcontrol to UART 2022-09-29 09:12:17 +02:00
dc90006982 Remove code duplication on nb_read 2022-09-29 07:58:11 +02:00
bb84d7a0ae Merge branch 'embassy-rs:master' into master 2022-09-28 14:33:03 -05:00
526e90d3f3 Update some outstanding questions 2022-09-28 14:27:34 -05:00
77ece3f903 Merge #983
983: Remove subghz static lifetime requirement r=lulf a=lulf



Co-authored-by: Ulf Lilleengen <lulf@redhat.com>
2022-09-28 09:33:50 +00:00
d7f7614b22 Remove subghz static lifetime requirement 2022-09-28 11:32:11 +02:00
823bd714fb Add E-H1 uart blocking & nb implementation 2022-09-28 05:19:43 +02:00
a89a0c2f12 Initial add for sx126x 2022-09-27 21:55:41 -05:00
9bb43ffe9a Merge #914
914: (embassy-rp): Add I2C master implementation r=Dirbaio a=MathiasKoch

This PR adds both blocking and DMA based async implementations of I2C master.

Both E-H 0.2 & E-H 1.0 abstractions are implemented as well.

### Questions & concerns:
- Do we need an I2C interrupt handler (for transfer done, abort & error handling?) (async only)
- Do we need to add some automatic attempt at unblocking an I2C bus in case of failures (see ref: 7ebfd553f3/src/i2c_dma.c (L116-L142))
- Should I add `vectored_{read, write}` implementations?

Co-authored-by: Mathias <mk@blackbird.online>
Co-authored-by: Mathias Koch <mk@blackbird.online>
2022-09-27 20:09:53 +00:00
bf1da0497c Allow unused fields temporarily in i2c 2022-09-27 22:08:49 +02:00
44c46e3c93 Move async i2c implementation to new PR, to merge working blocking implementation faster 2022-09-27 22:08:49 +02:00
b0d91e9f31 Apply suggestions from code review
Co-authored-by: Jacob Gonzalez <jacobgonzalez5252@gmail.com>
2022-09-27 22:08:49 +02:00
53c34ccc39 Add async API for I2C 2022-09-27 22:08:49 +02:00
be68d8ebb7 Add further i2c error types 2022-09-27 22:08:49 +02:00
603513e76e Fix blocking I2C 2022-09-27 22:08:49 +02:00
bcd3ab4ba1 Add blocking read & write for I2C 2022-09-27 22:08:49 +02:00
820e6462b6 Add preliminary I2C implementation for RP2040 2022-09-27 22:08:49 +02:00
5c882cf4fa Merge #979
979: usb: make HALs depend only on embassy-usb-driver. r=Dirbaio a=Dirbaio

Follow up to #972 

bors r+

Co-authored-by: Dario Nieuwenhuis <dirbaio@dirbaio.net>
2022-09-27 11:17:14 +00:00
17d8d11f73 usb: make HALs depend only on embassy-usb-driver. 2022-09-27 13:16:49 +02:00
82d4360756 Merge #934
934: (embassy-rp): Add Buffered UART implementation r=MathiasKoch a=MathiasKoch

### Questions & concerns: 
- ~~Would it make sense to add `RxBufferedUart` and `TxBufferedUart`, for cases where you would want to only buffer one way?~~
- ~~Do I need to be monitoring more interrupt flags than `Receive` & `Receive timeout`?~~

This PR adds working `BufferedUart` implementation, along with `RxBufferedUart` and `TxBufferedUart`. The implementation leaves room for improvement with respect to performance, as it still does not utilize DMA nor the internal UART buffers.

Co-authored-by: Mathias <mk@blackbird.online>
Co-authored-by: Dario Nieuwenhuis <dirbaio@dirbaio.net>
2022-09-27 06:00:33 +00:00
e129a97d48 Fix bufferedUart read and write tests 2022-09-27 07:45:10 +02:00
93354b812c Extend buffered-uart test to transmit 32 bytes 2022-09-27 05:54:31 +02:00
65907204d6 Rename from {Rx,Tx}BufferedUart to BufferedUart{Rx,Tx} to be compliant with stm32 and nrf implementations 2022-09-27 05:51:31 +02:00
cd539ba3a0 Rebase 2022-09-27 05:51:14 +02:00
86fd480672 Merge #973
973: Rework STM32 BufferedUart internals so we can split into Rx and Tx like embassy-nrf r=lulf a=guillaume-michel

Context:
On STM32, BufferedUart is not splittable into Rx and Tx part like the non buffered version. On embassy-nrf, a RefCell is used to make BufferedUarte splittable.

Description:
This PR add the possibility to split BufferedUart into Rx and Tx without adding breaking changes.

Hope somebody find it useful

Co-authored-by: Guillaume MICHEL <guillaume@squaremind.io>
2022-09-26 19:30:15 +00:00
de0070948c Merge #976
976: rp: enable time-driver in Cargo.toml instead of ci.sh r=Dirbaio a=Dirbaio

bors r+

Co-authored-by: Dario Nieuwenhuis <dirbaio@dirbaio.net>
2022-09-26 19:05:39 +00:00
7bbb4c22a1 Merge #977
977: Use firmware writer in stm32{f7, h7} example app r=lulf a=lulf

The new FirmwareWriter is useful in particular for these architectures due to the large erase sector size.

Co-authored-by: Ulf Lilleengen <ulf.lilleengen@gmail.com>
2022-09-26 18:47:05 +00:00
5bf6564e95 Use firmware writer in stm32{f7, h7} example app
The new FirmwareWriter is useful in particular for these architectures
due to the large erase sector size.
2022-09-26 20:36:57 +02:00
c863acd24f rp: set correct teleprobe target for rpi-pico tests. 2022-09-26 20:36:06 +02:00
f76444bdc4 Add HIL test for bufferedUart 2022-09-26 20:34:55 +02:00
b3dfd06dd6 Remove code-duplication in async bufferedUart implementations 2022-09-26 20:34:55 +02:00
1db9e464ff Enable embedded-io on nightly 2022-09-26 20:34:55 +02:00
d6af0f6286 Formatting 2022-09-26 20:34:55 +02:00
f2239d34cc Add bufferedUart, including a split version for only Rx or Tx 2022-09-26 20:34:55 +02:00
ee76831f93 Add BufferedUart implementation, and feature-guard time-driver initialization, to free up TIMER peripheral if not used with embassy executor 2022-09-26 20:34:55 +02:00
75e93cc142 rp: enable time-driver in Cargo.toml instead of ci.sh 2022-09-26 20:33:20 +02:00
049c31613b Merge #975
975: rp: Disable intrinsics by default. r=Dirbaio a=Dirbaio

bors r+

Co-authored-by: Dario Nieuwenhuis <dirbaio@dirbaio.net>
2022-09-26 17:57:48 +00:00
1e95c4fcff rp: Disable intrinsics by default. 2022-09-26 19:53:22 +02:00
daf2744716 Rework STM32 BufferedUart internals so we can split into Rx and Tx like embassy-nrf 2022-09-26 15:32:29 +02:00
49070c75b6 Merge #972
972: Restructure USB crates r=Dirbaio a=Dirbaio

- Split driver from `embassy-usb` to a separate crate. This allows making breaking changes to `embassy-usb` without having to bump all the crates with driver impls, such as HALs.
- Merge classes into `embassy-usb`. Now that breaking changes to `embassy-usb` aren't that bad, having everything in a single crate is much easier.

Co-authored-by: Dario Nieuwenhuis <dirbaio@dirbaio.net>
2022-09-26 11:32:41 +00:00
f27a47a37b usb: move classes into the embassy-usb crate. 2022-09-26 13:00:21 +02:00
f4f5824972 usb: do not allow converting Directon to/from u8 2022-09-26 12:35:33 +02:00
7f7c14b7bc usb: split driver trait to separate crate. 2022-09-26 12:29:27 +02:00
dc376a2390 Merge #965
965: (embassy-rp): add RP2040 ROM functions and intrinsics aliases r=Dirbaio a=MathiasKoch

Add RP2040 ROM functions described in section **2.8.3.1. Bootrom Functions** of https://datasheets.raspberrypi.com/rp2040/rp2040-datasheet.pdf

Make all ROM functions (normal and floating point) provide both a direct
call that does the operation and a module with a ptr() function to get
the function pointer.

Add a feature to enable automatic caching of the result of ROM table
function lookups.

Add a check for a V2 bootrom when using floating point functions that
require it.  Panic when it's not present.

Add a standardized macro for intrinsics export and connect the simple
ROM functions to intrinsics.

Direct copy from `rp-hal`! Full credit to those guys for all the heavy lifting.

Co-authored-by: Mathias <mk@blackbird.online>
Co-authored-by: Dario Nieuwenhuis <dirbaio@dirbaio.net>
2022-09-26 09:58:06 +00:00
fa7781c48d Add credits to rp-hal 2022-09-26 11:57:26 +02:00
a9efbf18c6 Merge #960
960: Add non blocking Bxcan constructor r=Dirbaio a=andyblarblar

This PR adds a non-blocking constructor to the Bxcan Can wrapper struct. This allows for the creation of the Can periferal without blocking for a sync with the Can bus.

Co-authored-by: Andrew Ealovega <Andrew@Ealovega.dev>
2022-09-26 09:39:55 +00:00
3c06a18b94 Merge #971
971: (embassy-boot): add blocking API to FirmwareUpdater r=lulf a=MathiasKoch

Also add a split `prepare_update` + `write_firmware` API, to allow for an optimized update API at the exchange of added complexity. 
The old API is left in place to allow users to choose the complexity level that fits their needs. 

Co-authored-by: Mathias <mk@blackbird.online>
2022-09-26 08:53:25 +00:00
6fa74b0c02 make prepare_update async 2022-09-26 10:36:21 +02:00
b2a327a858 Add get_state helpers to allow self-testing before calling mark_booted 2022-09-26 06:53:40 +02:00
7f16b1cd23 Add blocking API to FirmwareUpdater, and allow for a split prepare/write api 2022-09-26 06:01:18 +02:00
b743d9f48c Add HIL test for bufferedUart 2022-09-26 05:32:45 +02:00
3c24ad2db6 Merge #969
969: usb: fix compile errors with the log feature r=Dirbaio a=newAM



Co-authored-by: Alex Martens <alex@thinglab.org>
2022-09-25 21:59:52 +00:00
a226e86503 Merge #961
961: Parameterize Signal with RawMutex r=ivmarkov a=ivmarkov

The `RawMutex` parameter is deliberately chosen to be the second one, so as it can take as a default `CriticalSectionRawMutex`. This way backwards compatibility is preserved, and users utilizing the `critical-section` crate everywhere can just continue to use the more ergonomic single-generic-parameter version of Signal.

I'm thinking we should probably do the same for `Channel`, and move the `RawMutex` parameter as the last one in the list, with a `CriticalSectionRawMutex` being its default. But that's a backwards-incompatible change of course.

Co-authored-by: ivmarkov <ivan.markov@gmail.com>
2022-09-25 07:46:43 +00:00
c5ce02b30e Remove default, reorder generic params 2022-09-25 09:40:36 +03:00
8536666148 Remove default, reorder generic params 2022-09-24 20:27:27 +03:00
ca92302d03 Parameterize Signal with RawMutex 2022-09-24 20:26:51 +03:00
a45fb2d718 usb: fix compile errors with the log feature 2022-09-24 09:42:06 -07:00
eeb1515e9f Merge #958
958: Implement proper `Drop` for `BufferedUarte` r=lulf a=ZoeyR

The drop method in `BufferedUarte` was prone to hanging indefinitely and also didn't actually disable the peripheral. I mostly copied over the drop method from `Uarte` with some modifications since `BufferedUarte` could have a transmit lasting indefinitely.

Co-authored-by: Zoey Riordan <zoey@dos.cafe>
2022-09-23 11:58:43 +00:00
b4f2c2a05e Re-add timer.stop() 2022-09-23 12:34:02 +02:00
18dc0dea63 Drop rp2040-flash as dependency, as they pull in rp2040-hal for rom-data functions, which are now part of this HAL as well 2022-09-23 08:12:32 +02:00
9d674f0212 First iteration attempt on implementing generic flash mutation access for RP2040 2022-09-23 07:59:10 +02:00
816778e3fa Add RP2040 ROM functions and intrinsics aliases 2022-09-23 07:58:48 +02:00
4f33cc5d1a Replace futures::future::join -> embassy_futures::join::join. 2022-09-23 07:58:48 +02:00
2fed9f949a Replace futures::future::poll_fn -> core::future::poll_fn. 2022-09-23 07:58:48 +02:00
7412a859fd Update Rust nightly.
Removes feature(generic_associated_types)
2022-09-23 07:58:48 +02:00
0db1332da8 Implement RealTimeClock for embassy-rp 2022-09-23 07:58:48 +02:00
334dfcdb65 Take into account size of revert index
Fixes a bug in the partition assertions that ensures that the state
page(s) have enough space for 2x active partition range.

Add unit test to verify that panic is observed.
2022-09-23 07:58:48 +02:00
54ba472540 Remove BootFlash borrow
Compiler will infer a different lifetime for BootFlash than for the
borrowed flash, which makes it require more type annotations than if it
was just owning the type. Since it doesn't really matter if it owns or
borrows in practical use, change it to own so that it simplifies usage.
2022-09-23 07:58:48 +02:00
4322293f63 rp: let SPI RX overflow during async write 2022-09-23 07:58:48 +02:00
c14527486d rp: fix async SPI read and write 2022-09-23 07:58:48 +02:00
81298394b5 rp: remove extraneous newlines in logs 2022-09-23 07:58:48 +02:00
5d1576ea73 Add time-driver feature to docs 2022-09-23 07:58:48 +02:00
f46b838746 Feature-gate time-driver in embassy-rp 2022-09-23 07:58:48 +02:00
3f672c8a93 Make rustfmt happy 2022-09-23 07:58:48 +02:00
e5af4c4bce Add .into_inner() and .get_mut() to Mutex 2022-09-23 07:58:48 +02:00
7bb9620b1a make State::new() const, consistent with others 2022-09-23 07:58:48 +02:00
2e7916c5fe Add RP2040 ROM functions and intrinsics aliases 2022-09-23 06:45:44 +02:00
dacbc9acd5 Merge #963
963: Remove some uses of the `futures` crate r=Dirbaio a=Dirbaio

- use `Future` from `core`
- `poll_fn` is now stable in `core` since Rust 1.64 (out today)
- Use `join` from `embassy-futures`.


Co-authored-by: Dario Nieuwenhuis <dirbaio@dirbaio.net>
2022-09-22 15:46:07 +00:00
10d1ad2343 Replace futures::future::join -> embassy_futures::join::join. 2022-09-22 16:48:35 +02:00
a0487380da Replace futures::future::poll_fn -> core::future::poll_fn. 2022-09-22 16:42:49 +02:00
c1e25067da Merge #962
962: Update Rust nightly. r=Dirbaio a=Dirbaio



Co-authored-by: Dario Nieuwenhuis <dirbaio@dirbaio.net>
2022-09-22 14:39:02 +00:00
897b72c872 Update Rust nightly.
Removes feature(generic_associated_types)
2022-09-22 16:38:14 +02:00
5914d80968 Add non blocking Bxcan constructor.
Signed-off-by: Andrew Ealovega <Andrew@Ealovega.dev>
2022-09-21 22:29:57 -04:00
5f7e0eb2ae Fix builds on other nrf pacs 2022-09-21 14:06:56 +02:00
15b4f9db90 Remove unused function 2022-09-21 11:19:47 +02:00
0f55f5a73d Remove left-in comments and logs 2022-09-21 11:06:06 +02:00
3d708a459c Implement proper Drop for BufferedUarte 2022-09-21 10:48:02 +02:00
3b58ac1bf8 Merge #949
949: (embassy-rp): Implement RealTimeClock r=lulf a=MathiasKoch

Basically a 1:1 port of the great implementation effort made by `rp-hal`

Co-authored-by: Mathias <mk@blackbird.online>
2022-09-21 07:19:23 +00:00
1d3e41f970 Remove code-duplication in async bufferedUart implementations 2022-09-21 06:00:35 +02:00
44d7a84e47 Merge #957
957: Take into account size of revert index r=lulf a=lulf

Fixes a bug in the partition assertions that ensures that the state page(s) have enough space for 2x active partition range.

Add unit test to verify that panic is observed.

Co-authored-by: Ulf Lilleengen <ulf.lilleengen@gmail.com>
2022-09-20 12:56:46 +00:00
b418c0e4d6 Take into account size of revert index
Fixes a bug in the partition assertions that ensures that the state
page(s) have enough space for 2x active partition range.

Add unit test to verify that panic is observed.
2022-09-20 14:04:57 +02:00
11da25800b Merge #956
956: Remove BootFlash borrow r=lulf a=lulf

Compiler will infer a different lifetime for BootFlash than for the borrowed flash, which makes it require more type annotations than if it was just owning the type. Since it doesn't really matter if it owns or borrows in practical use, change it to own so that it simplifies usage.

Co-authored-by: Ulf Lilleengen <ulf.lilleengen@gmail.com>
2022-09-20 07:55:20 +00:00
d0fe654c82 Remove BootFlash borrow
Compiler will infer a different lifetime for BootFlash than for the
borrowed flash, which makes it require more type annotations than if it
was just owning the type. Since it doesn't really matter if it owns or
borrows in practical use, change it to own so that it simplifies usage.
2022-09-20 09:54:37 +02:00
6663390224 Merge #954
954: rp: fix async SPI read and write r=lulf a=newAM

Closes #953 

Co-authored-by: Alex Martens <alex@thinglab.org>
2022-09-19 07:26:10 +00:00
ac13675f3a Merge #955
955: rp: remove extraneous newlines in logs r=Dirbaio a=newAM



Co-authored-by: Alex Martens <alex@thinglab.org>
2022-09-18 23:22:14 +00:00
0c6933fefb rp: remove extraneous newlines in logs 2022-09-18 14:54:24 -07:00
295cc997ae rp: let SPI RX overflow during async write 2022-09-18 12:23:17 -07:00
ab1a6889a6 rp: fix async SPI read and write 2022-09-18 12:02:05 -07:00
336ebe54c0 Merge #952
952: (embassy-rp): Feature-gate time-driver r=MathiasKoch a=MathiasKoch



Co-authored-by: Mathias <mk@blackbird.online>
2022-09-16 14:46:35 +00:00
1c657d2d55 Add time-driver feature to docs 2022-09-16 16:45:59 +02:00
c495c765df Enable embedded-io on nightly 2022-09-16 13:23:52 +02:00
feb840c503 First iteration attempt on implementing generic flash mutation access for RP2040 2022-09-16 13:20:22 +02:00
feead3ae89 Implement RealTimeClock for embassy-rp 2022-09-16 13:18:23 +02:00
f7267d493f Feature-gate time-driver in embassy-rp 2022-09-16 12:54:26 +02:00
ec10460547 Merge #950
950: Add .into_inner() and .get_mut() to Mutex r=Dirbaio a=hulthe

Similar to the methods on std Mutex, these methods allow accessing the underlying data without locking the mutex when you have exclusive access to it.

Co-authored-by: Joakim Hulthe <joakim@hulthe.net>
2022-09-16 10:32:12 +00:00
79654510b7 Make rustfmt happy 2022-09-16 10:45:01 +02:00
70a3b85acc Add .into_inner() and .get_mut() to Mutex 2022-09-16 10:32:43 +02:00
9794bc59cc Merge #948
948: (embassy-stm32): make `State::new()` const, consistent with others r=Dirbaio a=FrozenDroid



Co-authored-by: Vincent Stakenburg <v.stakenburg@sinewave.nl>
2022-09-15 11:14:24 +00:00
c4d5c047d7 make State::new() const, consistent with others 2022-09-15 12:34:17 +02:00
809a4a127b Merge #946
946: sync/signal: wake old waker on overflow instead of panicking. r=Dirbaio a=Dirbaio

This makes behavior consistent with `WakerRegistration`. It allows canceling `wait` in one task and then calling `wait` in another. If two tasks are `wait`ing concurrently the signal will be received by only one of them, randomly.

Co-authored-by: Dario Nieuwenhuis <dirbaio@dirbaio.net>
2022-09-12 10:30:01 +00:00
ea5f2c71e0 sync/signal: wake old waker on overflow instead of panicking.
This makes behavior consistent with `WakerRegistration`. It allows canceling `wait`
in one task and then calling `wait` in another. If two tasks are `wait`ing
concurrently the signal will be received by only one of them, randomly.
2022-09-12 12:05:58 +02:00
b2d0f8d590 Formatting 2022-09-09 10:49:47 +02:00
31d85da78a Add bufferedUart, including a split version for only Rx or Tx 2022-09-09 10:48:52 +02:00
9611e7c9f2 Add BufferedUart implementation, and feature-guard time-driver initialization, to free up TIMER peripheral if not used with embassy executor 2022-09-09 10:48:30 +02:00
573c433f64 Merge pull request #945 from danbev/embassy-hal-common-typo
Fix typo in peripheral.rs
2022-09-08 19:09:32 +02:00
34ed3441ce Fix typo in peripheral.rs 2022-09-08 17:01:45 +02:00
5886679006 Merge #941
941: Add critical-section/std to std feature r=lulf a=danbev

This commit suggests adding `critical-section/std` to the std feature as
without this a link time error is generated:
```console
 cargo r --bin main
    Updating git repository `https://github.com/embassy-rs/embassy`
    Updating crates.io index
   Compiling embassy-executor v0.1.0 (https://github.com/embassy-rs/embassy?branch=master#50af13d4)
   Compiling embassy-macros v0.1.0 (https://github.com/embassy-rs/embassy?branch=master#50af13d4)
   Compiling embassy-time v0.1.0 (https://github.com/embassy-rs/embassy?branch=master#50af13d4)
   Compiling embassy-exploration v0.1.0 (/home/danielbevenius/work/rust/learning-rust/embassy)
error: linking with `cc` failed: exit status: 1
  |
  = note: "cc" "-m64" "/tmp/rustcSRodQD/symbols.o" "/home/danielbevenius/work/rust/learning-rust/embassy/target/debug/deps/main-1832a615156f75e5.14ikas4ibqicm80d.rcgu.o" "/home/danielbevenius/work/rust/learning-rust/embassy/target/debug/deps/main-1832a615156f75e5.18i7scdiuioyv581.rcgu.o" "/home/danielbevenius/work/rust/learning-rust/embassy/target/debug/deps/main-1832a615156f75e5.1ok8uqc3ldhimh25.rcgu.o" "/home/danielbevenius/work/rust/learning-rust/embassy/target/debug/deps/main-1832a615156f75e5.1xqethf2arni74gv.rcgu.o" "/home/danielbevenius/work/rust/learning-rust/embassy/target/debug/deps/main-1832a615156f75e5.21vi9l5ujpxyqdul.rcgu.o" "/home/danielbevenius/work/rust/learning-rust/embassy/target/debug/deps/main-1832a615156f75e5.2ddnd7hcxhe0zt3p.rcgu.o" "/home/danielbevenius/work/rust/learning-rust/embassy/target/debug/deps/main-1832a615156f75e5.2sfet6lnlsxolfnj.rcgu.o" "/home/danielbevenius/work/rust/learning-rust/embassy/target/debug/deps/main-1832a615156f75e5.2woh8eris2teqn61.rcgu.o" "/home/danielbevenius/work/rust/learning-rust/embassy/target/debug/deps/main-1832a615156f75e5.3ed4nji3ub904efe.rcgu.o" "/home/danielbevenius/work/rust/learning-rust/embassy/target/debug/deps/main-1832a615156f75e5.3j1qr10cbnc5hdky.rcgu.o" "/home/danielbevenius/work/rust/learning-rust/embassy/target/debug/deps/main-1832a615156f75e5.3kcxk9i0yzeygir7.rcgu.o" "/home/danielbevenius/work/rust/learning-rust/embassy/target/debug/deps/main-1832a615156f75e5.3ztlzj8m8r2zg8uw.rcgu.o" "/home/danielbevenius/work/rust/learning-rust/embassy/target/debug/deps/main-1832a615156f75e5.4121l5kfb4jvrdx7.rcgu.o" "/home/danielbevenius/work/rust/learning-rust/embassy/target/debug/deps/main-1832a615156f75e5.4edu5yotynsqm8za.rcgu.o" "/home/danielbevenius/work/rust/learning-rust/embassy/target/debug/deps/main-1832a615156f75e5.4v8bu7v01yjurri4.rcgu.o" "/home/danielbevenius/work/rust/learning-rust/embassy/target/debug/deps/main-1832a615156f75e5.4vcoqtkqdc34e9mz.rcgu.o" "/home/danielbevenius/work/rust/learning-rust/embassy/target/debug/deps/main-1832a615156f75e5.56mjzsmaddhhm01o.rcgu.o" "/home/danielbevenius/work/rust/learning-rust/embassy/target/debug/deps/main-1832a615156f75e5.5a0f5kqacoiz75iw.rcgu.o" "/home/danielbevenius/work/rust/learning-rust/embassy/target/debug/deps/main-1832a615156f75e5.5cdnitb2zabtf8r6.rcgu.o" "/home/danielbevenius/work/rust/learning-rust/embassy/target/debug/deps/main-1832a615156f75e5.857b2iuurxhsdws.rcgu.o" "/home/danielbevenius/work/rust/learning-rust/embassy/target/debug/deps/main-1832a615156f75e5.vdaib0obomov020.rcgu.o" "/home/danielbevenius/work/rust/learning-rust/embassy/target/debug/deps/main-1832a615156f75e5.wuy7mahwirenj6w.rcgu.o" "/home/danielbevenius/work/rust/learning-rust/embassy/target/debug/deps/main-1832a615156f75e5.2vd161v9s1cmfe2q.rcgu.o" "-Wl,--as-needed" "-L" "/home/danielbevenius/work/rust/learning-rust/embassy/target/debug/deps" "-L" "/home/danielbevenius/work/rust/learning-rust/embassy/target/debug/build/defmt-585554c438e0da4a/out" "-L" "/home/danielbevenius/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib" "-Wl,-Bstatic" "/home/danielbevenius/work/rust/learning-rust/embassy/target/debug/deps/libenv_logger-13edb7439f725ac4.rlib" "/home/danielbevenius/work/rust/learning-rust/embassy/target/debug/deps/libatty-12c8601fe43875b5.rlib" "/home/danielbevenius/work/rust/learning-rust/embassy/target/debug/deps/liblibc-9edce8612524dfa4.rlib" "/home/danielbevenius/work/rust/learning-rust/embassy/target/debug/deps/libtermcolor-6a61078d9610ba74.rlib" "/home/danielbevenius/work/rust/learning-rust/embassy/target/debug/deps/libhumantime-69fede984985242f.rlib" "/home/danielbevenius/work/rust/learning-rust/embassy/target/debug/deps/libregex-626fa72ec79b7aa5.rlib" "/home/danielbevenius/work/rust/learning-rust/embassy/target/debug/deps/libaho_corasick-056f10d3df8fc98c.rlib" "/home/danielbevenius/work/rust/learning-rust/embassy/target/debug/deps/libmemchr-e6bbee201eb478c9.rlib" "/home/danielbevenius/work/rust/learning-rust/embassy/target/debug/deps/libregex_syntax-5e1cc215759dcea0.rlib" "/home/danielbevenius/work/rust/learning-rust/embassy/target/debug/deps/liblog-41607b4997ff699e.rlib" "/home/danielbevenius/work/rust/learning-rust/embassy/target/debug/deps/libembassy_executor-3e221766d4969e83.rlib" "/home/danielbevenius/work/rust/learning-rust/embassy/target/debug/deps/libcfg_if-90e3626e3c41eec3.rlib" "/home/danielbevenius/work/rust/learning-rust/embassy/target/debug/deps/libstatic_cell-999da4360e12ca6a.rlib" "/home/danielbevenius/work/rust/learning-rust/embassy/target/debug/deps/libembassy_time-b2d6b6cd79ad554e.rlib" "/home/danielbevenius/work/rust/learning-rust/embassy/target/debug/deps/libdefmt-dd2e870ef9533c1d.rlib" "/home/danielbevenius/work/rust/learning-rust/embassy/target/debug/deps/libbitflags-05dfd5b5d1225bed.rlib" "/home/danielbevenius/work/rust/learning-rust/embassy/target/debug/deps/libfutures_util-5618995ea68d96a0.rlib" "/home/danielbevenius/work/rust/learning-rust/embassy/target/debug/deps/libpin_project_lite-d4223cfaa04037d7.rlib" "/home/danielbevenius/work/rust/learning-rust/embassy/target/debug/deps/libfutures_task-a4bc7de7fd258585.rlib" "/home/danielbevenius/work/rust/learning-rust/embassy/target/debug/deps/libpin_utils-b7ff504f33cec58a.rlib" "/home/danielbevenius/work/rust/learning-rust/embassy/target/debug/deps/libfutures_core-27e4b4b8992e0256.rlib" "/home/danielbevenius/work/rust/learning-rust/embassy/target/debug/deps/libembedded_hal-5a6482b51eb4cf4d.rlib" "/home/danielbevenius/work/rust/learning-rust/embassy/target/debug/deps/libvoid-7b6fd157ed8721ab.rlib" "/home/danielbevenius/work/rust/learning-rust/embassy/target/debug/deps/libnb-36022c86298e770b.rlib" "/home/danielbevenius/work/rust/learning-rust/embassy/target/debug/deps/libnb-80571a5b05fafda2.rlib" "/home/danielbevenius/work/rust/learning-rust/embassy/target/debug/deps/libcritical_section-b3e8f14a54b3edad.rlib" "/home/danielbevenius/work/rust/learning-rust/embassy/target/debug/deps/libatomic_polyfill-51ffda39a8157221.rlib" "-Wl,--start-group" "/home/danielbevenius/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libstd-4393e7d07259b8a4.rlib" "/home/danielbevenius/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libpanic_unwind-e13cbb326bcd01a4.rlib" "/home/danielbevenius/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libobject-13ac6af5403a52c8.rlib" "/home/danielbevenius/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libmemchr-a7b8febdd2acb289.rlib" "/home/danielbevenius/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libaddr2line-343513f0726f71ed.rlib" "/home/danielbevenius/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libgimli-8f833d900bfb98aa.rlib" "/home/danielbevenius/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/librustc_demangle-e97a7960ca6216c8.rlib" "/home/danielbevenius/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libstd_detect-683fb35093a61fcc.rlib" "/home/danielbevenius/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libhashbrown-f943c2d34bd4b56d.rlib" "/home/danielbevenius/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libminiz_oxide-72ce2aaa649404e0.rlib" "/home/danielbevenius/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libadler-ac5d08ad5339e92e.rlib" "/home/danielbevenius/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/librustc_std_workspace_alloc-dacfda262d5656fb.rlib" "/home/danielbevenius/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libunwind-e2056a834ba0712c.rlib" "/home/danielbevenius/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libcfg_if-a60649c148c6e2db.rlib" "/home/danielbevenius/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/liblibc-3e961d059b9bcde7.rlib" "/home/danielbevenius/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/liballoc-20f26f875d0170e2.rlib" "/home/danielbevenius/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/librustc_std_workspace_core-522518611024dce5.rlib" "/home/danielbevenius/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libcore-05898138a596088a.rlib" "-Wl,--end-group" "/home/danielbevenius/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libcompiler_builtins-5b83a1df856cf582.rlib" "-Wl,-Bdynamic" "-lc" "-lm" "-lrt" "-lpthread" "-lgcc_s" "-lutil" "-lrt" "-lpthread" "-lm" "-ldl" "-lc" "-Wl,--eh-frame-hdr" "-Wl,-znoexecstack" "-L" "/home/danielbevenius/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib" "-o" "/home/danielbevenius/work/rust/learning-rust/embassy/target/debug/deps/main-1832a615156f75e5" "-Wl,--gc-sections" "-pie" "-Wl,-zrelro,-znow" "-nodefaultlibs"
  = note: /usr/bin/ld: /home/danielbevenius/work/rust/learning-rust/embassy/target/debug/deps/libembassy_executor-3e221766d4969e83.rlib(embassy_executor-3e221766d4969e83.embassy_executor.0b7ca5ea-cgu.11.rcgu.o): in function `critical_section::acquire':
          /home/danielbevenius/.cargo/registry/src/github.com-1ecc6299db9ec823/critical-section-1.1.0/src/lib.rs:180: undefined reference to `_critical_section_1_0_acquire'
          /usr/bin/ld: /home/danielbevenius/work/rust/learning-rust/embassy/target/debug/deps/libembassy_executor-3e221766d4969e83.rlib(embassy_executor-3e221766d4969e83.embassy_executor.0b7ca5ea-cgu.11.rcgu.o): in function `critical_section::release':
          /home/danielbevenius/.cargo/registry/src/github.com-1ecc6299db9ec823/critical-section-1.1.0/src/lib.rs:197: undefined reference to `_critical_section_1_0_release'
          /usr/bin/ld: /home/danielbevenius/work/rust/learning-rust/embassy/target/debug/deps/libembassy_executor-3e221766d4969e83.rlib(embassy_executor-3e221766d4969e83.embassy_executor.0b7ca5ea-cgu.11.rcgu.o): in function `critical_section::acquire':
          /home/danielbevenius/.cargo/registry/src/github.com-1ecc6299db9ec823/critical-section-1.1.0/src/lib.rs:180: undefined reference to `_critical_section_1_0_acquire'
          /usr/bin/ld: /home/danielbevenius/work/rust/learning-rust/embassy/target/debug/deps/libembassy_executor-3e221766d4969e83.rlib(embassy_executor-3e221766d4969e83.embassy_executor.0b7ca5ea-cgu.11.rcgu.o): in function `critical_section::release':
          /home/danielbevenius/.cargo/registry/src/github.com-1ecc6299db9ec823/critical-section-1.1.0/src/lib.rs:197: undefined reference to `_critical_section_1_0_release'
          collect2: error: ld returned 1 exit status
          
  = help: some `extern` functions couldn't be found; some native libraries may need to be installed or have their path specified
  = note: use the `-l` flag to specify native libraries to link
  = note: use the `cargo:rustc-link-lib` directive to specify the native libraries to link with Cargo (see https://doc.rust-lang.org/cargo/reference/build-scripts.html#cargorustc-link-libkindname)

error: could not compile `embassy-exploration` due to previous error

```
I ran into this when updating an [example project](https://github.com/danbev/learning-rust/tree/master/embassy) I have to use `embassy-executor`. 

----
### Reproduce
Create a new project:
```console
$ cargo new --bin embassy-standalone
$ cd embassy-standalone
```
Update `Cargo.toml`:
```toml
[package]                                                                       
name = "embassy-standalone"                                                        
version = "0.1.0"                                                                  
edition = "2021"                                                                
                                                                                
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
                                                                                
[dependencies]                                                                  
embassy-executor = { version = "0.1.0", features = ["std", "integrated-timers", "nightly"] }
embassy-time = { version = "0.1.0", features = ["defmt", "defmt-timestamp-uptime", "std", "nightly"] }
log = "0.4.14"                                                                  
env_logger = "0.9.0"                                                            
#critical-section = { version = "1.1", features = [ "std" ] }                    
                                                                                
[patch.crates-io]                                                               
embassy-executor = { git = "https://github.com/embassy-rs/embassy", branch = "master" }
embassy-time = { git = "https://github.com/embassy-rs/embassy", branch = "master" }
```
Notice the commented out `critical-section` dependency which will produces the link error. 

Update `src/main.rs` with the code from [embassy.dev/dev/examples.html](https://embassy.dev/dev/examples.html):
```rust
#![feature(type_alias_impl_trait)]

use embassy_executor::Spawner;
use embassy_time::{Duration, Timer};
use log::*;

#[embassy_executor::task]
async fn run() {
    loop {
        info!("tick");
        Timer::after(Duration::from_secs(1)).await;
    }
}

#[embassy_executor::main]
async fn main(spawner: Spawner) {
    env_logger::builder()
        .filter_level(log::LevelFilter::Debug)
        .format_timestamp_nanos()
        .init();

    spawner.spawn(run()).unwrap();
}
```
Running the example:
```console
$ cargo r
```

Co-authored-by: Daniel Bevenius <daniel.bevenius@gmail.com>
2022-09-08 07:10:01 +00:00
7004b095c3 Add critical-section/std to std feature
This commit suggests adding critical-section/std to the std feature as
without this a link time error is generated.
2022-09-08 06:03:43 +02:00
22c32b5d5c Merge #939
939: time: add more tick rates, use 1mhz as default. r=Dirbaio a=Dirbaio



Co-authored-by: Dario Nieuwenhuis <dirbaio@dirbaio.net>
2022-09-04 22:13:34 +00:00
107bb0946a Merge #944
944: usbd-hid: 0.5.2 -> 0.6.0 r=newAM a=newAM

Changes: ccfda68832..2f1c984f2c

Co-authored-by: Alex Martens <alex@thinglab.org>
2022-09-04 21:39:09 +00:00
f66f20b1ce usbd-hid: 0.5.2 -> 0.6.0 2022-09-04 10:00:02 -07:00
6264fe39a5 Merge #839
839: Misc LoRaWAN improvements r=lulf a=timokroeger

Trying too get `embassy-lora` running on a [LoRa-E5 Dev Board](https://wiki.seeedstudio.com/LoRa_E5_Dev_Board/).
I can see the join message arriving in the The Things Network console but the device does not receive the accept message yet.
Opening this PR anyway because I think there are some nice things to decouple the lora crate from the nucleo board.

`@lulf` Could you test if this PR breaks your LoRa setup? Marking as draft for the time being.

Co-authored-by: Timo Kröger <timokroeger93@gmail.com>
Co-authored-by: Ulf Lilleengen <lulf@redhat.com>
2022-09-04 07:17:23 +00:00
7d5c1fcebf Merge #942
942: Update documentation link r=lulf a=danbev

This commit updates the documentation link which currently results in a
`404 Not Found`.

Co-authored-by: Daniel Bevenius <daniel.bevenius@gmail.com>
2022-09-04 06:07:29 +00:00
6cdff72d6d run cargo fmt 2022-09-03 20:36:18 +02:00
96eb669b34 Merge #943
943: Use embassy_executor::main in runtime.adoc r=lulf a=danbev

This commit replaces `embassy::main` with `embassy_executor::main` in the
runtime documentation page.

Refs: https://embassy.dev/dev/runtime.html

Co-authored-by: Daniel Bevenius <daniel.bevenius@gmail.com>
2022-09-03 18:06:03 +00:00
506e5a4493 Use embassy_executor::main in runtime.adoc
This commit replaces embassy::main with embassy_executor::main in the
runtime documentation page.

Refs: https://embassy.dev/dev/runtime.html
2022-09-03 15:09:36 +02:00
d6e8a11ea7 Update documentation link
This commit updates the documentation link which currently results in a
404 Not Found.
2022-09-03 14:50:37 +02:00
50af13d470 Merge #940
940: Fix a few clippy warnings r=lulf a=lulf



Co-authored-by: Ulf Lilleengen <lulf@redhat.com>
2022-09-02 06:43:22 +00:00
3aa0c13ba5 Fix a few clippy warnings 2022-09-02 08:42:42 +02:00
8b464d2668 Merge #935
935: Remove generic const expressions from embassy-boot r=lulf a=lulf

* Remove the need for generic const expressions and use buffers provided in the flash config.
* Extend embedded-storage traits to simplify generics.
* Document all public APIs
* Add toplevel README
* Expose AlignedBuffer type for convenience.
* Update examples

Co-authored-by: Ulf Lilleengen <lulf@redhat.com>
2022-09-02 06:26:08 +00:00
3ca7314476 Remove generic const expressions from embassy-boot
* Remove the need for generic const expressions and use buffers provided in the flash config.
* Extend embedded-storage traits to simplify generics.
* Document all public APIs
* Add toplevel README
* Expose AlignedBuffer type for convenience.
* Update examples
2022-09-02 08:25:36 +02:00
5327b9c289 time: add more tick rates, use 1mhz as default. 2022-09-02 00:59:34 +02:00
835b69456d Merge #933
933: embassy-rp: Add async SPI r=Dirbaio a=kalkyl



Co-authored-by: Henrik Alsér <henrik.alser@me.com>
Co-authored-by: Henrik Alsér <henrik@mindbite.se>
2022-09-01 13:52:47 +00:00
efe456ab14 Fix dma 2022-09-01 15:50:08 +02:00
9ff5c50774 Cleanup examples 2022-09-01 15:12:44 +02:00
71c130488b Reorder args 2022-09-01 15:12:44 +02:00
e2181cb439 rustfmt 2022-09-01 15:12:44 +02:00
b934f3f12e Remove cfg_if 2022-09-01 15:12:44 +02:00
3fce6ec649 Rearrange new:s 2022-09-01 15:12:44 +02:00
27905f1be1 Change DMA write/read to use raw pointers 2022-09-01 15:12:44 +02:00
7954cbc4e7 Add HIL tests 2022-09-01 15:12:44 +02:00
99dd2a9386 Reorder args 2022-09-01 15:12:44 +02:00
6d347af9fa transfer_in_place 2022-09-01 15:12:43 +02:00
c8ecc55710 Fix example 2022-09-01 15:12:43 +02:00
44150c4830 impl embedded-hal-async 2022-09-01 15:12:43 +02:00
07c64d902e example 2022-09-01 15:12:43 +02:00
e7d4bf258a dma 2022-09-01 15:12:43 +02:00
838f3065ea Merge #936
936: Add split() method to BufferedUarte in embassy-nrf r=ZoeyR a=ZoeyR

I haven't completed testing this yet. I'm creating this PR early so that I can get corrected if I went way off course.

This PR adds a `split()` method to `BufferedUarte` as discussed on matrix.

Co-authored-by: Zoey Riordan <zoey@dos.cafe>
2022-08-31 10:20:40 +00:00
1fb6bfbec9 Merge #938
938: Do not use cfg_if for embedded-hal-async feature gates. r=Dirbaio a=Dirbaio

Old code used `cfg_if!` because rustc still parses code inside disabled cfg's, and Rust stable at that time couldn't parse the new GAT where-clause location. This is not the case anymore.


bors r+

Co-authored-by: Dario Nieuwenhuis <dirbaio@dirbaio.net>
2022-08-31 01:11:49 +00:00
8ba421f324 Do not use cfg_if for embedded-hal-async feature gates.
Old code used `cfg_if!` because rustc still parses code inside disabled cfg's, and Rust stable at that time couldn't parse the new GAT where-clause location. This is not the case anymore.
2022-08-31 03:11:21 +02:00
38900a7fb0 Merge #928
928: Ensure that the sampling is stopped r=Dirbaio a=huntc

Ensures that nRF saadc sampling is stopped and is awaited prior to exiting the two sampling methods. Not doing so causes a potential power drain and the potential for dropped buffer writes when having finished continuous sampling.

Co-authored-by: huntc <huntchr@gmail.com>
2022-08-31 00:31:38 +00:00
fe08bdf0d8 Merge pull request #937 from embassy-rs/net-stable
net: feature-gate nightly-only async traits to allow building on stable.
2022-08-31 02:08:19 +02:00
30641d0564 Avoid context switch and wait for stop
Should be more efficient given the sub 100 cycles to wait.
2022-08-31 08:47:44 +10:00
464ae67108 net: feature-gate nightly-only async traits to allow building on stable. 2022-08-30 19:43:32 +02:00
171077bacf Avoid double-borrow 2022-08-30 15:57:38 +02:00
b2720117c4 Deduplicate IO methods 2022-08-30 15:48:50 +02:00
4781feafc4 Add split() method to BufferedUarte in embassy-nrf 2022-08-30 15:27:25 +02:00
dcd8c62169 Permits the future to be cancelled
Includes documentation
2022-08-30 20:56:56 +10:00
c0b7fd910e Additional doco 2022-08-30 09:49:04 +10:00
47069dfbe1 lora: Fix for stm32l0 exampe to build 2022-08-26 15:44:58 +02:00
c30b38586a lora: Fix unused import warning 2022-08-26 15:44:58 +02:00
2636a8dc2e Use released rust-lorawan with radio fixes 2022-08-26 15:44:58 +02:00
6dab322c58 Use LP as default 2022-08-26 15:44:58 +02:00
5d114479ff Adjust timings after offset calculation fix 2022-08-26 15:44:58 +02:00
1f36da5ca6 Make settings configurable 2022-08-26 15:44:58 +02:00
af845b7d44 Add impl for offset radio interface 2022-08-26 15:44:58 +02:00
308ca4b8e3 Use pub(crate) visibility for internal SPI
SubGhz provides a public interface for the radio connected to internal SPI
`#[allow(dead_code)]` is required for CI to succeed
2022-08-26 15:44:58 +02:00
60ca5e8479 lora: Improve TX/RX parameters
Match the settings used in the C driver
2022-08-26 15:44:58 +02:00
84240d49ea stm32wl: Fix RCC
* `MSIRGSEL = 1` was required for MSI accept the updated MSI range
* Reorder enable and clock switching to properly handle the jump from
the default 4MHz MSI to a higher MSI freuquency
2022-08-26 15:44:58 +02:00
f31116cafa lora: Make some options configurable
Call `config()` only once at construction not with every RX and TX operation.
The Lora-E5 only supports HP mode, use that instead.
The nucleo board supports both HP and LP and should continue to work.
2022-08-26 15:44:58 +02:00
69d80c086d lora: Use a trait for RF frontend switching
The Seeed Studio Lora-E5 module only has two control pins.
With the `RadioSwitch` trait the user can implement any method required
by the module/board to control the TX/RX direction of the radio frontend.
2022-08-26 15:44:58 +02:00
6ee29ff0bd lora: Propagate "defmt" feature to lora crates 2022-08-26 15:44:58 +02:00
8e8106ef55 lora: Improve IRQ handling
* Interrupt handler only triggers a waker:
Do the actual interrupt processing which involves SUBGHZ SPI coms in the task.
* Do not require a static state for the constructor.
* Remove unsafe from construcor.
2022-08-26 15:44:57 +02:00
61c666212f stm32wl: Do not require external SPI pins for SUBGHZ
For the Seeed Studio Lora-E5 those pins conflict with the radio frontend control GPIOS (PA4 and PA5).
2022-08-26 15:42:25 +02:00
9a873d1dbf Ensure that the sampling is stopped
Ensures that nRF saadc sampling is stopped and is awaited prior to exiting the two sampling methods. Not doing so causes a potential power drain and the potential for dropped buffer writes when having finished continuous sampling.
2022-08-26 14:40:20 +10:00
294 changed files with 15068 additions and 4067 deletions

86
.github/workflows/doc.yml vendored Normal file
View File

@ -0,0 +1,86 @@
name: Docs
on:
push:
branches: [master]
env:
BUILDER_THREADS: '1'
jobs:
doc:
runs-on: ubuntu-latest
# Since stm32 crates take SO LONG to build, we split them
# into a separate job. This way it doesn't slow down updating
# the rest.
strategy:
matrix:
crates:
- stm32
- rest
# This will ensure at most one doc build job is running at a time
# (for stm32 and non-stm32 independently).
# If another job is already running, the new job will wait.
# If another job is already waiting, it'll be canceled.
# This means some commits will be skipped, but that's fine because
# we only care that the latest gets built.
concurrency: doc-${{ matrix.crates }}
steps:
- uses: actions/checkout@v2
with:
submodules: true
- name: Install Rust targets
run: |
rustup target add x86_64-unknown-linux-gnu
rustup target add wasm32-unknown-unknown
rustup target add thumbv6m-none-eabi
rustup target add thumbv7m-none-eabi
rustup target add thumbv7em-none-eabi
rustup target add thumbv7em-none-eabihf
rustup target add thumbv8m.base-none-eabi
rustup target add thumbv8m.main-none-eabi
rustup target add thumbv8m.main-none-eabihf
- name: Install docserver
run: |
wget -q -O /usr/local/bin/builder "https://github.com/embassy-rs/docserver/releases/download/v0.3/builder"
chmod +x /usr/local/bin/builder
- name: build-stm32
if: ${{ matrix.crates=='stm32' }}
run: |
mkdir crates
builder ./embassy-stm32 crates/embassy-stm32/git.zup
builder ./stm32-metapac crates/stm32-metapac/git.zup
- name: build-rest
if: ${{ matrix.crates=='rest' }}
run: |
mkdir crates
builder ./embassy-boot/boot crates/embassy-boot/git.zup
builder ./embassy-boot/nrf crates/embassy-boot-nrf/git.zup
builder ./embassy-boot/stm32 crates/embassy-boot-stm32/git.zup
builder ./embassy-cortex-m crates/embassy-cortex-m/git.zup
builder ./embassy-embedded-hal crates/embassy-embedded-hal/git.zup
builder ./embassy-executor crates/embassy-executor/git.zup
builder ./embassy-futures crates/embassy-futures/git.zup
builder ./embassy-lora crates/embassy-lora/git.zup
builder ./embassy-net crates/embassy-net/git.zup
builder ./embassy-nrf crates/embassy-nrf/git.zup
builder ./embassy-rp crates/embassy-rp/git.zup
builder ./embassy-sync crates/embassy-sync/git.zup
builder ./embassy-time crates/embassy-time/git.zup
builder ./embassy-usb crates/embassy-usb/git.zup
builder ./embassy-usb-driver crates/embassy-usb-driver/git.zup
- name: upload
run: |
mkdir -p ~/.kube
echo "${{secrets.KUBECONFIG}}" > ~/.kube/config
POD=$(kubectl -n embassy get po -l app=docserver -o jsonpath={.items[0].metadata.name})
kubectl cp crates $POD:/data

View File

@ -11,7 +11,7 @@ env:
jobs:
all:
runs-on: ubuntu-20.04
runs-on: ubuntu-latest
needs: [build-nightly, build-stable, test]
steps:
- name: Done

View File

@ -20,10 +20,13 @@
//"embassy-executor/Cargo.toml",
//"embassy-sync/Cargo.toml",
"examples/nrf/Cargo.toml",
// "examples/nrf-rtos-trace/Cargo.toml",
// "examples/rp/Cargo.toml",
// "examples/std/Cargo.toml",
// "examples/stm32f0/Cargo.toml",
// "examples/stm32f1/Cargo.toml",
// "examples/stm32f2/Cargo.toml",
// "examples/stm32f3/Cargo.toml",
// "examples/stm32f4/Cargo.toml",
// "examples/stm32f7/Cargo.toml",
// "examples/stm32g0/Cargo.toml",
@ -32,8 +35,11 @@
// "examples/stm32l0/Cargo.toml",
// "examples/stm32l1/Cargo.toml",
// "examples/stm32l4/Cargo.toml",
// "examples/stm32l5/Cargo.toml",
// "examples/stm32u5/Cargo.toml",
// "examples/stm32wb/Cargo.toml",
// "examples/stm32wb55/Cargo.toml",
// "examples/stm32wl/Cargo.toml",
// "examples/stm32wl55/Cargo.toml",
// "examples/wasm/Cargo.toml",
],

View File

@ -2,7 +2,7 @@
Embassy is the next-generation framework for embedded applications. Write safe, correct and energy-efficient embedded code faster, using the Rust programming language, its async facilities, and the Embassy libraries.
## <a href="https://embassy.dev/embassy/dev/index.html">Documentation</a> - <a href="https://docs.embassy.dev/">API reference</a> - <a href="https://embassy.dev/">Website</a> - <a href="https://matrix.to/#/#embassy-rs:matrix.org">Chat</a>
## <a href="https://embassy.dev/dev/index.html">Documentation</a> - <a href="https://docs.embassy.dev/">API reference</a> - <a href="https://embassy.dev/">Website</a> - <a href="https://matrix.to/#/#embassy-rs:matrix.org">Chat</a>
## Rust + async ❤️ embedded
The Rust programming language is blazingly fast and memory-efficient, with no runtime, garbage collector or OS. It catches a wide variety of bugs at compile time, thanks to its full memory- and thread-safety, and expressive type system.
@ -31,7 +31,7 @@ The <a href="https://docs.embassy.dev/embassy-net/">embassy-net</a> network stac
The <a href="https://github.com/embassy-rs/nrf-softdevice">nrf-softdevice</a> crate provides Bluetooth Low Energy 4.x and 5.x support for nRF52 microcontrollers.
- **LoRa** -
<a href="https://docs.embassy.dev/embassy-lora/">embassy-lora</a> supports LoRa networking on STM32WL wireless microcontrollers and Semtech SX127x transceivers.
<a href="https://docs.embassy.dev/embassy-lora/">embassy-lora</a> supports LoRa networking on STM32WL wireless microcontrollers and Semtech SX126x and SX127x transceivers.
- **USB** -
<a href="https://docs.embassy.dev/embassy-usb/">embassy-usb</a> implements a device-side USB stack. Implementations for common classes such as USB serial (CDC ACM) and USB HID are available, and a rich builder API allows building your own.

44
ci.sh
View File

@ -36,6 +36,10 @@ cargo batch \
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,log \
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,defmt \
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv6m-none-eabi --features nightly,defmt \
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,pool-16 \
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,pool-16,unstable-traits \
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,pool-16,nightly \
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,pool-16,unstable-traits,nightly \
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nightly,nrf52805,gpiote,time-driver-rtc1 \
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nightly,nrf52810,gpiote,time-driver-rtc1 \
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nightly,nrf52811,gpiote,time-driver-rtc1 \
@ -54,25 +58,26 @@ cargo batch \
--- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features nightly,unstable-traits,log \
--- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features nightly,unstable-traits \
--- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features nightly \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f410tb,defmt,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f411ce,defmt,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f429zi,log,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32h755zi-cm7,defmt,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32h7b3ai,defmt,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32l476vg,defmt,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32wb15cc,defmt,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features nightly,stm32l072cz,defmt,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features nightly,stm32l041f6,defmt,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32l151cb-a,defmt,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f398ve,defmt,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features nightly,stm32g0c1ve,defmt,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f217zg,defmt,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features nightly,stm32l552ze,defmt,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features nightly,stm32wl54jc-cm0p,defmt,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32wle5ub,defmt,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f107vc,defmt,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f103re,defmt,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f100c4,defmt,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \
--- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features nightly,intrinsics \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f410tb,defmt,exti,time-driver-any,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f411ce,defmt,exti,time-driver-any,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f429zi,log,exti,time-driver-any,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32h755zi-cm7,defmt,exti,time-driver-any,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32h7b3ai,defmt,exti,time-driver-any,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32l476vg,defmt,exti,time-driver-any,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32wb15cc,defmt,exti,time-driver-any,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features nightly,stm32l072cz,defmt,exti,time-driver-any,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features nightly,stm32l041f6,defmt,exti,time-driver-any,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32l151cb-a,defmt,exti,time-driver-any,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f398ve,defmt,exti,time-driver-any,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features nightly,stm32g0c1ve,defmt,exti,time-driver-any,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f217zg,defmt,exti,time-driver-any,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features nightly,stm32l552ze,defmt,exti,time-driver-any,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features nightly,stm32wl54jc-cm0p,defmt,exti,time-driver-any,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32wle5ub,defmt,exti,time-driver-any,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f107vc,defmt,exti,time-driver-any,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f103re,defmt,exti,time-driver-any,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f100c4,defmt,exti,time-driver-any,unstable-traits \
--- build --release --manifest-path embassy-boot/nrf/Cargo.toml --target thumbv7em-none-eabi --features embassy-nrf/nrf52840 \
--- 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 \
@ -94,6 +99,7 @@ cargo batch \
--- build --release --manifest-path examples/stm32l0/Cargo.toml --target thumbv6m-none-eabi --out-dir out/examples/stm32l0 \
--- build --release --manifest-path examples/stm32l1/Cargo.toml --target thumbv7m-none-eabi --out-dir out/examples/stm32l1 \
--- build --release --manifest-path examples/stm32l4/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/stm32l4 \
--- build --release --manifest-path examples/stm32l5/Cargo.toml --target thumbv8m.main-none-eabihf --out-dir out/examples/stm32l5 \
--- build --release --manifest-path examples/stm32u5/Cargo.toml --target thumbv8m.main-none-eabihf --out-dir out/examples/stm32u5 \
--- build --release --manifest-path examples/stm32wb/Cargo.toml --target thumbv7em-none-eabihf --out-dir out/examples/stm32wb \
--- build --release --manifest-path examples/stm32wl/Cargo.toml --target thumbv7em-none-eabihf --out-dir out/examples/stm32wl \

View File

@ -13,6 +13,8 @@ cargo batch \
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features log \
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features defmt \
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv6m-none-eabi --features defmt \
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,pool-16 \
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,pool-16,unstable-traits \
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52805,gpiote,time-driver-rtc1 \
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52810,gpiote,time-driver-rtc1 \
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52811,gpiote,time-driver-rtc1 \
@ -30,38 +32,38 @@ cargo batch \
--- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features unstable-traits,defmt \
--- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features unstable-traits,log \
--- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32g473cc,defmt,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32g491re,defmt,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32u585zi,defmt,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wb55vy,defmt,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wl55uc-cm4,defmt,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l4r9zi,defmt,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f303vc,defmt,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f411ce,defmt,time-driver-any,embassy-time?/tick-32768hz \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f411ce,defmt,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f429zi,log,time-driver-any,embassy-time?/tick-32768hz \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f429zi,log,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi-cm7,defmt,time-driver-any,embassy-time?/tick-32768hz \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi-cm7,defmt,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l476vg,defmt,time-driver-any,embassy-time?/tick-32768hz \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l476vg,defmt,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32l072cz,defmt,time-driver-any,embassy-time?/tick-32768hz \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32l072cz,defmt,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32l151cb-a,defmt,time-driver-any,embassy-time?/tick-32768hz \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32l151cb-a,defmt,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f410tb,defmt,exti,time-driver-any,embassy-time?/tick-32768hz \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f410tb,defmt,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f429zi,log,exti,time-driver-any,embassy-time?/tick-32768hz \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f429zi,log,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi-cm7,defmt,exti,time-driver-any,embassy-time?/tick-32768hz \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi-cm7,defmt,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l476vg,defmt,exti,time-driver-any,embassy-time?/tick-32768hz \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l476vg,defmt,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32l072cz,defmt,exti,time-driver-any,embassy-time?/tick-32768hz \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32l072cz,defmt,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32l151cb-a,defmt,exti,time-driver-any,embassy-time?/tick-32768hz \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32l151cb-a,defmt,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f217zg,defmt,exti,time-driver-any,embassy-time?/tick-32768hz \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f217zg,defmt,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32g473cc,defmt,exti,time-driver-any,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32g491re,defmt,exti,time-driver-any,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32u585zi,defmt,exti,time-driver-any,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wb55vy,defmt,exti,time-driver-any,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wl55uc-cm4,defmt,exti,time-driver-any,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l4r9zi,defmt,exti,time-driver-any,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f303vc,defmt,exti,time-driver-any,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f411ce,defmt,time-driver-any \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f411ce,defmt,time-driver-any,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f429zi,log,time-driver-any \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f429zi,log,time-driver-any,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi-cm7,defmt,time-driver-any \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi-cm7,defmt,time-driver-any,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l476vg,defmt,time-driver-any \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l476vg,defmt,time-driver-any,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32l072cz,defmt,time-driver-any \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32l072cz,defmt,time-driver-any,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32l151cb-a,defmt,time-driver-any \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32l151cb-a,defmt,time-driver-any,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f410tb,defmt,exti,time-driver-any \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f410tb,defmt,exti,time-driver-any,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f429zi,log,exti,time-driver-any \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f429zi,log,exti,time-driver-any,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi-cm7,defmt,exti,time-driver-any \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi-cm7,defmt,exti,time-driver-any,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l476vg,defmt,exti,time-driver-any \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l476vg,defmt,exti,time-driver-any,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32l072cz,defmt,exti,time-driver-any \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32l072cz,defmt,exti,time-driver-any,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32l151cb-a,defmt,exti,time-driver-any \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32l151cb-a,defmt,exti,time-driver-any,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f217zg,defmt,exti,time-driver-any \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f217zg,defmt,exti,time-driver-any,unstable-traits \
--- build --release --manifest-path examples/nrf/Cargo.toml --target thumbv7em-none-eabi --no-default-features --out-dir out/examples/nrf --bin raw_spawn \
--- build --release --manifest-path examples/stm32l0/Cargo.toml --target thumbv6m-none-eabi --no-default-features --out-dir out/examples/stm32l0 --bin raw_spawn \

View File

@ -3,16 +3,16 @@ 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.1.0", path = "../../../../../embassy-executor", features = ["defmt", "nightly"] }
embassy-executor = { version = "0.1.0", path = "../../../../../embassy-executor", features = ["defmt", "nightly", "integrated-timers"] }
embassy-time = { version = "0.1.0", path = "../../../../../embassy-time", features = ["defmt", "nightly"] }
embassy-nrf = { version = "0.1.0", path = "../../../../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "nightly"] }
defmt = "0.3"
defmt-rtt = "0.3"
cortex-m = "0.7.3"
cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] }
cortex-m-rt = "0.7.0"
embedded-hal = "0.2.6"
panic-probe = { version = "0.3", features = ["print-defmt"] }

View File

@ -0,0 +1,35 @@
//! This build script copies the `memory.x` file from the crate root into
//! a directory where the linker can always find it at build time.
//! For many projects this is optional, as the linker always searches the
//! project root directory -- wherever `Cargo.toml` is. However, if you
//! are using a workspace or have a more complicated build setup, this
//! build script becomes required. Additionally, by requesting that
//! Cargo re-run the build script whenever `memory.x` is changed,
//! updating `memory.x` ensures a rebuild of the application with the
//! new memory settings.
use std::env;
use std::fs::File;
use std::io::Write;
use std::path::PathBuf;
fn main() {
// Put `memory.x` in our output directory and ensure it's
// on the linker search path.
let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
File::create(out.join("memory.x"))
.unwrap()
.write_all(include_bytes!("memory.x"))
.unwrap();
println!("cargo:rustc-link-search={}", out.display());
// By default, Cargo will re-run a build script whenever
// any file in the project changes. By specifying `memory.x`
// here, we ensure the build script is only re-run when
// `memory.x` is changed.
println!("cargo:rerun-if-changed=memory.x");
println!("cargo:rustc-link-arg-bins=--nmagic");
println!("cargo:rustc-link-arg-bins=-Tlink.x");
println!("cargo:rustc-link-arg-bins=-Tdefmt.x");
}

View File

@ -0,0 +1,7 @@
MEMORY
{
/* NOTE 1 K = 1 KiBi = 1024 bytes */
/* These values correspond to the NRF52840 with Softdevices S140 7.0.1 */
FLASH : ORIGIN = 0x00000000, LENGTH = 1024K
RAM : ORIGIN = 0x20000000, LENGTH = 256K
}

View File

@ -2,6 +2,7 @@
name = "blinky-async"
version = "0.1.0"
edition = "2021"
license = "MIT OR Apache-2.0"
[dependencies]
cortex-m = "0.7"

View File

@ -2,6 +2,7 @@
name = "blinky-hal"
version = "0.1.0"
edition = "2021"
license = "MIT OR Apache-2.0"
[dependencies]
cortex-m = "0.7"

View File

@ -2,6 +2,7 @@
name = "blinky-irq"
version = "0.1.0"
edition = "2021"
license = "MIT OR Apache-2.0"
[dependencies]
cortex-m = "0.7"

View File

@ -2,6 +2,7 @@
name = "blinky-pac"
version = "0.1.0"
edition = "2021"
license = "MIT OR Apache-2.0"
[dependencies]
cortex-m = "0.7"

View File

@ -21,7 +21,7 @@ Then, what follows are some declarations on how to deal with panics and faults.
[source,rust]
----
include::example$basic/src/main.rs[lines="11..12"]
include::example$basic/src/main.rs[lines="10"]
----
=== Task declaration
@ -30,7 +30,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="13..22"]
include::example$basic/src/main.rs[lines="12..20"]
----
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.
@ -45,23 +45,10 @@ The `Spawner` is the way the main application spawns other tasks. The `Periphera
[source,rust]
----
include::example$basic/src/main.rs[lines="23..-1"]
include::example$basic/src/main.rs[lines="22..-1"]
----
`#[embassy_executor::main]` takes an optional `config` parameter specifying a function that returns an instance of HAL's `Config` struct. For example:
```rust
fn embassy_config() -> embassy_nrf::config::Config {
embassy_nrf::config::Config::default()
}
#[embassy_executor::main(config = "embassy_config()")]
async fn main(_spawner: Spawner, p: embassy_nrf::Peripherals) {
// ...
}
```
What happens when the `blinker` task have 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::main]` macro. The macro does the following:
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::main]` macro. The macro does the following:
. Creates an Embassy Executor
. Initializes the microcontroller HAL to get the `Peripherals`
@ -76,7 +63,7 @@ The project definition needs to contain the embassy dependencies:
[source,toml]
----
include::example$basic/Cargo.toml[lines="8..9"]
include::example$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).

View File

@ -25,10 +25,19 @@ image::bootloader_flash.png[Bootloader flash layout]
The bootloader divides the storage into 4 main partitions, configurable when creating the bootloader
instance or via linker scripts:
* BOOTLOADER - Where the bootloader is placed. The bootloader itself consumes about 8kB of flash.
* ACTIVE - Where the main application is placed. The bootloader will attempt to load the application at the start of this partition. This partition is only written to by the bootloader.
* DFU - Where the application-to-be-swapped is placed. This partition is written to by the application.
* BOOTLOADER STATE - Where the bootloader stores the current state describing if the active and dfu partitions need to be swapped. When the new firmware has been written to the DFU partition, a flag is set to instruct the bootloader that the partitions should be swapped.
* BOOTLOADER - Where the bootloader is placed. The bootloader itself consumes about 8kB of flash, but if you need to debug it and have space available, increasing this to 24kB will allow you to run the bootloader with probe-rs.
* ACTIVE - Where the main application is placed. The bootloader will attempt to load the application at the start of this partition. This partition is only written to by the bootloader. The size required for this partition depends on the size of your application.
* DFU - Where the application-to-be-swapped is placed. This partition is written to by the application. This partition must be at least 1 page bigger than the ACTIVE partition, since the swap algorithm uses the extra space to ensure power safe copy of data:
+
Partition Size~dfu~= Partition Size~active~+ Page Size~active~
+
All values are specified in bytes.
* BOOTLOADER STATE - Where the bootloader stores the current state describing if the active and dfu partitions need to be swapped. When the new firmware has been written to the DFU partition, a magic field is written to instruct the bootloader that the partitions should be swapped. This partition must be able to store a magic field as well as the partition swap progress. The partition size given by:
+
Partition Size~state~ = Write Size~state~ + (2 × Partition Size~active~ / Page Size~active~)
+
All values are specified in bytes.
The partitions for ACTIVE (+BOOTLOADER), DFU and BOOTLOADER_STATE may be placed in separate flash. The page size used by the bootloader is determined by the lowest common multiple of the ACTIVE and DFU page sizes.
The BOOTLOADER_STATE partition must be big enough to store one word per page in the ACTIVE and DFU partitions combined.

View File

@ -8,7 +8,7 @@ The application we'll write is a simple 'push button, blink led' application, wh
== PAC version
The PAC is the lowest API for accessing peripherals and registers, if you don't count reading/writing directly to memory addresses. It provide distinct types
The PAC is the lowest API for accessing peripherals and registers, if you don't count reading/writing directly to memory addresses. It provides distinct types
to make accessing peripheral registers easier, but it does not prevent you from writing unsafe code.
Writing an application using the PAC directly is therefore not recommended, but if the functionality you want to use is not exposed in the upper layers, that's what you need to use.
@ -20,13 +20,13 @@ The blinky app using PAC is shown below:
include::example$layer-by-layer/blinky-pac/src/main.rs[]
----
As you can see, there are a lot of code needed to enable the peripheral clocks, configuring the input pins and the output pins of the application.
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.
Another downside of this application is that it is busy-looping while polling the button state. This prevents the microcontroller from utilizing any sleep mode to save power.
== HAL version
To simplify our application, we can use the HAL instead. The HAL exposes higher level APIs that handle details such
To simplify our application, we can use the HAL instead. The HAL exposes higher level APIs that handle details such as:
* Automatically enabling the peripheral clock when you're using the peripheral
* Deriving and applying register configuration from higher level types
@ -39,7 +39,7 @@ The HAL example is shown below:
include::example$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` hides all the details accessing the GPIO registers, and allow you to use a much simpler API to query the state of the button and toggle the LED output accordingly.
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.
The same downside from the PAC example still applies though: the application is busy looping and consuming more power than necessary.

View File

@ -20,7 +20,7 @@ IMPORTANT: The executor relies on tasks not blocking indefinitely, as this preve
image::embassy_executor.png[Executor model]
If you use the `#[embassy::main]` macro in your application, it creates the `Executor` for you and spawns the main entry point as the first task. You can also create the Executor manually, and you can in fact create multiple Executors.
If you use the `#[embassy_executor::main]` macro in your application, it creates the `Executor` for you and spawns the main entry point as the first task. You can also create the Executor manually, and you can in fact create multiple Executors.
== Interrupts

View File

@ -4,9 +4,9 @@ The link:https://github.com/embassy-rs/embassy/tree/master/embassy-stm32[Embassy
== The infinite variant problem
STM32 microcontrollers comes in many families and flavors, and supporting all of them is a big undertaking. Embassy has taken advantage of the fact
STM32 microcontrollers come in many families, and flavors and supporting all of them is a big undertaking. Embassy has taken advantage of the fact
that the STM32 peripheral versions are shared across chip families. Instead of re-implementing the SPI
peripheral for every STM32 chip family, embassy have a single SPI implementation that depends on
peripheral for every STM32 chip family, embassy has a single SPI implementation that depends on
code-generated register types that are identical for STM32 families with the same version of a given peripheral.
=== The metapac

30
embassy-boot/README.md Normal file
View File

@ -0,0 +1,30 @@
# embassy-boot
An [Embassy](https://embassy.dev) project.
A lightweight bootloader supporting firmware updates in a power-fail-safe way, with trial boots and rollbacks.
The bootloader can be used either as a library or be flashed directly with the default configuration derived from linker scripts.
By design, the bootloader does not provide any network capabilities. Networking capabilities for fetching new firmware can be provided by the user application, using the bootloader as a library for updating the firmware, or by using the bootloader as a library and adding this capability yourself.
## Hardware support
The bootloader supports different hardware in separate crates:
* `embassy-boot-nrf` - for the nRF microcontrollers.
* `embassy-boot-stm32` - for the STM32 microcontrollers.
## Minimum supported Rust version (MSRV)
`embassy-boot` requires Rust nightly to compile as it relies on async traits for interacting with the flash peripherals.
## License
This work is licensed under either of
- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or
<http://www.apache.org/licenses/LICENSE-2.0>)
- MIT license ([LICENSE-MIT](LICENSE-MIT) or <http://opensource.org/licenses/MIT>)
at your option.

View File

@ -3,6 +3,7 @@ edition = "2021"
name = "embassy-boot"
version = "0.1.0"
description = "Bootloader using Embassy"
license = "MIT OR Apache-2.0"
[package.metadata.embassy_docs]
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-boot-v$VERSION/embassy-boot/boot/src/"

File diff suppressed because it is too large Load Diff

View File

@ -3,6 +3,7 @@ edition = "2021"
name = "embassy-boot-nrf"
version = "0.1.0"
description = "Bootloader lib for nRF chips"
license = "MIT OR Apache-2.0"
[package.metadata.embassy_docs]
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-boot-nrf-v$VERSION/embassy-boot/nrf/src/"

View File

@ -1,24 +1,25 @@
#![no_std]
#![feature(generic_associated_types)]
#![feature(type_alias_impl_trait)]
#![allow(incomplete_features)]
#![feature(generic_const_exprs)]
#![warn(missing_docs)]
#![doc = include_str!("../../README.md")]
mod fmt;
pub use embassy_boot::{FirmwareUpdater, FlashConfig, FlashProvider, Partition, SingleFlashProvider};
pub use embassy_boot::{AlignedBuffer, BootFlash, FirmwareUpdater, FlashConfig, Partition, SingleFlashConfig};
use embassy_nrf::nvmc::{Nvmc, PAGE_SIZE};
use embassy_nrf::peripherals::WDT;
use embassy_nrf::wdt;
use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash};
/// A bootloader for nRF devices.
pub struct BootLoader {
boot: embassy_boot::BootLoader<PAGE_SIZE>,
boot: embassy_boot::BootLoader,
magic: AlignedBuffer<4>,
page: AlignedBuffer<PAGE_SIZE>,
}
impl BootLoader {
impl Default for BootLoader {
/// Create a new bootloader instance using parameters from linker script
pub fn default() -> Self {
fn default() -> Self {
extern "C" {
static __bootloader_state_start: u32;
static __bootloader_state_end: u32;
@ -53,26 +54,32 @@ impl BootLoader {
Self::new(active, dfu, state)
}
}
impl BootLoader {
/// Create a new bootloader instance using the supplied partitions for active, dfu and state.
pub fn new(active: Partition, dfu: Partition, state: Partition) -> Self {
Self {
boot: embassy_boot::BootLoader::new(active, dfu, state),
magic: AlignedBuffer([0; 4]),
page: AlignedBuffer([0; PAGE_SIZE]),
}
}
/// Boots the application without softdevice mechanisms
pub fn prepare<F: FlashProvider>(&mut self, flash: &mut F) -> usize
where
[(); <<F as FlashProvider>::STATE as FlashConfig>::FLASH::WRITE_SIZE]:,
[(); <<F as FlashProvider>::ACTIVE as FlashConfig>::FLASH::ERASE_SIZE]:,
{
match self.boot.prepare_boot(flash) {
/// Inspect the bootloader state and perform actions required before booting, such as swapping
/// firmware.
pub fn prepare<F: FlashConfig>(&mut self, flash: &mut F) -> usize {
match self.boot.prepare_boot(flash, &mut self.magic.0, &mut self.page.0) {
Ok(_) => self.boot.boot_address(),
Err(_) => panic!("boot prepare error!"),
}
}
/// Boots the application without softdevice mechanisms.
///
/// # Safety
///
/// This modifies the stack pointer and reset vector and will run code placed in the active partition.
#[cfg(not(feature = "softdevice"))]
pub unsafe fn load(&mut self, start: usize) -> ! {
let mut p = cortex_m::Peripherals::steal();
@ -81,6 +88,11 @@ impl BootLoader {
cortex_m::asm::bootload(start as *const u32)
}
/// Boots the application assuming softdevice is present.
///
/// # Safety
///
/// This modifies the stack pointer and reset vector and will run code placed in the active partition.
#[cfg(feature = "softdevice")]
pub unsafe fn load(&mut self, _app: usize) -> ! {
use nrf_softdevice_mbr as mbr;

View File

@ -3,6 +3,7 @@ edition = "2021"
name = "embassy-boot-stm32"
version = "0.1.0"
description = "Bootloader lib for STM32 chips"
license = "MIT OR Apache-2.0"
[package.metadata.embassy_docs]
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-boot-nrf-v$VERSION/embassy-boot/stm32/src/"

View File

@ -1,21 +1,57 @@
#![no_std]
#![feature(generic_associated_types)]
#![feature(type_alias_impl_trait)]
#![allow(incomplete_features)]
#![feature(generic_const_exprs)]
#![warn(missing_docs)]
#![doc = include_str!("../../README.md")]
mod fmt;
pub use embassy_boot::{FirmwareUpdater, FlashConfig, FlashProvider, Partition, SingleFlashProvider, State};
use embedded_storage::nor_flash::NorFlash;
pub use embassy_boot::{AlignedBuffer, BootFlash, FirmwareUpdater, FlashConfig, Partition, SingleFlashConfig, State};
pub struct BootLoader<const PAGE_SIZE: usize> {
boot: embassy_boot::BootLoader<PAGE_SIZE>,
/// A bootloader for STM32 devices.
pub struct BootLoader<const PAGE_SIZE: usize, const WRITE_SIZE: usize> {
boot: embassy_boot::BootLoader,
magic: AlignedBuffer<WRITE_SIZE>,
page: AlignedBuffer<PAGE_SIZE>,
}
impl<const PAGE_SIZE: usize> BootLoader<PAGE_SIZE> {
impl<const PAGE_SIZE: usize, const WRITE_SIZE: usize> BootLoader<PAGE_SIZE, WRITE_SIZE> {
/// Create a new bootloader instance using the supplied partitions for active, dfu and state.
pub fn new(active: Partition, dfu: Partition, state: Partition) -> Self {
Self {
boot: embassy_boot::BootLoader::new(active, dfu, state),
magic: AlignedBuffer([0; WRITE_SIZE]),
page: AlignedBuffer([0; PAGE_SIZE]),
}
}
/// Inspect the bootloader state and perform actions required before booting, such as swapping
/// firmware.
pub fn prepare<F: FlashConfig>(&mut self, flash: &mut F) -> usize {
match self.boot.prepare_boot(flash, self.magic.as_mut(), self.page.as_mut()) {
Ok(_) => embassy_stm32::flash::FLASH_BASE + self.boot.boot_address(),
Err(_) => panic!("boot prepare error!"),
}
}
/// Boots the application.
///
/// # Safety
///
/// This modifies the stack pointer and reset vector and will run code placed in the active partition.
pub unsafe fn load(&mut self, start: usize) -> ! {
trace!("Loading app at 0x{:x}", start);
#[allow(unused_mut)]
let mut p = cortex_m::Peripherals::steal();
#[cfg(not(armv6m))]
p.SCB.invalidate_icache();
p.SCB.vtor.write(start as u32);
cortex_m::asm::bootload(start as *const u32)
}
}
impl<const PAGE_SIZE: usize, const WRITE_SIZE: usize> Default for BootLoader<PAGE_SIZE, WRITE_SIZE> {
/// Create a new bootloader instance using parameters from linker script
pub fn default() -> Self {
fn default() -> Self {
extern "C" {
static __bootloader_state_start: u32;
static __bootloader_state_end: u32;
@ -50,34 +86,4 @@ impl<const PAGE_SIZE: usize> BootLoader<PAGE_SIZE> {
Self::new(active, dfu, state)
}
/// Create a new bootloader instance using the supplied partitions for active, dfu and state.
pub fn new(active: Partition, dfu: Partition, state: Partition) -> Self {
Self {
boot: embassy_boot::BootLoader::new(active, dfu, state),
}
}
/// Boots the application
pub fn prepare<F: FlashProvider>(&mut self, flash: &mut F) -> usize
where
[(); <<F as FlashProvider>::STATE as FlashConfig>::FLASH::WRITE_SIZE]:,
[(); <<F as FlashProvider>::ACTIVE as FlashConfig>::FLASH::ERASE_SIZE]:,
{
match self.boot.prepare_boot(flash) {
Ok(_) => embassy_stm32::flash::FLASH_BASE + self.boot.boot_address(),
Err(_) => panic!("boot prepare error!"),
}
}
pub unsafe fn load(&mut self, start: usize) -> ! {
trace!("Loading app at 0x{:x}", start);
#[allow(unused_mut)]
let mut p = cortex_m::Peripherals::steal();
#[cfg(not(armv6m))]
p.SCB.invalidate_icache();
p.SCB.vtor.write(start as u32);
cortex_m::asm::bootload(start as *const u32)
}
}

View File

@ -2,6 +2,7 @@
name = "embassy-cortex-m"
version = "0.1.0"
edition = "2021"
license = "MIT OR Apache-2.0"
[package.metadata.embassy_docs]
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-cortex-m-v$VERSION/embassy-cortex-m/src/"

View File

@ -2,13 +2,14 @@
name = "embassy-embedded-hal"
version = "0.1.0"
edition = "2021"
license = "MIT OR Apache-2.0"
[package.metadata.embassy_docs]
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-embedded-hal-v$VERSION/embassy-embedded-hal/src/"
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-embedded-hal/src/"
features = ["nightly", "std"]
target = "thumbv7em-none-eabi"
target = "x86_64-unknown-linux-gnu"
[features]
std = []
@ -18,8 +19,8 @@ nightly = ["embedded-hal-async", "embedded-storage-async"]
[dependencies]
embassy-sync = { version = "0.1.0", path = "../embassy-sync" }
embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] }
embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.8" }
embedded-hal-async = { version = "0.1.0-alpha.1", optional = true }
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9" }
embedded-hal-async = { version = "=0.1.0-alpha.3", optional = true }
embedded-storage = "0.3.0"
embedded-storage-async = { version = "0.3.0", optional = true }
nb = "1.0.0"

View File

@ -1,5 +1,5 @@
#![cfg_attr(not(feature = "std"), no_std)]
#![cfg_attr(feature = "nightly", feature(generic_associated_types, type_alias_impl_trait))]
#![cfg_attr(feature = "nightly", feature(type_alias_impl_trait))]
#![warn(missing_docs)]
//! Utilities to use `embedded-hal` traits with Embassy.

View File

@ -29,7 +29,7 @@ use core::future::Future;
use embassy_sync::blocking_mutex::raw::RawMutex;
use embassy_sync::mutex::Mutex;
use embedded_hal_1::digital::blocking::OutputPin;
use embedded_hal_1::digital::OutputPin;
use embedded_hal_1::spi::ErrorType;
use embedded_hal_async::spi;
@ -57,7 +57,7 @@ where
type Error = SpiDeviceError<BUS::Error, CS::Error>;
}
impl<M, BUS, CS> spi::SpiDevice for SpiDevice<'_, M, BUS, CS>
unsafe impl<M, BUS, CS> spi::SpiDevice for SpiDevice<'_, M, BUS, CS>
where
M: RawMutex + 'static,
BUS: spi::SpiBusFlush + 'static,
@ -122,7 +122,7 @@ where
type Error = SpiDeviceError<BUS::Error, CS::Error>;
}
impl<M, BUS, CS> spi::SpiDevice for SpiDeviceWithConfig<'_, M, BUS, CS>
unsafe impl<M, BUS, CS> spi::SpiDevice for SpiDeviceWithConfig<'_, M, BUS, CS>
where
M: RawMutex + 'static,
BUS: spi::SpiBusFlush + SetConfig + 'static,

View File

@ -20,8 +20,7 @@ use core::cell::RefCell;
use embassy_sync::blocking_mutex::raw::RawMutex;
use embassy_sync::blocking_mutex::Mutex;
use embedded_hal_1::i2c::blocking::{I2c, Operation};
use embedded_hal_1::i2c::ErrorType;
use embedded_hal_1::i2c::{ErrorType, I2c, Operation};
use crate::shared_bus::I2cDeviceError;
use crate::SetConfig;

View File

@ -22,9 +22,9 @@ use core::cell::RefCell;
use embassy_sync::blocking_mutex::raw::RawMutex;
use embassy_sync::blocking_mutex::Mutex;
use embedded_hal_1::digital::blocking::OutputPin;
use embedded_hal_1::digital::OutputPin;
use embedded_hal_1::spi;
use embedded_hal_1::spi::blocking::SpiBusFlush;
use embedded_hal_1::spi::SpiBusFlush;
use crate::shared_bus::SpiDeviceError;
use crate::SetConfig;
@ -50,7 +50,7 @@ where
type Error = SpiDeviceError<BUS::Error, CS::Error>;
}
impl<BUS, M, CS> embedded_hal_1::spi::blocking::SpiDevice for SpiDevice<'_, M, BUS, CS>
impl<BUS, M, CS> embedded_hal_1::spi::SpiDevice for SpiDevice<'_, M, BUS, CS>
where
M: RawMutex,
BUS: SpiBusFlush,
@ -146,7 +146,7 @@ where
type Error = SpiDeviceError<BUS::Error, CS::Error>;
}
impl<BUS, M, CS> embedded_hal_1::spi::blocking::SpiDevice for SpiDeviceWithConfig<'_, M, BUS, CS>
impl<BUS, M, CS> embedded_hal_1::spi::SpiDevice for SpiDeviceWithConfig<'_, M, BUS, CS>
where
M: RawMutex,
BUS: SpiBusFlush + SetConfig,

View File

@ -2,12 +2,19 @@
name = "embassy-executor"
version = "0.1.0"
edition = "2021"
license = "MIT OR Apache-2.0"
description = "async/await executor designed for embedded usage"
repository = "https://github.com/embassy-rs/embassy"
categories = [
"embedded",
"no-std",
"asynchronous",
]
[package.metadata.embassy_docs]
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-executor-v$VERSION/embassy-executor/src/"
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-executor/src/"
features = ["nightly", "defmt", "unstable-traits"]
features = ["nightly", "defmt"]
flavors = [
{ name = "std", target = "x86_64-unknown-linux-gnu", features = ["std"] },
{ name = "wasm", target = "wasm32-unknown-unknown", features = ["wasm"] },
@ -22,8 +29,8 @@ flavors = [
[features]
default = []
std = ["embassy-macros/std"]
wasm = ["dep:wasm-bindgen", "dep:js-sys", "embassy-macros/wasm"]
std = ["critical-section/std"]
wasm = ["dep:wasm-bindgen", "dep:js-sys"]
# Enable nightly-only features
nightly = []

View File

@ -8,18 +8,22 @@
pub(crate) mod fmt;
#[cfg(feature = "nightly")]
pub use embassy_macros::{main, task};
pub use embassy_macros::task;
cfg_if::cfg_if! {
if #[cfg(cortex_m)] {
#[path="arch/cortex_m.rs"]
mod arch;
pub use arch::*;
#[cfg(feature = "nightly")]
pub use embassy_macros::main_cortex_m as main;
}
else if #[cfg(target_arch="riscv32")] {
#[path="arch/riscv32.rs"]
mod arch;
pub use arch::*;
#[cfg(feature = "nightly")]
pub use embassy_macros::main_riscv as main;
}
else if #[cfg(all(target_arch="xtensa", feature = "nightly"))] {
#[path="arch/xtensa.rs"]
@ -30,11 +34,15 @@ cfg_if::cfg_if! {
#[path="arch/wasm.rs"]
mod arch;
pub use arch::*;
#[cfg(feature = "nightly")]
pub use embassy_macros::main_wasm as main;
}
else if #[cfg(feature="std")] {
#[path="arch/std.rs"]
mod arch;
pub use arch::*;
#[cfg(feature = "nightly")]
pub use embassy_macros::main_std as main;
}
}

View File

@ -354,46 +354,54 @@ impl Executor {
/// somehow schedule for `poll()` to be called later, at a time you know for sure there's
/// no `poll()` already running.
pub unsafe fn poll(&'static self) {
#[cfg(feature = "integrated-timers")]
self.timer_queue.dequeue_expired(Instant::now(), |task| wake_task(task));
loop {
#[cfg(feature = "integrated-timers")]
self.timer_queue.dequeue_expired(Instant::now(), |task| wake_task(task));
self.run_queue.dequeue_all(|p| {
let task = p.as_ref();
self.run_queue.dequeue_all(|p| {
let task = p.as_ref();
#[cfg(feature = "integrated-timers")]
task.expires_at.set(Instant::MAX);
let state = task.state.fetch_and(!STATE_RUN_QUEUED, Ordering::AcqRel);
if state & STATE_SPAWNED == 0 {
// If task is not running, ignore it. This can happen in the following scenario:
// - Task gets dequeued, poll starts
// - While task is being polled, it gets woken. It gets placed in the queue.
// - Task poll finishes, returning done=true
// - RUNNING bit is cleared, but the task is already in the queue.
return;
}
#[cfg(feature = "rtos-trace")]
trace::task_exec_begin(p.as_ptr() as u32);
// Run the task
task.poll_fn.read()(p as _);
#[cfg(feature = "rtos-trace")]
trace::task_exec_end();
// Enqueue or update into timer_queue
#[cfg(feature = "integrated-timers")]
self.timer_queue.update(p);
});
#[cfg(feature = "integrated-timers")]
task.expires_at.set(Instant::MAX);
let state = task.state.fetch_and(!STATE_RUN_QUEUED, Ordering::AcqRel);
if state & STATE_SPAWNED == 0 {
// If task is not running, ignore it. This can happen in the following scenario:
// - Task gets dequeued, poll starts
// - While task is being polled, it gets woken. It gets placed in the queue.
// - Task poll finishes, returning done=true
// - RUNNING bit is cleared, but the task is already in the queue.
return;
{
// If this is already in the past, set_alarm might return false
// In that case do another poll loop iteration.
let next_expiration = self.timer_queue.next_expiration();
if driver::set_alarm(self.alarm, next_expiration.as_ticks()) {
break;
}
}
#[cfg(feature = "rtos-trace")]
trace::task_exec_begin(p.as_ptr() as u32);
// Run the task
task.poll_fn.read()(p as _);
#[cfg(feature = "rtos-trace")]
trace::task_exec_end();
// Enqueue or update into timer_queue
#[cfg(feature = "integrated-timers")]
self.timer_queue.update(p);
});
#[cfg(feature = "integrated-timers")]
{
// If this is already in the past, set_alarm will immediately trigger the alarm.
// This will cause `signal_fn` to be called, which will cause `poll()` to be called again,
// so we immediately do another poll loop iteration.
let next_expiration = self.timer_queue.next_expiration();
driver::set_alarm(self.alarm, next_expiration.as_ticks());
#[cfg(not(feature = "integrated-timers"))]
{
break;
}
}
#[cfg(feature = "rtos-trace")]
@ -436,14 +444,21 @@ pub unsafe fn wake_task(task: NonNull<TaskHeader>) {
}
#[cfg(feature = "integrated-timers")]
#[no_mangle]
unsafe fn _embassy_time_schedule_wake(at: Instant, waker: &core::task::Waker) {
let task = waker::task_from_waker(waker);
let task = task.as_ref();
let expires_at = task.expires_at.get();
task.expires_at.set(expires_at.min(at));
struct TimerQueue;
#[cfg(feature = "integrated-timers")]
impl embassy_time::queue::TimerQueue for TimerQueue {
fn schedule_wake(&'static self, at: Instant, waker: &core::task::Waker) {
let task = waker::task_from_waker(waker);
let task = unsafe { task.as_ref() };
let expires_at = task.expires_at.get();
task.expires_at.set(expires_at.min(at));
}
}
#[cfg(feature = "integrated-timers")]
embassy_time::timer_queue_impl!(static TIMER_QUEUE: TimerQueue = TimerQueue);
#[cfg(feature = "rtos-trace")]
impl rtos_trace::RtosTraceOSCallbacks for Executor {
fn task_list() {

View File

@ -1,10 +1,9 @@
use core::future::poll_fn;
use core::marker::PhantomData;
use core::mem;
use core::ptr::NonNull;
use core::task::Poll;
use futures_util::future::poll_fn;
use super::raw;
/// Token to spawn a newly-created task in an executor.

View File

@ -2,6 +2,7 @@
name = "embassy-hal-common"
version = "0.1.0"
edition = "2021"
license = "MIT OR Apache-2.0"
[features]

View File

@ -6,7 +6,7 @@ use core::ops::{Deref, DerefMut};
/// This is functionally the same as a `&'a mut T`. The reason for having a
/// dedicated struct is memory efficiency:
///
/// Peripheral singletons are typically either zero-sized (for concrete peripehrals
/// Peripheral singletons are typically either zero-sized (for concrete peripherals
/// like `PA9` or `Spi4`) or very small (for example `AnyPin` which is 1 byte).
/// However `&mut T` is always 4 bytes for 32-bit targets, even if T is zero-sized.
/// PeripheralRef stores a copy of `T` instead, so it's the same size.

View File

@ -2,22 +2,26 @@
name = "embassy-lora"
version = "0.1.0"
edition = "2021"
license = "MIT OR Apache-2.0"
[package.metadata.embassy_docs]
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-lora-v$VERSION/embassy-lora/src/"
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-lora/src/"
features = ["time", "defmt"]
flavors = [
{ name = "sx127x", target = "thumbv7em-none-eabihf", features = ["sx127x", "embassy-stm32/stm32wl55jc-cm4", "embassy-stm32/time-driver-any", "embassy-time/tick-32768hz"] },
{ name = "stm32wl", target = "thumbv7em-none-eabihf", features = ["stm32wl", "embassy-stm32/stm32wl55jc-cm4", "embassy-stm32/time-driver-any", "embassy-time/tick-32768hz"] },
{ name = "sx126x", target = "thumbv7em-none-eabihf", features = ["sx126x"] },
{ name = "sx127x", target = "thumbv7em-none-eabihf", features = ["sx127x", "embassy-stm32/stm32wl55jc-cm4", "embassy-stm32/time-driver-any"] },
{ name = "stm32wl", target = "thumbv7em-none-eabihf", features = ["stm32wl", "embassy-stm32/stm32wl55jc-cm4", "embassy-stm32/time-driver-any"] },
]
[lib]
[features]
sx126x = []
sx127x = []
stm32wl = ["embassy-stm32", "embassy-stm32/subghz"]
time = []
defmt = ["dep:defmt", "lorawan/defmt", "lorawan-device/defmt"]
[dependencies]
@ -27,12 +31,12 @@ log = { version = "0.4.14", optional = true }
embassy-time = { version = "0.1.0", path = "../embassy-time" }
embassy-sync = { version = "0.1.0", path = "../embassy-sync" }
embassy-stm32 = { version = "0.1.0", path = "../embassy-stm32", default-features = false, optional = true }
embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.8" }
embedded-hal-async = { version = "0.1.0-alpha.1" }
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9" }
embedded-hal-async = { version = "=0.1.0-alpha.3" }
embassy-hal-common = { version = "0.1.0", path = "../embassy-hal-common", default-features = false }
futures = { version = "0.3.17", default-features = false, features = [ "async-await" ] }
embedded-hal = { version = "0.2", features = ["unproven"] }
bit_field = { version = "0.10" }
lorawan-device = { version = "0.7.1", default-features = false, features = ["async"] }
lorawan-device = { version = "0.8.0", default-features = false, features = ["async"] }
lorawan = { version = "0.7.1", default-features = false }

View File

@ -1,6 +1,5 @@
#![no_std]
#![feature(type_alias_impl_trait)]
#![feature(generic_associated_types)]
//! embassy-lora is a collection of async radio drivers that integrate with the lorawan-device
//! crate's async LoRaWAN MAC implementation.
@ -8,16 +7,40 @@ pub(crate) mod fmt;
#[cfg(feature = "stm32wl")]
pub mod stm32wl;
#[cfg(feature = "sx126x")]
pub mod sx126x;
#[cfg(feature = "sx127x")]
pub mod sx127x;
#[cfg(feature = "time")]
use embassy_time::{Duration, Instant, Timer};
/// A convenience timer to use with the LoRaWAN crate
pub struct LoraTimer;
#[cfg(feature = "time")]
pub struct LoraTimer {
start: Instant,
}
#[cfg(feature = "time")]
impl LoraTimer {
pub fn new() -> Self {
Self { start: Instant::now() }
}
}
#[cfg(feature = "time")]
impl lorawan_device::async_device::radio::Timer for LoraTimer {
fn reset(&mut self) {
self.start = Instant::now();
}
type AtFuture<'m> = impl core::future::Future<Output = ()> + 'm;
fn at<'m>(&'m mut self, millis: u64) -> Self::AtFuture<'m> {
Timer::at(self.start + Duration::from_millis(millis))
}
type DelayFuture<'m> = impl core::future::Future<Output = ()> + 'm;
fn delay_ms<'m>(&'m mut self, millis: u64) -> Self::DelayFuture<'m> {
embassy_time::Timer::after(embassy_time::Duration::from_millis(millis))
Timer::after(Duration::from_millis(millis))
}
}

View File

@ -1,18 +1,16 @@
//! A radio driver integration for the radio found on STM32WL family devices.
use core::future::Future;
use core::mem::MaybeUninit;
use core::future::{poll_fn, Future};
use core::task::Poll;
use embassy_hal_common::{into_ref, PeripheralRef};
use embassy_hal_common::{into_ref, Peripheral, PeripheralRef};
use embassy_stm32::dma::NoDma;
use embassy_stm32::gpio::{AnyPin, Output};
use embassy_stm32::interrupt::{InterruptExt, SUBGHZ_RADIO};
use embassy_stm32::interrupt::{Interrupt, InterruptExt, SUBGHZ_RADIO};
use embassy_stm32::subghz::{
CalibrateImage, CfgIrq, CodingRate, Error, HeaderType, Irq, LoRaBandwidth, LoRaModParams, LoRaPacketParams,
LoRaSyncWord, Ocp, PaConfig, PaSel, PacketType, RampTime, RegMode, RfFreq, SpreadingFactor as SF, StandbyClk,
CalibrateImage, CfgIrq, CodingRate, Error, HeaderType, HseTrim, Irq, LoRaBandwidth, LoRaModParams,
LoRaPacketParams, LoRaSyncWord, Ocp, PaConfig, PacketType, RegMode, RfFreq, SpreadingFactor as SF, StandbyClk,
Status, SubGhz, TcxoMode, TcxoTrim, Timeout, TxParams,
};
use embassy_stm32::Peripheral;
use embassy_sync::signal::Signal;
use embassy_sync::waitqueue::AtomicWaker;
use lorawan_device::async_device::radio::{Bandwidth, PhyRxTx, RfConfig, RxQuality, SpreadingFactor, TxConfig};
use lorawan_device::async_device::Timings;
@ -28,102 +26,52 @@ pub enum State {
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct RadioError;
static IRQ: Signal<(Status, u16)> = Signal::new();
struct StateInner<'d> {
radio: SubGhz<'d, NoDma, NoDma>,
switch: RadioSwitch<'d>,
}
/// External state storage for the radio state
pub struct SubGhzState<'a>(MaybeUninit<StateInner<'a>>);
impl<'d> SubGhzState<'d> {
pub const fn new() -> Self {
Self(MaybeUninit::uninit())
}
}
static IRQ_WAKER: AtomicWaker = AtomicWaker::new();
/// The radio peripheral keeping the radio state and owning the radio IRQ.
pub struct SubGhzRadio<'d> {
state: *mut StateInner<'d>,
_irq: PeripheralRef<'d, SUBGHZ_RADIO>,
pub struct SubGhzRadio<'d, RS> {
radio: SubGhz<'d, NoDma, NoDma>,
switch: RS,
irq: PeripheralRef<'d, SUBGHZ_RADIO>,
}
impl<'d> SubGhzRadio<'d> {
#[derive(Default)]
#[non_exhaustive]
pub struct SubGhzRadioConfig {
pub reg_mode: RegMode,
pub calibrate_image: CalibrateImage,
pub pa_config: PaConfig,
pub tx_params: TxParams,
}
impl<'d, RS: RadioSwitch> SubGhzRadio<'d, RS> {
/// Create a new instance of a SubGhz radio for LoRaWAN.
///
/// # Safety
/// Do not leak self or futures
pub unsafe fn new(
state: &'d mut SubGhzState<'d>,
radio: SubGhz<'d, NoDma, NoDma>,
switch: RadioSwitch<'d>,
pub fn new(
mut radio: SubGhz<'d, NoDma, NoDma>,
switch: RS,
irq: impl Peripheral<P = SUBGHZ_RADIO> + 'd,
) -> Self {
config: SubGhzRadioConfig,
) -> Result<Self, RadioError> {
into_ref!(irq);
let mut inner = StateInner { radio, switch };
inner.radio.reset();
let state_ptr = state.0.as_mut_ptr();
state_ptr.write(inner);
radio.reset();
irq.disable();
irq.set_handler(|p| {
// This is safe because we only get interrupts when configured for, so
// the radio will be awaiting on the signal at this point. If not, the ISR will
// anyway only adjust the state in the IRQ signal state.
let state = &mut *(p as *mut StateInner<'d>);
state.on_interrupt();
irq.set_handler(|_| {
IRQ_WAKER.wake();
unsafe { SUBGHZ_RADIO::steal().disable() };
});
irq.set_handler_context(state_ptr as *mut ());
irq.enable();
Self {
state: state_ptr,
_irq: irq,
}
}
}
configure_radio(&mut radio, config)?;
impl<'d> StateInner<'d> {
/// Configure radio settings in preparation for TX or RX
pub(crate) fn configure(&mut self) -> Result<(), RadioError> {
trace!("Configuring STM32WL SUBGHZ radio");
self.radio.set_standby(StandbyClk::Rc)?;
let tcxo_mode = TcxoMode::new()
.set_txco_trim(TcxoTrim::Volts1pt7)
.set_timeout(Timeout::from_duration_sat(core::time::Duration::from_millis(40)));
self.radio.set_tcxo_mode(&tcxo_mode)?;
self.radio.set_regulator_mode(RegMode::Ldo)?;
self.radio.calibrate_image(CalibrateImage::ISM_863_870)?;
self.radio.set_buffer_base_address(0, 0)?;
self.radio
.set_pa_config(&PaConfig::new().set_pa_duty_cycle(0x1).set_hp_max(0x0).set_pa(PaSel::Lp))?;
self.radio.set_pa_ocp(Ocp::Max140m)?;
// let tx_params = TxParams::LP_14.set_ramp_time(RampTime::Micros40);
self.radio
.set_tx_params(&TxParams::new().set_ramp_time(RampTime::Micros40).set_power(0x0A))?;
self.radio.set_packet_type(PacketType::LoRa)?;
self.radio.set_lora_sync_word(LoRaSyncWord::Public)?;
trace!("Done initializing STM32WL SUBGHZ radio");
Ok(())
Ok(Self { radio, switch, irq })
}
/// Perform a transmission with the given parameters and payload. Returns any time adjustements needed form
/// the upcoming RX window start.
async fn do_tx(&mut self, config: TxConfig, buf: &[u8]) -> Result<u32, RadioError> {
//trace!("TX Request: {}", config);
trace!("TX START");
self.switch.set_tx_lp();
self.configure()?;
trace!("TX request: {:?}", config);
self.switch.set_tx();
self.radio
.set_rf_frequency(&RfFreq::from_frequency(config.rf.frequency))?;
@ -139,34 +87,26 @@ impl<'d> StateInner<'d> {
self.radio.set_lora_packet_params(&packet_params)?;
let irq_cfg = CfgIrq::new()
.irq_enable_all(Irq::TxDone)
.irq_enable_all(Irq::RxDone)
.irq_enable_all(Irq::Timeout);
let irq_cfg = CfgIrq::new().irq_enable_all(Irq::TxDone).irq_enable_all(Irq::Timeout);
self.radio.set_irq_cfg(&irq_cfg)?;
self.radio.set_buffer_base_address(0, 0)?;
self.radio.write_buffer(0, buf)?;
self.radio.set_tx(Timeout::DISABLED)?;
// The maximum airtime for any LoRaWAN package is 2793.5ms.
// The value of 4000ms is copied from C driver and gives us a good safety margin.
self.radio.set_tx(Timeout::from_millis_sat(4000))?;
trace!("TX started");
loop {
let (_status, irq_status) = IRQ.wait().await;
IRQ.reset();
let (_status, irq_status) = self.irq_wait().await;
if irq_status & Irq::TxDone.mask() != 0 {
let stats = self.radio.lora_stats()?;
let (status, error_mask) = self.radio.op_error()?;
trace!(
"TX done. Stats: {:?}. OP error: {:?}, mask {:?}",
stats,
status,
error_mask
);
trace!("TX done");
return Ok(0);
} else if irq_status & Irq::Timeout.mask() != 0 {
trace!("TX timeout");
}
if irq_status & Irq::Timeout.mask() != 0 {
return Err(RadioError);
}
}
@ -174,10 +114,15 @@ impl<'d> StateInner<'d> {
fn set_lora_mod_params(&mut self, config: RfConfig) -> Result<(), Error> {
let mod_params = LoRaModParams::new()
.set_sf(convert_spreading_factor(config.spreading_factor))
.set_bw(convert_bandwidth(config.bandwidth))
.set_sf(convert_spreading_factor(&config.spreading_factor))
.set_bw(convert_bandwidth(&config.bandwidth))
.set_cr(CodingRate::Cr45)
.set_ldro_en(true);
.set_ldro_en(matches!(
(config.spreading_factor, config.bandwidth),
(SpreadingFactor::_12, Bandwidth::_125KHz)
| (SpreadingFactor::_12, Bandwidth::_250KHz)
| (SpreadingFactor::_11, Bandwidth::_125KHz)
));
self.radio.set_lora_mod_params(&mod_params)
}
@ -185,10 +130,8 @@ impl<'d> StateInner<'d> {
/// be able to hold a single LoRaWAN packet.
async fn do_rx(&mut self, config: RfConfig, buf: &mut [u8]) -> Result<(usize, RxQuality), RadioError> {
assert!(buf.len() >= 255);
trace!("RX START");
// trace!("Starting RX: {}", config);
trace!("RX request: {:?}", config);
self.switch.set_rx();
self.configure()?;
self.radio.set_rf_frequency(&RfFreq::from_frequency(config.frequency))?;
@ -198,77 +141,114 @@ impl<'d> StateInner<'d> {
.set_preamble_len(8)
.set_header_type(HeaderType::Variable)
.set_payload_len(0xFF)
.set_crc_en(true)
.set_crc_en(false)
.set_invert_iq(true);
self.radio.set_lora_packet_params(&packet_params)?;
let irq_cfg = CfgIrq::new()
.irq_enable_all(Irq::RxDone)
.irq_enable_all(Irq::PreambleDetected)
.irq_enable_all(Irq::HeaderValid)
.irq_enable_all(Irq::HeaderErr)
.irq_enable_all(Irq::Timeout)
.irq_enable_all(Irq::Err);
.irq_enable_all(Irq::Err)
.irq_enable_all(Irq::Timeout);
self.radio.set_irq_cfg(&irq_cfg)?;
self.radio.set_buffer_base_address(0, 0)?;
// NOTE: Upper layer handles timeout by cancelling the future
self.radio.set_rx(Timeout::DISABLED)?;
trace!("RX started");
loop {
let (status, irq_status) = IRQ.wait().await;
IRQ.reset();
trace!("RX IRQ {:?}, {:?}", status, irq_status);
if irq_status & Irq::RxDone.mask() != 0 {
let (status, len, ptr) = self.radio.rx_buffer_status()?;
let (_status, irq_status) = self.irq_wait().await;
if irq_status & Irq::RxDone.mask() != 0 {
let (_status, len, ptr) = self.radio.rx_buffer_status()?;
let packet_status = self.radio.lora_packet_status()?;
let rssi = packet_status.rssi_pkt().to_integer();
let snr = packet_status.snr_pkt().to_integer();
trace!(
"RX done. Received {} bytes. RX status: {:?}. Pkt status: {:?}",
len,
status.cmd(),
packet_status,
);
self.radio.read_buffer(ptr, &mut buf[..len as usize])?;
self.radio.set_standby(StandbyClk::Rc)?;
#[cfg(feature = "defmt")]
trace!("RX done: {=[u8]:#02X}", &mut buf[..len as usize]);
#[cfg(feature = "log")]
trace!("RX done: {:02x?}", &mut buf[..len as usize]);
return Ok((len as usize, RxQuality::new(rssi, snr as i8)));
} else if irq_status & (Irq::Timeout.mask() | Irq::TxDone.mask()) != 0 {
}
if irq_status & Irq::Timeout.mask() != 0 {
return Err(RadioError);
}
}
}
/// Read interrupt status and store in global signal
fn on_interrupt(&mut self) {
let (status, irq_status) = self.radio.irq_status().expect("error getting irq status");
self.radio
.clear_irq_status(irq_status)
.expect("error clearing irq status");
if irq_status & Irq::PreambleDetected.mask() != 0 {
trace!("Preamble detected, ignoring");
} else {
IRQ.signal((status, irq_status));
}
async fn irq_wait(&mut self) -> (Status, u16) {
poll_fn(|cx| {
self.irq.unpend();
self.irq.enable();
IRQ_WAKER.register(cx.waker());
let (status, irq_status) = self.radio.irq_status().expect("error getting irq status");
self.radio
.clear_irq_status(irq_status)
.expect("error clearing irq status");
trace!("SUGHZ IRQ 0b{:016b}, {:?}", irq_status, status);
if irq_status == 0 {
Poll::Pending
} else {
Poll::Ready((status, irq_status))
}
})
.await
}
}
impl PhyRxTx for SubGhzRadio<'static> {
fn configure_radio(radio: &mut SubGhz<'_, NoDma, NoDma>, config: SubGhzRadioConfig) -> Result<(), RadioError> {
trace!("Configuring STM32WL SUBGHZ radio");
radio.set_regulator_mode(config.reg_mode)?;
radio.set_standby(StandbyClk::Rc)?;
let tcxo_mode = TcxoMode::new()
.set_txco_trim(TcxoTrim::Volts1pt7)
.set_timeout(Timeout::from_duration_sat(core::time::Duration::from_millis(100)));
radio.set_tcxo_mode(&tcxo_mode)?;
// Reduce input capacitance as shown in Reference Manual "Figure 23. HSE32 TCXO control".
// The STM32CUBE C driver also does this.
radio.set_hse_in_trim(HseTrim::MIN)?;
// Re-calibrate everything after setting the TXCO config.
radio.calibrate(0x7F)?;
radio.calibrate_image(config.calibrate_image)?;
radio.set_pa_config(&config.pa_config)?;
radio.set_tx_params(&config.tx_params)?;
radio.set_pa_ocp(Ocp::Max140m)?;
radio.set_packet_type(PacketType::LoRa)?;
radio.set_lora_sync_word(LoRaSyncWord::Public)?;
trace!("Done initializing STM32WL SUBGHZ radio");
Ok(())
}
impl<'d, RS: RadioSwitch> PhyRxTx for SubGhzRadio<'d, RS> {
type PhyError = RadioError;
type TxFuture<'m> = impl Future<Output = Result<u32, Self::PhyError>> + 'm;
type TxFuture<'m> = impl Future<Output = Result<u32, Self::PhyError>> + 'm where Self: 'm;
fn tx<'m>(&'m mut self, config: TxConfig, buf: &'m [u8]) -> Self::TxFuture<'m> {
async move {
let inner = unsafe { &mut *self.state };
inner.do_tx(config, buf).await
}
async move { self.do_tx(config, buf).await }
}
type RxFuture<'m> = impl Future<Output = Result<(usize, RxQuality), Self::PhyError>> + 'm;
type RxFuture<'m> = impl Future<Output = Result<(usize, RxQuality), Self::PhyError>> + 'm where Self: 'm;
fn rx<'m>(&'m mut self, config: RfConfig, buf: &'m mut [u8]) -> Self::RxFuture<'m> {
async move {
let inner = unsafe { &mut *self.state };
inner.do_rx(config, buf).await
}
async move { self.do_rx(config, buf).await }
}
}
@ -278,48 +258,21 @@ impl From<embassy_stm32::spi::Error> for RadioError {
}
}
impl<'d> Timings for SubGhzRadio<'d> {
impl<'d, RS> Timings for SubGhzRadio<'d, RS> {
fn get_rx_window_offset_ms(&self) -> i32 {
-200
-500
}
fn get_rx_window_duration_ms(&self) -> u32 {
800
3000
}
}
/// Represents the radio switch found on STM32WL based boards, used to control the radio for transmission or reception.
pub struct RadioSwitch<'d> {
ctrl1: Output<'d, AnyPin>,
ctrl2: Output<'d, AnyPin>,
ctrl3: Output<'d, AnyPin>,
pub trait RadioSwitch {
fn set_rx(&mut self);
fn set_tx(&mut self);
}
impl<'d> RadioSwitch<'d> {
pub fn new(ctrl1: Output<'d, AnyPin>, ctrl2: Output<'d, AnyPin>, ctrl3: Output<'d, AnyPin>) -> Self {
Self { ctrl1, ctrl2, ctrl3 }
}
pub(crate) fn set_rx(&mut self) {
self.ctrl1.set_high();
self.ctrl2.set_low();
self.ctrl3.set_high();
}
pub(crate) fn set_tx_lp(&mut self) {
self.ctrl1.set_high();
self.ctrl2.set_high();
self.ctrl3.set_high();
}
#[allow(dead_code)]
pub(crate) fn set_tx_hp(&mut self) {
self.ctrl2.set_high();
self.ctrl1.set_low();
self.ctrl3.set_high();
}
}
fn convert_spreading_factor(sf: SpreadingFactor) -> SF {
fn convert_spreading_factor(sf: &SpreadingFactor) -> SF {
match sf {
SpreadingFactor::_7 => SF::Sf7,
SpreadingFactor::_8 => SF::Sf8,
@ -330,7 +283,7 @@ fn convert_spreading_factor(sf: SpreadingFactor) -> SF {
}
}
fn convert_bandwidth(bw: Bandwidth) -> LoRaBandwidth {
fn convert_bandwidth(bw: &Bandwidth) -> LoRaBandwidth {
match bw {
Bandwidth::_125KHz => LoRaBandwidth::Bw125,
Bandwidth::_250KHz => LoRaBandwidth::Bw250,

View File

@ -0,0 +1,153 @@
use core::future::Future;
use defmt::Format;
use embedded_hal::digital::v2::OutputPin;
use embedded_hal_async::digital::Wait;
use embedded_hal_async::spi::*;
use lorawan_device::async_device::radio::{PhyRxTx, RfConfig, RxQuality, TxConfig};
use lorawan_device::async_device::Timings;
mod sx126x_lora;
use sx126x_lora::LoRa;
use self::sx126x_lora::mod_params::RadioError;
/// Semtech Sx126x LoRa peripheral
pub struct Sx126xRadio<SPI, CTRL, WAIT, BUS>
where
SPI: SpiBus<u8, Error = BUS> + 'static,
CTRL: OutputPin + 'static,
WAIT: Wait + 'static,
BUS: Error + Format + 'static,
{
pub lora: LoRa<SPI, CTRL, WAIT>,
}
impl<SPI, CTRL, WAIT, BUS> Sx126xRadio<SPI, CTRL, WAIT, BUS>
where
SPI: SpiBus<u8, Error = BUS> + 'static,
CTRL: OutputPin + 'static,
WAIT: Wait + 'static,
BUS: Error + Format + 'static,
{
pub async fn new(
spi: SPI,
cs: CTRL,
reset: CTRL,
antenna_rx: CTRL,
antenna_tx: CTRL,
dio1: WAIT,
busy: WAIT,
enable_public_network: bool,
) -> Result<Self, RadioError<BUS>> {
let mut lora = LoRa::new(spi, cs, reset, antenna_rx, antenna_tx, dio1, busy);
lora.init().await?;
lora.set_lora_modem(enable_public_network).await?;
Ok(Self { lora })
}
}
impl<SPI, CTRL, WAIT, BUS> Timings for Sx126xRadio<SPI, CTRL, WAIT, BUS>
where
SPI: SpiBus<u8, Error = BUS> + 'static,
CTRL: OutputPin + 'static,
WAIT: Wait + 'static,
BUS: Error + Format + 'static,
{
fn get_rx_window_offset_ms(&self) -> i32 {
-500
}
fn get_rx_window_duration_ms(&self) -> u32 {
800
}
}
impl<SPI, CTRL, WAIT, BUS> PhyRxTx for Sx126xRadio<SPI, CTRL, WAIT, BUS>
where
SPI: SpiBus<u8, Error = BUS> + 'static,
CTRL: OutputPin + 'static,
WAIT: Wait + 'static,
BUS: Error + Format + 'static,
{
type PhyError = RadioError<BUS>;
type TxFuture<'m> = impl Future<Output = Result<u32, Self::PhyError>> + 'm
where
SPI: 'm,
CTRL: 'm,
WAIT: 'm,
BUS: 'm;
fn tx<'m>(&'m mut self, config: TxConfig, buffer: &'m [u8]) -> Self::TxFuture<'m> {
trace!("TX START");
async move {
self.lora
.set_tx_config(
config.pw,
config.rf.spreading_factor.into(),
config.rf.bandwidth.into(),
config.rf.coding_rate.into(),
8,
false,
true,
false,
0,
false,
)
.await?;
self.lora.set_max_payload_length(buffer.len() as u8).await?;
self.lora.set_channel(config.rf.frequency).await?;
self.lora.send(buffer, 0xffffff).await?;
self.lora.process_irq(None, None, None).await?;
trace!("TX DONE");
return Ok(0);
}
}
type RxFuture<'m> = impl Future<Output = Result<(usize, RxQuality), Self::PhyError>> + 'm
where
SPI: 'm,
CTRL: 'm,
WAIT: 'm,
BUS: 'm;
fn rx<'m>(&'m mut self, config: RfConfig, receiving_buffer: &'m mut [u8]) -> Self::RxFuture<'m> {
trace!("RX START");
async move {
self.lora
.set_rx_config(
config.spreading_factor.into(),
config.bandwidth.into(),
config.coding_rate.into(),
8,
4,
false,
0u8,
true,
false,
0,
true,
true,
)
.await?;
self.lora.set_max_payload_length(receiving_buffer.len() as u8).await?;
self.lora.set_channel(config.frequency).await?;
self.lora.rx(90 * 1000).await?;
let mut received_len = 0u8;
self.lora
.process_irq(Some(receiving_buffer), Some(&mut received_len), None)
.await?;
trace!("RX DONE");
let packet_status = self.lora.get_latest_packet_status();
let mut rssi = 0i16;
let mut snr = 0i8;
if packet_status.is_some() {
rssi = packet_status.unwrap().rssi as i16;
snr = packet_status.unwrap().snr;
}
Ok((received_len as usize, RxQuality::new(rssi, snr)))
}
}
}

View File

@ -0,0 +1,256 @@
use embassy_time::{Duration, Timer};
use embedded_hal::digital::v2::OutputPin;
use embedded_hal_async::digital::Wait;
use embedded_hal_async::spi::SpiBus;
use super::mod_params::RadioError::*;
use super::mod_params::*;
use super::LoRa;
// Defines the time required for the TCXO to wakeup [ms].
const BRD_TCXO_WAKEUP_TIME: u32 = 10;
// Provides board-specific functionality for Semtech SX126x-based boards.
impl<SPI, CTRL, WAIT, BUS> LoRa<SPI, CTRL, WAIT>
where
SPI: SpiBus<u8, Error = BUS>,
CTRL: OutputPin,
WAIT: Wait,
{
// De-initialize the radio I/Os pins interface. Useful when going into MCU low power modes.
pub(super) async fn brd_io_deinit(&mut self) -> Result<(), RadioError<BUS>> {
Ok(()) // no operation currently
}
// Initialize the TCXO power pin
pub(super) async fn brd_io_tcxo_init(&mut self) -> Result<(), RadioError<BUS>> {
let timeout = self.brd_get_board_tcxo_wakeup_time() << 6;
self.sub_set_dio3_as_tcxo_ctrl(TcxoCtrlVoltage::Ctrl1V7, timeout)
.await?;
Ok(())
}
// Initialize RF switch control pins
pub(super) async fn brd_io_rf_switch_init(&mut self) -> Result<(), RadioError<BUS>> {
self.sub_set_dio2_as_rf_switch_ctrl(true).await?;
Ok(())
}
// Initialize the radio debug pins
pub(super) async fn brd_io_dbg_init(&mut self) -> Result<(), RadioError<BUS>> {
Ok(()) // no operation currently
}
// Hardware reset of the radio
pub(super) async fn brd_reset(&mut self) -> Result<(), RadioError<BUS>> {
Timer::after(Duration::from_millis(10)).await;
self.reset.set_low().map_err(|_| Reset)?;
Timer::after(Duration::from_millis(20)).await;
self.reset.set_high().map_err(|_| Reset)?;
Timer::after(Duration::from_millis(10)).await;
Ok(())
}
// Wait while the busy pin is high
pub(super) async fn brd_wait_on_busy(&mut self) -> Result<(), RadioError<BUS>> {
self.busy.wait_for_low().await.map_err(|_| Busy)?;
Ok(())
}
// Wake up the radio
pub(super) async fn brd_wakeup(&mut self) -> Result<(), RadioError<BUS>> {
self.cs.set_low().map_err(|_| CS)?;
self.spi.write(&[OpCode::GetStatus.value()]).await.map_err(SPI)?;
self.spi.write(&[0x00]).await.map_err(SPI)?;
self.cs.set_high().map_err(|_| CS)?;
self.brd_wait_on_busy().await?;
self.brd_set_operating_mode(RadioMode::StandbyRC);
Ok(())
}
// Send a command that writes data to the radio
pub(super) async fn brd_write_command(&mut self, op_code: OpCode, buffer: &[u8]) -> Result<(), RadioError<BUS>> {
self.sub_check_device_ready().await?;
self.cs.set_low().map_err(|_| CS)?;
self.spi.write(&[op_code.value()]).await.map_err(SPI)?;
self.spi.write(buffer).await.map_err(SPI)?;
self.cs.set_high().map_err(|_| CS)?;
if op_code != OpCode::SetSleep {
self.brd_wait_on_busy().await?;
}
Ok(())
}
// Send a command that reads data from the radio, filling the provided buffer and returning a status
pub(super) async fn brd_read_command(&mut self, op_code: OpCode, buffer: &mut [u8]) -> Result<u8, RadioError<BUS>> {
let mut status = [0u8];
let mut input = [0u8];
self.sub_check_device_ready().await?;
self.cs.set_low().map_err(|_| CS)?;
self.spi.write(&[op_code.value()]).await.map_err(SPI)?;
self.spi.transfer(&mut status, &[0x00]).await.map_err(SPI)?;
for i in 0..buffer.len() {
self.spi.transfer(&mut input, &[0x00]).await.map_err(SPI)?;
buffer[i] = input[0];
}
self.cs.set_high().map_err(|_| CS)?;
self.brd_wait_on_busy().await?;
Ok(status[0])
}
// Write one or more bytes of data to the radio memory
pub(super) async fn brd_write_registers(
&mut self,
start_register: Register,
buffer: &[u8],
) -> Result<(), RadioError<BUS>> {
self.sub_check_device_ready().await?;
self.cs.set_low().map_err(|_| CS)?;
self.spi.write(&[OpCode::WriteRegister.value()]).await.map_err(SPI)?;
self.spi
.write(&[
((start_register.addr() & 0xFF00) >> 8) as u8,
(start_register.addr() & 0x00FF) as u8,
])
.await
.map_err(SPI)?;
self.spi.write(buffer).await.map_err(SPI)?;
self.cs.set_high().map_err(|_| CS)?;
self.brd_wait_on_busy().await?;
Ok(())
}
// Read one or more bytes of data from the radio memory
pub(super) async fn brd_read_registers(
&mut self,
start_register: Register,
buffer: &mut [u8],
) -> Result<(), RadioError<BUS>> {
let mut input = [0u8];
self.sub_check_device_ready().await?;
self.cs.set_low().map_err(|_| CS)?;
self.spi.write(&[OpCode::ReadRegister.value()]).await.map_err(SPI)?;
self.spi
.write(&[
((start_register.addr() & 0xFF00) >> 8) as u8,
(start_register.addr() & 0x00FF) as u8,
0x00u8,
])
.await
.map_err(SPI)?;
for i in 0..buffer.len() {
self.spi.transfer(&mut input, &[0x00]).await.map_err(SPI)?;
buffer[i] = input[0];
}
self.cs.set_high().map_err(|_| CS)?;
self.brd_wait_on_busy().await?;
Ok(())
}
// Write data to the buffer holding the payload in the radio
pub(super) async fn brd_write_buffer(&mut self, offset: u8, buffer: &[u8]) -> Result<(), RadioError<BUS>> {
self.sub_check_device_ready().await?;
self.cs.set_low().map_err(|_| CS)?;
self.spi.write(&[OpCode::WriteBuffer.value()]).await.map_err(SPI)?;
self.spi.write(&[offset]).await.map_err(SPI)?;
self.spi.write(buffer).await.map_err(SPI)?;
self.cs.set_high().map_err(|_| CS)?;
self.brd_wait_on_busy().await?;
Ok(())
}
// Read data from the buffer holding the payload in the radio
pub(super) async fn brd_read_buffer(&mut self, offset: u8, buffer: &mut [u8]) -> Result<(), RadioError<BUS>> {
let mut input = [0u8];
self.sub_check_device_ready().await?;
self.cs.set_low().map_err(|_| CS)?;
self.spi.write(&[OpCode::ReadBuffer.value()]).await.map_err(SPI)?;
self.spi.write(&[offset]).await.map_err(SPI)?;
self.spi.write(&[0x00]).await.map_err(SPI)?;
for i in 0..buffer.len() {
self.spi.transfer(&mut input, &[0x00]).await.map_err(SPI)?;
buffer[i] = input[0];
}
self.cs.set_high().map_err(|_| CS)?;
self.brd_wait_on_busy().await?;
Ok(())
}
// Set the radio output power
pub(super) async fn brd_set_rf_tx_power(&mut self, power: i8) -> Result<(), RadioError<BUS>> {
self.sub_set_tx_params(power, RampTime::Ramp40Us).await?;
Ok(())
}
// Get the radio type
pub(super) fn brd_get_radio_type(&mut self) -> RadioType {
RadioType::SX1262
}
// Quiesce the antenna(s).
pub(super) fn brd_ant_sleep(&mut self) -> Result<(), RadioError<BUS>> {
self.antenna_tx.set_low().map_err(|_| AntTx)?;
self.antenna_rx.set_low().map_err(|_| AntRx)?;
Ok(())
}
// Prepare the antenna(s) for a receive operation
pub(super) fn brd_ant_set_rx(&mut self) -> Result<(), RadioError<BUS>> {
self.antenna_tx.set_low().map_err(|_| AntTx)?;
self.antenna_rx.set_high().map_err(|_| AntRx)?;
Ok(())
}
// Prepare the antenna(s) for a send operation
pub(super) fn brd_ant_set_tx(&mut self) -> Result<(), RadioError<BUS>> {
self.antenna_rx.set_low().map_err(|_| AntRx)?;
self.antenna_tx.set_high().map_err(|_| AntTx)?;
Ok(())
}
// Check if the given RF frequency is supported by the hardware
pub(super) async fn brd_check_rf_frequency(&mut self, _frequency: u32) -> Result<bool, RadioError<BUS>> {
Ok(true)
}
// Get the duration required for the TCXO to wakeup [ms].
pub(super) fn brd_get_board_tcxo_wakeup_time(&mut self) -> u32 {
BRD_TCXO_WAKEUP_TIME
}
/* Get current state of the DIO1 pin - not currently needed if waiting on DIO1 instead of using an IRQ process
pub(super) async fn brd_get_dio1_pin_state(
&mut self,
) -> Result<u32, RadioError<BUS>> {
Ok(0)
}
*/
// Get the current radio operatiing mode
pub(super) fn brd_get_operating_mode(&mut self) -> RadioMode {
self.operating_mode
}
// Set/Update the current radio operating mode This function is only required to reflect the current radio operating mode when processing interrupts.
pub(super) fn brd_set_operating_mode(&mut self, mode: RadioMode) {
self.operating_mode = mode;
}
}

View File

@ -0,0 +1,732 @@
#![allow(dead_code)]
use embassy_time::{Duration, Timer};
use embedded_hal::digital::v2::OutputPin;
use embedded_hal_async::digital::Wait;
use embedded_hal_async::spi::SpiBus;
mod board_specific;
pub mod mod_params;
mod subroutine;
use mod_params::RadioError::*;
use mod_params::*;
// Syncwords for public and private networks
const LORA_MAC_PUBLIC_SYNCWORD: u16 = 0x3444;
const LORA_MAC_PRIVATE_SYNCWORD: u16 = 0x1424;
// Maximum number of registers that can be added to the retention list
const MAX_NUMBER_REGS_IN_RETENTION: u8 = 4;
// Possible LoRa bandwidths
const LORA_BANDWIDTHS: [Bandwidth; 3] = [Bandwidth::_125KHz, Bandwidth::_250KHz, Bandwidth::_500KHz];
// Radio complete wakeup time with margin for temperature compensation [ms]
const RADIO_WAKEUP_TIME: u32 = 3;
/// Provides high-level access to Semtech SX126x-based boards
pub struct LoRa<SPI, CTRL, WAIT> {
spi: SPI,
cs: CTRL,
reset: CTRL,
antenna_rx: CTRL,
antenna_tx: CTRL,
dio1: WAIT,
busy: WAIT,
operating_mode: RadioMode,
rx_continuous: bool,
max_payload_length: u8,
modulation_params: Option<ModulationParams>,
packet_type: PacketType,
packet_params: Option<PacketParams>,
packet_status: Option<PacketStatus>,
image_calibrated: bool,
frequency_error: u32,
}
impl<SPI, CTRL, WAIT, BUS> LoRa<SPI, CTRL, WAIT>
where
SPI: SpiBus<u8, Error = BUS>,
CTRL: OutputPin,
WAIT: Wait,
{
/// Builds and returns a new instance of the radio. Only one instance of the radio should exist at a time ()
pub fn new(spi: SPI, cs: CTRL, reset: CTRL, antenna_rx: CTRL, antenna_tx: CTRL, dio1: WAIT, busy: WAIT) -> Self {
Self {
spi,
cs,
reset,
antenna_rx,
antenna_tx,
dio1,
busy,
operating_mode: RadioMode::Sleep,
rx_continuous: false,
max_payload_length: 0xFFu8,
modulation_params: None,
packet_type: PacketType::LoRa,
packet_params: None,
packet_status: None,
image_calibrated: false,
frequency_error: 0u32, // where is volatile FrequencyError modified ???
}
}
/// Initialize the radio
pub async fn init(&mut self) -> Result<(), RadioError<BUS>> {
self.sub_init().await?;
self.sub_set_standby(StandbyMode::RC).await?;
self.sub_set_regulator_mode(RegulatorMode::UseDCDC).await?;
self.sub_set_buffer_base_address(0x00u8, 0x00u8).await?;
self.sub_set_tx_params(0i8, RampTime::Ramp200Us).await?;
self.sub_set_dio_irq_params(
IrqMask::All.value(),
IrqMask::All.value(),
IrqMask::None.value(),
IrqMask::None.value(),
)
.await?;
self.add_register_to_retention_list(Register::RxGain.addr()).await?;
self.add_register_to_retention_list(Register::TxModulation.addr())
.await?;
Ok(())
}
/// Return current radio state
pub fn get_status(&mut self) -> RadioState {
match self.brd_get_operating_mode() {
RadioMode::Transmit => RadioState::TxRunning,
RadioMode::Receive => RadioState::RxRunning,
RadioMode::ChannelActivityDetection => RadioState::ChannelActivityDetecting,
_ => RadioState::Idle,
}
}
/// Configure the radio for LoRa (FSK support should be provided in a separate driver, if desired)
pub async fn set_lora_modem(&mut self, enable_public_network: bool) -> Result<(), RadioError<BUS>> {
self.sub_set_packet_type(PacketType::LoRa).await?;
if enable_public_network {
self.brd_write_registers(
Register::LoRaSyncword,
&[
((LORA_MAC_PUBLIC_SYNCWORD >> 8) & 0xFF) as u8,
(LORA_MAC_PUBLIC_SYNCWORD & 0xFF) as u8,
],
)
.await?;
} else {
self.brd_write_registers(
Register::LoRaSyncword,
&[
((LORA_MAC_PRIVATE_SYNCWORD >> 8) & 0xFF) as u8,
(LORA_MAC_PRIVATE_SYNCWORD & 0xFF) as u8,
],
)
.await?;
}
Ok(())
}
/// Sets the channel frequency
pub async fn set_channel(&mut self, frequency: u32) -> Result<(), RadioError<BUS>> {
self.sub_set_rf_frequency(frequency).await?;
Ok(())
}
/* Checks if the channel is free for the given time. This is currently not implemented until a substitute
for switching to the FSK modem is found.
pub async fn is_channel_free(&mut self, frequency: u32, rxBandwidth: u32, rssiThresh: i16, maxCarrierSenseTime: u32) -> bool;
*/
/// Generate a 32 bit random value based on the RSSI readings, after disabling all interrupts. Ensure set_lora_modem() is called befrorehand.
/// After calling this function either set_rx_config() or set_tx_config() must be called.
pub async fn get_random_value(&mut self) -> Result<u32, RadioError<BUS>> {
self.sub_set_dio_irq_params(
IrqMask::None.value(),
IrqMask::None.value(),
IrqMask::None.value(),
IrqMask::None.value(),
)
.await?;
let result = self.sub_get_random().await?;
Ok(result)
}
/// Set the reception parameters for the LoRa modem (only). Ensure set_lora_modem() is called befrorehand.
/// spreading_factor [6: 64, 7: 128, 8: 256, 9: 512, 10: 1024, 11: 2048, 12: 4096 chips/symbol]
/// bandwidth [0: 125 kHz, 1: 250 kHz, 2: 500 kHz, 3: Reserved]
/// coding_rate [1: 4/5, 2: 4/6, 3: 4/7, 4: 4/8]
/// preamble_length length in symbols (the hardware adds 4 more symbols)
/// symb_timeout RxSingle timeout value in symbols
/// fixed_len fixed length packets [0: variable, 1: fixed]
/// payload_len payload length when fixed length is used
/// crc_on [0: OFF, 1: ON]
/// freq_hop_on intra-packet frequency hopping [0: OFF, 1: ON]
/// hop_period number of symbols between each hop
/// iq_inverted invert IQ signals [0: not inverted, 1: inverted]
/// rx_continuous reception mode [false: single mode, true: continuous mode]
pub async fn set_rx_config(
&mut self,
spreading_factor: SpreadingFactor,
bandwidth: Bandwidth,
coding_rate: CodingRate,
preamble_length: u16,
symb_timeout: u16,
fixed_len: bool,
payload_len: u8,
crc_on: bool,
_freq_hop_on: bool,
_hop_period: u8,
iq_inverted: bool,
rx_continuous: bool,
) -> Result<(), RadioError<BUS>> {
let mut symb_timeout_final = symb_timeout;
self.rx_continuous = rx_continuous;
if self.rx_continuous {
symb_timeout_final = 0;
}
if fixed_len {
self.max_payload_length = payload_len;
} else {
self.max_payload_length = 0xFFu8;
}
self.sub_set_stop_rx_timer_on_preamble_detect(false).await?;
let mut low_data_rate_optimize = 0x00u8;
if (((spreading_factor == SpreadingFactor::_11) || (spreading_factor == SpreadingFactor::_12))
&& (bandwidth == Bandwidth::_125KHz))
|| ((spreading_factor == SpreadingFactor::_12) && (bandwidth == Bandwidth::_250KHz))
{
low_data_rate_optimize = 0x01u8;
}
let modulation_params = ModulationParams {
spreading_factor: spreading_factor,
bandwidth: bandwidth,
coding_rate: coding_rate,
low_data_rate_optimize: low_data_rate_optimize,
};
let mut preamble_length_final = preamble_length;
if ((spreading_factor == SpreadingFactor::_5) || (spreading_factor == SpreadingFactor::_6))
&& (preamble_length < 12)
{
preamble_length_final = 12;
}
let packet_params = PacketParams {
preamble_length: preamble_length_final,
implicit_header: fixed_len,
payload_length: self.max_payload_length,
crc_on: crc_on,
iq_inverted: iq_inverted,
};
self.modulation_params = Some(modulation_params);
self.packet_params = Some(packet_params);
self.standby().await?;
self.sub_set_modulation_params().await?;
self.sub_set_packet_params().await?;
self.sub_set_lora_symb_num_timeout(symb_timeout_final).await?;
// Optimize the Inverted IQ Operation (see DS_SX1261-2_V1.2 datasheet chapter 15.4)
let mut iq_polarity = [0x00u8];
self.brd_read_registers(Register::IQPolarity, &mut iq_polarity).await?;
if iq_inverted {
self.brd_write_registers(Register::IQPolarity, &[iq_polarity[0] & (!(1 << 2))])
.await?;
} else {
self.brd_write_registers(Register::IQPolarity, &[iq_polarity[0] | (1 << 2)])
.await?;
}
Ok(())
}
/// Set the transmission parameters for the LoRa modem (only).
/// power output power [dBm]
/// spreading_factor [6: 64, 7: 128, 8: 256, 9: 512, 10: 1024, 11: 2048, 12: 4096 chips/symbol]
/// bandwidth [0: 125 kHz, 1: 250 kHz, 2: 500 kHz, 3: Reserved]
/// coding_rate [1: 4/5, 2: 4/6, 3: 4/7, 4: 4/8]
/// preamble_length length in symbols (the hardware adds 4 more symbols)
/// fixed_len fixed length packets [0: variable, 1: fixed]
/// crc_on [0: OFF, 1: ON]
/// freq_hop_on intra-packet frequency hopping [0: OFF, 1: ON]
/// hop_period number of symbols between each hop
/// iq_inverted invert IQ signals [0: not inverted, 1: inverted]
pub async fn set_tx_config(
&mut self,
power: i8,
spreading_factor: SpreadingFactor,
bandwidth: Bandwidth,
coding_rate: CodingRate,
preamble_length: u16,
fixed_len: bool,
crc_on: bool,
_freq_hop_on: bool,
_hop_period: u8,
iq_inverted: bool,
) -> Result<(), RadioError<BUS>> {
let mut low_data_rate_optimize = 0x00u8;
if (((spreading_factor == SpreadingFactor::_11) || (spreading_factor == SpreadingFactor::_12))
&& (bandwidth == Bandwidth::_125KHz))
|| ((spreading_factor == SpreadingFactor::_12) && (bandwidth == Bandwidth::_250KHz))
{
low_data_rate_optimize = 0x01u8;
}
let modulation_params = ModulationParams {
spreading_factor: spreading_factor,
bandwidth: bandwidth,
coding_rate: coding_rate,
low_data_rate_optimize: low_data_rate_optimize,
};
let mut preamble_length_final = preamble_length;
if ((spreading_factor == SpreadingFactor::_5) || (spreading_factor == SpreadingFactor::_6))
&& (preamble_length < 12)
{
preamble_length_final = 12;
}
let packet_params = PacketParams {
preamble_length: preamble_length_final,
implicit_header: fixed_len,
payload_length: self.max_payload_length,
crc_on: crc_on,
iq_inverted: iq_inverted,
};
self.modulation_params = Some(modulation_params);
self.packet_params = Some(packet_params);
self.standby().await?;
self.sub_set_modulation_params().await?;
self.sub_set_packet_params().await?;
// Handle modulation quality with the 500 kHz LoRa bandwidth (see DS_SX1261-2_V1.2 datasheet chapter 15.1)
let mut tx_modulation = [0x00u8];
self.brd_read_registers(Register::TxModulation, &mut tx_modulation)
.await?;
if bandwidth == Bandwidth::_500KHz {
self.brd_write_registers(Register::TxModulation, &[tx_modulation[0] & (!(1 << 2))])
.await?;
} else {
self.brd_write_registers(Register::TxModulation, &[tx_modulation[0] | (1 << 2)])
.await?;
}
self.brd_set_rf_tx_power(power).await?;
Ok(())
}
/// Check if the given RF frequency is supported by the hardware [true: supported, false: unsupported]
pub async fn check_rf_frequency(&mut self, frequency: u32) -> Result<bool, RadioError<BUS>> {
Ok(self.brd_check_rf_frequency(frequency).await?)
}
/// Computes the packet time on air in ms for the given payload for a LoRa modem (can only be called once set_rx_config or set_tx_config have been called)
/// spreading_factor [6: 64, 7: 128, 8: 256, 9: 512, 10: 1024, 11: 2048, 12: 4096 chips/symbol]
/// bandwidth [0: 125 kHz, 1: 250 kHz, 2: 500 kHz, 3: Reserved]
/// coding_rate [1: 4/5, 2: 4/6, 3: 4/7, 4: 4/8]
/// preamble_length length in symbols (the hardware adds 4 more symbols)
/// fixed_len fixed length packets [0: variable, 1: fixed]
/// payload_len sets payload length when fixed length is used
/// crc_on [0: OFF, 1: ON]
pub fn get_time_on_air(
&mut self,
spreading_factor: SpreadingFactor,
bandwidth: Bandwidth,
coding_rate: CodingRate,
preamble_length: u16,
fixed_len: bool,
payload_len: u8,
crc_on: bool,
) -> Result<u32, RadioError<BUS>> {
let numerator = 1000
* Self::get_lora_time_on_air_numerator(
spreading_factor,
bandwidth,
coding_rate,
preamble_length,
fixed_len,
payload_len,
crc_on,
);
let denominator = bandwidth.value_in_hz();
if denominator == 0 {
Err(RadioError::InvalidBandwidth)
} else {
Ok((numerator + denominator - 1) / denominator)
}
}
/// Send the buffer of the given size. Prepares the packet to be sent and sets the radio in transmission [timeout in ms]
pub async fn send(&mut self, buffer: &[u8], timeout: u32) -> Result<(), RadioError<BUS>> {
if self.packet_params.is_some() {
self.sub_set_dio_irq_params(
IrqMask::TxDone.value() | IrqMask::RxTxTimeout.value(),
IrqMask::TxDone.value() | IrqMask::RxTxTimeout.value(),
IrqMask::None.value(),
IrqMask::None.value(),
)
.await?;
let mut packet_params = self.packet_params.as_mut().unwrap();
packet_params.payload_length = buffer.len() as u8;
self.sub_set_packet_params().await?;
self.sub_send_payload(buffer, timeout).await?;
Ok(())
} else {
Err(RadioError::PacketParamsMissing)
}
}
/// Set the radio in sleep mode
pub async fn sleep(&mut self) -> Result<(), RadioError<BUS>> {
self.sub_set_sleep(SleepParams {
wakeup_rtc: false,
reset: false,
warm_start: true,
})
.await?;
Timer::after(Duration::from_millis(2)).await;
Ok(())
}
/// Set the radio in standby mode
pub async fn standby(&mut self) -> Result<(), RadioError<BUS>> {
self.sub_set_standby(StandbyMode::RC).await?;
Ok(())
}
/// Set the radio in reception mode for the given duration [0: continuous, others: timeout (ms)]
pub async fn rx(&mut self, timeout: u32) -> Result<(), RadioError<BUS>> {
self.sub_set_dio_irq_params(
IrqMask::All.value(),
IrqMask::All.value(),
IrqMask::None.value(),
IrqMask::None.value(),
)
.await?;
if self.rx_continuous {
self.sub_set_rx(0xFFFFFF).await?;
} else {
self.sub_set_rx(timeout << 6).await?;
}
Ok(())
}
/// Start a Channel Activity Detection
pub async fn start_cad(&mut self) -> Result<(), RadioError<BUS>> {
self.sub_set_dio_irq_params(
IrqMask::CADDone.value() | IrqMask::CADActivityDetected.value(),
IrqMask::CADDone.value() | IrqMask::CADActivityDetected.value(),
IrqMask::None.value(),
IrqMask::None.value(),
)
.await?;
self.sub_set_cad().await?;
Ok(())
}
/// Sets the radio in continuous wave transmission mode
/// frequency channel RF frequency
/// power output power [dBm]
/// timeout transmission mode timeout [s]
pub async fn set_tx_continuous_wave(
&mut self,
frequency: u32,
power: i8,
_timeout: u16,
) -> Result<(), RadioError<BUS>> {
self.sub_set_rf_frequency(frequency).await?;
self.brd_set_rf_tx_power(power).await?;
self.sub_set_tx_continuous_wave().await?;
Ok(())
}
/// Read the current RSSI value for the LoRa modem (only) [dBm]
pub async fn get_rssi(&mut self) -> Result<i16, RadioError<BUS>> {
let value = self.sub_get_rssi_inst().await?;
Ok(value as i16)
}
/// Write one or more radio registers with a buffer of a given size, starting at the first register address
pub async fn write_registers_from_buffer(
&mut self,
start_register: Register,
buffer: &[u8],
) -> Result<(), RadioError<BUS>> {
self.brd_write_registers(start_register, buffer).await?;
Ok(())
}
/// Read one or more radio registers into a buffer of a given size, starting at the first register address
pub async fn read_registers_into_buffer(
&mut self,
start_register: Register,
buffer: &mut [u8],
) -> Result<(), RadioError<BUS>> {
self.brd_read_registers(start_register, buffer).await?;
Ok(())
}
/// Set the maximum payload length (in bytes) for a LoRa modem (only).
pub async fn set_max_payload_length(&mut self, max: u8) -> Result<(), RadioError<BUS>> {
if self.packet_params.is_some() {
let packet_params = self.packet_params.as_mut().unwrap();
self.max_payload_length = max;
packet_params.payload_length = max;
self.sub_set_packet_params().await?;
Ok(())
} else {
Err(RadioError::PacketParamsMissing)
}
}
/// Get the time required for the board plus radio to get out of sleep [ms]
pub fn get_wakeup_time(&mut self) -> u32 {
self.brd_get_board_tcxo_wakeup_time() + RADIO_WAKEUP_TIME
}
/// Process the radio irq
pub async fn process_irq(
&mut self,
receiving_buffer: Option<&mut [u8]>,
received_len: Option<&mut u8>,
cad_activity_detected: Option<&mut bool>,
) -> Result<(), RadioError<BUS>> {
loop {
trace!("process_irq loop entered");
let de = self.sub_get_device_errors().await?;
trace!("device_errors: rc_64khz_calibration = {}, rc_13mhz_calibration = {}, pll_calibration = {}, adc_calibration = {}, image_calibration = {}, xosc_start = {}, pll_lock = {}, pa_ramp = {}",
de.rc_64khz_calibration, de.rc_13mhz_calibration, de.pll_calibration, de.adc_calibration, de.image_calibration, de.xosc_start, de.pll_lock, de.pa_ramp);
let st = self.sub_get_status().await?;
trace!(
"radio status: cmd_status: {:x}, chip_mode: {:x}",
st.cmd_status,
st.chip_mode
);
self.dio1.wait_for_high().await.map_err(|_| DIO1)?;
let operating_mode = self.brd_get_operating_mode();
let irq_flags = self.sub_get_irq_status().await?;
self.sub_clear_irq_status(irq_flags).await?;
trace!("process_irq DIO1 satisfied: irq_flags = {:x}", irq_flags);
// check for errors and unexpected interrupt masks (based on operation mode)
if (irq_flags & IrqMask::HeaderError.value()) == IrqMask::HeaderError.value() {
if !self.rx_continuous {
self.brd_set_operating_mode(RadioMode::StandbyRC);
}
return Err(RadioError::HeaderError);
} else if (irq_flags & IrqMask::CRCError.value()) == IrqMask::CRCError.value() {
if operating_mode == RadioMode::Receive {
if !self.rx_continuous {
self.brd_set_operating_mode(RadioMode::StandbyRC);
}
return Err(RadioError::CRCErrorOnReceive);
} else {
return Err(RadioError::CRCErrorUnexpected);
}
} else if (irq_flags & IrqMask::RxTxTimeout.value()) == IrqMask::RxTxTimeout.value() {
if operating_mode == RadioMode::Transmit {
self.brd_set_operating_mode(RadioMode::StandbyRC);
return Err(RadioError::TransmitTimeout);
} else if operating_mode == RadioMode::Receive {
self.brd_set_operating_mode(RadioMode::StandbyRC);
return Err(RadioError::ReceiveTimeout);
} else {
return Err(RadioError::TimeoutUnexpected);
}
} else if ((irq_flags & IrqMask::TxDone.value()) == IrqMask::TxDone.value())
&& (operating_mode != RadioMode::Transmit)
{
return Err(RadioError::TransmitDoneUnexpected);
} else if ((irq_flags & IrqMask::RxDone.value()) == IrqMask::RxDone.value())
&& (operating_mode != RadioMode::Receive)
{
return Err(RadioError::ReceiveDoneUnexpected);
} else if (((irq_flags & IrqMask::CADActivityDetected.value()) == IrqMask::CADActivityDetected.value())
|| ((irq_flags & IrqMask::CADDone.value()) == IrqMask::CADDone.value()))
&& (operating_mode != RadioMode::ChannelActivityDetection)
{
return Err(RadioError::CADUnexpected);
}
if (irq_flags & IrqMask::HeaderValid.value()) == IrqMask::HeaderValid.value() {
trace!("HeaderValid");
} else if (irq_flags & IrqMask::PreambleDetected.value()) == IrqMask::PreambleDetected.value() {
trace!("PreambleDetected");
} else if (irq_flags & IrqMask::SyncwordValid.value()) == IrqMask::SyncwordValid.value() {
trace!("SyncwordValid");
}
// handle completions
if (irq_flags & IrqMask::TxDone.value()) == IrqMask::TxDone.value() {
self.brd_set_operating_mode(RadioMode::StandbyRC);
return Ok(());
} else if (irq_flags & IrqMask::RxDone.value()) == IrqMask::RxDone.value() {
if !self.rx_continuous {
self.brd_set_operating_mode(RadioMode::StandbyRC);
// implicit header mode timeout behavior (see DS_SX1261-2_V1.2 datasheet chapter 15.3)
self.brd_write_registers(Register::RTCCtrl, &[0x00]).await?;
let mut evt_clr = [0x00u8];
self.brd_read_registers(Register::EvtClr, &mut evt_clr).await?;
evt_clr[0] |= 1 << 1;
self.brd_write_registers(Register::EvtClr, &evt_clr).await?;
}
if receiving_buffer.is_some() && received_len.is_some() {
*(received_len.unwrap()) = self.sub_get_payload(receiving_buffer.unwrap()).await?;
}
self.packet_status = self.sub_get_packet_status().await?.into();
return Ok(());
} else if (irq_flags & IrqMask::CADDone.value()) == IrqMask::CADDone.value() {
if cad_activity_detected.is_some() {
*(cad_activity_detected.unwrap()) =
(irq_flags & IrqMask::CADActivityDetected.value()) == IrqMask::CADActivityDetected.value();
}
self.brd_set_operating_mode(RadioMode::StandbyRC);
return Ok(());
}
// if DIO1 was driven high for reasons other than an error or operation completion (currently, PreambleDetected, SyncwordValid, and HeaderValid
// are in that category), loop to wait again
}
}
// SX126x-specific functions
/// Set the radio in reception mode with Max LNA gain for the given time (SX126x radios only) [0: continuous, others timeout in ms]
pub async fn set_rx_boosted(&mut self, timeout: u32) -> Result<(), RadioError<BUS>> {
self.sub_set_dio_irq_params(
IrqMask::All.value(),
IrqMask::All.value(),
IrqMask::None.value(),
IrqMask::None.value(),
)
.await?;
if self.rx_continuous {
self.sub_set_rx_boosted(0xFFFFFF).await?; // Rx continuous
} else {
self.sub_set_rx_boosted(timeout << 6).await?;
}
Ok(())
}
/// Set the Rx duty cycle management parameters (SX126x radios only)
/// rx_time structure describing reception timeout value
/// sleep_time structure describing sleep timeout value
pub async fn set_rx_duty_cycle(&mut self, rx_time: u32, sleep_time: u32) -> Result<(), RadioError<BUS>> {
self.sub_set_rx_duty_cycle(rx_time, sleep_time).await?;
Ok(())
}
pub fn get_latest_packet_status(&mut self) -> Option<PacketStatus> {
self.packet_status
}
// Utilities
async fn add_register_to_retention_list(&mut self, register_address: u16) -> Result<(), RadioError<BUS>> {
let mut buffer = [0x00u8; (1 + (2 * MAX_NUMBER_REGS_IN_RETENTION)) as usize];
// Read the address and registers already added to the list
self.brd_read_registers(Register::RetentionList, &mut buffer).await?;
let number_of_registers = buffer[0];
for i in 0..number_of_registers {
if register_address
== ((buffer[(1 + (2 * i)) as usize] as u16) << 8) | (buffer[(2 + (2 * i)) as usize] as u16)
{
return Ok(()); // register already in list
}
}
if number_of_registers < MAX_NUMBER_REGS_IN_RETENTION {
buffer[0] += 1; // increment number of registers
buffer[(1 + (2 * number_of_registers)) as usize] = ((register_address >> 8) & 0xFF) as u8;
buffer[(2 + (2 * number_of_registers)) as usize] = (register_address & 0xFF) as u8;
self.brd_write_registers(Register::RetentionList, &buffer).await?;
Ok(())
} else {
Err(RadioError::RetentionListExceeded)
}
}
fn get_lora_time_on_air_numerator(
spreading_factor: SpreadingFactor,
bandwidth: Bandwidth,
coding_rate: CodingRate,
preamble_length: u16,
fixed_len: bool,
payload_len: u8,
crc_on: bool,
) -> u32 {
let cell_denominator;
let cr_denominator = (coding_rate.value() as i32) + 4;
// Ensure that the preamble length is at least 12 symbols when using SF5 or SF6
let mut preamble_length_final = preamble_length;
if ((spreading_factor == SpreadingFactor::_5) || (spreading_factor == SpreadingFactor::_6))
&& (preamble_length < 12)
{
preamble_length_final = 12;
}
let mut low_data_rate_optimize = false;
if (((spreading_factor == SpreadingFactor::_11) || (spreading_factor == SpreadingFactor::_12))
&& (bandwidth == Bandwidth::_125KHz))
|| ((spreading_factor == SpreadingFactor::_12) && (bandwidth == Bandwidth::_250KHz))
{
low_data_rate_optimize = true;
}
let mut cell_numerator = ((payload_len as i32) << 3) + (if crc_on { 16 } else { 0 })
- (4 * spreading_factor.value() as i32)
+ (if fixed_len { 0 } else { 20 });
if spreading_factor.value() <= 6 {
cell_denominator = 4 * (spreading_factor.value() as i32);
} else {
cell_numerator += 8;
if low_data_rate_optimize {
cell_denominator = 4 * ((spreading_factor.value() as i32) - 2);
} else {
cell_denominator = 4 * (spreading_factor.value() as i32);
}
}
if cell_numerator < 0 {
cell_numerator = 0;
}
let mut intermediate: i32 = (((cell_numerator + cell_denominator - 1) / cell_denominator) * cr_denominator)
+ (preamble_length_final as i32)
+ 12;
if spreading_factor.value() <= 6 {
intermediate = intermediate + 2;
}
(((4 * intermediate) + 1) * (1 << (spreading_factor.value() - 2))) as u32
}
}

View File

@ -0,0 +1,469 @@
use core::fmt::Debug;
use lorawan_device::async_device::radio as device;
#[allow(clippy::upper_case_acronyms)]
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum RadioError<BUS> {
SPI(BUS),
CS,
Reset,
AntRx,
AntTx,
Busy,
DIO1,
PayloadSizeMismatch(usize, usize),
RetentionListExceeded,
InvalidBandwidth,
ModulationParamsMissing,
PacketParamsMissing,
HeaderError,
CRCErrorUnexpected,
CRCErrorOnReceive,
TransmitTimeout,
ReceiveTimeout,
TimeoutUnexpected,
TransmitDoneUnexpected,
ReceiveDoneUnexpected,
CADUnexpected,
}
pub struct RadioSystemError {
pub rc_64khz_calibration: bool,
pub rc_13mhz_calibration: bool,
pub pll_calibration: bool,
pub adc_calibration: bool,
pub image_calibration: bool,
pub xosc_start: bool,
pub pll_lock: bool,
pub pa_ramp: bool,
}
#[derive(Clone, Copy, PartialEq)]
pub enum PacketType {
GFSK = 0x00,
LoRa = 0x01,
None = 0x0F,
}
impl PacketType {
pub const fn value(self) -> u8 {
self as u8
}
pub fn to_enum(value: u8) -> Self {
if value == 0x00 {
PacketType::GFSK
} else if value == 0x01 {
PacketType::LoRa
} else {
PacketType::None
}
}
}
#[derive(Clone, Copy)]
pub struct PacketStatus {
pub rssi: i8,
pub snr: i8,
pub signal_rssi: i8,
pub freq_error: u32,
}
#[derive(Clone, Copy, PartialEq)]
pub enum RadioType {
SX1261,
SX1262,
}
#[derive(Clone, Copy, PartialEq)]
pub enum RadioMode {
Sleep = 0x00, // sleep mode
StandbyRC = 0x01, // standby mode with RC oscillator
StandbyXOSC = 0x02, // standby mode with XOSC oscillator
FrequencySynthesis = 0x03, // frequency synthesis mode
Transmit = 0x04, // transmit mode
Receive = 0x05, // receive mode
ReceiveDutyCycle = 0x06, // receive duty cycle mode
ChannelActivityDetection = 0x07, // channel activity detection mode
}
impl RadioMode {
/// Returns the value of the mode.
pub const fn value(self) -> u8 {
self as u8
}
pub fn to_enum(value: u8) -> Self {
if value == 0x00 {
RadioMode::Sleep
} else if value == 0x01 {
RadioMode::StandbyRC
} else if value == 0x02 {
RadioMode::StandbyXOSC
} else if value == 0x03 {
RadioMode::FrequencySynthesis
} else if value == 0x04 {
RadioMode::Transmit
} else if value == 0x05 {
RadioMode::Receive
} else if value == 0x06 {
RadioMode::ReceiveDutyCycle
} else if value == 0x07 {
RadioMode::ChannelActivityDetection
} else {
RadioMode::Sleep
}
}
}
pub enum RadioState {
Idle = 0x00,
RxRunning = 0x01,
TxRunning = 0x02,
ChannelActivityDetecting = 0x03,
}
impl RadioState {
/// Returns the value of the state.
pub fn value(self) -> u8 {
self as u8
}
}
pub struct RadioStatus {
pub cmd_status: u8,
pub chip_mode: u8,
}
impl RadioStatus {
pub fn value(self) -> u8 {
(self.chip_mode << 4) | (self.cmd_status << 1)
}
}
#[derive(Clone, Copy)]
pub enum IrqMask {
None = 0x0000,
TxDone = 0x0001,
RxDone = 0x0002,
PreambleDetected = 0x0004,
SyncwordValid = 0x0008,
HeaderValid = 0x0010,
HeaderError = 0x0020,
CRCError = 0x0040,
CADDone = 0x0080,
CADActivityDetected = 0x0100,
RxTxTimeout = 0x0200,
All = 0xFFFF,
}
impl IrqMask {
pub fn value(self) -> u16 {
self as u16
}
}
#[derive(Clone, Copy)]
pub enum Register {
PacketParams = 0x0704, // packet configuration
PayloadLength = 0x0702, // payload size
SynchTimeout = 0x0706, // recalculated number of symbols
Syncword = 0x06C0, // Syncword values
LoRaSyncword = 0x0740, // LoRa Syncword value
GeneratedRandomNumber = 0x0819, //32-bit generated random number
AnaLNA = 0x08E2, // disable the LNA
AnaMixer = 0x08E5, // disable the mixer
RxGain = 0x08AC, // RX gain (0x94: power saving, 0x96: rx boosted)
XTATrim = 0x0911, // device internal trimming capacitor
OCP = 0x08E7, // over current protection max value
RetentionList = 0x029F, // retention list
IQPolarity = 0x0736, // optimize the inverted IQ operation (see DS_SX1261-2_V1.2 datasheet chapter 15.4)
TxModulation = 0x0889, // modulation quality with 500 kHz LoRa Bandwidth (see DS_SX1261-2_V1.2 datasheet chapter 15.1)
TxClampCfg = 0x08D8, // better resistance to antenna mismatch (see DS_SX1261-2_V1.2 datasheet chapter 15.2)
RTCCtrl = 0x0902, // RTC control
EvtClr = 0x0944, // event clear
}
impl Register {
pub fn addr(self) -> u16 {
self as u16
}
}
#[derive(Clone, Copy, PartialEq)]
pub enum OpCode {
GetStatus = 0xC0,
WriteRegister = 0x0D,
ReadRegister = 0x1D,
WriteBuffer = 0x0E,
ReadBuffer = 0x1E,
SetSleep = 0x84,
SetStandby = 0x80,
SetFS = 0xC1,
SetTx = 0x83,
SetRx = 0x82,
SetRxDutyCycle = 0x94,
SetCAD = 0xC5,
SetTxContinuousWave = 0xD1,
SetTxContinuousPremable = 0xD2,
SetPacketType = 0x8A,
GetPacketType = 0x11,
SetRFFrequency = 0x86,
SetTxParams = 0x8E,
SetPAConfig = 0x95,
SetCADParams = 0x88,
SetBufferBaseAddress = 0x8F,
SetModulationParams = 0x8B,
SetPacketParams = 0x8C,
GetRxBufferStatus = 0x13,
GetPacketStatus = 0x14,
GetRSSIInst = 0x15,
GetStats = 0x10,
ResetStats = 0x00,
CfgDIOIrq = 0x08,
GetIrqStatus = 0x12,
ClrIrqStatus = 0x02,
Calibrate = 0x89,
CalibrateImage = 0x98,
SetRegulatorMode = 0x96,
GetErrors = 0x17,
ClrErrors = 0x07,
SetTCXOMode = 0x97,
SetTxFallbackMode = 0x93,
SetRFSwitchMode = 0x9D,
SetStopRxTimerOnPreamble = 0x9F,
SetLoRaSymbTimeout = 0xA0,
}
impl OpCode {
pub fn value(self) -> u8 {
self as u8
}
}
pub struct SleepParams {
pub wakeup_rtc: bool, // get out of sleep mode if wakeup signal received from RTC
pub reset: bool,
pub warm_start: bool,
}
impl SleepParams {
pub fn value(self) -> u8 {
((self.warm_start as u8) << 2) | ((self.reset as u8) << 1) | (self.wakeup_rtc as u8)
}
}
#[derive(Clone, Copy, PartialEq)]
pub enum StandbyMode {
RC = 0x00,
XOSC = 0x01,
}
impl StandbyMode {
pub fn value(self) -> u8 {
self as u8
}
}
#[derive(Clone, Copy)]
pub enum RegulatorMode {
UseLDO = 0x00,
UseDCDC = 0x01,
}
impl RegulatorMode {
pub fn value(self) -> u8 {
self as u8
}
}
#[derive(Clone, Copy)]
pub struct CalibrationParams {
pub rc64k_enable: bool, // calibrate RC64K clock
pub rc13m_enable: bool, // calibrate RC13M clock
pub pll_enable: bool, // calibrate PLL
pub adc_pulse_enable: bool, // calibrate ADC Pulse
pub adc_bulkn_enable: bool, // calibrate ADC bulkN
pub adc_bulkp_enable: bool, // calibrate ADC bulkP
pub img_enable: bool,
}
impl CalibrationParams {
pub fn value(self) -> u8 {
((self.img_enable as u8) << 6)
| ((self.adc_bulkp_enable as u8) << 5)
| ((self.adc_bulkn_enable as u8) << 4)
| ((self.adc_pulse_enable as u8) << 3)
| ((self.pll_enable as u8) << 2)
| ((self.rc13m_enable as u8) << 1)
| ((self.rc64k_enable as u8) << 0)
}
}
#[derive(Clone, Copy)]
pub enum TcxoCtrlVoltage {
Ctrl1V6 = 0x00,
Ctrl1V7 = 0x01,
Ctrl1V8 = 0x02,
Ctrl2V2 = 0x03,
Ctrl2V4 = 0x04,
Ctrl2V7 = 0x05,
Ctrl3V0 = 0x06,
Ctrl3V3 = 0x07,
}
impl TcxoCtrlVoltage {
pub fn value(self) -> u8 {
self as u8
}
}
#[derive(Clone, Copy)]
pub enum RampTime {
Ramp10Us = 0x00,
Ramp20Us = 0x01,
Ramp40Us = 0x02,
Ramp80Us = 0x03,
Ramp200Us = 0x04,
Ramp800Us = 0x05,
Ramp1700Us = 0x06,
Ramp3400Us = 0x07,
}
impl RampTime {
pub fn value(self) -> u8 {
self as u8
}
}
#[derive(Clone, Copy, PartialEq)]
pub enum SpreadingFactor {
_5 = 0x05,
_6 = 0x06,
_7 = 0x07,
_8 = 0x08,
_9 = 0x09,
_10 = 0x0A,
_11 = 0x0B,
_12 = 0x0C,
}
impl SpreadingFactor {
pub fn value(self) -> u8 {
self as u8
}
}
impl From<device::SpreadingFactor> for SpreadingFactor {
fn from(sf: device::SpreadingFactor) -> Self {
match sf {
device::SpreadingFactor::_7 => SpreadingFactor::_7,
device::SpreadingFactor::_8 => SpreadingFactor::_8,
device::SpreadingFactor::_9 => SpreadingFactor::_9,
device::SpreadingFactor::_10 => SpreadingFactor::_10,
device::SpreadingFactor::_11 => SpreadingFactor::_11,
device::SpreadingFactor::_12 => SpreadingFactor::_12,
}
}
}
#[derive(Clone, Copy, PartialEq)]
pub enum Bandwidth {
_500KHz = 0x06,
_250KHz = 0x05,
_125KHz = 0x04,
}
impl Bandwidth {
pub fn value(self) -> u8 {
self as u8
}
pub fn value_in_hz(self) -> u32 {
match self {
Bandwidth::_125KHz => 125000u32,
Bandwidth::_250KHz => 250000u32,
Bandwidth::_500KHz => 500000u32,
}
}
}
impl From<device::Bandwidth> for Bandwidth {
fn from(bw: device::Bandwidth) -> Self {
match bw {
device::Bandwidth::_500KHz => Bandwidth::_500KHz,
device::Bandwidth::_250KHz => Bandwidth::_250KHz,
device::Bandwidth::_125KHz => Bandwidth::_125KHz,
}
}
}
#[derive(Clone, Copy)]
pub enum CodingRate {
_4_5 = 0x01,
_4_6 = 0x02,
_4_7 = 0x03,
_4_8 = 0x04,
}
impl CodingRate {
pub fn value(self) -> u8 {
self as u8
}
}
impl From<device::CodingRate> for CodingRate {
fn from(cr: device::CodingRate) -> Self {
match cr {
device::CodingRate::_4_5 => CodingRate::_4_5,
device::CodingRate::_4_6 => CodingRate::_4_6,
device::CodingRate::_4_7 => CodingRate::_4_7,
device::CodingRate::_4_8 => CodingRate::_4_8,
}
}
}
#[derive(Clone, Copy)]
pub struct ModulationParams {
pub spreading_factor: SpreadingFactor,
pub bandwidth: Bandwidth,
pub coding_rate: CodingRate,
pub low_data_rate_optimize: u8,
}
#[derive(Clone, Copy)]
pub struct PacketParams {
pub preamble_length: u16, // number of LoRa symbols in the preamble
pub implicit_header: bool, // if the header is explicit, it will be transmitted in the LoRa packet, but is not transmitted if the header is implicit (known fixed length)
pub payload_length: u8,
pub crc_on: bool,
pub iq_inverted: bool,
}
#[derive(Clone, Copy)]
pub enum CADSymbols {
_1 = 0x00,
_2 = 0x01,
_4 = 0x02,
_8 = 0x03,
_16 = 0x04,
}
impl CADSymbols {
pub fn value(self) -> u8 {
self as u8
}
}
#[derive(Clone, Copy)]
pub enum CADExitMode {
CADOnly = 0x00,
CADRx = 0x01,
CADLBT = 0x10,
}
impl CADExitMode {
pub fn value(self) -> u8 {
self as u8
}
}

View File

@ -0,0 +1,674 @@
use embedded_hal::digital::v2::OutputPin;
use embedded_hal_async::digital::Wait;
use embedded_hal_async::spi::SpiBus;
use super::mod_params::*;
use super::LoRa;
// Internal frequency of the radio
const SX126X_XTAL_FREQ: u32 = 32000000;
// Scaling factor used to perform fixed-point operations
const SX126X_PLL_STEP_SHIFT_AMOUNT: u32 = 14;
// PLL step - scaled with SX126X_PLL_STEP_SHIFT_AMOUNT
const SX126X_PLL_STEP_SCALED: u32 = SX126X_XTAL_FREQ >> (25 - SX126X_PLL_STEP_SHIFT_AMOUNT);
// Maximum value for parameter symbNum
const SX126X_MAX_LORA_SYMB_NUM_TIMEOUT: u8 = 248;
// Provides board-specific functionality for Semtech SX126x-based boards
impl<SPI, CTRL, WAIT, BUS> LoRa<SPI, CTRL, WAIT>
where
SPI: SpiBus<u8, Error = BUS>,
CTRL: OutputPin,
WAIT: Wait,
{
// Initialize the radio driver
pub(super) async fn sub_init(&mut self) -> Result<(), RadioError<BUS>> {
self.brd_reset().await?;
self.brd_wakeup().await?;
self.sub_set_standby(StandbyMode::RC).await?;
self.brd_io_tcxo_init().await?;
self.brd_io_rf_switch_init().await?;
self.image_calibrated = false;
Ok(())
}
// Wakeup the radio if it is in Sleep mode and check that Busy is low
pub(super) async fn sub_check_device_ready(&mut self) -> Result<(), RadioError<BUS>> {
let operating_mode = self.brd_get_operating_mode();
if operating_mode == RadioMode::Sleep || operating_mode == RadioMode::ReceiveDutyCycle {
self.brd_wakeup().await?;
}
self.brd_wait_on_busy().await?;
Ok(())
}
// Save the payload to be sent in the radio buffer
pub(super) async fn sub_set_payload(&mut self, payload: &[u8]) -> Result<(), RadioError<BUS>> {
self.brd_write_buffer(0x00, payload).await?;
Ok(())
}
// Read the payload received.
pub(super) async fn sub_get_payload(&mut self, buffer: &mut [u8]) -> Result<u8, RadioError<BUS>> {
let (size, offset) = self.sub_get_rx_buffer_status().await?;
if (size as usize) > buffer.len() {
Err(RadioError::PayloadSizeMismatch(size as usize, buffer.len()))
} else {
self.brd_read_buffer(offset, buffer).await?;
Ok(size)
}
}
// Send a payload
pub(super) async fn sub_send_payload(&mut self, payload: &[u8], timeout: u32) -> Result<(), RadioError<BUS>> {
self.sub_set_payload(payload).await?;
self.sub_set_tx(timeout).await?;
Ok(())
}
// Get a 32-bit random value generated by the radio. A valid packet type must have been configured before using this command.
//
// The radio must be in reception mode before executing this function. This code can potentially result in interrupt generation. It is the responsibility of
// the calling code to disable radio interrupts before calling this function, and re-enable them afterwards if necessary, or be certain that any interrupts
// generated during this process will not cause undesired side-effects in the software.
//
// The random numbers produced by the generator do not have a uniform or Gaussian distribution. If uniformity is needed, perform appropriate software post-processing.
pub(super) async fn sub_get_random(&mut self) -> Result<u32, RadioError<BUS>> {
let mut reg_ana_lna_buffer_original = [0x00u8];
let mut reg_ana_mixer_buffer_original = [0x00u8];
let mut reg_ana_lna_buffer = [0x00u8];
let mut reg_ana_mixer_buffer = [0x00u8];
let mut number_buffer = [0x00u8, 0x00u8, 0x00u8, 0x00u8];
self.brd_read_registers(Register::AnaLNA, &mut reg_ana_lna_buffer_original)
.await?;
reg_ana_lna_buffer[0] = reg_ana_lna_buffer_original[0] & (!(1 << 0));
self.brd_write_registers(Register::AnaLNA, &reg_ana_lna_buffer).await?;
self.brd_read_registers(Register::AnaMixer, &mut reg_ana_mixer_buffer_original)
.await?;
reg_ana_mixer_buffer[0] = reg_ana_mixer_buffer_original[0] & (!(1 << 7));
self.brd_write_registers(Register::AnaMixer, &reg_ana_mixer_buffer)
.await?;
// Set radio in continuous reception
self.sub_set_rx(0xFFFFFFu32).await?;
self.brd_read_registers(Register::GeneratedRandomNumber, &mut number_buffer)
.await?;
self.sub_set_standby(StandbyMode::RC).await?;
self.brd_write_registers(Register::AnaLNA, &reg_ana_lna_buffer_original)
.await?;
self.brd_write_registers(Register::AnaMixer, &reg_ana_mixer_buffer_original)
.await?;
Ok(Self::convert_u8_buffer_to_u32(&number_buffer))
}
// Set the radio in sleep mode
pub(super) async fn sub_set_sleep(&mut self, sleep_config: SleepParams) -> Result<(), RadioError<BUS>> {
self.brd_ant_sleep()?;
if !sleep_config.warm_start {
self.image_calibrated = false;
}
self.brd_write_command(OpCode::SetSleep, &[sleep_config.value()])
.await?;
self.brd_set_operating_mode(RadioMode::Sleep);
Ok(())
}
// Set the radio in configuration mode
pub(super) async fn sub_set_standby(&mut self, mode: StandbyMode) -> Result<(), RadioError<BUS>> {
self.brd_write_command(OpCode::SetStandby, &[mode.value()]).await?;
if mode == StandbyMode::RC {
self.brd_set_operating_mode(RadioMode::StandbyRC);
} else {
self.brd_set_operating_mode(RadioMode::StandbyXOSC);
}
self.brd_ant_sleep()?;
Ok(())
}
// Set the radio in FS mode
pub(super) async fn sub_set_fs(&mut self) -> Result<(), RadioError<BUS>> {
// antenna settings ???
self.brd_write_command(OpCode::SetFS, &[]).await?;
self.brd_set_operating_mode(RadioMode::FrequencySynthesis);
Ok(())
}
// Set the radio in transmission mode with timeout specified
pub(super) async fn sub_set_tx(&mut self, timeout: u32) -> Result<(), RadioError<BUS>> {
let buffer = [
Self::timeout_1(timeout),
Self::timeout_2(timeout),
Self::timeout_3(timeout),
];
self.brd_ant_set_tx()?;
self.brd_set_operating_mode(RadioMode::Transmit);
self.brd_write_command(OpCode::SetTx, &buffer).await?;
Ok(())
}
// Set the radio in reception mode with timeout specified
pub(super) async fn sub_set_rx(&mut self, timeout: u32) -> Result<(), RadioError<BUS>> {
let buffer = [
Self::timeout_1(timeout),
Self::timeout_2(timeout),
Self::timeout_3(timeout),
];
self.brd_ant_set_rx()?;
self.brd_set_operating_mode(RadioMode::Receive);
self.brd_write_registers(Register::RxGain, &[0x94u8]).await?;
self.brd_write_command(OpCode::SetRx, &buffer).await?;
Ok(())
}
// Set the radio in reception mode with Boosted LNA gain and timeout specified
pub(super) async fn sub_set_rx_boosted(&mut self, timeout: u32) -> Result<(), RadioError<BUS>> {
let buffer = [
Self::timeout_1(timeout),
Self::timeout_2(timeout),
Self::timeout_3(timeout),
];
self.brd_ant_set_rx()?;
self.brd_set_operating_mode(RadioMode::Receive);
// set max LNA gain, increase current by ~2mA for around ~3dB in sensitivity
self.brd_write_registers(Register::RxGain, &[0x96u8]).await?;
self.brd_write_command(OpCode::SetRx, &buffer).await?;
Ok(())
}
// Set the Rx duty cycle management parameters
pub(super) async fn sub_set_rx_duty_cycle(&mut self, rx_time: u32, sleep_time: u32) -> Result<(), RadioError<BUS>> {
let buffer = [
((rx_time >> 16) & 0xFF) as u8,
((rx_time >> 8) & 0xFF) as u8,
(rx_time & 0xFF) as u8,
((sleep_time >> 16) & 0xFF) as u8,
((sleep_time >> 8) & 0xFF) as u8,
(sleep_time & 0xFF) as u8,
];
// antenna settings ???
self.brd_write_command(OpCode::SetRxDutyCycle, &buffer).await?;
self.brd_set_operating_mode(RadioMode::ReceiveDutyCycle);
Ok(())
}
// Set the radio in CAD mode
pub(super) async fn sub_set_cad(&mut self) -> Result<(), RadioError<BUS>> {
self.brd_ant_set_rx()?;
self.brd_write_command(OpCode::SetCAD, &[]).await?;
self.brd_set_operating_mode(RadioMode::ChannelActivityDetection);
Ok(())
}
// Set the radio in continuous wave transmission mode
pub(super) async fn sub_set_tx_continuous_wave(&mut self) -> Result<(), RadioError<BUS>> {
self.brd_ant_set_tx()?;
self.brd_write_command(OpCode::SetTxContinuousWave, &[]).await?;
self.brd_set_operating_mode(RadioMode::Transmit);
Ok(())
}
// Set the radio in continuous preamble transmission mode
pub(super) async fn sub_set_tx_infinite_preamble(&mut self) -> Result<(), RadioError<BUS>> {
self.brd_ant_set_tx()?;
self.brd_write_command(OpCode::SetTxContinuousPremable, &[]).await?;
self.brd_set_operating_mode(RadioMode::Transmit);
Ok(())
}
// Decide which interrupt will stop the internal radio rx timer.
// false timer stop after header/syncword detection
// true timer stop after preamble detection
pub(super) async fn sub_set_stop_rx_timer_on_preamble_detect(
&mut self,
enable: bool,
) -> Result<(), RadioError<BUS>> {
self.brd_write_command(OpCode::SetStopRxTimerOnPreamble, &[enable as u8])
.await?;
Ok(())
}
// Set the number of symbols the radio will wait to validate a reception
pub(super) async fn sub_set_lora_symb_num_timeout(&mut self, symb_num: u16) -> Result<(), RadioError<BUS>> {
let mut exp = 0u8;
let mut reg;
let mut mant = ((core::cmp::min(symb_num, SX126X_MAX_LORA_SYMB_NUM_TIMEOUT as u16) as u8) + 1) >> 1;
while mant > 31 {
mant = (mant + 3) >> 2;
exp += 1;
}
reg = mant << ((2 * exp) + 1);
self.brd_write_command(OpCode::SetLoRaSymbTimeout, &[reg]).await?;
if symb_num != 0 {
reg = exp + (mant << 3);
self.brd_write_registers(Register::SynchTimeout, &[reg]).await?;
}
Ok(())
}
// Set the power regulators operating mode (LDO or DC_DC). Using only LDO implies that the Rx or Tx current is doubled
pub(super) async fn sub_set_regulator_mode(&mut self, mode: RegulatorMode) -> Result<(), RadioError<BUS>> {
self.brd_write_command(OpCode::SetRegulatorMode, &[mode.value()])
.await?;
Ok(())
}
// Calibrate the given radio block
pub(super) async fn sub_calibrate(&mut self, calibrate_params: CalibrationParams) -> Result<(), RadioError<BUS>> {
self.brd_write_command(OpCode::Calibrate, &[calibrate_params.value()])
.await?;
Ok(())
}
// Calibrate the image rejection based on the given frequency
pub(super) async fn sub_calibrate_image(&mut self, freq: u32) -> Result<(), RadioError<BUS>> {
let mut cal_freq = [0x00u8, 0x00u8];
if freq > 900000000 {
cal_freq[0] = 0xE1;
cal_freq[1] = 0xE9;
} else if freq > 850000000 {
cal_freq[0] = 0xD7;
cal_freq[1] = 0xDB;
} else if freq > 770000000 {
cal_freq[0] = 0xC1;
cal_freq[1] = 0xC5;
} else if freq > 460000000 {
cal_freq[0] = 0x75;
cal_freq[1] = 0x81;
} else if freq > 425000000 {
cal_freq[0] = 0x6B;
cal_freq[1] = 0x6F;
}
self.brd_write_command(OpCode::CalibrateImage, &cal_freq).await?;
Ok(())
}
// Activate the extention of the timeout when a long preamble is used
pub(super) async fn sub_set_long_preamble(&mut self, _enable: u8) -> Result<(), RadioError<BUS>> {
Ok(()) // no operation currently
}
// Set the transmission parameters
// hp_max 0 for sx1261, 7 for sx1262
// device_sel 1 for sx1261, 0 for sx1262
// pa_lut 0 for 14dBm LUT, 1 for 22dBm LUT
pub(super) async fn sub_set_pa_config(
&mut self,
pa_duty_cycle: u8,
hp_max: u8,
device_sel: u8,
pa_lut: u8,
) -> Result<(), RadioError<BUS>> {
self.brd_write_command(OpCode::SetPAConfig, &[pa_duty_cycle, hp_max, device_sel, pa_lut])
.await?;
Ok(())
}
// Define into which mode the chip goes after a TX / RX done
pub(super) async fn sub_set_rx_tx_fallback_mode(&mut self, fallback_mode: u8) -> Result<(), RadioError<BUS>> {
self.brd_write_command(OpCode::SetTxFallbackMode, &[fallback_mode])
.await?;
Ok(())
}
// Set the IRQ mask and DIO masks
pub(super) async fn sub_set_dio_irq_params(
&mut self,
irq_mask: u16,
dio1_mask: u16,
dio2_mask: u16,
dio3_mask: u16,
) -> Result<(), RadioError<BUS>> {
let mut buffer = [0x00u8; 8];
buffer[0] = ((irq_mask >> 8) & 0x00FF) as u8;
buffer[1] = (irq_mask & 0x00FF) as u8;
buffer[2] = ((dio1_mask >> 8) & 0x00FF) as u8;
buffer[3] = (dio1_mask & 0x00FF) as u8;
buffer[4] = ((dio2_mask >> 8) & 0x00FF) as u8;
buffer[5] = (dio2_mask & 0x00FF) as u8;
buffer[6] = ((dio3_mask >> 8) & 0x00FF) as u8;
buffer[7] = (dio3_mask & 0x00FF) as u8;
self.brd_write_command(OpCode::CfgDIOIrq, &buffer).await?;
Ok(())
}
// Return the current IRQ status
pub(super) async fn sub_get_irq_status(&mut self) -> Result<u16, RadioError<BUS>> {
let mut irq_status = [0x00u8, 0x00u8];
self.brd_read_command(OpCode::GetIrqStatus, &mut irq_status).await?;
Ok(((irq_status[0] as u16) << 8) | (irq_status[1] as u16))
}
// Indicate if DIO2 is used to control an RF Switch
pub(super) async fn sub_set_dio2_as_rf_switch_ctrl(&mut self, enable: bool) -> Result<(), RadioError<BUS>> {
self.brd_write_command(OpCode::SetRFSwitchMode, &[enable as u8]).await?;
Ok(())
}
// Indicate if the radio main clock is supplied from a TCXO
// tcxo_voltage voltage used to control the TCXO on/off from DIO3
// timeout duration given to the TCXO to go to 32MHz
pub(super) async fn sub_set_dio3_as_tcxo_ctrl(
&mut self,
tcxo_voltage: TcxoCtrlVoltage,
timeout: u32,
) -> Result<(), RadioError<BUS>> {
let buffer = [
tcxo_voltage.value() & 0x07,
Self::timeout_1(timeout),
Self::timeout_2(timeout),
Self::timeout_3(timeout),
];
self.brd_write_command(OpCode::SetTCXOMode, &buffer).await?;
Ok(())
}
// Set the RF frequency (Hz)
pub(super) async fn sub_set_rf_frequency(&mut self, frequency: u32) -> Result<(), RadioError<BUS>> {
let mut buffer = [0x00u8; 4];
if !self.image_calibrated {
self.sub_calibrate_image(frequency).await?;
self.image_calibrated = true;
}
let freq_in_pll_steps = Self::convert_freq_in_hz_to_pll_step(frequency);
buffer[0] = ((freq_in_pll_steps >> 24) & 0xFF) as u8;
buffer[1] = ((freq_in_pll_steps >> 16) & 0xFF) as u8;
buffer[2] = ((freq_in_pll_steps >> 8) & 0xFF) as u8;
buffer[3] = (freq_in_pll_steps & 0xFF) as u8;
self.brd_write_command(OpCode::SetRFFrequency, &buffer).await?;
Ok(())
}
// Set the radio for the given protocol (LoRa or GFSK). This method has to be called before setting RF frequency, modulation paramaters, and packet paramaters.
pub(super) async fn sub_set_packet_type(&mut self, packet_type: PacketType) -> Result<(), RadioError<BUS>> {
self.packet_type = packet_type;
self.brd_write_command(OpCode::SetPacketType, &[packet_type.value()])
.await?;
Ok(())
}
// Get the current radio protocol (LoRa or GFSK)
pub(super) fn sub_get_packet_type(&mut self) -> PacketType {
self.packet_type
}
// Set the transmission parameters
// power RF output power [-18..13] dBm
// ramp_time transmission ramp up time
pub(super) async fn sub_set_tx_params(
&mut self,
mut power: i8,
ramp_time: RampTime,
) -> Result<(), RadioError<BUS>> {
if self.brd_get_radio_type() == RadioType::SX1261 {
if power == 15 {
self.sub_set_pa_config(0x06, 0x00, 0x01, 0x01).await?;
} else {
self.sub_set_pa_config(0x04, 0x00, 0x01, 0x01).await?;
}
if power >= 14 {
power = 14;
} else if power < -17 {
power = -17;
}
} else {
// Provide better resistance of the SX1262 Tx to antenna mismatch (see DS_SX1261-2_V1.2 datasheet chapter 15.2)
let mut tx_clamp_cfg = [0x00u8];
self.brd_read_registers(Register::TxClampCfg, &mut tx_clamp_cfg).await?;
tx_clamp_cfg[0] = tx_clamp_cfg[0] | (0x0F << 1);
self.brd_write_registers(Register::TxClampCfg, &tx_clamp_cfg).await?;
self.sub_set_pa_config(0x04, 0x07, 0x00, 0x01).await?;
if power > 22 {
power = 22;
} else if power < -9 {
power = -9;
}
}
// power conversion of negative number from i8 to u8 ???
self.brd_write_command(OpCode::SetTxParams, &[power as u8, ramp_time.value()])
.await?;
Ok(())
}
// Set the modulation parameters
pub(super) async fn sub_set_modulation_params(&mut self) -> Result<(), RadioError<BUS>> {
if self.modulation_params.is_some() {
let mut buffer = [0x00u8; 4];
// Since this driver only supports LoRa, ensure the packet type is set accordingly
self.sub_set_packet_type(PacketType::LoRa).await?;
let modulation_params = self.modulation_params.unwrap();
buffer[0] = modulation_params.spreading_factor.value();
buffer[1] = modulation_params.bandwidth.value();
buffer[2] = modulation_params.coding_rate.value();
buffer[3] = modulation_params.low_data_rate_optimize;
self.brd_write_command(OpCode::SetModulationParams, &buffer).await?;
Ok(())
} else {
Err(RadioError::ModulationParamsMissing)
}
}
// Set the packet parameters
pub(super) async fn sub_set_packet_params(&mut self) -> Result<(), RadioError<BUS>> {
if self.packet_params.is_some() {
let mut buffer = [0x00u8; 6];
// Since this driver only supports LoRa, ensure the packet type is set accordingly
self.sub_set_packet_type(PacketType::LoRa).await?;
let packet_params = self.packet_params.unwrap();
buffer[0] = ((packet_params.preamble_length >> 8) & 0xFF) as u8;
buffer[1] = (packet_params.preamble_length & 0xFF) as u8;
buffer[2] = packet_params.implicit_header as u8;
buffer[3] = packet_params.payload_length;
buffer[4] = packet_params.crc_on as u8;
buffer[5] = packet_params.iq_inverted as u8;
self.brd_write_command(OpCode::SetPacketParams, &buffer).await?;
Ok(())
} else {
Err(RadioError::PacketParamsMissing)
}
}
// Set the channel activity detection (CAD) parameters
// symbols number of symbols to use for CAD operations
// det_peak limit for detection of SNR peak used in the CAD
// det_min minimum symbol recognition for CAD
// exit_mode operation to be done at the end of CAD action
// timeout timeout value to abort the CAD activity
pub(super) async fn sub_set_cad_params(
&mut self,
symbols: CADSymbols,
det_peak: u8,
det_min: u8,
exit_mode: CADExitMode,
timeout: u32,
) -> Result<(), RadioError<BUS>> {
let mut buffer = [0x00u8; 7];
buffer[0] = symbols.value();
buffer[1] = det_peak;
buffer[2] = det_min;
buffer[3] = exit_mode.value();
buffer[4] = Self::timeout_1(timeout);
buffer[5] = Self::timeout_2(timeout);
buffer[6] = Self::timeout_3(timeout);
self.brd_write_command(OpCode::SetCADParams, &buffer).await?;
self.brd_set_operating_mode(RadioMode::ChannelActivityDetection);
Ok(())
}
// Set the data buffer base address for transmission and reception
pub(super) async fn sub_set_buffer_base_address(
&mut self,
tx_base_address: u8,
rx_base_address: u8,
) -> Result<(), RadioError<BUS>> {
self.brd_write_command(OpCode::SetBufferBaseAddress, &[tx_base_address, rx_base_address])
.await?;
Ok(())
}
// Get the current radio status
pub(super) async fn sub_get_status(&mut self) -> Result<RadioStatus, RadioError<BUS>> {
let status = self.brd_read_command(OpCode::GetStatus, &mut []).await?;
Ok(RadioStatus {
cmd_status: (status & (0x07 << 1)) >> 1,
chip_mode: (status & (0x07 << 4)) >> 4,
})
}
// Get the instantaneous RSSI value for the last packet received
pub(super) async fn sub_get_rssi_inst(&mut self) -> Result<i8, RadioError<BUS>> {
let mut buffer = [0x00u8];
self.brd_read_command(OpCode::GetRSSIInst, &mut buffer).await?;
let rssi: i8 = ((-(buffer[0] as i32)) >> 1) as i8; // check this ???
Ok(rssi)
}
// Get the last received packet buffer status
pub(super) async fn sub_get_rx_buffer_status(&mut self) -> Result<(u8, u8), RadioError<BUS>> {
if self.packet_params.is_some() {
let mut status = [0x00u8; 2];
let mut payload_length_buffer = [0x00u8];
self.brd_read_command(OpCode::GetRxBufferStatus, &mut status).await?;
if (self.sub_get_packet_type() == PacketType::LoRa) && self.packet_params.unwrap().implicit_header {
self.brd_read_registers(Register::PayloadLength, &mut payload_length_buffer)
.await?;
} else {
payload_length_buffer[0] = status[0];
}
let payload_length = payload_length_buffer[0];
let offset = status[1];
Ok((payload_length, offset))
} else {
Err(RadioError::PacketParamsMissing)
}
}
// Get the last received packet payload status
pub(super) async fn sub_get_packet_status(&mut self) -> Result<PacketStatus, RadioError<BUS>> {
let mut status = [0x00u8; 3];
self.brd_read_command(OpCode::GetPacketStatus, &mut status).await?;
// check this ???
let rssi = ((-(status[0] as i32)) >> 1) as i8;
let snr = ((status[1] as i8) + 2) >> 2;
let signal_rssi = ((-(status[2] as i32)) >> 1) as i8;
let freq_error = self.frequency_error;
Ok(PacketStatus {
rssi,
snr,
signal_rssi,
freq_error,
})
}
// Get the possible system errors
pub(super) async fn sub_get_device_errors(&mut self) -> Result<RadioSystemError, RadioError<BUS>> {
let mut errors = [0x00u8; 2];
self.brd_read_command(OpCode::GetErrors, &mut errors).await?;
Ok(RadioSystemError {
rc_64khz_calibration: (errors[1] & (1 << 0)) != 0,
rc_13mhz_calibration: (errors[1] & (1 << 1)) != 0,
pll_calibration: (errors[1] & (1 << 2)) != 0,
adc_calibration: (errors[1] & (1 << 3)) != 0,
image_calibration: (errors[1] & (1 << 4)) != 0,
xosc_start: (errors[1] & (1 << 5)) != 0,
pll_lock: (errors[1] & (1 << 6)) != 0,
pa_ramp: (errors[0] & (1 << 0)) != 0,
})
}
// Clear all the errors in the device
pub(super) async fn sub_clear_device_errors(&mut self) -> Result<(), RadioError<BUS>> {
self.brd_write_command(OpCode::ClrErrors, &[0x00u8, 0x00u8]).await?;
Ok(())
}
// Clear the IRQs
pub(super) async fn sub_clear_irq_status(&mut self, irq: u16) -> Result<(), RadioError<BUS>> {
let mut buffer = [0x00u8, 0x00u8];
buffer[0] = ((irq >> 8) & 0xFF) as u8;
buffer[1] = (irq & 0xFF) as u8;
self.brd_write_command(OpCode::ClrIrqStatus, &buffer).await?;
Ok(())
}
// Utility functions
fn timeout_1(timeout: u32) -> u8 {
((timeout >> 16) & 0xFF) as u8
}
fn timeout_2(timeout: u32) -> u8 {
((timeout >> 8) & 0xFF) as u8
}
fn timeout_3(timeout: u32) -> u8 {
(timeout & 0xFF) as u8
}
// check this ???
fn convert_u8_buffer_to_u32(buffer: &[u8; 4]) -> u32 {
let b0 = buffer[0] as u32;
let b1 = buffer[1] as u32;
let b2 = buffer[2] as u32;
let b3 = buffer[3] as u32;
(b0 << 24) | (b1 << 16) | (b2 << 8) | b3
}
fn convert_freq_in_hz_to_pll_step(freq_in_hz: u32) -> u32 {
// Get integer and fractional parts of the frequency computed with a PLL step scaled value
let steps_int = freq_in_hz / SX126X_PLL_STEP_SCALED;
let steps_frac = freq_in_hz - (steps_int * SX126X_PLL_STEP_SCALED);
(steps_int << SX126X_PLL_STEP_SHIFT_AMOUNT)
+ (((steps_frac << SX126X_PLL_STEP_SHIFT_AMOUNT) + (SX126X_PLL_STEP_SCALED >> 1)) / SX126X_PLL_STEP_SCALED)
}
}

View File

@ -2,6 +2,14 @@
name = "embassy-macros"
version = "0.1.0"
edition = "2021"
license = "MIT OR Apache-2.0"
description = "macros for creating the entry point and tasks for embassy-executor"
repository = "https://github.com/embassy-rs/embassy"
categories = [
"embedded",
"no-std",
"asynchronous",
]
[dependencies]
syn = { version = "1.0.76", features = ["full", "extra-traits"] }
@ -13,8 +21,5 @@ proc-macro2 = "1.0.29"
proc-macro = true
[features]
std = []
wasm = []
# Enabling this cause interrupt::take! to require embassy-executor
rtos-trace-interrupt = []

21
embassy-macros/README.md Normal file
View File

@ -0,0 +1,21 @@
# embassy-macros
An [Embassy](https://embassy.dev) project.
Macros for creating the main entry point and tasks that can be spawned by `embassy-executor`.
NOTE: The macros are re-exported by the `embassy-executor` crate which should be used instead of adding a direct dependency on the `embassy-macros` crate.
## Minimum supported Rust version (MSRV)
The `task` and `main` macros require the type alias impl trait (TAIT) nightly feature in order to compile.
## License
This work is licensed under either of
- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or
<http://www.apache.org/licenses/LICENSE-2.0>)
- MIT license ([LICENSE-MIT](LICENSE-MIT) or <http://opensource.org/licenses/MIT>)
at your option.

View File

@ -1,3 +1,4 @@
#![doc = include_str!("../README.md")]
extern crate proc_macro;
use proc_macro::TokenStream;
@ -6,6 +7,36 @@ mod macros;
mod util;
use macros::*;
/// Declares an async task that can be run by `embassy-executor`. The optional `pool_size` parameter can be used to specify how
/// many concurrent tasks can be spawned (default is 1) for the function.
///
///
/// The following restrictions apply:
///
/// * The function must be declared `async`.
/// * The function must not use generics.
/// * The optional `pool_size` attribute must be 1 or greater.
///
///
/// ## Examples
///
/// Declaring a task taking no arguments:
///
/// ``` rust
/// #[embassy_executor::task]
/// async fn mytask() {
/// // Function body
/// }
/// ```
///
/// Declaring a task with a given pool size:
///
/// ``` rust
/// #[embassy_executor::task(pool_size = 4)]
/// async fn mytask() {
/// // Function body
/// }
/// ```
#[proc_macro_attribute]
pub fn task(args: TokenStream, item: TokenStream) -> TokenStream {
let args = syn::parse_macro_input!(args as syn::AttributeArgs);
@ -14,11 +45,104 @@ pub fn task(args: TokenStream, item: TokenStream) -> TokenStream {
task::run(args, f).unwrap_or_else(|x| x).into()
}
/// Creates a new `executor` instance and declares an application entry point for Cortex-M spawning the corresponding function body as an async task.
///
/// The following restrictions apply:
///
/// * The function must accept exactly 1 parameter, an `embassy_executor::Spawner` handle that it can use to spawn additional tasks.
/// * The function must be declared `async`.
/// * The function must not use generics.
/// * Only a single `main` task may be declared.
///
/// ## Examples
/// Spawning a task:
///
/// ``` rust
/// #[embassy_executor::main]
/// async fn main(_s: embassy_executor::Spawner) {
/// // Function body
/// }
/// ```
#[proc_macro_attribute]
pub fn main(args: TokenStream, item: TokenStream) -> TokenStream {
pub fn main_cortex_m(args: TokenStream, item: TokenStream) -> TokenStream {
let args = syn::parse_macro_input!(args as syn::AttributeArgs);
let f = syn::parse_macro_input!(item as syn::ItemFn);
main::run(args, f).unwrap_or_else(|x| x).into()
main::run(args, f, main::cortex_m()).unwrap_or_else(|x| x).into()
}
/// Creates a new `executor` instance and declares an application entry point for RISC-V spawning the corresponding function body as an async task.
///
/// The following restrictions apply:
///
/// * The function must accept exactly 1 parameter, an `embassy_executor::Spawner` handle that it can use to spawn additional tasks.
/// * The function must be declared `async`.
/// * The function must not use generics.
/// * Only a single `main` task may be declared.
///
/// ## Examples
/// Spawning a task:
///
/// ``` rust
/// #[embassy_executor::main]
/// async fn main(_s: embassy_executor::Spawner) {
/// // Function body
/// }
/// ```
#[proc_macro_attribute]
pub fn main_riscv(args: TokenStream, item: TokenStream) -> TokenStream {
let args = syn::parse_macro_input!(args as syn::AttributeArgs);
let f = syn::parse_macro_input!(item as syn::ItemFn);
main::run(args, f, main::riscv()).unwrap_or_else(|x| x).into()
}
/// Creates a new `executor` instance and declares an application entry point for STD spawning the corresponding function body as an async task.
///
/// The following restrictions apply:
///
/// * The function must accept exactly 1 parameter, an `embassy_executor::Spawner` handle that it can use to spawn additional tasks.
/// * The function must be declared `async`.
/// * The function must not use generics.
/// * Only a single `main` task may be declared.
///
/// ## Examples
/// Spawning a task:
///
/// ``` rust
/// #[embassy_executor::main]
/// async fn main(_s: embassy_executor::Spawner) {
/// // Function body
/// }
/// ```
#[proc_macro_attribute]
pub fn main_std(args: TokenStream, item: TokenStream) -> TokenStream {
let args = syn::parse_macro_input!(args as syn::AttributeArgs);
let f = syn::parse_macro_input!(item as syn::ItemFn);
main::run(args, f, main::std()).unwrap_or_else(|x| x).into()
}
/// Creates a new `executor` instance and declares an application entry point for WASM spawning the corresponding function body as an async task.
///
/// The following restrictions apply:
///
/// * The function must accept exactly 1 parameter, an `embassy_executor::Spawner` handle that it can use to spawn additional tasks.
/// * The function must be declared `async`.
/// * The function must not use generics.
/// * Only a single `main` task may be declared.
///
/// ## Examples
/// Spawning a task:
///
/// ``` rust
/// #[embassy_executor::main]
/// async fn main(_s: embassy_executor::Spawner) {
/// // Function body
/// }
/// ```
#[proc_macro_attribute]
pub fn main_wasm(args: TokenStream, item: TokenStream) -> TokenStream {
let args = syn::parse_macro_input!(args as syn::AttributeArgs);
let f = syn::parse_macro_input!(item as syn::ItemFn);
main::run(args, f, main::wasm()).unwrap_or_else(|x| x).into()
}
#[proc_macro_attribute]

View File

@ -7,7 +7,62 @@ use crate::util::ctxt::Ctxt;
#[derive(Debug, FromMeta)]
struct Args {}
pub fn run(args: syn::AttributeArgs, f: syn::ItemFn) -> Result<TokenStream, TokenStream> {
pub fn riscv() -> TokenStream {
quote! {
#[riscv_rt::entry]
fn main() -> ! {
let mut executor = ::embassy_executor::Executor::new();
let executor = unsafe { __make_static(&mut executor) };
executor.run(|spawner| {
spawner.must_spawn(__embassy_main(spawner));
})
}
}
}
pub fn cortex_m() -> TokenStream {
quote! {
#[cortex_m_rt::entry]
fn main() -> ! {
let mut executor = ::embassy_executor::Executor::new();
let executor = unsafe { __make_static(&mut executor) };
executor.run(|spawner| {
spawner.must_spawn(__embassy_main(spawner));
})
}
}
}
pub fn wasm() -> TokenStream {
quote! {
#[wasm_bindgen::prelude::wasm_bindgen(start)]
pub fn main() -> Result<(), wasm_bindgen::JsValue> {
static EXECUTOR: ::embassy_executor::_export::StaticCell<::embassy_executor::Executor> = ::embassy_executor::_export::StaticCell::new();
let executor = EXECUTOR.init(::embassy_executor::Executor::new());
executor.start(|spawner| {
spawner.spawn(__embassy_main(spawner)).unwrap();
});
Ok(())
}
}
}
pub fn std() -> TokenStream {
quote! {
fn main() -> ! {
let mut executor = ::embassy_executor::Executor::new();
let executor = unsafe { __make_static(&mut executor) };
executor.run(|spawner| {
spawner.must_spawn(__embassy_main(spawner));
})
}
}
}
pub fn run(args: syn::AttributeArgs, f: syn::ItemFn, main: TokenStream) -> Result<TokenStream, TokenStream> {
#[allow(unused_variables)]
let args = Args::from_list(&args).map_err(|e| e.write_errors())?;
@ -30,46 +85,6 @@ pub fn run(args: syn::AttributeArgs, f: syn::ItemFn) -> Result<TokenStream, Toke
let f_body = f.block;
#[cfg(feature = "wasm")]
let main = quote! {
#[wasm_bindgen::prelude::wasm_bindgen(start)]
pub fn main() -> Result<(), wasm_bindgen::JsValue> {
static EXECUTOR: ::embassy_executor::_export::StaticCell<::embassy_executor::Executor> = ::embassy_executor::_export::StaticCell::new();
let executor = EXECUTOR.init(::embassy_executor::Executor::new());
executor.start(|spawner| {
spawner.spawn(__embassy_main(spawner)).unwrap();
});
Ok(())
}
};
#[cfg(all(feature = "std", not(feature = "wasm")))]
let main = quote! {
fn main() -> ! {
let mut executor = ::embassy_executor::Executor::new();
let executor = unsafe { __make_static(&mut executor) };
executor.run(|spawner| {
spawner.must_spawn(__embassy_main(spawner));
})
}
};
#[cfg(all(not(feature = "std"), not(feature = "wasm")))]
let main = quote! {
#[cortex_m_rt::entry]
fn main() -> ! {
let mut executor = ::embassy_executor::Executor::new();
let executor = unsafe { __make_static(&mut executor) };
executor.run(|spawner| {
spawner.must_spawn(__embassy_main(spawner));
})
}
};
let result = quote! {
#[::embassy_executor::task()]
async fn __embassy_main(#fargs) {

View File

@ -2,12 +2,13 @@
name = "embassy-net"
version = "0.1.0"
edition = "2021"
license = "MIT OR Apache-2.0"
[package.metadata.embassy_docs]
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-v$VERSION/embassy-net/src/"
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net/src/"
features = [ "pool-4", "defmt", "tcp", "dns", "dhcpv4", "proto-ipv6", "medium-ethernet", "medium-ip", "embassy-time/tick-1mhz"]
features = ["pool-4", "defmt", "tcp", "dns", "dhcpv4", "proto-ipv6", "medium-ethernet", "medium-ip"]
target = "thumbv7em-none-eabi"
[features]
@ -16,6 +17,9 @@ std = []
defmt = ["dep:defmt", "smoltcp/defmt"]
nightly = ["dep:embedded-io", "embedded-io?/async", "dep:embedded-nal-async"]
unstable-traits = []
udp = ["smoltcp/socket-udp"]
tcp = ["smoltcp/socket-tcp"]
dns = ["smoltcp/socket-dns"]
@ -30,7 +34,6 @@ pool-16 = []
pool-32 = []
pool-64 = []
pool-128 = []
unstable-traits = []
[dependencies]
@ -39,7 +42,7 @@ log = { version = "0.4.14", optional = true }
embassy-time = { version = "0.1.0", path = "../embassy-time" }
embassy-sync = { version = "0.1.0", path = "../embassy-sync" }
embedded-io = { version = "0.3.0", features = [ "async" ] }
embedded-io = { version = "0.3.1", optional = true }
managed = { version = "0.8.0", default-features = false, features = [ "map" ] }
heapless = { version = "0.7.5", default-features = false }
@ -49,7 +52,7 @@ stable_deref_trait = { version = "1.2.0", default-features = false }
futures = { version = "0.3.17", default-features = false, features = [ "async-await" ] }
atomic-pool = "1.0"
atomic-polyfill = "1.0.1"
embedded-nal-async = "0.2.0"
embedded-nal-async = { version = "0.2.0", optional = true }
[dependencies.smoltcp]
version = "0.8.0"

View File

@ -1,6 +1,5 @@
#![cfg_attr(not(feature = "std"), no_std)]
#![allow(clippy::new_without_default)]
#![feature(generic_associated_types, type_alias_impl_trait)]
#![cfg_attr(feature = "nightly", feature(type_alias_impl_trait))]
// This mod MUST go first, so that the others see its macros.
pub(crate) mod fmt;

View File

@ -1,10 +1,9 @@
use core::cell::UnsafeCell;
use core::future::Future;
use core::future::{poll_fn, Future};
use core::task::{Context, Poll};
use embassy_sync::waitqueue::WakerRegistration;
use embassy_time::{Instant, Timer};
use futures::future::poll_fn;
use futures::pin_mut;
use heapless::Vec;
#[cfg(feature = "dhcpv4")]

View File

@ -1,9 +1,8 @@
use core::cell::UnsafeCell;
use core::future::Future;
use core::future::poll_fn;
use core::mem;
use core::task::Poll;
use futures::future::poll_fn;
use smoltcp::iface::{Interface, SocketHandle};
use smoltcp::socket::tcp;
use smoltcp::time::Duration;
@ -55,6 +54,18 @@ pub struct TcpWriter<'a> {
io: TcpIo<'a>,
}
impl<'a> TcpReader<'a> {
pub async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Error> {
self.io.read(buf).await
}
}
impl<'a> TcpWriter<'a> {
pub async fn write(&mut self, buf: &[u8]) -> Result<usize, Error> {
self.io.write(buf).await
}
}
impl<'a> TcpSocket<'a> {
pub fn new<D: Device>(stack: &'a Stack<D>, rx_buffer: &'a mut [u8], tx_buffer: &'a mut [u8]) -> Self {
// safety: not accessed reentrantly.
@ -92,7 +103,7 @@ impl<'a> TcpSocket<'a> {
Err(tcp::ConnectError::Unaddressable) => return Err(ConnectError::NoRoute),
}
futures::future::poll_fn(|cx| unsafe {
poll_fn(|cx| unsafe {
self.io.with_mut(|s, _| match s.state() {
tcp::State::Closed | tcp::State::TimeWait => Poll::Ready(Err(ConnectError::ConnectionReset)),
tcp::State::Listen => unreachable!(),
@ -117,7 +128,7 @@ impl<'a> TcpSocket<'a> {
Err(tcp::ListenError::Unaddressable) => return Err(AcceptError::InvalidPort),
}
futures::future::poll_fn(|cx| unsafe {
poll_fn(|cx| unsafe {
self.io.with_mut(|s, _| match s.state() {
tcp::State::Listen | tcp::State::SynSent | tcp::State::SynReceived => {
s.register_send_waker(cx.waker());
@ -129,6 +140,14 @@ impl<'a> TcpSocket<'a> {
.await
}
pub async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Error> {
self.io.read(buf).await
}
pub async fn write(&mut self, buf: &[u8]) -> Result<usize, Error> {
self.io.write(buf).await
}
pub fn set_timeout(&mut self, duration: Option<Duration>) {
unsafe { self.io.with_mut(|s, _| s.set_timeout(duration)) }
}
@ -241,6 +260,7 @@ impl<'d> TcpIo<'d> {
.await
}
#[allow(unused)]
async fn flush(&mut self) -> Result<(), Error> {
poll_fn(move |_| {
Poll::Ready(Ok(())) // TODO: Is there a better implementation for this?
@ -249,88 +269,96 @@ impl<'d> TcpIo<'d> {
}
}
impl embedded_io::Error for ConnectError {
fn kind(&self) -> embedded_io::ErrorKind {
embedded_io::ErrorKind::Other
#[cfg(feature = "nightly")]
mod embedded_io_impls {
use core::future::Future;
use super::*;
impl embedded_io::Error for ConnectError {
fn kind(&self) -> embedded_io::ErrorKind {
embedded_io::ErrorKind::Other
}
}
}
impl embedded_io::Error for Error {
fn kind(&self) -> embedded_io::ErrorKind {
embedded_io::ErrorKind::Other
impl embedded_io::Error for Error {
fn kind(&self) -> embedded_io::ErrorKind {
embedded_io::ErrorKind::Other
}
}
}
impl<'d> embedded_io::Io for TcpSocket<'d> {
type Error = Error;
}
impl<'d> embedded_io::Io for TcpSocket<'d> {
type Error = Error;
}
impl<'d> embedded_io::asynch::Read for TcpSocket<'d> {
type ReadFuture<'a> = impl Future<Output = Result<usize, Self::Error>>
impl<'d> embedded_io::asynch::Read for TcpSocket<'d> {
type ReadFuture<'a> = impl Future<Output = Result<usize, Self::Error>> + 'a
where
Self: 'a;
fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> {
self.io.read(buf)
fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> {
self.io.read(buf)
}
}
}
impl<'d> embedded_io::asynch::Write for TcpSocket<'d> {
type WriteFuture<'a> = impl Future<Output = Result<usize, Self::Error>>
impl<'d> embedded_io::asynch::Write for TcpSocket<'d> {
type WriteFuture<'a> = impl Future<Output = Result<usize, Self::Error>> + 'a
where
Self: 'a;
fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> {
self.io.write(buf)
}
fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> {
self.io.write(buf)
}
type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>>
type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a
where
Self: 'a;
fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> {
self.io.flush()
fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> {
self.io.flush()
}
}
}
impl<'d> embedded_io::Io for TcpReader<'d> {
type Error = Error;
}
impl<'d> embedded_io::Io for TcpReader<'d> {
type Error = Error;
}
impl<'d> embedded_io::asynch::Read for TcpReader<'d> {
type ReadFuture<'a> = impl Future<Output = Result<usize, Self::Error>>
impl<'d> embedded_io::asynch::Read for TcpReader<'d> {
type ReadFuture<'a> = impl Future<Output = Result<usize, Self::Error>> + 'a
where
Self: 'a;
fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> {
self.io.read(buf)
fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> {
self.io.read(buf)
}
}
}
impl<'d> embedded_io::Io for TcpWriter<'d> {
type Error = Error;
}
impl<'d> embedded_io::Io for TcpWriter<'d> {
type Error = Error;
}
impl<'d> embedded_io::asynch::Write for TcpWriter<'d> {
type WriteFuture<'a> = impl Future<Output = Result<usize, Self::Error>>
impl<'d> embedded_io::asynch::Write for TcpWriter<'d> {
type WriteFuture<'a> = impl Future<Output = Result<usize, Self::Error>> + 'a
where
Self: 'a;
fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> {
self.io.write(buf)
}
fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> {
self.io.write(buf)
}
type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>>
type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a
where
Self: 'a;
fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> {
self.io.flush()
fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> {
self.io.flush()
}
}
}
#[cfg(feature = "unstable-traits")]
#[cfg(all(feature = "unstable-traits", feature = "nightly"))]
pub mod client {
use core::future::Future;
use core::mem::MaybeUninit;
use core::ptr::NonNull;
@ -417,7 +445,7 @@ pub mod client {
impl<'d, const N: usize, const TX_SZ: usize, const RX_SZ: usize> embedded_io::asynch::Read
for TcpConnection<'d, N, TX_SZ, RX_SZ>
{
type ReadFuture<'a> = impl Future<Output = Result<usize, Self::Error>>
type ReadFuture<'a> = impl Future<Output = Result<usize, Self::Error>> + 'a
where
Self: 'a;
@ -429,7 +457,7 @@ pub mod client {
impl<'d, const N: usize, const TX_SZ: usize, const RX_SZ: usize> embedded_io::asynch::Write
for TcpConnection<'d, N, TX_SZ, RX_SZ>
{
type WriteFuture<'a> = impl Future<Output = Result<usize, Self::Error>>
type WriteFuture<'a> = impl Future<Output = Result<usize, Self::Error>> + 'a
where
Self: 'a;
@ -437,7 +465,7 @@ pub mod client {
self.socket.write(buf)
}
type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>>
type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a
where
Self: 'a;

View File

@ -1,8 +1,8 @@
use core::cell::UnsafeCell;
use core::future::poll_fn;
use core::mem;
use core::task::Poll;
use futures::future::poll_fn;
use smoltcp::iface::{Interface, SocketHandle};
use smoltcp::socket::udp::{self, PacketMetadata};
use smoltcp::wire::{IpEndpoint, IpListenEndpoint};

View File

@ -2,6 +2,7 @@
name = "embassy-nrf"
version = "0.1.0"
edition = "2021"
license = "MIT OR Apache-2.0"
[package.metadata.embassy_docs]
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-nrf-v$VERSION/embassy-nrf/src/"
@ -18,10 +19,10 @@ flavors = [
time = ["dep:embassy-time"]
defmt = ["dep:defmt", "embassy-executor/defmt", "embassy-sync/defmt", "embassy-usb?/defmt", "embedded-io?/defmt", "embassy-embedded-hal/defmt"]
defmt = ["dep:defmt", "embassy-executor/defmt", "embassy-sync/defmt", "embassy-usb-driver?/defmt", "embedded-io?/defmt", "embassy-embedded-hal/defmt"]
# Enable nightly-only features
nightly = ["embedded-hal-1", "embedded-hal-async", "embassy-usb", "embedded-storage-async", "dep:embedded-io", "embassy-embedded-hal/nightly"]
nightly = ["embedded-hal-1", "embedded-hal-async", "dep:embassy-usb-driver", "embedded-storage-async", "dep:embedded-io", "embassy-embedded-hal/nightly"]
# Reexport the PAC for the currently enabled chip at `embassy_nrf::pac`.
# This is unstable because semver-minor (non-breaking) releases of embassy-nrf may major-bump (breaking) the PAC version.
@ -57,7 +58,7 @@ _nrf5340-net = ["_nrf5340", "nrf5340-net-pac"]
_nrf5340 = ["_gpio-p1", "_dppi"]
_nrf9160 = ["nrf9160-pac", "_dppi"]
_time-driver = ["dep:embassy-time", "embassy-time?/tick-32768hz"]
_time-driver = ["dep:embassy-time", "embassy-time?/tick-hz-32_768"]
_ppi = []
_dppi = []
@ -70,12 +71,12 @@ embassy-sync = { version = "0.1.0", path = "../embassy-sync" }
embassy-cortex-m = { version = "0.1.0", path = "../embassy-cortex-m", features = ["prio-bits-3"]}
embassy-hal-common = {version = "0.1.0", path = "../embassy-hal-common" }
embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" }
embassy-usb = {version = "0.1.0", path = "../embassy-usb", optional=true }
embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optional=true }
embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] }
embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.8", optional = true}
embedded-hal-async = { version = "0.1.0-alpha.1", optional = true}
embedded-io = { version = "0.3.0", features = ["async"], optional = true }
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9", optional = true}
embedded-hal-async = { version = "=0.1.0-alpha.3", optional = true}
embedded-io = { version = "0.3.1", features = ["async"], optional = true }
defmt = { version = "0.3", optional = true }
log = { version = "0.4.14", optional = true }
@ -89,13 +90,13 @@ embedded-storage = "0.3.0"
embedded-storage-async = { version = "0.3.0", optional = true }
cfg-if = "1.0.0"
nrf52805-pac = { version = "0.11.0", optional = true, features = [ "rt" ] }
nrf52810-pac = { version = "0.11.0", optional = true, features = [ "rt" ] }
nrf52811-pac = { version = "0.11.0", optional = true, features = [ "rt" ] }
nrf52820-pac = { version = "0.11.0", optional = true, features = [ "rt" ] }
nrf52832-pac = { version = "0.11.0", optional = true, features = [ "rt" ] }
nrf52833-pac = { version = "0.11.0", optional = true, features = [ "rt" ] }
nrf52840-pac = { version = "0.11.0", optional = true, features = [ "rt" ] }
nrf5340-app-pac = { version = "0.11.0", optional = true, features = [ "rt" ] }
nrf5340-net-pac = { version = "0.11.0", optional = true, features = [ "rt" ] }
nrf9160-pac = { version = "0.11.0", optional = true, features = [ "rt" ] }
nrf52805-pac = { version = "0.12.0", optional = true, features = [ "rt" ] }
nrf52810-pac = { version = "0.12.0", optional = true, features = [ "rt" ] }
nrf52811-pac = { version = "0.12.0", optional = true, features = [ "rt" ] }
nrf52820-pac = { version = "0.12.0", optional = true, features = [ "rt" ] }
nrf52832-pac = { version = "0.12.0", optional = true, features = [ "rt" ] }
nrf52833-pac = { version = "0.12.0", optional = true, features = [ "rt" ] }
nrf52840-pac = { version = "0.12.0", optional = true, features = [ "rt" ] }
nrf5340-app-pac = { version = "0.12.0", optional = true, features = [ "rt" ] }
nrf5340-net-pac = { version = "0.12.0", optional = true, features = [ "rt" ] }
nrf9160-pac = { version = "0.12.0", optional = true, features = [ "rt" ] }

View File

@ -13,8 +13,9 @@
//!
//! Please also see [crate::uarte] to understand when [BufferedUarte] should be used.
use core::cell::RefCell;
use core::cmp::min;
use core::future::Future;
use core::future::{poll_fn, Future};
use core::sync::atomic::{compiler_fence, Ordering};
use core::task::Poll;
@ -22,11 +23,10 @@ use embassy_cortex_m::peripheral::{PeripheralMutex, PeripheralState, StateStorag
use embassy_hal_common::ring_buffer::RingBuffer;
use embassy_hal_common::{into_ref, PeripheralRef};
use embassy_sync::waitqueue::WakerRegistration;
use futures::future::poll_fn;
// Re-export SVD variants to allow user to directly set values
pub use pac::uarte0::{baudrate::BAUDRATE_A as Baudrate, config::PARITY_A as Parity};
use crate::gpio::Pin as GpioPin;
use crate::gpio::{self, Pin as GpioPin};
use crate::interrupt::InterruptExt;
use crate::ppi::{AnyConfigurableChannel, ConfigurableChannel, Event, Ppi, Task};
use crate::timer::{Frequency, Instance as TimerInstance, Timer};
@ -71,7 +71,7 @@ struct StateInner<'d, U: UarteInstance, T: TimerInstance> {
/// Interface to a UARTE instance
pub struct BufferedUarte<'d, U: UarteInstance, T: TimerInstance> {
inner: PeripheralMutex<'d, StateInner<'d, U, T>>,
inner: RefCell<PeripheralMutex<'d, StateInner<'d, U, T>>>,
}
impl<'d, U: UarteInstance, T: TimerInstance> Unpin for BufferedUarte<'d, U, T> {}
@ -169,7 +169,7 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> {
ppi_ch2.enable();
Self {
inner: PeripheralMutex::new(irq, &mut state.0, move || StateInner {
inner: RefCell::new(PeripheralMutex::new(irq, &mut state.0, move || StateInner {
_peri: peri,
timer,
_ppi_ch1: ppi_ch1,
@ -182,13 +182,13 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> {
tx: RingBuffer::new(tx_buffer),
tx_state: TxState::Idle,
tx_waker: WakerRegistration::new(),
}),
})),
}
}
/// Adjust the baud rate to the provided value.
pub fn set_baudrate(&mut self, baudrate: Baudrate) {
self.inner.with(|state| {
self.inner.borrow_mut().with(|state| {
let r = U::regs();
let timeout = 0x8000_0000 / (baudrate as u32 / 40);
@ -198,21 +198,16 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> {
r.baudrate.write(|w| w.baudrate().variant(baudrate));
});
}
}
impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::Io for BufferedUarte<'d, U, T> {
type Error = core::convert::Infallible;
}
pub fn split<'u>(&'u mut self) -> (BufferedUarteRx<'u, 'd, U, T>, BufferedUarteTx<'u, 'd, U, T>) {
(BufferedUarteRx { inner: self }, BufferedUarteTx { inner: self })
}
impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Read for BufferedUarte<'d, U, T> {
type ReadFuture<'a> = impl Future<Output = Result<usize, Self::Error>>
where
Self: 'a;
fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> {
async fn inner_read<'a>(&'a self, buf: &'a mut [u8]) -> Result<usize, core::convert::Infallible> {
poll_fn(move |cx| {
let mut do_pend = false;
let res = self.inner.with(|state| {
let mut inner = self.inner.borrow_mut();
let res = inner.with(|state| {
compiler_fence(Ordering::SeqCst);
trace!("poll_read");
@ -232,62 +227,18 @@ impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Read for Buffe
Poll::Pending
});
if do_pend {
self.inner.pend();
inner.pend();
}
res
})
.await
}
}
impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::BufRead for BufferedUarte<'d, U, T> {
type FillBufFuture<'a> = impl Future<Output = Result<&'a [u8], Self::Error>>
where
Self: 'a;
fn fill_buf<'a>(&'a mut self) -> Self::FillBufFuture<'a> {
async fn inner_write<'a>(&'a self, buf: &'a [u8]) -> Result<usize, core::convert::Infallible> {
poll_fn(move |cx| {
self.inner.with(|state| {
compiler_fence(Ordering::SeqCst);
trace!("fill_buf");
// We have data ready in buffer? Return it.
let buf = state.rx.pop_buf();
if !buf.is_empty() {
trace!(" got {:?} {:?}", buf.as_ptr() as u32, buf.len());
let buf: &[u8] = buf;
// Safety: buffer lives as long as uart
let buf: &[u8] = unsafe { core::mem::transmute(buf) };
return Poll::Ready(Ok(buf));
}
trace!(" empty");
state.rx_waker.register(cx.waker());
Poll::<Result<&[u8], Self::Error>>::Pending
})
})
}
fn consume(&mut self, amt: usize) {
let signal = self.inner.with(|state| {
let full = state.rx.is_full();
state.rx.pop(amt);
full
});
if signal {
self.inner.pend();
}
}
}
impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Write for BufferedUarte<'d, U, T> {
type WriteFuture<'a> = impl Future<Output = Result<usize, Self::Error>>
where
Self: 'a;
fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> {
poll_fn(move |cx| {
let res = self.inner.with(|state| {
let mut inner = self.inner.borrow_mut();
let res = inner.with(|state| {
trace!("poll_write: {:?}", buf.len());
let tx_buf = state.tx.push_buf();
@ -308,19 +259,16 @@ impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Write for Buff
Poll::Ready(Ok(n))
});
self.inner.pend();
inner.pend();
res
})
.await
}
type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>>
where
Self: 'a;
fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> {
async fn inner_flush<'a>(&'a self) -> Result<(), core::convert::Infallible> {
poll_fn(move |cx| {
self.inner.with(|state| {
self.inner.borrow_mut().with(|state| {
trace!("poll_flush");
if !state.tx.is_empty() {
@ -332,6 +280,147 @@ impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Write for Buff
Poll::Ready(Ok(()))
})
})
.await
}
async fn inner_fill_buf<'a>(&'a self) -> Result<&'a [u8], core::convert::Infallible> {
poll_fn(move |cx| {
self.inner.borrow_mut().with(|state| {
compiler_fence(Ordering::SeqCst);
trace!("fill_buf");
// We have data ready in buffer? Return it.
let buf = state.rx.pop_buf();
if !buf.is_empty() {
trace!(" got {:?} {:?}", buf.as_ptr() as u32, buf.len());
let buf: &[u8] = buf;
// Safety: buffer lives as long as uart
let buf: &[u8] = unsafe { core::mem::transmute(buf) };
return Poll::Ready(Ok(buf));
}
trace!(" empty");
state.rx_waker.register(cx.waker());
Poll::<Result<&[u8], core::convert::Infallible>>::Pending
})
})
.await
}
fn inner_consume(&self, amt: usize) {
let mut inner = self.inner.borrow_mut();
let signal = inner.with(|state| {
let full = state.rx.is_full();
state.rx.pop(amt);
full
});
if signal {
inner.pend();
}
}
}
pub struct BufferedUarteTx<'u, 'd, U: UarteInstance, T: TimerInstance> {
inner: &'u BufferedUarte<'d, U, T>,
}
pub struct BufferedUarteRx<'u, 'd, U: UarteInstance, T: TimerInstance> {
inner: &'u BufferedUarte<'d, U, T>,
}
impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::Io for BufferedUarte<'d, U, T> {
type Error = core::convert::Infallible;
}
impl<'u, 'd, U: UarteInstance, T: TimerInstance> embedded_io::Io for BufferedUarteRx<'u, 'd, U, T> {
type Error = core::convert::Infallible;
}
impl<'u, 'd, U: UarteInstance, T: TimerInstance> embedded_io::Io for BufferedUarteTx<'u, 'd, U, T> {
type Error = core::convert::Infallible;
}
impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Read for BufferedUarte<'d, U, T> {
type ReadFuture<'a> = impl Future<Output = Result<usize, Self::Error>> + 'a
where
Self: 'a;
fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> {
self.inner_read(buf)
}
}
impl<'u, 'd: 'u, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Read for BufferedUarteRx<'u, 'd, U, T> {
type ReadFuture<'a> = impl Future<Output = Result<usize, Self::Error>> + 'a
where
Self: 'a;
fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> {
self.inner.inner_read(buf)
}
}
impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::BufRead for BufferedUarte<'d, U, T> {
type FillBufFuture<'a> = impl Future<Output = Result<&'a [u8], Self::Error>> + 'a
where
Self: 'a;
fn fill_buf<'a>(&'a mut self) -> Self::FillBufFuture<'a> {
self.inner_fill_buf()
}
fn consume(&mut self, amt: usize) {
self.inner_consume(amt)
}
}
impl<'u, 'd: 'u, U: UarteInstance, T: TimerInstance> embedded_io::asynch::BufRead for BufferedUarteRx<'u, 'd, U, T> {
type FillBufFuture<'a> = impl Future<Output = Result<&'a [u8], Self::Error>> + 'a
where
Self: 'a;
fn fill_buf<'a>(&'a mut self) -> Self::FillBufFuture<'a> {
self.inner.inner_fill_buf()
}
fn consume(&mut self, amt: usize) {
self.inner.inner_consume(amt)
}
}
impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Write for BufferedUarte<'d, U, T> {
type WriteFuture<'a> = impl Future<Output = Result<usize, Self::Error>> + 'a
where
Self: 'a;
fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> {
self.inner_write(buf)
}
type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a
where
Self: 'a;
fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> {
self.inner_flush()
}
}
impl<'u, 'd: 'u, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Write for BufferedUarteTx<'u, 'd, U, T> {
type WriteFuture<'a> = impl Future<Output = Result<usize, Self::Error>> + 'a
where
Self: 'a;
fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> {
self.inner.inner_write(buf)
}
type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a
where
Self: 'a;
fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> {
self.inner.inner_flush()
}
}
@ -339,21 +428,23 @@ impl<'a, U: UarteInstance, T: TimerInstance> Drop for StateInner<'a, U, T> {
fn drop(&mut self) {
let r = U::regs();
// TODO this probably deadlocks. do like Uarte instead.
self.timer.stop();
if let RxState::Receiving = self.rx_state {
r.tasks_stoprx.write(|w| unsafe { w.bits(1) });
}
if let TxState::Transmitting(_) = self.tx_state {
r.tasks_stoptx.write(|w| unsafe { w.bits(1) });
}
if let RxState::Receiving = self.rx_state {
low_power_wait_until(|| r.events_endrx.read().bits() == 1);
}
if let TxState::Transmitting(_) = self.tx_state {
low_power_wait_until(|| r.events_endtx.read().bits() == 1);
}
r.inten.reset();
r.events_rxto.reset();
r.tasks_stoprx.write(|w| unsafe { w.bits(1) });
r.events_txstopped.reset();
r.tasks_stoptx.write(|w| unsafe { w.bits(1) });
while r.events_txstopped.read().bits() == 0 {}
while r.events_rxto.read().bits() == 0 {}
r.enable.write(|w| w.enable().disabled());
gpio::deconfigure_pin(r.psel.rxd.read().bits());
gpio::deconfigure_pin(r.psel.txd.read().bits());
gpio::deconfigure_pin(r.psel.rts.read().bits());
gpio::deconfigure_pin(r.psel.cts.read().bits());
}
}
@ -459,13 +550,3 @@ impl<'a, U: UarteInstance, T: TimerInstance> PeripheralState for StateInner<'a,
trace!("irq: end");
}
}
/// Low power blocking wait loop using WFE/SEV.
fn low_power_wait_until(mut condition: impl FnMut() -> bool) {
while !condition() {
// WFE might "eat" an event that would have otherwise woken the executor.
cortex_m::asm::wfe();
}
// Retrigger an event to be transparent to the executor.
cortex_m::asm::sev();
}

View File

@ -131,8 +131,12 @@ impl_uarte!(UARTE0, UARTE0, UARTE0_UART0);
impl_spim!(SPI0, SPIM0, SPIM0_SPIS0_SPI0);
impl_spis!(SPI0, SPIS0, SPIM0_SPIS0_SPI0);
impl_twim!(TWI0, TWIM0, TWIM0_TWIS0_TWI0);
impl_twis!(TWI0, TWIS0, TWIM0_TWIS0_TWI0);
impl_timer!(TIMER0, TIMER0, TIMER0);
impl_timer!(TIMER1, TIMER1, TIMER1);
impl_timer!(TIMER2, TIMER2, TIMER2);
@ -193,8 +197,8 @@ impl_ppi_channel!(PPI_CH29, 29 => static);
impl_ppi_channel!(PPI_CH30, 30 => static);
impl_ppi_channel!(PPI_CH31, 31 => static);
impl_saadc_input!(P0_04, ANALOGINPUT2);
impl_saadc_input!(P0_05, ANALOGINPUT3);
impl_saadc_input!(P0_04, ANALOG_INPUT2);
impl_saadc_input!(P0_05, ANALOG_INPUT3);
pub mod irqs {
use embassy_cortex_m::interrupt::_export::declare;

View File

@ -128,14 +128,21 @@ embassy_hal_common::peripherals! {
// QDEC
QDEC,
// PDM
PDM,
}
impl_uarte!(UARTE0, UARTE0, UARTE0_UART0);
impl_spim!(SPI0, SPIM0, SPIM0_SPIS0_SPI0);
impl_spis!(SPI0, SPIS0, SPIM0_SPIS0_SPI0);
impl_twim!(TWI0, TWIM0, TWIM0_TWIS0_TWI0);
impl_twis!(TWI0, TWIS0, TWIM0_TWIS0_TWI0);
impl_pwm!(PWM0, PWM0, PWM0);
impl_timer!(TIMER0, TIMER0, TIMER0);
@ -208,14 +215,14 @@ impl_ppi_channel!(PPI_CH29, 29 => static);
impl_ppi_channel!(PPI_CH30, 30 => static);
impl_ppi_channel!(PPI_CH31, 31 => static);
impl_saadc_input!(P0_02, ANALOGINPUT0);
impl_saadc_input!(P0_03, ANALOGINPUT1);
impl_saadc_input!(P0_04, ANALOGINPUT2);
impl_saadc_input!(P0_05, ANALOGINPUT3);
impl_saadc_input!(P0_28, ANALOGINPUT4);
impl_saadc_input!(P0_29, ANALOGINPUT5);
impl_saadc_input!(P0_30, ANALOGINPUT6);
impl_saadc_input!(P0_31, ANALOGINPUT7);
impl_saadc_input!(P0_02, ANALOG_INPUT0);
impl_saadc_input!(P0_03, ANALOG_INPUT1);
impl_saadc_input!(P0_04, ANALOG_INPUT2);
impl_saadc_input!(P0_05, ANALOG_INPUT3);
impl_saadc_input!(P0_28, ANALOG_INPUT4);
impl_saadc_input!(P0_29, ANALOG_INPUT5);
impl_saadc_input!(P0_30, ANALOG_INPUT6);
impl_saadc_input!(P0_31, ANALOG_INPUT7);
pub mod irqs {
use embassy_cortex_m::interrupt::_export::declare;

View File

@ -128,6 +128,9 @@ embassy_hal_common::peripherals! {
// QDEC
QDEC,
// PDM
PDM,
}
impl_uarte!(UARTE0, UARTE0, UARTE0_UART0);
@ -135,8 +138,13 @@ impl_uarte!(UARTE0, UARTE0, UARTE0_UART0);
impl_spim!(TWISPI0, SPIM0, TWIM0_TWIS0_TWI0_SPIM0_SPIS0_SPI0);
impl_spim!(SPI1, SPIM1, SPIM1_SPIS1_SPI1);
impl_spis!(TWISPI0, SPIS0, TWIM0_TWIS0_TWI0_SPIM0_SPIS0_SPI0);
impl_spis!(SPI1, SPIS1, SPIM1_SPIS1_SPI1);
impl_twim!(TWISPI0, TWIM0, TWIM0_TWIS0_TWI0_SPIM0_SPIS0_SPI0);
impl_twis!(TWISPI0, TWIS0, TWIM0_TWIS0_TWI0_SPIM0_SPIS0_SPI0);
impl_pwm!(PWM0, PWM0, PWM0);
impl_timer!(TIMER0, TIMER0, TIMER0);
@ -209,14 +217,14 @@ impl_ppi_channel!(PPI_CH29, 29 => static);
impl_ppi_channel!(PPI_CH30, 30 => static);
impl_ppi_channel!(PPI_CH31, 31 => static);
impl_saadc_input!(P0_02, ANALOGINPUT0);
impl_saadc_input!(P0_03, ANALOGINPUT1);
impl_saadc_input!(P0_04, ANALOGINPUT2);
impl_saadc_input!(P0_05, ANALOGINPUT3);
impl_saadc_input!(P0_28, ANALOGINPUT4);
impl_saadc_input!(P0_29, ANALOGINPUT5);
impl_saadc_input!(P0_30, ANALOGINPUT6);
impl_saadc_input!(P0_31, ANALOGINPUT7);
impl_saadc_input!(P0_02, ANALOG_INPUT0);
impl_saadc_input!(P0_03, ANALOG_INPUT1);
impl_saadc_input!(P0_04, ANALOG_INPUT2);
impl_saadc_input!(P0_05, ANALOG_INPUT3);
impl_saadc_input!(P0_28, ANALOG_INPUT4);
impl_saadc_input!(P0_29, ANALOG_INPUT5);
impl_saadc_input!(P0_30, ANALOG_INPUT6);
impl_saadc_input!(P0_31, ANALOG_INPUT7);
pub mod irqs {
use embassy_cortex_m::interrupt::_export::declare;

View File

@ -136,9 +136,15 @@ impl_uarte!(UARTE0, UARTE0, UARTE0_UART0);
impl_spim!(TWISPI0, SPIM0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0);
impl_spim!(TWISPI1, SPIM1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1);
impl_spis!(TWISPI0, SPIS0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0);
impl_spis!(TWISPI1, SPIS1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1);
impl_twim!(TWISPI0, TWIM0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0);
impl_twim!(TWISPI1, TWIM1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1);
impl_twis!(TWISPI0, TWIS0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0);
impl_twis!(TWISPI1, TWIS1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1);
impl_timer!(TIMER0, TIMER0, TIMER0);
impl_timer!(TIMER1, TIMER1, TIMER1);
impl_timer!(TIMER2, TIMER2, TIMER2);

View File

@ -146,9 +146,16 @@ impl_spim!(TWISPI0, SPIM0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0);
impl_spim!(TWISPI1, SPIM1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1);
impl_spim!(SPI2, SPIM2, SPIM2_SPIS2_SPI2);
impl_spis!(TWISPI0, SPIS0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0);
impl_spis!(TWISPI1, SPIS1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1);
impl_spis!(SPI2, SPIS2, SPIM2_SPIS2_SPI2);
impl_twim!(TWISPI0, TWIM0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0);
impl_twim!(TWISPI1, TWIM1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1);
impl_twis!(TWISPI0, TWIS0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0);
impl_twis!(TWISPI1, TWIS1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1);
impl_pwm!(PWM0, PWM0, PWM0);
impl_pwm!(PWM1, PWM1, PWM1);
impl_pwm!(PWM2, PWM2, PWM2);
@ -225,14 +232,14 @@ impl_ppi_channel!(PPI_CH29, 29 => static);
impl_ppi_channel!(PPI_CH30, 30 => static);
impl_ppi_channel!(PPI_CH31, 31 => static);
impl_saadc_input!(P0_02, ANALOGINPUT0);
impl_saadc_input!(P0_03, ANALOGINPUT1);
impl_saadc_input!(P0_04, ANALOGINPUT2);
impl_saadc_input!(P0_05, ANALOGINPUT3);
impl_saadc_input!(P0_28, ANALOGINPUT4);
impl_saadc_input!(P0_29, ANALOGINPUT5);
impl_saadc_input!(P0_30, ANALOGINPUT6);
impl_saadc_input!(P0_31, ANALOGINPUT7);
impl_saadc_input!(P0_02, ANALOG_INPUT0);
impl_saadc_input!(P0_03, ANALOG_INPUT1);
impl_saadc_input!(P0_04, ANALOG_INPUT2);
impl_saadc_input!(P0_05, ANALOG_INPUT3);
impl_saadc_input!(P0_28, ANALOG_INPUT4);
impl_saadc_input!(P0_29, ANALOG_INPUT5);
impl_saadc_input!(P0_30, ANALOG_INPUT6);
impl_saadc_input!(P0_31, ANALOG_INPUT7);
pub mod irqs {
use embassy_cortex_m::interrupt::_export::declare;

View File

@ -158,6 +158,9 @@ embassy_hal_common::peripherals! {
// QDEC
QDEC,
// PDM
PDM,
}
#[cfg(feature = "nightly")]
@ -171,9 +174,16 @@ impl_spim!(TWISPI1, SPIM1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1);
impl_spim!(SPI2, SPIM2, SPIM2_SPIS2_SPI2);
impl_spim!(SPI3, SPIM3, SPIM3);
impl_spis!(TWISPI0, SPIS0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0);
impl_spis!(TWISPI1, SPIS1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1);
impl_spis!(SPI2, SPIS2, SPIM2_SPIS2_SPI2);
impl_twim!(TWISPI0, TWIM0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0);
impl_twim!(TWISPI1, TWIM1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1);
impl_twis!(TWISPI0, TWIS0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0);
impl_twis!(TWISPI1, TWIS1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1);
impl_pwm!(PWM0, PWM0, PWM0);
impl_pwm!(PWM1, PWM1, PWM1);
impl_pwm!(PWM2, PWM2, PWM2);
@ -268,14 +278,14 @@ impl_ppi_channel!(PPI_CH29, 29 => static);
impl_ppi_channel!(PPI_CH30, 30 => static);
impl_ppi_channel!(PPI_CH31, 31 => static);
impl_saadc_input!(P0_02, ANALOGINPUT0);
impl_saadc_input!(P0_03, ANALOGINPUT1);
impl_saadc_input!(P0_04, ANALOGINPUT2);
impl_saadc_input!(P0_05, ANALOGINPUT3);
impl_saadc_input!(P0_28, ANALOGINPUT4);
impl_saadc_input!(P0_29, ANALOGINPUT5);
impl_saadc_input!(P0_30, ANALOGINPUT6);
impl_saadc_input!(P0_31, ANALOGINPUT7);
impl_saadc_input!(P0_02, ANALOG_INPUT0);
impl_saadc_input!(P0_03, ANALOG_INPUT1);
impl_saadc_input!(P0_04, ANALOG_INPUT2);
impl_saadc_input!(P0_05, ANALOG_INPUT3);
impl_saadc_input!(P0_28, ANALOG_INPUT4);
impl_saadc_input!(P0_29, ANALOG_INPUT5);
impl_saadc_input!(P0_30, ANALOG_INPUT6);
impl_saadc_input!(P0_31, ANALOG_INPUT7);
pub mod irqs {
use embassy_cortex_m::interrupt::_export::declare;

View File

@ -161,6 +161,9 @@ embassy_hal_common::peripherals! {
// TEMP
TEMP,
// PDM
PDM,
}
#[cfg(feature = "nightly")]
@ -174,9 +177,16 @@ impl_spim!(TWISPI1, SPIM1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1);
impl_spim!(SPI2, SPIM2, SPIM2_SPIS2_SPI2);
impl_spim!(SPI3, SPIM3, SPIM3);
impl_spis!(TWISPI0, SPIS0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0);
impl_spis!(TWISPI1, SPIS1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1);
impl_spis!(SPI2, SPIS2, SPIM2_SPIS2_SPI2);
impl_twim!(TWISPI0, TWIM0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0);
impl_twim!(TWISPI1, TWIM1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1);
impl_twis!(TWISPI0, TWIS0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0);
impl_twis!(TWISPI1, TWIS1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1);
impl_pwm!(PWM0, PWM0, PWM0);
impl_pwm!(PWM1, PWM1, PWM1);
impl_pwm!(PWM2, PWM2, PWM2);
@ -273,14 +283,14 @@ impl_ppi_channel!(PPI_CH29, 29 => static);
impl_ppi_channel!(PPI_CH30, 30 => static);
impl_ppi_channel!(PPI_CH31, 31 => static);
impl_saadc_input!(P0_02, ANALOGINPUT0);
impl_saadc_input!(P0_03, ANALOGINPUT1);
impl_saadc_input!(P0_04, ANALOGINPUT2);
impl_saadc_input!(P0_05, ANALOGINPUT3);
impl_saadc_input!(P0_28, ANALOGINPUT4);
impl_saadc_input!(P0_29, ANALOGINPUT5);
impl_saadc_input!(P0_30, ANALOGINPUT6);
impl_saadc_input!(P0_31, ANALOGINPUT7);
impl_saadc_input!(P0_02, ANALOG_INPUT0);
impl_saadc_input!(P0_03, ANALOG_INPUT1);
impl_saadc_input!(P0_04, ANALOG_INPUT2);
impl_saadc_input!(P0_05, ANALOG_INPUT3);
impl_saadc_input!(P0_28, ANALOG_INPUT4);
impl_saadc_input!(P0_29, ANALOG_INPUT5);
impl_saadc_input!(P0_30, ANALOG_INPUT6);
impl_saadc_input!(P0_31, ANALOG_INPUT7);
pub mod irqs {
use embassy_cortex_m::interrupt::_export::declare;

View File

@ -361,11 +361,21 @@ impl_spim!(UARTETWISPI1, SPIM1, SERIAL1);
impl_spim!(UARTETWISPI2, SPIM2, SERIAL2);
impl_spim!(UARTETWISPI3, SPIM3, SERIAL3);
impl_spis!(UARTETWISPI0, SPIS0, SERIAL0);
impl_spis!(UARTETWISPI1, SPIS1, SERIAL1);
impl_spis!(UARTETWISPI2, SPIS2, SERIAL2);
impl_spis!(UARTETWISPI3, SPIS3, SERIAL3);
impl_twim!(UARTETWISPI0, TWIM0, SERIAL0);
impl_twim!(UARTETWISPI1, TWIM1, SERIAL1);
impl_twim!(UARTETWISPI2, TWIM2, SERIAL2);
impl_twim!(UARTETWISPI3, TWIM3, SERIAL3);
impl_twis!(UARTETWISPI0, TWIS0, SERIAL0);
impl_twis!(UARTETWISPI1, TWIS1, SERIAL1);
impl_twis!(UARTETWISPI2, TWIS2, SERIAL2);
impl_twis!(UARTETWISPI3, TWIS3, SERIAL3);
impl_pwm!(PWM0, PWM0, PWM0);
impl_pwm!(PWM1, PWM1, PWM1);
impl_pwm!(PWM2, PWM2, PWM2);
@ -458,14 +468,14 @@ impl_ppi_channel!(PPI_CH29, 29 => configurable);
impl_ppi_channel!(PPI_CH30, 30 => configurable);
impl_ppi_channel!(PPI_CH31, 31 => configurable);
impl_saadc_input!(P0_13, ANALOGINPUT0);
impl_saadc_input!(P0_14, ANALOGINPUT1);
impl_saadc_input!(P0_15, ANALOGINPUT2);
impl_saadc_input!(P0_16, ANALOGINPUT3);
impl_saadc_input!(P0_17, ANALOGINPUT4);
impl_saadc_input!(P0_18, ANALOGINPUT5);
impl_saadc_input!(P0_19, ANALOGINPUT6);
impl_saadc_input!(P0_20, ANALOGINPUT7);
impl_saadc_input!(P0_13, ANALOG_INPUT0);
impl_saadc_input!(P0_14, ANALOG_INPUT1);
impl_saadc_input!(P0_15, ANALOG_INPUT2);
impl_saadc_input!(P0_16, ANALOG_INPUT3);
impl_saadc_input!(P0_17, ANALOG_INPUT4);
impl_saadc_input!(P0_18, ANALOG_INPUT5);
impl_saadc_input!(P0_19, ANALOG_INPUT6);
impl_saadc_input!(P0_20, ANALOG_INPUT7);
pub mod irqs {
use embassy_cortex_m::interrupt::_export::declare;

View File

@ -238,7 +238,9 @@ embassy_hal_common::peripherals! {
impl_uarte!(UARTETWISPI0, UARTE0, SERIAL0);
impl_spim!(UARTETWISPI0, SPIM0, SERIAL0);
impl_spis!(UARTETWISPI0, SPIS0, SERIAL0);
impl_twim!(UARTETWISPI0, TWIM0, SERIAL0);
impl_twis!(UARTETWISPI0, TWIS0, SERIAL0);
impl_timer!(TIMER0, TIMER0, TIMER0);
impl_timer!(TIMER1, TIMER1, TIMER1);

View File

@ -260,6 +260,9 @@ embassy_hal_common::peripherals! {
P0_29,
P0_30,
P0_31,
// PDM
PDM,
}
impl_uarte!(UARTETWISPI0, UARTE0, UARTE0_SPIM0_SPIS0_TWIM0_TWIS0);
@ -272,11 +275,21 @@ impl_spim!(UARTETWISPI1, SPIM1, UARTE1_SPIM1_SPIS1_TWIM1_TWIS1);
impl_spim!(UARTETWISPI2, SPIM2, UARTE2_SPIM2_SPIS2_TWIM2_TWIS2);
impl_spim!(UARTETWISPI3, SPIM3, UARTE3_SPIM3_SPIS3_TWIM3_TWIS3);
impl_spis!(UARTETWISPI0, SPIS0, UARTE0_SPIM0_SPIS0_TWIM0_TWIS0);
impl_spis!(UARTETWISPI1, SPIS1, UARTE1_SPIM1_SPIS1_TWIM1_TWIS1);
impl_spis!(UARTETWISPI2, SPIS2, UARTE2_SPIM2_SPIS2_TWIM2_TWIS2);
impl_spis!(UARTETWISPI3, SPIS3, UARTE3_SPIM3_SPIS3_TWIM3_TWIS3);
impl_twim!(UARTETWISPI0, TWIM0, UARTE0_SPIM0_SPIS0_TWIM0_TWIS0);
impl_twim!(UARTETWISPI1, TWIM1, UARTE1_SPIM1_SPIS1_TWIM1_TWIS1);
impl_twim!(UARTETWISPI2, TWIM2, UARTE2_SPIM2_SPIS2_TWIM2_TWIS2);
impl_twim!(UARTETWISPI3, TWIM3, UARTE3_SPIM3_SPIS3_TWIM3_TWIS3);
impl_twis!(UARTETWISPI0, TWIS0, UARTE0_SPIM0_SPIS0_TWIM0_TWIS0);
impl_twis!(UARTETWISPI1, TWIS1, UARTE1_SPIM1_SPIS1_TWIM1_TWIS1);
impl_twis!(UARTETWISPI2, TWIS2, UARTE2_SPIM2_SPIS2_TWIM2_TWIS2);
impl_twis!(UARTETWISPI3, TWIS3, UARTE3_SPIM3_SPIS3_TWIM3_TWIS3);
impl_pwm!(PWM0, PWM0, PWM0);
impl_pwm!(PWM1, PWM1, PWM1);
impl_pwm!(PWM2, PWM2, PWM2);
@ -336,14 +349,14 @@ impl_ppi_channel!(PPI_CH13, 13 => configurable);
impl_ppi_channel!(PPI_CH14, 14 => configurable);
impl_ppi_channel!(PPI_CH15, 15 => configurable);
impl_saadc_input!(P0_13, ANALOGINPUT0);
impl_saadc_input!(P0_14, ANALOGINPUT1);
impl_saadc_input!(P0_15, ANALOGINPUT2);
impl_saadc_input!(P0_16, ANALOGINPUT3);
impl_saadc_input!(P0_17, ANALOGINPUT4);
impl_saadc_input!(P0_18, ANALOGINPUT5);
impl_saadc_input!(P0_19, ANALOGINPUT6);
impl_saadc_input!(P0_20, ANALOGINPUT7);
impl_saadc_input!(P0_13, ANALOG_INPUT0);
impl_saadc_input!(P0_14, ANALOG_INPUT1);
impl_saadc_input!(P0_15, ANALOG_INPUT2);
impl_saadc_input!(P0_16, ANALOG_INPUT3);
impl_saadc_input!(P0_17, ANALOG_INPUT4);
impl_saadc_input!(P0_18, ANALOG_INPUT5);
impl_saadc_input!(P0_19, ANALOG_INPUT6);
impl_saadc_input!(P0_20, ANALOG_INPUT7);
pub mod irqs {
use embassy_cortex_m::interrupt::_export::declare;

View File

@ -574,7 +574,7 @@ mod eh1 {
type Error = Infallible;
}
impl<'d, T: Pin> embedded_hal_1::digital::blocking::InputPin for Input<'d, T> {
impl<'d, T: Pin> embedded_hal_1::digital::InputPin for Input<'d, T> {
fn is_high(&self) -> Result<bool, Self::Error> {
Ok(self.is_high())
}
@ -588,7 +588,7 @@ mod eh1 {
type Error = Infallible;
}
impl<'d, T: Pin> embedded_hal_1::digital::blocking::OutputPin for Output<'d, T> {
impl<'d, T: Pin> embedded_hal_1::digital::OutputPin for Output<'d, T> {
fn set_high(&mut self) -> Result<(), Self::Error> {
Ok(self.set_high())
}
@ -598,7 +598,7 @@ mod eh1 {
}
}
impl<'d, T: Pin> embedded_hal_1::digital::blocking::StatefulOutputPin for Output<'d, T> {
impl<'d, T: Pin> embedded_hal_1::digital::StatefulOutputPin for Output<'d, T> {
fn is_set_high(&self) -> Result<bool, Self::Error> {
Ok(self.is_set_high())
}
@ -615,7 +615,7 @@ mod eh1 {
/// Implement [`InputPin`] for [`Flex`];
///
/// If the pin is not in input mode the result is unspecified.
impl<'d, T: Pin> embedded_hal_1::digital::blocking::InputPin for Flex<'d, T> {
impl<'d, T: Pin> embedded_hal_1::digital::InputPin for Flex<'d, T> {
fn is_high(&self) -> Result<bool, Self::Error> {
Ok(self.is_high())
}
@ -625,7 +625,7 @@ mod eh1 {
}
}
impl<'d, T: Pin> embedded_hal_1::digital::blocking::OutputPin for Flex<'d, T> {
impl<'d, T: Pin> embedded_hal_1::digital::OutputPin for Flex<'d, T> {
fn set_high(&mut self) -> Result<(), Self::Error> {
Ok(self.set_high())
}
@ -635,7 +635,7 @@ mod eh1 {
}
}
impl<'d, T: Pin> embedded_hal_1::digital::blocking::StatefulOutputPin for Flex<'d, T> {
impl<'d, T: Pin> embedded_hal_1::digital::StatefulOutputPin for Flex<'d, T> {
fn is_set_high(&self) -> Result<bool, Self::Error> {
Ok(self.is_set_high())
}

View File

@ -1,10 +1,9 @@
use core::convert::Infallible;
use core::future::Future;
use core::future::{poll_fn, Future};
use core::task::{Context, Poll};
use embassy_hal_common::{impl_peripheral, Peripheral, PeripheralRef};
use embassy_hal_common::{impl_peripheral, into_ref, Peripheral, PeripheralRef};
use embassy_sync::waitqueue::AtomicWaker;
use futures::future::poll_fn;
use crate::gpio::sealed::Pin as _;
use crate::gpio::{AnyPin, Flex, Input, Output, Pin as GpioPin};
@ -149,7 +148,7 @@ impl Iterator for BitIter {
/// GPIOTE channel driver in input mode
pub struct InputChannel<'d, C: Channel, T: GpioPin> {
ch: C,
ch: PeripheralRef<'d, C>,
pin: Input<'d, T>,
}
@ -163,7 +162,9 @@ impl<'d, C: Channel, T: GpioPin> Drop for InputChannel<'d, C, T> {
}
impl<'d, C: Channel, T: GpioPin> InputChannel<'d, C, T> {
pub fn new(ch: C, pin: Input<'d, T>, polarity: InputChannelPolarity) -> Self {
pub fn new(ch: impl Peripheral<P = C> + 'd, pin: Input<'d, T>, polarity: InputChannelPolarity) -> Self {
into_ref!(ch);
let g = regs();
let num = ch.number();
@ -216,7 +217,7 @@ impl<'d, C: Channel, T: GpioPin> InputChannel<'d, C, T> {
/// GPIOTE channel driver in output mode
pub struct OutputChannel<'d, C: Channel, T: GpioPin> {
ch: C,
ch: PeripheralRef<'d, C>,
_pin: Output<'d, T>,
}
@ -230,7 +231,8 @@ impl<'d, C: Channel, T: GpioPin> Drop for OutputChannel<'d, C, T> {
}
impl<'d, C: Channel, T: GpioPin> OutputChannel<'d, C, T> {
pub fn new(ch: C, pin: Output<'d, T>, polarity: OutputChannelPolarity) -> Self {
pub fn new(ch: impl Peripheral<P = C> + 'd, pin: Output<'d, T>, polarity: OutputChannelPolarity) -> Self {
into_ref!(ch);
let g = regs();
let num = ch.number();
@ -458,7 +460,7 @@ mod eh1 {
type Error = Infallible;
}
impl<'d, C: Channel, T: GpioPin> embedded_hal_1::digital::blocking::InputPin for InputChannel<'d, C, T> {
impl<'d, C: Channel, T: GpioPin> embedded_hal_1::digital::InputPin for InputChannel<'d, C, T> {
fn is_high(&self) -> Result<bool, Self::Error> {
Ok(self.pin.is_high())
}
@ -469,72 +471,73 @@ mod eh1 {
}
}
cfg_if::cfg_if! {
if #[cfg(all(feature = "unstable-traits", feature = "nightly"))] {
use futures::FutureExt;
#[cfg(all(feature = "unstable-traits", feature = "nightly"))]
mod eha {
use futures::FutureExt;
impl<'d, T: GpioPin> embedded_hal_async::digital::Wait for Input<'d, T> {
type WaitForHighFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
use super::*;
fn wait_for_high<'a>(&'a mut self) -> Self::WaitForHighFuture<'a> {
self.wait_for_high().map(Ok)
}
impl<'d, T: GpioPin> embedded_hal_async::digital::Wait for Input<'d, T> {
type WaitForHighFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
type WaitForLowFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
fn wait_for_low<'a>(&'a mut self) -> Self::WaitForLowFuture<'a> {
self.wait_for_low().map(Ok)
}
type WaitForRisingEdgeFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
fn wait_for_rising_edge<'a>(&'a mut self) -> Self::WaitForRisingEdgeFuture<'a> {
self.wait_for_rising_edge().map(Ok)
}
type WaitForFallingEdgeFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
fn wait_for_falling_edge<'a>(&'a mut self) -> Self::WaitForFallingEdgeFuture<'a> {
self.wait_for_falling_edge().map(Ok)
}
type WaitForAnyEdgeFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
fn wait_for_any_edge<'a>(&'a mut self) -> Self::WaitForAnyEdgeFuture<'a> {
self.wait_for_any_edge().map(Ok)
}
fn wait_for_high<'a>(&'a mut self) -> Self::WaitForHighFuture<'a> {
self.wait_for_high().map(Ok)
}
impl<'d, T: GpioPin> embedded_hal_async::digital::Wait for Flex<'d, T> {
type WaitForHighFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
type WaitForLowFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
fn wait_for_high<'a>(&'a mut self) -> Self::WaitForHighFuture<'a> {
self.wait_for_high().map(Ok)
}
fn wait_for_low<'a>(&'a mut self) -> Self::WaitForLowFuture<'a> {
self.wait_for_low().map(Ok)
}
type WaitForLowFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
type WaitForRisingEdgeFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
fn wait_for_low<'a>(&'a mut self) -> Self::WaitForLowFuture<'a> {
self.wait_for_low().map(Ok)
}
fn wait_for_rising_edge<'a>(&'a mut self) -> Self::WaitForRisingEdgeFuture<'a> {
self.wait_for_rising_edge().map(Ok)
}
type WaitForRisingEdgeFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
type WaitForFallingEdgeFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
fn wait_for_rising_edge<'a>(&'a mut self) -> Self::WaitForRisingEdgeFuture<'a> {
self.wait_for_rising_edge().map(Ok)
}
fn wait_for_falling_edge<'a>(&'a mut self) -> Self::WaitForFallingEdgeFuture<'a> {
self.wait_for_falling_edge().map(Ok)
}
type WaitForFallingEdgeFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
type WaitForAnyEdgeFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
fn wait_for_falling_edge<'a>(&'a mut self) -> Self::WaitForFallingEdgeFuture<'a> {
self.wait_for_falling_edge().map(Ok)
}
fn wait_for_any_edge<'a>(&'a mut self) -> Self::WaitForAnyEdgeFuture<'a> {
self.wait_for_any_edge().map(Ok)
}
}
type WaitForAnyEdgeFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
impl<'d, T: GpioPin> embedded_hal_async::digital::Wait for Flex<'d, T> {
type WaitForHighFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
fn wait_for_any_edge<'a>(&'a mut self) -> Self::WaitForAnyEdgeFuture<'a> {
self.wait_for_any_edge().map(Ok)
}
fn wait_for_high<'a>(&'a mut self) -> Self::WaitForHighFuture<'a> {
self.wait_for_high().map(Ok)
}
type WaitForLowFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
fn wait_for_low<'a>(&'a mut self) -> Self::WaitForLowFuture<'a> {
self.wait_for_low().map(Ok)
}
type WaitForRisingEdgeFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
fn wait_for_rising_edge<'a>(&'a mut self) -> Self::WaitForRisingEdgeFuture<'a> {
self.wait_for_rising_edge().map(Ok)
}
type WaitForFallingEdgeFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
fn wait_for_falling_edge<'a>(&'a mut self) -> Self::WaitForFallingEdgeFuture<'a> {
self.wait_for_falling_edge().map(Ok)
}
type WaitForAnyEdgeFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
fn wait_for_any_edge<'a>(&'a mut self) -> Self::WaitForAnyEdgeFuture<'a> {
self.wait_for_any_edge().map(Ok)
}
}
}

View File

@ -43,7 +43,7 @@
//! mutable slices always reside in RAM.
#![no_std]
#![cfg_attr(feature = "nightly", feature(generic_associated_types, type_alias_impl_trait))]
#![cfg_attr(feature = "nightly", feature(type_alias_impl_trait))]
#[cfg(not(any(
feature = "nrf51",
@ -76,6 +76,14 @@ pub mod gpio;
pub mod gpiote;
#[cfg(not(any(feature = "_nrf5340", feature = "_nrf9160")))]
pub mod nvmc;
#[cfg(any(
feature = "nrf52810",
feature = "nrf52811",
feature = "nrf52833",
feature = "nrf52840",
feature = "_nrf9160"
))]
pub mod pdm;
pub mod ppi;
#[cfg(not(any(feature = "nrf52805", feature = "nrf52820", feature = "_nrf5340-net")))]
pub mod pwm;
@ -88,10 +96,12 @@ pub mod rng;
#[cfg(not(any(feature = "nrf52820", feature = "_nrf5340-net")))]
pub mod saadc;
pub mod spim;
pub mod spis;
#[cfg(not(any(feature = "_nrf5340", feature = "_nrf9160")))]
pub mod temp;
pub mod timer;
pub mod twim;
pub mod twis;
pub mod uarte;
#[cfg(any(
feature = "_nrf5340-app",
@ -259,5 +269,12 @@ pub fn init(config: config::Config) -> Peripherals {
#[cfg(feature = "_time-driver")]
time_driver::init(config.time_interrupt_priority);
// Disable UARTE (enabled by default for some reason)
#[cfg(feature = "_nrf9160")]
unsafe {
(*pac::UARTE0::ptr()).enable.write(|w| w.enable().disabled());
(*pac::UARTE1::ptr()).enable.write(|w| w.enable().disabled());
}
peripherals
}

242
embassy-nrf/src/pdm.rs Normal file
View File

@ -0,0 +1,242 @@
//! PDM mirophone interface
use core::marker::PhantomData;
use core::sync::atomic::{compiler_fence, Ordering};
use core::task::Poll;
use embassy_hal_common::drop::OnDrop;
use embassy_hal_common::{into_ref, PeripheralRef};
use embassy_sync::waitqueue::AtomicWaker;
use futures::future::poll_fn;
use crate::chip::EASY_DMA_SIZE;
use crate::gpio::sealed::Pin;
use crate::gpio::{AnyPin, Pin as GpioPin};
use crate::interrupt::{self, InterruptExt};
use crate::peripherals::PDM;
use crate::{pac, Peripheral};
/// PDM microphone interface
pub struct Pdm<'d> {
irq: PeripheralRef<'d, interrupt::PDM>,
phantom: PhantomData<&'d PDM>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub enum Error {
BufferTooLong,
BufferZeroLength,
NotRunning,
}
static WAKER: AtomicWaker = AtomicWaker::new();
static DUMMY_BUFFER: [i16; 1] = [0; 1];
impl<'d> Pdm<'d> {
/// Create PDM driver
pub fn new(
pdm: impl Peripheral<P = PDM> + 'd,
irq: impl Peripheral<P = interrupt::PDM> + 'd,
clk: impl Peripheral<P = impl GpioPin> + 'd,
din: impl Peripheral<P = impl GpioPin> + 'd,
config: Config,
) -> Self {
into_ref!(clk, din);
Self::new_inner(pdm, irq, clk.map_into(), din.map_into(), config)
}
fn new_inner(
_pdm: impl Peripheral<P = PDM> + 'd,
irq: impl Peripheral<P = interrupt::PDM> + 'd,
clk: PeripheralRef<'d, AnyPin>,
din: PeripheralRef<'d, AnyPin>,
config: Config,
) -> Self {
into_ref!(irq);
let r = Self::regs();
// setup gpio pins
din.conf().write(|w| w.input().set_bit());
r.psel.din.write(|w| unsafe { w.bits(din.psel_bits()) });
clk.set_low();
clk.conf().write(|w| w.dir().output());
r.psel.clk.write(|w| unsafe { w.bits(clk.psel_bits()) });
// configure
// use default for
// - gain right
// - gain left
// - clk
// - ratio
r.mode.write(|w| {
w.edge().bit(config.edge == Edge::LeftRising);
w.operation().bit(config.operation_mode == OperationMode::Mono);
w
});
r.gainl.write(|w| w.gainl().default_gain());
r.gainr.write(|w| w.gainr().default_gain());
// IRQ
irq.disable();
irq.set_handler(|_| {
let r = Self::regs();
r.intenclr.write(|w| w.end().clear());
WAKER.wake();
});
irq.enable();
r.enable.write(|w| w.enable().set_bit());
Self {
phantom: PhantomData,
irq,
}
}
/// Start sampling microphon data into a dummy buffer
/// Usefull to start the microphon and keep it active between recording samples
pub async fn start(&mut self) {
let r = Self::regs();
// start dummy sampling because microphon needs some setup time
r.sample
.ptr
.write(|w| unsafe { w.sampleptr().bits(DUMMY_BUFFER.as_ptr() as u32) });
r.sample
.maxcnt
.write(|w| unsafe { w.buffsize().bits(DUMMY_BUFFER.len() as _) });
r.tasks_start.write(|w| w.tasks_start().set_bit());
}
/// Stop sampling microphon data inta a dummy buffer
pub async fn stop(&mut self) {
let r = Self::regs();
r.tasks_stop.write(|w| w.tasks_stop().set_bit());
r.events_started.reset();
}
pub async fn sample(&mut self, buffer: &mut [i16]) -> Result<(), Error> {
if buffer.len() == 0 {
return Err(Error::BufferZeroLength);
}
if buffer.len() > EASY_DMA_SIZE {
return Err(Error::BufferTooLong);
}
let r = Self::regs();
if r.events_started.read().events_started().bit_is_clear() {
return Err(Error::NotRunning);
}
let drop = OnDrop::new(move || {
r.intenclr.write(|w| w.end().clear());
r.events_stopped.reset();
// reset to dummy buffer
r.sample
.ptr
.write(|w| unsafe { w.sampleptr().bits(DUMMY_BUFFER.as_ptr() as u32) });
r.sample
.maxcnt
.write(|w| unsafe { w.buffsize().bits(DUMMY_BUFFER.len() as _) });
while r.events_stopped.read().bits() == 0 {}
});
// setup user buffer
let ptr = buffer.as_ptr();
let len = buffer.len();
r.sample.ptr.write(|w| unsafe { w.sampleptr().bits(ptr as u32) });
r.sample.maxcnt.write(|w| unsafe { w.buffsize().bits(len as _) });
// wait till the current sample is finished and the user buffer sample is started
Self::wait_for_sample().await;
// reset the buffer back to the dummy buffer
r.sample
.ptr
.write(|w| unsafe { w.sampleptr().bits(DUMMY_BUFFER.as_ptr() as u32) });
r.sample
.maxcnt
.write(|w| unsafe { w.buffsize().bits(DUMMY_BUFFER.len() as _) });
// wait till the user buffer is sampled
Self::wait_for_sample().await;
drop.defuse();
Ok(())
}
async fn wait_for_sample() {
let r = Self::regs();
r.events_end.reset();
r.intenset.write(|w| w.end().set());
compiler_fence(Ordering::SeqCst);
poll_fn(|cx| {
WAKER.register(cx.waker());
if r.events_end.read().events_end().bit_is_set() {
return Poll::Ready(());
}
Poll::Pending
})
.await;
compiler_fence(Ordering::SeqCst);
}
fn regs() -> &'static pac::pdm::RegisterBlock {
unsafe { &*pac::PDM::ptr() }
}
}
/// PDM microphone driver Config
pub struct Config {
/// Use stero or mono operation
pub operation_mode: OperationMode,
/// On which edge the left channel should be samples
pub edge: Edge,
}
impl Default for Config {
fn default() -> Self {
Self {
operation_mode: OperationMode::Mono,
edge: Edge::LeftFalling,
}
}
}
#[derive(PartialEq)]
pub enum OperationMode {
Mono,
Stereo,
}
#[derive(PartialEq)]
pub enum Edge {
LeftRising,
LeftFalling,
}
impl<'d> Drop for Pdm<'d> {
fn drop(&mut self) {
let r = Self::regs();
r.tasks_stop.write(|w| w.tasks_stop().set_bit());
self.irq.disable();
r.enable.write(|w| w.enable().disabled());
r.psel.din.reset();
r.psel.clk.reset();
}
}

View File

@ -1,10 +1,10 @@
//! Quadrature decoder interface
use core::future::poll_fn;
use core::task::Poll;
use embassy_hal_common::{into_ref, PeripheralRef};
use embassy_sync::waitqueue::AtomicWaker;
use futures::future::poll_fn;
use crate::gpio::sealed::Pin as _;
use crate::gpio::{AnyPin, Pin as GpioPin};

View File

@ -1,11 +1,11 @@
#![macro_use]
use core::future::poll_fn;
use core::ptr;
use core::task::Poll;
use embassy_hal_common::drop::DropBomb;
use embassy_hal_common::{into_ref, PeripheralRef};
use futures::future::poll_fn;
use crate::gpio::{self, Pin as GpioPin};
use crate::interrupt::{Interrupt, InterruptExt};

View File

@ -1,3 +1,4 @@
use core::future::poll_fn;
use core::ptr;
use core::sync::atomic::{AtomicPtr, Ordering};
use core::task::Poll;
@ -5,7 +6,6 @@ use core::task::Poll;
use embassy_hal_common::drop::OnDrop;
use embassy_hal_common::{into_ref, PeripheralRef};
use embassy_sync::waitqueue::AtomicWaker;
use futures::future::poll_fn;
use crate::interrupt::InterruptExt;
use crate::peripherals::RNG;

View File

@ -1,11 +1,12 @@
#![macro_use]
use core::future::poll_fn;
use core::sync::atomic::{compiler_fence, Ordering};
use core::task::Poll;
use embassy_hal_common::drop::OnDrop;
use embassy_hal_common::{impl_peripheral, into_ref, PeripheralRef};
use embassy_sync::waitqueue::AtomicWaker;
use futures::future::poll_fn;
use pac::{saadc, SAADC};
use saadc::ch::config::{GAIN_A, REFSEL_A, RESP_A, TACQ_A};
// We treat the positive and negative channels with the same enum values to keep our type tidy and given they are the same
@ -219,7 +220,13 @@ impl<'d, const N: usize> Saadc<'d, N> {
}
/// One shot sampling. The buffer must be the same size as the number of channels configured.
/// The sampling is stopped prior to returning in order to reduce power consumption (power
/// consumption remains higher if sampling is not stopped explicitly). Cancellation will
/// also cause the sampling to be stopped.
pub async fn sample(&mut self, buf: &mut [i16; N]) {
// In case the future is dropped, stop the task and wait for it to end.
OnDrop::new(Self::stop_sampling_immediately);
let r = Self::regs();
// Set up the DMA
@ -270,6 +277,12 @@ impl<'d, const N: usize> Saadc<'d, N> {
/// taken to acquire the samples into a single buffer. You should measure the
/// time taken by the callback and set the sample buffer size accordingly.
/// Exceeding this time can lead to samples becoming dropped.
///
/// The sampling is stopped prior to returning in order to reduce power consumption (power
/// consumption remains higher if sampling is not stopped explicitly), and to
/// free the buffers from being used by the peripheral. Cancellation will
/// also cause the sampling to be stopped.
pub async fn run_task_sampler<S, T: TimerInstance, const N0: usize>(
&mut self,
timer: &mut T,
@ -321,6 +334,9 @@ impl<'d, const N: usize> Saadc<'d, N> {
I: FnMut(),
S: FnMut(&[[i16; N]]) -> SamplerState,
{
// In case the future is dropped, stop the task and wait for it to end.
OnDrop::new(Self::stop_sampling_immediately);
let r = Self::regs();
// Establish mode and sample rate
@ -404,6 +420,19 @@ impl<'d, const N: usize> Saadc<'d, N> {
})
.await;
}
// Stop sampling and wait for it to stop in a blocking fashion
fn stop_sampling_immediately() {
let r = Self::regs();
compiler_fence(Ordering::SeqCst);
r.events_stopped.reset();
r.tasks_stop.write(|w| unsafe { w.bits(1) });
while r.events_stopped.read().bits() == 0 {}
r.events_stopped.reset();
}
}
impl<'d> Saadc<'d, 1> {

View File

@ -1,12 +1,12 @@
#![macro_use]
use core::future::poll_fn;
use core::sync::atomic::{compiler_fence, Ordering};
use core::task::Poll;
use embassy_embedded_hal::SetConfig;
use embassy_hal_common::{into_ref, PeripheralRef};
pub use embedded_hal_02::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3};
use futures::future::poll_fn;
pub use pac::spim0::frequency::FREQUENCY_A as Frequency;
use crate::chip::FORCE_COPY_BUFFER_SIZE;
@ -446,25 +446,25 @@ mod eh1 {
type Error = Error;
}
impl<'d, T: Instance> embedded_hal_1::spi::blocking::SpiBusFlush for Spim<'d, T> {
impl<'d, T: Instance> embedded_hal_1::spi::SpiBusFlush for Spim<'d, T> {
fn flush(&mut self) -> Result<(), Self::Error> {
Ok(())
}
}
impl<'d, T: Instance> embedded_hal_1::spi::blocking::SpiBusRead<u8> for Spim<'d, T> {
impl<'d, T: Instance> embedded_hal_1::spi::SpiBusRead<u8> for Spim<'d, T> {
fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
self.blocking_transfer(words, &[])
}
}
impl<'d, T: Instance> embedded_hal_1::spi::blocking::SpiBusWrite<u8> for Spim<'d, T> {
impl<'d, T: Instance> embedded_hal_1::spi::SpiBusWrite<u8> for Spim<'d, T> {
fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {
self.blocking_write(words)
}
}
impl<'d, T: Instance> embedded_hal_1::spi::blocking::SpiBus<u8> for Spim<'d, T> {
impl<'d, T: Instance> embedded_hal_1::spi::SpiBus<u8> for Spim<'d, T> {
fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> {
self.blocking_transfer(read, write)
}
@ -475,49 +475,47 @@ mod eh1 {
}
}
cfg_if::cfg_if! {
if #[cfg(all(feature = "unstable-traits", feature = "nightly"))] {
use core::future::Future;
#[cfg(all(feature = "unstable-traits", feature = "nightly"))]
mod eha {
use core::future::Future;
impl<'d, T: Instance> embedded_hal_async::spi::SpiBusFlush for Spim<'d, T> {
type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
use super::*;
fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> {
async move { Ok(()) }
}
impl<'d, T: Instance> embedded_hal_async::spi::SpiBusFlush for Spim<'d, T> {
type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> {
async move { Ok(()) }
}
}
impl<'d, T: Instance> embedded_hal_async::spi::SpiBusRead<u8> for Spim<'d, T> {
type ReadFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
fn read<'a>(&'a mut self, words: &'a mut [u8]) -> Self::ReadFuture<'a> {
self.read(words)
}
}
impl<'d, T: Instance> embedded_hal_async::spi::SpiBusWrite<u8> for Spim<'d, T> {
type WriteFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
fn write<'a>(&'a mut self, data: &'a [u8]) -> Self::WriteFuture<'a> {
self.write(data)
}
}
impl<'d, T: Instance> embedded_hal_async::spi::SpiBus<u8> for Spim<'d, T> {
type TransferFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
fn transfer<'a>(&'a mut self, rx: &'a mut [u8], tx: &'a [u8]) -> Self::TransferFuture<'a> {
self.transfer(rx, tx)
}
impl<'d, T: Instance> embedded_hal_async::spi::SpiBusRead<u8> for Spim<'d, T> {
type ReadFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
type TransferInPlaceFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
fn read<'a>(&'a mut self, words: &'a mut [u8]) -> Self::ReadFuture<'a> {
self.read(words)
}
}
impl<'d, T: Instance> embedded_hal_async::spi::SpiBusWrite<u8> for Spim<'d, T> {
type WriteFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
fn write<'a>(&'a mut self, data: &'a [u8]) -> Self::WriteFuture<'a> {
self.write(data)
}
}
impl<'d, T: Instance> embedded_hal_async::spi::SpiBus<u8> for Spim<'d, T> {
type TransferFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
fn transfer<'a>(&'a mut self, rx: &'a mut [u8], tx: &'a [u8]) -> Self::TransferFuture<'a> {
self.transfer(rx, tx)
}
type TransferInPlaceFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
fn transfer_in_place<'a>(
&'a mut self,
words: &'a mut [u8],
) -> Self::TransferInPlaceFuture<'a> {
self.transfer_in_place(words)
}
fn transfer_in_place<'a>(&'a mut self, words: &'a mut [u8]) -> Self::TransferInPlaceFuture<'a> {
self.transfer_in_place(words)
}
}
}

539
embassy-nrf/src/spis.rs Normal file
View File

@ -0,0 +1,539 @@
#![macro_use]
use core::future::poll_fn;
use core::sync::atomic::{compiler_fence, Ordering};
use core::task::Poll;
use embassy_embedded_hal::SetConfig;
use embassy_hal_common::{into_ref, PeripheralRef};
pub use embedded_hal_02::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3};
use crate::chip::FORCE_COPY_BUFFER_SIZE;
use crate::gpio::sealed::Pin as _;
use crate::gpio::{self, AnyPin, Pin as GpioPin};
use crate::interrupt::{Interrupt, InterruptExt};
use crate::util::{slice_in_ram_or, slice_ptr_parts, slice_ptr_parts_mut};
use crate::{pac, Peripheral};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub enum Error {
TxBufferTooLong,
RxBufferTooLong,
/// EasyDMA can only read from data memory, read only buffers in flash will fail.
DMABufferNotInDataMemory,
}
/// Interface for the SPIS peripheral using EasyDMA to offload the transmission and reception workload.
///
/// For more details about EasyDMA, consult the module documentation.
pub struct Spis<'d, T: Instance> {
_p: PeripheralRef<'d, T>,
}
#[non_exhaustive]
pub struct Config {
pub mode: Mode,
pub orc: u8,
pub def: u8,
pub auto_acquire: bool,
}
impl Default for Config {
fn default() -> Self {
Self {
mode: MODE_0,
orc: 0x00,
def: 0x00,
auto_acquire: true,
}
}
}
impl<'d, T: Instance> Spis<'d, T> {
pub fn new(
spis: impl Peripheral<P = T> + 'd,
irq: impl Peripheral<P = T::Interrupt> + 'd,
cs: impl Peripheral<P = impl GpioPin> + 'd,
sck: impl Peripheral<P = impl GpioPin> + 'd,
miso: impl Peripheral<P = impl GpioPin> + 'd,
mosi: impl Peripheral<P = impl GpioPin> + 'd,
config: Config,
) -> Self {
into_ref!(cs, sck, miso, mosi);
Self::new_inner(
spis,
irq,
cs.map_into(),
sck.map_into(),
Some(miso.map_into()),
Some(mosi.map_into()),
config,
)
}
pub fn new_txonly(
spis: impl Peripheral<P = T> + 'd,
irq: impl Peripheral<P = T::Interrupt> + 'd,
cs: impl Peripheral<P = impl GpioPin> + 'd,
sck: impl Peripheral<P = impl GpioPin> + 'd,
miso: impl Peripheral<P = impl GpioPin> + 'd,
config: Config,
) -> Self {
into_ref!(cs, sck, miso);
Self::new_inner(
spis,
irq,
cs.map_into(),
sck.map_into(),
Some(miso.map_into()),
None,
config,
)
}
pub fn new_rxonly(
spis: impl Peripheral<P = T> + 'd,
irq: impl Peripheral<P = T::Interrupt> + 'd,
cs: impl Peripheral<P = impl GpioPin> + 'd,
sck: impl Peripheral<P = impl GpioPin> + 'd,
mosi: impl Peripheral<P = impl GpioPin> + 'd,
config: Config,
) -> Self {
into_ref!(cs, sck, mosi);
Self::new_inner(
spis,
irq,
cs.map_into(),
sck.map_into(),
None,
Some(mosi.map_into()),
config,
)
}
fn new_inner(
spis: impl Peripheral<P = T> + 'd,
irq: impl Peripheral<P = T::Interrupt> + 'd,
cs: PeripheralRef<'d, AnyPin>,
sck: PeripheralRef<'d, AnyPin>,
miso: Option<PeripheralRef<'d, AnyPin>>,
mosi: Option<PeripheralRef<'d, AnyPin>>,
config: Config,
) -> Self {
compiler_fence(Ordering::SeqCst);
into_ref!(spis, irq, cs, sck);
let r = T::regs();
// Configure pins.
sck.conf().write(|w| w.input().connect().drive().h0h1());
r.psel.sck.write(|w| unsafe { w.bits(sck.psel_bits()) });
cs.conf().write(|w| w.input().connect().drive().h0h1());
r.psel.csn.write(|w| unsafe { w.bits(cs.psel_bits()) });
if let Some(mosi) = &mosi {
mosi.conf().write(|w| w.input().connect().drive().h0h1());
r.psel.mosi.write(|w| unsafe { w.bits(mosi.psel_bits()) });
}
if let Some(miso) = &miso {
miso.conf().write(|w| w.dir().output().drive().h0h1());
r.psel.miso.write(|w| unsafe { w.bits(miso.psel_bits()) });
}
// Enable SPIS instance.
r.enable.write(|w| w.enable().enabled());
// Configure mode.
let mode = config.mode;
r.config.write(|w| {
match mode {
MODE_0 => {
w.order().msb_first();
w.cpol().active_high();
w.cpha().leading();
}
MODE_1 => {
w.order().msb_first();
w.cpol().active_high();
w.cpha().trailing();
}
MODE_2 => {
w.order().msb_first();
w.cpol().active_low();
w.cpha().leading();
}
MODE_3 => {
w.order().msb_first();
w.cpol().active_low();
w.cpha().trailing();
}
}
w
});
// Set over-read character.
let orc = config.orc;
r.orc.write(|w| unsafe { w.orc().bits(orc) });
// Set default character.
let def = config.def;
r.def.write(|w| unsafe { w.def().bits(def) });
// Configure auto-acquire on 'transfer end' event.
if config.auto_acquire {
r.shorts.write(|w| w.end_acquire().bit(true));
}
// Disable all events interrupts.
r.intenclr.write(|w| unsafe { w.bits(0xFFFF_FFFF) });
irq.set_handler(Self::on_interrupt);
irq.unpend();
irq.enable();
Self { _p: spis }
}
fn on_interrupt(_: *mut ()) {
let r = T::regs();
let s = T::state();
if r.events_end.read().bits() != 0 {
s.waker.wake();
r.intenclr.write(|w| w.end().clear());
}
if r.events_acquired.read().bits() != 0 {
s.waker.wake();
r.intenclr.write(|w| w.acquired().clear());
}
}
fn prepare(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(), Error> {
slice_in_ram_or(tx, Error::DMABufferNotInDataMemory)?;
// NOTE: RAM slice check for rx is not necessary, as a mutable
// slice can only be built from data located in RAM.
compiler_fence(Ordering::SeqCst);
let r = T::regs();
// Set up the DMA write.
let (ptr, len) = slice_ptr_parts(tx);
r.txd.ptr.write(|w| unsafe { w.ptr().bits(ptr as _) });
r.txd.maxcnt.write(|w| unsafe { w.maxcnt().bits(len as _) });
// Set up the DMA read.
let (ptr, len) = slice_ptr_parts_mut(rx);
r.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr as _) });
r.rxd.maxcnt.write(|w| unsafe { w.maxcnt().bits(len as _) });
// Reset end event.
r.events_end.reset();
// Release the semaphore.
r.tasks_release.write(|w| unsafe { w.bits(1) });
Ok(())
}
fn blocking_inner_from_ram(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(usize, usize), Error> {
compiler_fence(Ordering::SeqCst);
let r = T::regs();
// Acquire semaphore.
if r.semstat.read().bits() != 1 {
r.events_acquired.reset();
r.tasks_acquire.write(|w| unsafe { w.bits(1) });
// Wait until CPU has acquired the semaphore.
while r.semstat.read().bits() != 1 {}
}
self.prepare(rx, tx)?;
// Wait for 'end' event.
while r.events_end.read().bits() == 0 {}
let n_rx = r.rxd.amount.read().bits() as usize;
let n_tx = r.txd.amount.read().bits() as usize;
compiler_fence(Ordering::SeqCst);
Ok((n_rx, n_tx))
}
fn blocking_inner(&mut self, rx: &mut [u8], tx: &[u8]) -> Result<(usize, usize), Error> {
match self.blocking_inner_from_ram(rx, tx) {
Ok(n) => Ok(n),
Err(Error::DMABufferNotInDataMemory) => {
trace!("Copying SPIS tx buffer into RAM for DMA");
let tx_ram_buf = &mut [0; FORCE_COPY_BUFFER_SIZE][..tx.len()];
tx_ram_buf.copy_from_slice(tx);
self.blocking_inner_from_ram(rx, tx_ram_buf)
}
Err(error) => Err(error),
}
}
async fn async_inner_from_ram(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(usize, usize), Error> {
let r = T::regs();
let s = T::state();
// Clear status register.
r.status.write(|w| w.overflow().clear().overread().clear());
// Acquire semaphore.
if r.semstat.read().bits() != 1 {
// Reset and enable the acquire event.
r.events_acquired.reset();
r.intenset.write(|w| w.acquired().set());
// Request acquiring the SPIS semaphore.
r.tasks_acquire.write(|w| unsafe { w.bits(1) });
// Wait until CPU has acquired the semaphore.
poll_fn(|cx| {
s.waker.register(cx.waker());
if r.events_acquired.read().bits() == 1 {
r.events_acquired.reset();
return Poll::Ready(());
}
Poll::Pending
})
.await;
}
self.prepare(rx, tx)?;
// Wait for 'end' event.
r.intenset.write(|w| w.end().set());
poll_fn(|cx| {
s.waker.register(cx.waker());
if r.events_end.read().bits() != 0 {
r.events_end.reset();
return Poll::Ready(());
}
Poll::Pending
})
.await;
let n_rx = r.rxd.amount.read().bits() as usize;
let n_tx = r.txd.amount.read().bits() as usize;
compiler_fence(Ordering::SeqCst);
Ok((n_rx, n_tx))
}
async fn async_inner(&mut self, rx: &mut [u8], tx: &[u8]) -> Result<(usize, usize), Error> {
match self.async_inner_from_ram(rx, tx).await {
Ok(n) => Ok(n),
Err(Error::DMABufferNotInDataMemory) => {
trace!("Copying SPIS tx buffer into RAM for DMA");
let tx_ram_buf = &mut [0; FORCE_COPY_BUFFER_SIZE][..tx.len()];
tx_ram_buf.copy_from_slice(tx);
self.async_inner_from_ram(rx, tx_ram_buf).await
}
Err(error) => Err(error),
}
}
/// Reads data from the SPI bus without sending anything. Blocks until `cs` is deasserted.
/// Returns number of bytes read.
pub fn blocking_read(&mut self, data: &mut [u8]) -> Result<usize, Error> {
self.blocking_inner(data, &[]).map(|n| n.0)
}
/// Simultaneously sends and receives data. Blocks until the transmission is completed.
/// If necessary, the write buffer will be copied into RAM (see struct description for detail).
/// Returns number of bytes transferred `(n_rx, n_tx)`.
pub fn blocking_transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(usize, usize), Error> {
self.blocking_inner(read, write)
}
/// Same as [`blocking_transfer`](Spis::blocking_transfer) but will fail instead of copying data into RAM. Consult the module level documentation to learn more.
/// Returns number of bytes transferred `(n_rx, n_tx)`.
pub fn blocking_transfer_from_ram(&mut self, read: &mut [u8], write: &[u8]) -> Result<(usize, usize), Error> {
self.blocking_inner_from_ram(read, write)
}
/// Simultaneously sends and receives data.
/// Places the received data into the same buffer and blocks until the transmission is completed.
/// Returns number of bytes transferred.
pub fn blocking_transfer_in_place(&mut self, data: &mut [u8]) -> Result<usize, Error> {
self.blocking_inner_from_ram(data, data).map(|n| n.0)
}
/// Sends data, discarding any received data. Blocks until the transmission is completed.
/// If necessary, the write buffer will be copied into RAM (see struct description for detail).
/// Returns number of bytes written.
pub fn blocking_write(&mut self, data: &[u8]) -> Result<usize, Error> {
self.blocking_inner(&mut [], data).map(|n| n.1)
}
/// Same as [`blocking_write`](Spis::blocking_write) but will fail instead of copying data into RAM. Consult the module level documentation to learn more.
/// Returns number of bytes written.
pub fn blocking_write_from_ram(&mut self, data: &[u8]) -> Result<usize, Error> {
self.blocking_inner_from_ram(&mut [], data).map(|n| n.1)
}
/// Reads data from the SPI bus without sending anything.
/// Returns number of bytes read.
pub async fn read(&mut self, data: &mut [u8]) -> Result<usize, Error> {
self.async_inner(data, &[]).await.map(|n| n.0)
}
/// Simultaneously sends and receives data.
/// If necessary, the write buffer will be copied into RAM (see struct description for detail).
/// Returns number of bytes transferred `(n_rx, n_tx)`.
pub async fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(usize, usize), Error> {
self.async_inner(read, write).await
}
/// Same as [`transfer`](Spis::transfer) but will fail instead of copying data into RAM. Consult the module level documentation to learn more.
/// Returns number of bytes transferred `(n_rx, n_tx)`.
pub async fn transfer_from_ram(&mut self, read: &mut [u8], write: &[u8]) -> Result<(usize, usize), Error> {
self.async_inner_from_ram(read, write).await
}
/// Simultaneously sends and receives data. Places the received data into the same buffer.
/// Returns number of bytes transferred.
pub async fn transfer_in_place(&mut self, data: &mut [u8]) -> Result<usize, Error> {
self.async_inner_from_ram(data, data).await.map(|n| n.0)
}
/// Sends data, discarding any received data.
/// If necessary, the write buffer will be copied into RAM (see struct description for detail).
/// Returns number of bytes written.
pub async fn write(&mut self, data: &[u8]) -> Result<usize, Error> {
self.async_inner(&mut [], data).await.map(|n| n.1)
}
/// Same as [`write`](Spis::write) but will fail instead of copying data into RAM. Consult the module level documentation to learn more.
/// Returns number of bytes written.
pub async fn write_from_ram(&mut self, data: &[u8]) -> Result<usize, Error> {
self.async_inner_from_ram(&mut [], data).await.map(|n| n.1)
}
/// Checks if last transaction overread.
pub fn is_overread(&mut self) -> bool {
T::regs().status.read().overread().is_present()
}
/// Checks if last transaction overflowed.
pub fn is_overflow(&mut self) -> bool {
T::regs().status.read().overflow().is_present()
}
}
impl<'d, T: Instance> Drop for Spis<'d, T> {
fn drop(&mut self) {
trace!("spis drop");
// Disable
let r = T::regs();
r.enable.write(|w| w.enable().disabled());
gpio::deconfigure_pin(r.psel.sck.read().bits());
gpio::deconfigure_pin(r.psel.csn.read().bits());
gpio::deconfigure_pin(r.psel.miso.read().bits());
gpio::deconfigure_pin(r.psel.mosi.read().bits());
trace!("spis drop: done");
}
}
pub(crate) mod sealed {
use embassy_sync::waitqueue::AtomicWaker;
use super::*;
pub struct State {
pub waker: AtomicWaker,
}
impl State {
pub const fn new() -> Self {
Self {
waker: AtomicWaker::new(),
}
}
}
pub trait Instance {
fn regs() -> &'static pac::spis0::RegisterBlock;
fn state() -> &'static State;
}
}
pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static {
type Interrupt: Interrupt;
}
macro_rules! impl_spis {
($type:ident, $pac_type:ident, $irq:ident) => {
impl crate::spis::sealed::Instance for peripherals::$type {
fn regs() -> &'static pac::spis0::RegisterBlock {
unsafe { &*pac::$pac_type::ptr() }
}
fn state() -> &'static crate::spis::sealed::State {
static STATE: crate::spis::sealed::State = crate::spis::sealed::State::new();
&STATE
}
}
impl crate::spis::Instance for peripherals::$type {
type Interrupt = crate::interrupt::$irq;
}
};
}
// ====================
impl<'d, T: Instance> SetConfig for Spis<'d, T> {
type Config = Config;
fn set_config(&mut self, config: &Self::Config) {
let r = T::regs();
// Configure mode.
let mode = config.mode;
r.config.write(|w| {
match mode {
MODE_0 => {
w.order().msb_first();
w.cpol().active_high();
w.cpha().leading();
}
MODE_1 => {
w.order().msb_first();
w.cpol().active_high();
w.cpha().trailing();
}
MODE_2 => {
w.order().msb_first();
w.cpol().active_low();
w.cpha().leading();
}
MODE_3 => {
w.order().msb_first();
w.cpol().active_low();
w.cpha().trailing();
}
}
w
});
// Set over-read character.
let orc = config.orc;
r.orc.write(|w| unsafe { w.orc().bits(orc) });
// Set default character.
let def = config.def;
r.def.write(|w| unsafe { w.def().bits(def) });
// Configure auto-acquire on 'transfer end' event.
let auto_acquire = config.auto_acquire;
r.shorts.write(|w| w.end_acquire().bit(auto_acquire));
}
}

View File

@ -1,12 +1,12 @@
//! Temperature sensor interface.
use core::future::poll_fn;
use core::task::Poll;
use embassy_hal_common::drop::OnDrop;
use embassy_hal_common::{into_ref, PeripheralRef};
use embassy_sync::waitqueue::AtomicWaker;
use fixed::types::I30F2;
use futures::future::poll_fn;
use crate::interrupt::InterruptExt;
use crate::peripherals::TEMP;

View File

@ -243,22 +243,25 @@ impl Driver for RtcDriver {
})
}
fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) {
fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool {
critical_section::with(|cs| {
let n = alarm.id() as _;
let alarm = self.get_alarm(cs, alarm);
alarm.timestamp.set(timestamp);
let t = self.now();
// If alarm timestamp has passed, trigger it instantly.
if timestamp <= t {
self.trigger_alarm(n, cs);
return;
}
let r = rtc();
let t = self.now();
if timestamp <= t {
// If alarm timestamp has passed the alarm will not fire.
// Disarm the alarm and return `false` to indicate that.
r.intenclr.write(|w| unsafe { w.bits(compare_n(n)) });
alarm.timestamp.set(u64::MAX);
return false;
}
// If it hasn't triggered yet, setup it in the compare channel.
// Write the CC value regardless of whether we're going to enable it now or not.
@ -287,6 +290,8 @@ impl Driver for RtcDriver {
// It will be setup later by `next_period`.
r.intenclr.write(|w| unsafe { w.bits(compare_n(n)) });
}
true
})
}
}

View File

@ -1,12 +1,12 @@
#![macro_use]
use core::future::poll_fn;
use core::marker::PhantomData;
use core::task::Poll;
use embassy_hal_common::drop::OnDrop;
use embassy_hal_common::{into_ref, PeripheralRef};
use embassy_sync::waitqueue::AtomicWaker;
use futures::future::poll_fn;
use crate::interrupt::{Interrupt, InterruptExt};
use crate::ppi::{Event, Task};

View File

@ -6,7 +6,7 @@
//!
//! - nRF52832: Section 33
//! - nRF52840: Section 6.31
use core::future::Future;
use core::future::{poll_fn, Future};
use core::sync::atomic::compiler_fence;
use core::sync::atomic::Ordering::SeqCst;
use core::task::Poll;
@ -16,7 +16,6 @@ use embassy_hal_common::{into_ref, PeripheralRef};
use embassy_sync::waitqueue::AtomicWaker;
#[cfg(feature = "time")]
use embassy_time::{Duration, Instant};
use futures::future::poll_fn;
use crate::chip::{EASY_DMA_SIZE, FORCE_COPY_BUFFER_SIZE};
use crate::gpio::Pin as GpioPin;
@ -794,7 +793,7 @@ mod eh1 {
type Error = Error;
}
impl<'d, T: Instance> embedded_hal_1::i2c::blocking::I2c for Twim<'d, T> {
impl<'d, T: Instance> embedded_hal_1::i2c::I2c for Twim<'d, T> {
fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> {
self.blocking_read(address, buffer)
}
@ -824,57 +823,57 @@ mod eh1 {
fn transaction<'a>(
&mut self,
_address: u8,
_operations: &mut [embedded_hal_1::i2c::blocking::Operation<'a>],
_operations: &mut [embedded_hal_1::i2c::Operation<'a>],
) -> Result<(), Self::Error> {
todo!();
}
fn transaction_iter<'a, O>(&mut self, _address: u8, _operations: O) -> Result<(), Self::Error>
where
O: IntoIterator<Item = embedded_hal_1::i2c::blocking::Operation<'a>>,
O: IntoIterator<Item = embedded_hal_1::i2c::Operation<'a>>,
{
todo!();
}
}
}
cfg_if::cfg_if! {
if #[cfg(all(feature = "unstable-traits", feature = "nightly"))] {
impl<'d, T: Instance> embedded_hal_async::i2c::I2c for Twim<'d, T> {
type ReadFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
#[cfg(all(feature = "unstable-traits", feature = "nightly"))]
mod eha {
use super::*;
impl<'d, T: Instance> embedded_hal_async::i2c::I2c for Twim<'d, T> {
type ReadFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
fn read<'a>(&'a mut self, address: u8, buffer: &'a mut [u8]) -> Self::ReadFuture<'a> {
self.read(address, buffer)
}
fn read<'a>(&'a mut self, address: u8, buffer: &'a mut [u8]) -> Self::ReadFuture<'a> {
self.read(address, buffer)
}
type WriteFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
type WriteFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
fn write<'a>(&'a mut self, address: u8, bytes: &'a [u8]) -> Self::WriteFuture<'a> {
self.write(address, bytes)
}
fn write<'a>(&'a mut self, address: u8, bytes: &'a [u8]) -> Self::WriteFuture<'a> {
self.write(address, bytes)
}
type WriteReadFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
type WriteReadFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
fn write_read<'a>(
&'a mut self,
address: u8,
wr_buffer: &'a [u8],
rd_buffer: &'a mut [u8],
) -> Self::WriteReadFuture<'a> {
self.write_read(address, wr_buffer, rd_buffer)
}
fn write_read<'a>(
&'a mut self,
address: u8,
wr_buffer: &'a [u8],
rd_buffer: &'a mut [u8],
) -> Self::WriteReadFuture<'a> {
self.write_read(address, wr_buffer, rd_buffer)
}
type TransactionFuture<'a, 'b> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a, 'b: 'a;
type TransactionFuture<'a, 'b> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a, 'b: 'a;
fn transaction<'a, 'b>(
&'a mut self,
address: u8,
operations: &'a mut [embedded_hal_async::i2c::Operation<'b>],
) -> Self::TransactionFuture<'a, 'b> {
let _ = address;
let _ = operations;
async move { todo!() }
}
fn transaction<'a, 'b>(
&'a mut self,
address: u8,
operations: &'a mut [embedded_hal_async::i2c::Operation<'b>],
) -> Self::TransactionFuture<'a, 'b> {
let _ = address;
let _ = operations;
async move { todo!() }
}
}
}

759
embassy-nrf/src/twis.rs Normal file
View File

@ -0,0 +1,759 @@
#![macro_use]
//! HAL interface to the TWIS peripheral.
//!
//! See product specification:
//!
//! - nRF52832: Section 33
//! - nRF52840: Section 6.31
use core::future::{poll_fn, Future};
use core::sync::atomic::compiler_fence;
use core::sync::atomic::Ordering::SeqCst;
use core::task::Poll;
use embassy_hal_common::{into_ref, PeripheralRef};
use embassy_sync::waitqueue::AtomicWaker;
#[cfg(feature = "time")]
use embassy_time::{Duration, Instant};
use crate::chip::{EASY_DMA_SIZE, FORCE_COPY_BUFFER_SIZE};
use crate::gpio::Pin as GpioPin;
use crate::interrupt::{Interrupt, InterruptExt};
use crate::util::slice_in_ram_or;
use crate::{gpio, pac, Peripheral};
#[non_exhaustive]
pub struct Config {
pub address0: u8,
pub address1: Option<u8>,
pub orc: u8,
pub sda_high_drive: bool,
pub sda_pullup: bool,
pub scl_high_drive: bool,
pub scl_pullup: bool,
}
impl Default for Config {
fn default() -> Self {
Self {
address0: 0x55,
address1: None,
orc: 0x00,
scl_high_drive: false,
sda_pullup: false,
sda_high_drive: false,
scl_pullup: false,
}
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
enum Status {
Read,
Write,
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub enum Error {
TxBufferTooLong,
RxBufferTooLong,
DataNack,
Bus,
DMABufferNotInDataMemory,
Overflow,
OverRead,
Timeout,
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Command {
Read,
WriteRead(usize),
Write(usize),
}
/// Interface to a TWIS instance using EasyDMA to offload the transmission and reception workload.
///
/// For more details about EasyDMA, consult the module documentation.
pub struct Twis<'d, T: Instance> {
_p: PeripheralRef<'d, T>,
}
impl<'d, T: Instance> Twis<'d, T> {
pub fn new(
twis: impl Peripheral<P = T> + 'd,
irq: impl Peripheral<P = T::Interrupt> + 'd,
sda: impl Peripheral<P = impl GpioPin> + 'd,
scl: impl Peripheral<P = impl GpioPin> + 'd,
config: Config,
) -> Self {
into_ref!(twis, irq, sda, scl);
let r = T::regs();
// Configure pins
sda.conf().write(|w| {
w.dir().input();
w.input().connect();
if config.sda_high_drive {
w.drive().h0d1();
} else {
w.drive().s0d1();
}
if config.sda_pullup {
w.pull().pullup();
}
w
});
scl.conf().write(|w| {
w.dir().input();
w.input().connect();
if config.scl_high_drive {
w.drive().h0d1();
} else {
w.drive().s0d1();
}
if config.scl_pullup {
w.pull().pullup();
}
w
});
// Select pins.
r.psel.sda.write(|w| unsafe { w.bits(sda.psel_bits()) });
r.psel.scl.write(|w| unsafe { w.bits(scl.psel_bits()) });
// Enable TWIS instance.
r.enable.write(|w| w.enable().enabled());
// Disable all events interrupts
r.intenclr.write(|w| unsafe { w.bits(0xFFFF_FFFF) });
// Set address
r.address[0].write(|w| unsafe { w.address().bits(config.address0) });
r.config.write(|w| w.address0().enabled());
if let Some(address1) = config.address1 {
r.address[1].write(|w| unsafe { w.address().bits(address1) });
r.config.modify(|_r, w| w.address1().enabled());
}
// Set over-read character
r.orc.write(|w| unsafe { w.orc().bits(config.orc) });
// Generate suspend on read event
r.shorts.write(|w| w.read_suspend().enabled());
irq.set_handler(Self::on_interrupt);
irq.unpend();
irq.enable();
Self { _p: twis }
}
fn on_interrupt(_: *mut ()) {
let r = T::regs();
let s = T::state();
if r.events_read.read().bits() != 0 || r.events_write.read().bits() != 0 {
s.waker.wake();
r.intenclr.modify(|_r, w| w.read().clear().write().clear());
}
if r.events_stopped.read().bits() != 0 {
s.waker.wake();
r.intenclr.modify(|_r, w| w.stopped().clear());
}
if r.events_error.read().bits() != 0 {
s.waker.wake();
r.intenclr.modify(|_r, w| w.error().clear());
}
}
/// Set TX buffer, checking that it is in RAM and has suitable length.
unsafe fn set_tx_buffer(&mut self, buffer: &[u8]) -> Result<(), Error> {
slice_in_ram_or(buffer, Error::DMABufferNotInDataMemory)?;
if buffer.len() > EASY_DMA_SIZE {
return Err(Error::TxBufferTooLong);
}
let r = T::regs();
r.txd.ptr.write(|w|
// We're giving the register a pointer to the stack. Since we're
// waiting for the I2C transaction to end before this stack pointer
// becomes invalid, there's nothing wrong here.
//
// The PTR field is a full 32 bits wide and accepts the full range
// of values.
w.ptr().bits(buffer.as_ptr() as u32));
r.txd.maxcnt.write(|w|
// We're giving it the length of the buffer, so no danger of
// accessing invalid memory. We have verified that the length of the
// buffer fits in an `u8`, so the cast to `u8` is also fine.
//
// The MAXCNT field is 8 bits wide and accepts the full range of
// values.
w.maxcnt().bits(buffer.len() as _));
Ok(())
}
/// Set RX buffer, checking that it has suitable length.
unsafe fn set_rx_buffer(&mut self, buffer: &mut [u8]) -> Result<(), Error> {
// NOTE: RAM slice check is not necessary, as a mutable
// slice can only be built from data located in RAM.
if buffer.len() > EASY_DMA_SIZE {
return Err(Error::RxBufferTooLong);
}
let r = T::regs();
r.rxd.ptr.write(|w|
// We're giving the register a pointer to the stack. Since we're
// waiting for the I2C transaction to end before this stack pointer
// becomes invalid, there's nothing wrong here.
//
// The PTR field is a full 32 bits wide and accepts the full range
// of values.
w.ptr().bits(buffer.as_mut_ptr() as u32));
r.rxd.maxcnt.write(|w|
// We're giving it the length of the buffer, so no danger of
// accessing invalid memory. We have verified that the length of the
// buffer fits in an `u8`, so the cast to the type of maxcnt
// is also fine.
//
// Note that that nrf52840 maxcnt is a wider
// type than a u8, so we use a `_` cast rather than a `u8` cast.
// The MAXCNT field is thus at least 8 bits wide and accepts the
// full range of values that fit in a `u8`.
w.maxcnt().bits(buffer.len() as _));
Ok(())
}
fn clear_errorsrc(&mut self) {
let r = T::regs();
r.errorsrc
.write(|w| w.overflow().bit(true).overread().bit(true).dnack().bit(true));
}
/// Returns matched address for latest command.
pub fn address_match(&self) -> u8 {
let r = T::regs();
r.address[r.match_.read().bits() as usize].read().address().bits()
}
/// Returns the index of the address matched in the latest command.
pub fn address_match_index(&self) -> usize {
T::regs().match_.read().bits() as _
}
/// Wait for read, write, stop or error
fn blocking_listen_wait(&mut self) -> Result<Status, Error> {
let r = T::regs();
loop {
if r.events_error.read().bits() != 0 {
r.events_error.reset();
r.tasks_stop.write(|w| unsafe { w.bits(1) });
while r.events_stopped.read().bits() == 0 {}
return Err(Error::Overflow);
}
if r.events_stopped.read().bits() != 0 {
r.events_stopped.reset();
return Err(Error::Bus);
}
if r.events_read.read().bits() != 0 {
r.events_read.reset();
return Ok(Status::Read);
}
if r.events_write.read().bits() != 0 {
r.events_write.reset();
return Ok(Status::Write);
}
}
}
/// Wait for stop, repeated start or error
fn blocking_listen_wait_end(&mut self, status: Status) -> Result<Command, Error> {
let r = T::regs();
loop {
// stop if an error occured
if r.events_error.read().bits() != 0 {
r.events_error.reset();
r.tasks_stop.write(|w| unsafe { w.bits(1) });
return Err(Error::Overflow);
} else if r.events_stopped.read().bits() != 0 {
r.events_stopped.reset();
return match status {
Status::Read => Ok(Command::Read),
Status::Write => {
let n = r.rxd.amount.read().bits() as usize;
Ok(Command::Write(n))
}
};
} else if r.events_read.read().bits() != 0 {
r.events_read.reset();
let n = r.rxd.amount.read().bits() as usize;
return Ok(Command::WriteRead(n));
}
}
}
/// Wait for stop or error
fn blocking_wait(&mut self) -> Result<usize, Error> {
let r = T::regs();
loop {
// stop if an error occured
if r.events_error.read().bits() != 0 {
r.events_error.reset();
r.tasks_stop.write(|w| unsafe { w.bits(1) });
let errorsrc = r.errorsrc.read();
if errorsrc.overread().is_detected() {
return Err(Error::OverRead);
} else if errorsrc.dnack().is_received() {
return Err(Error::DataNack);
} else {
return Err(Error::Bus);
}
} else if r.events_stopped.read().bits() != 0 {
r.events_stopped.reset();
let n = r.txd.amount.read().bits() as usize;
return Ok(n);
}
}
}
/// Wait for stop or error with timeout
#[cfg(feature = "time")]
fn blocking_wait_timeout(&mut self, timeout: Duration) -> Result<usize, Error> {
let r = T::regs();
let deadline = Instant::now() + timeout;
loop {
// stop if an error occured
if r.events_error.read().bits() != 0 {
r.events_error.reset();
r.tasks_stop.write(|w| unsafe { w.bits(1) });
let errorsrc = r.errorsrc.read();
if errorsrc.overread().is_detected() {
return Err(Error::OverRead);
} else if errorsrc.dnack().is_received() {
return Err(Error::DataNack);
} else {
return Err(Error::Bus);
}
} else if r.events_stopped.read().bits() != 0 {
r.events_stopped.reset();
let n = r.txd.amount.read().bits() as usize;
return Ok(n);
} else if Instant::now() > deadline {
r.tasks_stop.write(|w| unsafe { w.bits(1) });
return Err(Error::Timeout);
}
}
}
/// Wait for read, write, stop or error with timeout
#[cfg(feature = "time")]
fn blocking_listen_wait_timeout(&mut self, timeout: Duration) -> Result<Status, Error> {
let r = T::regs();
let deadline = Instant::now() + timeout;
loop {
if r.events_error.read().bits() != 0 {
r.events_error.reset();
r.tasks_stop.write(|w| unsafe { w.bits(1) });
while r.events_stopped.read().bits() == 0 {}
return Err(Error::Overflow);
}
if r.events_stopped.read().bits() != 0 {
r.events_stopped.reset();
return Err(Error::Bus);
}
if r.events_read.read().bits() != 0 {
r.events_read.reset();
return Ok(Status::Read);
}
if r.events_write.read().bits() != 0 {
r.events_write.reset();
return Ok(Status::Write);
}
if Instant::now() > deadline {
r.tasks_stop.write(|w| unsafe { w.bits(1) });
return Err(Error::Timeout);
}
}
}
/// Wait for stop, repeated start or error with timeout
#[cfg(feature = "time")]
fn blocking_listen_wait_end_timeout(&mut self, status: Status, timeout: Duration) -> Result<Command, Error> {
let r = T::regs();
let deadline = Instant::now() + timeout;
loop {
// stop if an error occured
if r.events_error.read().bits() != 0 {
r.events_error.reset();
r.tasks_stop.write(|w| unsafe { w.bits(1) });
return Err(Error::Overflow);
} else if r.events_stopped.read().bits() != 0 {
r.events_stopped.reset();
return match status {
Status::Read => Ok(Command::Read),
Status::Write => {
let n = r.rxd.amount.read().bits() as usize;
Ok(Command::Write(n))
}
};
} else if r.events_read.read().bits() != 0 {
r.events_read.reset();
let n = r.rxd.amount.read().bits() as usize;
return Ok(Command::WriteRead(n));
} else if Instant::now() > deadline {
r.tasks_stop.write(|w| unsafe { w.bits(1) });
return Err(Error::Timeout);
}
}
}
/// Wait for stop or error
fn async_wait(&mut self) -> impl Future<Output = Result<usize, Error>> {
poll_fn(move |cx| {
let r = T::regs();
let s = T::state();
s.waker.register(cx.waker());
// stop if an error occured
if r.events_error.read().bits() != 0 {
r.events_error.reset();
r.tasks_stop.write(|w| unsafe { w.bits(1) });
let errorsrc = r.errorsrc.read();
if errorsrc.overread().is_detected() {
return Poll::Ready(Err(Error::OverRead));
} else if errorsrc.dnack().is_received() {
return Poll::Ready(Err(Error::DataNack));
} else {
return Poll::Ready(Err(Error::Bus));
}
} else if r.events_stopped.read().bits() != 0 {
r.events_stopped.reset();
let n = r.txd.amount.read().bits() as usize;
return Poll::Ready(Ok(n));
}
Poll::Pending
})
}
/// Wait for read or write
fn async_listen_wait(&mut self) -> impl Future<Output = Result<Status, Error>> {
poll_fn(move |cx| {
let r = T::regs();
let s = T::state();
s.waker.register(cx.waker());
// stop if an error occured
if r.events_error.read().bits() != 0 {
r.events_error.reset();
r.tasks_stop.write(|w| unsafe { w.bits(1) });
return Poll::Ready(Err(Error::Overflow));
} else if r.events_read.read().bits() != 0 {
r.events_read.reset();
return Poll::Ready(Ok(Status::Read));
} else if r.events_write.read().bits() != 0 {
r.events_write.reset();
return Poll::Ready(Ok(Status::Write));
} else if r.events_stopped.read().bits() != 0 {
r.events_stopped.reset();
return Poll::Ready(Err(Error::Bus));
}
Poll::Pending
})
}
/// Wait for stop, repeated start or error
fn async_listen_wait_end(&mut self, status: Status) -> impl Future<Output = Result<Command, Error>> {
poll_fn(move |cx| {
let r = T::regs();
let s = T::state();
s.waker.register(cx.waker());
// stop if an error occured
if r.events_error.read().bits() != 0 {
r.events_error.reset();
r.tasks_stop.write(|w| unsafe { w.bits(1) });
return Poll::Ready(Err(Error::Overflow));
} else if r.events_stopped.read().bits() != 0 {
r.events_stopped.reset();
return match status {
Status::Read => Poll::Ready(Ok(Command::Read)),
Status::Write => {
let n = r.rxd.amount.read().bits() as usize;
Poll::Ready(Ok(Command::Write(n)))
}
};
} else if r.events_read.read().bits() != 0 {
r.events_read.reset();
let n = r.rxd.amount.read().bits() as usize;
return Poll::Ready(Ok(Command::WriteRead(n)));
}
Poll::Pending
})
}
fn setup_respond_from_ram(&mut self, buffer: &[u8], inten: bool) -> Result<(), Error> {
let r = T::regs();
compiler_fence(SeqCst);
// Set up the DMA write.
unsafe { self.set_tx_buffer(buffer)? };
// Clear events
r.events_stopped.reset();
r.events_error.reset();
self.clear_errorsrc();
if inten {
r.intenset.write(|w| w.stopped().set().error().set());
} else {
r.intenclr.write(|w| w.stopped().clear().error().clear());
}
// Start write operation.
r.tasks_preparetx.write(|w| unsafe { w.bits(1) });
r.tasks_resume.write(|w| unsafe { w.bits(1) });
Ok(())
}
fn setup_respond(&mut self, wr_buffer: &[u8], inten: bool) -> Result<(), Error> {
match self.setup_respond_from_ram(wr_buffer, inten) {
Ok(_) => Ok(()),
Err(Error::DMABufferNotInDataMemory) => {
trace!("Copying TWIS tx buffer into RAM for DMA");
let tx_ram_buf = &mut [0; FORCE_COPY_BUFFER_SIZE][..wr_buffer.len()];
tx_ram_buf.copy_from_slice(wr_buffer);
self.setup_respond_from_ram(&tx_ram_buf, inten)
}
Err(error) => Err(error),
}
}
fn setup_listen(&mut self, buffer: &mut [u8], inten: bool) -> Result<(), Error> {
let r = T::regs();
compiler_fence(SeqCst);
// Set up the DMA read.
unsafe { self.set_rx_buffer(buffer)? };
// Clear events
r.events_read.reset();
r.events_write.reset();
r.events_stopped.reset();
r.events_error.reset();
self.clear_errorsrc();
if inten {
r.intenset
.write(|w| w.stopped().set().error().set().read().set().write().set());
} else {
r.intenclr
.write(|w| w.stopped().clear().error().clear().read().clear().write().clear());
}
// Start read operation.
r.tasks_preparerx.write(|w| unsafe { w.bits(1) });
Ok(())
}
fn setup_listen_end(&mut self, inten: bool) -> Result<(), Error> {
let r = T::regs();
compiler_fence(SeqCst);
// Clear events
r.events_read.reset();
r.events_write.reset();
r.events_stopped.reset();
r.events_error.reset();
self.clear_errorsrc();
if inten {
r.intenset.write(|w| w.stopped().set().error().set().read().set());
} else {
r.intenclr.write(|w| w.stopped().clear().error().clear().read().clear());
}
Ok(())
}
/// Wait for commands from an I2C master.
/// `buffer` is provided in case master does a 'write' and is unused for 'read'.
/// The buffer must have a length of at most 255 bytes on the nRF52832
/// and at most 65535 bytes on the nRF52840.
/// To know which one of the addresses were matched, call `address_match` or `address_match_index`
pub fn blocking_listen(&mut self, buffer: &mut [u8]) -> Result<Command, Error> {
self.setup_listen(buffer, false)?;
let status = self.blocking_listen_wait()?;
if status == Status::Write {
self.setup_listen_end(false)?;
let command = self.blocking_listen_wait_end(status)?;
return Ok(command);
}
Ok(Command::Read)
}
/// Respond to an I2C master READ command.
/// Returns the number of bytes written.
/// The buffer must have a length of at most 255 bytes on the nRF52832
/// and at most 65535 bytes on the nRF52840.
pub fn blocking_respond_to_read(&mut self, buffer: &[u8]) -> Result<usize, Error> {
self.setup_respond(buffer, false)?;
self.blocking_wait()
}
/// Same as [`blocking_respond_to_read`](Twis::blocking_respond_to_read) but will fail instead of copying data into RAM.
/// Consult the module level documentation to learn more.
pub fn blocking_respond_to_read_from_ram(&mut self, buffer: &[u8]) -> Result<usize, Error> {
self.setup_respond_from_ram(buffer, false)?;
self.blocking_wait()
}
// ===========================================
/// Wait for commands from an I2C master, with timeout.
/// `buffer` is provided in case master does a 'write' and is unused for 'read'.
/// The buffer must have a length of at most 255 bytes on the nRF52832
/// and at most 65535 bytes on the nRF52840.
/// To know which one of the addresses were matched, call `address_match` or `address_match_index`
#[cfg(feature = "time")]
pub fn blocking_listen_timeout(&mut self, buffer: &mut [u8], timeout: Duration) -> Result<Command, Error> {
self.setup_listen(buffer, false)?;
let status = self.blocking_listen_wait_timeout(timeout)?;
if status == Status::Write {
self.setup_listen_end(false)?;
let command = self.blocking_listen_wait_end_timeout(status, timeout)?;
return Ok(command);
}
Ok(Command::Read)
}
/// Respond to an I2C master READ command with timeout.
/// Returns the number of bytes written.
/// See [`blocking_respond_to_read`].
#[cfg(feature = "time")]
pub fn blocking_respond_to_read_timeout(&mut self, buffer: &[u8], timeout: Duration) -> Result<usize, Error> {
self.setup_respond(buffer, false)?;
self.blocking_wait_timeout(timeout)
}
/// Same as [`blocking_respond_to_read_timeout`](Twis::blocking_respond_to_read_timeout) but will fail instead of copying data into RAM.
/// Consult the module level documentation to learn more.
#[cfg(feature = "time")]
pub fn blocking_respond_to_read_from_ram_timeout(
&mut self,
buffer: &[u8],
timeout: Duration,
) -> Result<usize, Error> {
self.setup_respond_from_ram(buffer, false)?;
self.blocking_wait_timeout(timeout)
}
// ===========================================
/// Wait asynchronously for commands from an I2C master.
/// `buffer` is provided in case master does a 'write' and is unused for 'read'.
/// The buffer must have a length of at most 255 bytes on the nRF52832
/// and at most 65535 bytes on the nRF52840.
/// To know which one of the addresses were matched, call `address_match` or `address_match_index`
pub async fn listen(&mut self, buffer: &mut [u8]) -> Result<Command, Error> {
self.setup_listen(buffer, true)?;
let status = self.async_listen_wait().await?;
if status == Status::Write {
self.setup_listen_end(true)?;
let command = self.async_listen_wait_end(status).await?;
return Ok(command);
}
Ok(Command::Read)
}
/// Respond to an I2C master READ command, asynchronously.
/// Returns the number of bytes written.
/// The buffer must have a length of at most 255 bytes on the nRF52832
/// and at most 65535 bytes on the nRF52840.
pub async fn respond_to_read(&mut self, buffer: &[u8]) -> Result<usize, Error> {
self.setup_respond(buffer, true)?;
self.async_wait().await
}
/// Same as [`respond_to_read`](Twis::respond_to_read) but will fail instead of copying data into RAM. Consult the module level documentation to learn more.
pub async fn respond_to_read_from_ram(&mut self, buffer: &[u8]) -> Result<usize, Error> {
self.setup_respond_from_ram(buffer, true)?;
self.async_wait().await
}
}
impl<'a, T: Instance> Drop for Twis<'a, T> {
fn drop(&mut self) {
trace!("twis drop");
// TODO: check for abort
// disable!
let r = T::regs();
r.enable.write(|w| w.enable().disabled());
gpio::deconfigure_pin(r.psel.sda.read().bits());
gpio::deconfigure_pin(r.psel.scl.read().bits());
trace!("twis drop: done");
}
}
pub(crate) mod sealed {
use super::*;
pub struct State {
pub waker: AtomicWaker,
}
impl State {
pub const fn new() -> Self {
Self {
waker: AtomicWaker::new(),
}
}
}
pub trait Instance {
fn regs() -> &'static pac::twis0::RegisterBlock;
fn state() -> &'static State;
}
}
pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static {
type Interrupt: Interrupt;
}
macro_rules! impl_twis {
($type:ident, $pac_type:ident, $irq:ident) => {
impl crate::twis::sealed::Instance for peripherals::$type {
fn regs() -> &'static pac::twis0::RegisterBlock {
unsafe { &*pac::$pac_type::ptr() }
}
fn state() -> &'static crate::twis::sealed::State {
static STATE: crate::twis::sealed::State = crate::twis::sealed::State::new();
&STATE
}
}
impl crate::twis::Instance for peripherals::$type {
type Interrupt = crate::interrupt::$irq;
}
};
}

View File

@ -13,12 +13,12 @@
//! memory may be used given that buffers are passed in directly to its read and write
//! methods.
use core::future::poll_fn;
use core::sync::atomic::{compiler_fence, Ordering};
use core::task::Poll;
use embassy_hal_common::drop::OnDrop;
use embassy_hal_common::{into_ref, PeripheralRef};
use futures::future::poll_fn;
use pac::uarte0::RegisterBlock;
// Re-export SVD variants to allow user to directly set values.
pub use pac::uarte0::{baudrate::BAUDRATE_A as Baudrate, config::PARITY_A as Parity};
@ -173,6 +173,61 @@ impl<'d, T: Instance> Uarte<'d, T> {
(self.tx, self.rx)
}
/// Split the Uarte into a transmitter and receiver that will
/// return on idle, which is determined as the time it takes
/// for two bytes to be received.
pub fn split_with_idle<U: TimerInstance>(
self,
timer: impl Peripheral<P = U> + 'd,
ppi_ch1: impl Peripheral<P = impl ConfigurableChannel + 'd> + 'd,
ppi_ch2: impl Peripheral<P = impl ConfigurableChannel + 'd> + 'd,
) -> (UarteTx<'d, T>, UarteRxWithIdle<'d, T, U>) {
let mut timer = Timer::new(timer);
into_ref!(ppi_ch1, ppi_ch2);
let r = T::regs();
// BAUDRATE register values are `baudrate * 2^32 / 16000000`
// source: https://devzone.nordicsemi.com/f/nordic-q-a/391/uart-baudrate-register-values
//
// We want to stop RX if line is idle for 2 bytes worth of time
// That is 20 bits (each byte is 1 start bit + 8 data bits + 1 stop bit)
// This gives us the amount of 16M ticks for 20 bits.
let baudrate = r.baudrate.read().baudrate().variant().unwrap();
let timeout = 0x8000_0000 / (baudrate as u32 / 40);
timer.set_frequency(Frequency::F16MHz);
timer.cc(0).write(timeout);
timer.cc(0).short_compare_clear();
timer.cc(0).short_compare_stop();
let mut ppi_ch1 = Ppi::new_one_to_two(
ppi_ch1.map_into(),
Event::from_reg(&r.events_rxdrdy),
timer.task_clear(),
timer.task_start(),
);
ppi_ch1.enable();
let mut ppi_ch2 = Ppi::new_one_to_one(
ppi_ch2.map_into(),
timer.cc(0).event_compare(),
Task::from_reg(&r.tasks_stoprx),
);
ppi_ch2.enable();
(
self.tx,
UarteRxWithIdle {
rx: self.rx,
timer,
ppi_ch1: ppi_ch1,
_ppi_ch2: ppi_ch2,
},
)
}
/// Return the endtx event for use with PPI
pub fn event_endtx(&self) -> Event {
let r = T::regs();
@ -597,6 +652,117 @@ impl<'a, T: Instance> Drop for UarteRx<'a, T> {
}
}
pub struct UarteRxWithIdle<'d, T: Instance, U: TimerInstance> {
rx: UarteRx<'d, T>,
timer: Timer<'d, U>,
ppi_ch1: Ppi<'d, AnyConfigurableChannel, 1, 2>,
_ppi_ch2: Ppi<'d, AnyConfigurableChannel, 1, 1>,
}
impl<'d, T: Instance, U: TimerInstance> UarteRxWithIdle<'d, T, U> {
pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> {
self.ppi_ch1.disable();
self.rx.read(buffer).await
}
pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> {
self.ppi_ch1.disable();
self.rx.blocking_read(buffer)
}
pub async fn read_until_idle(&mut self, buffer: &mut [u8]) -> Result<usize, Error> {
if buffer.len() == 0 {
return Err(Error::BufferZeroLength);
}
if buffer.len() > EASY_DMA_SIZE {
return Err(Error::BufferTooLong);
}
let ptr = buffer.as_ptr();
let len = buffer.len();
let r = T::regs();
let s = T::state();
self.ppi_ch1.enable();
let drop = OnDrop::new(|| {
self.timer.stop();
r.intenclr.write(|w| w.endrx().clear());
r.events_rxto.reset();
r.tasks_stoprx.write(|w| unsafe { w.bits(1) });
while r.events_endrx.read().bits() == 0 {}
});
r.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr as u32) });
r.rxd.maxcnt.write(|w| unsafe { w.maxcnt().bits(len as _) });
r.events_endrx.reset();
r.intenset.write(|w| w.endrx().set());
compiler_fence(Ordering::SeqCst);
r.tasks_startrx.write(|w| unsafe { w.bits(1) });
poll_fn(|cx| {
s.endrx_waker.register(cx.waker());
if r.events_endrx.read().bits() != 0 {
return Poll::Ready(());
}
Poll::Pending
})
.await;
compiler_fence(Ordering::SeqCst);
let n = r.rxd.amount.read().amount().bits() as usize;
self.timer.stop();
r.events_rxstarted.reset();
drop.defuse();
Ok(n)
}
pub fn blocking_read_until_idle(&mut self, buffer: &mut [u8]) -> Result<usize, Error> {
if buffer.len() == 0 {
return Err(Error::BufferZeroLength);
}
if buffer.len() > EASY_DMA_SIZE {
return Err(Error::BufferTooLong);
}
let ptr = buffer.as_ptr();
let len = buffer.len();
let r = T::regs();
self.ppi_ch1.enable();
r.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr as u32) });
r.rxd.maxcnt.write(|w| unsafe { w.maxcnt().bits(len as _) });
r.events_endrx.reset();
r.intenclr.write(|w| w.endrx().clear());
compiler_fence(Ordering::SeqCst);
r.tasks_startrx.write(|w| unsafe { w.bits(1) });
while r.events_endrx.read().bits() == 0 {}
compiler_fence(Ordering::SeqCst);
let n = r.rxd.amount.read().amount().bits() as usize;
self.timer.stop();
r.events_rxstarted.reset();
Ok(n)
}
}
#[cfg(not(any(feature = "_nrf9160", feature = "nrf5340")))]
pub(crate) fn apply_workaround_for_enable_anomaly(_r: &crate::pac::uarte0::RegisterBlock) {
// Do nothing
@ -665,270 +831,6 @@ pub(crate) fn drop_tx_rx(r: &pac::uarte0::RegisterBlock, s: &sealed::State) {
}
}
/// Interface to an UARTE peripheral that uses an additional timer and two PPI channels,
/// allowing it to implement the ReadUntilIdle trait.
pub struct UarteWithIdle<'d, U: Instance, T: TimerInstance> {
tx: UarteTx<'d, U>,
rx: UarteRxWithIdle<'d, U, T>,
}
impl<'d, U: Instance, T: TimerInstance> UarteWithIdle<'d, U, T> {
/// Create a new UARTE without hardware flow control
pub fn new(
uarte: impl Peripheral<P = U> + 'd,
timer: impl Peripheral<P = T> + 'd,
ppi_ch1: impl Peripheral<P = impl ConfigurableChannel + 'd> + 'd,
ppi_ch2: impl Peripheral<P = impl ConfigurableChannel + 'd> + 'd,
irq: impl Peripheral<P = U::Interrupt> + 'd,
rxd: impl Peripheral<P = impl GpioPin> + 'd,
txd: impl Peripheral<P = impl GpioPin> + 'd,
config: Config,
) -> Self {
into_ref!(rxd, txd);
Self::new_inner(
uarte,
timer,
ppi_ch1,
ppi_ch2,
irq,
rxd.map_into(),
txd.map_into(),
None,
None,
config,
)
}
/// Create a new UARTE with hardware flow control (RTS/CTS)
pub fn new_with_rtscts(
uarte: impl Peripheral<P = U> + 'd,
timer: impl Peripheral<P = T> + 'd,
ppi_ch1: impl Peripheral<P = impl ConfigurableChannel + 'd> + 'd,
ppi_ch2: impl Peripheral<P = impl ConfigurableChannel + 'd> + 'd,
irq: impl Peripheral<P = U::Interrupt> + 'd,
rxd: impl Peripheral<P = impl GpioPin> + 'd,
txd: impl Peripheral<P = impl GpioPin> + 'd,
cts: impl Peripheral<P = impl GpioPin> + 'd,
rts: impl Peripheral<P = impl GpioPin> + 'd,
config: Config,
) -> Self {
into_ref!(rxd, txd, cts, rts);
Self::new_inner(
uarte,
timer,
ppi_ch1,
ppi_ch2,
irq,
rxd.map_into(),
txd.map_into(),
Some(cts.map_into()),
Some(rts.map_into()),
config,
)
}
fn new_inner(
uarte: impl Peripheral<P = U> + 'd,
timer: impl Peripheral<P = T> + 'd,
ppi_ch1: impl Peripheral<P = impl ConfigurableChannel + 'd> + 'd,
ppi_ch2: impl Peripheral<P = impl ConfigurableChannel + 'd> + 'd,
irq: impl Peripheral<P = U::Interrupt> + 'd,
rxd: PeripheralRef<'d, AnyPin>,
txd: PeripheralRef<'d, AnyPin>,
cts: Option<PeripheralRef<'d, AnyPin>>,
rts: Option<PeripheralRef<'d, AnyPin>>,
config: Config,
) -> Self {
let baudrate = config.baudrate;
let (tx, rx) = Uarte::new_inner(uarte, irq, rxd, txd, cts, rts, config).split();
let mut timer = Timer::new(timer);
into_ref!(ppi_ch1, ppi_ch2);
let r = U::regs();
// BAUDRATE register values are `baudrate * 2^32 / 16000000`
// source: https://devzone.nordicsemi.com/f/nordic-q-a/391/uart-baudrate-register-values
//
// We want to stop RX if line is idle for 2 bytes worth of time
// That is 20 bits (each byte is 1 start bit + 8 data bits + 1 stop bit)
// This gives us the amount of 16M ticks for 20 bits.
let timeout = 0x8000_0000 / (baudrate as u32 / 40);
timer.set_frequency(Frequency::F16MHz);
timer.cc(0).write(timeout);
timer.cc(0).short_compare_clear();
timer.cc(0).short_compare_stop();
let mut ppi_ch1 = Ppi::new_one_to_two(
ppi_ch1.map_into(),
Event::from_reg(&r.events_rxdrdy),
timer.task_clear(),
timer.task_start(),
);
ppi_ch1.enable();
let mut ppi_ch2 = Ppi::new_one_to_one(
ppi_ch2.map_into(),
timer.cc(0).event_compare(),
Task::from_reg(&r.tasks_stoprx),
);
ppi_ch2.enable();
Self {
tx,
rx: UarteRxWithIdle {
rx,
timer,
ppi_ch1: ppi_ch1,
_ppi_ch2: ppi_ch2,
},
}
}
/// Split the Uarte into a transmitter and receiver, which is
/// particuarly useful when having two tasks correlating to
/// transmitting and receiving.
pub fn split(self) -> (UarteTx<'d, U>, UarteRxWithIdle<'d, U, T>) {
(self.tx, self.rx)
}
pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> {
self.rx.read(buffer).await
}
pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> {
self.tx.write(buffer).await
}
pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> {
self.rx.blocking_read(buffer)
}
pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> {
self.tx.blocking_write(buffer)
}
pub async fn read_until_idle(&mut self, buffer: &mut [u8]) -> Result<usize, Error> {
self.rx.read_until_idle(buffer).await
}
pub fn blocking_read_until_idle(&mut self, buffer: &mut [u8]) -> Result<usize, Error> {
self.rx.blocking_read_until_idle(buffer)
}
}
pub struct UarteRxWithIdle<'d, U: Instance, T: TimerInstance> {
rx: UarteRx<'d, U>,
timer: Timer<'d, T>,
ppi_ch1: Ppi<'d, AnyConfigurableChannel, 1, 2>,
_ppi_ch2: Ppi<'d, AnyConfigurableChannel, 1, 1>,
}
impl<'d, U: Instance, T: TimerInstance> UarteRxWithIdle<'d, U, T> {
pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> {
self.ppi_ch1.disable();
self.rx.read(buffer).await
}
pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> {
self.ppi_ch1.disable();
self.rx.blocking_read(buffer)
}
pub async fn read_until_idle(&mut self, buffer: &mut [u8]) -> Result<usize, Error> {
if buffer.len() == 0 {
return Err(Error::BufferZeroLength);
}
if buffer.len() > EASY_DMA_SIZE {
return Err(Error::BufferTooLong);
}
let ptr = buffer.as_ptr();
let len = buffer.len();
let r = U::regs();
let s = U::state();
self.ppi_ch1.enable();
let drop = OnDrop::new(|| {
self.timer.stop();
r.intenclr.write(|w| w.endrx().clear());
r.events_rxto.reset();
r.tasks_stoprx.write(|w| unsafe { w.bits(1) });
while r.events_endrx.read().bits() == 0 {}
});
r.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr as u32) });
r.rxd.maxcnt.write(|w| unsafe { w.maxcnt().bits(len as _) });
r.events_endrx.reset();
r.intenset.write(|w| w.endrx().set());
compiler_fence(Ordering::SeqCst);
r.tasks_startrx.write(|w| unsafe { w.bits(1) });
poll_fn(|cx| {
s.endrx_waker.register(cx.waker());
if r.events_endrx.read().bits() != 0 {
return Poll::Ready(());
}
Poll::Pending
})
.await;
compiler_fence(Ordering::SeqCst);
let n = r.rxd.amount.read().amount().bits() as usize;
self.timer.stop();
r.events_rxstarted.reset();
drop.defuse();
Ok(n)
}
pub fn blocking_read_until_idle(&mut self, buffer: &mut [u8]) -> Result<usize, Error> {
if buffer.len() == 0 {
return Err(Error::BufferZeroLength);
}
if buffer.len() > EASY_DMA_SIZE {
return Err(Error::BufferTooLong);
}
let ptr = buffer.as_ptr();
let len = buffer.len();
let r = U::regs();
self.ppi_ch1.enable();
r.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr as u32) });
r.rxd.maxcnt.write(|w| unsafe { w.maxcnt().bits(len as _) });
r.events_endrx.reset();
r.intenclr.write(|w| w.endrx().clear());
compiler_fence(Ordering::SeqCst);
r.tasks_startrx.write(|w| unsafe { w.bits(1) });
while r.events_endrx.read().bits() == 0 {}
compiler_fence(Ordering::SeqCst);
let n = r.rxd.amount.read().amount().bits() as usize;
self.timer.stop();
r.events_rxstarted.reset();
Ok(n)
}
}
pub(crate) mod sealed {
use core::sync::atomic::AtomicU8;
@ -1006,18 +908,6 @@ mod eh02 {
Ok(())
}
}
impl<'d, U: Instance, T: TimerInstance> embedded_hal_02::blocking::serial::Write<u8> for UarteWithIdle<'d, U, T> {
type Error = Error;
fn bwrite_all(&mut self, buffer: &[u8]) -> Result<(), Self::Error> {
self.blocking_write(buffer)
}
fn bflush(&mut self) -> Result<(), Self::Error> {
Ok(())
}
}
}
#[cfg(feature = "unstable-traits")]
@ -1040,7 +930,7 @@ mod eh1 {
type Error = Error;
}
impl<'d, T: Instance> embedded_hal_1::serial::blocking::Write for Uarte<'d, T> {
impl<'d, T: Instance> embedded_hal_1::serial::Write for Uarte<'d, T> {
fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> {
self.blocking_write(buffer)
}
@ -1054,7 +944,7 @@ mod eh1 {
type Error = Error;
}
impl<'d, T: Instance> embedded_hal_1::serial::blocking::Write for UarteTx<'d, T> {
impl<'d, T: Instance> embedded_hal_1::serial::Write for UarteTx<'d, T> {
fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> {
self.blocking_write(buffer)
}
@ -1067,84 +957,59 @@ mod eh1 {
impl<'d, T: Instance> embedded_hal_1::serial::ErrorType for UarteRx<'d, T> {
type Error = Error;
}
impl<'d, U: Instance, T: TimerInstance> embedded_hal_1::serial::ErrorType for UarteWithIdle<'d, U, T> {
type Error = Error;
}
}
cfg_if::cfg_if! {
if #[cfg(all(feature = "unstable-traits", feature = "nightly", feature = "_todo_embedded_hal_serial"))] {
use core::future::Future;
#[cfg(all(
feature = "unstable-traits",
feature = "nightly",
feature = "_todo_embedded_hal_serial"
))]
mod eha {
use core::future::Future;
impl<'d, T: Instance> embedded_hal_async::serial::Read for Uarte<'d, T> {
type ReadFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
use super::*;
fn read<'a>(&'a mut self, buffer: &'a mut [u8]) -> Self::ReadFuture<'a> {
self.read(buffer)
}
impl<'d, T: Instance> embedded_hal_async::serial::Read for Uarte<'d, T> {
type ReadFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
fn read<'a>(&'a mut self, buffer: &'a mut [u8]) -> Self::ReadFuture<'a> {
self.read(buffer)
}
}
impl<'d, T: Instance> embedded_hal_async::serial::Write for Uarte<'d, T> {
type WriteFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
fn write<'a>(&'a mut self, buffer: &'a [u8]) -> Self::WriteFuture<'a> {
self.write(buffer)
}
impl<'d, T: Instance> embedded_hal_async::serial::Write for Uarte<'d, T> {
type WriteFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
fn write<'a>(&'a mut self, buffer: &'a [u8]) -> Self::WriteFuture<'a> {
self.write(buffer)
}
fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> {
async move { Ok(()) }
}
}
type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
impl<'d, T: Instance> embedded_hal_async::serial::Write for UarteTx<'d, T> {
type WriteFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> {
async move { Ok(()) }
}
fn write<'a>(&'a mut self, buffer: &'a [u8]) -> Self::WriteFuture<'a> {
self.write(buffer)
}
impl<'d, T: Instance> embedded_hal_async::serial::Write for UarteTx<'d, T> {
type WriteFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
fn write<'a>(&'a mut self, buffer: &'a [u8]) -> Self::WriteFuture<'a> {
self.write(buffer)
}
type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> {
async move { Ok(()) }
}
fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> {
async move { Ok(()) }
}
}
impl<'d, T: Instance> embedded_hal_async::serial::Read for UarteRx<'d, T> {
type ReadFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
impl<'d, T: Instance> embedded_hal_async::serial::Read for UarteRx<'d, T> {
type ReadFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
fn read<'a>(&'a mut self, buffer: &'a mut [u8]) -> Self::ReadFuture<'a> {
self.read(buffer)
}
}
impl<'d, U: Instance, T: TimerInstance> embedded_hal_async::serial::Read
for UarteWithIdle<'d, U, T>
{
type ReadFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
fn read<'a>(&'a mut self, buffer: &'a mut [u8]) -> Self::ReadFuture<'a> {
self.read(buffer)
}
}
impl<'d, U: Instance, T: TimerInstance> embedded_hal_async::serial::Write
for UarteWithIdle<'d, U, T>
{
type WriteFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
fn write<'a>(&'a mut self, buffer: &'a [u8]) -> Self::WriteFuture<'a> {
self.write(buffer)
}
type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> {
async move { Ok(()) }
}
fn read<'a>(&'a mut self, buffer: &'a mut [u8]) -> Self::ReadFuture<'a> {
self.read(buffer)
}
}
}

View File

@ -1,5 +1,6 @@
#![macro_use]
use core::future::{poll_fn, Future};
use core::marker::PhantomData;
use core::mem::MaybeUninit;
use core::sync::atomic::{compiler_fence, AtomicBool, AtomicU32, Ordering};
@ -8,11 +9,8 @@ use core::task::Poll;
use cortex_m::peripheral::NVIC;
use embassy_hal_common::{into_ref, PeripheralRef};
use embassy_sync::waitqueue::AtomicWaker;
pub use embassy_usb;
use embassy_usb::driver::{self, EndpointError, Event, Unsupported};
use embassy_usb::types::{EndpointAddress, EndpointInfo, EndpointType, UsbDirection};
use futures::future::poll_fn;
use futures::Future;
use embassy_usb_driver as driver;
use embassy_usb_driver::{Direction, EndpointAddress, EndpointError, EndpointInfo, EndpointType, Event, Unsupported};
use pac::usbd::RegisterBlock;
use crate::interrupt::{Interrupt, InterruptExt};
@ -244,7 +242,7 @@ impl<'d, T: Instance, P: UsbSupply + 'd> driver::Driver<'d> for Driver<'d, T, P>
interval: u8,
) -> Result<Self::EndpointIn, driver::EndpointAllocError> {
let index = self.alloc_in.allocate(ep_type)?;
let ep_addr = EndpointAddress::from_parts(index, UsbDirection::In);
let ep_addr = EndpointAddress::from_parts(index, Direction::In);
Ok(Endpoint::new(EndpointInfo {
addr: ep_addr,
ep_type,
@ -260,7 +258,7 @@ impl<'d, T: Instance, P: UsbSupply + 'd> driver::Driver<'d> for Driver<'d, T, P>
interval: u8,
) -> Result<Self::EndpointOut, driver::EndpointAllocError> {
let index = self.alloc_out.allocate(ep_type)?;
let ep_addr = EndpointAddress::from_parts(index, UsbDirection::Out);
let ep_addr = EndpointAddress::from_parts(index, Direction::Out);
Ok(Endpoint::new(EndpointInfo {
addr: ep_addr,
ep_type,
@ -315,7 +313,7 @@ impl<'d, T: Instance, P: UsbSupply> driver::Bus for Bus<'d, T, P> {
}
})
.await;
regs.eventcause.write(|w| w.ready().set_bit()); // Write 1 to clear.
regs.eventcause.write(|w| w.ready().clear_bit_by_one());
errata::post_enable();
@ -369,24 +367,24 @@ impl<'d, T: Instance, P: UsbSupply> driver::Bus for Bus<'d, T, P> {
let r = regs.eventcause.read();
if r.isooutcrc().bit() {
regs.eventcause.write(|w| w.isooutcrc().set_bit());
regs.eventcause.write(|w| w.isooutcrc().detected());
trace!("USB event: isooutcrc");
}
if r.usbwuallowed().bit() {
regs.eventcause.write(|w| w.usbwuallowed().set_bit());
regs.eventcause.write(|w| w.usbwuallowed().allowed());
trace!("USB event: usbwuallowed");
}
if r.suspend().bit() {
regs.eventcause.write(|w| w.suspend().set_bit());
regs.eventcause.write(|w| w.suspend().detected());
regs.lowpower.write(|w| w.lowpower().low_power());
return Poll::Ready(Event::Suspend);
}
if r.resume().bit() {
regs.eventcause.write(|w| w.resume().set_bit());
regs.eventcause.write(|w| w.resume().detected());
return Poll::Ready(Event::Resume);
}
if r.ready().bit() {
regs.eventcause.write(|w| w.ready().set_bit());
regs.eventcause.write(|w| w.ready().ready());
trace!("USB event: ready");
}
@ -429,8 +427,8 @@ impl<'d, T: Instance, P: UsbSupply> driver::Bus for Bus<'d, T, P> {
let regs = T::regs();
let i = ep_addr.index();
match ep_addr.direction() {
UsbDirection::Out => regs.halted.epout[i].read().getstatus().is_halted(),
UsbDirection::In => regs.halted.epin[i].read().getstatus().is_halted(),
Direction::Out => regs.halted.epout[i].read().getstatus().is_halted(),
Direction::In => regs.halted.epin[i].read().getstatus().is_halted(),
}
}
@ -443,7 +441,7 @@ impl<'d, T: Instance, P: UsbSupply> driver::Bus for Bus<'d, T, P> {
debug!("endpoint_set_enabled {:?} {}", ep_addr, enabled);
match ep_addr.direction() {
UsbDirection::In => {
Direction::In => {
let mut was_enabled = false;
regs.epinen.modify(|r, w| {
let mut bits = r.bits();
@ -467,7 +465,7 @@ impl<'d, T: Instance, P: UsbSupply> driver::Bus for Bus<'d, T, P> {
In::waker(i).wake();
}
UsbDirection::Out => {
Direction::Out => {
regs.epouten.modify(|r, w| {
let mut bits = r.bits();
if enabled {
@ -514,7 +512,7 @@ impl<'d, T: Instance, P: UsbSupply> driver::Bus for Bus<'d, T, P> {
} else if r.resume().bit() {
Poll::Ready(())
} else if r.usbwuallowed().bit() {
regs.eventcause.write(|w| w.usbwuallowed().set_bit());
regs.eventcause.write(|w| w.usbwuallowed().allowed());
regs.dpdmvalue.write(|w| w.state().resume());
regs.tasks_dpdmdrive.write(|w| w.tasks_dpdmdrive().set_bit());

View File

@ -2,17 +2,18 @@
name = "embassy-rp"
version = "0.1.0"
edition = "2021"
license = "MIT OR Apache-2.0"
[package.metadata.embassy_docs]
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-rp-v$VERSION/embassy-rp/src/"
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-rp/src/"
features = ["nightly", "defmt", "unstable-pac", "unstable-traits"]
features = ["nightly", "defmt", "unstable-pac", "unstable-traits", "time-driver"]
flavors = [
{ name = "rp2040", target = "thumbv6m-none-eabi" },
]
[features]
defmt = ["dep:defmt", "embassy-usb?/defmt"]
defmt = ["dep:defmt", "embassy-usb-driver?/defmt"]
# Reexport the PAC for the currently enabled chip at `embassy_rp::pac`.
# This is unstable because semver-minor (non-breaking) releases of embassy-rp may major-bump (breaking) the PAC version.
@ -20,21 +21,28 @@ defmt = ["dep:defmt", "embassy-usb?/defmt"]
# There are no plans to make this stable.
unstable-pac = []
time-driver = []
rom-func-cache = []
intrinsics = []
rom-v2-intrinsics = []
# Enable nightly-only features
nightly = ["embassy-executor/nightly", "embedded-hal-1", "embedded-hal-async", "embassy-embedded-hal/nightly", "dep:embassy-usb"]
nightly = ["embassy-executor/nightly", "embedded-hal-1", "embedded-hal-async", "embassy-embedded-hal/nightly", "dep:embassy-usb-driver", "dep:embedded-io"]
# Implement embedded-hal 1.0 alpha traits.
# Implement embedded-hal-async traits if `nightly` is set as well.
unstable-traits = ["embedded-hal-1"]
unstable-traits = ["embedded-hal-1", "embedded-hal-nb"]
[dependencies]
embassy-sync = { version = "0.1.0", path = "../embassy-sync" }
embassy-executor = { version = "0.1.0", path = "../embassy-executor" }
embassy-time = { version = "0.1.0", path = "../embassy-time", features = [ "tick-1mhz" ] }
embassy-time = { version = "0.1.0", path = "../embassy-time", features = [ "tick-hz-1_000_000" ] }
embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
embassy-cortex-m = { version = "0.1.0", path = "../embassy-cortex-m", features = ["prio-bits-2"]}
embassy-hal-common = {version = "0.1.0", path = "../embassy-hal-common" }
embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" }
embassy-usb = {version = "0.1.0", path = "../embassy-usb", optional = true }
embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optional = true }
atomic-polyfill = "1.0.1"
defmt = { version = "0.3", optional = true }
log = { version = "0.4.14", optional = true }
@ -44,10 +52,14 @@ cortex-m-rt = ">=0.6.15,<0.8"
cortex-m = "0.7.6"
critical-section = "1.1"
futures = { version = "0.3.17", default-features = false, features = ["async-await"] }
chrono = { version = "0.4", default-features = false, optional = true }
embedded-io = { version = "0.3.1", features = ["async"], optional = true }
embedded-storage = { version = "0.3" }
rp2040-pac2 = { git = "https://github.com/embassy-rs/rp2040-pac2", rev="017e3c9007b2d3b6965f0d85b5bf8ce3fa6d7364", features = ["rt"] }
#rp2040-pac2 = { path = "../../rp2040-pac2", features = ["rt"] }
embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] }
embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.8", optional = true}
embedded-hal-async = { version = "0.1.0-alpha.1", optional = true}
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9", optional = true}
embedded-hal-async = { version = "=0.1.0-alpha.3", optional = true}
embedded-hal-nb = { version = "=1.0.0-alpha.1", optional = true}

173
embassy-rp/src/adc.rs Normal file
View File

@ -0,0 +1,173 @@
use core::future::poll_fn;
use core::marker::PhantomData;
use core::sync::atomic::{compiler_fence, Ordering};
use core::task::Poll;
use embassy_hal_common::into_ref;
use embassy_sync::waitqueue::AtomicWaker;
use embedded_hal_02::adc::{Channel, OneShot};
use crate::interrupt::{self, InterruptExt};
use crate::peripherals::ADC;
use crate::{pac, peripherals, Peripheral};
static WAKER: AtomicWaker = AtomicWaker::new();
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub enum Error {
// No errors for now
}
#[non_exhaustive]
pub struct Config {}
impl Default for Config {
fn default() -> Self {
Self {}
}
}
pub struct Adc<'d> {
phantom: PhantomData<&'d ADC>,
}
impl<'d> Adc<'d> {
#[inline]
fn regs() -> pac::adc::Adc {
pac::ADC
}
#[inline]
fn reset() -> pac::resets::regs::Peripherals {
let mut ret = pac::resets::regs::Peripherals::default();
ret.set_adc(true);
ret
}
pub fn new(
_inner: impl Peripheral<P = ADC> + 'd,
irq: impl Peripheral<P = interrupt::ADC_IRQ_FIFO> + 'd,
_config: Config,
) -> Self {
into_ref!(irq);
unsafe {
let reset = Self::reset();
crate::reset::reset(reset);
crate::reset::unreset_wait(reset);
let r = Self::regs();
// Enable ADC
r.cs().write(|w| w.set_en(true));
// Wait for ADC ready
while !r.cs().read().ready() {}
}
// Setup IRQ
irq.disable();
irq.set_handler(|_| unsafe {
let r = Self::regs();
r.inte().write(|w| w.set_fifo(false));
WAKER.wake();
});
irq.unpend();
irq.enable();
Self { phantom: PhantomData }
}
async fn wait_for_ready() {
let r = Self::regs();
unsafe {
r.inte().write(|w| w.set_fifo(true));
compiler_fence(Ordering::SeqCst);
poll_fn(|cx| {
WAKER.register(cx.waker());
if r.cs().read().ready() {
return Poll::Ready(());
}
Poll::Pending
})
.await;
}
}
pub async fn read<PIN: Channel<Adc<'d>, ID = u8>>(&mut self, _pin: &mut PIN) -> u16 {
let r = Self::regs();
unsafe {
r.cs().modify(|w| {
w.set_ainsel(PIN::channel());
w.set_start_once(true)
});
Self::wait_for_ready().await;
r.result().read().result().into()
}
}
pub async fn read_temperature(&mut self) -> u16 {
let r = Self::regs();
unsafe {
r.cs().modify(|w| w.set_ts_en(true));
if !r.cs().read().ready() {
Self::wait_for_ready().await;
}
r.cs().modify(|w| {
w.set_ainsel(4);
w.set_start_once(true)
});
Self::wait_for_ready().await;
r.result().read().result().into()
}
}
pub fn blocking_read<PIN: Channel<Adc<'d>, ID = u8>>(&mut self, _pin: &mut PIN) -> u16 {
let r = Self::regs();
unsafe {
r.cs().modify(|w| {
w.set_ainsel(PIN::channel());
w.set_start_once(true)
});
while !r.cs().read().ready() {}
r.result().read().result().into()
}
}
pub fn blocking_read_temperature(&mut self) -> u16 {
let r = Self::regs();
unsafe {
r.cs().modify(|w| w.set_ts_en(true));
while !r.cs().read().ready() {}
r.cs().modify(|w| {
w.set_ainsel(4);
w.set_start_once(true)
});
while !r.cs().read().ready() {}
r.result().read().result().into()
}
}
}
macro_rules! impl_pin {
($pin:ident, $channel:expr) => {
impl Channel<Adc<'static>> for peripherals::$pin {
type ID = u8;
fn channel() -> u8 {
$channel
}
}
};
}
impl_pin!(PIN_26, 0);
impl_pin!(PIN_27, 1);
impl_pin!(PIN_28, 2);
impl_pin!(PIN_29, 3);
impl<WORD, PIN> OneShot<Adc<'static>, WORD, PIN> for Adc<'static>
where
WORD: From<u16>,
PIN: Channel<Adc<'static>, ID = u8>,
{
type Error = ();
fn read(&mut self, pin: &mut PIN) -> nb::Result<WORD, Self::Error> {
Ok(self.blocking_read(pin).into())
}
}

View File

@ -122,7 +122,7 @@ pub(crate) fn clk_peri_freq() -> u32 {
125_000_000
}
pub(crate) fn _clk_rtc_freq() -> u32 {
pub(crate) fn clk_rtc_freq() -> u32 {
46875
}

View File

@ -1,3 +1,4 @@
use core::future::Future;
use core::pin::Pin;
use core::sync::atomic::{compiler_fence, Ordering};
use core::task::{Context, Poll};
@ -5,7 +6,6 @@ use core::task::{Context, Poll};
use embassy_cortex_m::interrupt::{Interrupt, InterruptExt};
use embassy_hal_common::{impl_peripheral, into_ref, Peripheral, PeripheralRef};
use embassy_sync::waitqueue::AtomicWaker;
use futures::Future;
use pac::dma::vals::DataSize;
use crate::pac::dma::vals;
@ -40,10 +40,10 @@ pub(crate) unsafe fn init() {
pub unsafe fn read<'a, C: Channel, W: Word>(
ch: impl Peripheral<P = C> + 'a,
from: *const W,
to: &mut [W],
to: *mut [W],
dreq: u8,
) -> Transfer<'a, C> {
let (to_ptr, len) = crate::dma::slice_ptr_parts_mut(to);
let (to_ptr, len) = crate::dma::slice_ptr_parts(to);
copy_inner(
ch,
from as *const u32,
@ -58,7 +58,7 @@ pub unsafe fn read<'a, C: Channel, W: Word>(
pub unsafe fn write<'a, C: Channel, W: Word>(
ch: impl Peripheral<P = C> + 'a,
from: &[W],
from: *const [W],
to: *mut W,
dreq: u8,
) -> Transfer<'a, C> {
@ -75,6 +75,25 @@ pub unsafe fn write<'a, C: Channel, W: Word>(
)
}
pub unsafe fn write_repeated<'a, C: Channel, W: Word>(
ch: impl Peripheral<P = C> + 'a,
to: *mut W,
len: usize,
dreq: u8,
) -> Transfer<'a, C> {
let dummy: u32 = 0;
copy_inner(
ch,
&dummy as *const u32,
to as *mut u32,
len,
W::size(),
false,
false,
dreq,
)
}
pub unsafe fn copy<'a, C: Channel, W: Word>(
ch: impl Peripheral<P = C> + 'a,
from: &[W],
@ -172,7 +191,7 @@ impl<'a, C: Channel> Future for Transfer<'a, C> {
}
}
const CHANNEL_COUNT: usize = 12;
pub(crate) const CHANNEL_COUNT: usize = 12;
const NEW_AW: AtomicWaker = AtomicWaker::new();
static CHANNEL_WAKERS: [AtomicWaker; CHANNEL_COUNT] = [NEW_AW; CHANNEL_COUNT];

463
embassy-rp/src/flash.rs Normal file
View File

@ -0,0 +1,463 @@
use core::marker::PhantomData;
use embassy_hal_common::Peripheral;
use embedded_storage::nor_flash::{
check_erase, check_read, check_write, ErrorType, MultiwriteNorFlash, NorFlash, NorFlashError, NorFlashErrorKind,
ReadNorFlash,
};
use crate::peripherals::FLASH;
pub const FLASH_BASE: usize = 0x10000000;
// **NOTE**:
//
// These limitations are currently enforced because of using the
// RP2040 boot-rom flash functions, that are optimized for flash compatibility
// rather than performance.
pub const PAGE_SIZE: usize = 256;
pub const WRITE_SIZE: usize = 1;
pub const READ_SIZE: usize = 1;
pub const ERASE_SIZE: usize = 4096;
/// Error type for NVMC operations.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Error {
/// Opration using a location not in flash.
OutOfBounds,
/// Unaligned operation or using unaligned buffers.
Unaligned,
Other,
}
impl From<NorFlashErrorKind> for Error {
fn from(e: NorFlashErrorKind) -> Self {
match e {
NorFlashErrorKind::NotAligned => Self::Unaligned,
NorFlashErrorKind::OutOfBounds => Self::OutOfBounds,
_ => Self::Other,
}
}
}
impl NorFlashError for Error {
fn kind(&self) -> NorFlashErrorKind {
match self {
Self::OutOfBounds => NorFlashErrorKind::OutOfBounds,
Self::Unaligned => NorFlashErrorKind::NotAligned,
Self::Other => NorFlashErrorKind::Other,
}
}
}
pub struct Flash<'d, T: Instance, const FLASH_SIZE: usize>(PhantomData<&'d mut T>);
impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, FLASH_SIZE> {
pub fn new(_flash: impl Peripheral<P = T> + 'd) -> Self {
Self(PhantomData)
}
pub fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> {
check_read(self, offset, bytes.len())?;
let flash_data = unsafe { core::slice::from_raw_parts((FLASH_BASE as u32 + offset) as *const u8, bytes.len()) };
bytes.copy_from_slice(flash_data);
Ok(())
}
pub fn capacity(&self) -> usize {
FLASH_SIZE
}
pub fn erase(&mut self, from: u32, to: u32) -> Result<(), Error> {
check_erase(self, from, to)?;
trace!(
"Erasing from 0x{:x} to 0x{:x}",
FLASH_BASE as u32 + from,
FLASH_BASE as u32 + to
);
let len = to - from;
unsafe { self.in_ram(|| ram_helpers::flash_range_erase(from, len, true)) };
Ok(())
}
pub fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> {
check_write(self, offset, bytes.len())?;
trace!("Writing {:?} bytes to 0x{:x}", bytes.len(), FLASH_BASE as u32 + offset);
let end_offset = offset as usize + bytes.len();
let padded_offset = (offset as *const u8).align_offset(PAGE_SIZE);
let start_padding = core::cmp::min(padded_offset, bytes.len());
// Pad in the beginning
if start_padding > 0 {
let start = PAGE_SIZE - padded_offset;
let end = start + start_padding;
let mut pad_buf = [0xFF_u8; PAGE_SIZE];
pad_buf[start..end].copy_from_slice(&bytes[..start_padding]);
let unaligned_offset = offset as usize - start;
unsafe { self.in_ram(|| ram_helpers::flash_range_program(unaligned_offset as u32, &pad_buf, true)) }
}
let remaining_len = bytes.len() - start_padding;
let end_padding = start_padding + PAGE_SIZE * (remaining_len / PAGE_SIZE);
// Write aligned slice of length in multiples of 256 bytes
// If the remaining bytes to be written is more than a full page.
if remaining_len >= PAGE_SIZE {
let mut aligned_offset = if start_padding > 0 {
offset as usize + padded_offset
} else {
offset as usize
};
if bytes.as_ptr() as usize >= 0x2000_0000 {
let aligned_data = &bytes[start_padding..end_padding];
unsafe { self.in_ram(|| ram_helpers::flash_range_program(aligned_offset as u32, aligned_data, true)) }
} else {
for chunk in bytes[start_padding..end_padding].chunks_exact(PAGE_SIZE) {
let mut ram_buf = [0xFF_u8; PAGE_SIZE];
ram_buf.copy_from_slice(chunk);
unsafe { self.in_ram(|| ram_helpers::flash_range_program(aligned_offset as u32, &ram_buf, true)) }
aligned_offset += PAGE_SIZE;
}
}
}
// Pad in the end
let rem_offset = (end_offset as *const u8).align_offset(PAGE_SIZE);
let rem_padding = remaining_len % PAGE_SIZE;
if rem_padding > 0 {
let mut pad_buf = [0xFF_u8; PAGE_SIZE];
pad_buf[..rem_padding].copy_from_slice(&bytes[end_padding..]);
let unaligned_offset = end_offset - (PAGE_SIZE - rem_offset);
unsafe { self.in_ram(|| ram_helpers::flash_range_program(unaligned_offset as u32, &pad_buf, true)) }
}
Ok(())
}
/// Make sure to uphold the contract points with rp2040-flash.
/// - interrupts must be disabled
/// - DMA must not access flash memory
unsafe fn in_ram(&mut self, operation: impl FnOnce()) {
let dma_status = &mut [false; crate::dma::CHANNEL_COUNT];
// TODO: Make sure CORE1 is paused during the entire duration of the RAM function
critical_section::with(|_| {
// Pause all DMA channels for the duration of the ram operation
for (number, status) in dma_status.iter_mut().enumerate() {
let ch = crate::pac::DMA.ch(number as _);
*status = ch.ctrl_trig().read().en();
if *status {
ch.ctrl_trig().modify(|w| w.set_en(false));
}
}
// Run our flash operation in RAM
operation();
// Re-enable previously enabled DMA channels
for (number, status) in dma_status.iter().enumerate() {
let ch = crate::pac::DMA.ch(number as _);
if *status {
ch.ctrl_trig().modify(|w| w.set_en(true));
}
}
});
}
}
impl<'d, T: Instance, const FLASH_SIZE: usize> ErrorType for Flash<'d, T, FLASH_SIZE> {
type Error = Error;
}
impl<'d, T: Instance, const FLASH_SIZE: usize> ReadNorFlash for Flash<'d, T, FLASH_SIZE> {
const READ_SIZE: usize = READ_SIZE;
fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {
self.read(offset, bytes)
}
fn capacity(&self) -> usize {
self.capacity()
}
}
impl<'d, T: Instance, const FLASH_SIZE: usize> MultiwriteNorFlash for Flash<'d, T, FLASH_SIZE> {}
impl<'d, T: Instance, const FLASH_SIZE: usize> NorFlash for Flash<'d, T, FLASH_SIZE> {
const WRITE_SIZE: usize = WRITE_SIZE;
const ERASE_SIZE: usize = ERASE_SIZE;
fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {
self.erase(from, to)
}
fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {
self.write(offset, bytes)
}
}
#[allow(dead_code)]
mod ram_helpers {
use core::marker::PhantomData;
use crate::rom_data;
#[repr(C)]
struct FlashFunctionPointers<'a> {
connect_internal_flash: unsafe extern "C" fn() -> (),
flash_exit_xip: unsafe extern "C" fn() -> (),
flash_range_erase: Option<unsafe extern "C" fn(addr: u32, count: usize, block_size: u32, block_cmd: u8) -> ()>,
flash_range_program: Option<unsafe extern "C" fn(addr: u32, data: *const u8, count: usize) -> ()>,
flash_flush_cache: unsafe extern "C" fn() -> (),
flash_enter_cmd_xip: unsafe extern "C" fn() -> (),
phantom: PhantomData<&'a ()>,
}
#[allow(unused)]
fn flash_function_pointers(erase: bool, write: bool) -> FlashFunctionPointers<'static> {
FlashFunctionPointers {
connect_internal_flash: rom_data::connect_internal_flash::ptr(),
flash_exit_xip: rom_data::flash_exit_xip::ptr(),
flash_range_erase: if erase {
Some(rom_data::flash_range_erase::ptr())
} else {
None
},
flash_range_program: if write {
Some(rom_data::flash_range_program::ptr())
} else {
None
},
flash_flush_cache: rom_data::flash_flush_cache::ptr(),
flash_enter_cmd_xip: rom_data::flash_enter_cmd_xip::ptr(),
phantom: PhantomData,
}
}
#[allow(unused)]
/// # Safety
///
/// `boot2` must contain a valid 2nd stage boot loader which can be called to re-initialize XIP mode
unsafe fn flash_function_pointers_with_boot2(erase: bool, write: bool, boot2: &[u32; 64]) -> FlashFunctionPointers {
let boot2_fn_ptr = (boot2 as *const u32 as *const u8).offset(1);
let boot2_fn: unsafe extern "C" fn() -> () = core::mem::transmute(boot2_fn_ptr);
FlashFunctionPointers {
connect_internal_flash: rom_data::connect_internal_flash::ptr(),
flash_exit_xip: rom_data::flash_exit_xip::ptr(),
flash_range_erase: if erase {
Some(rom_data::flash_range_erase::ptr())
} else {
None
},
flash_range_program: if write {
Some(rom_data::flash_range_program::ptr())
} else {
None
},
flash_flush_cache: rom_data::flash_flush_cache::ptr(),
flash_enter_cmd_xip: boot2_fn,
phantom: PhantomData,
}
}
/// Erase a flash range starting at `addr` with length `len`.
///
/// `addr` and `len` must be multiples of 4096
///
/// If `use_boot2` is `true`, a copy of the 2nd stage boot loader
/// is used to re-initialize the XIP engine after flashing.
///
/// # Safety
///
/// Nothing must access flash while this is running.
/// Usually this means:
/// - interrupts must be disabled
/// - 2nd core must be running code from RAM or ROM with interrupts disabled
/// - DMA must not access flash memory
///
/// `addr` and `len` parameters must be valid and are not checked.
pub unsafe fn flash_range_erase(addr: u32, len: u32, use_boot2: bool) {
let mut boot2 = [0u32; 256 / 4];
let ptrs = if use_boot2 {
rom_data::memcpy44(&mut boot2 as *mut _, super::FLASH_BASE as *const _, 256);
flash_function_pointers_with_boot2(true, false, &boot2)
} else {
flash_function_pointers(true, false)
};
core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
write_flash_inner(addr, len, None, &ptrs as *const FlashFunctionPointers);
}
/// Erase and rewrite a flash range starting at `addr` with data `data`.
///
/// `addr` and `data.len()` must be multiples of 4096
///
/// If `use_boot2` is `true`, a copy of the 2nd stage boot loader
/// is used to re-initialize the XIP engine after flashing.
///
/// # Safety
///
/// Nothing must access flash while this is running.
/// Usually this means:
/// - interrupts must be disabled
/// - 2nd core must be running code from RAM or ROM with interrupts disabled
/// - DMA must not access flash memory
///
/// `addr` and `len` parameters must be valid and are not checked.
pub unsafe fn flash_range_erase_and_program(addr: u32, data: &[u8], use_boot2: bool) {
let mut boot2 = [0u32; 256 / 4];
let ptrs = if use_boot2 {
rom_data::memcpy44(&mut boot2 as *mut _, super::FLASH_BASE as *const _, 256);
flash_function_pointers_with_boot2(true, true, &boot2)
} else {
flash_function_pointers(true, true)
};
core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
write_flash_inner(
addr,
data.len() as u32,
Some(data),
&ptrs as *const FlashFunctionPointers,
);
}
/// Write a flash range starting at `addr` with data `data`.
///
/// `addr` and `data.len()` must be multiples of 256
///
/// If `use_boot2` is `true`, a copy of the 2nd stage boot loader
/// is used to re-initialize the XIP engine after flashing.
///
/// # Safety
///
/// Nothing must access flash while this is running.
/// Usually this means:
/// - interrupts must be disabled
/// - 2nd core must be running code from RAM or ROM with interrupts disabled
/// - DMA must not access flash memory
///
/// `addr` and `len` parameters must be valid and are not checked.
pub unsafe fn flash_range_program(addr: u32, data: &[u8], use_boot2: bool) {
let mut boot2 = [0u32; 256 / 4];
let ptrs = if use_boot2 {
rom_data::memcpy44(&mut boot2 as *mut _, super::FLASH_BASE as *const _, 256);
flash_function_pointers_with_boot2(false, true, &boot2)
} else {
flash_function_pointers(false, true)
};
core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
write_flash_inner(
addr,
data.len() as u32,
Some(data),
&ptrs as *const FlashFunctionPointers,
);
}
/// # Safety
///
/// Nothing must access flash while this is running.
/// Usually this means:
/// - interrupts must be disabled
/// - 2nd core must be running code from RAM or ROM with interrupts disabled
/// - DMA must not access flash memory
/// Length of data must be a multiple of 4096
/// addr must be aligned to 4096
#[inline(never)]
#[link_section = ".data.ram_func"]
unsafe fn write_flash_inner(addr: u32, len: u32, data: Option<&[u8]>, ptrs: *const FlashFunctionPointers) {
/*
Should be equivalent to:
rom_data::connect_internal_flash();
rom_data::flash_exit_xip();
rom_data::flash_range_erase(addr, len, 1 << 31, 0); // if selected
rom_data::flash_range_program(addr, data as *const _, len); // if selected
rom_data::flash_flush_cache();
rom_data::flash_enter_cmd_xip();
*/
#[cfg(target_arch = "arm")]
core::arch::asm!(
"mov r8, r0",
"mov r9, r2",
"mov r10, r1",
"ldr r4, [{ptrs}, #0]",
"blx r4", // connect_internal_flash()
"ldr r4, [{ptrs}, #4]",
"blx r4", // flash_exit_xip()
"mov r0, r8", // r0 = addr
"mov r1, r10", // r1 = len
"movs r2, #1",
"lsls r2, r2, #31", // r2 = 1 << 31
"movs r3, #0", // r3 = 0
"ldr r4, [{ptrs}, #8]",
"cmp r4, #0",
"beq 1f",
"blx r4", // flash_range_erase(addr, len, 1 << 31, 0)
"1:",
"mov r0, r8", // r0 = addr
"mov r1, r9", // r0 = data
"mov r2, r10", // r2 = len
"ldr r4, [{ptrs}, #12]",
"cmp r4, #0",
"beq 1f",
"blx r4", // flash_range_program(addr, data, len);
"1:",
"ldr r4, [{ptrs}, #16]",
"blx r4", // flash_flush_cache();
"ldr r4, [{ptrs}, #20]",
"blx r4", // flash_enter_cmd_xip();
ptrs = in(reg) ptrs,
// Registers r8-r15 are not allocated automatically,
// so assign them manually. We need to use them as
// otherwise there are not enough registers available.
in("r0") addr,
in("r2") data.map(|d| d.as_ptr()).unwrap_or(core::ptr::null()),
in("r1") len,
out("r3") _,
out("r4") _,
lateout("r8") _,
lateout("r9") _,
lateout("r10") _,
clobber_abi("C"),
);
}
}
mod sealed {
pub trait Instance {}
}
pub trait Instance: sealed::Instance {}
impl sealed::Instance for FLASH {}
impl Instance for FLASH {}

View File

@ -159,7 +159,7 @@ unsafe fn IO_IRQ_BANK0() {
w.set_edge_low(pin_group, false);
}
InterruptTrigger::LevelHigh => {
debug!("IO_IRQ_BANK0 pin {} LevelHigh triggered\n", pin);
debug!("IO_IRQ_BANK0 pin {} LevelHigh triggered", pin);
w.set_level_high(pin_group, false);
}
InterruptTrigger::LevelLow => {
@ -198,7 +198,7 @@ impl<'d, T: Pin> InputFuture<'d, T> {
critical_section::with(|_| {
pin.int_proc().inte((pin.pin() / 8) as usize).modify(|w| match level {
InterruptTrigger::LevelHigh => {
debug!("InputFuture::new enable LevelHigh for pin {} \n", pin.pin());
debug!("InputFuture::new enable LevelHigh for pin {}", pin.pin());
w.set_level_high(pin_group, true);
}
InterruptTrigger::LevelLow => {
@ -245,45 +245,45 @@ impl<'d, T: Pin> Future for InputFuture<'d, T> {
// the pin and if it has been disabled that means it was done by the
// interrupt service routine, so we then know that the event/trigger
// happened and Poll::Ready will be returned.
debug!("{:?} for pin {}\n", self.level, self.pin.pin());
debug!("{:?} for pin {}", self.level, self.pin.pin());
match self.level {
InterruptTrigger::AnyEdge => {
if !inte.edge_high(pin_group) && !inte.edge_low(pin_group) {
#[rustfmt::skip]
debug!("{:?} for pin {} was cleared, return Poll::Ready\n", self.level, self.pin.pin());
debug!("{:?} for pin {} was cleared, return Poll::Ready", self.level, self.pin.pin());
return Poll::Ready(());
}
}
InterruptTrigger::LevelHigh => {
if !inte.level_high(pin_group) {
#[rustfmt::skip]
debug!("{:?} for pin {} was cleared, return Poll::Ready\n", self.level, self.pin.pin());
debug!("{:?} for pin {} was cleared, return Poll::Ready", self.level, self.pin.pin());
return Poll::Ready(());
}
}
InterruptTrigger::LevelLow => {
if !inte.level_low(pin_group) {
#[rustfmt::skip]
debug!("{:?} for pin {} was cleared, return Poll::Ready\n", self.level, self.pin.pin());
debug!("{:?} for pin {} was cleared, return Poll::Ready", self.level, self.pin.pin());
return Poll::Ready(());
}
}
InterruptTrigger::EdgeHigh => {
if !inte.edge_high(pin_group) {
#[rustfmt::skip]
debug!("{:?} for pin {} was cleared, return Poll::Ready\n", self.level, self.pin.pin());
debug!("{:?} for pin {} was cleared, return Poll::Ready", self.level, self.pin.pin());
return Poll::Ready(());
}
}
InterruptTrigger::EdgeLow => {
if !inte.edge_low(pin_group) {
#[rustfmt::skip]
debug!("{:?} for pin {} was cleared, return Poll::Ready\n", self.level, self.pin.pin());
debug!("{:?} for pin {} was cleared, return Poll::Ready", self.level, self.pin.pin());
return Poll::Ready(());
}
}
}
debug!("InputFuture::poll return Poll::Pending\n");
debug!("InputFuture::poll return Poll::Pending");
Poll::Pending
}
}
@ -599,12 +599,12 @@ pub(crate) mod sealed {
fn pin_bank(&self) -> u8;
#[inline]
fn pin(&self) -> u8 {
fn _pin(&self) -> u8 {
self.pin_bank() & 0x1f
}
#[inline]
fn bank(&self) -> Bank {
fn _bank(&self) -> Bank {
if self.pin_bank() & 0x20 == 0 {
Bank::Bank0
} else {
@ -613,35 +613,35 @@ pub(crate) mod sealed {
}
fn io(&self) -> pac::io::Gpio {
let block = match self.bank() {
let block = match self._bank() {
Bank::Bank0 => crate::pac::IO_BANK0,
Bank::Qspi => crate::pac::IO_QSPI,
};
block.gpio(self.pin() as _)
block.gpio(self._pin() as _)
}
fn pad_ctrl(&self) -> Reg<pac::pads::regs::GpioCtrl, RW> {
let block = match self.bank() {
let block = match self._bank() {
Bank::Bank0 => crate::pac::PADS_BANK0,
Bank::Qspi => crate::pac::PADS_QSPI,
};
block.gpio(self.pin() as _)
block.gpio(self._pin() as _)
}
fn sio_out(&self) -> pac::sio::Gpio {
SIO.gpio_out(self.bank() as _)
SIO.gpio_out(self._bank() as _)
}
fn sio_oe(&self) -> pac::sio::Gpio {
SIO.gpio_oe(self.bank() as _)
SIO.gpio_oe(self._bank() as _)
}
fn sio_in(&self) -> Reg<u32, RW> {
SIO.gpio_in(self.bank() as _)
SIO.gpio_in(self._bank() as _)
}
fn int_proc(&self) -> pac::io::Int {
let io_block = match self.bank() {
let io_block = match self._bank() {
Bank::Bank0 => crate::pac::IO_BANK0,
Bank::Qspi => crate::pac::IO_QSPI,
};
@ -658,6 +658,18 @@ pub trait Pin: Peripheral<P = Self> + Into<AnyPin> + sealed::Pin + Sized + 'stat
pin_bank: self.pin_bank(),
}
}
/// Returns the pin number within a bank
#[inline]
fn pin(&self) -> u8 {
self._pin()
}
/// Returns the bank of this pin
#[inline]
fn bank(&self) -> Bank {
self._bank()
}
}
pub struct AnyPin {
@ -867,7 +879,7 @@ mod eh1 {
type Error = Infallible;
}
impl<'d, T: Pin> embedded_hal_1::digital::blocking::InputPin for Input<'d, T> {
impl<'d, T: Pin> embedded_hal_1::digital::InputPin for Input<'d, T> {
fn is_high(&self) -> Result<bool, Self::Error> {
Ok(self.is_high())
}
@ -881,7 +893,7 @@ mod eh1 {
type Error = Infallible;
}
impl<'d, T: Pin> embedded_hal_1::digital::blocking::OutputPin for Output<'d, T> {
impl<'d, T: Pin> embedded_hal_1::digital::OutputPin for Output<'d, T> {
fn set_high(&mut self) -> Result<(), Self::Error> {
Ok(self.set_high())
}
@ -891,7 +903,7 @@ mod eh1 {
}
}
impl<'d, T: Pin> embedded_hal_1::digital::blocking::StatefulOutputPin for Output<'d, T> {
impl<'d, T: Pin> embedded_hal_1::digital::StatefulOutputPin for Output<'d, T> {
fn is_set_high(&self) -> Result<bool, Self::Error> {
Ok(self.is_set_high())
}
@ -901,7 +913,7 @@ mod eh1 {
}
}
impl<'d, T: Pin> embedded_hal_1::digital::blocking::ToggleableOutputPin for Output<'d, T> {
impl<'d, T: Pin> embedded_hal_1::digital::ToggleableOutputPin for Output<'d, T> {
fn toggle(&mut self) -> Result<(), Self::Error> {
Ok(self.toggle())
}
@ -911,7 +923,7 @@ mod eh1 {
type Error = Infallible;
}
impl<'d, T: Pin> embedded_hal_1::digital::blocking::OutputPin for OutputOpenDrain<'d, T> {
impl<'d, T: Pin> embedded_hal_1::digital::OutputPin for OutputOpenDrain<'d, T> {
fn set_high(&mut self) -> Result<(), Self::Error> {
Ok(self.set_high())
}
@ -921,7 +933,7 @@ mod eh1 {
}
}
impl<'d, T: Pin> embedded_hal_1::digital::blocking::StatefulOutputPin for OutputOpenDrain<'d, T> {
impl<'d, T: Pin> embedded_hal_1::digital::StatefulOutputPin for OutputOpenDrain<'d, T> {
fn is_set_high(&self) -> Result<bool, Self::Error> {
Ok(self.is_set_high())
}
@ -931,7 +943,7 @@ mod eh1 {
}
}
impl<'d, T: Pin> embedded_hal_1::digital::blocking::ToggleableOutputPin for OutputOpenDrain<'d, T> {
impl<'d, T: Pin> embedded_hal_1::digital::ToggleableOutputPin for OutputOpenDrain<'d, T> {
fn toggle(&mut self) -> Result<(), Self::Error> {
Ok(self.toggle())
}
@ -941,7 +953,7 @@ mod eh1 {
type Error = Infallible;
}
impl<'d, T: Pin> embedded_hal_1::digital::blocking::InputPin for Flex<'d, T> {
impl<'d, T: Pin> embedded_hal_1::digital::InputPin for Flex<'d, T> {
fn is_high(&self) -> Result<bool, Self::Error> {
Ok(self.is_high())
}
@ -951,7 +963,7 @@ mod eh1 {
}
}
impl<'d, T: Pin> embedded_hal_1::digital::blocking::OutputPin for Flex<'d, T> {
impl<'d, T: Pin> embedded_hal_1::digital::OutputPin for Flex<'d, T> {
fn set_high(&mut self) -> Result<(), Self::Error> {
Ok(self.set_high())
}
@ -961,7 +973,7 @@ mod eh1 {
}
}
impl<'d, T: Pin> embedded_hal_1::digital::blocking::StatefulOutputPin for Flex<'d, T> {
impl<'d, T: Pin> embedded_hal_1::digital::StatefulOutputPin for Flex<'d, T> {
fn is_set_high(&self) -> Result<bool, Self::Error> {
Ok(self.is_set_high())
}
@ -971,7 +983,7 @@ mod eh1 {
}
}
impl<'d, T: Pin> embedded_hal_1::digital::blocking::ToggleableOutputPin for Flex<'d, T> {
impl<'d, T: Pin> embedded_hal_1::digital::ToggleableOutputPin for Flex<'d, T> {
fn toggle(&mut self) -> Result<(), Self::Error> {
Ok(self.toggle())
}

919
embassy-rp/src/i2c.rs Normal file
View File

@ -0,0 +1,919 @@
use core::future;
use core::marker::PhantomData;
use core::task::Poll;
use embassy_cortex_m::interrupt::InterruptExt;
use embassy_hal_common::{into_ref, PeripheralRef};
use embassy_sync::waitqueue::AtomicWaker;
use pac::i2c;
use crate::gpio::sealed::Pin;
use crate::gpio::AnyPin;
use crate::{pac, peripherals, Peripheral};
/// I2C error abort reason
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum AbortReason {
/// A bus operation was not acknowledged, e.g. due to the addressed device
/// not being available on the bus or the device not being ready to process
/// requests at the moment
NoAcknowledge,
/// The arbitration was lost, e.g. electrical problems with the clock signal
ArbitrationLoss,
Other(u32),
}
/// I2C error
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Error {
/// I2C abort with error
Abort(AbortReason),
/// User passed in a read buffer that was 0 length
InvalidReadBufferLength,
/// User passed in a write buffer that was 0 length
InvalidWriteBufferLength,
/// Target i2c address is out of range
AddressOutOfRange(u16),
/// Target i2c address is reserved
AddressReserved(u16),
}
#[non_exhaustive]
#[derive(Copy, Clone)]
pub struct Config {
pub frequency: u32,
}
impl Default for Config {
fn default() -> Self {
Self { frequency: 100_000 }
}
}
const FIFO_SIZE: u8 = 16;
pub struct I2c<'d, T: Instance, M: Mode> {
phantom: PhantomData<(&'d mut T, M)>,
}
impl<'d, T: Instance> I2c<'d, T, Blocking> {
pub fn new_blocking(
peri: impl Peripheral<P = T> + 'd,
scl: impl Peripheral<P = impl SclPin<T>> + 'd,
sda: impl Peripheral<P = impl SdaPin<T>> + 'd,
config: Config,
) -> Self {
into_ref!(scl, sda);
Self::new_inner(peri, scl.map_into(), sda.map_into(), config)
}
}
impl<'d, T: Instance> I2c<'d, T, Async> {
pub fn new_async(
peri: impl Peripheral<P = T> + 'd,
scl: impl Peripheral<P = impl SclPin<T>> + 'd,
sda: impl Peripheral<P = impl SdaPin<T>> + 'd,
irq: impl Peripheral<P = T::Interrupt> + 'd,
config: Config,
) -> Self {
into_ref!(scl, sda, irq);
let i2c = Self::new_inner(peri, scl.map_into(), sda.map_into(), config);
irq.set_handler(Self::on_interrupt);
unsafe {
let i2c = T::regs();
// mask everything initially
i2c.ic_intr_mask().write_value(i2c::regs::IcIntrMask(0));
}
irq.unpend();
debug_assert!(!irq.is_pending());
irq.enable();
i2c
}
/// Calls `f` to check if we are ready or not.
/// If not, `g` is called once the waker is set (to eg enable the required interrupts).
async fn wait_on<F, U, G>(&mut self, mut f: F, mut g: G) -> U
where
F: FnMut(&mut Self) -> Poll<U>,
G: FnMut(&mut Self),
{
future::poll_fn(|cx| {
let r = f(self);
if r.is_pending() {
T::waker().register(cx.waker());
g(self);
}
r
})
.await
}
// Mask interrupts and wake any task waiting for this interrupt
unsafe fn on_interrupt(_: *mut ()) {
let i2c = T::regs();
i2c.ic_intr_mask().write_value(pac::i2c::regs::IcIntrMask::default());
T::waker().wake();
}
async fn read_async_internal(&mut self, buffer: &mut [u8], restart: bool, send_stop: bool) -> Result<(), Error> {
if buffer.is_empty() {
return Err(Error::InvalidReadBufferLength);
}
let p = T::regs();
let mut remaining = buffer.len();
let mut remaining_queue = buffer.len();
let mut abort_reason = Ok(());
while remaining > 0 {
// Waggle SCK - basically the same as write
let tx_fifo_space = Self::tx_fifo_capacity();
let mut batch = 0;
debug_assert!(remaining_queue > 0);
for _ in 0..remaining_queue.min(tx_fifo_space as usize) {
remaining_queue -= 1;
let last = remaining_queue == 0;
batch += 1;
unsafe {
p.ic_data_cmd().write(|w| {
w.set_restart(restart && remaining_queue == buffer.len() - 1);
w.set_stop(last && send_stop);
w.set_cmd(true);
});
}
}
// We've either run out of txfifo or just plain finished setting up
// the clocks for the message - either way we need to wait for rx
// data.
debug_assert!(batch > 0);
let res = self
.wait_on(
|me| {
let rxfifo = Self::rx_fifo_len();
if let Err(abort_reason) = me.read_and_clear_abort_reason() {
Poll::Ready(Err(abort_reason))
} else if rxfifo >= batch {
Poll::Ready(Ok(rxfifo))
} else {
Poll::Pending
}
},
|_me| unsafe {
// Set the read threshold to the number of bytes we're
// expecting so we don't get spurious interrupts.
p.ic_rx_tl().write(|w| w.set_rx_tl(batch - 1));
p.ic_intr_mask().modify(|w| {
w.set_m_rx_full(true);
w.set_m_tx_abrt(true);
});
},
)
.await;
match res {
Err(reason) => {
abort_reason = Err(reason);
break;
}
Ok(rxfifo) => {
// Fetch things from rx fifo. We're assuming we're the only
// rxfifo reader, so nothing else can take things from it.
let rxbytes = (rxfifo as usize).min(remaining);
let received = buffer.len() - remaining;
for b in &mut buffer[received..received + rxbytes] {
*b = unsafe { p.ic_data_cmd().read().dat() };
}
remaining -= rxbytes;
}
};
}
self.wait_stop_det(abort_reason, send_stop).await
}
async fn write_async_internal(
&mut self,
bytes: impl IntoIterator<Item = u8>,
send_stop: bool,
) -> Result<(), Error> {
let p = T::regs();
let mut bytes = bytes.into_iter().peekable();
let res = 'xmit: loop {
let tx_fifo_space = Self::tx_fifo_capacity();
for _ in 0..tx_fifo_space {
if let Some(byte) = bytes.next() {
let last = bytes.peek().is_none();
unsafe {
p.ic_data_cmd().write(|w| {
w.set_stop(last && send_stop);
w.set_cmd(false);
w.set_dat(byte);
});
}
} else {
break 'xmit Ok(());
}
}
let res = self
.wait_on(
|me| {
if let abort_reason @ Err(_) = me.read_and_clear_abort_reason() {
Poll::Ready(abort_reason)
} else if !Self::tx_fifo_full() {
// resume if there's any space free in the tx fifo
Poll::Ready(Ok(()))
} else {
Poll::Pending
}
},
|_me| unsafe {
// Set tx "free" threshold a little high so that we get
// woken before the fifo completely drains to minimize
// transfer stalls.
p.ic_tx_tl().write(|w| w.set_tx_tl(1));
p.ic_intr_mask().modify(|w| {
w.set_m_tx_empty(true);
w.set_m_tx_abrt(true);
})
},
)
.await;
if res.is_err() {
break res;
}
};
self.wait_stop_det(res, send_stop).await
}
/// Helper to wait for a stop bit, for both tx and rx. If we had an abort,
/// then we'll get a hardware-generated stop, otherwise wait for a stop if
/// we're expecting it.
///
/// Also handles an abort which arises while processing the tx fifo.
async fn wait_stop_det(&mut self, had_abort: Result<(), Error>, do_stop: bool) -> Result<(), Error> {
if had_abort.is_err() || do_stop {
let p = T::regs();
let had_abort2 = self
.wait_on(
|me| unsafe {
// We could see an abort while processing fifo backlog,
// so handle it here.
let abort = me.read_and_clear_abort_reason();
if had_abort.is_ok() && abort.is_err() {
Poll::Ready(abort)
} else if p.ic_raw_intr_stat().read().stop_det() {
Poll::Ready(Ok(()))
} else {
Poll::Pending
}
},
|_me| unsafe {
p.ic_intr_mask().modify(|w| {
w.set_m_stop_det(true);
w.set_m_tx_abrt(true);
});
},
)
.await;
unsafe {
p.ic_clr_stop_det().read();
}
had_abort.and(had_abort2)
} else {
had_abort
}
}
pub async fn read_async(&mut self, addr: u16, buffer: &mut [u8]) -> Result<(), Error> {
Self::setup(addr)?;
self.read_async_internal(buffer, false, true).await
}
pub async fn write_async(&mut self, addr: u16, bytes: impl IntoIterator<Item = u8>) -> Result<(), Error> {
Self::setup(addr)?;
self.write_async_internal(bytes, true).await
}
}
impl<'d, T: Instance + 'd, M: Mode> I2c<'d, T, M> {
fn new_inner(
_peri: impl Peripheral<P = T> + 'd,
scl: PeripheralRef<'d, AnyPin>,
sda: PeripheralRef<'d, AnyPin>,
config: Config,
) -> Self {
into_ref!(_peri);
assert!(config.frequency <= 1_000_000);
assert!(config.frequency > 0);
let p = T::regs();
unsafe {
let reset = T::reset();
crate::reset::reset(reset);
crate::reset::unreset_wait(reset);
p.ic_enable().write(|w| w.set_enable(false));
// Select controller mode & speed
p.ic_con().modify(|w| {
// Always use "fast" mode (<= 400 kHz, works fine for standard
// mode too)
w.set_speed(i2c::vals::Speed::FAST);
w.set_master_mode(true);
w.set_ic_slave_disable(true);
w.set_ic_restart_en(true);
w.set_tx_empty_ctrl(true);
});
// Set FIFO watermarks to 1 to make things simpler. This is encoded
// by a register value of 0.
p.ic_tx_tl().write(|w| w.set_tx_tl(0));
p.ic_rx_tl().write(|w| w.set_rx_tl(0));
// Configure SCL & SDA pins
scl.io().ctrl().write(|w| w.set_funcsel(3));
sda.io().ctrl().write(|w| w.set_funcsel(3));
scl.pad_ctrl().write(|w| {
w.set_schmitt(true);
w.set_ie(true);
w.set_od(false);
w.set_pue(true);
w.set_pde(false);
});
sda.pad_ctrl().write(|w| {
w.set_schmitt(true);
w.set_ie(true);
w.set_od(false);
w.set_pue(true);
w.set_pde(false);
});
// Configure baudrate
// There are some subtleties to I2C timing which we are completely
// ignoring here See:
// https://github.com/raspberrypi/pico-sdk/blob/bfcbefafc5d2a210551a4d9d80b4303d4ae0adf7/src/rp2_common/hardware_i2c/i2c.c#L69
let clk_base = crate::clocks::clk_peri_freq();
let period = (clk_base + config.frequency / 2) / config.frequency;
let lcnt = period * 3 / 5; // spend 3/5 (60%) of the period low
let hcnt = period - lcnt; // and 2/5 (40%) of the period high
// Check for out-of-range divisors:
assert!(hcnt <= 0xffff);
assert!(lcnt <= 0xffff);
assert!(hcnt >= 8);
assert!(lcnt >= 8);
// Per I2C-bus specification a device in standard or fast mode must
// internally provide a hold time of at least 300ns for the SDA
// signal to bridge the undefined region of the falling edge of SCL.
// A smaller hold time of 120ns is used for fast mode plus.
let sda_tx_hold_count = if config.frequency < 1_000_000 {
// sda_tx_hold_count = clk_base [cycles/s] * 300ns * (1s /
// 1e9ns) Reduce 300/1e9 to 3/1e7 to avoid numbers that don't
// fit in uint. Add 1 to avoid division truncation.
((clk_base * 3) / 10_000_000) + 1
} else {
// fast mode plus requires a clk_base > 32MHz
assert!(clk_base >= 32_000_000);
// sda_tx_hold_count = clk_base [cycles/s] * 120ns * (1s /
// 1e9ns) Reduce 120/1e9 to 3/25e6 to avoid numbers that don't
// fit in uint. Add 1 to avoid division truncation.
((clk_base * 3) / 25_000_000) + 1
};
assert!(sda_tx_hold_count <= lcnt - 2);
p.ic_fs_scl_hcnt().write(|w| w.set_ic_fs_scl_hcnt(hcnt as u16));
p.ic_fs_scl_lcnt().write(|w| w.set_ic_fs_scl_lcnt(lcnt as u16));
p.ic_fs_spklen()
.write(|w| w.set_ic_fs_spklen(if lcnt < 16 { 1 } else { (lcnt / 16) as u8 }));
p.ic_sda_hold()
.modify(|w| w.set_ic_sda_tx_hold(sda_tx_hold_count as u16));
// Enable I2C block
p.ic_enable().write(|w| w.set_enable(true));
}
Self { phantom: PhantomData }
}
fn setup(addr: u16) -> Result<(), Error> {
if addr >= 0x80 {
return Err(Error::AddressOutOfRange(addr));
}
if i2c_reserved_addr(addr) {
return Err(Error::AddressReserved(addr));
}
let p = T::regs();
unsafe {
p.ic_enable().write(|w| w.set_enable(false));
p.ic_tar().write(|w| w.set_ic_tar(addr));
p.ic_enable().write(|w| w.set_enable(true));
}
Ok(())
}
#[inline]
fn tx_fifo_full() -> bool {
Self::tx_fifo_capacity() == 0
}
#[inline]
fn tx_fifo_capacity() -> u8 {
let p = T::regs();
unsafe { FIFO_SIZE - p.ic_txflr().read().txflr() }
}
#[inline]
fn rx_fifo_len() -> u8 {
let p = T::regs();
unsafe { p.ic_rxflr().read().rxflr() }
}
fn read_and_clear_abort_reason(&mut self) -> Result<(), Error> {
let p = T::regs();
unsafe {
let abort_reason = p.ic_tx_abrt_source().read();
if abort_reason.0 != 0 {
// Note clearing the abort flag also clears the reason, and this
// instance of flag is clear-on-read! Note also the
// IC_CLR_TX_ABRT register always reads as 0.
p.ic_clr_tx_abrt().read();
let reason = if abort_reason.abrt_7b_addr_noack()
| abort_reason.abrt_10addr1_noack()
| abort_reason.abrt_10addr2_noack()
{
AbortReason::NoAcknowledge
} else if abort_reason.arb_lost() {
AbortReason::ArbitrationLoss
} else {
AbortReason::Other(abort_reason.0)
};
Err(Error::Abort(reason))
} else {
Ok(())
}
}
}
fn read_blocking_internal(&mut self, buffer: &mut [u8], restart: bool, send_stop: bool) -> Result<(), Error> {
if buffer.is_empty() {
return Err(Error::InvalidReadBufferLength);
}
let p = T::regs();
let lastindex = buffer.len() - 1;
for (i, byte) in buffer.iter_mut().enumerate() {
let first = i == 0;
let last = i == lastindex;
// NOTE(unsafe) We have &mut self
unsafe {
// wait until there is space in the FIFO to write the next byte
while Self::tx_fifo_full() {}
p.ic_data_cmd().write(|w| {
w.set_restart(restart && first);
w.set_stop(send_stop && last);
w.set_cmd(true);
});
while Self::rx_fifo_len() == 0 {
self.read_and_clear_abort_reason()?;
}
*byte = p.ic_data_cmd().read().dat();
}
}
Ok(())
}
fn write_blocking_internal(&mut self, bytes: &[u8], send_stop: bool) -> Result<(), Error> {
if bytes.is_empty() {
return Err(Error::InvalidWriteBufferLength);
}
let p = T::regs();
for (i, byte) in bytes.iter().enumerate() {
let last = i == bytes.len() - 1;
// NOTE(unsafe) We have &mut self
unsafe {
p.ic_data_cmd().write(|w| {
w.set_stop(send_stop && last);
w.set_dat(*byte);
});
// Wait until the transmission of the address/data from the
// internal shift register has completed. For this to function
// correctly, the TX_EMPTY_CTRL flag in IC_CON must be set. The
// TX_EMPTY_CTRL flag was set in i2c_init.
while !p.ic_raw_intr_stat().read().tx_empty() {}
let abort_reason = self.read_and_clear_abort_reason();
if abort_reason.is_err() || (send_stop && last) {
// If the transaction was aborted or if it completed
// successfully wait until the STOP condition has occured.
while !p.ic_raw_intr_stat().read().stop_det() {}
p.ic_clr_stop_det().read().clr_stop_det();
}
// Note the hardware issues a STOP automatically on an abort
// condition. Note also the hardware clears RX FIFO as well as
// TX on abort, ecause we set hwparam
// IC_AVOID_RX_FIFO_FLUSH_ON_TX_ABRT to 0.
abort_reason?;
}
}
Ok(())
}
// =========================
// Blocking public API
// =========================
pub fn blocking_read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> {
Self::setup(address.into())?;
self.read_blocking_internal(buffer, true, true)
// Automatic Stop
}
pub fn blocking_write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Error> {
Self::setup(address.into())?;
self.write_blocking_internal(bytes, true)
}
pub fn blocking_write_read(&mut self, address: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Error> {
Self::setup(address.into())?;
self.write_blocking_internal(bytes, false)?;
self.read_blocking_internal(buffer, true, true)
// Automatic Stop
}
}
mod eh02 {
use super::*;
impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::i2c::Read for I2c<'d, T, M> {
type Error = Error;
fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> {
self.blocking_read(address, buffer)
}
}
impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::i2c::Write for I2c<'d, T, M> {
type Error = Error;
fn write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Self::Error> {
self.blocking_write(address, bytes)
}
}
impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::i2c::WriteRead for I2c<'d, T, M> {
type Error = Error;
fn write_read(&mut self, address: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Self::Error> {
self.blocking_write_read(address, bytes, buffer)
}
}
}
#[cfg(feature = "unstable-traits")]
mod eh1 {
use super::*;
impl embedded_hal_1::i2c::Error for Error {
fn kind(&self) -> embedded_hal_1::i2c::ErrorKind {
match *self {
Self::Abort(AbortReason::ArbitrationLoss) => embedded_hal_1::i2c::ErrorKind::ArbitrationLoss,
Self::Abort(AbortReason::NoAcknowledge) => {
embedded_hal_1::i2c::ErrorKind::NoAcknowledge(embedded_hal_1::i2c::NoAcknowledgeSource::Address)
}
Self::Abort(AbortReason::Other(_)) => embedded_hal_1::i2c::ErrorKind::Other,
Self::InvalidReadBufferLength => embedded_hal_1::i2c::ErrorKind::Other,
Self::InvalidWriteBufferLength => embedded_hal_1::i2c::ErrorKind::Other,
Self::AddressOutOfRange(_) => embedded_hal_1::i2c::ErrorKind::Other,
Self::AddressReserved(_) => embedded_hal_1::i2c::ErrorKind::Other,
}
}
}
impl<'d, T: Instance, M: Mode> embedded_hal_1::i2c::ErrorType for I2c<'d, T, M> {
type Error = Error;
}
impl<'d, T: Instance, M: Mode> embedded_hal_1::i2c::I2c for I2c<'d, T, M> {
fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> {
self.blocking_read(address, buffer)
}
fn write(&mut self, address: u8, buffer: &[u8]) -> Result<(), Self::Error> {
self.blocking_write(address, buffer)
}
fn write_iter<B>(&mut self, address: u8, bytes: B) -> Result<(), Self::Error>
where
B: IntoIterator<Item = u8>,
{
let mut peekable = bytes.into_iter().peekable();
Self::setup(address.into())?;
while let Some(tx) = peekable.next() {
self.write_blocking_internal(&[tx], peekable.peek().is_none())?;
}
Ok(())
}
fn write_iter_read<B>(&mut self, address: u8, bytes: B, buffer: &mut [u8]) -> Result<(), Self::Error>
where
B: IntoIterator<Item = u8>,
{
let peekable = bytes.into_iter().peekable();
Self::setup(address.into())?;
for tx in peekable {
self.write_blocking_internal(&[tx], false)?
}
self.read_blocking_internal(buffer, true, true)
}
fn write_read(&mut self, address: u8, wr_buffer: &[u8], rd_buffer: &mut [u8]) -> Result<(), Self::Error> {
self.blocking_write_read(address, wr_buffer, rd_buffer)
}
fn transaction<'a>(
&mut self,
address: u8,
operations: &mut [embedded_hal_1::i2c::Operation<'a>],
) -> Result<(), Self::Error> {
Self::setup(address.into())?;
for i in 0..operations.len() {
let last = i == operations.len() - 1;
match &mut operations[i] {
embedded_hal_1::i2c::Operation::Read(buf) => self.read_blocking_internal(buf, false, last)?,
embedded_hal_1::i2c::Operation::Write(buf) => self.write_blocking_internal(buf, last)?,
}
}
Ok(())
}
fn transaction_iter<'a, O>(&mut self, address: u8, operations: O) -> Result<(), Self::Error>
where
O: IntoIterator<Item = embedded_hal_1::i2c::Operation<'a>>,
{
Self::setup(address.into())?;
let mut peekable = operations.into_iter().peekable();
while let Some(operation) = peekable.next() {
let last = peekable.peek().is_none();
match operation {
embedded_hal_1::i2c::Operation::Read(buf) => self.read_blocking_internal(buf, false, last)?,
embedded_hal_1::i2c::Operation::Write(buf) => self.write_blocking_internal(buf, last)?,
}
}
Ok(())
}
}
}
#[cfg(all(feature = "unstable-traits", feature = "nightly"))]
mod nightly {
use core::future::Future;
use embedded_hal_1::i2c::Operation;
use embedded_hal_async::i2c::AddressMode;
use super::*;
impl<'d, A, T> embedded_hal_async::i2c::I2c<A> for I2c<'d, T, Async>
where
A: AddressMode + Into<u16> + 'static,
T: Instance + 'd,
{
type ReadFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a
where Self: 'a;
type WriteFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a
where Self: 'a;
type WriteReadFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a
where Self: 'a;
type TransactionFuture<'a, 'b> = impl Future<Output = Result<(), Error>> + 'a
where Self: 'a, 'b: 'a;
fn read<'a>(&'a mut self, address: A, buffer: &'a mut [u8]) -> Self::ReadFuture<'a> {
let addr: u16 = address.into();
async move {
Self::setup(addr)?;
self.read_async_internal(buffer, false, true).await
}
}
fn write<'a>(&'a mut self, address: A, write: &'a [u8]) -> Self::WriteFuture<'a> {
let addr: u16 = address.into();
async move {
Self::setup(addr)?;
self.write_async_internal(write.iter().copied(), true).await
}
}
fn write_read<'a>(
&'a mut self,
address: A,
bytes: &'a [u8],
buffer: &'a mut [u8],
) -> Self::WriteReadFuture<'a> {
let addr: u16 = address.into();
async move {
Self::setup(addr)?;
self.write_async_internal(bytes.iter().cloned(), false).await?;
self.read_async_internal(buffer, false, true).await
}
}
fn transaction<'a, 'b>(
&'a mut self,
address: A,
operations: &'a mut [Operation<'b>],
) -> Self::TransactionFuture<'a, 'b> {
let addr: u16 = address.into();
async move {
let mut iterator = operations.iter_mut();
while let Some(op) = iterator.next() {
let last = iterator.len() == 0;
match op {
Operation::Read(buffer) => {
Self::setup(addr)?;
self.read_async_internal(buffer, false, last).await?;
}
Operation::Write(buffer) => {
Self::setup(addr)?;
self.write_async_internal(buffer.into_iter().cloned(), last).await?;
}
}
}
Ok(())
}
}
}
}
fn i2c_reserved_addr(addr: u16) -> bool {
(addr & 0x78) == 0 || (addr & 0x78) == 0x78
}
mod sealed {
use embassy_cortex_m::interrupt::Interrupt;
use embassy_sync::waitqueue::AtomicWaker;
pub trait Instance {
const TX_DREQ: u8;
const RX_DREQ: u8;
type Interrupt: Interrupt;
fn regs() -> crate::pac::i2c::I2c;
fn reset() -> crate::pac::resets::regs::Peripherals;
fn waker() -> &'static AtomicWaker;
}
pub trait Mode {}
pub trait SdaPin<T: Instance> {}
pub trait SclPin<T: Instance> {}
}
pub trait Mode: sealed::Mode {}
macro_rules! impl_mode {
($name:ident) => {
impl sealed::Mode for $name {}
impl Mode for $name {}
};
}
pub struct Blocking;
pub struct Async;
impl_mode!(Blocking);
impl_mode!(Async);
pub trait Instance: sealed::Instance {}
macro_rules! impl_instance {
($type:ident, $irq:ident, $reset:ident, $tx_dreq:expr, $rx_dreq:expr) => {
impl sealed::Instance for peripherals::$type {
const TX_DREQ: u8 = $tx_dreq;
const RX_DREQ: u8 = $rx_dreq;
type Interrupt = crate::interrupt::$irq;
#[inline]
fn regs() -> pac::i2c::I2c {
pac::$type
}
#[inline]
fn reset() -> pac::resets::regs::Peripherals {
let mut ret = pac::resets::regs::Peripherals::default();
ret.$reset(true);
ret
}
#[inline]
fn waker() -> &'static AtomicWaker {
static WAKER: AtomicWaker = AtomicWaker::new();
&WAKER
}
}
impl Instance for peripherals::$type {}
};
}
impl_instance!(I2C0, I2C0_IRQ, set_i2c0, 32, 33);
impl_instance!(I2C1, I2C1_IRQ, set_i2c1, 34, 35);
pub trait SdaPin<T: Instance>: sealed::SdaPin<T> + crate::gpio::Pin {}
pub trait SclPin<T: Instance>: sealed::SclPin<T> + crate::gpio::Pin {}
macro_rules! impl_pin {
($pin:ident, $instance:ident, $function:ident) => {
impl sealed::$function<peripherals::$instance> for peripherals::$pin {}
impl $function<peripherals::$instance> for peripherals::$pin {}
};
}
impl_pin!(PIN_0, I2C0, SdaPin);
impl_pin!(PIN_1, I2C0, SclPin);
impl_pin!(PIN_2, I2C1, SdaPin);
impl_pin!(PIN_3, I2C1, SclPin);
impl_pin!(PIN_4, I2C0, SdaPin);
impl_pin!(PIN_5, I2C0, SclPin);
impl_pin!(PIN_6, I2C1, SdaPin);
impl_pin!(PIN_7, I2C1, SclPin);
impl_pin!(PIN_8, I2C0, SdaPin);
impl_pin!(PIN_9, I2C0, SclPin);
impl_pin!(PIN_10, I2C1, SdaPin);
impl_pin!(PIN_11, I2C1, SclPin);
impl_pin!(PIN_12, I2C0, SdaPin);
impl_pin!(PIN_13, I2C0, SclPin);
impl_pin!(PIN_14, I2C1, SdaPin);
impl_pin!(PIN_15, I2C1, SclPin);
impl_pin!(PIN_16, I2C0, SdaPin);
impl_pin!(PIN_17, I2C0, SclPin);
impl_pin!(PIN_18, I2C1, SdaPin);
impl_pin!(PIN_19, I2C1, SclPin);
impl_pin!(PIN_20, I2C0, SdaPin);
impl_pin!(PIN_21, I2C0, SclPin);
impl_pin!(PIN_22, I2C1, SdaPin);
impl_pin!(PIN_23, I2C1, SclPin);
impl_pin!(PIN_24, I2C0, SdaPin);
impl_pin!(PIN_25, I2C0, SclPin);
impl_pin!(PIN_26, I2C1, SdaPin);
impl_pin!(PIN_27, I2C1, SclPin);
impl_pin!(PIN_28, I2C0, SdaPin);
impl_pin!(PIN_29, I2C0, SclPin);

View File

@ -0,0 +1,276 @@
#![macro_use]
// Credit: taken from `rp-hal` (also licensed Apache+MIT)
// https://github.com/rp-rs/rp-hal/blob/main/rp2040-hal/src/intrinsics.rs
/// Generate a series of aliases for an intrinsic function.
macro_rules! intrinsics_aliases {
(
extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty,
) => {};
(
unsafe extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty,
) => {};
(
extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty,
$alias:ident
$($rest:ident)*
) => {
#[cfg(all(target_arch = "arm", feature = "intrinsics"))]
intrinsics! {
extern $abi fn $alias( $($argname: $ty),* ) -> $ret {
$name($($argname),*)
}
}
intrinsics_aliases! {
extern $abi fn $name( $($argname: $ty),* ) -> $ret,
$($rest)*
}
};
(
unsafe extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty,
$alias:ident
$($rest:ident)*
) => {
#[cfg(all(target_arch = "arm", feature = "intrinsics"))]
intrinsics! {
unsafe extern $abi fn $alias( $($argname: $ty),* ) -> $ret {
$name($($argname),*)
}
}
intrinsics_aliases! {
unsafe extern $abi fn $name( $($argname: $ty),* ) -> $ret,
$($rest)*
}
};
}
/// The macro used to define overridden intrinsics.
///
/// This is heavily inspired by the macro used by compiler-builtins. The idea
/// is to abstract anything special that needs to be done to override an
/// intrinsic function. Intrinsic generation is disabled for non-ARM targets
/// so things like CI and docs generation do not have problems. Additionally
/// they can be disabled by disabling the crate feature `intrinsics` for
/// testing or comparing performance.
///
/// Like the compiler-builtins macro, it accepts a series of functions that
/// looks like normal Rust code:
///
/// intrinsics! {
/// extern "C" fn foo(a: i32) -> u32 {
/// // ...
/// }
///
/// #[nonstandard_attribute]
/// extern "C" fn bar(a: i32) -> u32 {
/// // ...
/// }
/// }
///
/// Each function can also be decorated with nonstandard attributes to control
/// additional behaviour:
///
/// * `slower_than_default` - indicates that the override is slower than the
/// default implementation. Currently this just disables the override
/// entirely.
/// * `bootrom_v2` - indicates that the override is only available
/// on a V2 bootrom or higher. Only enabled when the feature
/// `rom-v2-intrinsics` is set.
/// * `alias` - accepts a list of names to alias the intrinsic to.
/// * `aeabi` - accepts a list of ARM EABI names to alias to.
///
macro_rules! intrinsics {
() => {};
(
#[slower_than_default]
$(#[$($attr:tt)*])*
extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty {
$($body:tt)*
}
$($rest:tt)*
) => {
// Not exported, but defined so the actual implementation is
// considered used
#[allow(dead_code)]
fn $name( $($argname: $ty),* ) -> $ret {
$($body)*
}
intrinsics!($($rest)*);
};
(
#[bootrom_v2]
$(#[$($attr:tt)*])*
extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty {
$($body:tt)*
}
$($rest:tt)*
) => {
// Not exported, but defined so the actual implementation is
// considered used
#[cfg(not(feature = "rom-v2-intrinsics"))]
#[allow(dead_code)]
fn $name( $($argname: $ty),* ) -> $ret {
$($body)*
}
#[cfg(feature = "rom-v2-intrinsics")]
intrinsics! {
$(#[$($attr)*])*
extern $abi fn $name( $($argname: $ty),* ) -> $ret {
$($body)*
}
}
intrinsics!($($rest)*);
};
(
#[alias = $($alias:ident),*]
$(#[$($attr:tt)*])*
extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty {
$($body:tt)*
}
$($rest:tt)*
) => {
intrinsics! {
$(#[$($attr)*])*
extern $abi fn $name( $($argname: $ty),* ) -> $ret {
$($body)*
}
}
intrinsics_aliases! {
extern $abi fn $name( $($argname: $ty),* ) -> $ret,
$($alias) *
}
intrinsics!($($rest)*);
};
(
#[alias = $($alias:ident),*]
$(#[$($attr:tt)*])*
unsafe extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty {
$($body:tt)*
}
$($rest:tt)*
) => {
intrinsics! {
$(#[$($attr)*])*
unsafe extern $abi fn $name( $($argname: $ty),* ) -> $ret {
$($body)*
}
}
intrinsics_aliases! {
unsafe extern $abi fn $name( $($argname: $ty),* ) -> $ret,
$($alias) *
}
intrinsics!($($rest)*);
};
(
#[aeabi = $($alias:ident),*]
$(#[$($attr:tt)*])*
extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty {
$($body:tt)*
}
$($rest:tt)*
) => {
intrinsics! {
$(#[$($attr)*])*
extern $abi fn $name( $($argname: $ty),* ) -> $ret {
$($body)*
}
}
intrinsics_aliases! {
extern "aapcs" fn $name( $($argname: $ty),* ) -> $ret,
$($alias) *
}
intrinsics!($($rest)*);
};
(
$(#[$($attr:tt)*])*
extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty {
$($body:tt)*
}
$($rest:tt)*
) => {
#[cfg(all(target_arch = "arm", feature = "intrinsics"))]
$(#[$($attr)*])*
extern $abi fn $name( $($argname: $ty),* ) -> $ret {
$($body)*
}
#[cfg(all(target_arch = "arm", feature = "intrinsics"))]
mod $name {
#[no_mangle]
$(#[$($attr)*])*
pub extern $abi fn $name( $($argname: $ty),* ) -> $ret {
super::$name($($argname),*)
}
}
// Not exported, but defined so the actual implementation is
// considered used
#[cfg(not(all(target_arch = "arm", feature = "intrinsics")))]
#[allow(dead_code)]
fn $name( $($argname: $ty),* ) -> $ret {
$($body)*
}
intrinsics!($($rest)*);
};
(
$(#[$($attr:tt)*])*
unsafe extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty {
$($body:tt)*
}
$($rest:tt)*
) => {
#[cfg(all(target_arch = "arm", feature = "intrinsics"))]
$(#[$($attr)*])*
unsafe extern $abi fn $name( $($argname: $ty),* ) -> $ret {
$($body)*
}
#[cfg(all(target_arch = "arm", feature = "intrinsics"))]
mod $name {
#[no_mangle]
$(#[$($attr)*])*
pub unsafe extern $abi fn $name( $($argname: $ty),* ) -> $ret {
super::$name($($argname),*)
}
}
// Not exported, but defined so the actual implementation is
// considered used
#[cfg(not(all(target_arch = "arm", feature = "intrinsics")))]
#[allow(dead_code)]
unsafe fn $name( $($argname: $ty),* ) -> $ret {
$($body)*
}
intrinsics!($($rest)*);
};
}

View File

@ -1,19 +1,27 @@
#![no_std]
#![cfg_attr(feature = "nightly", feature(generic_associated_types, type_alias_impl_trait))]
#![cfg_attr(feature = "nightly", feature(type_alias_impl_trait))]
// This mod MUST go first, so that the others see its macros.
pub(crate) mod fmt;
mod intrinsics;
pub mod adc;
pub mod dma;
pub mod gpio;
pub mod i2c;
pub mod interrupt;
pub mod rom_data;
pub mod rtc;
pub mod spi;
#[cfg(feature = "time-driver")]
pub mod timer;
pub mod uart;
#[cfg(feature = "nightly")]
pub mod usb;
mod clocks;
pub mod flash;
mod reset;
// Reexports
@ -70,6 +78,9 @@ embassy_hal_common::peripherals! {
SPI0,
SPI1,
I2C0,
I2C1,
DMA_CH0,
DMA_CH1,
DMA_CH2,
@ -84,6 +95,12 @@ embassy_hal_common::peripherals! {
DMA_CH11,
USB,
RTC,
FLASH,
ADC,
}
#[link_section = ".boot2"]
@ -108,6 +125,7 @@ pub fn init(_config: config::Config) -> Peripherals {
unsafe {
clocks::init();
#[cfg(feature = "time-driver")]
timer::init();
dma::init();
}

733
embassy-rp/src/rom_data.rs Normal file
View File

@ -0,0 +1,733 @@
//! Functions and data from the RPI Bootrom.
//!
//! From the [RP2040 datasheet](https://datasheets.raspberrypi.org/rp2040/rp2040-datasheet.pdf), Section 2.8.2.1:
//!
//! > The Bootrom contains a number of public functions that provide useful
//! > RP2040 functionality that might be needed in the absence of any other code
//! > on the device, as well as highly optimized versions of certain key
//! > functionality that would otherwise have to take up space in most user
//! > binaries.
// Credit: taken from `rp-hal` (also licensed Apache+MIT)
// https://github.com/rp-rs/rp-hal/blob/main/rp2040-hal/src/rom_data.rs
/// A bootrom function table code.
pub type RomFnTableCode = [u8; 2];
/// This function searches for (table)
type RomTableLookupFn<T> = unsafe extern "C" fn(*const u16, u32) -> T;
/// The following addresses are described at `2.8.2. Bootrom Contents`
/// Pointer to the lookup table function supplied by the rom.
const ROM_TABLE_LOOKUP_PTR: *const u16 = 0x0000_0018 as _;
/// Pointer to helper functions lookup table.
const FUNC_TABLE: *const u16 = 0x0000_0014 as _;
/// Pointer to the public data lookup table.
const DATA_TABLE: *const u16 = 0x0000_0016 as _;
/// Address of the version number of the ROM.
const VERSION_NUMBER: *const u8 = 0x0000_0013 as _;
/// Retrive rom content from a table using a code.
fn rom_table_lookup<T>(table: *const u16, tag: RomFnTableCode) -> T {
unsafe {
let rom_table_lookup_ptr: *const u32 = rom_hword_as_ptr(ROM_TABLE_LOOKUP_PTR);
let rom_table_lookup: RomTableLookupFn<T> = core::mem::transmute(rom_table_lookup_ptr);
rom_table_lookup(rom_hword_as_ptr(table) as *const u16, u16::from_le_bytes(tag) as u32)
}
}
/// To save space, the ROM likes to store memory pointers (which are 32-bit on
/// the Cortex-M0+) using only the bottom 16-bits. The assumption is that the
/// values they point at live in the first 64 KiB of ROM, and the ROM is mapped
/// to address `0x0000_0000` and so 16-bits are always sufficient.
///
/// This functions grabs a 16-bit value from ROM and expands it out to a full 32-bit pointer.
unsafe fn rom_hword_as_ptr(rom_address: *const u16) -> *const u32 {
let ptr: u16 = *rom_address;
ptr as *const u32
}
macro_rules! declare_rom_function {
(
$(#[$outer:meta])*
fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty
$lookup:block
) => {
#[doc = r"Additional access for the `"]
#[doc = stringify!($name)]
#[doc = r"` ROM function."]
pub mod $name {
/// Retrieve a function pointer.
#[cfg(not(feature = "rom-func-cache"))]
pub fn ptr() -> extern "C" fn( $($argname: $ty),* ) -> $ret {
let p: *const u32 = $lookup;
unsafe {
let func : extern "C" fn( $($argname: $ty),* ) -> $ret = core::mem::transmute(p);
func
}
}
/// Retrieve a function pointer.
#[cfg(feature = "rom-func-cache")]
pub fn ptr() -> extern "C" fn( $($argname: $ty),* ) -> $ret {
use core::sync::atomic::{AtomicU16, Ordering};
// All pointers in the ROM fit in 16 bits, so we don't need a
// full width word to store the cached value.
static CACHED_PTR: AtomicU16 = AtomicU16::new(0);
// This is safe because the lookup will always resolve
// to the same value. So even if an interrupt or another
// core starts at the same time, it just repeats some
// work and eventually writes back the correct value.
let p: *const u32 = match CACHED_PTR.load(Ordering::Relaxed) {
0 => {
let raw: *const u32 = $lookup;
CACHED_PTR.store(raw as u16, Ordering::Relaxed);
raw
},
val => val as *const u32,
};
unsafe {
let func : extern "C" fn( $($argname: $ty),* ) -> $ret = core::mem::transmute(p);
func
}
}
}
$(#[$outer])*
pub extern "C" fn $name( $($argname: $ty),* ) -> $ret {
$name::ptr()($($argname),*)
}
};
(
$(#[$outer:meta])*
unsafe fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty
$lookup:block
) => {
#[doc = r"Additional access for the `"]
#[doc = stringify!($name)]
#[doc = r"` ROM function."]
pub mod $name {
/// Retrieve a function pointer.
#[cfg(not(feature = "rom-func-cache"))]
pub fn ptr() -> unsafe extern "C" fn( $($argname: $ty),* ) -> $ret {
let p: *const u32 = $lookup;
unsafe {
let func : unsafe extern "C" fn( $($argname: $ty),* ) -> $ret = core::mem::transmute(p);
func
}
}
/// Retrieve a function pointer.
#[cfg(feature = "rom-func-cache")]
pub fn ptr() -> unsafe extern "C" fn( $($argname: $ty),* ) -> $ret {
use core::sync::atomic::{AtomicU16, Ordering};
// All pointers in the ROM fit in 16 bits, so we don't need a
// full width word to store the cached value.
static CACHED_PTR: AtomicU16 = AtomicU16::new(0);
// This is safe because the lookup will always resolve
// to the same value. So even if an interrupt or another
// core starts at the same time, it just repeats some
// work and eventually writes back the correct value.
let p: *const u32 = match CACHED_PTR.load(Ordering::Relaxed) {
0 => {
let raw: *const u32 = $lookup;
CACHED_PTR.store(raw as u16, Ordering::Relaxed);
raw
},
val => val as *const u32,
};
unsafe {
let func : unsafe extern "C" fn( $($argname: $ty),* ) -> $ret = core::mem::transmute(p);
func
}
}
}
$(#[$outer])*
pub unsafe extern "C" fn $name( $($argname: $ty),* ) -> $ret {
$name::ptr()($($argname),*)
}
};
}
macro_rules! rom_functions {
() => {};
(
$(#[$outer:meta])*
$c:literal fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty;
$($rest:tt)*
) => {
declare_rom_function! {
$(#[$outer])*
fn $name( $($argname: $ty),* ) -> $ret {
$crate::rom_data::rom_table_lookup($crate::rom_data::FUNC_TABLE, *$c)
}
}
rom_functions!($($rest)*);
};
(
$(#[$outer:meta])*
$c:literal unsafe fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty;
$($rest:tt)*
) => {
declare_rom_function! {
$(#[$outer])*
unsafe fn $name( $($argname: $ty),* ) -> $ret {
$crate::rom_data::rom_table_lookup($crate::rom_data::FUNC_TABLE, *$c)
}
}
rom_functions!($($rest)*);
};
}
rom_functions! {
/// Return a count of the number of 1 bits in value.
b"P3" fn popcount32(value: u32) -> u32;
/// Return the bits of value in the reverse order.
b"R3" fn reverse32(value: u32) -> u32;
/// Return the number of consecutive high order 0 bits of value. If value is zero, returns 32.
b"L3" fn clz32(value: u32) -> u32;
/// Return the number of consecutive low order 0 bits of value. If value is zero, returns 32.
b"T3" fn ctz32(value: u32) -> u32;
/// Resets the RP2040 and uses the watchdog facility to re-start in BOOTSEL mode:
/// * gpio_activity_pin_mask is provided to enable an 'activity light' via GPIO attached LED
/// for the USB Mass Storage Device:
/// * 0 No pins are used as per cold boot.
/// * Otherwise a single bit set indicating which GPIO pin should be set to output and
/// raised whenever there is mass storage activity from the host.
/// * disable_interface_mask may be used to control the exposed USB interfaces:
/// * 0 To enable both interfaces (as per cold boot).
/// * 1 To disable the USB Mass Storage Interface.
/// * 2 to Disable the USB PICOBOOT Interface.
b"UB" fn reset_to_usb_boot(gpio_activity_pin_mask: u32, disable_interface_mask: u32) -> ();
/// Sets n bytes start at ptr to the value c and returns ptr
b"MS" unsafe fn memset(ptr: *mut u8, c: u8, n: u32) -> *mut u8;
/// Sets n bytes start at ptr to the value c and returns ptr.
///
/// Note this is a slightly more efficient variant of _memset that may only
/// be used if ptr is word aligned.
// Note the datasheet does not match the actual ROM for the code here, see
// https://github.com/raspberrypi/pico-feedback/issues/217
b"S4" unsafe fn memset4(ptr: *mut u32, c: u8, n: u32) -> *mut u32;
/// Copies n bytes starting at src to dest and returns dest. The results are undefined if the
/// regions overlap.
b"MC" unsafe fn memcpy(dest: *mut u8, src: *const u8, n: u32) -> *mut u8;
/// Copies n bytes starting at src to dest and returns dest. The results are undefined if the
/// regions overlap.
///
/// Note this is a slightly more efficient variant of _memcpy that may only be
/// used if dest and src are word aligned.
b"C4" unsafe fn memcpy44(dest: *mut u32, src: *const u32, n: u32) -> *mut u8;
/// Restore all QSPI pad controls to their default state, and connect the SSI to the QSPI pads.
b"IF" unsafe fn connect_internal_flash() -> ();
/// First set up the SSI for serial-mode operations, then issue the fixed XIP exit sequence.
///
/// Note that the bootrom code uses the IO forcing logic to drive the CS pin, which must be
/// cleared before returning the SSI to XIP mode (e.g. by a call to _flash_flush_cache). This
/// function configures the SSI with a fixed SCK clock divisor of /6.
b"EX" unsafe fn flash_exit_xip() -> ();
/// Erase a count bytes, starting at addr (offset from start of flash). Optionally, pass a
/// block erase command e.g. D8h block erase, and the size of the block erased by this
/// command — this function will use the larger block erase where possible, for much higher
/// erase speed. addr must be aligned to a 4096-byte sector, and count must be a multiple of
/// 4096 bytes.
b"RE" unsafe fn flash_range_erase(addr: u32, count: usize, block_size: u32, block_cmd: u8) -> ();
/// Program data to a range of flash addresses starting at `addr` (and
/// offset from the start of flash) and `count` bytes in size. The value
/// `addr` must be aligned to a 256-byte boundary, and `count` must be a
/// multiple of 256.
b"RP" unsafe fn flash_range_program(addr: u32, data: *const u8, count: usize) -> ();
/// Flush and enable the XIP cache. Also clears the IO forcing on QSPI CSn, so that the SSI can
/// drive the flashchip select as normal.
b"FC" unsafe fn flash_flush_cache() -> ();
/// Configure the SSI to generate a standard 03h serial read command, with 24 address bits,
/// upon each XIP access. This is a very slow XIP configuration, but is very widely supported.
/// The debugger calls this function after performing a flash erase/programming operation, so
/// that the freshly-programmed code and data is visible to the debug host, without having to
/// know exactly what kind of flash device is connected.
b"CX" unsafe fn flash_enter_cmd_xip() -> ();
/// This is the method that is entered by core 1 on reset to wait to be launched by core 0.
/// There are few cases where you should call this method (resetting core 1 is much better).
/// This method does not return and should only ever be called on core 1.
b"WV" unsafe fn wait_for_vector() -> !;
}
// Various C intrinsics in the ROM
intrinsics! {
#[alias = __popcountdi2]
extern "C" fn __popcountsi2(x: u32) -> u32 {
popcount32(x)
}
#[alias = __clzdi2]
extern "C" fn __clzsi2(x: u32) -> u32 {
clz32(x)
}
#[alias = __ctzdi2]
extern "C" fn __ctzsi2(x: u32) -> u32 {
ctz32(x)
}
// __rbit is only unofficial, but it show up in the ARM documentation,
// so may as well hook it up.
#[alias = __rbitl]
extern "C" fn __rbit(x: u32) -> u32 {
reverse32(x)
}
unsafe extern "aapcs" fn __aeabi_memset(dest: *mut u8, n: usize, c: i32) -> () {
// Different argument order
memset(dest, c as u8, n as u32);
}
#[alias = __aeabi_memset8]
unsafe extern "aapcs" fn __aeabi_memset4(dest: *mut u8, n: usize, c: i32) -> () {
// Different argument order
memset4(dest as *mut u32, c as u8, n as u32);
}
unsafe extern "aapcs" fn __aeabi_memclr(dest: *mut u8, n: usize) -> () {
memset(dest, 0, n as u32);
}
#[alias = __aeabi_memclr8]
unsafe extern "aapcs" fn __aeabi_memclr4(dest: *mut u8, n: usize) -> () {
memset4(dest as *mut u32, 0, n as u32);
}
unsafe extern "aapcs" fn __aeabi_memcpy(dest: *mut u8, src: *const u8, n: usize) -> () {
memcpy(dest, src, n as u32);
}
#[alias = __aeabi_memcpy8]
unsafe extern "aapcs" fn __aeabi_memcpy4(dest: *mut u8, src: *const u8, n: usize) -> () {
memcpy44(dest as *mut u32, src as *const u32, n as u32);
}
}
unsafe fn convert_str(s: *const u8) -> &'static str {
let mut end = s;
while *end != 0 {
end = end.add(1);
}
let s = core::slice::from_raw_parts(s, end.offset_from(s) as usize);
core::str::from_utf8_unchecked(s)
}
/// The version number of the rom.
pub fn rom_version_number() -> u8 {
unsafe { *VERSION_NUMBER }
}
/// The Raspberry Pi Trading Ltd copyright string.
pub fn copyright_string() -> &'static str {
let s: *const u8 = rom_table_lookup(DATA_TABLE, *b"CR");
unsafe { convert_str(s) }
}
/// The 8 most significant hex digits of the Bootrom git revision.
pub fn git_revision() -> u32 {
let s: *const u32 = rom_table_lookup(DATA_TABLE, *b"GR");
unsafe { *s }
}
/// The start address of the floating point library code and data.
///
/// This and fplib_end along with the individual function pointers in
/// soft_float_table can be used to copy the floating point implementation into
/// RAM if desired.
pub fn fplib_start() -> *const u8 {
rom_table_lookup(DATA_TABLE, *b"FS")
}
/// See Table 180 in the RP2040 datasheet for the contents of this table.
pub fn soft_float_table() -> *const usize {
rom_table_lookup(DATA_TABLE, *b"SF")
}
/// The end address of the floating point library code and data.
pub fn fplib_end() -> *const u8 {
rom_table_lookup(DATA_TABLE, *b"FE")
}
/// This entry is only present in the V2 bootrom. See Table 182 in the RP2040 datasheet for the contents of this table.
pub fn soft_double_table() -> *const usize {
if rom_version_number() < 2 {
panic!(
"Double precision operations require V2 bootrom (found: V{})",
rom_version_number()
);
}
rom_table_lookup(DATA_TABLE, *b"SD")
}
/// ROM functions using single-precision arithmetic (i.e. 'f32' in Rust terms)
pub mod float_funcs {
macro_rules! make_functions {
(
$(
$(#[$outer:meta])*
$offset:literal $name:ident (
$( $aname:ident : $aty:ty ),*
) -> $ret:ty;
)*
) => {
$(
declare_rom_function! {
$(#[$outer])*
fn $name( $( $aname : $aty ),* ) -> $ret {
let table: *const usize = $crate::rom_data::soft_float_table();
unsafe {
// This is the entry in the table. Our offset is given as a
// byte offset, but we want the table index (each pointer in
// the table is 4 bytes long)
let entry: *const usize = table.offset($offset / 4);
// Read the pointer from the table
core::ptr::read(entry) as *const u32
}
}
}
)*
}
}
make_functions! {
/// Calculates `a + b`
0x00 fadd(a: f32, b: f32) -> f32;
/// Calculates `a - b`
0x04 fsub(a: f32, b: f32) -> f32;
/// Calculates `a * b`
0x08 fmul(a: f32, b: f32) -> f32;
/// Calculates `a / b`
0x0c fdiv(a: f32, b: f32) -> f32;
// 0x10 and 0x14 are deprecated
/// Calculates `sqrt(v)` (or return -Infinity if v is negative)
0x18 fsqrt(v: f32) -> f32;
/// Converts an f32 to a signed integer,
/// rounding towards -Infinity, and clamping the result to lie within the
/// range `-0x80000000` to `0x7FFFFFFF`
0x1c float_to_int(v: f32) -> i32;
/// Converts an f32 to an signed fixed point
/// integer representation where n specifies the position of the binary
/// point in the resulting fixed point representation, e.g.
/// `f(0.5f, 16) == 0x8000`. This method rounds towards -Infinity,
/// and clamps the resulting integer to lie within the range `0x00000000` to
/// `0xFFFFFFFF`
0x20 float_to_fix(v: f32, n: i32) -> i32;
/// Converts an f32 to an unsigned integer,
/// rounding towards -Infinity, and clamping the result to lie within the
/// range `0x00000000` to `0xFFFFFFFF`
0x24 float_to_uint(v: f32) -> u32;
/// Converts an f32 to an unsigned fixed point
/// integer representation where n specifies the position of the binary
/// point in the resulting fixed point representation, e.g.
/// `f(0.5f, 16) == 0x8000`. This method rounds towards -Infinity,
/// and clamps the resulting integer to lie within the range `0x00000000` to
/// `0xFFFFFFFF`
0x28 float_to_ufix(v: f32, n: i32) -> u32;
/// Converts a signed integer to the nearest
/// f32 value, rounding to even on tie
0x2c int_to_float(v: i32) -> f32;
/// Converts a signed fixed point integer
/// representation to the nearest f32 value, rounding to even on tie. `n`
/// specifies the position of the binary point in fixed point, so `f =
/// nearest(v/(2^n))`
0x30 fix_to_float(v: i32, n: i32) -> f32;
/// Converts an unsigned integer to the nearest
/// f32 value, rounding to even on tie
0x34 uint_to_float(v: u32) -> f32;
/// Converts an unsigned fixed point integer
/// representation to the nearest f32 value, rounding to even on tie. `n`
/// specifies the position of the binary point in fixed point, so `f =
/// nearest(v/(2^n))`
0x38 ufix_to_float(v: u32, n: i32) -> f32;
/// Calculates the cosine of `angle`. The value
/// of `angle` is in radians, and must be in the range `-1024` to `1024`
0x3c fcos(angle: f32) -> f32;
/// Calculates the sine of `angle`. The value of
/// `angle` is in radians, and must be in the range `-1024` to `1024`
0x40 fsin(angle: f32) -> f32;
/// Calculates the tangent of `angle`. The value
/// of `angle` is in radians, and must be in the range `-1024` to `1024`
0x44 ftan(angle: f32) -> f32;
// 0x48 is deprecated
/// Calculates the exponential value of `v`,
/// i.e. `e ** v`
0x4c fexp(v: f32) -> f32;
/// Calculates the natural logarithm of `v`. If `v <= 0` return -Infinity
0x50 fln(v: f32) -> f32;
}
macro_rules! make_functions_v2 {
(
$(
$(#[$outer:meta])*
$offset:literal $name:ident (
$( $aname:ident : $aty:ty ),*
) -> $ret:ty;
)*
) => {
$(
declare_rom_function! {
$(#[$outer])*
fn $name( $( $aname : $aty ),* ) -> $ret {
if $crate::rom_data::rom_version_number() < 2 {
panic!(
"Floating point function requires V2 bootrom (found: V{})",
$crate::rom_data::rom_version_number()
);
}
let table: *const usize = $crate::rom_data::soft_float_table();
unsafe {
// This is the entry in the table. Our offset is given as a
// byte offset, but we want the table index (each pointer in
// the table is 4 bytes long)
let entry: *const usize = table.offset($offset / 4);
// Read the pointer from the table
core::ptr::read(entry) as *const u32
}
}
}
)*
}
}
// These are only on BootROM v2 or higher
make_functions_v2! {
/// Compares two floating point numbers, returning:
/// • 0 if a == b
/// • -1 if a < b
/// • 1 if a > b
0x54 fcmp(a: f32, b: f32) -> i32;
/// Computes the arc tangent of `y/x` using the
/// signs of arguments to determine the correct quadrant
0x58 fatan2(y: f32, x: f32) -> f32;
/// Converts a signed 64-bit integer to the
/// nearest f32 value, rounding to even on tie
0x5c int64_to_float(v: i64) -> f32;
/// Converts a signed fixed point 64-bit integer
/// representation to the nearest f32 value, rounding to even on tie. `n`
/// specifies the position of the binary point in fixed point, so `f =
/// nearest(v/(2^n))`
0x60 fix64_to_float(v: i64, n: i32) -> f32;
/// Converts an unsigned 64-bit integer to the
/// nearest f32 value, rounding to even on tie
0x64 uint64_to_float(v: u64) -> f32;
/// Converts an unsigned fixed point 64-bit
/// integer representation to the nearest f32 value, rounding to even on
/// tie. `n` specifies the position of the binary point in fixed point, so
/// `f = nearest(v/(2^n))`
0x68 ufix64_to_float(v: u64, n: i32) -> f32;
/// Convert an f32 to a signed 64-bit integer, rounding towards -Infinity,
/// and clamping the result to lie within the range `-0x8000000000000000` to
/// `0x7FFFFFFFFFFFFFFF`
0x6c float_to_int64(v: f32) -> i64;
/// Converts an f32 to a signed fixed point
/// 64-bit integer representation where n specifies the position of the
/// binary point in the resulting fixed point representation - e.g. `f(0.5f,
/// 16) == 0x8000`. This method rounds towards -Infinity, and clamps the
/// resulting integer to lie within the range `-0x8000000000000000` to
/// `0x7FFFFFFFFFFFFFFF`
0x70 float_to_fix64(v: f32, n: i32) -> f32;
/// Converts an f32 to an unsigned 64-bit
/// integer, rounding towards -Infinity, and clamping the result to lie
/// within the range `0x0000000000000000` to `0xFFFFFFFFFFFFFFFF`
0x74 float_to_uint64(v: f32) -> u64;
/// Converts an f32 to an unsigned fixed point
/// 64-bit integer representation where n specifies the position of the
/// binary point in the resulting fixed point representation, e.g. `f(0.5f,
/// 16) == 0x8000`. This method rounds towards -Infinity, and clamps the
/// resulting integer to lie within the range `0x0000000000000000` to
/// `0xFFFFFFFFFFFFFFFF`
0x78 float_to_ufix64(v: f32, n: i32) -> u64;
/// Converts an f32 to an f64.
0x7c float_to_double(v: f32) -> f64;
}
}
/// Functions using double-precision arithmetic (i.e. 'f64' in Rust terms)
pub mod double_funcs {
macro_rules! make_double_funcs {
(
$(
$(#[$outer:meta])*
$offset:literal $name:ident (
$( $aname:ident : $aty:ty ),*
) -> $ret:ty;
)*
) => {
$(
declare_rom_function! {
$(#[$outer])*
fn $name( $( $aname : $aty ),* ) -> $ret {
let table: *const usize = $crate::rom_data::soft_double_table();
unsafe {
// This is the entry in the table. Our offset is given as a
// byte offset, but we want the table index (each pointer in
// the table is 4 bytes long)
let entry: *const usize = table.offset($offset / 4);
// Read the pointer from the table
core::ptr::read(entry) as *const u32
}
}
}
)*
}
}
make_double_funcs! {
/// Calculates `a + b`
0x00 dadd(a: f64, b: f64) -> f64;
/// Calculates `a - b`
0x04 dsub(a: f64, b: f64) -> f64;
/// Calculates `a * b`
0x08 dmul(a: f64, b: f64) -> f64;
/// Calculates `a / b`
0x0c ddiv(a: f64, b: f64) -> f64;
// 0x10 and 0x14 are deprecated
/// Calculates `sqrt(v)` (or return -Infinity if v is negative)
0x18 dsqrt(v: f64) -> f64;
/// Converts an f64 to a signed integer,
/// rounding towards -Infinity, and clamping the result to lie within the
/// range `-0x80000000` to `0x7FFFFFFF`
0x1c double_to_int(v: f64) -> i32;
/// Converts an f64 to an signed fixed point
/// integer representation where n specifies the position of the binary
/// point in the resulting fixed point representation, e.g.
/// `f(0.5f, 16) == 0x8000`. This method rounds towards -Infinity,
/// and clamps the resulting integer to lie within the range `0x00000000` to
/// `0xFFFFFFFF`
0x20 double_to_fix(v: f64, n: i32) -> i32;
/// Converts an f64 to an unsigned integer,
/// rounding towards -Infinity, and clamping the result to lie within the
/// range `0x00000000` to `0xFFFFFFFF`
0x24 double_to_uint(v: f64) -> u32;
/// Converts an f64 to an unsigned fixed point
/// integer representation where n specifies the position of the binary
/// point in the resulting fixed point representation, e.g.
/// `f(0.5f, 16) == 0x8000`. This method rounds towards -Infinity,
/// and clamps the resulting integer to lie within the range `0x00000000` to
/// `0xFFFFFFFF`
0x28 double_to_ufix(v: f64, n: i32) -> u32;
/// Converts a signed integer to the nearest
/// double value, rounding to even on tie
0x2c int_to_double(v: i32) -> f64;
/// Converts a signed fixed point integer
/// representation to the nearest double value, rounding to even on tie. `n`
/// specifies the position of the binary point in fixed point, so `f =
/// nearest(v/(2^n))`
0x30 fix_to_double(v: i32, n: i32) -> f64;
/// Converts an unsigned integer to the nearest
/// double value, rounding to even on tie
0x34 uint_to_double(v: u32) -> f64;
/// Converts an unsigned fixed point integer
/// representation to the nearest double value, rounding to even on tie. `n`
/// specifies the position of the binary point in fixed point, so f =
/// nearest(v/(2^n))
0x38 ufix_to_double(v: u32, n: i32) -> f64;
/// Calculates the cosine of `angle`. The value
/// of `angle` is in radians, and must be in the range `-1024` to `1024`
0x3c dcos(angle: f64) -> f64;
/// Calculates the sine of `angle`. The value of
/// `angle` is in radians, and must be in the range `-1024` to `1024`
0x40 dsin(angle: f64) -> f64;
/// Calculates the tangent of `angle`. The value
/// of `angle` is in radians, and must be in the range `-1024` to `1024`
0x44 dtan(angle: f64) -> f64;
// 0x48 is deprecated
/// Calculates the exponential value of `v`,
/// i.e. `e ** v`
0x4c dexp(v: f64) -> f64;
/// Calculates the natural logarithm of v. If v <= 0 return -Infinity
0x50 dln(v: f64) -> f64;
// These are only on BootROM v2 or higher
/// Compares two floating point numbers, returning:
/// • 0 if a == b
/// • -1 if a < b
/// • 1 if a > b
0x54 dcmp(a: f64, b: f64) -> i32;
/// Computes the arc tangent of `y/x` using the
/// signs of arguments to determine the correct quadrant
0x58 datan2(y: f64, x: f64) -> f64;
/// Converts a signed 64-bit integer to the
/// nearest double value, rounding to even on tie
0x5c int64_to_double(v: i64) -> f64;
/// Converts a signed fixed point 64-bit integer
/// representation to the nearest double value, rounding to even on tie. `n`
/// specifies the position of the binary point in fixed point, so `f =
/// nearest(v/(2^n))`
0x60 fix64_to_doubl(v: i64, n: i32) -> f64;
/// Converts an unsigned 64-bit integer to the
/// nearest double value, rounding to even on tie
0x64 uint64_to_double(v: u64) -> f64;
/// Converts an unsigned fixed point 64-bit
/// integer representation to the nearest double value, rounding to even on
/// tie. `n` specifies the position of the binary point in fixed point, so
/// `f = nearest(v/(2^n))`
0x68 ufix64_to_double(v: u64, n: i32) -> f64;
/// Convert an f64 to a signed 64-bit integer, rounding towards -Infinity,
/// and clamping the result to lie within the range `-0x8000000000000000` to
/// `0x7FFFFFFFFFFFFFFF`
0x6c double_to_int64(v: f64) -> i64;
/// Converts an f64 to a signed fixed point
/// 64-bit integer representation where n specifies the position of the
/// binary point in the resulting fixed point representation - e.g. `f(0.5f,
/// 16) == 0x8000`. This method rounds towards -Infinity, and clamps the
/// resulting integer to lie within the range `-0x8000000000000000` to
/// `0x7FFFFFFFFFFFFFFF`
0x70 double_to_fix64(v: f64, n: i32) -> i64;
/// Converts an f64 to an unsigned 64-bit
/// integer, rounding towards -Infinity, and clamping the result to lie
/// within the range `0x0000000000000000` to `0xFFFFFFFFFFFFFFFF`
0x74 double_to_uint64(v: f64) -> u64;
/// Converts an f64 to an unsigned fixed point
/// 64-bit integer representation where n specifies the position of the
/// binary point in the resulting fixed point representation, e.g. `f(0.5f,
/// 16) == 0x8000`. This method rounds towards -Infinity, and clamps the
/// resulting integer to lie within the range `0x0000000000000000` to
/// `0xFFFFFFFFFFFFFFFF`
0x78 double_to_ufix64(v: f64, n: i32) -> u64;
/// Converts an f64 to an f32
0x7c double_to_float(v: f64) -> f32;
}
}

View File

@ -0,0 +1,62 @@
use chrono::{Datelike, Timelike};
use crate::pac::rtc::regs::{Rtc0, Rtc1, Setup0, Setup1};
/// Alias for [`chrono::NaiveDateTime`]
pub type DateTime = chrono::NaiveDateTime;
/// Alias for [`chrono::Weekday`]
pub type DayOfWeek = chrono::Weekday;
/// Errors regarding the [`DateTime`] and [`DateTimeFilter`] structs.
///
/// [`DateTimeFilter`]: struct.DateTimeFilter.html
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Error {
/// The [DateTime] has an invalid year. The year must be between 0 and 4095.
InvalidYear,
/// The [DateTime] contains an invalid date.
InvalidDate,
/// The [DateTime] contains an invalid time.
InvalidTime,
}
pub(super) fn day_of_week_to_u8(dotw: DayOfWeek) -> u8 {
dotw.num_days_from_sunday() as u8
}
pub(crate) fn validate_datetime(dt: &DateTime) -> Result<(), Error> {
if dt.year() < 0 || dt.year() > 4095 {
// rp2040 can't hold these years
Err(Error::InvalidYear)
} else {
// The rest of the chrono date is assumed to be valid
Ok(())
}
}
pub(super) fn write_setup_0(dt: &DateTime, w: &mut Setup0) {
w.set_year(dt.year() as u16);
w.set_month(dt.month() as u8);
w.set_day(dt.day() as u8);
}
pub(super) fn write_setup_1(dt: &DateTime, w: &mut Setup1) {
w.set_dotw(dt.weekday().num_days_from_sunday() as u8);
w.set_hour(dt.hour() as u8);
w.set_min(dt.minute() as u8);
w.set_sec(dt.second() as u8);
}
pub(super) fn datetime_from_registers(rtc_0: Rtc0, rtc_1: Rtc1) -> Result<DateTime, Error> {
let year = rtc_1.year() as i32;
let month = rtc_1.month() as u32;
let day = rtc_1.day() as u32;
let hour = rtc_0.hour() as u32;
let minute = rtc_0.min() as u32;
let second = rtc_0.sec() as u32;
let date = chrono::NaiveDate::from_ymd_opt(year, month, day).ok_or(Error::InvalidDate)?;
let time = chrono::NaiveTime::from_hms_opt(hour, minute, second).ok_or(Error::InvalidTime)?;
Ok(DateTime::new(date, time))
}

View File

@ -0,0 +1,127 @@
use crate::pac::rtc::regs::{Rtc0, Rtc1, Setup0, Setup1};
/// Errors regarding the [`DateTime`] and [`DateTimeFilter`] structs.
///
/// [`DateTimeFilter`]: struct.DateTimeFilter.html
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Error {
/// The [DateTime] contains an invalid year value. Must be between `0..=4095`.
InvalidYear,
/// The [DateTime] contains an invalid month value. Must be between `1..=12`.
InvalidMonth,
/// The [DateTime] contains an invalid day value. Must be between `1..=31`.
InvalidDay,
/// The [DateTime] contains an invalid day of week. Must be between `0..=6` where 0 is Sunday.
InvalidDayOfWeek(
/// The value of the DayOfWeek that was given.
u8,
),
/// The [DateTime] contains an invalid hour value. Must be between `0..=23`.
InvalidHour,
/// The [DateTime] contains an invalid minute value. Must be between `0..=59`.
InvalidMinute,
/// The [DateTime] contains an invalid second value. Must be between `0..=59`.
InvalidSecond,
}
/// Structure containing date and time information
pub struct DateTime {
/// 0..4095
pub year: u16,
/// 1..12, 1 is January
pub month: u8,
/// 1..28,29,30,31 depending on month
pub day: u8,
///
pub day_of_week: DayOfWeek,
/// 0..23
pub hour: u8,
/// 0..59
pub minute: u8,
/// 0..59
pub second: u8,
}
/// A day of the week
#[repr(u8)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
#[allow(missing_docs)]
pub enum DayOfWeek {
Sunday = 0,
Monday = 1,
Tuesday = 2,
Wednesday = 3,
Thursday = 4,
Friday = 5,
Saturday = 6,
}
fn day_of_week_from_u8(v: u8) -> Result<DayOfWeek, Error> {
Ok(match v {
0 => DayOfWeek::Sunday,
1 => DayOfWeek::Monday,
2 => DayOfWeek::Tuesday,
3 => DayOfWeek::Wednesday,
4 => DayOfWeek::Thursday,
5 => DayOfWeek::Friday,
6 => DayOfWeek::Saturday,
x => return Err(Error::InvalidDayOfWeek(x)),
})
}
pub(super) fn day_of_week_to_u8(dotw: DayOfWeek) -> u8 {
dotw as u8
}
pub(super) fn validate_datetime(dt: &DateTime) -> Result<(), Error> {
if dt.year > 4095 {
Err(Error::InvalidYear)
} else if dt.month < 1 || dt.month > 12 {
Err(Error::InvalidMonth)
} else if dt.day < 1 || dt.day > 31 {
Err(Error::InvalidDay)
} else if dt.hour > 23 {
Err(Error::InvalidHour)
} else if dt.minute > 59 {
Err(Error::InvalidMinute)
} else if dt.second > 59 {
Err(Error::InvalidSecond)
} else {
Ok(())
}
}
pub(super) fn write_setup_0(dt: &DateTime, w: &mut Setup0) {
w.set_year(dt.year);
w.set_month(dt.month);
w.set_day(dt.day);
}
pub(super) fn write_setup_1(dt: &DateTime, w: &mut Setup1) {
w.set_dotw(dt.day_of_week as u8);
w.set_hour(dt.hour);
w.set_min(dt.minute);
w.set_sec(dt.second);
}
pub(super) fn datetime_from_registers(rtc_0: Rtc0, rtc_1: Rtc1) -> Result<DateTime, Error> {
let year = rtc_1.year();
let month = rtc_1.month();
let day = rtc_1.day();
let day_of_week = rtc_0.dotw();
let hour = rtc_0.hour();
let minute = rtc_0.min();
let second = rtc_0.sec();
let day_of_week = day_of_week_from_u8(day_of_week)?;
Ok(DateTime {
year,
month,
day,
day_of_week,
hour,
minute,
second,
})
}

View File

@ -0,0 +1,100 @@
use super::DayOfWeek;
use crate::pac::rtc::regs::{IrqSetup0, IrqSetup1};
/// A filter used for [`RealTimeClock::schedule_alarm`].
///
/// [`RealTimeClock::schedule_alarm`]: struct.RealTimeClock.html#method.schedule_alarm
#[derive(Default)]
pub struct DateTimeFilter {
/// The year that this alarm should trigger on, `None` if the RTC alarm should not trigger on a year value.
pub year: Option<u16>,
/// The month that this alarm should trigger on, `None` if the RTC alarm should not trigger on a month value.
pub month: Option<u8>,
/// The day that this alarm should trigger on, `None` if the RTC alarm should not trigger on a day value.
pub day: Option<u8>,
/// The day of week that this alarm should trigger on, `None` if the RTC alarm should not trigger on a day of week value.
pub day_of_week: Option<DayOfWeek>,
/// The hour that this alarm should trigger on, `None` if the RTC alarm should not trigger on a hour value.
pub hour: Option<u8>,
/// The minute that this alarm should trigger on, `None` if the RTC alarm should not trigger on a minute value.
pub minute: Option<u8>,
/// The second that this alarm should trigger on, `None` if the RTC alarm should not trigger on a second value.
pub second: Option<u8>,
}
impl DateTimeFilter {
/// Set a filter on the given year
pub fn year(mut self, year: u16) -> Self {
self.year = Some(year);
self
}
/// Set a filter on the given month
pub fn month(mut self, month: u8) -> Self {
self.month = Some(month);
self
}
/// Set a filter on the given day
pub fn day(mut self, day: u8) -> Self {
self.day = Some(day);
self
}
/// Set a filter on the given day of the week
pub fn day_of_week(mut self, day_of_week: DayOfWeek) -> Self {
self.day_of_week = Some(day_of_week);
self
}
/// Set a filter on the given hour
pub fn hour(mut self, hour: u8) -> Self {
self.hour = Some(hour);
self
}
/// Set a filter on the given minute
pub fn minute(mut self, minute: u8) -> Self {
self.minute = Some(minute);
self
}
/// Set a filter on the given second
pub fn second(mut self, second: u8) -> Self {
self.second = Some(second);
self
}
}
// register helper functions
impl DateTimeFilter {
pub(super) fn write_setup_0(&self, w: &mut IrqSetup0) {
if let Some(year) = self.year {
w.set_year_ena(true);
w.set_year(year);
}
if let Some(month) = self.month {
w.set_month_ena(true);
w.set_month(month);
}
if let Some(day) = self.day {
w.set_day_ena(true);
w.set_day(day);
}
}
pub(super) fn write_setup_1(&self, w: &mut IrqSetup1) {
if let Some(day_of_week) = self.day_of_week {
w.set_dotw_ena(true);
let bits = super::datetime::day_of_week_to_u8(day_of_week);
w.set_dotw(bits);
}
if let Some(hour) = self.hour {
w.set_hour_ena(true);
w.set_hour(hour);
}
if let Some(minute) = self.minute {
w.set_min_ena(true);
w.set_min(minute);
}
if let Some(second) = self.second {
w.set_sec_ena(true);
w.set_sec(second);
}
}
}

190
embassy-rp/src/rtc/mod.rs Normal file
View File

@ -0,0 +1,190 @@
mod filter;
use embassy_hal_common::{into_ref, Peripheral, PeripheralRef};
pub use self::filter::DateTimeFilter;
#[cfg_attr(feature = "chrono", path = "datetime_chrono.rs")]
#[cfg_attr(not(feature = "chrono"), path = "datetime_no_deps.rs")]
mod datetime;
pub use self::datetime::{DateTime, DayOfWeek, Error as DateTimeError};
use crate::clocks::clk_rtc_freq;
/// A reference to the real time clock of the system
pub struct RealTimeClock<'d, T: Instance> {
inner: PeripheralRef<'d, T>,
}
impl<'d, T: Instance> RealTimeClock<'d, T> {
/// Create a new instance of the real time clock, with the given date as an initial value.
///
/// # Errors
///
/// Will return `RtcError::InvalidDateTime` if the datetime is not a valid range.
pub fn new(inner: impl Peripheral<P = T> + 'd, initial_date: DateTime) -> Result<Self, RtcError> {
into_ref!(inner);
// Set the RTC divider
unsafe {
inner
.regs()
.clkdiv_m1()
.write(|w| w.set_clkdiv_m1(clk_rtc_freq() as u16 - 1))
};
let mut result = Self { inner };
result.set_leap_year_check(true); // should be on by default, make sure this is the case.
result.set_datetime(initial_date)?;
Ok(result)
}
/// Enable or disable the leap year check. The rp2040 chip will always add a Feb 29th on every year that is divisable by 4, but this may be incorrect (e.g. on century years). This function allows you to disable this check.
///
/// Leap year checking is enabled by default.
pub fn set_leap_year_check(&mut self, leap_year_check_enabled: bool) {
unsafe {
self.inner
.regs()
.ctrl()
.modify(|w| w.set_force_notleapyear(!leap_year_check_enabled))
};
}
/// Checks to see if this RealTimeClock is running
pub fn is_running(&self) -> bool {
unsafe { self.inner.regs().ctrl().read().rtc_active() }
}
/// Set the datetime to a new value.
///
/// # Errors
///
/// Will return `RtcError::InvalidDateTime` if the datetime is not a valid range.
pub fn set_datetime(&mut self, t: DateTime) -> Result<(), RtcError> {
self::datetime::validate_datetime(&t).map_err(RtcError::InvalidDateTime)?;
// disable RTC while we configure it
unsafe {
self.inner.regs().ctrl().modify(|w| w.set_rtc_enable(false));
while self.inner.regs().ctrl().read().rtc_active() {
core::hint::spin_loop();
}
self.inner.regs().setup_0().write(|w| {
self::datetime::write_setup_0(&t, w);
});
self.inner.regs().setup_1().write(|w| {
self::datetime::write_setup_1(&t, w);
});
// Load the new datetime and re-enable RTC
self.inner.regs().ctrl().write(|w| w.set_load(true));
self.inner.regs().ctrl().write(|w| w.set_rtc_enable(true));
while !self.inner.regs().ctrl().read().rtc_active() {
core::hint::spin_loop();
}
}
Ok(())
}
/// Return the current datetime.
///
/// # Errors
///
/// Will return an `RtcError::InvalidDateTime` if the stored value in the system is not a valid [`DayOfWeek`].
pub fn now(&self) -> Result<DateTime, RtcError> {
if !self.is_running() {
return Err(RtcError::NotRunning);
}
let rtc_0 = unsafe { self.inner.regs().rtc_0().read() };
let rtc_1 = unsafe { self.inner.regs().rtc_1().read() };
self::datetime::datetime_from_registers(rtc_0, rtc_1).map_err(RtcError::InvalidDateTime)
}
/// Disable the alarm that was scheduled with [`schedule_alarm`].
///
/// [`schedule_alarm`]: #method.schedule_alarm
pub fn disable_alarm(&mut self) {
unsafe {
self.inner.regs().irq_setup_0().modify(|s| s.set_match_ena(false));
while self.inner.regs().irq_setup_0().read().match_active() {
core::hint::spin_loop();
}
}
}
/// Schedule an alarm. The `filter` determines at which point in time this alarm is set.
///
/// Keep in mind that the filter only triggers on the specified time. If you want to schedule this alarm every minute, you have to call:
/// ```no_run
/// # #[cfg(feature = "chrono")]
/// # fn main() { }
/// # #[cfg(not(feature = "chrono"))]
/// # fn main() {
/// # use embassy_rp::rtc::{RealTimeClock, DateTimeFilter};
/// # let mut real_time_clock: RealTimeClock = unsafe { core::mem::zeroed() };
/// let now = real_time_clock.now().unwrap();
/// real_time_clock.schedule_alarm(
/// DateTimeFilter::default()
/// .minute(if now.minute == 59 { 0 } else { now.minute + 1 })
/// );
/// # }
/// ```
pub fn schedule_alarm(&mut self, filter: DateTimeFilter) {
self.disable_alarm();
unsafe {
self.inner.regs().irq_setup_0().write(|w| {
filter.write_setup_0(w);
});
self.inner.regs().irq_setup_1().write(|w| {
filter.write_setup_1(w);
});
self.inner.regs().inte().modify(|w| w.set_rtc(true));
// Set the enable bit and check if it is set
self.inner.regs().irq_setup_0().modify(|w| w.set_match_ena(true));
while !self.inner.regs().irq_setup_0().read().match_active() {
core::hint::spin_loop();
}
}
}
/// Clear the interrupt. This should be called every time the `RTC_IRQ` interrupt is triggered,
/// or the next [`schedule_alarm`] will never fire.
///
/// [`schedule_alarm`]: #method.schedule_alarm
pub fn clear_interrupt(&mut self) {
self.disable_alarm();
}
}
/// Errors that can occur on methods on [RtcClock]
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum RtcError {
/// An invalid DateTime was given or stored on the hardware.
InvalidDateTime(DateTimeError),
/// The RTC clock is not running
NotRunning,
}
mod sealed {
pub trait Instance {
fn regs(&self) -> crate::pac::rtc::Rtc;
}
}
pub trait Instance: sealed::Instance {}
impl sealed::Instance for crate::peripherals::RTC {
fn regs(&self) -> crate::pac::rtc::Rtc {
crate::pac::RTC
}
}
impl Instance for crate::peripherals::RTC {}

View File

@ -1,7 +1,11 @@
use core::marker::PhantomData;
use embassy_embedded_hal::SetConfig;
use embassy_futures::join::join;
use embassy_hal_common::{into_ref, PeripheralRef};
pub use embedded_hal_02::spi::{Phase, Polarity};
use crate::dma::{AnyChannel, Channel};
use crate::gpio::sealed::Pin as _;
use crate::gpio::{AnyPin, Pin as GpioPin};
use crate::{pac, peripherals, Peripheral};
@ -30,8 +34,11 @@ impl Default for Config {
}
}
pub struct Spi<'d, T: Instance> {
pub struct Spi<'d, T: Instance, M: Mode> {
inner: PeripheralRef<'d, T>,
tx_dma: Option<PeripheralRef<'d, AnyChannel>>,
rx_dma: Option<PeripheralRef<'d, AnyChannel>>,
phantom: PhantomData<(&'d mut T, M)>,
}
fn div_roundup(a: u32, b: u32) -> u32 {
@ -57,51 +64,15 @@ fn calc_prescs(freq: u32) -> (u8, u8) {
((presc * 2) as u8, (postdiv - 1) as u8)
}
impl<'d, T: Instance> Spi<'d, T> {
pub fn new(
inner: impl Peripheral<P = T> + 'd,
clk: impl Peripheral<P = impl ClkPin<T> + 'd> + 'd,
mosi: impl Peripheral<P = impl MosiPin<T> + 'd> + 'd,
miso: impl Peripheral<P = impl MisoPin<T> + 'd> + 'd,
config: Config,
) -> Self {
into_ref!(clk, mosi, miso);
Self::new_inner(
inner,
Some(clk.map_into()),
Some(mosi.map_into()),
Some(miso.map_into()),
None,
config,
)
}
pub fn new_txonly(
inner: impl Peripheral<P = T> + 'd,
clk: impl Peripheral<P = impl ClkPin<T> + 'd> + 'd,
mosi: impl Peripheral<P = impl MosiPin<T> + 'd> + 'd,
config: Config,
) -> Self {
into_ref!(clk, mosi);
Self::new_inner(inner, Some(clk.map_into()), Some(mosi.map_into()), None, None, config)
}
pub fn new_rxonly(
inner: impl Peripheral<P = T> + 'd,
clk: impl Peripheral<P = impl ClkPin<T> + 'd> + 'd,
miso: impl Peripheral<P = impl MisoPin<T> + 'd> + 'd,
config: Config,
) -> Self {
into_ref!(clk, miso);
Self::new_inner(inner, Some(clk.map_into()), None, Some(miso.map_into()), None, config)
}
impl<'d, T: Instance, M: Mode> Spi<'d, T, M> {
fn new_inner(
inner: impl Peripheral<P = T> + 'd,
clk: Option<PeripheralRef<'d, AnyPin>>,
mosi: Option<PeripheralRef<'d, AnyPin>>,
miso: Option<PeripheralRef<'d, AnyPin>>,
cs: Option<PeripheralRef<'d, AnyPin>>,
tx_dma: Option<PeripheralRef<'d, AnyChannel>>,
rx_dma: Option<PeripheralRef<'d, AnyChannel>>,
config: Config,
) -> Self {
into_ref!(inner);
@ -134,7 +105,12 @@ impl<'d, T: Instance> Spi<'d, T> {
pin.io().ctrl().write(|w| w.set_funcsel(1));
}
}
Self { inner }
Self {
inner,
tx_dma,
rx_dma,
phantom: PhantomData,
}
}
pub fn blocking_write(&mut self, data: &[u8]) -> Result<(), Error> {
@ -225,19 +201,236 @@ impl<'d, T: Instance> Spi<'d, T> {
}
}
impl<'d, T: Instance> Spi<'d, T, Blocking> {
pub fn new_blocking(
inner: impl Peripheral<P = T> + 'd,
clk: impl Peripheral<P = impl ClkPin<T> + 'd> + 'd,
mosi: impl Peripheral<P = impl MosiPin<T> + 'd> + 'd,
miso: impl Peripheral<P = impl MisoPin<T> + 'd> + 'd,
config: Config,
) -> Self {
into_ref!(clk, mosi, miso);
Self::new_inner(
inner,
Some(clk.map_into()),
Some(mosi.map_into()),
Some(miso.map_into()),
None,
None,
None,
config,
)
}
pub fn new_blocking_txonly(
inner: impl Peripheral<P = T> + 'd,
clk: impl Peripheral<P = impl ClkPin<T> + 'd> + 'd,
mosi: impl Peripheral<P = impl MosiPin<T> + 'd> + 'd,
config: Config,
) -> Self {
into_ref!(clk, mosi);
Self::new_inner(
inner,
Some(clk.map_into()),
Some(mosi.map_into()),
None,
None,
None,
None,
config,
)
}
pub fn new_blocking_rxonly(
inner: impl Peripheral<P = T> + 'd,
clk: impl Peripheral<P = impl ClkPin<T> + 'd> + 'd,
miso: impl Peripheral<P = impl MisoPin<T> + 'd> + 'd,
config: Config,
) -> Self {
into_ref!(clk, miso);
Self::new_inner(
inner,
Some(clk.map_into()),
None,
Some(miso.map_into()),
None,
None,
None,
config,
)
}
}
impl<'d, T: Instance> Spi<'d, T, Async> {
pub fn new(
inner: impl Peripheral<P = T> + 'd,
clk: impl Peripheral<P = impl ClkPin<T> + 'd> + 'd,
mosi: impl Peripheral<P = impl MosiPin<T> + 'd> + 'd,
miso: impl Peripheral<P = impl MisoPin<T> + 'd> + 'd,
tx_dma: impl Peripheral<P = impl Channel> + 'd,
rx_dma: impl Peripheral<P = impl Channel> + 'd,
config: Config,
) -> Self {
into_ref!(tx_dma, rx_dma, clk, mosi, miso);
Self::new_inner(
inner,
Some(clk.map_into()),
Some(mosi.map_into()),
Some(miso.map_into()),
None,
Some(tx_dma.map_into()),
Some(rx_dma.map_into()),
config,
)
}
pub fn new_txonly(
inner: impl Peripheral<P = T> + 'd,
clk: impl Peripheral<P = impl ClkPin<T> + 'd> + 'd,
mosi: impl Peripheral<P = impl MosiPin<T> + 'd> + 'd,
tx_dma: impl Peripheral<P = impl Channel> + 'd,
config: Config,
) -> Self {
into_ref!(tx_dma, clk, mosi);
Self::new_inner(
inner,
Some(clk.map_into()),
Some(mosi.map_into()),
None,
None,
Some(tx_dma.map_into()),
None,
config,
)
}
pub fn new_rxonly(
inner: impl Peripheral<P = T> + 'd,
clk: impl Peripheral<P = impl ClkPin<T> + 'd> + 'd,
miso: impl Peripheral<P = impl MisoPin<T> + 'd> + 'd,
rx_dma: impl Peripheral<P = impl Channel> + 'd,
config: Config,
) -> Self {
into_ref!(rx_dma, clk, miso);
Self::new_inner(
inner,
Some(clk.map_into()),
None,
Some(miso.map_into()),
None,
None,
Some(rx_dma.map_into()),
config,
)
}
pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> {
let tx_ch = self.tx_dma.as_mut().unwrap();
let tx_transfer = unsafe {
self.inner.regs().dmacr().modify(|reg| {
reg.set_txdmae(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.
crate::dma::write(tx_ch, buffer, self.inner.regs().dr().ptr() as *mut _, T::TX_DREQ)
};
tx_transfer.await;
let p = self.inner.regs();
unsafe {
while p.sr().read().bsy() {}
// clear RX FIFO contents to prevent stale reads
while p.sr().read().rne() {
let _: u16 = p.dr().read().data();
}
// clear RX overrun interrupt
p.icr().write(|w| w.set_roric(true));
}
Ok(())
}
pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> {
unsafe {
self.inner.regs().dmacr().write(|reg| {
reg.set_rxdmae(true);
reg.set_txdmae(true);
})
};
let tx_ch = self.tx_dma.as_mut().unwrap();
let tx_transfer = unsafe {
// If we don't assign future to a variable, the data register pointer
// is held across an await and makes the future non-Send.
crate::dma::write_repeated(tx_ch, self.inner.regs().dr().ptr() as *mut u8, buffer.len(), T::TX_DREQ)
};
let rx_ch = self.rx_dma.as_mut().unwrap();
let rx_transfer = unsafe {
// If we don't assign future to a variable, the data register pointer
// is held across an await and makes the future non-Send.
crate::dma::read(rx_ch, self.inner.regs().dr().ptr() as *const _, buffer, T::RX_DREQ)
};
join(tx_transfer, rx_transfer).await;
Ok(())
}
pub async fn transfer(&mut self, rx_buffer: &mut [u8], tx_buffer: &[u8]) -> Result<(), Error> {
self.transfer_inner(rx_buffer, tx_buffer).await
}
pub async fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Error> {
self.transfer_inner(words, words).await
}
async fn transfer_inner(&mut self, rx_ptr: *mut [u8], tx_ptr: *const [u8]) -> Result<(), Error> {
let (_, from_len) = crate::dma::slice_ptr_parts(tx_ptr);
let (_, to_len) = crate::dma::slice_ptr_parts_mut(rx_ptr);
assert_eq!(from_len, to_len);
unsafe {
self.inner.regs().dmacr().write(|reg| {
reg.set_rxdmae(true);
reg.set_txdmae(true);
})
};
let tx_ch = self.tx_dma.as_mut().unwrap();
let tx_transfer = unsafe {
// If we don't assign future to a variable, the data register pointer
// is held across an await and makes the future non-Send.
crate::dma::write(tx_ch, tx_ptr, self.inner.regs().dr().ptr() as *mut _, T::TX_DREQ)
};
let rx_ch = self.rx_dma.as_mut().unwrap();
let rx_transfer = unsafe {
// If we don't assign future to a variable, the data register pointer
// is held across an await and makes the future non-Send.
crate::dma::read(rx_ch, self.inner.regs().dr().ptr() as *const _, rx_ptr, T::RX_DREQ)
};
join(tx_transfer, rx_transfer).await;
Ok(())
}
}
mod sealed {
use super::*;
pub trait Mode {}
pub trait Instance {
const TX_DREQ: u8;
const RX_DREQ: u8;
fn regs(&self) -> pac::spi::Spi;
}
}
pub trait Mode: sealed::Mode {}
pub trait Instance: sealed::Instance {}
macro_rules! impl_instance {
($type:ident, $irq:ident) => {
($type:ident, $irq:ident, $tx_dreq:expr, $rx_dreq:expr) => {
impl sealed::Instance for peripherals::$type {
const TX_DREQ: u8 = $tx_dreq;
const RX_DREQ: u8 = $rx_dreq;
fn regs(&self) -> pac::spi::Spi {
pac::$type
}
@ -246,8 +439,8 @@ macro_rules! impl_instance {
};
}
impl_instance!(SPI0, Spi0);
impl_instance!(SPI1, Spi1);
impl_instance!(SPI0, Spi0, 16, 17);
impl_instance!(SPI1, Spi1, 18, 19);
pub trait ClkPin<T: Instance>: GpioPin {}
pub trait CsPin<T: Instance>: GpioPin {}
@ -281,12 +474,25 @@ impl_pin!(PIN_17, SPI0, CsPin);
impl_pin!(PIN_18, SPI0, ClkPin);
impl_pin!(PIN_19, SPI0, MosiPin);
macro_rules! impl_mode {
($name:ident) => {
impl sealed::Mode for $name {}
impl Mode for $name {}
};
}
pub struct Blocking;
pub struct Async;
impl_mode!(Blocking);
impl_mode!(Async);
// ====================
mod eh02 {
use super::*;
impl<'d, T: Instance> embedded_hal_02::blocking::spi::Transfer<u8> for Spi<'d, T> {
impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::spi::Transfer<u8> for Spi<'d, T, M> {
type Error = Error;
fn transfer<'w>(&mut self, words: &'w mut [u8]) -> Result<&'w [u8], Self::Error> {
self.blocking_transfer_in_place(words)?;
@ -294,7 +500,7 @@ mod eh02 {
}
}
impl<'d, T: Instance> embedded_hal_02::blocking::spi::Write<u8> for Spi<'d, T> {
impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::spi::Write<u8> for Spi<'d, T, M> {
type Error = Error;
fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {
@ -313,29 +519,29 @@ mod eh1 {
}
}
impl<'d, T: Instance> embedded_hal_1::spi::ErrorType for Spi<'d, T> {
impl<'d, T: Instance, M: Mode> embedded_hal_1::spi::ErrorType for Spi<'d, T, M> {
type Error = Error;
}
impl<'d, T: Instance> embedded_hal_1::spi::blocking::SpiBusFlush for Spi<'d, T> {
impl<'d, T: Instance, M: Mode> embedded_hal_1::spi::SpiBusFlush for Spi<'d, T, M> {
fn flush(&mut self) -> Result<(), Self::Error> {
Ok(())
}
}
impl<'d, T: Instance> embedded_hal_1::spi::blocking::SpiBusRead<u8> for Spi<'d, T> {
impl<'d, T: Instance, M: Mode> embedded_hal_1::spi::SpiBusRead<u8> for Spi<'d, T, M> {
fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
self.blocking_transfer(words, &[])
}
}
impl<'d, T: Instance> embedded_hal_1::spi::blocking::SpiBusWrite<u8> for Spi<'d, T> {
impl<'d, T: Instance, M: Mode> embedded_hal_1::spi::SpiBusWrite<u8> for Spi<'d, T, M> {
fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {
self.blocking_write(words)
}
}
impl<'d, T: Instance> embedded_hal_1::spi::blocking::SpiBus<u8> for Spi<'d, T> {
impl<'d, T: Instance, M: Mode> embedded_hal_1::spi::SpiBus<u8> for Spi<'d, T, M> {
fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> {
self.blocking_transfer(read, write)
}
@ -346,7 +552,52 @@ mod eh1 {
}
}
impl<'d, T: Instance> SetConfig for Spi<'d, T> {
#[cfg(all(feature = "unstable-traits", feature = "nightly"))]
mod eha {
use core::future::Future;
use super::*;
impl<'d, T: Instance> embedded_hal_async::spi::SpiBusFlush for Spi<'d, T, Async> {
type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> {
async { Ok(()) }
}
}
impl<'d, T: Instance> embedded_hal_async::spi::SpiBusWrite<u8> for Spi<'d, T, Async> {
type WriteFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
fn write<'a>(&'a mut self, data: &'a [u8]) -> Self::WriteFuture<'a> {
self.write(data)
}
}
impl<'d, T: Instance> embedded_hal_async::spi::SpiBusRead<u8> for Spi<'d, T, Async> {
type ReadFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
fn read<'a>(&'a mut self, data: &'a mut [u8]) -> Self::ReadFuture<'a> {
self.read(data)
}
}
impl<'d, T: Instance> embedded_hal_async::spi::SpiBus<u8> for Spi<'d, T, Async> {
type TransferFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
fn transfer<'a>(&'a mut self, rx: &'a mut [u8], tx: &'a [u8]) -> Self::TransferFuture<'a> {
self.transfer(rx, tx)
}
type TransferInPlaceFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
fn transfer_in_place<'a>(&'a mut self, words: &'a mut [u8]) -> Self::TransferInPlaceFuture<'a> {
self.transfer_in_place(words)
}
}
}
impl<'d, T: Instance, M: Mode> SetConfig for Spi<'d, T, M> {
type Config = Config;
fn set_config(&mut self, config: &Self::Config) {
let p = self.inner.regs();

View File

@ -68,7 +68,7 @@ impl Driver for TimerDriver {
})
}
fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) {
fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool {
let n = alarm.id() as usize;
critical_section::with(|cs| {
let alarm = &self.alarms.borrow(cs)[n];
@ -81,11 +81,16 @@ impl Driver for TimerDriver {
unsafe { pac::TIMER.alarm(n).write_value(timestamp as u32) };
let now = self.now();
// If alarm timestamp has passed, trigger it instantly.
// This disarms it.
if timestamp <= now {
self.trigger_alarm(n, cs);
// If alarm timestamp has passed the alarm will not fire.
// Disarm the alarm and return `false` to indicate that.
unsafe { pac::TIMER.armed().write(|w| w.set_armed(1 << n)) }
alarm.timestamp.set(u64::MAX);
false
} else {
true
}
})
}

View File

@ -0,0 +1,489 @@
use core::future::{poll_fn, Future};
use core::task::{Poll, Waker};
use atomic_polyfill::{compiler_fence, Ordering};
use embassy_cortex_m::peripheral::{PeripheralMutex, PeripheralState, StateStorage};
use embassy_hal_common::ring_buffer::RingBuffer;
use embassy_sync::waitqueue::WakerRegistration;
use super::*;
pub struct State<'d, T: Instance>(StateStorage<FullStateInner<'d, T>>);
impl<'d, T: Instance> State<'d, T> {
pub const fn new() -> Self {
Self(StateStorage::new())
}
}
pub struct RxState<'d, T: Instance>(StateStorage<RxStateInner<'d, T>>);
impl<'d, T: Instance> RxState<'d, T> {
pub const fn new() -> Self {
Self(StateStorage::new())
}
}
pub struct TxState<'d, T: Instance>(StateStorage<TxStateInner<'d, T>>);
impl<'d, T: Instance> TxState<'d, T> {
pub const fn new() -> Self {
Self(StateStorage::new())
}
}
struct RxStateInner<'d, T: Instance> {
phantom: PhantomData<&'d mut T>,
waker: WakerRegistration,
buf: RingBuffer<'d>,
}
struct TxStateInner<'d, T: Instance> {
phantom: PhantomData<&'d mut T>,
waker: WakerRegistration,
buf: RingBuffer<'d>,
}
struct FullStateInner<'d, T: Instance> {
rx: RxStateInner<'d, T>,
tx: TxStateInner<'d, T>,
}
unsafe impl<'d, T: Instance> Send for RxStateInner<'d, T> {}
unsafe impl<'d, T: Instance> Sync for RxStateInner<'d, T> {}
unsafe impl<'d, T: Instance> Send for TxStateInner<'d, T> {}
unsafe impl<'d, T: Instance> Sync for TxStateInner<'d, T> {}
unsafe impl<'d, T: Instance> Send for FullStateInner<'d, T> {}
unsafe impl<'d, T: Instance> Sync for FullStateInner<'d, T> {}
pub struct BufferedUart<'d, T: Instance> {
inner: PeripheralMutex<'d, FullStateInner<'d, T>>,
}
pub struct BufferedUartRx<'d, T: Instance> {
inner: PeripheralMutex<'d, RxStateInner<'d, T>>,
}
pub struct BufferedUartTx<'d, T: Instance> {
inner: PeripheralMutex<'d, TxStateInner<'d, T>>,
}
impl<'d, T: Instance> Unpin for BufferedUart<'d, T> {}
impl<'d, T: Instance> Unpin for BufferedUartRx<'d, T> {}
impl<'d, T: Instance> Unpin for BufferedUartTx<'d, T> {}
impl<'d, T: Instance> BufferedUart<'d, T> {
pub fn new<M: Mode>(
state: &'d mut State<'d, T>,
_uart: Uart<'d, T, M>,
irq: impl Peripheral<P = T::Interrupt> + 'd,
tx_buffer: &'d mut [u8],
rx_buffer: &'d mut [u8],
) -> BufferedUart<'d, T> {
into_ref!(irq);
let r = T::regs();
unsafe {
r.uartimsc().modify(|w| {
w.set_rxim(true);
w.set_rtim(true);
w.set_txim(true);
});
}
Self {
inner: PeripheralMutex::new(irq, &mut state.0, move || FullStateInner {
tx: TxStateInner {
phantom: PhantomData,
waker: WakerRegistration::new(),
buf: RingBuffer::new(tx_buffer),
},
rx: RxStateInner {
phantom: PhantomData,
waker: WakerRegistration::new(),
buf: RingBuffer::new(rx_buffer),
},
}),
}
}
}
impl<'d, T: Instance> BufferedUartRx<'d, T> {
pub fn new<M: Mode>(
state: &'d mut RxState<'d, T>,
_uart: UartRx<'d, T, M>,
irq: impl Peripheral<P = T::Interrupt> + 'd,
rx_buffer: &'d mut [u8],
) -> BufferedUartRx<'d, T> {
into_ref!(irq);
let r = T::regs();
unsafe {
r.uartimsc().modify(|w| {
w.set_rxim(true);
w.set_rtim(true);
});
}
Self {
inner: PeripheralMutex::new(irq, &mut state.0, move || RxStateInner {
phantom: PhantomData,
buf: RingBuffer::new(rx_buffer),
waker: WakerRegistration::new(),
}),
}
}
}
impl<'d, T: Instance> BufferedUartTx<'d, T> {
pub fn new<M: Mode>(
state: &'d mut TxState<'d, T>,
_uart: UartTx<'d, T, M>,
irq: impl Peripheral<P = T::Interrupt> + 'd,
tx_buffer: &'d mut [u8],
) -> BufferedUartTx<'d, T> {
into_ref!(irq);
let r = T::regs();
unsafe {
r.uartimsc().modify(|w| {
w.set_txim(true);
});
}
Self {
inner: PeripheralMutex::new(irq, &mut state.0, move || TxStateInner {
phantom: PhantomData,
buf: RingBuffer::new(tx_buffer),
waker: WakerRegistration::new(),
}),
}
}
}
impl<'d, T: Instance> PeripheralState for FullStateInner<'d, T>
where
Self: 'd,
{
type Interrupt = T::Interrupt;
fn on_interrupt(&mut self) {
self.rx.on_interrupt();
self.tx.on_interrupt();
}
}
impl<'d, T: Instance> RxStateInner<'d, T>
where
Self: 'd,
{
fn read(&mut self, buf: &mut [u8], waker: &Waker) -> (Poll<Result<usize, Error>>, bool) {
// We have data ready in buffer? Return it.
let mut do_pend = false;
let data = self.buf.pop_buf();
if !data.is_empty() {
let len = data.len().min(buf.len());
buf[..len].copy_from_slice(&data[..len]);
if self.buf.is_full() {
do_pend = true;
}
self.buf.pop(len);
return (Poll::Ready(Ok(len)), do_pend);
}
self.waker.register(waker);
(Poll::Pending, do_pend)
}
fn fill_buf<'a>(&mut self, waker: &Waker) -> Poll<Result<&'a [u8], Error>> {
// We have data ready in buffer? Return it.
let buf = self.buf.pop_buf();
if !buf.is_empty() {
let buf: &[u8] = buf;
// Safety: buffer lives as long as uart
let buf: &[u8] = unsafe { core::mem::transmute(buf) };
return Poll::Ready(Ok(buf));
}
self.waker.register(waker);
Poll::Pending
}
fn consume(&mut self, amt: usize) -> bool {
let full = self.buf.is_full();
self.buf.pop(amt);
full
}
}
impl<'d, T: Instance> PeripheralState for RxStateInner<'d, T>
where
Self: 'd,
{
type Interrupt = T::Interrupt;
fn on_interrupt(&mut self) {
let r = T::regs();
unsafe {
let ris = r.uartris().read();
// Clear interrupt flags
r.uarticr().modify(|w| {
w.set_rxic(true);
w.set_rtic(true);
});
if ris.peris() {
warn!("Parity error");
r.uarticr().modify(|w| {
w.set_peic(true);
});
}
if ris.feris() {
warn!("Framing error");
r.uarticr().modify(|w| {
w.set_feic(true);
});
}
if ris.beris() {
warn!("Break error");
r.uarticr().modify(|w| {
w.set_beic(true);
});
}
if ris.oeris() {
warn!("Overrun error");
r.uarticr().modify(|w| {
w.set_oeic(true);
});
}
if !r.uartfr().read().rxfe() {
let buf = self.buf.push_buf();
if !buf.is_empty() {
buf[0] = r.uartdr().read().data();
self.buf.push(1);
} else {
warn!("RX buffer full, discard received byte");
}
if self.buf.is_full() {
self.waker.wake();
}
}
if ris.rtris() {
self.waker.wake();
};
}
}
}
impl<'d, T: Instance> TxStateInner<'d, T>
where
Self: 'd,
{
fn write(&mut self, buf: &[u8], waker: &Waker) -> (Poll<Result<usize, Error>>, bool) {
let empty = self.buf.is_empty();
let tx_buf = self.buf.push_buf();
if tx_buf.is_empty() {
self.waker.register(waker);
return (Poll::Pending, empty);
}
let n = core::cmp::min(tx_buf.len(), buf.len());
tx_buf[..n].copy_from_slice(&buf[..n]);
self.buf.push(n);
(Poll::Ready(Ok(n)), empty)
}
fn flush(&mut self, waker: &Waker) -> Poll<Result<(), Error>> {
if !self.buf.is_empty() {
self.waker.register(waker);
return Poll::Pending;
}
Poll::Ready(Ok(()))
}
}
impl<'d, T: Instance> PeripheralState for TxStateInner<'d, T>
where
Self: 'd,
{
type Interrupt = T::Interrupt;
fn on_interrupt(&mut self) {
let r = T::regs();
unsafe {
let buf = self.buf.pop_buf();
if !buf.is_empty() {
r.uartimsc().modify(|w| {
w.set_txim(true);
});
r.uartdr().write(|w| w.set_data(buf[0].into()));
self.buf.pop(1);
self.waker.wake();
} else {
// Disable interrupt until we have something to transmit again
r.uartimsc().modify(|w| {
w.set_txim(false);
});
}
}
}
}
impl embedded_io::Error for Error {
fn kind(&self) -> embedded_io::ErrorKind {
embedded_io::ErrorKind::Other
}
}
impl<'d, T: Instance> embedded_io::Io for BufferedUart<'d, T> {
type Error = Error;
}
impl<'d, T: Instance> embedded_io::Io for BufferedUartRx<'d, T> {
type Error = Error;
}
impl<'d, T: Instance> embedded_io::Io for BufferedUartTx<'d, T> {
type Error = Error;
}
impl<'d, T: Instance + 'd> embedded_io::asynch::Read for BufferedUart<'d, T> {
type ReadFuture<'a> = impl Future<Output = Result<usize, Self::Error>> + 'a
where
Self: 'a;
fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> {
poll_fn(move |cx| {
let (res, do_pend) = self.inner.with(|state| {
compiler_fence(Ordering::SeqCst);
state.rx.read(buf, cx.waker())
});
if do_pend {
self.inner.pend();
}
res
})
}
}
impl<'d, T: Instance + 'd> embedded_io::asynch::Read for BufferedUartRx<'d, T> {
type ReadFuture<'a> = impl Future<Output = Result<usize, Self::Error>> + 'a
where
Self: 'a;
fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> {
poll_fn(move |cx| {
let (res, do_pend) = self.inner.with(|state| {
compiler_fence(Ordering::SeqCst);
state.read(buf, cx.waker())
});
if do_pend {
self.inner.pend();
}
res
})
}
}
impl<'d, T: Instance + 'd> embedded_io::asynch::BufRead for BufferedUart<'d, T> {
type FillBufFuture<'a> = impl Future<Output = Result<&'a [u8], Self::Error>> + 'a
where
Self: 'a;
fn fill_buf<'a>(&'a mut self) -> Self::FillBufFuture<'a> {
poll_fn(move |cx| {
self.inner.with(|state| {
compiler_fence(Ordering::SeqCst);
state.rx.fill_buf(cx.waker())
})
})
}
fn consume(&mut self, amt: usize) {
let signal = self.inner.with(|state| state.rx.consume(amt));
if signal {
self.inner.pend();
}
}
}
impl<'d, T: Instance + 'd> embedded_io::asynch::BufRead for BufferedUartRx<'d, T> {
type FillBufFuture<'a> = impl Future<Output = Result<&'a [u8], Self::Error>> + 'a
where
Self: 'a;
fn fill_buf<'a>(&'a mut self) -> Self::FillBufFuture<'a> {
poll_fn(move |cx| {
self.inner.with(|state| {
compiler_fence(Ordering::SeqCst);
state.fill_buf(cx.waker())
})
})
}
fn consume(&mut self, amt: usize) {
let signal = self.inner.with(|state| state.consume(amt));
if signal {
self.inner.pend();
}
}
}
impl<'d, T: Instance + 'd> embedded_io::asynch::Write for BufferedUart<'d, T> {
type WriteFuture<'a> = impl Future<Output = Result<usize, Self::Error>> + 'a
where
Self: 'a;
fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> {
poll_fn(move |cx| {
let (poll, empty) = self.inner.with(|state| state.tx.write(buf, cx.waker()));
if empty {
self.inner.pend();
}
poll
})
}
type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a
where
Self: 'a;
fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> {
poll_fn(move |cx| self.inner.with(|state| state.tx.flush(cx.waker())))
}
}
impl<'d, T: Instance + 'd> embedded_io::asynch::Write for BufferedUartTx<'d, T> {
type WriteFuture<'a> = impl Future<Output = Result<usize, Self::Error>> + 'a
where
Self: 'a;
fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> {
poll_fn(move |cx| {
let (poll, empty) = self.inner.with(|state| state.write(buf, cx.waker()));
if empty {
self.inner.pend();
}
poll
})
}
type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a
where
Self: 'a;
fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> {
poll_fn(move |cx| self.inner.with(|state| state.flush(cx.waker())))
}
}

Some files were not shown because too many files have changed in this diff Show More