├── .gitignore ├── LICENSE-APACHE ├── LICENSE-MIT ├── NOTE.txt ├── embassy-esp32c3 ├── .cargo │ └── config.toml ├── Cargo.toml └── src │ ├── driver.rs │ ├── interrupt.rs │ ├── lib.rs │ ├── rtc_cntl.rs │ ├── serial.rs │ ├── systimer.rs │ └── timer.rs ├── embassy-macros ├── Cargo.toml └── src │ ├── lib.rs │ ├── macros │ ├── interrupt.rs │ ├── interrupt_declare.rs │ ├── interrupt_take.rs │ ├── main.rs │ ├── mod.rs │ └── task.rs │ └── util │ ├── ctxt.rs │ ├── mod.rs │ └── path.rs ├── embassy ├── Cargo.toml ├── build.rs └── src │ ├── blocking_mutex │ ├── mod.rs │ └── raw.rs │ ├── channel │ ├── channel.rs │ ├── mod.rs │ └── signal.rs │ ├── executor │ ├── arch │ │ ├── cortex_m.rs │ │ ├── riscv32.rs │ │ ├── std.rs │ │ └── wasm.rs │ ├── mod.rs │ ├── raw │ │ ├── mod.rs │ │ ├── run_queue.rs │ │ ├── timer_queue.rs │ │ ├── util.rs │ │ └── waker.rs │ └── spawner.rs │ ├── fmt.rs │ ├── interrupt │ ├── interrupt.rs │ └── interrupt_riscv32.rs │ ├── io │ ├── error.rs │ ├── mod.rs │ ├── std.rs │ ├── traits.rs │ └── util │ │ ├── copy_buf.rs │ │ ├── drain.rs │ │ ├── flush.rs │ │ ├── mod.rs │ │ ├── read.rs │ │ ├── read_buf.rs │ │ ├── read_byte.rs │ │ ├── read_exact.rs │ │ ├── read_to_end.rs │ │ ├── read_while.rs │ │ ├── skip_while.rs │ │ ├── split.rs │ │ ├── write.rs │ │ ├── write_all.rs │ │ └── write_byte.rs │ ├── lib.rs │ ├── mutex.rs │ ├── time │ ├── delay.rs │ ├── driver.rs │ ├── driver_std.rs │ ├── driver_wasm.rs │ ├── duration.rs │ ├── instant.rs │ ├── mod.rs │ └── timer.rs │ ├── util │ ├── forever.rs │ ├── mod.rs │ ├── select_all.rs │ └── yield_now.rs │ └── waitqueue │ ├── mod.rs │ ├── waker.rs │ └── waker_agnostic.rs ├── examples ├── async_join │ ├── .cargo │ │ ├── config.save.toml │ │ └── config.toml │ ├── .gitignore │ ├── .vscode │ │ └── settings.json │ ├── Cargo.toml │ ├── LICENSE-APACHE │ ├── LICENSE-MIT │ ├── README.md │ ├── rust-toolchain.toml │ └── src │ │ └── main.rs ├── concurrent_timers │ ├── .cargo │ │ ├── config.save.toml │ │ └── config.toml │ ├── .gitignore │ ├── .vscode │ │ └── settings.json │ ├── Cargo.toml │ ├── LICENSE-APACHE │ ├── LICENSE-MIT │ ├── README.md │ ├── rust-toolchain.toml │ └── src │ │ └── main.rs ├── hello_world │ ├── .cargo │ │ ├── config.save.toml │ │ └── config.toml │ ├── .gitignore │ ├── .vscode │ │ └── settings.json │ ├── Cargo.toml │ ├── LICENSE-APACHE │ ├── LICENSE-MIT │ ├── README.md │ ├── rust-toolchain.toml │ └── src │ │ └── main.rs ├── interrupt_mode │ ├── .cargo │ │ ├── config.save.toml │ │ └── config.toml │ ├── .gitignore │ ├── .vscode │ │ └── settings.json │ ├── Cargo.toml │ ├── LICENSE-APACHE │ ├── LICENSE-MIT │ ├── README.md │ ├── rust-toolchain.toml │ └── src │ │ └── main.rs ├── multiple_tasks │ ├── .cargo │ │ ├── config.save.toml │ │ └── config.toml │ ├── .gitignore │ ├── .vscode │ │ └── settings.json │ ├── Cargo.toml │ ├── LICENSE-APACHE │ ├── LICENSE-MIT │ ├── README.md │ ├── rust-toolchain.toml │ └── src │ │ └── main.rs └── previous_race_condition │ ├── .cargo │ ├── config.save.toml │ └── config.toml │ ├── .gitignore │ ├── .vscode │ └── settings.json │ ├── Cargo.toml │ ├── LICENSE-APACHE │ ├── LICENSE-MIT │ ├── README.md │ ├── rust-toolchain.toml │ └── src │ └── main.rs ├── readme.md └── test ├── test_SW_INT1 ├── .cargo │ ├── config.save.toml │ └── config.toml ├── .gitignore ├── .vscode │ └── settings.json ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── rust-toolchain.toml └── src │ └── main.rs ├── test_disable_interrupt ├── .cargo │ ├── config.save.toml │ └── config.toml ├── .gitignore ├── .vscode │ └── settings.json ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── rust-toolchain.toml └── src │ └── main.rs └── test_time_driver ├── .cargo ├── config.save.toml └── config.toml ├── .gitignore ├── .vscode └── settings.json ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── rust-toolchain.toml └── src └── main.rs /.gitignore: -------------------------------------------------------------------------------- 1 | */debug/* 2 | # Generated by Cargo 3 | # will have compiled files and executables 4 | */debug/ 5 | */target/ 6 | 7 | # These are backup files generated by rustfmt 8 | **/*.rs.bk 9 | 10 | # MSVC Windows builds of rustc generate these, which store debugging information 11 | *.pdb 12 | 13 | */target 14 | */target_ci 15 | */target_ci_stable 16 | Cargo.lock 17 | third_party 18 | # /Cargo.toml 19 | 20 | */out/ -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright [year] [fullname] 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /NOTE.txt: -------------------------------------------------------------------------------- 1 | I did not rebase this repo (though I probably should) since it is being submitted for my bachelor thesis and I want to show as much of the work as I can, sorry for the large repo size! 2 | repo link: https://github.com/osobiehl/riscv-embassy-mess-work -------------------------------------------------------------------------------- /embassy-esp32c3/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [target.'cfg(all(target_arch = "riscv32", target_os = "none"))'] 2 | runner = "espflash --monitor" 3 | rustflags = [ 4 | "-C", "link-arg=-Tlinkall.x" 5 | ] 6 | 7 | # for testing: you can specify this target to see atomic emulation in action 8 | [target.riscv32imac-unknown-none-elf] 9 | runner = "espflash --monitor" 10 | rustflags = [ 11 | "-C", "link-arg=-Tlinkall.x" 12 | ] 13 | 14 | [build] 15 | target = "riscv32imc-unknown-none-elf" 16 | 17 | [unstable] 18 | build-std = [ "core" ] 19 | -------------------------------------------------------------------------------- /embassy-esp32c3/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "embassy_esp32c3" 3 | version = "0.1.0" 4 | authors = ["osobiehl "] 5 | edition = "2021" 6 | license = "MIT OR Apache-2.0" 7 | 8 | [dependencies] 9 | panic-halt = "0.2" 10 | esp32c3-hal = { package = "esp32c3-hal", git = "https://github.com/esp-rs/esp-hal.git" } 11 | riscv-rt = { version = "0.8", optional = true } 12 | nb = "1.0.0" 13 | embassy = { version = "0.1.0", path = "../embassy", features = [] } 14 | embassy-macros = { path = "../embassy-macros", features = ["esp32c3"]} 15 | r0 = "1.0.0" 16 | riscv-atomic-emulation-trap = "0.1.0" 17 | atomic-polyfill = "0.1.5" 18 | 19 | 20 | futures = { version = "0.3.17", default-features = false, features = ["async-await"] } 21 | heapless = { version = "0.7.5", default-features = false } 22 | esp32c3 = "0.4.0" 23 | riscv = "0.7" 24 | critical-section = "0.2.5" 25 | embedded-hal = { version = "0.2", features = ["unproven"] } 26 | void = { version = "1.0", default-features = false } 27 | 28 | 29 | [features] 30 | default = ["rt"] 31 | rt = ["riscv-rt"] 32 | -------------------------------------------------------------------------------- /embassy-esp32c3/src/rtc_cntl.rs: -------------------------------------------------------------------------------- 1 | use esp32c3::RTC_CNTL; 2 | // use crate::pac::RTC_CNTL; 3 | pub struct RtcCntl { 4 | rtc_cntl: RTC_CNTL, 5 | } 6 | 7 | impl RtcCntl { 8 | pub fn free(self)->RTC_CNTL{ 9 | self.rtc_cntl 10 | } 11 | pub fn new(rtc_cntl: RTC_CNTL) -> Self { 12 | Self { rtc_cntl } 13 | } 14 | 15 | pub fn set_super_wdt_enable(&mut self, enable: bool) { 16 | self.set_swd_write_protection(false); 17 | 18 | self.rtc_cntl 19 | .swd_conf 20 | .write(|w| w.swd_auto_feed_en().bit(!enable)); 21 | 22 | self.set_swd_write_protection(true); 23 | } 24 | 25 | fn set_swd_write_protection(&mut self, enable: bool) { 26 | let wkey = if enable { 0u32 } else { 0x8F1D_312A }; 27 | 28 | self.rtc_cntl 29 | .swd_wprotect 30 | .write(|w| unsafe { w.swd_wkey().bits(wkey) }); 31 | } 32 | 33 | pub fn set_wdt_enable(&mut self, enable: bool) { 34 | self.set_wdt_write_protection(false); 35 | 36 | if !enable { 37 | self.rtc_cntl.wdtconfig0.write(|w| unsafe { w.bits(0) }); 38 | } else { 39 | self.rtc_cntl.wdtconfig0.write(|w| w.wdt_en().bit(enable)); 40 | } 41 | 42 | self.set_wdt_write_protection(true); 43 | } 44 | 45 | fn set_wdt_write_protection(&mut self, enable: bool) { 46 | let wkey = if enable { 0u32 } else { 0x50D8_3AA1 }; 47 | 48 | self.rtc_cntl 49 | .wdtwprotect 50 | .write(|w| unsafe { w.wdt_wkey().bits(wkey) }); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /embassy-esp32c3/src/serial.rs: -------------------------------------------------------------------------------- 1 | //! UART driver 2 | 3 | use embedded_hal::serial::{Read, Write}; 4 | 5 | #[cfg(any(feature = "esp32", feature = "esp32s3"))] 6 | use crate::pac::UART2; 7 | use esp32c3::{uart0::RegisterBlock, UART0, UART1}; 8 | 9 | const UART_FIFO_SIZE: u16 = 128; 10 | 11 | /// Custom serial error type 12 | #[derive(Debug)] 13 | pub enum Error {} 14 | 15 | /// UART driver 16 | pub struct Serial { 17 | uart: T, 18 | } 19 | 20 | impl Serial { 21 | /// Create a new UART instance 22 | pub fn new(uart: T) -> Result { 23 | let mut serial = Serial { uart }; 24 | serial.uart.disable_rx_interrupts(); 25 | serial.uart.disable_tx_interrupts(); 26 | 27 | Ok(serial) 28 | } 29 | 30 | /// Writes bytes 31 | pub fn write_bytes(&mut self, data: &[u8]) -> Result<(), Error> { 32 | data.iter().try_for_each(|c| nb::block!(self.write(*c))) 33 | } 34 | 35 | /// Return the raw interface to the underlying UART instance 36 | pub fn free(self) -> T { 37 | self.uart 38 | } 39 | } 40 | 41 | /// UART peripheral instance 42 | pub trait Instance { 43 | fn register_block(&self) -> &RegisterBlock; 44 | 45 | fn disable_tx_interrupts(&mut self) { 46 | self.register_block().int_clr.write(|w| { 47 | w.txfifo_empty_int_clr() 48 | .set_bit() 49 | .tx_brk_done_int_clr() 50 | .set_bit() 51 | .tx_brk_idle_done_int_clr() 52 | .set_bit() 53 | .tx_done_int_clr() 54 | .set_bit() 55 | }); 56 | 57 | self.register_block().int_ena.write(|w| { 58 | w.txfifo_empty_int_ena() 59 | .clear_bit() 60 | .tx_brk_done_int_ena() 61 | .clear_bit() 62 | .tx_brk_idle_done_int_ena() 63 | .clear_bit() 64 | .tx_done_int_ena() 65 | .clear_bit() 66 | }); 67 | } 68 | 69 | fn disable_rx_interrupts(&mut self) { 70 | self.register_block().int_clr.write(|w| { 71 | w.rxfifo_full_int_clr() 72 | .set_bit() 73 | .rxfifo_ovf_int_clr() 74 | .set_bit() 75 | .rxfifo_tout_int_clr() 76 | .set_bit() 77 | }); 78 | 79 | self.register_block().int_ena.write(|w| { 80 | w.rxfifo_full_int_ena() 81 | .clear_bit() 82 | .rxfifo_ovf_int_ena() 83 | .clear_bit() 84 | .rxfifo_tout_int_ena() 85 | .clear_bit() 86 | }); 87 | } 88 | 89 | fn get_tx_fifo_count(&mut self) -> u16 { 90 | self.register_block() 91 | .status 92 | .read() 93 | .txfifo_cnt() 94 | .bits() 95 | .into() 96 | } 97 | 98 | fn get_rx_fifo_count(&mut self) -> u16 { 99 | self.register_block() 100 | .status 101 | .read() 102 | .rxfifo_cnt() 103 | .bits() 104 | .into() 105 | } 106 | 107 | fn is_tx_idle(&mut self) -> bool { 108 | #[cfg(feature = "esp32")] 109 | let idle = self.register_block().status.read().st_utx_out().bits() == 0x0u8; 110 | #[cfg(not(feature = "esp32"))] 111 | let idle = self.register_block().fsm_status.read().st_utx_out().bits() == 0x0u8; 112 | 113 | idle 114 | } 115 | 116 | fn is_rx_idle(&mut self) -> bool { 117 | #[cfg(feature = "esp32")] 118 | let idle = self.register_block().status.read().st_urx_out().bits() == 0x0u8; 119 | #[cfg(not(feature = "esp32"))] 120 | let idle = self.register_block().fsm_status.read().st_urx_out().bits() == 0x0u8; 121 | 122 | idle 123 | } 124 | } 125 | 126 | impl Instance for UART0 { 127 | #[inline(always)] 128 | fn register_block(&self) -> &RegisterBlock { 129 | self 130 | } 131 | } 132 | 133 | impl Instance for UART1 { 134 | #[inline(always)] 135 | fn register_block(&self) -> &RegisterBlock { 136 | self 137 | } 138 | } 139 | 140 | #[cfg(any(feature = "esp32", feature = "esp32s3"))] 141 | impl Instance for UART2 { 142 | #[inline(always)] 143 | fn register_block(&self) -> &RegisterBlock { 144 | self 145 | } 146 | } 147 | 148 | #[cfg(feature = "ufmt")] 149 | impl ufmt_write::uWrite for Serial 150 | where 151 | T: Instance, 152 | { 153 | type Error = Error; 154 | 155 | #[inline] 156 | fn write_str(&mut self, s: &str) -> Result<(), Self::Error> { 157 | self.write_bytes(s.as_bytes()) 158 | } 159 | 160 | #[inline] 161 | fn write_char(&mut self, ch: char) -> Result<(), Self::Error> { 162 | let mut buffer = [0u8; 4]; 163 | self.write_bytes(ch.encode_utf8(&mut buffer).as_bytes()) 164 | } 165 | } 166 | 167 | impl core::fmt::Write for Serial 168 | where 169 | T: Instance, 170 | { 171 | #[inline] 172 | fn write_str(&mut self, s: &str) -> core::fmt::Result { 173 | self.write_bytes(s.as_bytes()).map_err(|_| core::fmt::Error) 174 | } 175 | } 176 | 177 | impl Write for Serial 178 | where 179 | T: Instance, 180 | { 181 | type Error = Error; 182 | 183 | fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> { 184 | if self.uart.get_tx_fifo_count() < UART_FIFO_SIZE { 185 | self.uart 186 | .register_block() 187 | .fifo 188 | .write(|w| unsafe { w.rxfifo_rd_byte().bits(word) }); 189 | 190 | Ok(()) 191 | } else { 192 | Err(nb::Error::WouldBlock) 193 | } 194 | } 195 | 196 | fn flush(&mut self) -> nb::Result<(), Self::Error> { 197 | if self.uart.is_tx_idle() { 198 | Ok(()) 199 | } else { 200 | Err(nb::Error::WouldBlock) 201 | } 202 | } 203 | } 204 | 205 | impl Read for Serial 206 | where 207 | T: Instance, 208 | { 209 | type Error = Error; 210 | 211 | fn read(&mut self) -> nb::Result { 212 | if self.uart.get_rx_fifo_count() > 0 { 213 | let value = self 214 | .uart 215 | .register_block() 216 | .fifo 217 | .read() 218 | .rxfifo_rd_byte() 219 | .bits(); 220 | 221 | Ok(value) 222 | } else { 223 | Err(nb::Error::WouldBlock) 224 | } 225 | } 226 | } 227 | -------------------------------------------------------------------------------- /embassy-macros/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "embassy-macros" 3 | version = "0.1.0" 4 | authors = ["Dario Nieuwenhuis "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | syn = { version = "1.0.76", features = ["full", "extra-traits"] } 9 | quote = "1.0.9" 10 | darling = "0.13.0" 11 | proc-macro2 = "1.0.29" 12 | 13 | [lib] 14 | proc-macro = true 15 | 16 | [features] 17 | nrf = [] 18 | stm32 = [] 19 | rp = [] 20 | std = [] 21 | wasm = [] 22 | esp32c3 = [] 23 | -------------------------------------------------------------------------------- /embassy-macros/src/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate proc_macro; 2 | 3 | use proc_macro::TokenStream; 4 | 5 | mod macros; 6 | mod util; 7 | use macros::*; 8 | 9 | #[proc_macro_attribute] 10 | pub fn task(args: TokenStream, item: TokenStream) -> TokenStream { 11 | let args = syn::parse_macro_input!(args as syn::AttributeArgs); 12 | let f = syn::parse_macro_input!(item as syn::ItemFn); 13 | 14 | task::run(args, f).unwrap_or_else(|x| x).into() 15 | } 16 | 17 | #[proc_macro_attribute] 18 | pub fn main(args: TokenStream, item: TokenStream) -> TokenStream { 19 | let args = syn::parse_macro_input!(args as syn::AttributeArgs); 20 | let f = syn::parse_macro_input!(item as syn::ItemFn); 21 | main::run(args, f).unwrap_or_else(|x| x).into() 22 | } 23 | 24 | #[proc_macro_attribute] 25 | pub fn interrupt(args: TokenStream, item: TokenStream) -> TokenStream { 26 | let args = syn::parse_macro_input!(args as syn::AttributeArgs); 27 | let f = syn::parse_macro_input!(item as syn::ItemFn); 28 | interrupt::run(args, f).unwrap_or_else(|x| x).into() 29 | } 30 | 31 | #[proc_macro] 32 | pub fn interrupt_declare(item: TokenStream) -> TokenStream { 33 | let name = syn::parse_macro_input!(item as syn::Ident); 34 | interrupt_declare::run(name).unwrap_or_else(|x| x).into() 35 | } 36 | 37 | /// # interrupt_take procedural macro 38 | /// 39 | /// core::panic! is used as a default way to panic in this macro as there is no sensible way of enabling/disabling defmt for macro generation. 40 | /// We are aware that this brings bloat in the form of core::fmt, but the bloat is already included with e.g. array indexing panics. 41 | /// To get rid of this bloat, use the compiler flags `-Zbuild-std=core -Zbuild-std-features=panic_immediate_abort`. 42 | #[proc_macro] 43 | pub fn interrupt_take(item: TokenStream) -> TokenStream { 44 | let name = syn::parse_macro_input!(item as syn::Ident); 45 | interrupt_take::run(name).unwrap_or_else(|x| x).into() 46 | } 47 | -------------------------------------------------------------------------------- /embassy-macros/src/macros/interrupt.rs: -------------------------------------------------------------------------------- 1 | use darling::FromMeta; 2 | use proc_macro2::TokenStream; 3 | use quote::quote; 4 | use std::iter; 5 | use syn::ReturnType; 6 | use syn::{Type, Visibility}; 7 | 8 | use crate::util::ctxt::Ctxt; 9 | 10 | #[derive(Debug, FromMeta)] 11 | struct Args {} 12 | 13 | pub fn run(args: syn::AttributeArgs, mut f: syn::ItemFn) -> Result { 14 | let _args = Args::from_list(&args).map_err(|e| e.write_errors())?; 15 | 16 | let ident = f.sig.ident.clone(); 17 | let ident_s = ident.to_string(); 18 | 19 | // XXX should we blacklist other attributes? 20 | 21 | let valid_signature = f.sig.constness.is_none() 22 | && f.vis == Visibility::Inherited 23 | && f.sig.abi.is_none() 24 | && f.sig.inputs.is_empty() 25 | && f.sig.generics.params.is_empty() 26 | && f.sig.generics.where_clause.is_none() 27 | && f.sig.variadic.is_none() 28 | && match f.sig.output { 29 | ReturnType::Default => true, 30 | ReturnType::Type(_, ref ty) => match **ty { 31 | Type::Tuple(ref tuple) => tuple.elems.is_empty(), 32 | Type::Never(..) => true, 33 | _ => false, 34 | }, 35 | }; 36 | 37 | let ctxt = Ctxt::new(); 38 | 39 | if !valid_signature { 40 | ctxt.error_spanned_by( 41 | &f.sig, 42 | "`#[interrupt]` handlers must have signature `[unsafe] fn() [-> !]`", 43 | ); 44 | } 45 | 46 | ctxt.check()?; 47 | 48 | f.block.stmts = iter::once( 49 | syn::parse2(quote! {{ 50 | // Check that this interrupt actually exists 51 | let __irq_exists_check: interrupt::#ident; 52 | }}) 53 | .unwrap(), 54 | ) 55 | .chain(f.block.stmts) 56 | .collect(); 57 | 58 | let result = quote!( 59 | #[doc(hidden)] 60 | #[export_name = #ident_s] 61 | #[allow(non_snake_case)] 62 | #f 63 | ); 64 | 65 | Ok(result) 66 | } 67 | -------------------------------------------------------------------------------- /embassy-macros/src/macros/interrupt_declare.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::TokenStream; 2 | use quote::{format_ident, quote}; 3 | 4 | pub fn run(name: syn::Ident) -> Result { 5 | let name = format_ident!("{}", name); 6 | let name_interrupt = format_ident!("{}", name); 7 | let name_handler = format!("__EMBASSY_{}_HANDLER", name); 8 | 9 | let result = quote! { 10 | #[allow(non_camel_case_types)] 11 | pub struct #name_interrupt(()); 12 | unsafe impl ::embassy::interrupt::Interrupt for #name_interrupt { 13 | type Priority = crate::interrupt::Priority; 14 | fn number(&self) -> u16 { 15 | use cortex_m::interrupt::InterruptNumber; 16 | let irq = InterruptEnum::#name; 17 | irq.number() as u16 18 | } 19 | unsafe fn steal() -> Self { 20 | Self(()) 21 | } 22 | unsafe fn __handler(&self) -> &'static ::embassy::interrupt::Handler { 23 | #[export_name = #name_handler] 24 | static HANDLER: ::embassy::interrupt::Handler = ::embassy::interrupt::Handler::new(); 25 | &HANDLER 26 | } 27 | } 28 | 29 | unsafe impl ::embassy::util::Unborrow for #name_interrupt { 30 | type Target = #name_interrupt; 31 | unsafe fn unborrow(self) -> #name_interrupt { 32 | self 33 | } 34 | } 35 | }; 36 | Ok(result) 37 | } 38 | -------------------------------------------------------------------------------- /embassy-macros/src/macros/interrupt_take.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::TokenStream; 2 | use quote::{format_ident, quote}; 3 | 4 | pub fn run(name: syn::Ident) -> Result { 5 | let name = format!("{}", name); 6 | let name_interrupt = format_ident!("{}", name); 7 | let name_handler = format!("__EMBASSY_{}_HANDLER", name); 8 | 9 | let result = quote! { 10 | { 11 | #[allow(non_snake_case)] 12 | #[export_name = #name] 13 | pub unsafe extern "C" fn trampoline() { 14 | extern "C" { 15 | #[link_name = #name_handler] 16 | static HANDLER: ::embassy::interrupt::Handler; 17 | } 18 | 19 | let func = HANDLER.func.load(::embassy::export::atomic::Ordering::Relaxed); 20 | let ctx = HANDLER.ctx.load(::embassy::export::atomic::Ordering::Relaxed); 21 | let func: fn(*mut ()) = ::core::mem::transmute(func); 22 | func(ctx) 23 | } 24 | 25 | static TAKEN: ::embassy::export::atomic::AtomicBool = ::embassy::export::atomic::AtomicBool::new(false); 26 | 27 | if TAKEN.compare_exchange(false, true, ::embassy::export::atomic::Ordering::AcqRel, ::embassy::export::atomic::Ordering::Acquire).is_err() { 28 | core::panic!("IRQ Already taken"); 29 | } 30 | 31 | let irq: interrupt::#name_interrupt = unsafe { ::core::mem::transmute(()) }; 32 | irq 33 | } 34 | }; 35 | Ok(result) 36 | } 37 | -------------------------------------------------------------------------------- /embassy-macros/src/macros/main.rs: -------------------------------------------------------------------------------- 1 | use darling::FromMeta; 2 | use proc_macro2::TokenStream; 3 | use quote::quote; 4 | 5 | use crate::util::ctxt::Ctxt; 6 | use crate::util::path::ModulePrefix; 7 | 8 | #[cfg(feature = "stm32")] 9 | const HAL: Option<&str> = Some("embassy_stm32"); 10 | #[cfg(feature = "nrf")] 11 | const HAL: Option<&str> = Some("embassy_nrf"); 12 | #[cfg(feature = "rp")] 13 | const HAL: Option<&str> = Some("embassy_rp"); 14 | #[cfg(feature = "esp32c3")] 15 | const HAL: Option<&str> = Some("embassy_esp32c3"); 16 | 17 | #[cfg(not(any(feature = "stm32", feature = "nrf", feature = "rp", feature = "esp32c3")))] 18 | const HAL: Option<&str> = None; 19 | 20 | #[derive(Debug, FromMeta)] 21 | struct Args { 22 | #[darling(default)] 23 | embassy_prefix: ModulePrefix, 24 | 25 | #[allow(unused)] 26 | #[darling(default)] 27 | config: Option, 28 | } 29 | 30 | pub fn run(args: syn::AttributeArgs, f: syn::ItemFn) -> Result { 31 | let args = Args::from_list(&args).map_err(|e| e.write_errors())?; 32 | 33 | let fargs = f.sig.inputs.clone(); 34 | 35 | let ctxt = Ctxt::new(); 36 | 37 | if f.sig.asyncness.is_none() { 38 | ctxt.error_spanned_by(&f.sig, "task functions must be async"); 39 | } 40 | if !f.sig.generics.params.is_empty() { 41 | ctxt.error_spanned_by(&f.sig, "task functions must not be generic"); 42 | } 43 | 44 | if HAL.is_some() && fargs.len() != 2 { 45 | ctxt.error_spanned_by(&f.sig, "main function must have 2 arguments"); 46 | } 47 | if HAL.is_none() && fargs.len() != 1 { 48 | ctxt.error_spanned_by(&f.sig, "main function must have 2 arguments"); 49 | } 50 | 51 | ctxt.check()?; 52 | 53 | let embassy_prefix = args.embassy_prefix; 54 | let embassy_prefix_lit = embassy_prefix.literal(); 55 | let embassy_path = embassy_prefix.append("embassy").path(); 56 | let f_body = f.block; 57 | 58 | #[cfg(feature = "wasm")] 59 | let main = quote! { 60 | #[wasm_bindgen::prelude::wasm_bindgen(start)] 61 | pub fn main() -> Result<(), wasm_bindgen::JsValue> { 62 | static EXECUTOR: #embassy_path::util::Forever<#embassy_path::executor::Executor> = #embassy_path::util::Forever::new(); 63 | let executor = EXECUTOR.put(#embassy_path::executor::Executor::new()); 64 | 65 | executor.start(|spawner| { 66 | spawner.spawn(__embassy_main(spawner)).unwrap(); 67 | }); 68 | 69 | Ok(()) 70 | } 71 | }; 72 | 73 | #[cfg(all(feature = "std", not(feature = "wasm")))] 74 | let main = quote! { 75 | fn main() -> ! { 76 | let mut executor = #embassy_path::executor::Executor::new(); 77 | let executor = unsafe { __make_static(&mut executor) }; 78 | 79 | executor.run(|spawner| { 80 | spawner.must_spawn(__embassy_main(spawner)); 81 | }) 82 | } 83 | }; 84 | 85 | 86 | 87 | #[cfg(feature="arm")] 88 | let main = { 89 | let config = args 90 | .config 91 | .map(|s| s.parse::().unwrap()) 92 | .unwrap_or_else(|| { 93 | syn::Expr::Verbatim(quote! { 94 | Default::default() 95 | }) 96 | }); 97 | 98 | let (hal_setup, peris_arg) = match HAL { 99 | Some(hal) => { 100 | let embassy_hal_path = embassy_prefix.append(hal).path(); 101 | ( 102 | quote!( 103 | let p = #embassy_hal_path::init(#config); 104 | ), 105 | quote!(p), 106 | ) 107 | } 108 | None => (quote!(), quote!()), 109 | }; 110 | 111 | quote! { 112 | #[cortex_m_rt::entry] 113 | fn main() -> ! { 114 | #hal_setup 115 | 116 | let mut executor = #embassy_path::executor::Executor::new(); 117 | let executor = unsafe { __make_static(&mut executor) }; 118 | 119 | executor.run(|spawner| { 120 | spawner.must_spawn(__embassy_main(spawner, #peris_arg)); 121 | }) 122 | } 123 | } 124 | }; 125 | // TODO FIX 126 | #[cfg(all(not(feature="arm"), not(feature="std"), not(feature="wasm")))] 127 | let main = { 128 | let config = args 129 | .config 130 | .map(|s| s.parse::().unwrap()) 131 | .unwrap_or_else(|| { 132 | syn::Expr::Verbatim(quote! { 133 | Default::default() 134 | }) 135 | }); 136 | 137 | let (hal_setup, peris_arg) = match HAL { 138 | Some(hal) => { 139 | let embassy_hal_path = embassy_prefix.append(hal).path(); 140 | ( 141 | quote!( 142 | let p = #embassy_hal_path::init(#config); 143 | ), 144 | quote!(p), 145 | ) 146 | } 147 | None => (quote!(), quote!()), 148 | }; 149 | 150 | quote! { 151 | #[riscv_rt::entry] 152 | fn main() -> ! { 153 | #hal_setup 154 | 155 | let mut executor = #embassy_path::executor::Executor::new(); 156 | let executor = unsafe { __make_static(&mut executor) }; 157 | 158 | executor.run(|spawner| { 159 | spawner.must_spawn(__embassy_main(spawner, #peris_arg)); 160 | }) 161 | } 162 | } 163 | }; 164 | 165 | let result = quote! { 166 | #[#embassy_path::task(embassy_prefix = #embassy_prefix_lit)] 167 | async fn __embassy_main(#fargs) { 168 | #f_body 169 | } 170 | 171 | unsafe fn __make_static(t: &mut T) -> &'static mut T { 172 | ::core::mem::transmute(t) 173 | } 174 | //TODO find out why this is still in the red 175 | #main 176 | }; 177 | 178 | Ok(result) 179 | } 180 | -------------------------------------------------------------------------------- /embassy-macros/src/macros/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod interrupt; 2 | pub mod interrupt_declare; 3 | pub mod interrupt_take; 4 | pub mod main; 5 | pub mod task; 6 | -------------------------------------------------------------------------------- /embassy-macros/src/macros/task.rs: -------------------------------------------------------------------------------- 1 | use darling::FromMeta; 2 | use proc_macro2::TokenStream; 3 | use quote::{format_ident, quote}; 4 | 5 | use crate::util::ctxt::Ctxt; 6 | use crate::util::path::ModulePrefix; 7 | 8 | #[derive(Debug, FromMeta)] 9 | struct Args { 10 | #[darling(default)] 11 | pool_size: Option, 12 | #[darling(default)] 13 | embassy_prefix: ModulePrefix, 14 | } 15 | 16 | pub fn run(args: syn::AttributeArgs, f: syn::ItemFn) -> Result { 17 | let args = Args::from_list(&args).map_err(|e| e.write_errors())?; 18 | 19 | let embassy_prefix = args.embassy_prefix.append("embassy"); 20 | let embassy_path = embassy_prefix.path(); 21 | 22 | let pool_size: usize = args.pool_size.unwrap_or(1); 23 | 24 | let ctxt = Ctxt::new(); 25 | 26 | if f.sig.asyncness.is_none() { 27 | ctxt.error_spanned_by(&f.sig, "task functions must be async"); 28 | } 29 | if !f.sig.generics.params.is_empty() { 30 | ctxt.error_spanned_by(&f.sig, "task functions must not be generic"); 31 | } 32 | if pool_size < 1 { 33 | ctxt.error_spanned_by(&f.sig, "pool_size must be 1 or greater"); 34 | } 35 | 36 | let mut arg_types = Vec::new(); 37 | let mut arg_names = Vec::new(); 38 | let mut arg_indexes = Vec::new(); 39 | let mut fargs = f.sig.inputs.clone(); 40 | 41 | for (i, arg) in fargs.iter_mut().enumerate() { 42 | match arg { 43 | syn::FnArg::Receiver(_) => { 44 | ctxt.error_spanned_by(arg, "task functions must not have receiver arguments"); 45 | } 46 | syn::FnArg::Typed(t) => match t.pat.as_mut() { 47 | syn::Pat::Ident(id) => { 48 | arg_names.push(id.ident.clone()); 49 | arg_types.push(t.ty.clone()); 50 | arg_indexes.push(syn::Index::from(i)); 51 | id.mutability = None; 52 | } 53 | _ => { 54 | ctxt.error_spanned_by( 55 | arg, 56 | "pattern matching in task arguments is not yet supported", 57 | ); 58 | } 59 | }, 60 | } 61 | } 62 | 63 | ctxt.check()?; 64 | 65 | let task_ident = f.sig.ident.clone(); 66 | let task_inner_ident = format_ident!("__{}_task", task_ident); 67 | let mod_ident = format_ident!("__{}_mod", task_ident); 68 | let args_ident = format_ident!("__{}_args", task_ident); 69 | 70 | let mut task_inner = f; 71 | let visibility = task_inner.vis.clone(); 72 | task_inner.vis = syn::Visibility::Inherited; 73 | task_inner.sig.ident = task_inner_ident.clone(); 74 | 75 | let result = quote! { 76 | #task_inner 77 | 78 | #[allow(non_camel_case_types)] 79 | type #args_ident = (#(#arg_types,)*); 80 | 81 | mod #mod_ident { 82 | use #embassy_path::executor::SpawnToken; 83 | use #embassy_path::executor::raw::TaskStorage; 84 | 85 | type Fut = impl ::core::future::Future + 'static; 86 | 87 | #[allow(clippy::declare_interior_mutable_const)] 88 | const NEW_TS: TaskStorage = TaskStorage::new(); 89 | 90 | static POOL: [TaskStorage; #pool_size] = [NEW_TS; #pool_size]; 91 | 92 | pub(super) fn task(args: super::#args_ident) -> SpawnToken { 93 | unsafe { TaskStorage::spawn_pool(&POOL, move || super::#task_inner_ident(#(args.#arg_indexes),*)) } 94 | } 95 | } 96 | 97 | #visibility fn #task_ident(#fargs) -> #embassy_path::executor::SpawnToken { 98 | #mod_ident::task((#(#arg_names,)*)) 99 | } 100 | }; 101 | 102 | Ok(result) 103 | } -------------------------------------------------------------------------------- /embassy-macros/src/util/ctxt.rs: -------------------------------------------------------------------------------- 1 | // nifty utility borrowed from serde :) 2 | // https://github.com/serde-rs/serde/blob/master/serde_derive/src/internals/ctxt.rs 3 | 4 | use proc_macro2::TokenStream; 5 | use quote::{quote, ToTokens}; 6 | use std::cell::RefCell; 7 | use std::fmt::Display; 8 | use std::thread; 9 | use syn; 10 | 11 | /// A type to collect errors together and format them. 12 | /// 13 | /// Dropping this object will cause a panic. It must be consumed using `check`. 14 | /// 15 | /// References can be shared since this type uses run-time exclusive mut checking. 16 | #[derive(Default)] 17 | pub struct Ctxt { 18 | // The contents will be set to `None` during checking. This is so that checking can be 19 | // enforced. 20 | errors: RefCell>>, 21 | } 22 | 23 | impl Ctxt { 24 | /// Create a new context object. 25 | /// 26 | /// This object contains no errors, but will still trigger a panic if it is not `check`ed. 27 | pub fn new() -> Self { 28 | Ctxt { 29 | errors: RefCell::new(Some(Vec::new())), 30 | } 31 | } 32 | 33 | /// Add an error to the context object with a tokenenizable object. 34 | /// 35 | /// The object is used for spanning in error messages. 36 | pub fn error_spanned_by(&self, obj: A, msg: T) { 37 | self.errors 38 | .borrow_mut() 39 | .as_mut() 40 | .unwrap() 41 | // Curb monomorphization from generating too many identical methods. 42 | .push(syn::Error::new_spanned(obj.into_token_stream(), msg)); 43 | } 44 | 45 | /// Add one of Syn's parse errors. 46 | #[allow(unused)] 47 | pub fn syn_error(&self, err: syn::Error) { 48 | self.errors.borrow_mut().as_mut().unwrap().push(err); 49 | } 50 | 51 | /// Consume this object, producing a formatted error string if there are errors. 52 | pub fn check(self) -> Result<(), TokenStream> { 53 | let errors = self.errors.borrow_mut().take().unwrap(); 54 | match errors.len() { 55 | 0 => Ok(()), 56 | _ => Err(to_compile_errors(errors)), 57 | } 58 | } 59 | } 60 | 61 | fn to_compile_errors(errors: Vec) -> proc_macro2::TokenStream { 62 | let compile_errors = errors.iter().map(syn::Error::to_compile_error); 63 | quote!(#(#compile_errors)*) 64 | } 65 | 66 | impl Drop for Ctxt { 67 | fn drop(&mut self) { 68 | if !thread::panicking() && self.errors.borrow().is_some() { 69 | panic!("forgot to check for errors"); 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /embassy-macros/src/util/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod ctxt; 2 | pub mod path; 3 | -------------------------------------------------------------------------------- /embassy-macros/src/util/path.rs: -------------------------------------------------------------------------------- 1 | use darling::{FromMeta, Result}; 2 | use proc_macro2::Span; 3 | use syn::{LitStr, Path}; 4 | 5 | #[derive(Debug)] 6 | pub struct ModulePrefix { 7 | literal: LitStr, 8 | } 9 | 10 | impl ModulePrefix { 11 | pub fn new(path: &str) -> Self { 12 | let literal = LitStr::new(path, Span::call_site()); 13 | Self { literal } 14 | } 15 | 16 | pub fn append(&self, component: &str) -> ModulePrefix { 17 | let mut lit = self.literal().value(); 18 | lit.push_str(component); 19 | Self::new(lit.as_str()) 20 | } 21 | 22 | pub fn path(&self) -> Path { 23 | self.literal.parse().unwrap() 24 | } 25 | 26 | pub fn literal(&self) -> &LitStr { 27 | &self.literal 28 | } 29 | } 30 | 31 | impl FromMeta for ModulePrefix { 32 | fn from_string(value: &str) -> Result { 33 | Ok(ModulePrefix::new(value)) 34 | } 35 | } 36 | 37 | impl Default for ModulePrefix { 38 | fn default() -> ModulePrefix { 39 | ModulePrefix::new("::") 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /embassy/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "embassy" 3 | version = "0.1.0" 4 | authors = ["Dario Nieuwenhuis "] 5 | edition = "2018" 6 | resolver = "2" 7 | 8 | [package.metadata.embassy_docs] 9 | src_base = "https://github.com/embassy-rs/embassy/blob/embassy-v$VERSION/embassy/src/" 10 | src_base_git = "https://github.com/embassy-rs/embassy/blob/master/embassy/src/" 11 | features = ["nightly", "defmt", "unstable-traits", "time", "time-tick-1mhz", "time-tick-16mhz"] 12 | flavors = [ 13 | { name = "std", target = "x86_64-unknown-linux-gnu", features = ["std"] }, 14 | { name = "wasm", target = "wasm32-unknown-unknown", features = ["wasm"] }, 15 | { name = "thumbv6m-none-eabi", target = "thumbv6m-none-eabi", features = [] }, 16 | { name = "thumbv7m-none-eabi", target = "thumbv7m-none-eabi", features = [] }, 17 | { name = "thumbv7em-none-eabi", target = "thumbv7em-none-eabi", features = [] }, 18 | { name = "thumbv7em-none-eabihf", target = "thumbv7em-none-eabihf", features = [] }, 19 | { name = "thumbv8m.base-none-eabi", target = "thumbv8m.base-none-eabi", features = [] }, 20 | { name = "thumbv8m.main-none-eabi", target = "thumbv8m.main-none-eabi", features = [] }, 21 | { name = "thumbv8m.main-none-eabihf", target = "thumbv8m.main-none-eabihf", features = [] }, 22 | { name = "riscv32imc-unknown-none-elf", target = "riscv32imc-unknown-none-elf", features = ["riscv32"] }, 23 | ] 24 | 25 | [features] 26 | default = [] 27 | std = ["futures/std", "time", "time-tick-1mhz", "embassy-macros/std"] 28 | wasm = ["wasm-bindgen", "js-sys", "embassy-macros/wasm", "wasm-timer", "time", "time-tick-1mhz"] 29 | 30 | # Enable nightly-only features 31 | nightly = ["embedded-hal-async"] 32 | 33 | # Implement embedded-hal 1.0 alpha and embedded-hal-async traits. 34 | # Implement embedded-hal-async traits if `nightly` is set as well. 35 | unstable-traits = ["embedded-hal-1"] 36 | 37 | # Display a timestamp of the number of seconds since startup next to defmt log messages 38 | # To use this you must have a time driver provided. 39 | defmt-timestamp-uptime = ["defmt"] 40 | 41 | # Enable `embassy::time` module. 42 | # NOTE: This feature is only intended to be enabled by crates providing the time driver implementation. 43 | # Enabling it directly without supplying a time driver will fail to link. 44 | time = [] 45 | 46 | # Set the `embassy::time` tick rate. 47 | # NOTE: This feature is only intended to be enabled by crates providing the time driver implementation. 48 | # If you're not writing your own driver, check the driver documentation to customize the tick rate. 49 | # If you're writing a driver and your tick rate is not listed here, please add it and send a PR! 50 | time-tick-32768hz = ["time"] 51 | time-tick-1000hz = ["time"] 52 | time-tick-1mhz = ["time"] 53 | time-tick-16mhz = ["time"] 54 | 55 | executor-agnostic = [] 56 | 57 | [dependencies] 58 | defmt = { version = "0.3", optional = true } 59 | log = { version = "0.4.14", optional = true } 60 | embedded-hal-02 = { package = "embedded-hal", version = "0.2.6" } 61 | embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.7", git = "https://github.com/embassy-rs/embedded-hal", branch = "embassy2", optional = true} 62 | embedded-hal-async = { version = "0.0.1", git = "https://github.com/embassy-rs/embedded-hal", branch = "embassy2", optional = true} 63 | #TODO REFACTOR SO NOT ADDED FOR CORTEX_M architectures 64 | 65 | futures = { version = "0.3.17", default-features = false, features = [ "cfg-target-has-atomic", "unstable" ] } 66 | pin-project = { version = "1.0.8", default-features = false } 67 | embassy-macros = { version = "0.1.0", path = "../embassy-macros"} 68 | atomic-polyfill = "0.1.5" 69 | critical-section = "0.2.5" 70 | heapless = "0.7.5" 71 | cfg-if = "1.0.0" 72 | 73 | # WASM dependencies 74 | wasm-bindgen = { version = "0.2.76", features = ["nightly"], optional = true } 75 | js-sys = { version = "0.3", optional = true } 76 | wasm-timer = { version = "0.2.5", optional = true } 77 | 78 | [target."thumbv6m-none-eabi".dependencies] 79 | cortex-m = "0.7.3" 80 | [target."thumbv7m-none-eabi".dependencies] 81 | cortex-m = "0.7.3" 82 | [target."thumbv7em-none-eabi".dependencies] 83 | cortex-m = "0.7.3" 84 | [target."thumbv7em-none-eabihf".dependencies] 85 | cortex-m = "0.7.3" 86 | [target."thumbv8m.base-none-eabi".dependencies] 87 | cortex-m = "0.7.3" 88 | [target."thumbv8m.main-none-eabi".dependencies] 89 | cortex-m = "0.7.3" 90 | [target."thumbv8m.main-none-eabihf".dependencies] 91 | cortex-m = "0.7.3" 92 | [target."riscv32imc-unknown-none-elf".dependencies] 93 | riscv = "0.7.0" 94 | 95 | 96 | 97 | [dev-dependencies] 98 | embassy = { path = ".", features = ["executor-agnostic"] } 99 | futures-executor = { version = "0.3.17", features = [ "thread-pool" ] } 100 | futures-test = "0.3.17" 101 | futures-timer = "3.0.2" 102 | futures-util = { version = "0.3.17", features = [ "channel" ] } 103 | -------------------------------------------------------------------------------- /embassy/build.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | 3 | fn main() { 4 | let target = env::var("TARGET").unwrap(); 5 | 6 | if target.starts_with("thumbv6m-") { 7 | println!("cargo:rustc-cfg=cortex_m"); 8 | println!("cargo:rustc-cfg=armv6m"); 9 | } else if target.starts_with("thumbv7m-") { 10 | println!("cargo:rustc-cfg=cortex_m"); 11 | println!("cargo:rustc-cfg=armv7m"); 12 | } else if target.starts_with("thumbv7em-") { 13 | println!("cargo:rustc-cfg=cortex_m"); 14 | println!("cargo:rustc-cfg=armv7m"); 15 | println!("cargo:rustc-cfg=armv7em"); // (not currently used) 16 | } else if target.starts_with("thumbv8m.base") { 17 | println!("cargo:rustc-cfg=cortex_m"); 18 | println!("cargo:rustc-cfg=armv8m"); 19 | println!("cargo:rustc-cfg=armv8m_base"); 20 | } else if target.starts_with("thumbv8m.main") { 21 | println!("cargo:rustc-cfg=cortex_m"); 22 | println!("cargo:rustc-cfg=armv8m"); 23 | println!("cargo:rustc-cfg=armv8m_main"); 24 | } 25 | 26 | if target.ends_with("-eabihf") { 27 | println!("cargo:rustc-cfg=has_fpu"); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /embassy/src/blocking_mutex/mod.rs: -------------------------------------------------------------------------------- 1 | //! Blocking mutex (not async) 2 | 3 | pub mod raw; 4 | 5 | use self::raw::RawMutex; 6 | use core::cell::UnsafeCell; 7 | 8 | /// Any object implementing this trait guarantees exclusive access to the data contained 9 | /// within the mutex for the duration of the lock. 10 | /// Adapted from . 11 | pub struct Mutex { 12 | // NOTE: `raw` must be FIRST, so when using ThreadModeMutex the "can't drop in non-thread-mode" gets 13 | // to run BEFORE dropping `data`. 14 | raw: R, 15 | data: UnsafeCell, 16 | } 17 | 18 | unsafe impl Send for Mutex {} 19 | unsafe impl Sync for Mutex {} 20 | 21 | impl Mutex { 22 | /// Creates a new mutex in an unlocked state ready for use. 23 | #[cfg(feature = "nightly")] 24 | #[inline] 25 | pub const fn new(val: T) -> Mutex { 26 | Mutex { 27 | raw: R::INIT, 28 | data: UnsafeCell::new(val), 29 | } 30 | } 31 | 32 | /// Creates a new mutex in an unlocked state ready for use. 33 | #[cfg(not(feature = "nightly"))] 34 | #[inline] 35 | pub fn new(val: T) -> Mutex { 36 | Mutex { 37 | raw: R::INIT, 38 | data: UnsafeCell::new(val), 39 | } 40 | } 41 | 42 | /// Creates a critical section and grants temporary access to the protected data. 43 | pub fn lock(&self, f: impl FnOnce(&T) -> U) -> U { 44 | self.raw.lock(|| { 45 | let ptr = self.data.get() as *const T; 46 | let inner = unsafe { &*ptr }; 47 | f(inner) 48 | }) 49 | } 50 | } 51 | 52 | impl Mutex { 53 | /// Creates a new mutex based on a pre-existing raw mutex. 54 | /// 55 | /// This allows creating a mutex in a constant context on stable Rust. 56 | #[inline] 57 | pub const fn const_new(raw_mutex: R, val: T) -> Mutex { 58 | Mutex { 59 | raw: raw_mutex, 60 | data: UnsafeCell::new(val), 61 | } 62 | } 63 | 64 | /// Consumes this mutex, returning the underlying data. 65 | #[inline] 66 | pub fn into_inner(self) -> T { 67 | self.data.into_inner() 68 | } 69 | 70 | /// Returns a mutable reference to the underlying data. 71 | /// 72 | /// Since this call borrows the `Mutex` mutably, no actual locking needs to 73 | /// take place---the mutable borrow statically guarantees no locks exist. 74 | #[inline] 75 | pub fn get_mut(&mut self) -> &mut T { 76 | unsafe { &mut *self.data.get() } 77 | } 78 | } 79 | 80 | pub type CriticalSectionMutex = Mutex; 81 | pub type NoopMutex = Mutex; 82 | 83 | impl Mutex { 84 | /// Borrows the data for the duration of the critical section 85 | pub fn borrow<'cs>(&'cs self, _cs: critical_section::CriticalSection<'cs>) -> &'cs T { 86 | let ptr = self.data.get() as *const T; 87 | unsafe { &*ptr } 88 | } 89 | } 90 | 91 | impl Mutex { 92 | /// Borrows the data 93 | pub fn borrow(&self) -> &T { 94 | let ptr = self.data.get() as *const T; 95 | unsafe { &*ptr } 96 | } 97 | } 98 | 99 | // ThreadModeMutex does NOT use the generic mutex from above because it's special: 100 | // it's Send+Sync even if T: !Send. There's no way to do that without specialization (I think?). 101 | // 102 | // There's still a ThreadModeRawMutex for use with the generic Mutex (handy with Channel, for example), 103 | // but that will require T: Send even though it shouldn't be needed. 104 | 105 | #[cfg(any(cortex_m, feature = "std",target_arch="riscv32"))] 106 | pub use thread_mode_mutex::*; 107 | #[cfg(any(cortex_m, feature = "std", riscv,target_arch="riscv32"))] 108 | mod thread_mode_mutex { 109 | use super::*; 110 | 111 | /// A "mutex" that only allows borrowing from thread mode. 112 | /// 113 | /// # Safety 114 | /// 115 | /// **This Mutex is only safe on single-core systems.** 116 | /// 117 | /// On multi-core systems, a `ThreadModeMutex` **is not sufficient** to ensure exclusive access. 118 | pub struct ThreadModeMutex { 119 | inner: UnsafeCell, 120 | } 121 | 122 | // NOTE: ThreadModeMutex only allows borrowing from one execution context ever: thread mode. 123 | // Therefore it cannot be used to send non-sendable stuff between execution contexts, so it can 124 | // be Send+Sync even if T is not Send (unlike CriticalSectionMutex) 125 | unsafe impl Sync for ThreadModeMutex {} 126 | unsafe impl Send for ThreadModeMutex {} 127 | 128 | impl ThreadModeMutex { 129 | /// Creates a new mutex 130 | pub const fn new(value: T) -> Self { 131 | ThreadModeMutex { 132 | inner: UnsafeCell::new(value), 133 | } 134 | } 135 | } 136 | 137 | impl ThreadModeMutex { 138 | pub fn lock(&self, f: impl FnOnce(&T) -> R) -> R { 139 | f(self.borrow()) 140 | } 141 | 142 | /// Borrows the data 143 | pub fn borrow(&self) -> &T { 144 | assert!( 145 | raw::in_thread_mode(), 146 | "ThreadModeMutex can only be borrowed from thread mode." 147 | ); 148 | unsafe { &*self.inner.get() } 149 | } 150 | } 151 | 152 | impl Drop for ThreadModeMutex { 153 | fn drop(&mut self) { 154 | // Only allow dropping from thread mode. Dropping calls drop on the inner `T`, so 155 | // `drop` needs the same guarantees as `lock`. `ThreadModeMutex` is Send even if 156 | // T isn't, so without this check a user could create a ThreadModeMutex in thread mode, 157 | // send it to interrupt context and drop it there, which would "send" a T even if T is not Send. 158 | assert!( 159 | raw::in_thread_mode(), 160 | "ThreadModeMutex can only be dropped from thread mode." 161 | ); 162 | 163 | // Drop of the inner `T` happens after this. 164 | } 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /embassy/src/blocking_mutex/raw.rs: -------------------------------------------------------------------------------- 1 | use core::marker::PhantomData; 2 | 3 | pub trait RawMutex { 4 | const INIT: Self; 5 | 6 | fn lock(&self, f: impl FnOnce() -> R) -> R; 7 | } 8 | 9 | pub struct CriticalSectionRawMutex { 10 | _phantom: PhantomData<()>, 11 | } 12 | unsafe impl Send for CriticalSectionRawMutex {} 13 | unsafe impl Sync for CriticalSectionRawMutex {} 14 | 15 | impl CriticalSectionRawMutex { 16 | pub const fn new() -> Self { 17 | Self { 18 | _phantom: PhantomData, 19 | } 20 | } 21 | } 22 | 23 | impl RawMutex for CriticalSectionRawMutex { 24 | const INIT: Self = Self::new(); 25 | 26 | fn lock(&self, f: impl FnOnce() -> R) -> R { 27 | critical_section::with(|_| f()) 28 | } 29 | } 30 | 31 | // ================ 32 | 33 | pub struct NoopRawMutex { 34 | _phantom: PhantomData<*mut ()>, 35 | } 36 | 37 | unsafe impl Send for NoopRawMutex {} 38 | 39 | impl NoopRawMutex { 40 | pub const fn new() -> Self { 41 | Self { 42 | _phantom: PhantomData, 43 | } 44 | } 45 | } 46 | 47 | impl RawMutex for NoopRawMutex { 48 | const INIT: Self = Self::new(); 49 | fn lock(&self, f: impl FnOnce() -> R) -> R { 50 | f() 51 | } 52 | } 53 | 54 | // ================ 55 | 56 | #[cfg(any(cortex_m, feature = "std",target_arch="riscv32"))] 57 | mod thread_mode { 58 | use super::*; 59 | 60 | pub struct ThreadModeRawMutex { 61 | _phantom: PhantomData<()>, 62 | } 63 | 64 | unsafe impl Send for ThreadModeRawMutex {} 65 | unsafe impl Sync for ThreadModeRawMutex {} 66 | 67 | impl ThreadModeRawMutex { 68 | pub const fn new() -> Self { 69 | Self { 70 | _phantom: PhantomData, 71 | } 72 | } 73 | } 74 | 75 | impl RawMutex for ThreadModeRawMutex { 76 | const INIT: Self = Self::new(); 77 | fn lock(&self, f: impl FnOnce() -> R) -> R { 78 | assert!( 79 | in_thread_mode(), 80 | "ThreadModeMutex can only be locked from thread mode." 81 | ); 82 | 83 | f() 84 | } 85 | } 86 | 87 | impl Drop for ThreadModeRawMutex { 88 | fn drop(&mut self) { 89 | // Only allow dropping from thread mode. Dropping calls drop on the inner `T`, so 90 | // `drop` needs the same guarantees as `lock`. `ThreadModeMutex` is Send even if 91 | // T isn't, so without this check a user could create a ThreadModeMutex in thread mode, 92 | // send it to interrupt context and drop it there, which would "send" a T even if T is not Send. 93 | assert!( 94 | in_thread_mode(), 95 | "ThreadModeMutex can only be dropped from thread mode." 96 | ); 97 | 98 | // Drop of the inner `T` happens after this. 99 | } 100 | } 101 | 102 | pub(crate) fn in_thread_mode() -> bool { 103 | #[cfg(feature = "std")] 104 | return Some("main") == std::thread::current().name(); 105 | // TODO refactor for interrupt mode on riscv 106 | #[cfg(target_arch="riscv32")] 107 | return true; 108 | #[cfg(cortex_m)] 109 | return cortex_m::peripheral::SCB::vect_active() 110 | == cortex_m::peripheral::scb::VectActive::ThreadMode; 111 | } 112 | } 113 | #[cfg(any(cortex_m, feature = "std", target_arch="riscv32"))] 114 | pub use thread_mode::*; 115 | -------------------------------------------------------------------------------- /embassy/src/channel/mod.rs: -------------------------------------------------------------------------------- 1 | //! Async channels 2 | 3 | pub mod channel; 4 | pub use channel::*; 5 | 6 | pub mod signal; 7 | pub use signal::*; 8 | -------------------------------------------------------------------------------- /embassy/src/channel/signal.rs: -------------------------------------------------------------------------------- 1 | use core::cell::UnsafeCell; 2 | use core::future::Future; 3 | use core::mem; 4 | use core::task::{Context, Poll, Waker}; 5 | 6 | /// Synchronization primitive. Allows creating awaitable signals that may be passed between tasks. 7 | /// For a simple use-case where the receiver is only ever interested in the latest value of 8 | /// something, Signals work well. For more advanced use cases, you might want to use [`Channel`](crate::channel::channel::Channel) instead.. 9 | /// 10 | /// Signals are generally declared as being a static const and then borrowed as required. 11 | /// 12 | /// ``` 13 | /// use embassy::channel::signal::Signal; 14 | /// 15 | /// enum SomeCommand { 16 | /// On, 17 | /// Off, 18 | /// } 19 | /// 20 | /// static SOME_SIGNAL: Signal = Signal::new(); 21 | /// ``` 22 | pub struct Signal { 23 | state: UnsafeCell>, 24 | } 25 | 26 | enum State { 27 | None, 28 | Waiting(Waker), 29 | Signaled(T), 30 | } 31 | 32 | unsafe impl Send for Signal {} 33 | unsafe impl Sync for Signal {} 34 | 35 | impl Signal { 36 | pub const fn new() -> Self { 37 | Self { 38 | state: UnsafeCell::new(State::None), 39 | } 40 | } 41 | } 42 | 43 | impl Signal { 44 | /// Mark this Signal as completed. 45 | pub fn signal(&self, val: T) { 46 | critical_section::with(|_| unsafe { 47 | let state = &mut *self.state.get(); 48 | if let State::Waiting(waker) = mem::replace(state, State::Signaled(val)) { 49 | waker.wake(); 50 | } 51 | }) 52 | } 53 | 54 | pub fn reset(&self) { 55 | critical_section::with(|_| unsafe { 56 | let state = &mut *self.state.get(); 57 | *state = State::None 58 | }) 59 | } 60 | 61 | pub fn poll_wait(&self, cx: &mut Context<'_>) -> Poll { 62 | critical_section::with(|_| unsafe { 63 | let state = &mut *self.state.get(); 64 | match state { 65 | State::None => { 66 | *state = State::Waiting(cx.waker().clone()); 67 | Poll::Pending 68 | } 69 | State::Waiting(w) if w.will_wake(cx.waker()) => Poll::Pending, 70 | State::Waiting(_) => panic!("waker overflow"), 71 | State::Signaled(_) => match mem::replace(state, State::None) { 72 | State::Signaled(res) => Poll::Ready(res), 73 | _ => unreachable!(), 74 | }, 75 | } 76 | }) 77 | } 78 | 79 | /// Future that completes when this Signal has been signaled. 80 | pub fn wait(&self) -> impl Future + '_ { 81 | futures::future::poll_fn(move |cx| self.poll_wait(cx)) 82 | } 83 | 84 | /// non-blocking method to check whether this signal has been signaled. 85 | pub fn signaled(&self) -> bool { 86 | critical_section::with(|_| matches!(unsafe { &*self.state.get() }, State::Signaled(_))) 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /embassy/src/executor/arch/cortex_m.rs: -------------------------------------------------------------------------------- 1 | use core::marker::PhantomData; 2 | use core::ptr; 3 | 4 | use super::{raw, Spawner}; 5 | use crate::interrupt::{Interrupt, InterruptExt}; 6 | 7 | /// Thread mode executor, using WFE/SEV. 8 | /// 9 | /// This is the simplest and most common kind of executor. It runs on 10 | /// thread mode (at the lowest priority level), and uses the `WFE` ARM instruction 11 | /// to sleep when it has no more work to do. When a task is woken, a `SEV` instruction 12 | /// is executed, to make the `WFE` exit from sleep and poll the task. 13 | /// 14 | /// This executor allows for ultra low power consumption for chips where `WFE` 15 | /// triggers low-power sleep without extra steps. If your chip requires extra steps, 16 | /// you may use [`raw::Executor`] directly to program custom behavior. 17 | pub struct Executor { 18 | inner: raw::Executor, 19 | not_send: PhantomData<*mut ()>, 20 | } 21 | 22 | impl Executor { 23 | /// Create a new Executor. 24 | pub fn new() -> Self { 25 | Self { 26 | inner: raw::Executor::new(|_| cortex_m::asm::sev(), ptr::null_mut()), 27 | not_send: PhantomData, 28 | } 29 | } 30 | 31 | /// Run the executor. 32 | /// 33 | /// The `init` closure is called with a [`Spawner`] that spawns tasks on 34 | /// this executor. Use it to spawn the initial task(s). After `init` returns, 35 | /// the executor starts running the tasks. 36 | /// 37 | /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`), 38 | /// for example by passing it as an argument to the initial tasks. 39 | /// 40 | /// This function requires `&'static mut self`. This means you have to store the 41 | /// Executor instance in a place where it'll live forever and grants you mutable 42 | /// access. There's a few ways to do this: 43 | /// 44 | /// - a [Forever](crate::util::Forever) (safe) 45 | /// - a `static mut` (unsafe) 46 | /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe) 47 | /// 48 | /// This function never returns. 49 | pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { 50 | init(self.inner.spawner()); 51 | 52 | loop { 53 | unsafe { self.inner.poll() }; 54 | cortex_m::asm::wfe(); 55 | } 56 | } 57 | } 58 | 59 | fn pend_by_number(n: u16) { 60 | #[derive(Clone, Copy)] 61 | struct N(u16); 62 | unsafe impl cortex_m::interrupt::InterruptNumber for N { 63 | fn number(self) -> u16 { 64 | self.0 65 | } 66 | } 67 | cortex_m::peripheral::NVIC::pend(N(n)) 68 | } 69 | 70 | /// Interrupt mode executor. 71 | /// 72 | /// This executor runs tasks in interrupt mode. The interrupt handler is set up 73 | /// to poll tasks, and when a task is woken the interrupt is pended from software. 74 | /// 75 | /// This allows running async tasks at a priority higher than thread mode. One 76 | /// use case is to leave thread mode free for non-async tasks. Another use case is 77 | /// to run multiple executors: one in thread mode for low priority tasks and another in 78 | /// interrupt mode for higher priority tasks. Higher priority tasks will preempt lower 79 | /// priority ones. 80 | /// 81 | /// It is even possible to run multiple interrupt mode executors at different priorities, 82 | /// by assigning different priorities to the interrupts. For an example on how to do this, 83 | /// See the 'multiprio' example for 'embassy-nrf'. 84 | /// 85 | /// To use it, you have to pick an interrupt that won't be used by the hardware. 86 | /// Some chips reserve some interrupts for this purpose, sometimes named "software interrupts" (SWI). 87 | /// If this is not the case, you may use an interrupt from any unused peripheral. 88 | /// 89 | /// It is somewhat more complex to use, it's recommended to use the thread-mode 90 | /// [`Executor`] instead, if it works for your use case. 91 | pub struct InterruptExecutor { 92 | irq: I, 93 | inner: raw::Executor, 94 | not_send: PhantomData<*mut ()>, 95 | } 96 | 97 | impl InterruptExecutor { 98 | /// Create a new Executor. 99 | pub fn new(irq: I) -> Self { 100 | let ctx = irq.number() as *mut (); 101 | Self { 102 | irq, 103 | inner: raw::Executor::new(|ctx| pend_by_number(ctx as u16), ctx), 104 | not_send: PhantomData, 105 | } 106 | } 107 | 108 | /// Start the executor. 109 | /// 110 | /// The `init` closure is called from interrupt mode, with a [`Spawner`] that spawns tasks on 111 | /// this executor. Use it to spawn the initial task(s). After `init` returns, 112 | /// the interrupt is configured so that the executor starts running the tasks. 113 | /// Once the executor is started, `start` returns. 114 | /// 115 | /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`), 116 | /// for example by passing it as an argument to the initial tasks. 117 | /// 118 | /// This function requires `&'static mut self`. This means you have to store the 119 | /// Executor instance in a place where it'll live forever and grants you mutable 120 | /// access. There's a few ways to do this: 121 | /// 122 | /// - a [Forever](crate::util::Forever) (safe) 123 | /// - a `static mut` (unsafe) 124 | /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe) 125 | pub fn start(&'static mut self, init: impl FnOnce(Spawner) + Send) { 126 | self.irq.disable(); 127 | 128 | init(self.inner.spawner()); 129 | 130 | self.irq.set_handler(|ctx| unsafe { 131 | let executor = &*(ctx as *const raw::Executor); 132 | executor.poll(); 133 | }); 134 | self.irq.set_handler_context(&self.inner as *const _ as _); 135 | self.irq.enable(); 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /embassy/src/executor/arch/std.rs: -------------------------------------------------------------------------------- 1 | use std::marker::PhantomData; 2 | use std::sync::{Condvar, Mutex}; 3 | 4 | use super::{raw, Spawner}; 5 | 6 | /// Single-threaded std-based executor. 7 | pub struct Executor { 8 | inner: raw::Executor, 9 | not_send: PhantomData<*mut ()>, 10 | signaler: &'static Signaler, 11 | } 12 | 13 | impl Executor { 14 | /// Create a new Executor. 15 | pub fn new() -> Self { 16 | let signaler = &*Box::leak(Box::new(Signaler::new())); 17 | Self { 18 | inner: raw::Executor::new( 19 | |p| unsafe { 20 | let s = &*(p as *const () as *const Signaler); 21 | s.signal() 22 | }, 23 | signaler as *const _ as _, 24 | ), 25 | not_send: PhantomData, 26 | signaler, 27 | } 28 | } 29 | 30 | /// Run the executor. 31 | /// 32 | /// The `init` closure is called with a [`Spawner`] that spawns tasks on 33 | /// this executor. Use it to spawn the initial task(s). After `init` returns, 34 | /// the executor starts running the tasks. 35 | /// 36 | /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`), 37 | /// for example by passing it as an argument to the initial tasks. 38 | /// 39 | /// This function requires `&'static mut self`. This means you have to store the 40 | /// Executor instance in a place where it'll live forever and grants you mutable 41 | /// access. There's a few ways to do this: 42 | /// 43 | /// - a [Forever](crate::util::Forever) (safe) 44 | /// - a `static mut` (unsafe) 45 | /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe) 46 | /// 47 | /// This function never returns. 48 | pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { 49 | init(self.inner.spawner()); 50 | 51 | loop { 52 | unsafe { self.inner.poll() }; 53 | self.signaler.wait() 54 | } 55 | } 56 | } 57 | 58 | struct Signaler { 59 | mutex: Mutex, 60 | condvar: Condvar, 61 | } 62 | 63 | impl Signaler { 64 | fn new() -> Self { 65 | Self { 66 | mutex: Mutex::new(false), 67 | condvar: Condvar::new(), 68 | } 69 | } 70 | 71 | fn wait(&self) { 72 | let mut signaled = self.mutex.lock().unwrap(); 73 | while !*signaled { 74 | signaled = self.condvar.wait(signaled).unwrap(); 75 | } 76 | *signaled = false; 77 | } 78 | 79 | fn signal(&self) { 80 | let mut signaled = self.mutex.lock().unwrap(); 81 | *signaled = true; 82 | self.condvar.notify_one(); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /embassy/src/executor/arch/wasm.rs: -------------------------------------------------------------------------------- 1 | use core::marker::PhantomData; 2 | use js_sys::Promise; 3 | use wasm_bindgen::prelude::*; 4 | 5 | use super::{ 6 | raw::{self, util::UninitCell}, 7 | Spawner, 8 | }; 9 | 10 | /// WASM executor, wasm_bindgen to schedule tasks on the JS event loop. 11 | pub struct Executor { 12 | inner: raw::Executor, 13 | ctx: &'static WasmContext, 14 | not_send: PhantomData<*mut ()>, 15 | } 16 | 17 | pub(crate) struct WasmContext { 18 | promise: Promise, 19 | closure: UninitCell>, 20 | } 21 | 22 | impl WasmContext { 23 | pub fn new() -> Self { 24 | Self { 25 | promise: Promise::resolve(&JsValue::undefined()), 26 | closure: UninitCell::uninit(), 27 | } 28 | } 29 | } 30 | 31 | impl Executor { 32 | /// Create a new Executor. 33 | pub fn new() -> Self { 34 | let ctx = &*Box::leak(Box::new(WasmContext::new())); 35 | let inner = raw::Executor::new( 36 | |p| unsafe { 37 | let ctx = &*(p as *const () as *const WasmContext); 38 | let _ = ctx.promise.then(ctx.closure.as_mut()); 39 | }, 40 | ctx as *const _ as _, 41 | ); 42 | Self { 43 | inner, 44 | not_send: PhantomData, 45 | ctx, 46 | } 47 | } 48 | 49 | /// Run the executor. 50 | /// 51 | /// The `init` closure is called with a [`Spawner`] that spawns tasks on 52 | /// this executor. Use it to spawn the initial task(s). After `init` returns, 53 | /// the executor starts running the tasks. 54 | /// 55 | /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`), 56 | /// for example by passing it as an argument to the initial tasks. 57 | /// 58 | /// This function requires `&'static mut self`. This means you have to store the 59 | /// Executor instance in a place where it'll live forever and grants you mutable 60 | /// access. There's a few ways to do this: 61 | /// 62 | /// - a [Forever](crate::util::Forever) (safe) 63 | /// - a `static mut` (unsafe) 64 | /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe) 65 | pub fn start(&'static mut self, init: impl FnOnce(Spawner)) { 66 | unsafe { 67 | let executor = &self.inner; 68 | self.ctx.closure.write(Closure::new(move |_| { 69 | executor.poll(); 70 | })); 71 | init(self.inner.spawner()); 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /embassy/src/executor/mod.rs: -------------------------------------------------------------------------------- 1 | //! Async task executor. 2 | 3 | #![deny(missing_docs)] 4 | 5 | cfg_if::cfg_if! { 6 | if #[cfg(cortex_m)] { 7 | #[path="arch/cortex_m.rs"] 8 | mod arch; 9 | pub use arch::*; 10 | } 11 | else if #[cfg(target_arch="riscv32")] { 12 | #[path="arch/riscv32.rs"] 13 | mod arch; 14 | pub use arch::*; 15 | } 16 | 17 | else if #[cfg(feature="wasm")] { 18 | #[path="arch/wasm.rs"] 19 | mod arch; 20 | pub use arch::*; 21 | } 22 | else if #[cfg(feature="std")] { 23 | #[path="arch/std.rs"] 24 | mod arch; 25 | pub use arch::*; 26 | } 27 | } 28 | 29 | pub mod raw; 30 | 31 | mod spawner; 32 | pub use spawner::*; 33 | -------------------------------------------------------------------------------- /embassy/src/executor/raw/run_queue.rs: -------------------------------------------------------------------------------- 1 | use atomic_polyfill::{AtomicPtr, Ordering}; 2 | use core::ptr; 3 | use core::ptr::NonNull; 4 | use critical_section::CriticalSection; 5 | 6 | use super::TaskHeader; 7 | 8 | pub(crate) struct RunQueueItem { 9 | next: AtomicPtr, 10 | } 11 | 12 | impl RunQueueItem { 13 | pub const fn new() -> Self { 14 | Self { 15 | next: AtomicPtr::new(ptr::null_mut()), 16 | } 17 | } 18 | } 19 | 20 | /// Atomic task queue using a very, very simple lock-free linked-list queue: 21 | /// 22 | /// To enqueue a task, task.next is set to the old head, and head is atomically set to task. 23 | /// 24 | /// Dequeuing is done in batches: the queue is emptied by atomically replacing head with 25 | /// null. Then the batch is iterated following the next pointers until null is reached. 26 | /// 27 | /// Note that batches will be iterated in the reverse order as they were enqueued. This is OK 28 | /// for our purposes: it can't create fairness problems since the next batch won't run until the 29 | /// current batch is completely processed, so even if a task enqueues itself instantly (for example 30 | /// by waking its own waker) can't prevent other tasks from running. 31 | pub(crate) struct RunQueue { 32 | head: AtomicPtr, 33 | } 34 | 35 | impl RunQueue { 36 | pub const fn new() -> Self { 37 | Self { 38 | head: AtomicPtr::new(ptr::null_mut()), 39 | } 40 | } 41 | 42 | /// Enqueues an item. Returns true if the queue was empty. 43 | /// 44 | /// # Safety 45 | /// 46 | /// `item` must NOT be already enqueued in any queue. 47 | #[inline(always)] 48 | pub(crate) unsafe fn enqueue(&self, _cs: CriticalSection, task: *mut TaskHeader) -> bool { 49 | let prev = self.head.load(Ordering::Relaxed); 50 | (*task).run_queue_item.next.store(prev, Ordering::Relaxed); 51 | self.head.store(task, Ordering::Relaxed); 52 | prev.is_null() 53 | } 54 | 55 | /// Empty the queue, then call `on_task` for each task that was in the queue. 56 | /// NOTE: It is OK for `on_task` to enqueue more tasks. In this case they're left in the queue 57 | /// and will be processed by the *next* call to `dequeue_all`, *not* the current one. 58 | pub(crate) fn dequeue_all(&self, on_task: impl Fn(NonNull)) { 59 | // Atomically empty the queue. 60 | let mut ptr = self.head.swap(ptr::null_mut(), Ordering::AcqRel); 61 | 62 | // Iterate the linked list of tasks that were previously in the queue. 63 | while let Some(task) = NonNull::new(ptr) { 64 | // If the task re-enqueues itself, the `next` pointer will get overwritten. 65 | // Therefore, first read the next pointer, and only then process the task. 66 | let next = unsafe { task.as_ref() } 67 | .run_queue_item 68 | .next 69 | .load(Ordering::Relaxed); 70 | 71 | on_task(task); 72 | 73 | ptr = next 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /embassy/src/executor/raw/timer_queue.rs: -------------------------------------------------------------------------------- 1 | use atomic_polyfill::Ordering; 2 | use core::cell::Cell; 3 | use core::cmp::min; 4 | use core::ptr; 5 | use core::ptr::NonNull; 6 | 7 | use super::{TaskHeader, STATE_TIMER_QUEUED}; 8 | use crate::time::Instant; 9 | 10 | pub(crate) struct TimerQueueItem { 11 | next: Cell<*mut TaskHeader>, 12 | } 13 | 14 | impl TimerQueueItem { 15 | pub const fn new() -> Self { 16 | Self { 17 | next: Cell::new(ptr::null_mut()), 18 | } 19 | } 20 | } 21 | 22 | pub(crate) struct TimerQueue { 23 | head: Cell<*mut TaskHeader>, 24 | } 25 | 26 | impl TimerQueue { 27 | pub const fn new() -> Self { 28 | Self { 29 | head: Cell::new(ptr::null_mut()), 30 | } 31 | } 32 | // if task is already in queue: do nothing 33 | pub(crate) unsafe fn update(&self, p: NonNull) { 34 | let task = p.as_ref(); 35 | if task.expires_at.get() != Instant::MAX { 36 | let old_state = task.state.fetch_or(STATE_TIMER_QUEUED, Ordering::AcqRel); 37 | let is_new = old_state & STATE_TIMER_QUEUED == 0; 38 | //if task is new (not in timer queue), add it to timer Queue 39 | if is_new { 40 | task.timer_queue_item.next.set(self.head.get()); 41 | self.head.set(p.as_ptr()); 42 | } 43 | } 44 | } 45 | 46 | pub(crate) unsafe fn next_expiration(&self) -> Instant { 47 | let mut res = Instant::MAX; 48 | self.retain(|p| { 49 | let task = p.as_ref(); 50 | let expires = task.expires_at.get(); 51 | res = min(res, expires); 52 | expires != Instant::MAX 53 | }); 54 | res 55 | } 56 | 57 | pub(crate) unsafe fn dequeue_expired( 58 | &self, 59 | now: Instant, 60 | on_task: impl Fn(NonNull), 61 | ) { 62 | self.retain(|p| { 63 | let task = p.as_ref(); 64 | if task.expires_at.get() <= now { 65 | on_task(p); 66 | false 67 | } else { 68 | true 69 | } 70 | }); 71 | } 72 | 73 | pub(crate) unsafe fn retain(&self, mut f: impl FnMut(NonNull) -> bool) { 74 | let mut prev = &self.head; 75 | while !prev.get().is_null() { 76 | let p = NonNull::new_unchecked(prev.get()); 77 | let task = &*p.as_ptr(); 78 | if f(p) { 79 | // Skip to next 80 | //change (pointer to a Cell<*mut TaskHeader>) to point to next element 81 | prev = &task.timer_queue_item.next; 82 | } else { 83 | // Remove it 84 | //set the element inside the cell to the next item 85 | prev.set(task.timer_queue_item.next.get()); 86 | task.state.fetch_and(!STATE_TIMER_QUEUED, Ordering::AcqRel); 87 | } 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /embassy/src/executor/raw/util.rs: -------------------------------------------------------------------------------- 1 | use core::cell::UnsafeCell; 2 | use core::mem::MaybeUninit; 3 | use core::ptr; 4 | 5 | pub(crate) struct UninitCell(MaybeUninit>); 6 | impl UninitCell { 7 | pub const fn uninit() -> Self { 8 | Self(MaybeUninit::uninit()) 9 | } 10 | 11 | pub unsafe fn as_mut_ptr(&self) -> *mut T { 12 | (*self.0.as_ptr()).get() 13 | } 14 | 15 | #[allow(clippy::mut_from_ref)] 16 | pub unsafe fn as_mut(&self) -> &mut T { 17 | &mut *self.as_mut_ptr() 18 | } 19 | 20 | pub unsafe fn write(&self, val: T) { 21 | ptr::write(self.as_mut_ptr(), val) 22 | } 23 | 24 | pub unsafe fn drop_in_place(&self) { 25 | ptr::drop_in_place(self.as_mut_ptr()) 26 | } 27 | } 28 | 29 | impl UninitCell { 30 | pub unsafe fn read(&self) -> T { 31 | ptr::read(self.as_mut_ptr()) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /embassy/src/executor/raw/waker.rs: -------------------------------------------------------------------------------- 1 | use core::mem; 2 | use core::ptr::NonNull; 3 | use core::task::{RawWaker, RawWakerVTable, Waker}; 4 | 5 | use super::TaskHeader; 6 | 7 | const VTABLE: RawWakerVTable = RawWakerVTable::new(clone, wake, wake, drop); 8 | 9 | unsafe fn clone(p: *const ()) -> RawWaker { 10 | RawWaker::new(p, &VTABLE) 11 | } 12 | 13 | unsafe fn wake(p: *const ()) { 14 | (*(p as *mut TaskHeader)).enqueue() 15 | } 16 | 17 | unsafe fn drop(_: *const ()) { 18 | // nop 19 | } 20 | 21 | pub(crate) unsafe fn from_task(p: NonNull) -> Waker { 22 | Waker::from_raw(RawWaker::new(p.as_ptr() as _, &VTABLE)) 23 | } 24 | 25 | /// Get a task pointer from a waker. 26 | /// 27 | /// This can used as an optimization in wait queues to store task pointers 28 | /// (1 word) instead of full Wakers (2 words). This saves a bit of RAM and helps 29 | /// avoid dynamic dispatch. 30 | /// 31 | /// You can use the returned task pointer to wake the task with [`wake_task`](super::wake_task). 32 | /// 33 | /// # Panics 34 | /// 35 | /// Panics if the waker is not created by the Embassy executor. 36 | pub unsafe fn task_from_waker(waker: &Waker) -> NonNull { 37 | let hack: &WakerHack = mem::transmute(waker); 38 | if hack.vtable != &VTABLE { 39 | panic!("Found waker not created by the embassy executor. Consider enabling the `executor-agnostic` feature on the `embassy` crate.") 40 | } 41 | NonNull::new_unchecked(hack.data as *mut TaskHeader) 42 | } 43 | 44 | struct WakerHack { 45 | data: *const (), 46 | vtable: &'static RawWakerVTable, 47 | } 48 | -------------------------------------------------------------------------------- /embassy/src/executor/spawner.rs: -------------------------------------------------------------------------------- 1 | use core::marker::PhantomData; 2 | use core::mem; 3 | use core::ptr::NonNull; 4 | 5 | use super::raw; 6 | 7 | /// Token to spawn a newly-created task in an executor. 8 | /// 9 | /// When calling a task function (like `#[embassy::task] async fn my_task() { ... }`), the returned 10 | /// value is a `SpawnToken` that represents an instance of the task, ready to spawn. You must 11 | /// then spawn it into an executor, typically with [`Spawner::spawn()`]. 12 | /// 13 | /// # Panics 14 | /// 15 | /// Dropping a SpawnToken instance panics. You may not "abort" spawning a task in this way. 16 | /// Once you've invoked a task function and obtained a SpawnToken, you *must* spawn it. 17 | #[must_use = "Calling a task function does nothing on its own. You must spawn the returned SpawnToken, typically with Spawner::spawn()"] 18 | pub struct SpawnToken { 19 | raw_task: Option>, 20 | phantom: PhantomData<*mut F>, 21 | } 22 | 23 | impl SpawnToken { 24 | pub(crate) unsafe fn new(raw_task: NonNull) -> Self { 25 | Self { 26 | raw_task: Some(raw_task), 27 | phantom: PhantomData, 28 | } 29 | } 30 | 31 | pub(crate) fn new_failed() -> Self { 32 | Self { 33 | raw_task: None, 34 | phantom: PhantomData, 35 | } 36 | } 37 | } 38 | 39 | impl Drop for SpawnToken { 40 | fn drop(&mut self) { 41 | // TODO deallocate the task instead. 42 | panic!("SpawnToken instances may not be dropped. You must pass them to Spawner::spawn()") 43 | } 44 | } 45 | 46 | /// Error returned when spawning a task. 47 | #[derive(Copy, Clone, Debug)] 48 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] 49 | pub enum SpawnError { 50 | /// Too many instances of this task are already running. 51 | /// 52 | /// By default, a task marked with `#[embassy::task]` can only have one instance 53 | /// running at a time. You may allow multiple instances to run in parallel with 54 | /// `#[embassy::task(pool_size = 4)]`, at the cost of higher RAM usage. 55 | Busy, 56 | } 57 | 58 | /// Handle to spawn tasks into an executor. 59 | /// 60 | /// This Spawner can spawn any task (Send and non-Send ones), but it can 61 | /// only be used in the executor thread (it is not Send itself). 62 | /// 63 | /// If you want to spawn tasks from another thread, use [SendSpawner]. 64 | #[derive(Copy, Clone)] 65 | pub struct Spawner { 66 | executor: &'static raw::Executor, 67 | not_send: PhantomData<*mut ()>, 68 | } 69 | 70 | impl Spawner { 71 | pub(crate) fn new(executor: &'static raw::Executor) -> Self { 72 | Self { 73 | executor, 74 | not_send: PhantomData, 75 | } 76 | } 77 | 78 | /// Spawn a task into an executor. 79 | /// 80 | /// You obtain the `token` by calling a task function (i.e. one marked with `#[embassy::task]`). 81 | pub fn spawn(&self, token: SpawnToken) -> Result<(), SpawnError> { 82 | let task = token.raw_task; 83 | mem::forget(token); 84 | 85 | match task { 86 | Some(task) => { 87 | unsafe { self.executor.spawn(task) }; 88 | Ok(()) 89 | } 90 | None => Err(SpawnError::Busy), 91 | } 92 | } 93 | 94 | /// Used by the `embassy_macros::main!` macro to throw an error when spawn 95 | /// fails. This is here to allow conditional use of `defmt::unwrap!` 96 | /// without introducing a `defmt` feature in the `embassy_macros` package, 97 | /// which would require use of `-Z namespaced-features`. 98 | pub fn must_spawn(&self, token: SpawnToken) { 99 | unwrap!(self.spawn(token)); 100 | } 101 | 102 | /// Convert this Spawner to a SendSpawner. This allows you to send the 103 | /// spawner to other threads, but the spawner loses the ability to spawn 104 | /// non-Send tasks. 105 | pub fn make_send(&self) -> SendSpawner { 106 | SendSpawner { 107 | executor: self.executor, 108 | not_send: PhantomData, 109 | } 110 | } 111 | } 112 | 113 | /// Handle to spawn tasks into an executor from any thread. 114 | /// 115 | /// This Spawner can be used from any thread (it is Send), but it can 116 | /// only spawn Send tasks. The reason for this is spawning is effectively 117 | /// "sending" the tasks to the executor thread. 118 | /// 119 | /// If you want to spawn non-Send tasks, use [Spawner]. 120 | #[derive(Copy, Clone)] 121 | pub struct SendSpawner { 122 | executor: &'static raw::Executor, 123 | not_send: PhantomData<*mut ()>, 124 | } 125 | 126 | unsafe impl Send for SendSpawner {} 127 | unsafe impl Sync for SendSpawner {} 128 | 129 | impl SendSpawner { 130 | /// Spawn a task into an executor. 131 | /// 132 | /// You obtain the `token` by calling a task function (i.e. one marked with `#[embassy::task]`). 133 | pub fn spawn(&self, token: SpawnToken) -> Result<(), SpawnError> { 134 | let header = token.raw_task; 135 | mem::forget(token); 136 | 137 | match header { 138 | Some(header) => { 139 | unsafe { self.executor.spawn(header) }; 140 | Ok(()) 141 | } 142 | None => Err(SpawnError::Busy), 143 | } 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /embassy/src/interrupt/interrupt.rs: -------------------------------------------------------------------------------- 1 | use atomic_polyfill::{compiler_fence, AtomicPtr, Ordering}; 2 | use core::mem; 3 | use core::ptr; 4 | use cortex_m::peripheral::NVIC; 5 | 6 | pub use embassy_macros::interrupt_declare as declare; 7 | pub use embassy_macros::interrupt_take as take; 8 | 9 | /// Implementation detail, do not use outside embassy crates. 10 | #[doc(hidden)] 11 | pub struct Handler { 12 | pub func: AtomicPtr<()>, 13 | pub ctx: AtomicPtr<()>, 14 | } 15 | 16 | impl Handler { 17 | pub const fn new() -> Self { 18 | Self { 19 | func: AtomicPtr::new(ptr::null_mut()), 20 | ctx: AtomicPtr::new(ptr::null_mut()), 21 | } 22 | } 23 | } 24 | 25 | #[derive(Clone, Copy)] 26 | pub(crate) struct NrWrap(pub(crate) u16); 27 | unsafe impl cortex_m::interrupt::InterruptNumber for NrWrap { 28 | fn number(self) -> u16 { 29 | self.0 30 | } 31 | } 32 | 33 | pub unsafe trait Interrupt: crate::util::Unborrow { 34 | type Priority: From + Into + Copy; 35 | fn number(&self) -> u16; 36 | unsafe fn steal() -> Self; 37 | 38 | /// Implementation detail, do not use outside embassy crates. 39 | #[doc(hidden)] 40 | unsafe fn __handler(&self) -> &'static Handler; 41 | } 42 | 43 | pub trait InterruptExt: Interrupt { 44 | fn set_handler(&self, func: unsafe fn(*mut ())); 45 | fn remove_handler(&self); 46 | fn set_handler_context(&self, ctx: *mut ()); 47 | fn enable(&self); 48 | fn disable(&self); 49 | #[cfg(not(armv6m))] 50 | fn is_active(&self) -> bool; 51 | fn is_enabled(&self) -> bool; 52 | fn is_pending(&self) -> bool; 53 | fn pend(&self); 54 | fn unpend(&self); 55 | fn get_priority(&self) -> Self::Priority; 56 | fn set_priority(&self, prio: Self::Priority); 57 | } 58 | 59 | impl InterruptExt for T { 60 | fn set_handler(&self, func: unsafe fn(*mut ())) { 61 | compiler_fence(Ordering::SeqCst); 62 | let handler = unsafe { self.__handler() }; 63 | handler.func.store(func as *mut (), Ordering::Relaxed); 64 | compiler_fence(Ordering::SeqCst); 65 | } 66 | 67 | fn remove_handler(&self) { 68 | compiler_fence(Ordering::SeqCst); 69 | let handler = unsafe { self.__handler() }; 70 | handler.func.store(ptr::null_mut(), Ordering::Relaxed); 71 | compiler_fence(Ordering::SeqCst); 72 | } 73 | 74 | fn set_handler_context(&self, ctx: *mut ()) { 75 | let handler = unsafe { self.__handler() }; 76 | handler.ctx.store(ctx, Ordering::Relaxed); 77 | } 78 | 79 | #[inline] 80 | fn enable(&self) { 81 | compiler_fence(Ordering::SeqCst); 82 | unsafe { 83 | NVIC::unmask(NrWrap(self.number())); 84 | } 85 | } 86 | 87 | #[inline] 88 | fn disable(&self) { 89 | NVIC::mask(NrWrap(self.number())); 90 | compiler_fence(Ordering::SeqCst); 91 | } 92 | 93 | #[inline] 94 | #[cfg(not(armv6m))] 95 | fn is_active(&self) -> bool { 96 | NVIC::is_active(NrWrap(self.number())) 97 | } 98 | 99 | #[inline] 100 | fn is_enabled(&self) -> bool { 101 | NVIC::is_enabled(NrWrap(self.number())) 102 | } 103 | 104 | #[inline] 105 | fn is_pending(&self) -> bool { 106 | NVIC::is_pending(NrWrap(self.number())) 107 | } 108 | 109 | #[inline] 110 | fn pend(&self) { 111 | NVIC::pend(NrWrap(self.number())) 112 | } 113 | 114 | #[inline] 115 | fn unpend(&self) { 116 | NVIC::unpend(NrWrap(self.number())) 117 | } 118 | 119 | #[inline] 120 | fn get_priority(&self) -> Self::Priority { 121 | Self::Priority::from(NVIC::get_priority(NrWrap(self.number()))) 122 | } 123 | 124 | #[inline] 125 | fn set_priority(&self, prio: Self::Priority) { 126 | unsafe { 127 | let mut nvic: cortex_m::peripheral::NVIC = mem::transmute(()); 128 | nvic.set_priority(NrWrap(self.number()), prio.into()) 129 | } 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /embassy/src/interrupt/interrupt_riscv32.rs: -------------------------------------------------------------------------------- 1 | 2 | 3 | use atomic_polyfill::{ AtomicPtr}; 4 | use core::ptr; 5 | // use cortex_m::peripheral::NVIC; 6 | 7 | pub use embassy_macros::interrupt_declare as declare; 8 | pub use embassy_macros::interrupt_take as take; 9 | 10 | /// Implementation detail, do not use outside embassy crates. 11 | #[doc(hidden)] 12 | pub struct Handler { 13 | pub func: AtomicPtr<()>, 14 | pub ctx: AtomicPtr<()>, 15 | } 16 | 17 | impl Handler { 18 | pub const fn new() -> Self { 19 | Self { 20 | func: AtomicPtr::new(ptr::null_mut()), 21 | ctx: AtomicPtr::new(ptr::null_mut()), 22 | } 23 | } 24 | } 25 | 26 | pub unsafe trait Interrupt: crate::util::Unborrow { 27 | type Priority: From + Into + Copy; 28 | fn number(&self) -> isize; 29 | unsafe fn steal() -> Self; 30 | 31 | /// Implementation detail, do not use outside embassy crates. 32 | #[doc(hidden)] 33 | unsafe fn __handler(&self) -> &'static Handler; 34 | } 35 | 36 | pub trait InterruptExt: Interrupt { 37 | fn set_handler(&self, func: unsafe fn(*mut ())); 38 | fn remove_handler(&self); 39 | fn set_handler_context(&self, ctx: *mut ()); 40 | fn enable(&self); 41 | fn disable(&self); 42 | #[cfg(not(armv6m))] 43 | fn is_active(&self) -> bool; 44 | fn is_enabled(&self) -> bool; 45 | fn is_pending(&self) -> bool; 46 | fn pend(&self); 47 | fn unpend(&self); 48 | fn get_priority(&self) -> Self::Priority; 49 | fn set_priority(&self, prio: Self::Priority); 50 | } 51 | 52 | -------------------------------------------------------------------------------- /embassy/src/io/mod.rs: -------------------------------------------------------------------------------- 1 | mod error; 2 | #[cfg(feature = "std")] 3 | mod std; 4 | mod traits; 5 | mod util; 6 | 7 | pub use self::error::*; 8 | #[cfg(feature = "std")] 9 | pub use self::std::*; 10 | pub use self::traits::*; 11 | pub use self::util::*; 12 | -------------------------------------------------------------------------------- /embassy/src/io/std.rs: -------------------------------------------------------------------------------- 1 | use core::pin::Pin; 2 | use core::task::{Context, Poll}; 3 | use futures::io as std_io; 4 | 5 | use super::{AsyncBufRead, AsyncWrite, Result}; 6 | 7 | pub struct FromStdIo(T); 8 | 9 | impl FromStdIo { 10 | pub fn new(inner: T) -> Self { 11 | Self(inner) 12 | } 13 | } 14 | 15 | impl AsyncBufRead for FromStdIo { 16 | fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { 17 | let Self(inner) = unsafe { self.get_unchecked_mut() }; 18 | unsafe { Pin::new_unchecked(inner) } 19 | .poll_fill_buf(cx) 20 | .map_err(|e| e.into()) 21 | } 22 | fn consume(self: Pin<&mut Self>, amt: usize) { 23 | let Self(inner) = unsafe { self.get_unchecked_mut() }; 24 | unsafe { Pin::new_unchecked(inner) }.consume(amt) 25 | } 26 | } 27 | 28 | impl AsyncWrite for FromStdIo { 29 | fn poll_write(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) -> Poll> { 30 | let Self(inner) = unsafe { self.get_unchecked_mut() }; 31 | unsafe { Pin::new_unchecked(inner) } 32 | .poll_write(cx, buf) 33 | .map_err(|e| e.into()) 34 | } 35 | fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { 36 | let Self(inner) = unsafe { self.get_unchecked_mut() }; 37 | unsafe { Pin::new_unchecked(inner) } 38 | .poll_flush(cx) 39 | .map_err(|e| e.into()) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /embassy/src/io/util/copy_buf.rs: -------------------------------------------------------------------------------- 1 | use core::future::Future; 2 | use core::pin::Pin; 3 | use core::task::{Context, Poll}; 4 | use futures::ready; 5 | use pin_project::pin_project; 6 | 7 | use crate::io::{AsyncBufRead, AsyncWrite, Error, Result}; 8 | 9 | /// Creates a future which copies all the bytes from one object to another. 10 | /// 11 | /// The returned future will copy all the bytes read from this `AsyncBufRead` into the 12 | /// `writer` specified. This future will only complete once the `reader` has hit 13 | /// EOF and all bytes have been written to and flushed from the `writer` 14 | /// provided. 15 | /// 16 | /// On success the number of bytes is returned. 17 | /// 18 | /// # Examples 19 | /// 20 | /// ``` ignore 21 | /// # futures::executor::block_on(async { 22 | /// use futures::io::{self, AsyncWriteExt, Cursor}; 23 | /// 24 | /// let reader = Cursor::new([1, 2, 3, 4]); 25 | /// let mut writer = Cursor::new(vec![0u8; 5]); 26 | /// 27 | /// let bytes = io::copy_buf(reader, &mut writer).await?; 28 | /// writer.close().await?; 29 | /// 30 | /// assert_eq!(bytes, 4); 31 | /// assert_eq!(writer.into_inner(), [1, 2, 3, 4, 0]); 32 | /// # Ok::<(), Box>(()) }).unwrap(); 33 | /// ``` 34 | pub fn copy_buf(reader: R, writer: &mut W) -> CopyBuf<'_, R, W> 35 | where 36 | R: AsyncBufRead, 37 | W: AsyncWrite + Unpin + ?Sized, 38 | { 39 | CopyBuf { 40 | reader, 41 | writer, 42 | amt: 0, 43 | } 44 | } 45 | 46 | /// Future for the [`copy_buf()`] function. 47 | #[pin_project] 48 | #[derive(Debug)] 49 | #[must_use = "futures do nothing unless you `.await` or poll them"] 50 | pub struct CopyBuf<'a, R, W: ?Sized> { 51 | #[pin] 52 | reader: R, 53 | writer: &'a mut W, 54 | amt: usize, 55 | } 56 | 57 | impl Future for CopyBuf<'_, R, W> 58 | where 59 | R: AsyncBufRead, 60 | W: AsyncWrite + Unpin + ?Sized, 61 | { 62 | type Output = Result; 63 | 64 | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { 65 | let mut this = self.project(); 66 | loop { 67 | let buffer = ready!(this.reader.as_mut().poll_fill_buf(cx))?; 68 | if buffer.is_empty() { 69 | return Poll::Ready(Ok(*this.amt)); 70 | } 71 | 72 | let i = ready!(Pin::new(&mut this.writer).poll_write(cx, buffer))?; 73 | if i == 0 { 74 | return Poll::Ready(Err(Error::WriteZero)); 75 | } 76 | *this.amt += i; 77 | this.reader.as_mut().consume(i); 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /embassy/src/io/util/drain.rs: -------------------------------------------------------------------------------- 1 | use core::pin::Pin; 2 | use futures::future::Future; 3 | use futures::task::{Context, Poll}; 4 | 5 | use super::super::error::Result; 6 | use super::super::traits::AsyncBufRead; 7 | 8 | pub struct Drain<'a, R: ?Sized> { 9 | reader: &'a mut R, 10 | } 11 | 12 | impl Unpin for Drain<'_, R> {} 13 | 14 | impl<'a, R: AsyncBufRead + ?Sized + Unpin> Drain<'a, R> { 15 | pub(super) fn new(reader: &'a mut R) -> Self { 16 | Self { reader } 17 | } 18 | } 19 | 20 | impl<'a, R: AsyncBufRead + ?Sized + Unpin> Future for Drain<'a, R> { 21 | type Output = Result; 22 | 23 | fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { 24 | let Self { reader } = &mut *self; 25 | let mut reader = Pin::new(reader); 26 | 27 | let mut n = 0; 28 | 29 | loop { 30 | match reader.as_mut().poll_fill_buf(cx) { 31 | Poll::Pending => return Poll::Ready(Ok(n)), 32 | Poll::Ready(Err(e)) => return Poll::Ready(Err(e)), 33 | Poll::Ready(Ok(buf)) => { 34 | let len = buf.len(); 35 | n += len; 36 | reader.as_mut().consume(len); 37 | } 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /embassy/src/io/util/flush.rs: -------------------------------------------------------------------------------- 1 | use core::pin::Pin; 2 | use futures::future::Future; 3 | use futures::ready; 4 | use futures::task::{Context, Poll}; 5 | 6 | use super::super::error::Result; 7 | use super::super::traits::AsyncWrite; 8 | 9 | /// Future for the [`flush`](super::AsyncWriteExt::flush) method. 10 | #[derive(Debug)] 11 | #[must_use = "futures do nothing unless you `.await` or poll them"] 12 | pub struct Flush<'a, W: ?Sized> { 13 | writer: &'a mut W, 14 | } 15 | 16 | impl Unpin for Flush<'_, W> {} 17 | 18 | impl<'a, W: AsyncWrite + ?Sized + Unpin> Flush<'a, W> { 19 | pub(super) fn new(writer: &'a mut W) -> Self { 20 | Flush { writer } 21 | } 22 | } 23 | 24 | impl Future for Flush<'_, W> { 25 | type Output = Result<()>; 26 | 27 | fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { 28 | let this = &mut *self; 29 | let _ = ready!(Pin::new(&mut this.writer).poll_flush(cx))?; 30 | Poll::Ready(Ok(())) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /embassy/src/io/util/mod.rs: -------------------------------------------------------------------------------- 1 | use core::cmp::min; 2 | use core::pin::Pin; 3 | use core::task::{Context, Poll}; 4 | use futures::ready; 5 | 6 | mod read; 7 | pub use self::read::Read; 8 | 9 | mod read_buf; 10 | pub use self::read_buf::ReadBuf; 11 | 12 | mod read_byte; 13 | pub use self::read_byte::ReadByte; 14 | 15 | mod read_exact; 16 | pub use self::read_exact::ReadExact; 17 | 18 | mod read_while; 19 | pub use self::read_while::ReadWhile; 20 | 21 | mod read_to_end; 22 | pub use self::read_to_end::ReadToEnd; 23 | 24 | mod skip_while; 25 | pub use self::skip_while::SkipWhile; 26 | 27 | mod drain; 28 | pub use self::drain::Drain; 29 | 30 | mod flush; 31 | pub use self::flush::Flush; 32 | 33 | mod write; 34 | pub use self::write::Write; 35 | 36 | mod write_all; 37 | pub use self::write_all::WriteAll; 38 | 39 | mod write_byte; 40 | pub use self::write_byte::WriteByte; 41 | 42 | #[cfg(feature = "alloc")] 43 | mod split; 44 | #[cfg(feature = "alloc")] 45 | pub use self::split::{split, ReadHalf, WriteHalf}; 46 | 47 | mod copy_buf; 48 | pub use self::copy_buf::{copy_buf, CopyBuf}; 49 | 50 | use super::error::Result; 51 | use super::traits::{AsyncBufRead, AsyncWrite}; 52 | 53 | pub trait AsyncBufReadExt: AsyncBufRead { 54 | fn poll_read( 55 | mut self: Pin<&mut Self>, 56 | cx: &mut Context<'_>, 57 | buf: &mut [u8], 58 | ) -> Poll> 59 | where 60 | Self: Unpin, 61 | { 62 | let mut this = &mut *self; 63 | let rbuf = ready!(Pin::new(&mut this).poll_fill_buf(cx))?; 64 | let n = min(buf.len(), rbuf.len()); 65 | buf[..n].copy_from_slice(&rbuf[..n]); 66 | Pin::new(&mut this).consume(n); 67 | Poll::Ready(Ok(n)) 68 | } 69 | 70 | fn read_while<'a, F: Fn(u8) -> bool>( 71 | &'a mut self, 72 | buf: &'a mut [u8], 73 | f: F, 74 | ) -> ReadWhile<'a, Self, F> 75 | where 76 | Self: Unpin, 77 | { 78 | ReadWhile::new(self, f, buf) 79 | } 80 | 81 | fn skip_while bool>(&mut self, f: F) -> SkipWhile 82 | where 83 | Self: Unpin, 84 | { 85 | SkipWhile::new(self, f) 86 | } 87 | 88 | fn drain(&mut self) -> Drain 89 | where 90 | Self: Unpin, 91 | { 92 | Drain::new(self) 93 | } 94 | 95 | fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Read<'a, Self> 96 | where 97 | Self: Unpin, 98 | { 99 | Read::new(self, buf) 100 | } 101 | 102 | fn read_buf(&mut self) -> ReadBuf 103 | where 104 | Self: Unpin, 105 | { 106 | ReadBuf::new(self) 107 | } 108 | 109 | fn read_byte(&mut self) -> ReadByte 110 | where 111 | Self: Unpin, 112 | { 113 | ReadByte::new(self) 114 | } 115 | 116 | fn read_exact<'a>(&'a mut self, buf: &'a mut [u8]) -> ReadExact<'a, Self> 117 | where 118 | Self: Unpin, 119 | { 120 | ReadExact::new(self, buf) 121 | } 122 | 123 | fn read_to_end<'a>(&'a mut self, buf: &'a mut [u8]) -> ReadToEnd<'a, Self> 124 | where 125 | Self: Unpin, 126 | { 127 | ReadToEnd::new(self, buf) 128 | } 129 | } 130 | 131 | impl AsyncBufReadExt for R {} 132 | 133 | pub async fn read_line( 134 | r: &mut R, 135 | buf: &mut [u8], 136 | ) -> Result { 137 | r.skip_while(|b| b == b'\r' || b == b'\n').await?; 138 | let n = r.read_while(buf, |b| b != b'\r' && b != b'\n').await?; 139 | r.skip_while(|b| b == b'\r').await?; 140 | //assert_eq!(b'\n', r.read_byte().await?); 141 | r.read_byte().await?; 142 | Ok(n) 143 | } 144 | 145 | pub trait AsyncWriteExt: AsyncWrite { 146 | fn write_all<'a>(&'a mut self, buf: &'a [u8]) -> WriteAll<'a, Self> 147 | where 148 | Self: Unpin, 149 | { 150 | WriteAll::new(self, buf) 151 | } 152 | 153 | fn write_byte(&mut self, byte: u8) -> WriteByte 154 | where 155 | Self: Unpin, 156 | { 157 | WriteByte::new(self, byte) 158 | } 159 | 160 | fn write<'a>(&'a mut self, buf: &'a [u8]) -> Write<'a, Self> 161 | where 162 | Self: Unpin, 163 | { 164 | Write::new(self, buf) 165 | } 166 | 167 | /// Awaits until all bytes have actually been written, and 168 | /// not just enqueued as per the other "write" methods. 169 | fn flush<'a>(&mut self) -> Flush 170 | where 171 | Self: Unpin, 172 | { 173 | Flush::new(self) 174 | } 175 | } 176 | 177 | impl AsyncWriteExt for R {} 178 | -------------------------------------------------------------------------------- /embassy/src/io/util/read.rs: -------------------------------------------------------------------------------- 1 | use super::super::error::Result; 2 | use super::super::traits::AsyncBufRead; 3 | 4 | use core::cmp::min; 5 | 6 | use core::pin::Pin; 7 | use futures::future::Future; 8 | use futures::ready; 9 | use futures::task::{Context, Poll}; 10 | 11 | /// Future for the [`read_exact`](super::AsyncBufReadExt::read_exact) method. 12 | #[derive(Debug)] 13 | #[must_use = "futures do nothing unless you `.await` or poll them"] 14 | pub struct Read<'a, R: ?Sized> { 15 | reader: &'a mut R, 16 | buf: &'a mut [u8], 17 | } 18 | 19 | impl Unpin for Read<'_, R> {} 20 | 21 | impl<'a, R: AsyncBufRead + ?Sized + Unpin> Read<'a, R> { 22 | pub(super) fn new(reader: &'a mut R, buf: &'a mut [u8]) -> Self { 23 | Read { reader, buf } 24 | } 25 | } 26 | 27 | impl Future for Read<'_, R> { 28 | type Output = Result; 29 | 30 | fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { 31 | let this = &mut *self; 32 | let buf = ready!(Pin::new(&mut this.reader).poll_fill_buf(cx))?; 33 | 34 | let n = min(this.buf.len(), buf.len()); 35 | this.buf[..n].copy_from_slice(&buf[..n]); 36 | Pin::new(&mut this.reader).consume(n); 37 | Poll::Ready(Ok(n)) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /embassy/src/io/util/read_buf.rs: -------------------------------------------------------------------------------- 1 | use super::super::error::Result; 2 | use super::super::traits::AsyncBufRead; 3 | 4 | use core::pin::Pin; 5 | use futures::future::Future; 6 | use futures::ready; 7 | use futures::task::{Context, Poll}; 8 | 9 | pub struct ReadBuf<'a, R: ?Sized> { 10 | reader: Option<&'a mut R>, 11 | } 12 | 13 | impl Unpin for ReadBuf<'_, R> {} 14 | 15 | impl<'a, R: AsyncBufRead + ?Sized + Unpin> ReadBuf<'a, R> { 16 | pub(super) fn new(reader: &'a mut R) -> Self { 17 | ReadBuf { 18 | reader: Some(reader), 19 | } 20 | } 21 | } 22 | 23 | impl<'a, R: AsyncBufRead + ?Sized + Unpin> Future for ReadBuf<'a, R> { 24 | type Output = Result<&'a [u8]>; 25 | 26 | fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { 27 | let this = &mut *self; 28 | 29 | let buf = ready!(Pin::new(this.reader.as_mut().unwrap()).poll_fill_buf(cx))?; 30 | let buf: &'a [u8] = unsafe { core::mem::transmute(buf) }; 31 | this.reader = None; 32 | Poll::Ready(Ok(buf)) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /embassy/src/io/util/read_byte.rs: -------------------------------------------------------------------------------- 1 | use core::pin::Pin; 2 | use futures::future::Future; 3 | use futures::ready; 4 | use futures::task::{Context, Poll}; 5 | 6 | use super::super::error::{Error, Result}; 7 | use super::super::traits::AsyncBufRead; 8 | 9 | pub struct ReadByte<'a, R: ?Sized> { 10 | reader: &'a mut R, 11 | } 12 | 13 | impl Unpin for ReadByte<'_, R> {} 14 | 15 | impl<'a, R: AsyncBufRead + ?Sized + Unpin> ReadByte<'a, R> { 16 | pub(super) fn new(reader: &'a mut R) -> Self { 17 | Self { reader } 18 | } 19 | } 20 | 21 | impl<'a, R: AsyncBufRead + ?Sized + Unpin> Future for ReadByte<'a, R> { 22 | type Output = Result; 23 | 24 | fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { 25 | let Self { reader } = &mut *self; 26 | let mut reader = Pin::new(reader); 27 | let rbuf = ready!(reader.as_mut().poll_fill_buf(cx))?; 28 | if rbuf.is_empty() { 29 | return Poll::Ready(Err(Error::UnexpectedEof)); 30 | } 31 | 32 | let r = rbuf[0]; 33 | reader.as_mut().consume(1); 34 | Poll::Ready(Ok(r)) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /embassy/src/io/util/read_exact.rs: -------------------------------------------------------------------------------- 1 | use super::super::error::{Error, Result}; 2 | use super::super::traits::AsyncBufRead; 3 | 4 | use core::cmp::min; 5 | use core::mem; 6 | use core::pin::Pin; 7 | use futures::future::Future; 8 | use futures::ready; 9 | use futures::task::{Context, Poll}; 10 | 11 | /// Future for the [`read_exact`](super::AsyncBufReadExt::read_exact) method. 12 | #[derive(Debug)] 13 | #[must_use = "futures do nothing unless you `.await` or poll them"] 14 | pub struct ReadExact<'a, R: ?Sized> { 15 | reader: &'a mut R, 16 | buf: &'a mut [u8], 17 | } 18 | 19 | impl Unpin for ReadExact<'_, R> {} 20 | 21 | impl<'a, R: AsyncBufRead + ?Sized + Unpin> ReadExact<'a, R> { 22 | pub(super) fn new(reader: &'a mut R, buf: &'a mut [u8]) -> Self { 23 | ReadExact { reader, buf } 24 | } 25 | } 26 | 27 | impl Future for ReadExact<'_, R> { 28 | type Output = Result<()>; 29 | 30 | fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { 31 | let this = &mut *self; 32 | while !this.buf.is_empty() { 33 | let buf = ready!(Pin::new(&mut this.reader).poll_fill_buf(cx))?; 34 | if buf.is_empty() { 35 | return Poll::Ready(Err(Error::UnexpectedEof)); 36 | } 37 | 38 | let n = min(this.buf.len(), buf.len()); 39 | this.buf[..n].copy_from_slice(&buf[..n]); 40 | Pin::new(&mut this.reader).consume(n); 41 | { 42 | let (_, rest) = mem::take(&mut this.buf).split_at_mut(n); 43 | this.buf = rest; 44 | } 45 | } 46 | Poll::Ready(Ok(())) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /embassy/src/io/util/read_to_end.rs: -------------------------------------------------------------------------------- 1 | use core::cmp::min; 2 | use core::pin::Pin; 3 | use futures::future::Future; 4 | use futures::ready; 5 | use futures::task::{Context, Poll}; 6 | 7 | use super::super::error::{Error, Result}; 8 | use super::super::traits::AsyncBufRead; 9 | 10 | pub struct ReadToEnd<'a, R: ?Sized> { 11 | reader: &'a mut R, 12 | buf: &'a mut [u8], 13 | n: usize, 14 | } 15 | 16 | impl Unpin for ReadToEnd<'_, R> {} 17 | 18 | impl<'a, R: AsyncBufRead + ?Sized + Unpin> ReadToEnd<'a, R> { 19 | pub(super) fn new(reader: &'a mut R, buf: &'a mut [u8]) -> Self { 20 | Self { reader, buf, n: 0 } 21 | } 22 | } 23 | 24 | impl<'a, R: AsyncBufRead + ?Sized + Unpin> Future for ReadToEnd<'a, R> { 25 | type Output = Result; 26 | 27 | fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { 28 | let Self { reader, buf, n } = &mut *self; 29 | let mut reader = Pin::new(reader); 30 | loop { 31 | let rbuf = ready!(reader.as_mut().poll_fill_buf(cx))?; 32 | if rbuf.is_empty() { 33 | return Poll::Ready(Ok(*n)); 34 | } 35 | 36 | if *n == buf.len() { 37 | return Poll::Ready(Err(Error::Truncated)); 38 | } 39 | 40 | // truncate data if it doesn't fit in buf 41 | let p = min(rbuf.len(), buf.len() - *n); 42 | buf[*n..*n + p].copy_from_slice(&rbuf[..p]); 43 | *n += p; 44 | 45 | reader.as_mut().consume(p); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /embassy/src/io/util/read_while.rs: -------------------------------------------------------------------------------- 1 | use core::cmp::min; 2 | use core::pin::Pin; 3 | use futures::future::Future; 4 | use futures::ready; 5 | use futures::task::{Context, Poll}; 6 | 7 | use super::super::error::{Error, Result}; 8 | use super::super::traits::AsyncBufRead; 9 | 10 | pub struct ReadWhile<'a, R: ?Sized, F> { 11 | reader: &'a mut R, 12 | buf: &'a mut [u8], 13 | n: usize, 14 | f: F, 15 | } 16 | 17 | impl Unpin for ReadWhile<'_, R, F> {} 18 | 19 | impl<'a, R: AsyncBufRead + ?Sized + Unpin, F: Fn(u8) -> bool> ReadWhile<'a, R, F> { 20 | pub(super) fn new(reader: &'a mut R, f: F, buf: &'a mut [u8]) -> Self { 21 | Self { 22 | reader, 23 | f, 24 | buf, 25 | n: 0, 26 | } 27 | } 28 | } 29 | 30 | impl<'a, R: AsyncBufRead + ?Sized + Unpin, F: Fn(u8) -> bool> Future for ReadWhile<'a, R, F> { 31 | type Output = Result; 32 | 33 | fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { 34 | let Self { reader, f, buf, n } = &mut *self; 35 | let mut reader = Pin::new(reader); 36 | loop { 37 | let rbuf = ready!(reader.as_mut().poll_fill_buf(cx))?; 38 | if rbuf.is_empty() { 39 | return Poll::Ready(Err(Error::UnexpectedEof)); 40 | } 41 | 42 | let (p, done) = match rbuf.iter().position(|&b| !f(b)) { 43 | Some(p) => (p, true), 44 | None => (rbuf.len(), false), 45 | }; 46 | 47 | // truncate data if it doesn't fit in buf 48 | let p2 = min(p, buf.len() - *n); 49 | buf[*n..*n + p2].copy_from_slice(&rbuf[..p2]); 50 | *n += p2; 51 | 52 | // consume it all, even if it doesn't fit. 53 | // Otherwise we can deadlock because we never read to the ending char 54 | reader.as_mut().consume(p); 55 | 56 | if done { 57 | return Poll::Ready(Ok(*n)); 58 | } 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /embassy/src/io/util/skip_while.rs: -------------------------------------------------------------------------------- 1 | use core::iter::Iterator; 2 | use core::pin::Pin; 3 | use futures::future::Future; 4 | use futures::ready; 5 | use futures::task::{Context, Poll}; 6 | 7 | use super::super::error::{Error, Result}; 8 | use super::super::traits::AsyncBufRead; 9 | 10 | pub struct SkipWhile<'a, R: ?Sized, F> { 11 | reader: &'a mut R, 12 | f: F, 13 | } 14 | 15 | impl Unpin for SkipWhile<'_, R, F> {} 16 | 17 | impl<'a, R: AsyncBufRead + ?Sized + Unpin, F: Fn(u8) -> bool> SkipWhile<'a, R, F> { 18 | pub(super) fn new(reader: &'a mut R, f: F) -> Self { 19 | Self { reader, f } 20 | } 21 | } 22 | 23 | impl<'a, R: AsyncBufRead + ?Sized + Unpin, F: Fn(u8) -> bool> Future for SkipWhile<'a, R, F> { 24 | type Output = Result<()>; 25 | 26 | fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { 27 | let Self { reader, f } = &mut *self; 28 | let mut reader = Pin::new(reader); 29 | loop { 30 | let buf = ready!(reader.as_mut().poll_fill_buf(cx))?; 31 | if buf.is_empty() { 32 | return Poll::Ready(Err(Error::UnexpectedEof)); 33 | } 34 | 35 | let (p, done) = match buf.iter().position(|b| !f(*b)) { 36 | Some(p) => (p, true), 37 | None => (buf.len(), false), 38 | }; 39 | reader.as_mut().consume(p); 40 | if done { 41 | return Poll::Ready(Ok(())); 42 | } 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /embassy/src/io/util/split.rs: -------------------------------------------------------------------------------- 1 | use alloc::rc::Rc; 2 | use core::cell::UnsafeCell; 3 | use core::pin::Pin; 4 | use futures::task::{Context, Poll}; 5 | 6 | use super::super::error::Result; 7 | use super::super::traits::{AsyncBufRead, AsyncWrite}; 8 | 9 | /// The readable half of an object returned from `AsyncBufRead::split`. 10 | #[derive(Debug)] 11 | pub struct ReadHalf { 12 | handle: Rc>, 13 | } 14 | 15 | /// The writable half of an object returned from `AsyncBufRead::split`. 16 | #[derive(Debug)] 17 | pub struct WriteHalf { 18 | handle: Rc>, 19 | } 20 | 21 | impl AsyncBufRead for ReadHalf { 22 | fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { 23 | Pin::new(unsafe { &mut *self.handle.get() }).poll_fill_buf(cx) 24 | } 25 | 26 | fn consume(self: Pin<&mut Self>, amt: usize) { 27 | Pin::new(unsafe { &mut *self.handle.get() }).consume(amt) 28 | } 29 | } 30 | 31 | impl AsyncWrite for WriteHalf { 32 | fn poll_write(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) -> Poll> { 33 | Pin::new(unsafe { &mut *self.handle.get() }).poll_write(cx, buf) 34 | } 35 | fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { 36 | Pin::new(unsafe { &mut *self.handle.get() }).poll_flush(cx) 37 | } 38 | } 39 | 40 | pub fn split(t: T) -> (ReadHalf, WriteHalf) { 41 | let c = Rc::new(UnsafeCell::new(t)); 42 | (ReadHalf { handle: c.clone() }, WriteHalf { handle: c }) 43 | } 44 | -------------------------------------------------------------------------------- /embassy/src/io/util/write.rs: -------------------------------------------------------------------------------- 1 | use core::pin::Pin; 2 | use futures::future::Future; 3 | use futures::ready; 4 | use futures::task::{Context, Poll}; 5 | 6 | use super::super::error::Result; 7 | use super::super::traits::AsyncWrite; 8 | 9 | /// Future for the [`write_all`](super::AsyncWriteExt::write_all) method. 10 | #[derive(Debug)] 11 | #[must_use = "futures do nothing unless you `.await` or poll them"] 12 | pub struct Write<'a, W: ?Sized> { 13 | writer: &'a mut W, 14 | buf: &'a [u8], 15 | } 16 | 17 | impl Unpin for Write<'_, W> {} 18 | 19 | impl<'a, W: AsyncWrite + ?Sized + Unpin> Write<'a, W> { 20 | pub(super) fn new(writer: &'a mut W, buf: &'a [u8]) -> Self { 21 | Write { writer, buf } 22 | } 23 | } 24 | 25 | impl Future for Write<'_, W> { 26 | type Output = Result; 27 | 28 | fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { 29 | let this = &mut *self; 30 | let n = ready!(Pin::new(&mut this.writer).poll_write(cx, this.buf))?; 31 | Poll::Ready(Ok(n)) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /embassy/src/io/util/write_all.rs: -------------------------------------------------------------------------------- 1 | use core::mem; 2 | use core::pin::Pin; 3 | use futures::future::Future; 4 | use futures::ready; 5 | use futures::task::{Context, Poll}; 6 | 7 | use super::super::error::Result; 8 | use super::super::traits::AsyncWrite; 9 | 10 | /// Future for the [`write_all`](super::AsyncWriteExt::write_all) method. 11 | #[derive(Debug)] 12 | #[must_use = "futures do nothing unless you `.await` or poll them"] 13 | pub struct WriteAll<'a, W: ?Sized> { 14 | writer: &'a mut W, 15 | buf: &'a [u8], 16 | } 17 | 18 | impl Unpin for WriteAll<'_, W> {} 19 | 20 | impl<'a, W: AsyncWrite + ?Sized + Unpin> WriteAll<'a, W> { 21 | pub(super) fn new(writer: &'a mut W, buf: &'a [u8]) -> Self { 22 | WriteAll { writer, buf } 23 | } 24 | } 25 | 26 | impl Future for WriteAll<'_, W> { 27 | type Output = Result<()>; 28 | 29 | fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { 30 | let this = &mut *self; 31 | while !this.buf.is_empty() { 32 | let n = ready!(Pin::new(&mut this.writer).poll_write(cx, this.buf))?; 33 | { 34 | let (_, rest) = mem::take(&mut this.buf).split_at(n); 35 | this.buf = rest; 36 | } 37 | if n == 0 { 38 | panic!(); 39 | } 40 | } 41 | 42 | Poll::Ready(Ok(())) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /embassy/src/io/util/write_byte.rs: -------------------------------------------------------------------------------- 1 | use core::pin::Pin; 2 | use futures::future::Future; 3 | use futures::ready; 4 | use futures::task::{Context, Poll}; 5 | 6 | use super::super::error::Result; 7 | use super::super::traits::AsyncWrite; 8 | 9 | /// Future for the [`write_all`](super::AsyncWriteExt::write_all) method. 10 | #[derive(Debug)] 11 | #[must_use = "futures do nothing unless you `.await` or poll them"] 12 | pub struct WriteByte<'a, W: ?Sized> { 13 | writer: &'a mut W, 14 | byte: u8, 15 | } 16 | 17 | impl Unpin for WriteByte<'_, W> {} 18 | 19 | impl<'a, W: AsyncWrite + ?Sized + Unpin> WriteByte<'a, W> { 20 | pub(super) fn new(writer: &'a mut W, byte: u8) -> Self { 21 | WriteByte { writer, byte } 22 | } 23 | } 24 | 25 | impl Future for WriteByte<'_, W> { 26 | type Output = Result<()>; 27 | 28 | fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { 29 | let this = &mut *self; 30 | let buf = [this.byte; 1]; 31 | let n = ready!(Pin::new(&mut this.writer).poll_write(cx, &buf))?; 32 | if n == 0 { 33 | panic!(); 34 | } 35 | assert!(n == 1); 36 | 37 | Poll::Ready(Ok(())) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /embassy/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(not(any(feature = "std", feature = "wasm")), no_std)] 2 | #![cfg_attr( 3 | feature = "nightly", 4 | feature(generic_associated_types, type_alias_impl_trait) 5 | )] 6 | #![allow(clippy::new_without_default)] 7 | 8 | // This mod MUST go first, so that the others see its macros. 9 | pub(crate) mod fmt; 10 | 11 | pub mod blocking_mutex; 12 | pub mod channel; 13 | pub mod executor; 14 | 15 | #[cfg(target_arch="riscv32")] 16 | 17 | cfg_if::cfg_if! { 18 | if #[cfg(cortex_m)] { 19 | #[path="interrupt/interrupt.rs"] 20 | pub mod interrupt; 21 | } 22 | else if #[cfg(target_arch="riscv32")] { 23 | #[path="interrupt/interrupt_riscv32.rs"] 24 | pub mod interrupt; 25 | // pub use interrupt::*; 26 | 27 | } 28 | 29 | } 30 | 31 | 32 | // #[cfg(cortex_m)] 33 | // pub mod interrupt; 34 | pub mod io; 35 | pub mod mutex; 36 | #[cfg(feature = "time")] 37 | pub mod time; 38 | pub mod util; 39 | pub mod waitqueue; 40 | 41 | #[cfg(feature = "nightly")] 42 | pub use embassy_macros::{main, task}; 43 | 44 | #[doc(hidden)] 45 | /// Implementation details for embassy macros. DO NOT USE. 46 | pub mod export { 47 | pub use atomic_polyfill as atomic; 48 | } 49 | -------------------------------------------------------------------------------- /embassy/src/mutex.rs: -------------------------------------------------------------------------------- 1 | /// Async mutex. 2 | /// 3 | /// The mutex is generic over a blocking [`RawMutex`](crate::blocking_mutex::raw::RawMutex). 4 | /// The raw mutex is used to guard access to the internal "is locked" flag. It 5 | /// is held for very short periods only, while locking and unlocking. It is *not* held 6 | /// for the entire time the async Mutex is locked. 7 | use core::cell::{RefCell, UnsafeCell}; 8 | use core::ops::{Deref, DerefMut}; 9 | use core::task::Poll; 10 | use futures::future::poll_fn; 11 | 12 | use crate::blocking_mutex::raw::RawMutex; 13 | use crate::blocking_mutex::Mutex as BlockingMutex; 14 | use crate::waitqueue::WakerRegistration; 15 | 16 | /// Error returned by [`Mutex::try_lock`] 17 | #[derive(PartialEq, Eq, Clone, Copy, Debug)] 18 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] 19 | pub struct TryLockError; 20 | 21 | struct State { 22 | locked: bool, 23 | waker: WakerRegistration, 24 | } 25 | 26 | pub struct Mutex 27 | where 28 | M: RawMutex, 29 | T: ?Sized, 30 | { 31 | state: BlockingMutex>, 32 | inner: UnsafeCell, 33 | } 34 | 35 | unsafe impl Send for Mutex {} 36 | unsafe impl Sync for Mutex {} 37 | 38 | /// Async mutex. 39 | impl Mutex 40 | where 41 | M: RawMutex, 42 | { 43 | /// Create a new mutex with the given value. 44 | #[cfg(feature = "nightly")] 45 | pub const fn new(value: T) -> Self { 46 | Self { 47 | inner: UnsafeCell::new(value), 48 | state: BlockingMutex::new(RefCell::new(State { 49 | locked: false, 50 | waker: WakerRegistration::new(), 51 | })), 52 | } 53 | } 54 | 55 | /// Create a new mutex with the given value. 56 | #[cfg(not(feature = "nightly"))] 57 | pub fn new(value: T) -> Self { 58 | Self { 59 | inner: UnsafeCell::new(value), 60 | state: BlockingMutex::new(RefCell::new(State { 61 | locked: false, 62 | waker: WakerRegistration::new(), 63 | })), 64 | } 65 | } 66 | } 67 | 68 | impl Mutex 69 | where 70 | M: RawMutex, 71 | T: ?Sized, 72 | { 73 | /// Lock the mutex. 74 | /// 75 | /// This will wait for the mutex to be unlocked if it's already locked. 76 | pub async fn lock(&self) -> MutexGuard<'_, M, T> { 77 | poll_fn(|cx| { 78 | let ready = self.state.lock(|s| { 79 | let mut s = s.borrow_mut(); 80 | if s.locked { 81 | s.waker.register(cx.waker()); 82 | false 83 | } else { 84 | s.locked = true; 85 | true 86 | } 87 | }); 88 | 89 | if ready { 90 | Poll::Ready(MutexGuard { mutex: self }) 91 | } else { 92 | Poll::Pending 93 | } 94 | }) 95 | .await 96 | } 97 | 98 | /// Attempt to immediately lock the mutex. 99 | /// 100 | /// If the mutex is already locked, this will return an error instead of waiting. 101 | pub fn try_lock(&self) -> Result, TryLockError> { 102 | self.state.lock(|s| { 103 | let mut s = s.borrow_mut(); 104 | if s.locked { 105 | Err(TryLockError) 106 | } else { 107 | s.locked = true; 108 | Ok(()) 109 | } 110 | })?; 111 | 112 | Ok(MutexGuard { mutex: self }) 113 | } 114 | } 115 | 116 | /// Async mutex guard. 117 | /// 118 | /// Owning an instance of this type indicates having 119 | /// successfully locked the mutex, and grants access to the contents. 120 | /// 121 | /// Dropping it unlocks the mutex. 122 | pub struct MutexGuard<'a, M, T> 123 | where 124 | M: RawMutex, 125 | T: ?Sized, 126 | { 127 | mutex: &'a Mutex, 128 | } 129 | 130 | impl<'a, M, T> Drop for MutexGuard<'a, M, T> 131 | where 132 | M: RawMutex, 133 | T: ?Sized, 134 | { 135 | fn drop(&mut self) { 136 | self.mutex.state.lock(|s| { 137 | let mut s = s.borrow_mut(); 138 | s.locked = false; 139 | s.waker.wake(); 140 | }) 141 | } 142 | } 143 | 144 | impl<'a, M, T> Deref for MutexGuard<'a, M, T> 145 | where 146 | M: RawMutex, 147 | T: ?Sized, 148 | { 149 | type Target = T; 150 | fn deref(&self) -> &Self::Target { 151 | // Safety: the MutexGuard represents exclusive access to the contents 152 | // of the mutex, so it's OK to get it. 153 | unsafe { &*(self.mutex.inner.get() as *const T) } 154 | } 155 | } 156 | 157 | impl<'a, M, T> DerefMut for MutexGuard<'a, M, T> 158 | where 159 | M: RawMutex, 160 | T: ?Sized, 161 | { 162 | fn deref_mut(&mut self) -> &mut Self::Target { 163 | // Safety: the MutexGuard represents exclusive access to the contents 164 | // of the mutex, so it's OK to get it. 165 | unsafe { &mut *(self.mutex.inner.get()) } 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /embassy/src/time/delay.rs: -------------------------------------------------------------------------------- 1 | use super::{Duration, Instant}; 2 | 3 | /// Blocks for at least `duration`. 4 | pub fn block_for(duration: Duration) { 5 | let expires_at = Instant::now() + duration; 6 | while Instant::now() < expires_at {} 7 | } 8 | 9 | /// Type implementing async delays and blocking `embedded-hal` delays. 10 | /// 11 | /// The delays are implemented in a "best-effort" way, meaning that the cpu will block for at least 12 | /// the amount provided, but accuracy can be affected by many factors, including interrupt usage. 13 | /// Make sure to use a suitable tick rate for your use case. The tick rate is defined by the currently 14 | /// active driver. 15 | pub struct Delay; 16 | 17 | #[cfg(feature = "unstable-traits")] 18 | mod eh1 { 19 | use super::*; 20 | 21 | impl embedded_hal_1::delay::blocking::DelayUs for Delay { 22 | type Error = core::convert::Infallible; 23 | 24 | fn delay_us(&mut self, us: u32) -> Result<(), Self::Error> { 25 | Ok(block_for(Duration::from_micros(us as u64))) 26 | } 27 | 28 | fn delay_ms(&mut self, ms: u32) -> Result<(), Self::Error> { 29 | Ok(block_for(Duration::from_millis(ms as u64))) 30 | } 31 | } 32 | } 33 | 34 | cfg_if::cfg_if! { 35 | if #[cfg(all(feature = "unstable-traits", feature = "nightly"))] { 36 | use crate::time::Timer; 37 | use core::future::Future; 38 | use futures::FutureExt; 39 | 40 | impl embedded_hal_async::delay::DelayUs for Delay { 41 | type Error = core::convert::Infallible; 42 | 43 | type DelayUsFuture<'a> = impl Future> + 'a where Self: 'a; 44 | 45 | fn delay_us(&mut self, micros: u32) -> Self::DelayUsFuture<'_> { 46 | Timer::after(Duration::from_micros(micros as _)).map(Ok) 47 | } 48 | 49 | type DelayMsFuture<'a> = impl Future> + 'a where Self: 'a; 50 | 51 | fn delay_ms(&mut self, millis: u32) -> Self::DelayMsFuture<'_> { 52 | Timer::after(Duration::from_millis(millis as _)).map(Ok) 53 | } 54 | } 55 | } 56 | } 57 | 58 | mod eh02 { 59 | use super::*; 60 | use embedded_hal_02::blocking::delay::{DelayMs, DelayUs}; 61 | 62 | impl DelayMs for Delay { 63 | fn delay_ms(&mut self, ms: u8) { 64 | block_for(Duration::from_millis(ms as u64)) 65 | } 66 | } 67 | 68 | impl DelayMs for Delay { 69 | fn delay_ms(&mut self, ms: u16) { 70 | block_for(Duration::from_millis(ms as u64)) 71 | } 72 | } 73 | 74 | impl DelayMs for Delay { 75 | fn delay_ms(&mut self, ms: u32) { 76 | block_for(Duration::from_millis(ms as u64)) 77 | } 78 | } 79 | 80 | impl DelayUs for Delay { 81 | fn delay_us(&mut self, us: u8) { 82 | block_for(Duration::from_micros(us as u64)) 83 | } 84 | } 85 | 86 | impl DelayUs for Delay { 87 | fn delay_us(&mut self, us: u16) { 88 | block_for(Duration::from_micros(us as u64)) 89 | } 90 | } 91 | 92 | impl DelayUs for Delay { 93 | fn delay_us(&mut self, us: u32) { 94 | block_for(Duration::from_micros(us as u64)) 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /embassy/src/time/driver_wasm.rs: -------------------------------------------------------------------------------- 1 | use atomic_polyfill::{AtomicU8, Ordering}; 2 | use std::cell::UnsafeCell; 3 | use std::mem::MaybeUninit; 4 | use std::ptr; 5 | use std::sync::{Mutex, Once}; 6 | use wasm_bindgen::prelude::*; 7 | use wasm_timer::Instant as StdInstant; 8 | 9 | use crate::time::driver::{AlarmHandle, Driver}; 10 | 11 | const ALARM_COUNT: usize = 4; 12 | 13 | struct AlarmState { 14 | token: Option, 15 | closure: Option>, 16 | } 17 | 18 | unsafe impl Send for AlarmState {} 19 | 20 | impl AlarmState { 21 | const fn new() -> Self { 22 | Self { 23 | token: None, 24 | closure: None, 25 | } 26 | } 27 | } 28 | 29 | #[wasm_bindgen] 30 | extern "C" { 31 | fn setTimeout(closure: &Closure, millis: u32) -> f64; 32 | fn clearTimeout(token: f64); 33 | } 34 | 35 | struct TimeDriver { 36 | alarm_count: AtomicU8, 37 | 38 | once: Once, 39 | alarms: UninitCell>, 40 | zero_instant: UninitCell, 41 | } 42 | 43 | const ALARM_NEW: AlarmState = AlarmState::new(); 44 | crate::time_driver_impl!(static DRIVER: TimeDriver = TimeDriver { 45 | alarm_count: AtomicU8::new(0), 46 | once: Once::new(), 47 | alarms: UninitCell::uninit(), 48 | zero_instant: UninitCell::uninit(), 49 | }); 50 | 51 | impl TimeDriver { 52 | fn init(&self) { 53 | self.once.call_once(|| unsafe { 54 | self.alarms.write(Mutex::new([ALARM_NEW; ALARM_COUNT])); 55 | self.zero_instant.write(StdInstant::now()); 56 | }); 57 | } 58 | } 59 | 60 | impl Driver for TimeDriver { 61 | fn now(&self) -> u64 { 62 | self.init(); 63 | 64 | let zero = unsafe { self.zero_instant.read() }; 65 | StdInstant::now().duration_since(zero).as_micros() as u64 66 | } 67 | 68 | unsafe fn allocate_alarm(&self) -> Option { 69 | let id = self 70 | .alarm_count 71 | .fetch_update(Ordering::AcqRel, Ordering::Acquire, |x| { 72 | if x < ALARM_COUNT as u8 { 73 | Some(x + 1) 74 | } else { 75 | None 76 | } 77 | }); 78 | 79 | match id { 80 | Ok(id) => Some(AlarmHandle::new(id)), 81 | Err(_) => None, 82 | } 83 | } 84 | 85 | fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) { 86 | self.init(); 87 | let mut alarms = unsafe { self.alarms.as_ref() }.lock().unwrap(); 88 | let alarm = &mut alarms[alarm.id() as usize]; 89 | alarm.closure.replace(Closure::new(move || { 90 | callback(ctx); 91 | })); 92 | } 93 | 94 | fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) { 95 | self.init(); 96 | let mut alarms = unsafe { self.alarms.as_ref() }.lock().unwrap(); 97 | let alarm = &mut alarms[alarm.id() as usize]; 98 | let timeout = (timestamp - self.now()) as u32; 99 | if let Some(token) = alarm.token { 100 | clearTimeout(token); 101 | } 102 | alarm.token = Some(setTimeout(alarm.closure.as_ref().unwrap(), timeout / 1000)); 103 | } 104 | } 105 | 106 | pub(crate) struct UninitCell(MaybeUninit>); 107 | unsafe impl Send for UninitCell {} 108 | unsafe impl Sync for UninitCell {} 109 | 110 | impl UninitCell { 111 | pub const fn uninit() -> Self { 112 | Self(MaybeUninit::uninit()) 113 | } 114 | unsafe fn as_ptr(&self) -> *const T { 115 | (*self.0.as_ptr()).get() 116 | } 117 | 118 | pub unsafe fn as_mut_ptr(&self) -> *mut T { 119 | (*self.0.as_ptr()).get() 120 | } 121 | 122 | pub unsafe fn as_ref(&self) -> &T { 123 | &*self.as_ptr() 124 | } 125 | 126 | pub unsafe fn write(&self, val: T) { 127 | ptr::write(self.as_mut_ptr(), val) 128 | } 129 | } 130 | 131 | impl UninitCell { 132 | pub unsafe fn read(&self) -> T { 133 | ptr::read(self.as_mut_ptr()) 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /embassy/src/time/duration.rs: -------------------------------------------------------------------------------- 1 | use core::fmt; 2 | use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign}; 3 | 4 | use super::{GCD_1K, GCD_1M, TICKS_PER_SECOND}; 5 | 6 | #[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] 7 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] 8 | /// Represents the difference between two [Instant](struct.Instant.html)s 9 | pub struct Duration { 10 | pub(crate) ticks: u64, 11 | } 12 | 13 | impl Duration { 14 | /// The smallest value that can be represented by the `Duration` type. 15 | pub const MIN: Duration = Duration { ticks: u64::MIN }; 16 | /// The largest value that can be represented by the `Duration` type. 17 | pub const MAX: Duration = Duration { ticks: u64::MAX }; 18 | 19 | /// Tick count of the `Duration`. 20 | pub const fn as_ticks(&self) -> u64 { 21 | self.ticks 22 | } 23 | 24 | /// Convert the `Duration` to seconds, rounding down. 25 | pub const fn as_secs(&self) -> u64 { 26 | self.ticks / TICKS_PER_SECOND 27 | } 28 | 29 | /// Convert the `Duration` to milliseconds, rounding down. 30 | pub const fn as_millis(&self) -> u64 { 31 | self.ticks * (1000 / GCD_1K) / (TICKS_PER_SECOND / GCD_1K) 32 | } 33 | 34 | /// Convert the `Duration` to microseconds, rounding down. 35 | pub const fn as_micros(&self) -> u64 { 36 | self.ticks * (1_000_000 / GCD_1M) / (TICKS_PER_SECOND / GCD_1M) 37 | } 38 | 39 | /// Creates a duration from the specified number of clock ticks 40 | pub const fn from_ticks(ticks: u64) -> Duration { 41 | Duration { ticks } 42 | } 43 | 44 | /// Creates a duration from the specified number of seconds 45 | pub const fn from_secs(secs: u64) -> Duration { 46 | Duration { 47 | ticks: secs * TICKS_PER_SECOND, 48 | } 49 | } 50 | 51 | /// Creates a duration from the specified number of milliseconds 52 | pub const fn from_millis(millis: u64) -> Duration { 53 | Duration { 54 | ticks: millis * (TICKS_PER_SECOND / GCD_1K) / (1000 / GCD_1K), 55 | } 56 | } 57 | 58 | /// Creates a duration from the specified number of microseconds 59 | /// NOTE: Delays this small may be inaccurate. 60 | pub const fn from_micros(micros: u64) -> Duration { 61 | Duration { 62 | ticks: micros * (TICKS_PER_SECOND / GCD_1M) / (1_000_000 / GCD_1M), 63 | } 64 | } 65 | 66 | /// Adds one Duration to another, returning a new Duration or None in the event of an overflow. 67 | pub fn checked_add(self, rhs: Duration) -> Option { 68 | self.ticks 69 | .checked_add(rhs.ticks) 70 | .map(|ticks| Duration { ticks }) 71 | } 72 | 73 | /// Subtracts one Duration to another, returning a new Duration or None in the event of an overflow. 74 | pub fn checked_sub(self, rhs: Duration) -> Option { 75 | self.ticks 76 | .checked_sub(rhs.ticks) 77 | .map(|ticks| Duration { ticks }) 78 | } 79 | 80 | /// Multiplies one Duration by a scalar u32, returning a new Duration or None in the event of an overflow. 81 | pub fn checked_mul(self, rhs: u32) -> Option { 82 | self.ticks 83 | .checked_mul(rhs as _) 84 | .map(|ticks| Duration { ticks }) 85 | } 86 | 87 | /// Divides one Duration a scalar u32, returning a new Duration or None in the event of an overflow. 88 | pub fn checked_div(self, rhs: u32) -> Option { 89 | self.ticks 90 | .checked_div(rhs as _) 91 | .map(|ticks| Duration { ticks }) 92 | } 93 | } 94 | 95 | impl Add for Duration { 96 | type Output = Duration; 97 | 98 | fn add(self, rhs: Duration) -> Duration { 99 | self.checked_add(rhs) 100 | .expect("overflow when adding durations") 101 | } 102 | } 103 | 104 | impl AddAssign for Duration { 105 | fn add_assign(&mut self, rhs: Duration) { 106 | *self = *self + rhs; 107 | } 108 | } 109 | 110 | impl Sub for Duration { 111 | type Output = Duration; 112 | 113 | fn sub(self, rhs: Duration) -> Duration { 114 | self.checked_sub(rhs) 115 | .expect("overflow when subtracting durations") 116 | } 117 | } 118 | 119 | impl SubAssign for Duration { 120 | fn sub_assign(&mut self, rhs: Duration) { 121 | *self = *self - rhs; 122 | } 123 | } 124 | 125 | impl Mul for Duration { 126 | type Output = Duration; 127 | 128 | fn mul(self, rhs: u32) -> Duration { 129 | self.checked_mul(rhs) 130 | .expect("overflow when multiplying duration by scalar") 131 | } 132 | } 133 | 134 | impl Mul for u32 { 135 | type Output = Duration; 136 | 137 | fn mul(self, rhs: Duration) -> Duration { 138 | rhs * self 139 | } 140 | } 141 | 142 | impl MulAssign for Duration { 143 | fn mul_assign(&mut self, rhs: u32) { 144 | *self = *self * rhs; 145 | } 146 | } 147 | 148 | impl Div for Duration { 149 | type Output = Duration; 150 | 151 | fn div(self, rhs: u32) -> Duration { 152 | self.checked_div(rhs) 153 | .expect("divide by zero error when dividing duration by scalar") 154 | } 155 | } 156 | 157 | impl DivAssign for Duration { 158 | fn div_assign(&mut self, rhs: u32) { 159 | *self = *self / rhs; 160 | } 161 | } 162 | 163 | impl<'a> fmt::Display for Duration { 164 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 165 | write!(f, "{} ticks", self.ticks) 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /embassy/src/time/instant.rs: -------------------------------------------------------------------------------- 1 | use core::fmt; 2 | use core::ops::{Add, AddAssign, Sub, SubAssign}; 3 | 4 | use super::{driver, Duration, GCD_1K, GCD_1M, TICKS_PER_SECOND}; 5 | 6 | #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] 7 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] 8 | /// An Instant in time, based on the MCU's clock ticks since startup. 9 | pub struct Instant { 10 | ticks: u64, 11 | } 12 | 13 | impl Instant { 14 | /// The smallest (earliest) value that can be represented by the `Instant` type. 15 | pub const MIN: Instant = Instant { ticks: u64::MIN }; 16 | /// The largest (latest) value that can be represented by the `Instant` type. 17 | pub const MAX: Instant = Instant { ticks: u64::MAX }; 18 | 19 | /// Returns an Instant representing the current time. 20 | pub fn now() -> Instant { 21 | Instant { 22 | ticks: driver::now(), 23 | } 24 | } 25 | 26 | /// Create an Instant from a tick count since system boot. 27 | pub const fn from_ticks(ticks: u64) -> Self { 28 | Self { ticks } 29 | } 30 | 31 | /// Create an Instant from a microsecond count since system boot. 32 | pub const fn from_micros(micros: u64) -> Self { 33 | Self { 34 | ticks: micros * (TICKS_PER_SECOND / GCD_1M) / (1_000_000 / GCD_1M), 35 | } 36 | } 37 | 38 | /// Create an Instant from a millisecond count since system boot. 39 | pub const fn from_millis(millis: u64) -> Self { 40 | Self { 41 | ticks: millis * (TICKS_PER_SECOND / GCD_1K) / (1000 / GCD_1K), 42 | } 43 | } 44 | 45 | /// Create an Instant from a second count since system boot. 46 | pub const fn from_secs(seconds: u64) -> Self { 47 | Self { 48 | ticks: seconds * TICKS_PER_SECOND, 49 | } 50 | } 51 | 52 | /// Tick count since system boot. 53 | pub const fn as_ticks(&self) -> u64 { 54 | self.ticks 55 | } 56 | 57 | /// Seconds since system boot. 58 | pub const fn as_secs(&self) -> u64 { 59 | self.ticks / TICKS_PER_SECOND 60 | } 61 | 62 | /// Milliseconds since system boot. 63 | pub const fn as_millis(&self) -> u64 { 64 | self.ticks * (1000 / GCD_1K) / (TICKS_PER_SECOND / GCD_1K) 65 | } 66 | 67 | /// Microseconds since system boot. 68 | pub const fn as_micros(&self) -> u64 { 69 | self.ticks * (1_000_000 / GCD_1M) / (TICKS_PER_SECOND / GCD_1M) 70 | } 71 | 72 | /// Duration between this Instant and another Instant 73 | /// Panics on over/underflow. 74 | pub fn duration_since(&self, earlier: Instant) -> Duration { 75 | Duration { 76 | ticks: self.ticks.checked_sub(earlier.ticks).unwrap(), 77 | } 78 | } 79 | 80 | /// Duration between this Instant and another Instant 81 | pub fn checked_duration_since(&self, earlier: Instant) -> Option { 82 | if self.ticks < earlier.ticks { 83 | None 84 | } else { 85 | Some(Duration { 86 | ticks: self.ticks - earlier.ticks, 87 | }) 88 | } 89 | } 90 | 91 | /// Returns the duration since the "earlier" Instant. 92 | /// If the "earlier" instant is in the future, the duration is set to zero. 93 | pub fn saturating_duration_since(&self, earlier: Instant) -> Duration { 94 | Duration { 95 | ticks: if self.ticks < earlier.ticks { 96 | 0 97 | } else { 98 | self.ticks - earlier.ticks 99 | }, 100 | } 101 | } 102 | 103 | /// Duration elapsed since this Instant. 104 | pub fn elapsed(&self) -> Duration { 105 | Instant::now() - *self 106 | } 107 | 108 | /// Adds one Duration to self, returning a new `Instant` or None in the event of an overflow. 109 | pub fn checked_add(&self, duration: Duration) -> Option { 110 | self.ticks 111 | .checked_add(duration.ticks) 112 | .map(|ticks| Instant { ticks }) 113 | } 114 | 115 | /// Subtracts one Duration to self, returning a new `Instant` or None in the event of an overflow. 116 | pub fn checked_sub(&self, duration: Duration) -> Option { 117 | self.ticks 118 | .checked_sub(duration.ticks) 119 | .map(|ticks| Instant { ticks }) 120 | } 121 | } 122 | 123 | impl Add for Instant { 124 | type Output = Instant; 125 | 126 | fn add(self, other: Duration) -> Instant { 127 | self.checked_add(other) 128 | .expect("overflow when adding duration to instant") 129 | } 130 | } 131 | 132 | impl AddAssign for Instant { 133 | fn add_assign(&mut self, other: Duration) { 134 | *self = *self + other; 135 | } 136 | } 137 | 138 | impl Sub for Instant { 139 | type Output = Instant; 140 | 141 | fn sub(self, other: Duration) -> Instant { 142 | self.checked_sub(other) 143 | .expect("overflow when subtracting duration from instant") 144 | } 145 | } 146 | 147 | impl SubAssign for Instant { 148 | fn sub_assign(&mut self, other: Duration) { 149 | *self = *self - other; 150 | } 151 | } 152 | 153 | impl Sub for Instant { 154 | type Output = Duration; 155 | 156 | fn sub(self, other: Instant) -> Duration { 157 | self.duration_since(other) 158 | } 159 | } 160 | 161 | impl<'a> fmt::Display for Instant { 162 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 163 | write!(f, "{} ticks", self.ticks) 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /embassy/src/time/mod.rs: -------------------------------------------------------------------------------- 1 | //! Timekeeping, delays and timeouts. 2 | //! 3 | //! Timekeeping is done with elapsed time since system boot. Time is represented in 4 | //! ticks, where the tick rate is defined by the current driver, usually to match 5 | //! the tick rate of the hardware. 6 | //! 7 | //! Tick counts are 64 bits. At the highest supported tick rate of 1Mhz this supports 8 | //! representing time spans of up to ~584558 years, which is big enough for all practical 9 | //! purposes and allows not having to worry about overflows. 10 | //! 11 | //! [`Instant`] represents a given instant of time (relative to system boot), and [`Duration`] 12 | //! represents the duration of a span of time. They implement the math operations you'd expect, 13 | //! like addition and substraction. 14 | //! 15 | //! # Delays and timeouts 16 | //! 17 | //! [`Timer`] allows performing async delays. [`Ticker`] allows periodic delays without drifting over time. 18 | //! 19 | //! An implementation of the `embedded-hal` delay traits is provided by [`Delay`], for compatibility 20 | //! with libraries from the ecosystem. 21 | //! 22 | //! # Wall-clock time 23 | //! 24 | //! The `time` module deals exclusively with a monotonically increasing tick count. 25 | //! Therefore it has no direct support for wall-clock time ("real life" datetimes 26 | //! like `2021-08-24 13:33:21`). 27 | //! 28 | //! If persistence across reboots is not needed, support can be built on top of 29 | //! `embassy::time` by storing the offset between "seconds elapsed since boot" 30 | //! and "seconds since unix epoch". 31 | //! 32 | //! # Time driver 33 | //! 34 | //! The `time` module is backed by a global "time driver" specified at build time. 35 | //! Only one driver can be active in a program. 36 | //! 37 | //! All methods and structs transparently call into the active driver. This makes it 38 | //! possible for libraries to use `embassy::time` in a driver-agnostic way without 39 | //! requiring generic parameters. 40 | //! 41 | //! For more details, check the [`driver`] module. 42 | 43 | #![deny(missing_docs)] 44 | 45 | mod delay; 46 | pub mod driver; 47 | mod duration; 48 | mod instant; 49 | mod timer; 50 | 51 | #[cfg(feature = "std")] 52 | mod driver_std; 53 | 54 | #[cfg(feature = "wasm")] 55 | mod driver_wasm; 56 | 57 | pub use delay::{block_for, Delay}; 58 | pub use duration::Duration; 59 | pub use instant::Instant; 60 | pub use timer::{with_timeout, Ticker, TimeoutError, Timer}; 61 | 62 | #[cfg(feature = "time-tick-1000hz")] 63 | const TPS: u64 = 1_000; 64 | 65 | #[cfg(feature = "time-tick-32768hz")] 66 | const TPS: u64 = 32_768; 67 | 68 | #[cfg(feature = "time-tick-1mhz")] 69 | const TPS: u64 = 1_000_000; 70 | //TODO add ticks per second for esp32c3 71 | 72 | #[cfg(feature= "time-tick-16mhz")] 73 | const TPS: u64 = 16_000_000; 74 | 75 | /// Ticks per second of the global timebase. 76 | /// 77 | /// This value is specified by the `time-tick-*` Cargo features, which 78 | /// should be set by the time driver. Some drivers support a fixed tick rate, others 79 | /// allow you to choose a tick rate with Cargo features of their own. You should not 80 | /// set the `time-tick-*` features for embassy yourself as an end user. 81 | pub const TICKS_PER_SECOND: u64 = TPS; 82 | 83 | const fn gcd(a: u64, b: u64) -> u64 { 84 | if b == 0 { 85 | a 86 | } else { 87 | gcd(b, a % b) 88 | } 89 | } 90 | 91 | pub(crate) const GCD_1K: u64 = gcd(TICKS_PER_SECOND, 1_000); 92 | pub(crate) const GCD_1M: u64 = gcd(TICKS_PER_SECOND, 1_000_000); 93 | -------------------------------------------------------------------------------- /embassy/src/time/timer.rs: -------------------------------------------------------------------------------- 1 | use core::future::Future; 2 | use core::pin::Pin; 3 | use core::task::{Context, Poll}; 4 | use futures::{future::select, future::Either, pin_mut, Stream}; 5 | 6 | use crate::executor::raw; 7 | use crate::time::{Duration, Instant}; 8 | 9 | /// Error returned by [`with_timeout`] on timeout. 10 | pub struct TimeoutError; 11 | 12 | /// Runs a given future with a timeout. 13 | /// 14 | /// If the future completes before the timeout, its output is returned. Otherwise, on timeout, 15 | /// work on the future is stopped (`poll` is no longer called), the future is dropped and `Err(TimeoutError)` is returned. 16 | pub async fn with_timeout(timeout: Duration, fut: F) -> Result { 17 | let timeout_fut = Timer::after(timeout); 18 | pin_mut!(fut); 19 | match select(fut, timeout_fut).await { 20 | Either::Left((r, _)) => Ok(r), 21 | Either::Right(_) => Err(TimeoutError), 22 | } 23 | } 24 | 25 | /// A future that completes at a specified [Instant](struct.Instant.html). 26 | pub struct Timer { 27 | expires_at: Instant, 28 | yielded_once: bool, 29 | } 30 | 31 | impl Timer { 32 | /// Expire at specified [Instant](struct.Instant.html) 33 | pub fn at(expires_at: Instant) -> Self { 34 | Self { 35 | expires_at, 36 | yielded_once: false, 37 | } 38 | } 39 | 40 | /// Expire after specified [Duration](struct.Duration.html). 41 | /// This can be used as a `sleep` abstraction. 42 | /// 43 | /// Example: 44 | /// ``` no_run 45 | /// # #![feature(type_alias_impl_trait)] 46 | /// # 47 | /// # fn foo() {} 48 | /// use embassy::time::{Duration, Timer}; 49 | /// 50 | /// #[embassy::task] 51 | /// async fn demo_sleep_seconds() { 52 | /// // suspend this task for one second. 53 | /// Timer::after(Duration::from_secs(1)).await; 54 | /// } 55 | /// ``` 56 | pub fn after(duration: Duration) -> Self { 57 | Self { 58 | expires_at: Instant::now() + duration, 59 | yielded_once: false, 60 | } 61 | } 62 | } 63 | 64 | impl Unpin for Timer {} 65 | 66 | impl Future for Timer { 67 | type Output = (); 68 | fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { 69 | if self.yielded_once && self.expires_at <= Instant::now() { 70 | Poll::Ready(()) 71 | } else { 72 | unsafe { raw::register_timer(self.expires_at, cx.waker()) }; 73 | self.yielded_once = true; 74 | Poll::Pending 75 | } 76 | } 77 | } 78 | 79 | /// Asynchronous stream that yields every Duration, indefinitely. 80 | /// 81 | /// This stream will tick at uniform intervals, even if blocking work is performed between ticks. 82 | /// 83 | /// For instance, consider the following code fragment. 84 | /// ``` no_run 85 | /// # #![feature(type_alias_impl_trait)] 86 | /// # 87 | /// use embassy::time::{Duration, Timer}; 88 | /// # fn foo() {} 89 | /// 90 | /// #[embassy::task] 91 | /// async fn ticker_example_0() { 92 | /// loop { 93 | /// foo(); 94 | /// Timer::after(Duration::from_secs(1)).await; 95 | /// } 96 | /// } 97 | /// ``` 98 | /// 99 | /// This fragment will not call `foo` every second. 100 | /// Instead, it will call it every second + the time it took to previously call `foo`. 101 | /// 102 | /// Example using ticker, which will consistently call `foo` once a second. 103 | /// 104 | /// ``` no_run 105 | /// # #![feature(type_alias_impl_trait)] 106 | /// # 107 | /// use embassy::time::{Duration, Ticker}; 108 | /// use futures::StreamExt; 109 | /// # fn foo(){} 110 | /// 111 | /// #[embassy::task] 112 | /// async fn ticker_example_1() { 113 | /// let mut ticker = Ticker::every(Duration::from_secs(1)); 114 | /// loop { 115 | /// foo(); 116 | /// ticker.next().await; 117 | /// } 118 | /// } 119 | /// ``` 120 | pub struct Ticker { 121 | expires_at: Instant, 122 | duration: Duration, 123 | } 124 | 125 | impl Ticker { 126 | /// Creates a new ticker that ticks at the specified duration interval. 127 | pub fn every(duration: Duration) -> Self { 128 | let expires_at = Instant::now() + duration; 129 | Self { 130 | expires_at, 131 | duration, 132 | } 133 | } 134 | } 135 | 136 | impl Unpin for Ticker {} 137 | 138 | impl Stream for Ticker { 139 | type Item = (); 140 | fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { 141 | if self.expires_at <= Instant::now() { 142 | let dur = self.duration; 143 | self.expires_at += dur; 144 | Poll::Ready(Some(())) 145 | } else { 146 | unsafe { raw::register_timer(self.expires_at, cx.waker()) }; 147 | Poll::Pending 148 | } 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /embassy/src/util/forever.rs: -------------------------------------------------------------------------------- 1 | use core::cell::UnsafeCell; 2 | use core::mem::MaybeUninit; 3 | 4 | use atomic_polyfill::{AtomicBool, Ordering}; 5 | 6 | /// Type with static lifetime that may be written to once at runtime. 7 | /// 8 | /// This may be used to initialize static objects at runtime, typically in the init routine. 9 | /// This is useful for objects such as Embassy's RTC, which cannot be initialized in a const 10 | /// context. 11 | /// 12 | /// Note: IF a global mutable variable is desired, use a CriticalSectionMutex or ThreadModeMutex instead. 13 | /// 14 | /// ``` 15 | /// use embassy::util::Forever; 16 | /// // Using an integer for the sake of keeping this example self-contained, 17 | /// // see https://github.com/embassy-rs/embassy/wiki/Getting-Started for a more "proper" example. 18 | /// static SOME_INT: Forever =Forever::new(); 19 | /// 20 | /// // put returns a mutable pointer to the object stored in the forever, which may then be passed 21 | /// // around. 22 | /// let mut x = SOME_INT.put(42); 23 | /// assert_eq!(*x, 42); 24 | /// ``` 25 | pub struct Forever { 26 | used: AtomicBool, 27 | t: UnsafeCell>, 28 | } 29 | 30 | unsafe impl Send for Forever {} 31 | unsafe impl Sync for Forever {} 32 | 33 | impl Forever { 34 | #[inline(always)] 35 | pub const fn new() -> Self { 36 | Self { 37 | used: AtomicBool::new(false), 38 | t: UnsafeCell::new(MaybeUninit::uninit()), 39 | } 40 | } 41 | 42 | /// Gives this `Forever` a value. 43 | /// 44 | /// Panics if this `Forever` already has a value. 45 | /// 46 | /// Returns a mutable reference to the stored value. 47 | #[inline(always)] 48 | #[allow(clippy::mut_from_ref)] 49 | pub fn put(&'static self, val: T) -> &'static mut T { 50 | if self 51 | .used 52 | .compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed) 53 | .is_err() 54 | { 55 | panic!("Forever.put() called multiple times"); 56 | } 57 | 58 | unsafe { 59 | let p = self.t.get(); 60 | let p = (&mut *p).as_mut_ptr(); 61 | p.write(val); 62 | &mut *p 63 | } 64 | } 65 | 66 | #[inline(always)] 67 | #[allow(clippy::mut_from_ref)] 68 | pub fn put_with(&'static self, val: impl FnOnce() -> T) -> &'static mut T { 69 | if self 70 | .used 71 | .compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed) 72 | .is_err() 73 | { 74 | panic!("Forever.put() called multiple times"); 75 | } 76 | 77 | unsafe { 78 | let p = self.t.get(); 79 | let p = (&mut *p).as_mut_ptr(); 80 | p.write(val()); 81 | &mut *p 82 | } 83 | } 84 | 85 | #[inline(always)] 86 | #[allow(clippy::mut_from_ref)] 87 | pub unsafe fn steal(&'static self) -> &'static mut T { 88 | let p = self.t.get(); 89 | let p = (&mut *p).as_mut_ptr(); 90 | &mut *p 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /embassy/src/util/mod.rs: -------------------------------------------------------------------------------- 1 | //! Misc utilities 2 | 3 | mod forever; 4 | mod select_all; 5 | mod yield_now; 6 | 7 | pub use forever::*; 8 | pub use select_all::*; 9 | pub use yield_now::*; 10 | 11 | /// Unsafely unborrow an owned singleton out of a `&mut`. 12 | /// 13 | /// It is intended to be implemented for owned peripheral singletons, such as `USART3` or `AnyPin`. 14 | /// Unborrowing an owned `T` yields the same `T`. Unborrowing a `&mut T` yields a copy of the T. 15 | /// 16 | /// This allows writing HAL drivers that either own or borrow their peripherals, but that don't have 17 | /// to store pointers in the borrowed case. 18 | /// 19 | /// Safety: this trait can be used to copy non-Copy types. Implementors must not cause 20 | /// immediate UB when copied, and must not cause UB when copies are later used, provided they 21 | /// are only used according the [`Self::unborrow`] safety contract. 22 | /// 23 | pub unsafe trait Unborrow { 24 | /// Unborrow result type 25 | type Target; 26 | 27 | /// Unborrow a value. 28 | /// 29 | /// Safety: This returns a copy of a singleton that's normally not 30 | /// copiable. The returned copy must ONLY be used while the lifetime of `self` is 31 | /// valid, as if it were accessed through `self` every time. 32 | unsafe fn unborrow(self) -> Self::Target; 33 | } 34 | 35 | unsafe impl<'a, T: Unborrow> Unborrow for &'a mut T { 36 | type Target = T::Target; 37 | unsafe fn unborrow(self) -> Self::Target { 38 | T::unborrow(core::ptr::read(self)) 39 | } 40 | } 41 | 42 | pub trait Steal { 43 | unsafe fn steal() -> Self; 44 | } 45 | 46 | macro_rules! unsafe_impl_unborrow_tuples { 47 | ($($t:ident),+) => { 48 | unsafe impl<$($t),+> Unborrow for ($($t),+) 49 | where 50 | $( 51 | $t: Unborrow 52 | ),+ 53 | { 54 | type Target = ($($t),+); 55 | unsafe fn unborrow(self) -> Self::Target { 56 | self 57 | } 58 | } 59 | 60 | 61 | }; 62 | } 63 | 64 | unsafe_impl_unborrow_tuples!(A, B); 65 | unsafe_impl_unborrow_tuples!(A, B, C); 66 | unsafe_impl_unborrow_tuples!(A, B, C, D); 67 | unsafe_impl_unborrow_tuples!(A, B, C, D, E); 68 | unsafe_impl_unborrow_tuples!(A, B, C, D, E, F); 69 | unsafe_impl_unborrow_tuples!(A, B, C, D, E, F, G); 70 | unsafe_impl_unborrow_tuples!(A, B, C, D, E, F, G, H); 71 | unsafe_impl_unborrow_tuples!(A, B, C, D, E, F, G, H, I); 72 | unsafe_impl_unborrow_tuples!(A, B, C, D, E, F, G, H, I, J); 73 | unsafe_impl_unborrow_tuples!(A, B, C, D, E, F, G, H, I, J, K); 74 | unsafe_impl_unborrow_tuples!(A, B, C, D, E, F, G, H, I, J, K, L); 75 | -------------------------------------------------------------------------------- /embassy/src/util/select_all.rs: -------------------------------------------------------------------------------- 1 | use core::future::Future; 2 | use core::pin::Pin; 3 | use core::task::{Context, Poll}; 4 | 5 | /// Future for the [`select_all`] function. 6 | #[derive(Debug)] 7 | #[must_use = "futures do nothing unless you `.await` or poll them"] 8 | pub struct SelectAll { 9 | inner: [Fut; N], 10 | } 11 | 12 | impl Unpin for SelectAll {} 13 | 14 | /// Creates a new future which will select over a list of futures. 15 | /// 16 | /// The returned future will wait for any future within `iter` to be ready. Upon 17 | /// completion the item resolved will be returned, along with the index of the 18 | /// future that was ready. 19 | /// 20 | /// # Panics 21 | /// 22 | /// This function will panic if the array specified contains no items. 23 | pub fn select_all(arr: [Fut; N]) -> SelectAll { 24 | assert!(N > 0); 25 | SelectAll { inner: arr } 26 | } 27 | 28 | impl SelectAll { 29 | /// Consumes this combinator, returning the underlying futures. 30 | pub fn into_inner(self) -> [Fut; N] { 31 | self.inner 32 | } 33 | } 34 | 35 | impl Future for SelectAll { 36 | type Output = (Fut::Output, usize); 37 | 38 | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { 39 | // Safety: Since `self` is pinned, `inner` cannot move. Since `inner` cannot move, 40 | // its elements also cannot move. Therefore it is safe to access `inner` and pin 41 | // references to the contained futures. 42 | let item = unsafe { 43 | self.get_unchecked_mut() 44 | .inner 45 | .iter_mut() 46 | .enumerate() 47 | .find_map(|(i, f)| match Pin::new_unchecked(f).poll(cx) { 48 | Poll::Pending => None, 49 | Poll::Ready(e) => Some((i, e)), 50 | }) 51 | }; 52 | 53 | match item { 54 | Some((idx, res)) => Poll::Ready((res, idx)), 55 | None => Poll::Pending, 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /embassy/src/util/yield_now.rs: -------------------------------------------------------------------------------- 1 | use core::future::Future; 2 | use core::pin::Pin; 3 | use core::task::{Context, Poll}; 4 | 5 | /// Yield from the current task once, allowing other tasks to run. 6 | pub fn yield_now() -> impl Future { 7 | YieldNowFuture { yielded: false } 8 | } 9 | 10 | struct YieldNowFuture { 11 | yielded: bool, 12 | } 13 | 14 | impl Future for YieldNowFuture { 15 | type Output = (); 16 | fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { 17 | if self.yielded { 18 | Poll::Ready(()) 19 | } else { 20 | self.yielded = true; 21 | cx.waker().wake_by_ref(); 22 | Poll::Pending 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /embassy/src/waitqueue/mod.rs: -------------------------------------------------------------------------------- 1 | //! Async low-level wait queues 2 | 3 | #[cfg_attr(feature = "executor-agnostic", path = "waker_agnostic.rs")] 4 | mod waker; 5 | pub use waker::*; 6 | -------------------------------------------------------------------------------- /embassy/src/waitqueue/waker.rs: -------------------------------------------------------------------------------- 1 | use atomic_polyfill::{compiler_fence, AtomicPtr, Ordering}; 2 | use core::ptr::{self, NonNull}; 3 | use core::task::Waker; 4 | 5 | use crate::executor::raw::{task_from_waker, wake_task, TaskHeader}; 6 | 7 | /// Utility struct to register and wake a waker. 8 | #[derive(Debug)] 9 | pub struct WakerRegistration { 10 | waker: Option>, 11 | } 12 | 13 | impl WakerRegistration { 14 | pub const fn new() -> Self { 15 | Self { waker: None } 16 | } 17 | 18 | /// Register a waker. Overwrites the previous waker, if any. 19 | pub fn register(&mut self, w: &Waker) { 20 | let w = unsafe { task_from_waker(w) }; 21 | match self.waker { 22 | // Optimization: If both the old and new Wakers wake the same task, do nothing. 23 | Some(w2) if w == w2 => {} 24 | Some(w2) => { 25 | // We had a waker registered for another task. Wake it, so the other task can 26 | // reregister itself if it's still interested. 27 | // 28 | // If two tasks are waiting on the same thing concurrently, this will cause them 29 | // to wake each other in a loop fighting over this WakerRegistration. This wastes 30 | // CPU but things will still work. 31 | // 32 | // If the user wants to have two tasks waiting on the same thing they should use 33 | // a more appropriate primitive that can store multiple wakers. 34 | 35 | unsafe { wake_task(w2) } 36 | self.waker = Some(w); 37 | } 38 | None => self.waker = Some(w), 39 | } 40 | } 41 | 42 | /// Wake the registered waker, if any. 43 | pub fn wake(&mut self) { 44 | if let Some(w) = self.waker.take() { 45 | unsafe { wake_task(w) } 46 | } 47 | } 48 | } 49 | 50 | // SAFETY: `WakerRegistration` effectively contains an `Option`, 51 | // which is `Send` and `Sync`. 52 | unsafe impl Send for WakerRegistration {} 53 | unsafe impl Sync for WakerRegistration {} 54 | 55 | pub struct AtomicWaker { 56 | waker: AtomicPtr, 57 | } 58 | 59 | impl AtomicWaker { 60 | pub const fn new() -> Self { 61 | Self { 62 | waker: AtomicPtr::new(ptr::null_mut()), 63 | } 64 | } 65 | 66 | /// Register a waker. Overwrites the previous waker, if any. 67 | pub fn register(&self, w: &Waker) { 68 | let w = unsafe { task_from_waker(w) }; 69 | self.waker.store(w.as_ptr(), Ordering::Relaxed); 70 | compiler_fence(Ordering::SeqCst); 71 | } 72 | 73 | /// Wake the registered waker, if any. 74 | pub fn wake(&self) { 75 | let w2 = self.waker.load(Ordering::Relaxed); 76 | if let Some(w2) = NonNull::new(w2) { 77 | unsafe { wake_task(w2) }; 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /embassy/src/waitqueue/waker_agnostic.rs: -------------------------------------------------------------------------------- 1 | use core::cell::Cell; 2 | use core::mem; 3 | use core::task::Waker; 4 | 5 | use crate::blocking_mutex::raw::CriticalSectionRawMutex; 6 | use crate::blocking_mutex::Mutex; 7 | 8 | /// Utility struct to register and wake a waker. 9 | #[derive(Debug)] 10 | pub struct WakerRegistration { 11 | waker: Option, 12 | } 13 | 14 | impl WakerRegistration { 15 | pub const fn new() -> Self { 16 | Self { waker: None } 17 | } 18 | 19 | /// Register a waker. Overwrites the previous waker, if any. 20 | pub fn register(&mut self, w: &Waker) { 21 | match self.waker { 22 | // Optimization: If both the old and new Wakers wake the same task, we can simply 23 | // keep the old waker, skipping the clone. (In most executor implementations, 24 | // cloning a waker is somewhat expensive, comparable to cloning an Arc). 25 | Some(ref w2) if (w2.will_wake(w)) => {} 26 | _ => { 27 | // clone the new waker and store it 28 | if let Some(old_waker) = mem::replace(&mut self.waker, Some(w.clone())) { 29 | // We had a waker registered for another task. Wake it, so the other task can 30 | // reregister itself if it's still interested. 31 | // 32 | // If two tasks are waiting on the same thing concurrently, this will cause them 33 | // to wake each other in a loop fighting over this WakerRegistration. This wastes 34 | // CPU but things will still work. 35 | // 36 | // If the user wants to have two tasks waiting on the same thing they should use 37 | // a more appropriate primitive that can store multiple wakers. 38 | old_waker.wake() 39 | } 40 | } 41 | } 42 | } 43 | 44 | /// Wake the registered waker, if any. 45 | pub fn wake(&mut self) { 46 | if let Some(w) = self.waker.take() { 47 | w.wake() 48 | } 49 | } 50 | } 51 | 52 | /// Utility struct to register and wake a waker. 53 | pub struct AtomicWaker { 54 | waker: Mutex>>, 55 | } 56 | 57 | impl AtomicWaker { 58 | pub const fn new() -> Self { 59 | Self { 60 | waker: Mutex::const_new(CriticalSectionRawMutex::new(), Cell::new(None)), 61 | } 62 | } 63 | 64 | /// Register a waker. Overwrites the previous waker, if any. 65 | pub fn register(&self, w: &Waker) { 66 | critical_section::with(|cs| { 67 | let cell = self.waker.borrow(cs); 68 | cell.set(match cell.replace(None) { 69 | Some(w2) if (w2.will_wake(w)) => Some(w2), 70 | _ => Some(w.clone()), 71 | }) 72 | }) 73 | } 74 | 75 | /// Wake the registered waker, if any. 76 | pub fn wake(&self) { 77 | critical_section::with(|cs| { 78 | let cell = self.waker.borrow(cs); 79 | if let Some(w) = cell.replace(None) { 80 | w.wake_by_ref(); 81 | cell.set(Some(w)); 82 | } 83 | }) 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /examples/async_join/.cargo/config.save.toml: -------------------------------------------------------------------------------- 1 | [target.riscv32imc-unknown-none-elf] 2 | runner = "espflash --format=direct-boot --monitor" 3 | 4 | [build] 5 | rustflags = [ 6 | "-C", "link-arg=-Tlinkall.x", 7 | ] 8 | target = "riscv32imc-unknown-none-elf" 9 | 10 | [unstable] 11 | build-std = ["core"] 12 | -------------------------------------------------------------------------------- /examples/async_join/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [target.'cfg(all(target_arch = "riscv32", target_os = "none"))'] 2 | runner = "espflash --format=direct-boot --monitor" 3 | 4 | [build] 5 | rustflags = [ 6 | "-C", "link-arg=-Tlinkall.x", 7 | ] 8 | target = "riscv32imc-unknown-none-elf" 9 | 10 | [unstable] 11 | build-std = ["core"] 12 | -------------------------------------------------------------------------------- /examples/async_join/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | debug/ 4 | target/ 5 | 6 | # These are backup files generated by rustfmt 7 | **/*.rs.bk 8 | 9 | # MSVC Windows builds of rustc generate these, which store debugging information 10 | *.pdb 11 | -------------------------------------------------------------------------------- /examples/async_join/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "rust-analyzer.checkOnSave.allTargets": false, 3 | } 4 | -------------------------------------------------------------------------------- /examples/async_join/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "async_join" 3 | version = "0.1.0" 4 | authors = ["osobiehl "] 5 | edition = "2021" 6 | license = "MIT OR Apache-2.0" 7 | 8 | 9 | 10 | 11 | [dependencies] 12 | panic-halt = "0.2" 13 | esp32c3-hal = { package = "esp32c3-hal", git = "https://github.com/esp-rs/esp-hal.git" } 14 | riscv-rt = { version = "0.8", optional = true } 15 | nb = "1.0.0" 16 | embassy = { path = "../../embassy", features = [ "time", "time-tick-16mhz", "nightly"] } 17 | futures = { version = "0.3.17", default-features = false, features = ["async-await"] } 18 | heapless = { version = "0.7.5", default-features = false } 19 | embassy_esp32c3 = {path="../../embassy-esp32c3"} 20 | #embassy-macros = { version = "0.1.0", path = "../../embassy-macros", features=["esp32c3"]} 21 | serde = { version = "1.0.136", default-features = false } 22 | embedded-hal = { version = "0.2", features = ["unproven"] } 23 | critical-section = "0.2.5" 24 | 25 | defmt = "0.3" 26 | #defmt-rtt = "0.3" 27 | 28 | [features] 29 | default = ["nightly"] 30 | nightly = [ "riscv-rt" ] 31 | rt = ["riscv-rt"] 32 | -------------------------------------------------------------------------------- /examples/async_join/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright [year] [fullname] 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /examples/async_join/README.md: -------------------------------------------------------------------------------- 1 | # run 2 | `cargo espflash --monitor` 3 | 4 | 5 | -------------------------------------------------------------------------------- /examples/async_join/rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "nightly" 3 | -------------------------------------------------------------------------------- /examples/async_join/src/main.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | #![feature(type_alias_impl_trait)] 4 | 5 | 6 | use core::fmt::Write; 7 | 8 | // use esp32c3_hal::{pac::{Peripherals, LEDC, apb_ctrl::peri_backup_config}, prelude::*, RtcCntl, Serial, Timer as old_timer}; 9 | use panic_halt as _; 10 | use embassy; 11 | use embassy::executor::Spawner; 12 | use embassy::time::{Duration, Timer}; 13 | // use embassy_macros::{main, task}; 14 | use embassy_esp32c3::{Serial,}; 15 | use embassy_esp32c3::pac::{UART0, Peripherals}; 16 | use core::cell::RefCell; 17 | use embassy::blocking_mutex::CriticalSectionMutex as Mutex; 18 | 19 | static mut SERIAL: Mutex>>> = Mutex::new(RefCell::new(None)); 20 | 21 | fn log_interrupt(msg: &str) { 22 | critical_section::with(|cs| unsafe { 23 | let mut serial = SERIAL.borrow(cs).borrow_mut(); 24 | let serial = serial.as_mut().unwrap(); 25 | 26 | writeln!(serial, "{}", msg).ok(); 27 | }) 28 | } 29 | 30 | #[embassy::main] 31 | async fn main(_spawner: Spawner, _p: Peripherals){ 32 | let serial = Serial::new(_p.UART0).unwrap(); 33 | critical_section::with(move |_cs| unsafe { 34 | SERIAL.get_mut().replace(Some(serial)); 35 | }); 36 | 37 | // spawner.spawn(run3()); 38 | 39 | let t1 = async { 40 | loop { 41 | 42 | Timer::after(Duration::from_secs(1)).await; 43 | log_interrupt("frequent tick"); 44 | } 45 | }; 46 | let t2 = async { loop { 47 | 48 | Timer::after(Duration::from_secs(10)).await; 49 | log_interrupt("BIG INFREQUENT TICK"); 50 | } 51 | }; 52 | let _one_time_async = async { 53 | Timer::after(Duration::from_secs(3)).await; 54 | log_interrupt("occurring once before join!"); 55 | }.await; 56 | 57 | let one_time_join_async = async { 58 | Timer::after(Duration::from_secs(3)).await; 59 | log_interrupt("occurring once in join!"); 60 | }; 61 | log_interrupt("started join!"); 62 | 63 | futures::join!(t1,t2, one_time_join_async); 64 | } 65 | 66 | // #[embassy::task] 67 | // async fn __embassy_main(spawner: Spawner, _p: Peripherals){ 68 | 69 | // } 70 | -------------------------------------------------------------------------------- /examples/concurrent_timers/.cargo/config.save.toml: -------------------------------------------------------------------------------- 1 | [target.riscv32imc-unknown-none-elf] 2 | runner = "espflash --format=direct-boot --monitor" 3 | 4 | [build] 5 | rustflags = [ 6 | "-C", "link-arg=-Tlinkall.x", 7 | ] 8 | target = "riscv32imc-unknown-none-elf" 9 | 10 | [unstable] 11 | build-std = ["core"] 12 | -------------------------------------------------------------------------------- /examples/concurrent_timers/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [target.'cfg(all(target_arch = "riscv32", target_os = "none"))'] 2 | runner = "espflash --format=direct-boot --monitor" 3 | 4 | [build] 5 | rustflags = [ 6 | "-C", "link-arg=-Tlinkall.x", 7 | ] 8 | target = "riscv32imc-unknown-none-elf" 9 | 10 | [unstable] 11 | build-std = ["core"] 12 | -------------------------------------------------------------------------------- /examples/concurrent_timers/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | debug/ 4 | target/ 5 | 6 | # These are backup files generated by rustfmt 7 | **/*.rs.bk 8 | 9 | # MSVC Windows builds of rustc generate these, which store debugging information 10 | *.pdb 11 | -------------------------------------------------------------------------------- /examples/concurrent_timers/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "rust-analyzer.checkOnSave.allTargets": false, 3 | } 4 | -------------------------------------------------------------------------------- /examples/concurrent_timers/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "concurrent_timers" 3 | version = "0.1.0" 4 | authors = ["osobiehl "] 5 | edition = "2021" 6 | license = "MIT OR Apache-2.0" 7 | 8 | 9 | 10 | 11 | [dependencies] 12 | panic-halt = "0.2" 13 | esp32c3-hal = { package = "esp32c3-hal", git = "https://github.com/esp-rs/esp-hal.git" } 14 | riscv-rt = { version = "0.8", optional = true } 15 | nb = "1.0.0" 16 | embassy = { path = "../../embassy", features = [ "time", "time-tick-16mhz", "nightly"] } 17 | futures = { version = "0.3.17", default-features = false, features = ["async-await"] } 18 | heapless = { version = "0.7.5", default-features = false } 19 | embassy_esp32c3 = {path="../../embassy-esp32c3"} 20 | #embassy-macros = { version = "0.1.0", path = "../../embassy-macros", features=["esp32c3"]} 21 | serde = { version = "1.0.136", default-features = false } 22 | embedded-hal = { version = "0.2", features = ["unproven"] } 23 | critical-section = "0.2.5" 24 | 25 | defmt = "0.3" 26 | #defmt-rtt = "0.3" 27 | 28 | [features] 29 | default = ["nightly"] 30 | nightly = [ "riscv-rt" ] 31 | rt = ["riscv-rt"] 32 | -------------------------------------------------------------------------------- /examples/concurrent_timers/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright [year] [fullname] 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /examples/concurrent_timers/README.md: -------------------------------------------------------------------------------- 1 | # run 2 | `cargo espflash --monitor` 3 | 4 | -------------------------------------------------------------------------------- /examples/concurrent_timers/rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "nightly" 3 | -------------------------------------------------------------------------------- /examples/concurrent_timers/src/main.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | #![feature(type_alias_impl_trait)] 4 | 5 | use core::fmt::Write; 6 | 7 | // use esp32c3_hal::{pac::{Peripherals, LEDC, apb_ctrl::peri_backup_config}, prelude::*, RtcCntl, Serial, Timer as old_timer}; 8 | use embassy; 9 | use embassy::executor::Spawner; 10 | use embassy::time::{Duration, Timer}; 11 | use panic_halt as _; 12 | // use embassy_macros::{main, task}; 13 | use core::cell::RefCell; 14 | use embassy::blocking_mutex::CriticalSectionMutex as Mutex; 15 | use embassy_esp32c3::pac::{Peripherals, UART0}; 16 | use embassy_esp32c3::Serial; 17 | 18 | static mut SERIAL: Mutex>>> = Mutex::new(RefCell::new(None)); 19 | 20 | fn log_interrupt(msg: &str) { 21 | critical_section::with(|cs| unsafe { 22 | let mut serial = SERIAL.borrow(cs).borrow_mut(); 23 | let serial = serial.as_mut().unwrap(); 24 | 25 | writeln!(serial, "{}", msg).ok(); 26 | }) 27 | } 28 | 29 | #[embassy::main] 30 | async fn main(_spawner: Spawner, _p: Peripherals) { 31 | let serial = Serial::new(_p.UART0).unwrap(); 32 | critical_section::with(move |_cs| unsafe { 33 | SERIAL.get_mut().replace(Some(serial)); 34 | }); 35 | 36 | let async1 = async { 37 | loop { 38 | Timer::after(Duration::from_secs(10)).await; 39 | log_interrupt("BIG INFREQUENT TICK"); 40 | } 41 | }; 42 | 43 | let async2 = async { 44 | loop { 45 | log_interrupt("frequent tick"); 46 | Timer::after(Duration::from_secs(1)).await; 47 | } 48 | }; 49 | 50 | let async3 = async { 51 | loop { 52 | Timer::after(Duration::from_secs(5)).await; 53 | log_interrupt("medium frequent tick"); 54 | } 55 | }; 56 | 57 | futures::join!(async1, async2, async3); 58 | } 59 | 60 | // #[embassy::task] 61 | // async fn __embassy_main(spawner: Spawner, _p: Peripherals){ 62 | 63 | // } 64 | -------------------------------------------------------------------------------- /examples/hello_world/.cargo/config.save.toml: -------------------------------------------------------------------------------- 1 | [target.riscv32imc-unknown-none-elf] 2 | runner = "espflash --format=direct-boot --monitor" 3 | 4 | [build] 5 | rustflags = [ 6 | "-C", "link-arg=-Tlinkall.x", 7 | ] 8 | target = "riscv32imc-unknown-none-elf" 9 | 10 | [unstable] 11 | build-std = ["core"] 12 | -------------------------------------------------------------------------------- /examples/hello_world/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [target.'cfg(all(target_arch = "riscv32", target_os = "none"))'] 2 | runner = "espflash --format=direct-boot --monitor" 3 | 4 | [build] 5 | rustflags = [ 6 | "-C", "link-arg=-Tlinkall.x", 7 | ] 8 | target = "riscv32imc-unknown-none-elf" 9 | 10 | [unstable] 11 | build-std = ["core"] 12 | -------------------------------------------------------------------------------- /examples/hello_world/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | debug/ 4 | target/ 5 | 6 | # These are backup files generated by rustfmt 7 | **/*.rs.bk 8 | 9 | # MSVC Windows builds of rustc generate these, which store debugging information 10 | *.pdb 11 | -------------------------------------------------------------------------------- /examples/hello_world/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "rust-analyzer.checkOnSave.allTargets": false, 3 | } 4 | -------------------------------------------------------------------------------- /examples/hello_world/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "async_join" 3 | version = "0.1.0" 4 | authors = ["osobiehl "] 5 | edition = "2021" 6 | license = "MIT OR Apache-2.0" 7 | 8 | 9 | 10 | 11 | [dependencies] 12 | panic-halt = "0.2" 13 | esp32c3-hal = { package = "esp32c3-hal", git = "https://github.com/esp-rs/esp-hal.git" } 14 | riscv-rt = { version = "0.8", optional = true } 15 | nb = "1.0.0" 16 | embassy = { path = "../../embassy", features = [ "time", "time-tick-16mhz", "nightly"] } 17 | futures = { version = "0.3.17", default-features = false, features = ["async-await"] } 18 | heapless = { version = "0.7.5", default-features = false } 19 | embassy_esp32c3 = {path="../../embassy-esp32c3"} 20 | #embassy-macros = { version = "0.1.0", path = "../../embassy-macros", features=["esp32c3"]} 21 | serde = { version = "1.0.136", default-features = false } 22 | embedded-hal = { version = "0.2", features = ["unproven"] } 23 | critical-section = "0.2.5" 24 | 25 | defmt = "0.3" 26 | #defmt-rtt = "0.3" 27 | 28 | [features] 29 | default = ["nightly"] 30 | nightly = [ "riscv-rt" ] 31 | rt = ["riscv-rt"] 32 | -------------------------------------------------------------------------------- /examples/hello_world/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright [year] [fullname] 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /examples/hello_world/README.md: -------------------------------------------------------------------------------- 1 | # run 2 | `cargo espflash --monitor` 3 | 4 | -------------------------------------------------------------------------------- /examples/hello_world/rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "nightly" 3 | -------------------------------------------------------------------------------- /examples/hello_world/src/main.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | #![feature(type_alias_impl_trait)] 4 | use core::fmt::Write; 5 | use panic_halt as _; 6 | use embassy::{executor::Spawner, time::{Duration, Timer}}; 7 | use embassy_esp32c3::{pac::Peripherals, Serial}; 8 | #[embassy::main] 9 | async fn main(_spawner: Spawner, _p: Peripherals){ 10 | let mut serial0 = Serial::new(_p.UART0).unwrap(); 11 | loop { 12 | writeln!(serial0, "Hello world!").unwrap(); 13 | Timer::after(Duration::from_millis(1000)).await; 14 | } 15 | } 16 | 17 | // #[embassy::task] 18 | // async fn __embassy_main(spawner: Spawner, _p: Peripherals){ 19 | 20 | // } 21 | -------------------------------------------------------------------------------- /examples/interrupt_mode/.cargo/config.save.toml: -------------------------------------------------------------------------------- 1 | [target.riscv32imc-unknown-none-elf] 2 | runner = "espflash --format=direct-boot --monitor" 3 | 4 | [build] 5 | rustflags = [ 6 | "-C", "link-arg=-Tlinkall.x", 7 | ] 8 | target = "riscv32imc-unknown-none-elf" 9 | 10 | [unstable] 11 | build-std = ["core"] 12 | -------------------------------------------------------------------------------- /examples/interrupt_mode/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [target.'cfg(all(target_arch = "riscv32", target_os = "none"))'] 2 | runner = "espflash --format=direct-boot --monitor" 3 | 4 | [build] 5 | rustflags = [ 6 | "-C", "link-arg=-Tlinkall.x", 7 | ] 8 | target = "riscv32imc-unknown-none-elf" 9 | 10 | [unstable] 11 | build-std = ["core"] 12 | -------------------------------------------------------------------------------- /examples/interrupt_mode/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | debug/ 4 | target/ 5 | 6 | # These are backup files generated by rustfmt 7 | **/*.rs.bk 8 | 9 | # MSVC Windows builds of rustc generate these, which store debugging information 10 | *.pdb 11 | -------------------------------------------------------------------------------- /examples/interrupt_mode/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "rust-analyzer.checkOnSave.allTargets": false, 3 | } 4 | -------------------------------------------------------------------------------- /examples/interrupt_mode/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "interrupt_mode" 3 | version = "0.1.0" 4 | authors = ["osobiehl "] 5 | edition = "2021" 6 | license = "MIT OR Apache-2.0" 7 | 8 | 9 | 10 | 11 | [dependencies] 12 | panic-halt = "0.2" 13 | esp32c3-hal = { package = "esp32c3-hal", git = "https://github.com/esp-rs/esp-hal.git" } 14 | riscv-rt = { version = "0.8", optional = true } 15 | nb = "1.0.0" 16 | embassy = { path = "../../embassy", features = [ "time", "time-tick-16mhz", "nightly"] } 17 | futures = { version = "0.3.17", default-features = false, features = ["async-await"] } 18 | heapless = { version = "0.7.5", default-features = false } 19 | embassy_esp32c3 = {path="../../embassy-esp32c3"} 20 | #embassy-macros = { version = "0.1.0", path = "../../embassy-macros", features=["esp32c3"]} 21 | serde = { version = "1.0.136", default-features = false } 22 | embedded-hal = { version = "0.2", features = ["unproven"] } 23 | critical-section = "0.2.5" 24 | riscv = "0.8" 25 | 26 | defmt = "0.3" 27 | #defmt-rtt = "0.3" 28 | 29 | [features] 30 | default = ["nightly"] 31 | nightly = [ "riscv-rt" ] 32 | rt = ["riscv-rt"] 33 | -------------------------------------------------------------------------------- /examples/interrupt_mode/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright [year] [fullname] 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /examples/interrupt_mode/README.md: -------------------------------------------------------------------------------- 1 | # run 2 | `cargo espflash --monitor` 3 | 4 | -------------------------------------------------------------------------------- /examples/interrupt_mode/rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "nightly" 3 | -------------------------------------------------------------------------------- /examples/interrupt_mode/src/main.rs: -------------------------------------------------------------------------------- 1 | //! This example showcases how to create multiple Executor instances to run tasks at 2 | //! different priority levels. 3 | //! 4 | //! Low priority executor runs in thread mode (not interrupt), and uses `sev` for signaling 5 | //! there's work in the queue, and `wfe` for waiting for work. 6 | //! 7 | //! Medium and high priority executors run in two interrupts with different priorities. 8 | //! Signaling work is done by pending the interrupt. No "waiting" needs to be done explicitly, since 9 | //! when there's work the interrupt will trigger and run the executor. 10 | //! 11 | //! Sample output below. Note that high priority ticks can interrupt everything else, and 12 | //! medium priority computations can interrupt low priority computations, making them to appear 13 | //! to take significantly longer time. 14 | //! 15 | //! ```not_rust 16 | //! [med] Starting long computation 17 | //! [med] done in 992 ms 18 | //! [high] tick! 19 | //! [low] Starting long computation 20 | //! [med] Starting long computation 21 | //! [high] tick! 22 | //! [high] tick! 23 | //! [med] done in 993 ms 24 | //! [med] Starting long computation 25 | //! [high] tick! 26 | //! [high] tick! 27 | //! [med] done in 993 ms 28 | //! [low] done in 3972 ms 29 | //! [med] Starting long computation 30 | //! [high] tick! 31 | //! [high] tick! 32 | //! [med] done in 993 ms 33 | //! ``` 34 | //! 35 | //! For comparison, try changing the code so all 3 tasks get spawned on the low priority executor. 36 | //! You will get an output like the following. Note that no computation is ever interrupted. 37 | //! 38 | //! ```not_rust 39 | //! [high] tick! 40 | //! [med] Starting long computation 41 | //! [med] done in 496 ms 42 | //! [low] Starting long computation 43 | //! [low] done in 992 ms 44 | //! [med] Starting long computation 45 | //! [med] done in 496 ms 46 | //! [high] tick! 47 | //! [low] Starting long computation 48 | //! [low] done in 992 ms 49 | //! [high] tick! 50 | //! [med] Starting long computation 51 | //! [med] done in 496 ms 52 | //! [high] tick! 53 | //! ``` 54 | //! 55 | 56 | #![no_std] 57 | #![no_main] 58 | #![feature(type_alias_impl_trait)] 59 | 60 | // use cortex_m_rt::entry; 61 | // use defmt::{info, unwrap}; 62 | use core::fmt::Write; 63 | use embassy::executor::{Executor, InterruptExecutor}; 64 | use embassy::time::{Duration, Timer}; 65 | use embassy::util::Forever; 66 | 67 | // use esp32c3_hal::{pac::{Peripherals, LEDC, apb_ctrl::peri_backup_config}, prelude::*, RtcCntl, Serial, Timer as old_timer}; 68 | use embassy; 69 | 70 | use panic_halt as _; 71 | // use embassy_macros::{main, task}; 72 | use core::cell::RefCell; 73 | 74 | use embassy::blocking_mutex::CriticalSectionMutex as Mutex; 75 | use embassy_esp32c3::interrupt::SW_INT1; 76 | use embassy_esp32c3::pac::UART0; 77 | use embassy_esp32c3::Serial; 78 | 79 | // use defmt_rtt as _; // global logger 80 | // use panic_probe as _; 81 | 82 | fn log_interrupt(msg: &str) { 83 | critical_section::with(|cs| unsafe { 84 | let mut serial = SERIAL.borrow(cs).borrow_mut(); 85 | let serial = serial.as_mut().unwrap(); 86 | 87 | writeln!(serial, "{}", msg).ok(); 88 | }) 89 | } 90 | #[no_mangle] 91 | pub fn __log(msg: &str) { 92 | log_interrupt(msg); 93 | } 94 | 95 | static mut SERIAL: Mutex>>> = Mutex::new(RefCell::new(None)); 96 | 97 | #[embassy::task] 98 | async fn run_high() { 99 | loop { 100 | 101 | Timer::after(Duration::from_secs(3)).await; 102 | log_interrupt("high priority interrupt preempts computation!"); 103 | } 104 | } 105 | 106 | #[embassy::task] 107 | async fn run_low() { 108 | // let mut delay = McycleDelay::new(20_000_000); 109 | loop { 110 | log_interrupt(" [low] Starting long computation"); 111 | 112 | // Spin-wait to simulate a long CPU computation 113 | let mut ctr = 0; 114 | let bignum = 0x2ffffff; 115 | while ctr < bignum { 116 | unsafe { riscv::asm::nop() } 117 | ctr += 1; 118 | } 119 | log_interrupt(" [low] finished long computation"); 120 | 121 | Timer::after(Duration::from_millis(20)).await; 122 | } 123 | } 124 | 125 | static EXECUTOR_HIGH: Forever> = Forever::new(); 126 | 127 | static EXECUTOR_LOW: Forever = Forever::new(); 128 | 129 | #[riscv_rt::entry] 130 | fn main() -> ! { 131 | let _p = embassy_esp32c3::init(Default::default()); 132 | let mut serial = Serial::new(_p.UART0).unwrap(); 133 | writeln!( 134 | serial, 135 | "if you flash in release mode, the compiler might remove the work simulation" 136 | ) 137 | .ok(); 138 | critical_section::with(move |_cs| unsafe { 139 | SERIAL.get_mut().replace(Some(serial)); 140 | }); 141 | 142 | // High-priority executor: SWI1_EGU1, priority level 6 143 | //highest priority 144 | log_interrupt("setting interrupt executor"); 145 | let int_executor = SW_INT1::new(embassy_esp32c3::interrupt::Priority::Priority15); 146 | let executor = EXECUTOR_HIGH.put(InterruptExecutor::new(int_executor)); 147 | log_interrupt("spawning interrupt executor task"); 148 | executor.start(|spawner| match spawner.spawn(run_high()) { 149 | Ok(_) => log_interrupt("task successfully spawned!"), 150 | Err(_) => log_interrupt("something went wrong!"), 151 | }); 152 | log_interrupt("spawning thread mode executor"); 153 | 154 | // Low priority executor: runs in thread mode, using WFE/SEV 155 | let executor = EXECUTOR_LOW.put(Executor::new()); 156 | executor.run(|spawner| { 157 | spawner.spawn(run_low()).ok(); 158 | }); 159 | } 160 | -------------------------------------------------------------------------------- /examples/multiple_tasks/.cargo/config.save.toml: -------------------------------------------------------------------------------- 1 | [target.riscv32imc-unknown-none-elf] 2 | runner = "espflash --format=direct-boot --monitor" 3 | 4 | [build] 5 | rustflags = [ 6 | "-C", "link-arg=-Tlinkall.x", 7 | ] 8 | target = "riscv32imc-unknown-none-elf" 9 | 10 | [unstable] 11 | build-std = ["core"] 12 | -------------------------------------------------------------------------------- /examples/multiple_tasks/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [target.'cfg(all(target_arch = "riscv32", target_os = "none"))'] 2 | runner = "espflash --format=direct-boot --monitor" 3 | 4 | [build] 5 | rustflags = [ 6 | "-C", "link-arg=-Tlinkall.x", 7 | ] 8 | target = "riscv32imc-unknown-none-elf" 9 | 10 | [unstable] 11 | build-std = ["core"] 12 | -------------------------------------------------------------------------------- /examples/multiple_tasks/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | debug/ 4 | target/ 5 | 6 | # These are backup files generated by rustfmt 7 | **/*.rs.bk 8 | 9 | # MSVC Windows builds of rustc generate these, which store debugging information 10 | *.pdb 11 | -------------------------------------------------------------------------------- /examples/multiple_tasks/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "rust-analyzer.checkOnSave.allTargets": false, 3 | } 4 | -------------------------------------------------------------------------------- /examples/multiple_tasks/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "multiple_tasks" 3 | version = "0.1.0" 4 | authors = ["osobiehl "] 5 | edition = "2021" 6 | license = "MIT OR Apache-2.0" 7 | 8 | 9 | 10 | 11 | [dependencies] 12 | panic-halt = "0.2" 13 | esp32c3-hal = { package = "esp32c3-hal", git = "https://github.com/esp-rs/esp-hal.git" } 14 | riscv-rt = { version = "0.8", optional = true } 15 | nb = "1.0.0" 16 | embassy = { path = "../../embassy", features = [ "time", "time-tick-16mhz", "nightly"] } 17 | futures = { version = "0.3.17", default-features = false, features = ["async-await"] } 18 | heapless = { version = "0.7.5", default-features = false } 19 | embassy_esp32c3 = {path="../../embassy-esp32c3"} 20 | #embassy-macros = { version = "0.1.0", path = "../../embassy-macros", features=["esp32c3"]} 21 | serde = { version = "1.0.136", default-features = false } 22 | embedded-hal = { version = "0.2", features = ["unproven"] } 23 | critical-section = "0.2.5" 24 | 25 | defmt = "0.3" 26 | #defmt-rtt = "0.3" 27 | 28 | [features] 29 | default = ["nightly"] 30 | nightly = [ "riscv-rt" ] 31 | rt = ["riscv-rt"] 32 | -------------------------------------------------------------------------------- /examples/multiple_tasks/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright [year] [fullname] 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /examples/multiple_tasks/README.md: -------------------------------------------------------------------------------- 1 | # run 2 | `cargo espflash --monitor` 3 | 4 | -------------------------------------------------------------------------------- /examples/multiple_tasks/rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "nightly" 3 | -------------------------------------------------------------------------------- /examples/multiple_tasks/src/main.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | #![feature(type_alias_impl_trait)] 4 | 5 | 6 | use core::fmt::Write; 7 | 8 | // use esp32c3_hal::{pac::{Peripherals, LEDC, apb_ctrl::peri_backup_config}, prelude::*, RtcCntl, Serial, Timer as old_timer}; 9 | use panic_halt as _; 10 | use embassy; 11 | use embassy::executor::Spawner; 12 | use embassy::time::{Duration, Timer}; 13 | use embassy_esp32c3::Serial; 14 | use embassy_esp32c3::pac::{UART0, Peripherals}; 15 | use core::cell::RefCell; 16 | 17 | use embassy::blocking_mutex::CriticalSectionMutex as Mutex; 18 | 19 | static mut SERIAL: Mutex>>> = Mutex::new(RefCell::new(None)); 20 | 21 | fn log_interrupt(msg: &str) { 22 | critical_section::with(|cs| unsafe { 23 | let mut serial = SERIAL.borrow(cs).borrow_mut(); 24 | let mut serial = serial.as_mut().unwrap(); 25 | 26 | writeln!(serial, "{}", msg).ok(); 27 | }) 28 | } 29 | 30 | 31 | #[embassy::task] 32 | async fn task1(){ 33 | loop { 34 | 35 | Timer::after(Duration::from_secs(1)).await; 36 | log_interrupt("frequent tick"); 37 | } 38 | } 39 | #[embassy::task] 40 | async fn task2(){ 41 | loop { 42 | 43 | Timer::after(Duration::from_secs(10)).await; 44 | log_interrupt("BIG INFREQUENT TICK"); 45 | } 46 | 47 | } 48 | #[embassy::task] 49 | async fn task3(){ 50 | loop { 51 | 52 | Timer::after(Duration::from_secs(5)).await; 53 | log_interrupt("Medium frequent tick"); 54 | } 55 | } 56 | 57 | #[embassy::main] 58 | async fn main(spawner: Spawner, _p: Peripherals){ 59 | let mut serial = Serial::new(_p.UART0).unwrap(); 60 | writeln!(serial, "getting into criticalsection").ok(); 61 | critical_section::with(move |_cs| unsafe { 62 | SERIAL.get_mut().replace(Some(serial)); 63 | }); 64 | 65 | spawner.must_spawn(task1()); 66 | spawner.must_spawn(task2()); 67 | spawner.must_spawn(task3()); 68 | } 69 | 70 | // #[embassy::task] 71 | // async fn __embassy_main(spawner: Spawner, _p: Peripherals){ 72 | 73 | // } 74 | -------------------------------------------------------------------------------- /examples/previous_race_condition/.cargo/config.save.toml: -------------------------------------------------------------------------------- 1 | [target.riscv32imc-unknown-none-elf] 2 | runner = "espflash --format=direct-boot --monitor" 3 | 4 | [build] 5 | rustflags = [ 6 | "-C", "link-arg=-Tlinkall.x", 7 | ] 8 | target = "riscv32imc-unknown-none-elf" 9 | 10 | [unstable] 11 | build-std = ["core"] 12 | -------------------------------------------------------------------------------- /examples/previous_race_condition/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [target.'cfg(all(target_arch = "riscv32", target_os = "none"))'] 2 | runner = "espflash --format=direct-boot --monitor" 3 | 4 | [build] 5 | rustflags = [ 6 | "-C", "link-arg=-Tlinkall.x", 7 | ] 8 | target = "riscv32imc-unknown-none-elf" 9 | 10 | [unstable] 11 | build-std = ["core"] 12 | -------------------------------------------------------------------------------- /examples/previous_race_condition/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | debug/ 4 | target/ 5 | 6 | # These are backup files generated by rustfmt 7 | **/*.rs.bk 8 | 9 | # MSVC Windows builds of rustc generate these, which store debugging information 10 | *.pdb 11 | -------------------------------------------------------------------------------- /examples/previous_race_condition/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "rust-analyzer.checkOnSave.allTargets": false, 3 | } 4 | -------------------------------------------------------------------------------- /examples/previous_race_condition/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "previous_race_condition" 3 | version = "0.1.0" 4 | authors = ["osobiehl "] 5 | edition = "2021" 6 | license = "MIT OR Apache-2.0" 7 | 8 | 9 | 10 | 11 | [dependencies] 12 | panic-halt = "0.2" 13 | esp32c3-hal = { package = "esp32c3-hal", git = "https://github.com/esp-rs/esp-hal.git" } 14 | riscv-rt = { version = "0.8", optional = true } 15 | nb = "1.0.0" 16 | embassy = { path = "../../embassy", features = [ "time", "time-tick-16mhz", "nightly"] } 17 | futures = { version = "0.3.17", default-features = false, features = ["async-await"] } 18 | heapless = { version = "0.7.5", default-features = false } 19 | embassy_esp32c3 = {path="../../embassy-esp32c3"} 20 | #embassy-macros = { version = "0.1.0", path = "../../embassy-macros", features=["esp32c3"]} 21 | serde = { version = "1.0.136", default-features = false } 22 | embedded-hal = { version = "0.2", features = ["unproven"] } 23 | critical-section = "0.2.5" 24 | 25 | defmt = "0.3" 26 | #defmt-rtt = "0.3" 27 | 28 | [features] 29 | default = ["nightly"] 30 | nightly = [ "riscv-rt" ] 31 | rt = ["riscv-rt"] 32 | -------------------------------------------------------------------------------- /examples/previous_race_condition/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright [year] [fullname] 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /examples/previous_race_condition/README.md: -------------------------------------------------------------------------------- 1 | # run 2 | `cargo espflash --monitor` 3 | 4 | ## About 5 | this code previously had a race condition that could cause the application to crash if both intrerrupts occurred before entering the `WFI` instruction in the thread mode executor. It should work fine now though -------------------------------------------------------------------------------- /examples/previous_race_condition/rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "nightly" 3 | -------------------------------------------------------------------------------- /examples/previous_race_condition/src/main.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | #![feature(type_alias_impl_trait)] 4 | 5 | use core::fmt::Write; 6 | 7 | // use esp32c3_hal::{pac::{Peripherals, LEDC, apb_ctrl::peri_backup_config}, prelude::*, RtcCntl, Serial, Timer as old_timer}; 8 | use embassy::executor::Spawner; 9 | use embassy::time::{Duration, Timer}; 10 | use panic_halt as _; 11 | // use embassy_macros::{main, task}; 12 | use core::cell::RefCell; 13 | use embassy_esp32c3::pac::{Peripherals, UART0}; 14 | use embassy_esp32c3::Serial; 15 | 16 | use embassy::blocking_mutex::CriticalSectionMutex as Mutex; 17 | 18 | static mut SERIAL: Mutex>>> = Mutex::new(RefCell::new(None)); 19 | 20 | fn log_interrupt(msg: &str) { 21 | critical_section::with(|cs| unsafe { 22 | let mut serial = SERIAL.borrow(cs).borrow_mut(); 23 | let serial = serial.as_mut().unwrap(); 24 | 25 | writeln!(serial, "{}", msg).ok(); 26 | }) 27 | } 28 | 29 | #[embassy::task] 30 | async fn task1() { 31 | loop { 32 | Timer::after(Duration::from_millis(50)).await; 33 | log_interrupt("tick1"); 34 | } 35 | } 36 | #[embassy::task] 37 | async fn task2() { 38 | loop { 39 | Timer::after(Duration::from_millis(50)).await; 40 | log_interrupt("tick2"); 41 | } 42 | } 43 | // #[embassy::task] 44 | // async fn task3(){ 45 | // loop { 46 | 47 | // Timer::after(Duration::from_millis(500)).await; 48 | // log_interrupt("Medium frequent tick"); 49 | // } 50 | // } 51 | 52 | #[embassy::main] 53 | async fn main(spawner: Spawner, _p: Peripherals) { 54 | let mut serial = Serial::new(_p.UART0).unwrap(); 55 | writeln!(serial, "getting into criticalsection").ok(); 56 | critical_section::with(move |_cs| unsafe { 57 | SERIAL.get_mut().replace(Some(serial)); 58 | }); 59 | 60 | spawner.must_spawn(task1()); 61 | spawner.must_spawn(task2()); 62 | // spawner.must_spawn(task3()); 63 | } 64 | 65 | // #[embassy::task] 66 | // async fn __embassy_main(spawner: Spawner, _p: Peripherals){ 67 | 68 | // } 69 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # EMBASSY FOR ESP32C3 # 2 | # Table of Contents 3 | 1. [About](#About) 4 | 2. [Quick Start](#Quick_Start) 5 | 3. [Building + Flashing](#) 6 | 4. [Testing](#Testing) 7 | 8 | ## About 9 | A quick and messy port of the Embassy Rust framework for RISCV esp32c3 monitors 10 | Due to how interrupts are implemented in the esp32c3, this implementation does NOT easily port over to other RISCV microprocessors. It makes use of the trap entry point implemented in [esp32c3-hal](https://github.com/esp-rs/esp-hal/tree/main/esp32c3-hal) to set up interrupt handlers. 11 | 12 | ## Quick_Start 13 | 14 | ### Important 15 | You need a nightly compiler to run the code in this repository. i.e. `rustup default nightly` 16 | 17 | 18 | Run `rustup target add riscv32imc-unknown-none-elf` to add the ESP32-C3's ISA as a target 19 | 20 | This project has been tested runing on `rustc 1.62.0-nightly (60e50fc1c 2022-04-04)`. 21 | 22 | If you encounter an error related to rust-src not being found, you need to add it. This is the source code of the Rust standard library, it is necessary to build the core Rust library. The error will tell you to install 23 | `rustup component add rust-src --toolchain nightly-` 24 | 25 | Follow the compiler's instructions in the error :^ ) 26 | 27 | ### flashing 28 | 29 | This project uses `cargo espflash` to flash an ESP32C3 microcontroller, you can install it by running `cargo install cargo-espflash` 30 | espflash should detect the proper baud-rate and device, in case it does not, please refer to the documentation of `cargo-espflash` 31 | ### Building + Flashing 32 | Building and flashing is done by going into the desired example directory, e.g `cd examples/hello_world` and then running `cargo espflash --monitor` . This should also provide a monitoring output to show written results 33 | 34 | ## Testing 35 | 36 | A test "suite" for embassy's time driver is also included in this project, it makes SYSTIMER interrupts can be properly scheduled and triggered concurrently. Refer to the flashing section to run the tests. There is also testing for the `WFI` instruction's functionality inside of a critical section, as well as a simple test to see if software interrupts can be triggered on the ESP32-C3 37 | 38 | ## Implementation 39 | This implementation currently only contains embassy's time_driver. No other parts of the HAL are implemented :) -------------------------------------------------------------------------------- /test/test_SW_INT1/.cargo/config.save.toml: -------------------------------------------------------------------------------- 1 | [target.riscv32imc-unknown-none-elf] 2 | runner = "espflash --format=direct-boot --monitor" 3 | 4 | [build] 5 | rustflags = [ 6 | "-C", "link-arg=-Tlinkall.x", 7 | ] 8 | target = "riscv32imc-unknown-none-elf" 9 | 10 | [unstable] 11 | build-std = ["core"] 12 | -------------------------------------------------------------------------------- /test/test_SW_INT1/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [target.'cfg(all(target_arch = "riscv32", target_os = "none"))'] 2 | runner = "espflash --format=direct-boot --monitor" 3 | 4 | [build] 5 | rustflags = [ 6 | "-C", "link-arg=-Tlinkall.x", 7 | ] 8 | target = "riscv32imc-unknown-none-elf" 9 | 10 | [unstable] 11 | build-std = ["core"] 12 | -------------------------------------------------------------------------------- /test/test_SW_INT1/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | debug/ 4 | target/ 5 | 6 | # These are backup files generated by rustfmt 7 | **/*.rs.bk 8 | 9 | # MSVC Windows builds of rustc generate these, which store debugging information 10 | *.pdb 11 | -------------------------------------------------------------------------------- /test/test_SW_INT1/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "rust-analyzer.checkOnSave.allTargets": false, 3 | } 4 | -------------------------------------------------------------------------------- /test/test_SW_INT1/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "test_sw_int_1" 3 | version = "0.1.0" 4 | authors = ["osobiehl "] 5 | edition = "2021" 6 | license = "MIT OR Apache-2.0" 7 | 8 | 9 | 10 | 11 | [dependencies] 12 | panic-halt = "0.2" 13 | esp32c3-hal = { package = "esp32c3-hal", git = "https://github.com/esp-rs/esp-hal.git" } 14 | riscv-rt = { version = "0.8", optional = true } 15 | nb = "1.0.0" 16 | embassy = { path = "../../embassy", features = [ "time", "time-tick-16mhz", "nightly"] } 17 | futures = { version = "0.3.17", default-features = false, features = ["async-await"] } 18 | heapless = { version = "0.7.5", default-features = false } 19 | embassy_esp32c3 = {path="../../embassy-esp32c3"} 20 | #embassy-macros = { version = "0.1.0", path = "../../embassy-macros", features=["esp32c3"]} 21 | serde = { version = "1.0.136", default-features = false } 22 | embedded-hal = { version = "0.2", features = ["unproven"] } 23 | critical-section = "0.2.5" 24 | riscv = "0.7" 25 | 26 | defmt = "0.3" 27 | #defmt-rtt = "0.3" 28 | 29 | [features] 30 | default = ["nightly"] 31 | nightly = [ "riscv-rt" ] 32 | rt = ["riscv-rt"] 33 | -------------------------------------------------------------------------------- /test/test_SW_INT1/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright [year] [fullname] 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /test/test_SW_INT1/README.md: -------------------------------------------------------------------------------- 1 | tests to make sure software interrupts can be pended / unpended / disabled 2 | run with cargo-espflash --monitor -------------------------------------------------------------------------------- /test/test_SW_INT1/rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "nightly" 3 | -------------------------------------------------------------------------------- /test/test_SW_INT1/src/main.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | #![feature(type_alias_impl_trait)] 4 | 5 | use core::fmt::Write; 6 | 7 | // use esp32c3_hal::{pac::{Peripherals, LEDC, apb_ctrl::peri_backup_config}, prelude::*, RtcCntl, Serial, Timer as old_timer}; 8 | use embassy; 9 | use panic_halt as _; 10 | // use embassy_macros::{main, task}; 11 | use core::cell::{Cell, RefCell}; 12 | use core::option::Option::{self, None, Some}; 13 | use embassy::blocking_mutex::CriticalSectionMutex as Mutex; 14 | use embassy::interrupt::InterruptExt; 15 | use embassy_esp32c3::config; 16 | use embassy_esp32c3::interrupt::{Priority, SW_INT1}; 17 | use embassy_esp32c3::pac::UART0; 18 | use embassy_esp32c3::{init, Serial}; 19 | // use embassy_esp32c3::{} 20 | 21 | // #[task] 22 | // async fn run(){ 23 | // static peripherals: Peripherals = Peripher::take().unwrap(); 24 | // static serial0: Serial = Serial::new(peripherals.UART0).unwrap(); 25 | // writeln!(serial0, "tick!"); 26 | // Timer::after(Duration::from_secs(1)).await; 27 | // } 28 | unsafe fn __make_static(t: &mut T) -> &'static mut T { 29 | ::core::mem::transmute(t) 30 | } 31 | static mut SERIAL: Mutex>>> = Mutex::new(RefCell::new(None)); 32 | static CTR: Mutex> = Mutex::new(Cell::new(0)); 33 | 34 | fn increment_ctr(any: *mut ()) { 35 | let irq = unsafe { &*(any as *mut SW_INT1) }; 36 | log_interrupt("unpending!"); 37 | irq.unpend(); 38 | critical_section::with(|cs| { 39 | let ctr = CTR.borrow(cs); 40 | let count = ctr.get(); 41 | ctr.set(count + 1); 42 | }); 43 | log_interrupt("software interrupt!"); 44 | } 45 | fn log_interrupt(msg: &str) { 46 | critical_section::with(|cs| unsafe { 47 | let mut serial = SERIAL.borrow(cs).borrow_mut(); 48 | let serial = serial.as_mut().unwrap(); 49 | 50 | writeln!(serial, "{}", msg).ok(); 51 | }) 52 | } 53 | 54 | #[riscv_rt::entry] 55 | fn main() -> ! { 56 | let peripherals = init(config::Config::default()); 57 | 58 | let mut serial = Serial::new(peripherals.UART0).unwrap(); 59 | writeln!(serial, "initializing enabling interrupt").ok(); 60 | 61 | let mut software_interrupt = SW_INT1::new(Priority::Priority4); 62 | software_interrupt.set_handler(increment_ctr); 63 | let intr_ptr: *mut SW_INT1 = &mut software_interrupt; 64 | software_interrupt.set_handler_context(intr_ptr as *mut ()); 65 | software_interrupt.enable(); 66 | //now we place our serial inside a mutex since we want to log from interrupts 67 | critical_section::with(move |_cs| unsafe { 68 | SERIAL.get_mut().replace(Some(serial)); 69 | }); 70 | log_interrupt("pending software interrupt, if no output is given, this fails"); 71 | software_interrupt.pend(); 72 | log_interrupt("DONE!"); 73 | software_interrupt.disable(); 74 | 75 | log_interrupt("disabling software interrupt, program should not start an interrupt"); 76 | software_interrupt.pend(); 77 | log_interrupt("Done!"); 78 | 79 | // _embassy_time_set_alarm_callback(, callback, ctx) 80 | 81 | // peripherals.UART0 = serial.free(); 82 | loop {} 83 | } 84 | 85 | // #[embassy::task] 86 | // async fn __embassy_main(spawner: Spawner, _p: Peripherals){ 87 | 88 | // } 89 | -------------------------------------------------------------------------------- /test/test_disable_interrupt/.cargo/config.save.toml: -------------------------------------------------------------------------------- 1 | [target.riscv32imc-unknown-none-elf] 2 | runner = "espflash --format=direct-boot --monitor" 3 | 4 | [build] 5 | rustflags = [ 6 | "-C", "link-arg=-Tlinkall.x", 7 | ] 8 | target = "riscv32imc-unknown-none-elf" 9 | 10 | [unstable] 11 | build-std = ["core"] 12 | -------------------------------------------------------------------------------- /test/test_disable_interrupt/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [target.'cfg(all(target_arch = "riscv32", target_os = "none"))'] 2 | runner = "espflash --format=direct-boot --monitor" 3 | 4 | [build] 5 | rustflags = [ 6 | "-C", "link-arg=-Tlinkall.x", 7 | ] 8 | target = "riscv32imc-unknown-none-elf" 9 | 10 | [unstable] 11 | build-std = ["core"] 12 | -------------------------------------------------------------------------------- /test/test_disable_interrupt/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | debug/ 4 | target/ 5 | 6 | # These are backup files generated by rustfmt 7 | **/*.rs.bk 8 | 9 | # MSVC Windows builds of rustc generate these, which store debugging information 10 | *.pdb 11 | -------------------------------------------------------------------------------- /test/test_disable_interrupt/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "rust-analyzer.checkOnSave.allTargets": false, 3 | } 4 | -------------------------------------------------------------------------------- /test/test_disable_interrupt/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "test_disable_interrupt" 3 | version = "0.1.0" 4 | authors = ["osobiehl "] 5 | edition = "2021" 6 | license = "MIT OR Apache-2.0" 7 | 8 | 9 | 10 | 11 | [dependencies] 12 | panic-halt = "0.2" 13 | esp32c3-hal = { package = "esp32c3-hal", git = "https://github.com/esp-rs/esp-hal.git" } 14 | riscv-rt = { version = "0.8", optional = true } 15 | nb = "1.0.0" 16 | embassy = { path = "../../embassy", features = [ "time", "time-tick-16mhz", "nightly"] } 17 | futures = { version = "0.3.17", default-features = false, features = ["async-await"] } 18 | heapless = { version = "0.7.5", default-features = false } 19 | embassy_esp32c3 = {path="../../embassy-esp32c3"} 20 | #embassy-macros = { version = "0.1.0", path = "../../embassy-macros", features=["esp32c3"]} 21 | serde = { version = "1.0.136", default-features = false } 22 | embedded-hal = { version = "0.2", features = ["unproven"] } 23 | critical-section = "0.2.5" 24 | riscv = "0.8" 25 | 26 | defmt = "0.3" 27 | #defmt-rtt = "0.3" 28 | 29 | [features] 30 | default = ["nightly"] 31 | nightly = [ "riscv-rt" ] 32 | rt = ["riscv-rt"] 33 | -------------------------------------------------------------------------------- /test/test_disable_interrupt/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright [year] [fullname] 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /test/test_disable_interrupt/README.md: -------------------------------------------------------------------------------- 1 | TESTS WHETHER WFI WILL PROPERLY RESUME EXECUTION IF AN INTERRUPT IS PENDING 2 | RUN USING cargo-espflash --monitor -------------------------------------------------------------------------------- /test/test_disable_interrupt/rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "nightly" 3 | -------------------------------------------------------------------------------- /test/test_time_driver/.cargo/config.save.toml: -------------------------------------------------------------------------------- 1 | [target.riscv32imc-unknown-none-elf] 2 | runner = "espflash --format=direct-boot --monitor" 3 | 4 | [build] 5 | rustflags = [ 6 | "-C", "link-arg=-Tlinkall.x", 7 | ] 8 | target = "riscv32imc-unknown-none-elf" 9 | 10 | [unstable] 11 | build-std = ["core"] 12 | -------------------------------------------------------------------------------- /test/test_time_driver/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [target.'cfg(all(target_arch = "riscv32", target_os = "none"))'] 2 | runner = "espflash --format=direct-boot --monitor" 3 | 4 | [build] 5 | rustflags = [ 6 | "-C", "link-arg=-Tlinkall.x", 7 | ] 8 | target = "riscv32imc-unknown-none-elf" 9 | 10 | [unstable] 11 | build-std = ["core"] 12 | -------------------------------------------------------------------------------- /test/test_time_driver/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | debug/ 4 | target/ 5 | 6 | # These are backup files generated by rustfmt 7 | **/*.rs.bk 8 | 9 | # MSVC Windows builds of rustc generate these, which store debugging information 10 | *.pdb 11 | -------------------------------------------------------------------------------- /test/test_time_driver/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "rust-analyzer.checkOnSave.allTargets": false, 3 | } 4 | -------------------------------------------------------------------------------- /test/test_time_driver/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "test_time_driver" 3 | version = "0.1.0" 4 | authors = ["osobiehl "] 5 | edition = "2021" 6 | license = "MIT OR Apache-2.0" 7 | 8 | 9 | 10 | 11 | [dependencies] 12 | panic-halt = "0.2" 13 | esp32c3-hal = { package = "esp32c3-hal", git = "https://github.com/esp-rs/esp-hal.git" } 14 | riscv-rt = { version = "0.8", optional = true } 15 | nb = "1.0.0" 16 | embassy = { path = "../../embassy", features = [ "time", "time-tick-16mhz", "nightly"] } 17 | futures = { version = "0.3.17", default-features = false, features = ["async-await"] } 18 | heapless = { version = "0.7.5", default-features = false } 19 | embassy_esp32c3 = {path="../../embassy-esp32c3"} 20 | #embassy-macros = { version = "0.1.0", path = "../../embassy-macros", features=["esp32c3"]} 21 | serde = { version = "1.0.136", default-features = false } 22 | embedded-hal = { version = "0.2", features = ["unproven"] } 23 | critical-section = "0.2.5" 24 | riscv = "0.7" 25 | 26 | defmt = "0.3" 27 | #defmt-rtt = "0.3" 28 | 29 | [features] 30 | default = ["nightly"] 31 | nightly = [ "riscv-rt" ] 32 | rt = ["riscv-rt"] 33 | -------------------------------------------------------------------------------- /test/test_time_driver/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright [year] [fullname] 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /test/test_time_driver/README.md: -------------------------------------------------------------------------------- 1 | A set of tests for the ESP32-C3's time driver implementation 2 | run using `cargo-espflash --monitor` -------------------------------------------------------------------------------- /test/test_time_driver/rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "nightly" 3 | --------------------------------------------------------------------------------