mirror of
https://github.com/esp-rs/esp-hal.git
synced 2025-09-28 12:50:53 +00:00
Assimilate lp-hal-procmacros
into esp-hal-procmacros
(#845)
* Assimilate `lp-hal-procmacros` into `esp-hal-procmacros` * Update `CHANGELOG.md`
This commit is contained in:
parent
f81dc50748
commit
c6af8ba88b
@ -25,9 +25,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- SYSTIMER ETM functionality (#828)
|
||||
- Adding async support for RSA peripheral(doesn't work properly for `esp32` chip - issue will be created)(#790)
|
||||
- Added sleep support for ESP32-C3 with timer and GPIO wakeups (#795)
|
||||
- Support for ULP-RISCV including Delay and GPIO (#840)
|
||||
- Support for ULP-RISCV including Delay and GPIO (#840, #845)
|
||||
- Add bare-bones SPI slave support, DMA only (#580, #843)
|
||||
- Embassy `#[main]` convenience macro
|
||||
- Embassy `#[main]` convenience macro (#841)
|
||||
|
||||
### Changed
|
||||
|
||||
|
@ -28,7 +28,7 @@ fugit = "0.3.7"
|
||||
log = { version = "0.4.20", optional = true }
|
||||
nb = "1.1.0"
|
||||
paste = "1.0.14"
|
||||
procmacros = { version = "0.6.1", package = "esp-hal-procmacros", path = "../esp-hal-procmacros" }
|
||||
procmacros = { version = "0.6.1", features = ["enum-dispatch", "ram"], package = "esp-hal-procmacros", path = "../esp-hal-procmacros" }
|
||||
strum = { version = "0.25.0", default-features = false, features = ["derive"] }
|
||||
void = { version = "1.0.2", default-features = false }
|
||||
usb-device = { version = "0.2.9", optional = true }
|
||||
|
@ -1,10 +1,6 @@
|
||||
[package]
|
||||
name = "esp-hal-procmacros"
|
||||
version = "0.6.1"
|
||||
authors = [
|
||||
"Jesse Braham <jesse@beta7.io>",
|
||||
"Björn Quentin <bjoern.quentin@mobile-j.de>",
|
||||
]
|
||||
name = "esp-hal-procmacros"
|
||||
version = "0.6.1"
|
||||
edition = "2021"
|
||||
rust-version = "1.67.0"
|
||||
description = "Procedural macros for ESP-HAL"
|
||||
@ -12,32 +8,37 @@ repository = "https://github.com/esp-rs/esp-hal"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
features = ["esp32c3", "interrupt"]
|
||||
features = ["esp32c3", "interrupt", "ram"]
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
darling = "0.20.3"
|
||||
proc-macro-crate = "1.3.1"
|
||||
proc-macro-error = "1.0.4"
|
||||
proc-macro2 = "1.0.66"
|
||||
quote = "1.0.33"
|
||||
syn = {version = "2.0.31", features = ["extra-traits", "full"]}
|
||||
object = {version = "0.32.1", optional = true}
|
||||
litrs = "0.4.0"
|
||||
# toml_edit is a dependency of proc-macro-crate. Unfortunately they raised their MSRV on 0.19.15 so we pin the version for now
|
||||
toml_edit = "=0.19.14"
|
||||
object = { version = "0.32.1", optional = true }
|
||||
proc-macro-crate = "2.0.0"
|
||||
proc-macro-error = "1.0.4"
|
||||
proc-macro2 = "1.0.69"
|
||||
quote = "1.0.33"
|
||||
syn = { version = "2.0.38", features = ["extra-traits", "full"] }
|
||||
|
||||
[features]
|
||||
esp32 = []
|
||||
esp32c2 = []
|
||||
esp32c3 = []
|
||||
esp32c6 = ["dep:object"]
|
||||
esp32h2 = []
|
||||
esp32s2 = ["dep:object"]
|
||||
esp32s3 = ["dep:object"]
|
||||
# Select a target device:
|
||||
esp32 = []
|
||||
esp32c2 = []
|
||||
esp32c3 = []
|
||||
esp32c6 = ["dep:object"]
|
||||
esp32c6-lp = []
|
||||
esp32h2 = []
|
||||
esp32s2 = ["dep:object"]
|
||||
esp32s2-ulp = []
|
||||
esp32s3 = ["dep:object"]
|
||||
esp32s3-ulp = []
|
||||
|
||||
interrupt = []
|
||||
rtc_slow = []
|
||||
embassy = []
|
||||
# Gated features:
|
||||
embassy = []
|
||||
enum-dispatch = []
|
||||
interrupt = []
|
||||
ram = []
|
||||
rtc_slow = []
|
||||
|
56
esp-hal-procmacros/src/enum_dispatch.rs
Normal file
56
esp-hal-procmacros/src/enum_dispatch.rs
Normal file
@ -0,0 +1,56 @@
|
||||
use proc_macro2::{Group, TokenTree};
|
||||
use syn::{
|
||||
parse::{Parse, ParseStream, Result},
|
||||
Ident,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct MakeGpioEnumDispatchMacro {
|
||||
pub name: String,
|
||||
pub filter: Vec<String>,
|
||||
pub elements: Vec<(String, usize)>,
|
||||
}
|
||||
|
||||
impl Parse for MakeGpioEnumDispatchMacro {
|
||||
fn parse(input: ParseStream) -> Result<Self> {
|
||||
let name = input.parse::<Ident>()?.to_string();
|
||||
let filter = input
|
||||
.parse::<Group>()?
|
||||
.stream()
|
||||
.into_iter()
|
||||
.map(|v| match v {
|
||||
TokenTree::Group(_) => String::new(),
|
||||
TokenTree::Ident(ident) => ident.to_string(),
|
||||
TokenTree::Punct(_) => String::new(),
|
||||
TokenTree::Literal(_) => String::new(),
|
||||
})
|
||||
.filter(|p| !p.is_empty())
|
||||
.collect();
|
||||
|
||||
let mut elements = vec![];
|
||||
|
||||
let mut stream = input.parse::<Group>()?.stream().into_iter();
|
||||
let mut element_name = String::new();
|
||||
loop {
|
||||
match stream.next() {
|
||||
Some(v) => match v {
|
||||
TokenTree::Ident(ident) => {
|
||||
element_name = ident.to_string();
|
||||
}
|
||||
TokenTree::Literal(lit) => {
|
||||
let index = lit.to_string().parse().unwrap();
|
||||
elements.push((element_name.clone(), index));
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
None => break,
|
||||
}
|
||||
}
|
||||
|
||||
Ok(MakeGpioEnumDispatchMacro {
|
||||
name,
|
||||
filter,
|
||||
elements,
|
||||
})
|
||||
}
|
||||
}
|
62
esp-hal-procmacros/src/interrupt.rs
Normal file
62
esp-hal-procmacros/src/interrupt.rs
Normal file
@ -0,0 +1,62 @@
|
||||
use proc_macro::{self, TokenStream};
|
||||
use syn::{parse::Error, spanned::Spanned, AttrStyle, Attribute};
|
||||
|
||||
pub(crate) enum WhiteListCaller {
|
||||
Interrupt,
|
||||
}
|
||||
|
||||
pub(crate) fn check_attr_whitelist(
|
||||
attrs: &[Attribute],
|
||||
caller: WhiteListCaller,
|
||||
) -> Result<(), TokenStream> {
|
||||
let whitelist = &[
|
||||
"doc",
|
||||
"link_section",
|
||||
"cfg",
|
||||
"allow",
|
||||
"warn",
|
||||
"deny",
|
||||
"forbid",
|
||||
"cold",
|
||||
"ram",
|
||||
"inline",
|
||||
];
|
||||
|
||||
'o: for attr in attrs {
|
||||
for val in whitelist {
|
||||
if eq(&attr, &val) {
|
||||
continue 'o;
|
||||
}
|
||||
}
|
||||
|
||||
let err_str = match caller {
|
||||
WhiteListCaller::Interrupt => {
|
||||
"this attribute is not allowed on an interrupt handler controlled by esp-hal"
|
||||
}
|
||||
};
|
||||
|
||||
return Err(Error::new(attr.span(), &err_str).to_compile_error().into());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn extract_cfgs(attrs: Vec<Attribute>) -> (Vec<Attribute>, Vec<Attribute>) {
|
||||
let mut cfgs = vec![];
|
||||
let mut not_cfgs = vec![];
|
||||
|
||||
for attr in attrs {
|
||||
if eq(&attr, "cfg") {
|
||||
cfgs.push(attr);
|
||||
} else {
|
||||
not_cfgs.push(attr);
|
||||
}
|
||||
}
|
||||
|
||||
(cfgs, not_cfgs)
|
||||
}
|
||||
|
||||
/// Returns `true` if `attr.path` matches `name`
|
||||
fn eq(attr: &Attribute, name: &str) -> bool {
|
||||
attr.style == AttrStyle::Outer && attr.path().is_ident(name)
|
||||
}
|
@ -47,34 +47,92 @@
|
||||
|
||||
#![doc(html_logo_url = "https://avatars.githubusercontent.com/u/46717278")]
|
||||
|
||||
use darling::{ast::NestedMeta, FromMeta};
|
||||
use proc_macro::{self, Span, TokenStream};
|
||||
use proc_macro2::Ident;
|
||||
use proc_macro_error::{abort, proc_macro_error};
|
||||
#[cfg(feature = "ram")]
|
||||
use darling::{ast::NestedMeta, Error as DarlingError, FromMeta};
|
||||
use proc_macro::TokenStream;
|
||||
use proc_macro_crate::FoundCrate;
|
||||
use proc_macro_error::proc_macro_error;
|
||||
use quote::quote;
|
||||
#[cfg(feature = "interrupt")]
|
||||
use syn::{
|
||||
parse,
|
||||
spanned::Spanned,
|
||||
AttrStyle,
|
||||
Attribute,
|
||||
ItemFn,
|
||||
Meta::Path,
|
||||
ReturnType,
|
||||
Type,
|
||||
Visibility,
|
||||
};
|
||||
use syn::{
|
||||
parse::{Parse, ParseStream},
|
||||
parse_macro_input,
|
||||
};
|
||||
use syn::parse_macro_input;
|
||||
|
||||
#[cfg(all(
|
||||
feature = "embassy",
|
||||
any(feature = "esp32", feature = "esp32s2", feature = "esp32s3")
|
||||
))]
|
||||
mod embassy_xtensa;
|
||||
#[cfg(feature = "enum-dispatch")]
|
||||
mod enum_dispatch;
|
||||
#[cfg(feature = "interrupt")]
|
||||
mod interrupt;
|
||||
#[cfg(any(
|
||||
feature = "esp32c6-lp",
|
||||
feature = "esp32s2-ulp",
|
||||
feature = "esp32s3-ulp"
|
||||
))]
|
||||
mod lp_core;
|
||||
|
||||
#[cfg(any(
|
||||
feature = "esp32c6",
|
||||
feature = "esp32s2",
|
||||
feature = "esp32s3",
|
||||
feature = "interrupt"
|
||||
))]
|
||||
fn get_hal_crate() -> (
|
||||
Result<FoundCrate, proc_macro_crate::Error>,
|
||||
proc_macro2::Ident,
|
||||
) {
|
||||
use proc_macro::Span;
|
||||
use proc_macro2::Ident;
|
||||
use proc_macro_crate::crate_name;
|
||||
|
||||
// Package name:
|
||||
#[cfg(feature = "esp32")]
|
||||
let hal_crate = crate_name("esp32-hal");
|
||||
#[cfg(feature = "esp32c2")]
|
||||
let hal_crate = crate_name("esp32c2-hal");
|
||||
#[cfg(feature = "esp32c3")]
|
||||
let hal_crate = crate_name("esp32c3-hal");
|
||||
#[cfg(feature = "esp32c6")]
|
||||
let hal_crate = crate_name("esp32c6-hal");
|
||||
#[cfg(feature = "esp32c6-lp")]
|
||||
let hal_crate = crate_name("esp32c6-lp-hal");
|
||||
#[cfg(feature = "esp32h2")]
|
||||
let hal_crate = crate_name("esp32h2-hal");
|
||||
#[cfg(feature = "esp32s2")]
|
||||
let hal_crate = crate_name("esp32s2-hal");
|
||||
#[cfg(feature = "esp32s2-ulp")]
|
||||
let hal_crate = crate_name("ulp-riscv-hal");
|
||||
#[cfg(feature = "esp32s3")]
|
||||
let hal_crate = crate_name("esp32s3-hal");
|
||||
#[cfg(feature = "esp32s3-ulp")]
|
||||
let hal_crate = crate_name("ulp-riscv-hal");
|
||||
|
||||
// Crate name:
|
||||
#[cfg(feature = "esp32")]
|
||||
let hal_crate_name = Ident::new("esp32_hal", Span::call_site().into());
|
||||
#[cfg(feature = "esp32c2")]
|
||||
let hal_crate_name = Ident::new("esp32c2_hal", Span::call_site().into());
|
||||
#[cfg(feature = "esp32c3")]
|
||||
let hal_crate_name = Ident::new("esp32c3_hal", Span::call_site().into());
|
||||
#[cfg(feature = "esp32c6")]
|
||||
let hal_crate_name = Ident::new("esp32c6_hal", Span::call_site().into());
|
||||
#[cfg(feature = "esp32c6-lp")]
|
||||
let hal_crate_name = Ident::new("esp32c6_lp_hal", Span::call_site().into());
|
||||
#[cfg(feature = "esp32h2")]
|
||||
let hal_crate_name = Ident::new("esp32h2_hal", Span::call_site().into());
|
||||
#[cfg(feature = "esp32s2")]
|
||||
let hal_crate_name = Ident::new("esp32s2_hal", Span::call_site().into());
|
||||
#[cfg(feature = "esp32s2-ulp")]
|
||||
let hal_crate_name = Ident::new("ulp_riscv_hal", Span::call_site().into());
|
||||
#[cfg(feature = "esp32s3")]
|
||||
let hal_crate_name = Ident::new("esp32s3_hal", Span::call_site().into());
|
||||
#[cfg(feature = "esp32s3-ulp")]
|
||||
let hal_crate_name = Ident::new("ulp_riscv_hal", Span::call_site().into());
|
||||
|
||||
(hal_crate, hal_crate_name)
|
||||
}
|
||||
|
||||
#[cfg(feature = "ram")]
|
||||
#[derive(Debug, Default, FromMeta)]
|
||||
#[darling(default)]
|
||||
struct RamArgs {
|
||||
@ -84,45 +142,6 @@ struct RamArgs {
|
||||
zeroed: bool,
|
||||
}
|
||||
|
||||
fn get_hal_crate() -> (
|
||||
Result<proc_macro_crate::FoundCrate, proc_macro_crate::Error>,
|
||||
proc_macro2::Ident,
|
||||
) {
|
||||
use proc_macro_crate::crate_name;
|
||||
|
||||
#[cfg(feature = "esp32")]
|
||||
let hal_crate = crate_name("esp32-hal");
|
||||
#[cfg(feature = "esp32s2")]
|
||||
let hal_crate = crate_name("esp32s2-hal");
|
||||
#[cfg(feature = "esp32s3")]
|
||||
let hal_crate = crate_name("esp32s3-hal");
|
||||
#[cfg(feature = "esp32c2")]
|
||||
let hal_crate = crate_name("esp32c2-hal");
|
||||
#[cfg(feature = "esp32c3")]
|
||||
let hal_crate = crate_name("esp32c3-hal");
|
||||
#[cfg(feature = "esp32c6")]
|
||||
let hal_crate = crate_name("esp32c6-hal");
|
||||
#[cfg(feature = "esp32h2")]
|
||||
let hal_crate = crate_name("esp32h2-hal");
|
||||
|
||||
#[cfg(feature = "esp32")]
|
||||
let hal_crate_name = Ident::new("esp32_hal", Span::call_site().into());
|
||||
#[cfg(feature = "esp32s2")]
|
||||
let hal_crate_name = Ident::new("esp32s2_hal", Span::call_site().into());
|
||||
#[cfg(feature = "esp32s3")]
|
||||
let hal_crate_name = Ident::new("esp32s3_hal", Span::call_site().into());
|
||||
#[cfg(feature = "esp32c2")]
|
||||
let hal_crate_name = Ident::new("esp32c2_hal", Span::call_site().into());
|
||||
#[cfg(feature = "esp32c3")]
|
||||
let hal_crate_name = Ident::new("esp32c3_hal", Span::call_site().into());
|
||||
#[cfg(feature = "esp32c6")]
|
||||
let hal_crate_name = Ident::new("esp32c6_hal", Span::call_site().into());
|
||||
#[cfg(feature = "esp32h2")]
|
||||
let hal_crate_name = Ident::new("esp32h2_hal", Span::call_site().into());
|
||||
|
||||
(hal_crate, hal_crate_name)
|
||||
}
|
||||
|
||||
/// This attribute allows placing statics and functions into ram.
|
||||
///
|
||||
/// Options that can be specified are rtc_slow or rtc_fast to use the
|
||||
@ -132,13 +151,17 @@ fn get_hal_crate() -> (
|
||||
/// (e.g. to persist it across resets or deep sleep mode for the RTC RAM)
|
||||
///
|
||||
/// Not all targets support RTC slow ram.
|
||||
#[cfg(feature = "ram")]
|
||||
#[proc_macro_attribute]
|
||||
#[proc_macro_error]
|
||||
pub fn ram(args: TokenStream, input: TokenStream) -> TokenStream {
|
||||
use proc_macro::Span;
|
||||
use proc_macro_error::abort;
|
||||
|
||||
let attr_args = match NestedMeta::parse_meta_list(args.into()) {
|
||||
Ok(v) => v,
|
||||
Err(e) => {
|
||||
return TokenStream::from(darling::Error::from(e).write_errors());
|
||||
return TokenStream::from(DarlingError::from(e).write_errors());
|
||||
}
|
||||
};
|
||||
|
||||
@ -200,6 +223,7 @@ pub fn ram(args: TokenStream, input: TokenStream) -> TokenStream {
|
||||
#section
|
||||
#item
|
||||
};
|
||||
|
||||
output.into()
|
||||
}
|
||||
|
||||
@ -231,7 +255,22 @@ pub fn ram(args: TokenStream, input: TokenStream) -> TokenStream {
|
||||
#[cfg(feature = "interrupt")]
|
||||
#[proc_macro_attribute]
|
||||
pub fn interrupt(args: TokenStream, input: TokenStream) -> TokenStream {
|
||||
use proc_macro_crate::FoundCrate;
|
||||
use std::iter;
|
||||
|
||||
use proc_macro::Span;
|
||||
use proc_macro2::Ident;
|
||||
use proc_macro_error::abort;
|
||||
use syn::{
|
||||
parse::Error as ParseError,
|
||||
spanned::Spanned,
|
||||
ItemFn,
|
||||
Meta,
|
||||
ReturnType,
|
||||
Type,
|
||||
Visibility,
|
||||
};
|
||||
|
||||
use self::interrupt::{check_attr_whitelist, extract_cfgs, WhiteListCaller};
|
||||
|
||||
let mut f: ItemFn = syn::parse(input).expect("`#[interrupt]` must be applied to a function");
|
||||
|
||||
@ -254,7 +293,7 @@ pub fn interrupt(args: TokenStream, input: TokenStream) -> TokenStream {
|
||||
|
||||
if attr_args.len() == 1 {
|
||||
match &attr_args[0] {
|
||||
NestedMeta::Meta(Path(x)) => {
|
||||
NestedMeta::Meta(Meta::Path(x)) => {
|
||||
ident_s = x.get_ident().unwrap();
|
||||
}
|
||||
_ => {
|
||||
@ -292,7 +331,7 @@ pub fn interrupt(args: TokenStream, input: TokenStream) -> TokenStream {
|
||||
&& f.sig.inputs.len() <= 1;
|
||||
|
||||
if !valid_signature {
|
||||
return parse::Error::new(
|
||||
return ParseError::new(
|
||||
f.span(),
|
||||
"`#[interrupt]` handlers must have signature `[unsafe] fn([&mut Context]) [-> !]`",
|
||||
)
|
||||
@ -320,7 +359,7 @@ pub fn interrupt(args: TokenStream, input: TokenStream) -> TokenStream {
|
||||
}
|
||||
};
|
||||
|
||||
f.block.stmts.extend(std::iter::once(
|
||||
f.block.stmts.extend(iter::once(
|
||||
syn::parse2(quote! {{
|
||||
// Check that this interrupt actually exists
|
||||
#interrupt_in_hal_crate;
|
||||
@ -371,133 +410,22 @@ pub fn interrupt(args: TokenStream, input: TokenStream) -> TokenStream {
|
||||
.into()
|
||||
}
|
||||
|
||||
#[cfg(feature = "interrupt")]
|
||||
enum WhiteListCaller {
|
||||
Interrupt,
|
||||
}
|
||||
|
||||
#[cfg(feature = "interrupt")]
|
||||
fn check_attr_whitelist(attrs: &[Attribute], caller: WhiteListCaller) -> Result<(), TokenStream> {
|
||||
let whitelist = &[
|
||||
"doc",
|
||||
"link_section",
|
||||
"cfg",
|
||||
"allow",
|
||||
"warn",
|
||||
"deny",
|
||||
"forbid",
|
||||
"cold",
|
||||
"ram",
|
||||
"inline",
|
||||
];
|
||||
|
||||
'o: for attr in attrs {
|
||||
for val in whitelist {
|
||||
if eq(&attr, &val) {
|
||||
continue 'o;
|
||||
}
|
||||
}
|
||||
|
||||
let err_str = match caller {
|
||||
WhiteListCaller::Interrupt => {
|
||||
"this attribute is not allowed on an interrupt handler controlled by esp-hal"
|
||||
}
|
||||
};
|
||||
|
||||
return Err(parse::Error::new(attr.span(), &err_str)
|
||||
.to_compile_error()
|
||||
.into());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns `true` if `attr.path` matches `name`
|
||||
#[cfg(feature = "interrupt")]
|
||||
fn eq(attr: &Attribute, name: &str) -> bool {
|
||||
attr.style == AttrStyle::Outer && attr.path().is_ident(name)
|
||||
}
|
||||
|
||||
#[cfg(feature = "interrupt")]
|
||||
fn extract_cfgs(attrs: Vec<Attribute>) -> (Vec<Attribute>, Vec<Attribute>) {
|
||||
let mut cfgs = vec![];
|
||||
let mut not_cfgs = vec![];
|
||||
|
||||
for attr in attrs {
|
||||
if eq(&attr, "cfg") {
|
||||
cfgs.push(attr);
|
||||
} else {
|
||||
not_cfgs.push(attr);
|
||||
}
|
||||
}
|
||||
|
||||
(cfgs, not_cfgs)
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct MakeGpioEnumDispatchMacro {
|
||||
name: String,
|
||||
filter: Vec<String>,
|
||||
elements: Vec<(String, usize)>,
|
||||
}
|
||||
|
||||
impl Parse for MakeGpioEnumDispatchMacro {
|
||||
fn parse(input: ParseStream) -> syn::parse::Result<Self> {
|
||||
let name = input.parse::<syn::Ident>()?.to_string();
|
||||
let filter = input
|
||||
.parse::<proc_macro2::Group>()?
|
||||
.stream()
|
||||
.into_iter()
|
||||
.map(|v| match v {
|
||||
proc_macro2::TokenTree::Group(_) => String::new(),
|
||||
proc_macro2::TokenTree::Ident(ident) => ident.to_string(),
|
||||
proc_macro2::TokenTree::Punct(_) => String::new(),
|
||||
proc_macro2::TokenTree::Literal(_) => String::new(),
|
||||
})
|
||||
.filter(|p| !p.is_empty())
|
||||
.collect();
|
||||
|
||||
let mut stream = input.parse::<proc_macro2::Group>()?.stream().into_iter();
|
||||
|
||||
let mut elements = vec![];
|
||||
|
||||
let mut element_name = String::new();
|
||||
loop {
|
||||
match stream.next() {
|
||||
Some(v) => match v {
|
||||
proc_macro2::TokenTree::Ident(ident) => {
|
||||
element_name = ident.to_string();
|
||||
}
|
||||
proc_macro2::TokenTree::Literal(lit) => {
|
||||
let index = lit.to_string().parse().unwrap();
|
||||
elements.push((element_name.clone(), index));
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
None => break,
|
||||
}
|
||||
}
|
||||
|
||||
Ok(MakeGpioEnumDispatchMacro {
|
||||
name,
|
||||
filter,
|
||||
elements,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Create an enum for erased GPIO pins, using the enum-dispatch pattern
|
||||
///
|
||||
/// Only used internally
|
||||
#[cfg(feature = "enum-dispatch")]
|
||||
#[proc_macro]
|
||||
pub fn make_gpio_enum_dispatch_macro(input: TokenStream) -> TokenStream {
|
||||
use quote::format_ident;
|
||||
|
||||
use self::enum_dispatch::MakeGpioEnumDispatchMacro;
|
||||
|
||||
let input = parse_macro_input!(input as MakeGpioEnumDispatchMacro);
|
||||
|
||||
let mut arms = Vec::new();
|
||||
|
||||
for (gpio_type, num) in input.elements {
|
||||
let enum_name = quote::format_ident!("ErasedPin");
|
||||
let variant_name = quote::format_ident!("Gpio{}", num);
|
||||
let enum_name = format_ident!("ErasedPin");
|
||||
let variant_name = format_ident!("Gpio{}", num);
|
||||
|
||||
if input.filter.contains(&gpio_type) {
|
||||
let arm = {
|
||||
@ -515,7 +443,7 @@ pub fn make_gpio_enum_dispatch_macro(input: TokenStream) -> TokenStream {
|
||||
}
|
||||
}
|
||||
|
||||
let macro_name = quote::format_ident!("{}", input.name);
|
||||
let macro_name = format_ident!("{}", input.name);
|
||||
|
||||
quote! {
|
||||
#[doc(hidden)]
|
||||
@ -533,7 +461,6 @@ pub fn make_gpio_enum_dispatch_macro(input: TokenStream) -> TokenStream {
|
||||
.into()
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "esp32c6", feature = "esp32s2", feature = "esp32s3"))]
|
||||
/// Load code to be run on the LP/ULP core.
|
||||
///
|
||||
/// ## Example
|
||||
@ -541,26 +468,16 @@ pub fn make_gpio_enum_dispatch_macro(input: TokenStream) -> TokenStream {
|
||||
/// let lp_core_code = load_lp_code!("path.elf");
|
||||
/// lp_core_code.run(&mut lp_core, lp_core::LpCoreWakeupSource::HpCpu, lp_pin);
|
||||
/// ````
|
||||
#[cfg(any(feature = "esp32c6", feature = "esp32s2", feature = "esp32s3"))]
|
||||
#[proc_macro]
|
||||
pub fn load_lp_code(input: TokenStream) -> TokenStream {
|
||||
use std::{fs, path};
|
||||
|
||||
use object::{Object, ObjectSection, ObjectSymbol};
|
||||
use proc_macro_crate::{crate_name, FoundCrate};
|
||||
#[cfg(not(feature = "interrupt"))]
|
||||
use proc_macro::Span;
|
||||
use syn::{parse, Ident};
|
||||
|
||||
#[cfg(feature = "esp32c6")]
|
||||
let hal_crate = crate_name("esp32c6-hal");
|
||||
#[cfg(feature = "esp32s2")]
|
||||
let hal_crate = crate_name("esp32s2-hal");
|
||||
#[cfg(feature = "esp32s3")]
|
||||
let hal_crate = crate_name("esp32s3-hal");
|
||||
|
||||
#[cfg(feature = "esp32c6")]
|
||||
let hal_crate_name = Ident::new("esp32c6_hal", Span::call_site().into());
|
||||
#[cfg(feature = "esp32s2")]
|
||||
let hal_crate_name = Ident::new("esp32s2_hal", Span::call_site().into());
|
||||
#[cfg(feature = "esp32s3")]
|
||||
let hal_crate_name = Ident::new("esp32s3_hal", Span::call_site().into());
|
||||
let (hal_crate, hal_crate_name) = get_hal_crate();
|
||||
|
||||
let hal_crate = match hal_crate {
|
||||
Ok(FoundCrate::Itself) => {
|
||||
@ -599,13 +516,13 @@ pub fn load_lp_code(input: TokenStream) -> TokenStream {
|
||||
};
|
||||
let elf_file = arg.value();
|
||||
|
||||
if !std::path::Path::new(elf_file).exists() {
|
||||
if !path::Path::new(elf_file).exists() {
|
||||
return parse::Error::new(Span::call_site().into(), "File not found")
|
||||
.to_compile_error()
|
||||
.into();
|
||||
}
|
||||
|
||||
let bin_data = std::fs::read(elf_file).unwrap();
|
||||
let bin_data = fs::read(elf_file).unwrap();
|
||||
let obj_file = object::File::parse(&*bin_data).unwrap();
|
||||
let sections = obj_file.sections();
|
||||
|
||||
@ -718,11 +635,105 @@ pub fn load_lp_code(input: TokenStream) -> TokenStream {
|
||||
.into()
|
||||
}
|
||||
|
||||
// just delegates to embassy's macro for RISC-V
|
||||
#[cfg(all(
|
||||
feature = "embassy",
|
||||
not(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3"))
|
||||
#[cfg(any(
|
||||
feature = "esp32c6-lp",
|
||||
feature = "esp32s2-ulp",
|
||||
feature = "esp32s3-ulp"
|
||||
))]
|
||||
#[proc_macro_error]
|
||||
#[proc_macro_attribute]
|
||||
pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream {
|
||||
use proc_macro2::{Ident, Span};
|
||||
use proc_macro_crate::crate_name;
|
||||
use quote::{format_ident, quote};
|
||||
use syn::{parse, parse_macro_input, spanned::Spanned, FnArg, ItemFn};
|
||||
|
||||
use self::lp_core::{extract_pin, get_simplename, make_magic_symbol_name};
|
||||
|
||||
#[cfg(feature = "esp32c6-lp")]
|
||||
let found_crate =
|
||||
crate_name("esp32c6-lp-hal").expect("esp32c6_lp_hal is present in `Cargo.toml`");
|
||||
#[cfg(any(feature = "esp32s2-ulp", feature = "esp32s3-ulp"))]
|
||||
let found_crate =
|
||||
crate_name("ulp-riscv-hal").expect("ulp-riscv-hal is present in `Cargo.toml`");
|
||||
|
||||
let hal_crate = match found_crate {
|
||||
#[cfg(feature = "esp32c6-lp")]
|
||||
FoundCrate::Itself => quote!(esp32c6_lp_hal),
|
||||
#[cfg(any(feature = "esp32s2-ulp", feature = "esp32s3-ulp"))]
|
||||
FoundCrate::Itself => quote!(ulp_riscv_hal),
|
||||
FoundCrate::Name(name) => {
|
||||
let ident = Ident::new(&name, Span::call_site());
|
||||
quote!( #ident::Something )
|
||||
}
|
||||
};
|
||||
|
||||
if !args.is_empty() {
|
||||
return parse::Error::new(Span::call_site(), "This attribute accepts no arguments")
|
||||
.to_compile_error()
|
||||
.into();
|
||||
}
|
||||
|
||||
let f = parse_macro_input!(input as ItemFn);
|
||||
|
||||
let mut argument_types = Vec::new();
|
||||
|
||||
let mut used_pins: Vec<u8> = Vec::new();
|
||||
for arg in &f.sig.inputs {
|
||||
match arg {
|
||||
FnArg::Receiver(_) => {
|
||||
return parse::Error::new(arg.span(), "invalid argument")
|
||||
.to_compile_error()
|
||||
.into();
|
||||
}
|
||||
FnArg::Typed(t) => {
|
||||
if get_simplename(&t.ty) != "GpioPin" {
|
||||
return parse::Error::new(arg.span(), "invalid argument to main")
|
||||
.to_compile_error()
|
||||
.into();
|
||||
}
|
||||
let pin = extract_pin(&t.ty);
|
||||
if used_pins.contains(&pin) {
|
||||
return parse::Error::new(arg.span(), "duplicate pin")
|
||||
.to_compile_error()
|
||||
.into();
|
||||
}
|
||||
used_pins.push(pin);
|
||||
|
||||
argument_types.push(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let magic_symbol_name = make_magic_symbol_name(&argument_types);
|
||||
|
||||
let param_names: Vec<Ident> = argument_types
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.map(|(num, _)| format_ident!("param{}", num))
|
||||
.collect();
|
||||
|
||||
quote!(
|
||||
#[allow(non_snake_case)]
|
||||
#[export_name = "main"]
|
||||
pub fn __risc_v_rt__main() -> ! {
|
||||
#[export_name = #magic_symbol_name]
|
||||
static ULP_MAGIC: [u32; 0] = [0u32; 0];
|
||||
|
||||
unsafe { ULP_MAGIC.as_ptr().read_volatile(); }
|
||||
|
||||
use #hal_crate as the_hal;
|
||||
#(
|
||||
let mut #param_names = unsafe { the_hal::gpio::conjour().unwrap() };
|
||||
)*
|
||||
|
||||
main(#(#param_names),*);
|
||||
}
|
||||
#f
|
||||
)
|
||||
.into()
|
||||
}
|
||||
|
||||
/// Creates a new `executor` instance and declares an application entry point
|
||||
/// spawning the corresponding function body as an async task.
|
||||
///
|
||||
@ -744,9 +755,13 @@ pub fn load_lp_code(input: TokenStream) -> TokenStream {
|
||||
/// // Function body
|
||||
/// }
|
||||
/// ```
|
||||
#[cfg(all(
|
||||
feature = "embassy",
|
||||
not(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3"))
|
||||
))]
|
||||
#[proc_macro_attribute]
|
||||
pub fn main(_args: TokenStream, input: TokenStream) -> TokenStream {
|
||||
let f = parse_macro_input!(input as ItemFn);
|
||||
let f = parse_macro_input!(input as syn::ItemFn);
|
||||
|
||||
let asyncness = f.sig.asyncness;
|
||||
let args = f.sig.inputs;
|
||||
@ -761,10 +776,6 @@ pub fn main(_args: TokenStream, input: TokenStream) -> TokenStream {
|
||||
.into()
|
||||
}
|
||||
|
||||
#[cfg(all(
|
||||
feature = "embassy",
|
||||
any(feature = "esp32", feature = "esp32s2", feature = "esp32s3")
|
||||
))]
|
||||
/// Creates a new `executor` instance and declares an application entry point
|
||||
/// spawning the corresponding function body as an async task.
|
||||
///
|
||||
@ -786,11 +797,19 @@ pub fn main(_args: TokenStream, input: TokenStream) -> TokenStream {
|
||||
/// // Function body
|
||||
/// }
|
||||
/// ```
|
||||
#[cfg(all(
|
||||
feature = "embassy",
|
||||
any(feature = "esp32", feature = "esp32s2", feature = "esp32s3")
|
||||
))]
|
||||
#[proc_macro_attribute]
|
||||
pub fn main(args: TokenStream, item: TokenStream) -> TokenStream {
|
||||
let args = syn::parse_macro_input!(args as embassy_xtensa::Args);
|
||||
let f = syn::parse_macro_input!(item as syn::ItemFn);
|
||||
embassy_xtensa::main::run(&args.meta, f, embassy_xtensa::main::main())
|
||||
.unwrap_or_else(|x| x)
|
||||
.into()
|
||||
use self::embassy_xtensa::{
|
||||
main::{main, run},
|
||||
Args,
|
||||
};
|
||||
|
||||
let args = parse_macro_input!(args as Args);
|
||||
let f = parse_macro_input!(item as syn::ItemFn);
|
||||
|
||||
run(&args.meta, f, main()).unwrap_or_else(|x| x).into()
|
||||
}
|
||||
|
71
esp-hal-procmacros/src/lp_core.rs
Normal file
71
esp-hal-procmacros/src/lp_core.rs
Normal file
@ -0,0 +1,71 @@
|
||||
use quote::quote;
|
||||
use syn::{GenericArgument, PatType, PathArguments, Type};
|
||||
|
||||
pub(crate) fn make_magic_symbol_name(args: &Vec<&PatType>) -> String {
|
||||
let mut res = String::from("__ULP_MAGIC_");
|
||||
for &a in args {
|
||||
let t = &a.ty;
|
||||
let quoted = to_string(&t);
|
||||
res.push_str("ed);
|
||||
res.push_str("$");
|
||||
}
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
pub(crate) fn get_simplename(t: &Type) -> String {
|
||||
String::from(match t {
|
||||
Type::Path(p) => String::from(&p.path.segments.last().unwrap().ident.to_string()),
|
||||
_ => String::new(),
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn extract_pin(ty: &Type) -> u8 {
|
||||
let mut res = 255u8;
|
||||
if let Type::Path(p) = ty {
|
||||
let segment = p.path.segments.last().unwrap();
|
||||
if let PathArguments::AngleBracketed(g) = &segment.arguments {
|
||||
for arg in &g.args {
|
||||
match arg {
|
||||
GenericArgument::Type(t) => {
|
||||
res = extract_pin(t);
|
||||
}
|
||||
GenericArgument::Const(c) => {
|
||||
res = ("e! { #c }.to_string()).parse().unwrap();
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
// This is a specialized implementation - won't fit other use-cases
|
||||
fn to_string(ty: &Type) -> String {
|
||||
let mut res = String::new();
|
||||
if let Type::Path(p) = ty {
|
||||
let segment = p.path.segments.last().unwrap();
|
||||
res.push_str(&segment.ident.to_string());
|
||||
|
||||
if let PathArguments::AngleBracketed(g) = &segment.arguments {
|
||||
res.push_str("<");
|
||||
for arg in &g.args {
|
||||
match arg {
|
||||
GenericArgument::Type(t) => {
|
||||
res.push_str(&to_string(t));
|
||||
}
|
||||
GenericArgument::Const(c) => {
|
||||
res.push_str(",");
|
||||
res.push_str("e! { #c }.to_string());
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
res.push_str(">");
|
||||
}
|
||||
}
|
||||
|
||||
res
|
||||
}
|
@ -24,7 +24,7 @@ categories = [
|
||||
critical-section = { version = "1.1.2", features = ["restore-state-u8"] }
|
||||
embedded-hal = { version = "0.2.7", features = ["unproven"] }
|
||||
esp32c6-lp = { git = "https://github.com/esp-rs/esp-pacs", rev = "a9cad5e", features = ["critical-section"] }
|
||||
lp-hal-procmacros = { path = "../lp-hal-procmacros", features = ["esp32c6"] }
|
||||
procmacros = { package = "esp-hal-procmacros", path = "../esp-hal-procmacros", features = ["esp32c6-lp"] }
|
||||
riscv = "0.10.1"
|
||||
paste = "1.0.14"
|
||||
|
||||
|
@ -12,4 +12,4 @@ pub use embedded_hal::{
|
||||
},
|
||||
prelude::*,
|
||||
};
|
||||
pub use lp_hal_procmacros::entry;
|
||||
pub use procmacros::entry;
|
||||
|
@ -1,36 +0,0 @@
|
||||
[package]
|
||||
name = "lp-hal-procmacros"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
rust-version = "1.67.0"
|
||||
description = "Procedural macros for the LP/ULP coprocessors"
|
||||
repository = "https://github.com/esp-rs/esp-hal"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
keywords = [
|
||||
"embedded",
|
||||
"embedded-hal",
|
||||
"esp",
|
||||
"esp32c6",
|
||||
"esp32s2",
|
||||
"esp32s3",
|
||||
"no-std",
|
||||
]
|
||||
categories = ["embedded", "hardware-support", "no-std"]
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
litrs = "0.4.0"
|
||||
proc-macro-crate = "1.3.1"
|
||||
proc-macro-error = "1.0.4"
|
||||
proc-macro2 = "1.0.63"
|
||||
quote = "1.0.28"
|
||||
syn = { version = "2.0.22", features = ["extra-traits", "full"] }
|
||||
toml_edit = "0.20.0"
|
||||
|
||||
[features]
|
||||
esp32c6 = []
|
||||
esp32s2 = []
|
||||
esp32s3 = []
|
@ -1,175 +0,0 @@
|
||||
use proc_macro::TokenStream;
|
||||
use proc_macro2::{Ident, Span};
|
||||
use proc_macro_crate::{crate_name, FoundCrate};
|
||||
use proc_macro_error::proc_macro_error;
|
||||
use quote::{format_ident, quote};
|
||||
use syn::{parse, parse_macro_input, spanned::Spanned, FnArg, ItemFn, PatType};
|
||||
|
||||
#[proc_macro_error]
|
||||
#[proc_macro_attribute]
|
||||
pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream {
|
||||
#[cfg(feature = "esp32c6")]
|
||||
let found_crate =
|
||||
crate_name("esp32c6-lp-hal").expect("esp32c6_lp_hal is present in `Cargo.toml`");
|
||||
#[cfg(any(feature = "esp32s2", feature = "esp32s3"))]
|
||||
let found_crate = crate_name("ulp-riscv-hal").expect("ulp-riscv-ha is present in `Cargo.toml`");
|
||||
|
||||
let hal_crate = match found_crate {
|
||||
#[cfg(feature = "esp32c6")]
|
||||
FoundCrate::Itself => quote!(esp32c6_lp_hal),
|
||||
#[cfg(any(feature = "esp32s2", feature = "esp32s3"))]
|
||||
FoundCrate::Itself => quote!(ulp_riscv_hal),
|
||||
FoundCrate::Name(name) => {
|
||||
let ident = Ident::new(&name, Span::call_site());
|
||||
quote!( #ident::Something )
|
||||
}
|
||||
};
|
||||
|
||||
if !args.is_empty() {
|
||||
return parse::Error::new(Span::call_site(), "This attribute accepts no arguments")
|
||||
.to_compile_error()
|
||||
.into();
|
||||
}
|
||||
|
||||
let f = parse_macro_input!(input as ItemFn);
|
||||
|
||||
let mut argument_types = Vec::new();
|
||||
|
||||
let mut used_pins: Vec<u8> = Vec::new();
|
||||
for arg in &f.sig.inputs {
|
||||
match arg {
|
||||
FnArg::Receiver(_) => {
|
||||
return parse::Error::new(arg.span(), "invalid argument")
|
||||
.to_compile_error()
|
||||
.into();
|
||||
}
|
||||
FnArg::Typed(t) => {
|
||||
if get_simplename(&t.ty) != "GpioPin" {
|
||||
return parse::Error::new(arg.span(), "invalid argument to main")
|
||||
.to_compile_error()
|
||||
.into();
|
||||
}
|
||||
let pin = extract_pin(&t.ty);
|
||||
if used_pins.contains(&pin) {
|
||||
return parse::Error::new(arg.span(), "duplicate pin")
|
||||
.to_compile_error()
|
||||
.into();
|
||||
}
|
||||
used_pins.push(pin);
|
||||
|
||||
argument_types.push(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let magic_symbol_name = make_magic_symbol_name(&argument_types);
|
||||
|
||||
let param_names: Vec<Ident> = argument_types
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.map(|(num, _)| format_ident!("param{}", num))
|
||||
.collect();
|
||||
|
||||
quote!(
|
||||
#[allow(non_snake_case)]
|
||||
#[export_name = "main"]
|
||||
pub fn __risc_v_rt__main() -> ! {
|
||||
#[export_name = #magic_symbol_name]
|
||||
static ULP_MAGIC: [u32; 0] = [0u32; 0];
|
||||
|
||||
unsafe { ULP_MAGIC.as_ptr().read_volatile(); }
|
||||
|
||||
use #hal_crate as the_hal;
|
||||
#(
|
||||
let mut #param_names = unsafe { the_hal::gpio::conjour().unwrap() };
|
||||
)*
|
||||
|
||||
main(#(#param_names),*);
|
||||
}
|
||||
#f
|
||||
)
|
||||
.into()
|
||||
}
|
||||
|
||||
fn make_magic_symbol_name(args: &Vec<&PatType>) -> String {
|
||||
let mut res = String::from("__ULP_MAGIC_");
|
||||
|
||||
for &a in args {
|
||||
let t = &a.ty;
|
||||
let quoted = to_string(&t);
|
||||
res.push_str("ed);
|
||||
res.push_str("$");
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
// this is a specialized implementation - won't fit other use-cases
|
||||
fn to_string(t: &syn::Type) -> String {
|
||||
let mut res = String::new();
|
||||
|
||||
match t {
|
||||
syn::Type::Path(p) => {
|
||||
let segment = p.path.segments.last().unwrap();
|
||||
res.push_str(&segment.ident.to_string());
|
||||
match &segment.arguments {
|
||||
syn::PathArguments::None => (),
|
||||
syn::PathArguments::Parenthesized(_) => (),
|
||||
syn::PathArguments::AngleBracketed(g) => {
|
||||
res.push_str("<");
|
||||
for arg in &g.args {
|
||||
match arg {
|
||||
syn::GenericArgument::Type(t) => {
|
||||
res.push_str(&to_string(t));
|
||||
}
|
||||
syn::GenericArgument::Const(c) => {
|
||||
res.push_str(",");
|
||||
res.push_str("e! { #c }.to_string());
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
res.push_str(">");
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
fn get_simplename(t: &syn::Type) -> String {
|
||||
String::from(match t {
|
||||
syn::Type::Path(p) => String::from(&p.path.segments.last().unwrap().ident.to_string()),
|
||||
_ => String::new(),
|
||||
})
|
||||
}
|
||||
|
||||
fn extract_pin(t: &syn::Type) -> u8 {
|
||||
let mut res = 255u8;
|
||||
|
||||
match t {
|
||||
syn::Type::Path(p) => {
|
||||
let segment = p.path.segments.last().unwrap();
|
||||
match &segment.arguments {
|
||||
syn::PathArguments::None => (),
|
||||
syn::PathArguments::Parenthesized(_) => (),
|
||||
syn::PathArguments::AngleBracketed(g) => {
|
||||
for arg in &g.args {
|
||||
match arg {
|
||||
syn::GenericArgument::Type(t) => {
|
||||
res = extract_pin(t);
|
||||
}
|
||||
syn::GenericArgument::Const(c) => {
|
||||
res = ("e! { #c }.to_string()).parse().unwrap();
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
res
|
||||
}
|
@ -23,10 +23,10 @@ categories = [
|
||||
|
||||
[dependencies]
|
||||
embedded-hal = { version = "0.2.7", features = ["unproven"] }
|
||||
lp-hal-procmacros = { path = "../lp-hal-procmacros" }
|
||||
procmacros = { package = "esp-hal-procmacros", path = "../esp-hal-procmacros" }
|
||||
paste = "1.0.14"
|
||||
esp32s2-ulp = { git = "https://github.com/esp-rs/esp-pacs", package = "esp32s2-ulp", optional = true }
|
||||
esp32s3-ulp = { git = "https://github.com/esp-rs/esp-pacs", package = "esp32s3-ulp", optional = true }
|
||||
esp32s2-ulp = { git = "https://github.com/esp-rs/esp-pacs", package = "esp32s2-ulp", optional = true }
|
||||
esp32s3-ulp = { git = "https://github.com/esp-rs/esp-pacs", package = "esp32s3-ulp", optional = true }
|
||||
|
||||
|
||||
[dev-dependencies]
|
||||
@ -35,5 +35,5 @@ panic-halt = "0.2.0"
|
||||
[features]
|
||||
default = []
|
||||
debug = []
|
||||
esp32s2 = [ "dep:esp32s2-ulp", "lp-hal-procmacros/esp32s2" ]
|
||||
esp32s3 = [ "dep:esp32s3-ulp", "lp-hal-procmacros/esp32s3" ]
|
||||
esp32s2 = [ "dep:esp32s2-ulp", "procmacros/esp32s2-ulp" ]
|
||||
esp32s3 = [ "dep:esp32s3-ulp", "procmacros/esp32s3-ulp" ]
|
||||
|
@ -12,4 +12,4 @@ pub use embedded_hal::{
|
||||
},
|
||||
prelude::*,
|
||||
};
|
||||
pub use lp_hal_procmacros::entry;
|
||||
pub use procmacros::entry;
|
||||
|
Loading…
x
Reference in New Issue
Block a user