Ability to precompute/cache config register values (#3011)

* Cache bus clock register and support clock source

* Recalculate eagerly to keep Config Copy

* Destabilize UART clock source

* Update I2C

* Changelog

* Refactor builder lite helpers for more flexibility

* Flatten config structs

* Add getters, hide fields

* Hide all config fields

* Fix changelog

* Document the precomputation
This commit is contained in:
Dániel Buga 2025-01-30 10:44:52 +01:00 committed by GitHub
parent bb1ad1485a
commit af6ee36d93
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
27 changed files with 579 additions and 450 deletions

View File

@ -9,6 +9,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
- Added `#[builder_lite(into)]` attribute that generates the setter with `impl Into<T>` parameter (#3011)
- Added `#[builder_lite(skip_setter)]` and `#[builder_lite(skip_getter)]` attribute to skip generating setters or getters (#3011)
- Added `#[builder_lite(skip)]` to ignore a field completely (#3011)
### Changed
### Fixed

View File

@ -2,16 +2,31 @@ use proc_macro::TokenStream;
use quote::{format_ident, quote};
use syn::{
parse::Error as ParseError,
punctuated::Punctuated,
spanned::Spanned,
Attribute,
Data,
DataStruct,
GenericArgument,
Ident,
Path,
PathArguments,
PathSegment,
Token,
Type,
};
const KNOWN_HELPERS: &[&str] = &[
// Generate the setter with `impl Into<FieldType>` as the argument
"into",
// Do not generate a getter or setter
"skip",
// Do not generate a setter
"skip_setter",
// Do not generate a getter
"skip_getter",
];
pub fn builder_lite_derive(item: TokenStream) -> TokenStream {
let input = syn::parse_macro_input!(item as syn::DeriveInput);
@ -19,34 +34,60 @@ pub fn builder_lite_derive(item: TokenStream) -> TokenStream {
let ident = input.ident;
let mut fns = Vec::new();
if let Data::Struct(DataStruct { fields, .. }) = &input.data {
for field in fields {
let field_ident = field.ident.as_ref().unwrap();
let field_type = &field.ty;
let Data::Struct(DataStruct { fields, .. }) = &input.data else {
return ParseError::new(
span,
"#[derive(Builder)] is only defined for structs, not for enums or unions!",
)
.to_compile_error()
.into();
};
for field in fields {
let helper_attributes = match collect_helper_attrs(&field.attrs) {
Ok(attr) => attr,
Err(err) => return err.to_compile_error().into(),
};
let function_ident = format_ident!("with_{}", field_ident);
// Ignore field if it has a `skip` helper attribute.
if helper_attributes.iter().any(|h| h == "skip") {
continue;
}
let maybe_path_type = extract_type_path(field_type)
.and_then(|path| extract_option_segment(path))
.and_then(|path_seg| match path_seg.arguments {
PathArguments::AngleBracketed(ref params) => params.args.first(),
_ => None,
})
.and_then(|generic_arg| match *generic_arg {
GenericArgument::Type(ref ty) => Some(ty),
_ => None,
});
let field_ident = field.ident.as_ref().unwrap();
let field_type = &field.ty;
let (field_type, field_assigns) = if let Some(inner_type) = maybe_path_type {
(inner_type, quote! { Some(#field_ident) })
} else {
(field_type, quote! { #field_ident })
};
let function_ident = format_ident!("with_{}", field_ident);
let maybe_path_type = extract_type_path(field_type)
.and_then(|path| extract_option_segment(path))
.and_then(|path_seg| match path_seg.arguments {
PathArguments::AngleBracketed(ref params) => params.args.first(),
_ => None,
})
.and_then(|generic_arg| match *generic_arg {
GenericArgument::Type(ref ty) => Some(ty),
_ => None,
});
let (mut field_setter_type, mut field_assigns) = if let Some(inner_type) = maybe_path_type {
(quote! { #inner_type }, quote! { Some(#field_ident) })
} else {
(quote! { #field_type }, quote! { #field_ident })
};
// Wrap type and assignment with `Into` if needed.
if helper_attributes.iter().any(|h| h == "into") {
field_setter_type = quote! { impl Into<#field_setter_type> };
field_assigns = quote! { #field_ident .into() };
} else {
field_setter_type = field_setter_type.clone();
}
if !helper_attributes.iter().any(|h| h == "skip_setter") {
fns.push(quote! {
#[doc = concat!(" Assign the given value to the `", stringify!(#field_ident) ,"` field.")]
#[must_use]
pub fn #function_ident(mut self, #field_ident: #field_type) -> Self {
pub fn #function_ident(mut self, #field_ident: #field_setter_type) -> Self {
self.#field_ident = #field_assigns;
self
}
@ -64,13 +105,27 @@ pub fn builder_lite_derive(item: TokenStream) -> TokenStream {
});
}
}
} else {
return ParseError::new(
span,
"#[derive(Builder)] is only defined for structs, not for enums or unions!",
)
.to_compile_error()
.into();
if !helper_attributes.iter().any(|h| h == "skip_getter") {
let docs = field.attrs.iter().filter_map(|attr| {
let syn::Meta::NameValue(ref attr) = attr.meta else {
return None;
};
if attr.path.is_ident("doc") {
let docstr = &attr.value;
Some(quote! { #[doc = #docstr] })
} else {
None
}
});
fns.push(quote! {
#(#docs)*
pub fn #field_ident(&self) -> #field_type {
self.#field_ident
}
});
}
}
let implementation = quote! {
@ -104,3 +159,30 @@ fn extract_option_segment(path: &Path) -> Option<&PathSegment> {
.find(|s| idents_of_path == *s)
.and_then(|_| path.segments.last())
}
fn collect_helper_attrs(attrs: &[Attribute]) -> Result<Vec<Ident>, syn::Error> {
let mut helper_attributes = Vec::new();
for attr in attrs
.iter()
.filter(|attr| attr.path().is_ident("builder_lite"))
{
for helper in attr.parse_args_with(|input: syn::parse::ParseStream| {
Punctuated::<Ident, Token![,]>::parse_terminated(input)
})? {
if !KNOWN_HELPERS.iter().any(|known| helper == *known) {
return Err(ParseError::new(
helper.span(),
format!(
"Unknown helper attribute `{}`. Only the following are allowed: {}",
helper,
KNOWN_HELPERS.join(", ")
),
));
}
helper_attributes.push(helper);
}
}
Ok(helper_attributes)
}

View File

@ -244,7 +244,7 @@ pub fn blocking_main(args: TokenStream, input: TokenStream) -> TokenStream {
/// ```
///
/// [Builder Lite]: https://matklad.github.io/2022/05/29/builder-lite.html
#[proc_macro_derive(BuilderLite)]
#[proc_macro_derive(BuilderLite, attributes(builder_lite))]
pub fn builder_lite_derive(item: TokenStream) -> TokenStream {
builder::builder_lite_derive(item)
}

View File

@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
- SPI: Added support for 3-wire SPI (#2919)
- UART: Add separate config for Rx and Tx (#2965)
- Added accessor methods to config structs (#3011)
### Changed
@ -43,6 +44,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Removed `Pin`, `RtcPin` and `RtcPinWithResistors` implementations from `Flex` (#2938)
- OutputOpenDrain has been removed (#3029)
- The fields of config structs are no longer public (#3011)
## [0.23.1] - 2025-01-15

View File

@ -107,6 +107,18 @@ e.g.
+ uart.read_bytes(&mut byte);
```
### UART halves have their configuration split too
`Uart::Config` structure now contains separate `RxConfig` and `TxConfig`:
```diff
- let config = Config::default().with_rx_fifo_full_threshold(30);
+ let config = Config::default()
+ .with_rx(RxConfig::default()
+ .with_fifo_full_threshold(30)
+ );
```
## `timer::wait` is now blocking
```diff
@ -162,19 +174,6 @@ config/config.toml
+ ESP_HAL_CONFIG_PSRAM_MODE = "octal"
```
## UART halves have their configuration split too
`Uart::Config` structure now contains separate `RxConfig` and `TxConfig`:
```diff
- let config = Config::default().with_rx_fifo_full_threshold(30);
+ let config = Config::default()
+ .with_rx(RxConfig::default()
+ .with_fifo_full_threshold(30)
+ );
```
## GPIO changes
GPIO drivers now take configuration structs.

View File

@ -33,7 +33,7 @@
//! ```
/// Watchdog status.
#[derive(Default, PartialEq)]
#[derive(Default, PartialEq, Clone, Copy)]
pub enum WatchdogStatus {
/// Enables a watchdog timer with the specified timeout.
Enabled(fugit::MicrosDurationU64),
@ -44,19 +44,19 @@ pub enum WatchdogStatus {
/// Watchdog configuration.
#[non_exhaustive]
#[derive(Default, procmacros::BuilderLite)]
#[derive(Default, Clone, Copy, procmacros::BuilderLite)]
pub struct WatchdogConfig {
#[cfg(not(any(esp32, esp32s2)))]
/// Enable the super watchdog timer, which has a trigger time of slightly
/// less than one second.
pub swd: bool,
swd: bool,
/// Configures the reset watchdog timer.
pub rwdt: WatchdogStatus,
rwdt: WatchdogStatus,
/// Configures the `timg0` watchdog timer.
pub timg0: WatchdogStatus,
timg0: WatchdogStatus,
#[cfg(timg1)]
/// Configures the `timg1` watchdog timer.
///
/// By default, the bootloader does not enable this watchdog timer.
pub timg1: WatchdogStatus,
timg1: WatchdogStatus,
}

View File

@ -1115,13 +1115,13 @@ pub enum DriveMode {
#[non_exhaustive]
pub struct OutputConfig {
/// Output drive mode.
pub drive_mode: DriveMode,
drive_mode: DriveMode,
/// Pin drive strength.
pub drive_strength: DriveStrength,
drive_strength: DriveStrength,
/// Pin pull direction.
pub pull: Pull,
pull: Pull,
}
impl Default for OutputConfig {
@ -1332,7 +1332,7 @@ impl<'d> Output<'d> {
#[non_exhaustive]
pub struct InputConfig {
/// Initial pull of the pin.
pub pull: Pull,
pull: Pull,
}
impl Default for InputConfig {

View File

@ -396,10 +396,10 @@ impl From<Ack> for u32 {
#[non_exhaustive]
pub struct Config {
/// The I2C clock frequency.
pub frequency: HertzU32,
frequency: HertzU32,
/// I2C SCL timeout period.
pub timeout: BusTimeout,
timeout: BusTimeout,
}
impl core::hash::Hash for Config {
@ -1399,17 +1399,7 @@ impl Driver<'_> {
set_filter(self.regs(), Some(7), Some(7));
// Configure frequency
let clocks = Clocks::get();
cfg_if::cfg_if! {
if #[cfg(esp32)] {
let clock = clocks.i2c_clock.convert();
} else if #[cfg(esp32s2)] {
let clock = clocks.apb_clock.convert();
} else {
let clock = clocks.xtal_clock.convert();
}
}
self.set_frequency(clock, config.frequency, config.timeout)?;
self.set_frequency(config, config.timeout)?;
self.update_config();
@ -1451,14 +1441,10 @@ impl Driver<'_> {
/// Sets the frequency of the I2C interface by calculating and applying the
/// associated timings - corresponds to i2c_ll_cal_bus_clk and
/// i2c_ll_set_bus_timing in ESP-IDF
fn set_frequency(
&self,
source_clk: HertzU32,
bus_freq: HertzU32,
timeout: BusTimeout,
) -> Result<(), ConfigError> {
let source_clk = source_clk.raw();
let bus_freq = bus_freq.raw();
fn set_frequency(&self, clock_config: &Config, timeout: BusTimeout) -> Result<(), ConfigError> {
let clocks = Clocks::get();
let source_clk = clocks.i2c_clock.raw();
let bus_freq = clock_config.frequency.raw();
let half_cycle: u32 = source_clk / bus_freq / 2;
let scl_low = half_cycle;
@ -1533,14 +1519,10 @@ impl Driver<'_> {
/// Sets the frequency of the I2C interface by calculating and applying the
/// associated timings - corresponds to i2c_ll_cal_bus_clk and
/// i2c_ll_set_bus_timing in ESP-IDF
fn set_frequency(
&self,
source_clk: HertzU32,
bus_freq: HertzU32,
timeout: BusTimeout,
) -> Result<(), ConfigError> {
let source_clk = source_clk.raw();
let bus_freq = bus_freq.raw();
fn set_frequency(&self, clock_config: &Config, timeout: BusTimeout) -> Result<(), ConfigError> {
let clocks = Clocks::get();
let source_clk = clocks.apb_clock.raw();
let bus_freq = clock_config.frequency.raw();
let half_cycle: u32 = source_clk / bus_freq / 2;
// SCL
@ -1595,14 +1577,10 @@ impl Driver<'_> {
/// Sets the frequency of the I2C interface by calculating and applying the
/// associated timings - corresponds to i2c_ll_cal_bus_clk and
/// i2c_ll_set_bus_timing in ESP-IDF
fn set_frequency(
&self,
source_clk: HertzU32,
bus_freq: HertzU32,
timeout: BusTimeout,
) -> Result<(), ConfigError> {
let source_clk = source_clk.raw();
let bus_freq = bus_freq.raw();
fn set_frequency(&self, clock_config: &Config, timeout: BusTimeout) -> Result<(), ConfigError> {
let clocks = Clocks::get();
let source_clk = clocks.xtal_clock.raw();
let bus_freq = clock_config.frequency.raw();
let clkm_div: u32 = source_clk / (bus_freq * 1024) + 1;
let sclk_freq: u32 = source_clk / clkm_div;

View File

@ -37,8 +37,7 @@
//! peripherals.GPIO16,
//! );
//!
//! let mut config = Config::default();
//! config.frequency = 20.MHz();
//! let config = Config::default().with_frequency(20.MHz());
//!
//! let lcd_cam = LcdCam::new(peripherals.LCD_CAM);
//! let mut camera = Camera::new(
@ -599,21 +598,21 @@ pub trait RxPins {
const BUS_WIDTH: usize;
}
#[derive(Debug, Clone, Copy, PartialEq)]
#[derive(Debug, Clone, Copy, PartialEq, procmacros::BuilderLite)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
/// Configuration settings for the Camera interface.
pub struct Config {
/// The pixel clock frequency for the camera interface.
pub frequency: HertzU32,
frequency: HertzU32,
/// The byte order for the camera data.
pub byte_order: ByteOrder,
byte_order: ByteOrder,
/// The bit order for the camera data.
pub bit_order: BitOrder,
bit_order: BitOrder,
/// The Vsync filter threshold.
pub vsync_filter_threshold: Option<VsyncFilterThreshold>,
vsync_filter_threshold: Option<VsyncFilterThreshold>,
}
impl Default for Config {

View File

@ -30,34 +30,34 @@
//!
//! let lcd_cam = LcdCam::new(peripherals.LCD_CAM);
//!
//! let mut config = dpi::Config::default();
//! config.frequency = 1.MHz();
//! config.clock_mode = ClockMode {
//! polarity: Polarity::IdleLow,
//! phase: Phase::ShiftLow,
//! };
//! config.format = Format {
//! enable_2byte_mode: true,
//! ..Default::default()
//! };
//! config.timing = FrameTiming {
//! horizontal_active_width: 480,
//! horizontal_total_width: 520,
//! horizontal_blank_front_porch: 10,
//! let config = dpi::Config::default()
//! .with_frequency(1.MHz())
//! .with_clock_mode(ClockMode {
//! polarity: Polarity::IdleLow,
//! phase: Phase::ShiftLow,
//! })
//! .with_format(Format {
//! enable_2byte_mode: true,
//! ..Default::default()
//! })
//! .with_timing(FrameTiming {
//! horizontal_active_width: 480,
//! horizontal_total_width: 520,
//! horizontal_blank_front_porch: 10,
//!
//! vertical_active_height: 480,
//! vertical_total_height: 510,
//! vertical_blank_front_porch: 10,
//! vertical_active_height: 480,
//! vertical_total_height: 510,
//! vertical_blank_front_porch: 10,
//!
//! hsync_width: 10,
//! vsync_width: 10,
//! hsync_width: 10,
//! vsync_width: 10,
//!
//! hsync_position: 0,
//! };
//! config.vsync_idle_level = Level::High;
//! config.hsync_idle_level = Level::High;
//! config.de_idle_level = Level::Low;
//! config.disable_black_region = false;
//! hsync_position: 0,
//! })
//! .with_vsync_idle_level(Level::High)
//! .with_hsync_idle_level(Level::High)
//! .with_de_idle_level(Level::Low)
//! .with_disable_black_region(false);
//!
//! let mut dpi = Dpi::new(lcd_cam.lcd, channel, config).unwrap()
//! .with_vsync(peripherals.GPIO3)
@ -694,46 +694,46 @@ impl<BUF: DmaTxBuffer, Dm: DriverMode> Drop for DpiTransfer<'_, BUF, Dm> {
/// Configuration settings for the RGB/DPI interface.
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq)]
#[derive(Debug, Clone, Copy, PartialEq, procmacros::BuilderLite)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Config {
/// Specifies the clock mode, including polarity and phase settings.
pub clock_mode: ClockMode,
clock_mode: ClockMode,
/// The frequency of the pixel clock.
pub frequency: HertzU32,
frequency: HertzU32,
/// Format of the byte data sent out.
pub format: Format,
format: Format,
/// Timing settings for the peripheral.
pub timing: FrameTiming,
timing: FrameTiming,
/// The vsync signal level in IDLE state.
pub vsync_idle_level: Level,
vsync_idle_level: Level,
/// The hsync signal level in IDLE state.
pub hsync_idle_level: Level,
hsync_idle_level: Level,
/// The de signal level in IDLE state.
pub de_idle_level: Level,
de_idle_level: Level,
/// If enabled, the hsync pulse will be sent out in vertical blanking lines.
/// i.e. When no valid data is actually sent out. Otherwise, hysnc
/// pulses will only be sent out in active region lines.
pub hs_blank_en: bool,
hs_blank_en: bool,
/// Disables blank region when LCD sends data out.
pub disable_black_region: bool,
disable_black_region: bool,
/// The output LCD_DE is delayed by module clock LCD_CLK.
pub de_mode: DelayMode,
de_mode: DelayMode,
/// The output LCD_HSYNC is delayed by module clock LCD_CLK.
pub hsync_mode: DelayMode,
hsync_mode: DelayMode,
/// The output LCD_VSYNC is delayed by module clock LCD_CLK.
pub vsync_mode: DelayMode,
vsync_mode: DelayMode,
/// The output data bits are delayed by module clock LCD_CLK.
pub output_bit_mode: DelayMode,
output_bit_mode: DelayMode,
}
impl Default for Config {

View File

@ -33,8 +33,7 @@
//! );
//! let lcd_cam = LcdCam::new(peripherals.LCD_CAM);
//!
//! let mut config = Config::default();
//! config.frequency = 20.MHz();
//! let config = Config::default().with_frequency(20.MHz());
//!
//! let mut i8080 = I8080::new(
//! lcd_cam.lcd,
@ -540,35 +539,35 @@ impl<BUF: DmaTxBuffer, Dm: DriverMode> Drop for I8080Transfer<'_, BUF, Dm> {
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
#[derive(Debug, Clone, Copy, PartialEq, procmacros::BuilderLite)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
/// Configuration settings for the I8080 interface.
pub struct Config {
/// Specifies the clock mode, including polarity and phase settings.
pub clock_mode: ClockMode,
clock_mode: ClockMode,
/// The frequency of the pixel clock.
pub frequency: HertzU32,
frequency: HertzU32,
/// Setup cycles expected, must be at least 1. (6 bits)
pub setup_cycles: usize,
setup_cycles: usize,
/// Hold cycles expected, must be at least 1. (13 bits)
pub hold_cycles: usize,
hold_cycles: usize,
/// The default value of LCD_CD.
pub cd_idle_edge: bool,
cd_idle_edge: bool,
/// The value of LCD_CD during CMD phase.
pub cd_cmd_edge: bool,
cd_cmd_edge: bool,
/// The value of LCD_CD during dummy phase.
pub cd_dummy_edge: bool,
cd_dummy_edge: bool,
/// The value of LCD_CD during data phase.
pub cd_data_edge: bool,
cd_data_edge: bool,
/// The output LCD_CD is delayed by module clock LCD_CLK.
pub cd_mode: DelayMode,
cd_mode: DelayMode,
/// The output data bits are delayed by module clock LCD_CLK.
pub output_bit_mode: DelayMode,
output_bit_mode: DelayMode,
}
impl Default for Config {

View File

@ -538,21 +538,21 @@ use crate::{
///
/// For usage examples, see the [config module documentation](crate::config).
#[non_exhaustive]
#[derive(Default, procmacros::BuilderLite)]
#[derive(Default, Clone, Copy, procmacros::BuilderLite)]
pub struct Config {
/// The CPU clock configuration.
pub cpu_clock: CpuClock,
cpu_clock: CpuClock,
/// Enable watchdog timer(s).
#[cfg(any(doc, feature = "unstable"))]
#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
pub watchdog: WatchdogConfig,
watchdog: WatchdogConfig,
/// PSRAM configuration.
#[cfg(any(doc, feature = "unstable"))]
#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
#[cfg(feature = "psram")]
pub psram: psram::PsramConfig,
psram: psram::PsramConfig,
}
/// Initialize the system.
@ -572,13 +572,13 @@ pub fn init(config: Config) -> Peripherals {
if #[cfg(feature = "unstable")]
{
#[cfg(not(any(esp32, esp32s2)))]
if config.watchdog.swd {
if config.watchdog.swd() {
rtc.swd.enable();
} else {
rtc.swd.disable();
}
match config.watchdog.rwdt {
match config.watchdog.rwdt() {
WatchdogStatus::Enabled(duration) => {
rtc.rwdt.enable();
rtc.rwdt
@ -589,7 +589,7 @@ pub fn init(config: Config) -> Peripherals {
}
}
match config.watchdog.timg0 {
match config.watchdog.timg0() {
WatchdogStatus::Enabled(duration) => {
let mut timg0_wd = crate::timer::timg::Wdt::<crate::peripherals::TIMG0>::new();
timg0_wd.enable();
@ -601,7 +601,7 @@ pub fn init(config: Config) -> Peripherals {
}
#[cfg(timg1)]
match config.watchdog.timg1 {
match config.watchdog.timg1() {
WatchdogStatus::Enabled(duration) => {
let mut timg1_wd = crate::timer::timg::Wdt::<crate::peripherals::TIMG1>::new();
timg1_wd.enable();

View File

@ -324,19 +324,19 @@ impl PulseCode for u32 {
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct TxChannelConfig {
/// Channel's clock divider
pub clk_divider: u8,
clk_divider: u8,
/// Set the idle output level to low/high
pub idle_output_level: Level,
idle_output_level: Level,
/// Enable idle output
pub idle_output: bool,
idle_output: bool,
/// Enable carrier modulation
pub carrier_modulation: bool,
carrier_modulation: bool,
/// Carrier high phase in ticks
pub carrier_high: u16,
carrier_high: u16,
/// Carrier low phase in ticks
pub carrier_low: u16,
carrier_low: u16,
/// Level of the carrier
pub carrier_level: Level,
carrier_level: Level,
}
impl Default for TxChannelConfig {
@ -358,19 +358,19 @@ impl Default for TxChannelConfig {
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct RxChannelConfig {
/// Channel's clock divider
pub clk_divider: u8,
clk_divider: u8,
/// Enable carrier demodulation
pub carrier_modulation: bool,
carrier_modulation: bool,
/// Carrier high phase in ticks
pub carrier_high: u16,
carrier_high: u16,
/// Carrier low phase in ticks
pub carrier_low: u16,
carrier_low: u16,
/// Level of the carrier
pub carrier_level: Level,
carrier_level: Level,
/// Filter threshold in ticks
pub filter_threshold: u8,
filter_threshold: u8,
/// Idle threshold in ticks
pub idle_threshold: u16,
idle_threshold: u16,
}
impl Default for RxChannelConfig {

View File

@ -41,7 +41,7 @@ pub use dma::*;
#[cfg(any(doc, feature = "unstable"))]
use embassy_embedded_hal::SetConfig;
use enumset::{EnumSet, EnumSetType};
use fugit::HertzU32;
use fugit::{HertzU32, RateExtU32};
#[cfg(place_spi_driver_in_ram)]
use procmacros::ram;
@ -416,42 +416,187 @@ impl Address {
}
}
/// SPI clock source.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[instability::unstable]
pub enum ClockSource {
/// Use the APB clock.
Apb,
// #[cfg(any(esp32c2, esp32c3, esp32s3))]
// Xtal,
}
/// SPI peripheral configuration
#[derive(Clone, Copy, Debug, PartialEq, Eq, procmacros::BuilderLite)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub struct Config {
/// SPI bus clock frequency.
pub frequency: HertzU32,
/// The precomputed clock configuration register value.
///
/// Clock divider calculations are relatively expensive, and the SPI
/// peripheral is commonly expected to be used in a shared bus
/// configuration, where different devices may need different bus clock
/// frequencies. To reduce the time required to reconfigure the bus, we
/// cache clock register's value here, for each configuration.
///
/// This field is not intended to be set by the user, and is only used
/// internally.
#[builder_lite(skip)]
reg: Result<u32, ConfigError>,
/// The target frequency
#[builder_lite(skip_setter)]
frequency: HertzU32,
/// The clock source
#[cfg_attr(not(feature = "unstable"), builder_lite(skip))]
#[builder_lite(skip_setter)]
clock_source: ClockSource,
/// SPI sample/shift mode.
pub mode: Mode,
mode: Mode,
/// Bit order of the read data.
pub read_bit_order: BitOrder,
read_bit_order: BitOrder,
/// Bit order of the written data.
pub write_bit_order: BitOrder,
write_bit_order: BitOrder,
}
impl Default for Config {
fn default() -> Self {
let mut this = Config {
reg: Ok(0),
frequency: 1_u32.MHz(),
clock_source: ClockSource::Apb,
mode: Mode::_0,
read_bit_order: BitOrder::MsbFirst,
write_bit_order: BitOrder::MsbFirst,
};
this.reg = this.recalculate();
this
}
}
impl core::hash::Hash for Config {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
self.reg.hash(state);
self.frequency.to_Hz().hash(state); // HertzU32 doesn't implement Hash
self.clock_source.hash(state);
self.mode.hash(state);
self.read_bit_order.hash(state);
self.write_bit_order.hash(state);
}
}
impl Default for Config {
fn default() -> Self {
use fugit::RateExtU32;
Config {
frequency: 1_u32.MHz(),
mode: Mode::_0,
read_bit_order: BitOrder::MsbFirst,
write_bit_order: BitOrder::MsbFirst,
impl Config {
/// Set the frequency of the SPI bus clock.
pub fn with_frequency(mut self, frequency: HertzU32) -> Self {
self.frequency = frequency;
self.reg = self.recalculate();
self
}
/// Set the clock source of the SPI bus.
#[instability::unstable]
pub fn with_clock_source(mut self, clock_source: ClockSource) -> Self {
self.clock_source = clock_source;
self.reg = self.recalculate();
self
}
fn recalculate(&self) -> Result<u32, ConfigError> {
// taken from https://github.com/apache/incubator-nuttx/blob/8267a7618629838231256edfa666e44b5313348e/arch/risc-v/src/esp32c3/esp32c3_spi.c#L496
let clocks = Clocks::get();
cfg_if::cfg_if! {
if #[cfg(esp32h2)] {
// ESP32-H2 is using PLL_48M_CLK source instead of APB_CLK
let apb_clk_freq = clocks.pll_48m_clock;
} else {
let apb_clk_freq = clocks.apb_clock;
}
}
let reg_val: u32;
let duty_cycle = 128;
// In HW, n, h and l fields range from 1 to 64, pre ranges from 1 to 8K.
// The value written to register is one lower than the used value.
if self.frequency > ((apb_clk_freq / 4) * 3) {
// Using APB frequency directly will give us the best result here.
reg_val = 1 << 31;
} else {
// For best duty cycle resolution, we want n to be as close to 32 as
// possible, but we also need a pre/n combo that gets us as close as
// possible to the intended frequency. To do this, we bruteforce n and
// calculate the best pre to go along with that. If there's a choice
// between pre/n combos that give the same result, use the one with the
// higher n.
let mut pre: i32;
let mut bestn: i32 = -1;
let mut bestpre: i32 = -1;
let mut besterr: i32 = 0;
let mut errval: i32;
let raw_freq = self.frequency.raw() as i32;
let raw_apb_freq = apb_clk_freq.raw() as i32;
// Start at n = 2. We need to be able to set h/l so we have at least
// one high and one low pulse.
for n in 2..64 {
// Effectively, this does:
// pre = round((APB_CLK_FREQ / n) / frequency)
pre = ((raw_apb_freq / n) + (raw_freq / 2)) / raw_freq;
if pre <= 0 {
pre = 1;
}
if pre > 16 {
pre = 16;
}
errval = (raw_apb_freq / (pre * n) - raw_freq).abs();
if bestn == -1 || errval <= besterr {
besterr = errval;
bestn = n;
bestpre = pre;
}
}
let n: i32 = bestn;
pre = bestpre;
let l: i32 = n;
// Effectively, this does:
// h = round((duty_cycle * n) / 256)
let mut h: i32 = (duty_cycle * n + 127) / 256;
if h <= 0 {
h = 1;
}
reg_val = (l as u32 - 1)
| ((h as u32 - 1) << 6)
| ((n as u32 - 1) << 12)
| ((pre as u32 - 1) << 18);
}
Ok(reg_val)
}
fn raw_clock_reg_value(&self) -> Result<u32, ConfigError> {
self.reg
}
}
@ -2820,88 +2965,6 @@ impl Driver {
Ok(())
}
// taken from https://github.com/apache/incubator-nuttx/blob/8267a7618629838231256edfa666e44b5313348e/arch/risc-v/src/esp32c3/esp32c3_spi.c#L496
fn setup(&self, frequency: HertzU32) {
let clocks = Clocks::get();
cfg_if::cfg_if! {
if #[cfg(esp32h2)] {
// ESP32-H2 is using PLL_48M_CLK source instead of APB_CLK
let apb_clk_freq = HertzU32::Hz(clocks.pll_48m_clock.to_Hz());
} else {
let apb_clk_freq = HertzU32::Hz(clocks.apb_clock.to_Hz());
}
}
let reg_val: u32;
let duty_cycle = 128;
// In HW, n, h and l fields range from 1 to 64, pre ranges from 1 to 8K.
// The value written to register is one lower than the used value.
if frequency > ((apb_clk_freq / 4) * 3) {
// Using APB frequency directly will give us the best result here.
reg_val = 1 << 31;
} else {
// For best duty cycle resolution, we want n to be as close to 32 as
// possible, but we also need a pre/n combo that gets us as close as
// possible to the intended frequency. To do this, we bruteforce n and
// calculate the best pre to go along with that. If there's a choice
// between pre/n combos that give the same result, use the one with the
// higher n.
let mut pre: i32;
let mut bestn: i32 = -1;
let mut bestpre: i32 = -1;
let mut besterr: i32 = 0;
let mut errval: i32;
// Start at n = 2. We need to be able to set h/l so we have at least
// one high and one low pulse.
for n in 2..64 {
// Effectively, this does:
// pre = round((APB_CLK_FREQ / n) / frequency)
pre = ((apb_clk_freq.raw() as i32 / n) + (frequency.raw() as i32 / 2))
/ frequency.raw() as i32;
if pre <= 0 {
pre = 1;
}
if pre > 16 {
pre = 16;
}
errval = (apb_clk_freq.raw() as i32 / (pre * n) - frequency.raw() as i32).abs();
if bestn == -1 || errval <= besterr {
besterr = errval;
bestn = n;
bestpre = pre;
}
}
let n: i32 = bestn;
pre = bestpre;
let l: i32 = n;
// Effectively, this does:
// h = round((duty_cycle * n) / 256)
let mut h: i32 = (duty_cycle * n + 127) / 256;
if h <= 0 {
h = 1;
}
reg_val = (l as u32 - 1)
| ((h as u32 - 1) << 6)
| ((n as u32 - 1) << 12)
| ((pre as u32 - 1) << 18);
}
self.regs().clock().write(|w| unsafe { w.bits(reg_val) });
}
/// Enable or disable listening for the given interrupts.
#[cfg_attr(not(feature = "unstable"), allow(dead_code))]
fn enable_listen(&self, interrupts: EnumSet<SpiInterrupt>, enable: bool) {
@ -3022,7 +3085,7 @@ impl Driver {
}
fn apply_config(&self, config: &Config) -> Result<(), ConfigError> {
self.ch_bus_freq(config.frequency);
self.ch_bus_freq(config)?;
self.set_bit_order(config.read_bit_order, config.write_bit_order);
self.set_data_mode(config.mode);
Ok(())
@ -3047,22 +3110,24 @@ impl Driver {
});
}
fn ch_bus_freq(&self, frequency: HertzU32) {
fn ch_bus_freq(&self, bus_clock_config: &Config) -> Result<(), ConfigError> {
fn enable_clocks(_reg_block: &RegisterBlock, _enable: bool) {
#[cfg(gdma)]
_reg_block.clk_gate().modify(|_, w| {
w.clk_en().bit(_enable);
w.mst_clk_active().bit(_enable);
w.mst_clk_sel().bit(_enable)
w.mst_clk_sel().bit(true) // TODO: support XTAL clock source
});
}
enable_clocks(self.regs(), false);
// Change clock frequency
self.setup(frequency);
let raw = bus_clock_config.raw_clock_reg_value()?;
enable_clocks(self.regs(), false);
self.regs().clock().write(|w| unsafe { w.bits(raw) });
enable_clocks(self.regs(), true);
Ok(())
}
#[cfg(not(any(esp32, esp32c3, esp32s2)))]

View File

@ -68,6 +68,7 @@ pub enum ClockSource {
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub struct Config {
/// Clock source for the temperature sensor
clock_source: ClockSource,
}

View File

@ -195,11 +195,11 @@ impl embedded_io::Error for Error {
}
}
// (outside of `config` module in order not to "use" it an extra time)
/// UART clock source
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
#[instability::unstable]
pub enum ClockSource {
/// APB_CLK clock source
#[cfg_attr(not(any(esp32c6, esp32h2, lp_uart)), default)]
@ -284,19 +284,20 @@ pub enum StopBits {
pub struct Config {
/// The baud rate (speed) of the UART communication in bits per second
/// (bps).
pub baudrate: u32,
baudrate: u32,
/// Number of data bits in each frame (5, 6, 7, or 8 bits).
pub data_bits: DataBits,
data_bits: DataBits,
/// Parity setting (None, Even, or Odd).
pub parity: Parity,
parity: Parity,
/// Number of stop bits in each frame (1, 1.5, or 2 bits).
pub stop_bits: StopBits,
stop_bits: StopBits,
/// Clock source used by the UART peripheral.
pub clock_source: ClockSource,
#[cfg_attr(not(feature = "unstable"), builder_lite(skip))]
clock_source: ClockSource,
/// UART Receive part configuration.
pub rx: RxConfig,
rx: RxConfig,
/// UART Transmit part configuration.
pub tx: TxConfig,
tx: TxConfig,
}
/// UART Receive part configuration.
@ -305,9 +306,23 @@ pub struct Config {
#[non_exhaustive]
pub struct RxConfig {
/// Threshold level at which the RX FIFO is considered full.
pub fifo_full_threshold: u16,
fifo_full_threshold: u16,
/// Optional timeout value for RX operations.
pub timeout: Option<u8>,
timeout: Option<u8>,
}
impl Default for Config {
fn default() -> Config {
Config {
rx: RxConfig::default(),
tx: TxConfig::default(),
baudrate: 115_200,
data_bits: Default::default(),
parity: Default::default(),
stop_bits: Default::default(),
clock_source: Default::default(),
}
}
}
/// UART Transmit part configuration.
@ -325,20 +340,6 @@ impl Default for RxConfig {
}
}
impl Default for Config {
fn default() -> Config {
Config {
rx: RxConfig::default(),
tx: TxConfig::default(),
baudrate: 115_200,
data_bits: Default::default(),
parity: Default::default(),
stop_bits: Default::default(),
clock_source: Default::default(),
}
}
}
impl Config {
/// Calculates the total symbol length in bits based on the configured
/// data bits, parity, and stop bits.
@ -370,17 +371,17 @@ impl Config {
pub struct AtCmdConfig {
/// Optional idle time before the AT command detection begins, in clock
/// cycles.
pub pre_idle_count: Option<u16>,
pre_idle_count: Option<u16>,
/// Optional idle time after the AT command detection ends, in clock
/// cycles.
pub post_idle_count: Option<u16>,
post_idle_count: Option<u16>,
/// Optional timeout between characters in the AT command, in clock
/// cycles.
pub gap_timeout: Option<u16>,
gap_timeout: Option<u16>,
/// The character that triggers the AT command detection.
pub cmd_char: u8,
cmd_char: u8,
/// Optional number of characters to detect as part of the AT command.
pub char_num: u8,
char_num: u8,
}
impl Default for AtCmdConfig {
@ -1912,7 +1913,7 @@ pub mod lp_uart {
// Override protocol parameters from the configuration
// uart_hal_set_baudrate(&hal, cfg->uart_proto_cfg.baud_rate, sclk_freq);
me.change_baud_internal(config.baudrate, config.clock_source);
me.change_baud_internal(&config);
// uart_hal_set_parity(&hal, cfg->uart_proto_cfg.parity);
me.change_parity(config.parity);
// uart_hal_set_data_bit_num(&hal, cfg->uart_proto_cfg.data_bits);
@ -1967,17 +1968,17 @@ pub mod lp_uart {
}
}
fn change_baud_internal(&mut self, baudrate: u32, clock_source: super::ClockSource) {
fn change_baud_internal(&mut self, config: &Config) {
// TODO: Currently it's not possible to use XtalD2Clk
let clk = 16_000_000_u32;
let max_div = 0b1111_1111_1111 - 1;
let clk_div = clk.div_ceil(max_div * baudrate);
let clk_div = clk.div_ceil(max_div * config.baudrate);
self.uart.register_block().clk_conf().modify(|_, w| unsafe {
w.sclk_div_a().bits(0);
w.sclk_div_b().bits(0);
w.sclk_div_num().bits(clk_div as u8 - 1);
w.sclk_sel().bits(match clock_source {
w.sclk_sel().bits(match config.clock_source {
super::ClockSource::Xtal => 3,
super::ClockSource::RcFast => 2,
super::ClockSource::Apb => panic!("Wrong clock source for LP_UART"),
@ -1986,7 +1987,7 @@ pub mod lp_uart {
});
let clk = clk / clk_div;
let divider = clk / baudrate;
let divider = clk / config.baudrate;
let divider = divider as u16;
self.uart
@ -2004,8 +2005,8 @@ pub mod lp_uart {
/// [`Apb`] is a wrong clock source for LP_UART
///
/// [`Apb`]: super::ClockSource::Apb
pub fn change_baud(&mut self, baudrate: u32, clock_source: super::ClockSource) {
self.change_baud_internal(baudrate, clock_source);
pub fn change_baud(&mut self, config: &Config) {
self.change_baud_internal(config);
self.txfifo_reset();
self.rxfifo_reset();
}
@ -2201,7 +2202,7 @@ impl Info {
}
fn apply_config(&self, config: &Config) -> Result<(), ConfigError> {
self.change_baud(config.baudrate, config.clock_source);
self.change_baud(config);
self.change_data_bits(config.data_bits);
self.change_parity(config.parity);
self.change_stop_bits(config.stop_bits);
@ -2390,17 +2391,17 @@ impl Info {
}
#[cfg(any(esp32c2, esp32c3, esp32s3))]
fn change_baud(&self, baudrate: u32, clock_source: ClockSource) {
fn change_baud(&self, config: &Config) {
use crate::peripherals::LPWR;
let clocks = Clocks::get();
let clk = match clock_source {
let clk = match config.clock_source {
ClockSource::Apb => clocks.apb_clock.to_Hz(),
ClockSource::Xtal => clocks.xtal_clock.to_Hz(),
ClockSource::RcFast => crate::soc::constants::RC_FAST_CLK.to_Hz(),
};
if clock_source == ClockSource::RcFast {
if config.clock_source == ClockSource::RcFast {
LPWR::regs()
.clk_conf()
.modify(|_, w| w.dig_clk8m_en().variant(true));
@ -2409,9 +2410,9 @@ impl Info {
}
let max_div = 0b1111_1111_1111 - 1;
let clk_div = clk.div_ceil(max_div * baudrate);
let clk_div = clk.div_ceil(max_div * config.baudrate);
self.regs().clk_conf().write(|w| unsafe {
w.sclk_sel().bits(match clock_source {
w.sclk_sel().bits(match config.clock_source {
ClockSource::Apb => 1,
ClockSource::RcFast => 2,
ClockSource::Xtal => 3,
@ -2423,7 +2424,7 @@ impl Info {
w.tx_sclk_en().bit(true)
});
let divider = (clk << 4) / (baudrate * clk_div);
let divider = (clk << 4) / (config.baudrate * clk_div);
let divider_integer = (divider >> 4) as u16;
let divider_frag = (divider & 0xf) as u8;
self.regs()
@ -2440,16 +2441,16 @@ impl Info {
}
#[cfg(any(esp32c6, esp32h2))]
fn change_baud(&self, baudrate: u32, clock_source: ClockSource) {
fn change_baud(&self, config: &Config) {
let clocks = Clocks::get();
let clk = match clock_source {
let clk = match config.clock_source {
ClockSource::Apb => clocks.apb_clock.to_Hz(),
ClockSource::Xtal => clocks.xtal_clock.to_Hz(),
ClockSource::RcFast => crate::soc::constants::RC_FAST_CLK.to_Hz(),
};
let max_div = 0b1111_1111_1111 - 1;
let clk_div = clk.div_ceil(max_div * baudrate);
let clk_div = clk.div_ceil(max_div * config.baudrate);
// UART clocks are configured via PCR
let pcr = crate::peripherals::PCR::regs();
@ -2462,7 +2463,7 @@ impl Info {
w.uart0_sclk_div_a().bits(0);
w.uart0_sclk_div_b().bits(0);
w.uart0_sclk_div_num().bits(clk_div as u8 - 1);
w.uart0_sclk_sel().bits(match clock_source {
w.uart0_sclk_sel().bits(match config.clock_source {
ClockSource::Apb => 1,
ClockSource::RcFast => 2,
ClockSource::Xtal => 3,
@ -2477,7 +2478,7 @@ impl Info {
w.uart1_sclk_div_a().bits(0);
w.uart1_sclk_div_b().bits(0);
w.uart1_sclk_div_num().bits(clk_div as u8 - 1);
w.uart1_sclk_sel().bits(match clock_source {
w.uart1_sclk_sel().bits(match config.clock_source {
ClockSource::Apb => 1,
ClockSource::RcFast => 2,
ClockSource::Xtal => 3,
@ -2487,7 +2488,7 @@ impl Info {
}
let clk = clk / clk_div;
let divider = clk / baudrate;
let divider = clk / config.baudrate;
let divider = divider as u16;
self.regs()
@ -2498,18 +2499,19 @@ impl Info {
}
#[cfg(any(esp32, esp32s2))]
fn change_baud(&self, baudrate: u32, clock_source: ClockSource) {
let clk = match clock_source {
fn change_baud(&self, config: &Config) {
let clk = match config.clock_source {
ClockSource::Apb => Clocks::get().apb_clock.to_Hz(),
// ESP32(/-S2) TRM, section 3.2.4.2 (6.2.4.2 for S2)
ClockSource::RefTick => crate::soc::constants::REF_TICK.to_Hz(),
};
self.regs()
.conf0()
.modify(|_, w| w.tick_ref_always_on().bit(clock_source == ClockSource::Apb));
self.regs().conf0().modify(|_, w| {
w.tick_ref_always_on()
.bit(config.clock_source == ClockSource::Apb)
});
let divider = clk / baudrate;
let divider = clk / config.baudrate;
self.regs()
.clkdiv()

View File

@ -8,7 +8,7 @@
use esp_hal::{
clock::CpuClock,
config::WatchdogStatus,
config::{WatchdogConfig, WatchdogStatus},
delay::Delay,
rtc_cntl::Rtc,
time::ExtU64,
@ -24,12 +24,11 @@ mod tests {
#[test]
fn test_feeding_timg0_wdt() {
let peripherals = esp_hal::init({
let mut config = Config::default();
config.watchdog.timg0 =
WatchdogStatus::Enabled(fugit::MicrosDurationU64::millis(500 as u64));
config
});
let peripherals = esp_hal::init(Config::default().with_watchdog(
WatchdogConfig::default().with_timg0(WatchdogStatus::Enabled(
fugit::MicrosDurationU64::millis(500),
)),
));
let timg0 = TimerGroup::new(peripherals.TIMG0);
let mut wdt0 = timg0.wdt;
@ -44,12 +43,11 @@ mod tests {
#[test]
#[cfg(timg1)]
fn test_feeding_timg1_wdt() {
let peripherals = esp_hal::init({
let mut config = Config::default();
config.watchdog.timg1 =
WatchdogStatus::Enabled(fugit::MicrosDurationU64::millis(500 as u64));
config
});
let peripherals = esp_hal::init(Config::default().with_watchdog(
WatchdogConfig::default().with_timg1(WatchdogStatus::Enabled(
fugit::MicrosDurationU64::millis(500),
)),
));
let timg1 = TimerGroup::new(peripherals.TIMG1);
let mut wdt1 = timg1.wdt;
@ -63,13 +61,15 @@ mod tests {
#[test]
fn test_feeding_timg0_wdt_max_clock() {
let peripherals = esp_hal::init({
let mut config = Config::default();
config.cpu_clock = CpuClock::max();
config.watchdog.timg0 =
WatchdogStatus::Enabled(fugit::MicrosDurationU64::millis(500 as u64));
config
});
let peripherals = esp_hal::init(
Config::default()
.with_cpu_clock(CpuClock::max())
.with_watchdog(
WatchdogConfig::default().with_timg0(WatchdogStatus::Enabled(
fugit::MicrosDurationU64::millis(500),
)),
),
);
let timg0 = TimerGroup::new(peripherals.TIMG0);
let mut wdt0 = timg0.wdt;
@ -84,12 +84,11 @@ mod tests {
#[test]
#[timeout(4)]
fn test_feeding_rtc_wdt() {
let peripherals = esp_hal::init({
let mut config = Config::default();
config.watchdog.rwdt =
WatchdogStatus::Enabled(fugit::MicrosDurationU64::millis(3000 as u64));
config
});
let peripherals = esp_hal::init(Config::default().with_watchdog(
WatchdogConfig::default().with_rwdt(WatchdogStatus::Enabled(
fugit::MicrosDurationU64::millis(3000),
)),
));
let mut rtc = Rtc::new(peripherals.LPWR);
let delay = Delay::new();

View File

@ -73,34 +73,34 @@ mod tests {
let (d6_in, d6_out) = peripherals.GPIO17.split();
let (d7_in, d7_out) = peripherals.GPIO16.split();
let mut config = dpi::Config::default();
config.clock_mode = ClockMode {
polarity: Polarity::IdleHigh,
phase: Phase::ShiftLow,
};
config.frequency = 500u32.kHz();
config.format = Format {
enable_2byte_mode: false,
..Default::default()
};
// Send a 50x50 video
config.timing = FrameTiming {
horizontal_total_width: 65,
hsync_width: 5,
horizontal_blank_front_porch: 10,
horizontal_active_width: 50,
let config = dpi::Config::default()
.with_clock_mode(ClockMode {
polarity: Polarity::IdleHigh,
phase: Phase::ShiftLow,
})
.with_frequency(500u32.kHz())
.with_format(Format {
enable_2byte_mode: false,
..Default::default()
})
// Send a 50x50 video
.with_timing(FrameTiming {
horizontal_total_width: 65,
hsync_width: 5,
horizontal_blank_front_porch: 10,
horizontal_active_width: 50,
vertical_total_height: 65,
vsync_width: 5,
vertical_blank_front_porch: 10,
vertical_active_height: 50,
vertical_total_height: 65,
vsync_width: 5,
vertical_blank_front_porch: 10,
vertical_active_height: 50,
hsync_position: 0,
};
config.vsync_idle_level = Level::High;
config.hsync_idle_level = Level::High;
config.de_idle_level = Level::Low;
config.disable_black_region = false;
hsync_position: 0,
})
.with_vsync_idle_level(Level::High)
.with_hsync_idle_level(Level::High)
.with_de_idle_level(Level::Low)
.with_disable_black_region(false);
let dpi = Dpi::new(lcd_cam.lcd, tx_channel, config)
.unwrap()
@ -117,14 +117,11 @@ mod tests {
.with_data6(d6_out)
.with_data7(d7_out);
let mut cam_config = cam::Config::default();
cam_config.frequency = 1u32.MHz();
let camera = Camera::new(
lcd_cam.cam,
rx_channel,
RxEightBits::new(d0_in, d1_in, d2_in, d3_in, d4_in, d5_in, d6_in, d7_in),
cam_config,
cam::Config::default().with_frequency(1u32.MHz()),
)
.unwrap()
.with_ctrl_pins_and_de(vsync_in, hsync_in, de_in)

View File

@ -76,11 +76,12 @@ mod tests {
fn test_i8080_8bit(ctx: Context<'static>) {
let pins = TxEightBits::new(NoPin, NoPin, NoPin, NoPin, NoPin, NoPin, NoPin, NoPin);
let i8080 = I8080::new(ctx.lcd_cam.lcd, ctx.dma, pins, {
let mut config = Config::default();
config.frequency = 20.MHz();
config
})
let i8080 = I8080::new(
ctx.lcd_cam.lcd,
ctx.dma,
pins,
Config::default().with_frequency(20.MHz()),
)
.unwrap();
let xfer = i8080.send(Command::<u8>::None, 0, ctx.dma_buf).unwrap();
@ -138,11 +139,12 @@ mod tests {
NoPin,
);
let mut i8080 = I8080::new(ctx.lcd_cam.lcd, ctx.dma, pins, {
let mut config = Config::default();
config.frequency = 20.MHz();
config
})
let mut i8080 = I8080::new(
ctx.lcd_cam.lcd,
ctx.dma,
pins,
Config::default().with_frequency(20.MHz()),
)
.unwrap()
.with_cs(cs_signal)
.with_ctrl_pins(NoPin, NoPin);
@ -259,11 +261,12 @@ mod tests {
unit3_signal,
);
let mut i8080 = I8080::new(ctx.lcd_cam.lcd, ctx.dma, pins, {
let mut config = Config::default();
config.frequency = 20.MHz();
config
})
let mut i8080 = I8080::new(
ctx.lcd_cam.lcd,
ctx.dma,
pins,
Config::default().with_frequency(20.MHz()),
)
.unwrap()
.with_cs(cs_signal)
.with_ctrl_pins(NoPin, NoPin);

View File

@ -51,11 +51,12 @@ mod tests {
async fn test_i8080_8bit(ctx: Context<'static>) {
let pins = TxEightBits::new(NoPin, NoPin, NoPin, NoPin, NoPin, NoPin, NoPin, NoPin);
let i8080 = I8080::new(ctx.lcd_cam.lcd, ctx.dma, pins, {
let mut config = Config::default();
config.frequency = 20.MHz();
config
})
let i8080 = I8080::new(
ctx.lcd_cam.lcd,
ctx.dma,
pins,
Config::default().with_frequency(20.MHz()),
)
.unwrap();
// explicitly drop the camera half to see if it disables clocks (unexpectedly,

View File

@ -108,10 +108,7 @@ mod tests {
let (_, tx) = hil_test::common_test_pins!(peripherals);
let tx_config = TxChannelConfig {
clk_divider: 255,
..TxChannelConfig::default()
};
let tx_config = TxChannelConfig::default().with_clk_divider(255);
let tx_channel = {
use esp_hal::rmt::TxChannelCreator;

View File

@ -22,6 +22,7 @@ lis3dh-async = "0.9.3"
ssd1306 = "0.9.0"
[features]
unstable = []
esp32 = ["esp-backtrace/esp32", "esp-hal/esp32", "esp-hal-embassy/esp32", "esp-println/esp32"]
esp32c2 = ["esp-backtrace/esp32c2", "esp-hal/esp32c2", "esp-hal-embassy/esp32c2", "esp-println/esp32c2"]
esp32c3 = ["esp-backtrace/esp32c3", "esp-hal/esp32c3", "esp-hal-embassy/esp32c3", "esp-println/esp32c3"]

View File

@ -32,11 +32,10 @@ async fn main(_spawner: Spawner) {
let timg0 = TimerGroup::new(peripherals.TIMG0);
esp_hal_embassy::init(timg0.timer0);
let i2c0 = I2c::new(peripherals.I2C0, {
let mut config = Config::default();
config.frequency = 400.kHz();
config
})
let i2c0 = I2c::new(
peripherals.I2C0,
Config::default().with_frequency(400.kHz()),
)
.unwrap()
.with_sda(peripherals.GPIO4)
.with_scl(peripherals.GPIO5)

View File

@ -32,11 +32,10 @@ async fn main(_spawner: Spawner) {
let timg0 = TimerGroup::new(peripherals.TIMG0);
esp_hal_embassy::init(timg0.timer0);
let mut i2c = I2c::new(peripherals.I2C0, {
let mut config = Config::default();
config.frequency = 400.kHz();
config
})
let mut i2c = I2c::new(
peripherals.I2C0,
Config::default().with_frequency(400.kHz()),
)
.unwrap()
.with_sda(peripherals.GPIO4)
.with_scl(peripherals.GPIO5)

View File

@ -66,8 +66,7 @@ fn main() -> ! {
peripherals.GPIO16,
);
let mut cam_config = cam::Config::default();
cam_config.frequency = 20u32.MHz();
let cam_config = cam::Config::default().with_frequency(20u32.MHz());
let lcd_cam = LcdCam::new(peripherals.LCD_CAM);
let camera = Camera::new(lcd_cam.cam, peripherals.DMA_CH0, cam_data_pins, cam_config)

View File

@ -135,34 +135,34 @@ fn main() -> ! {
let mut dma_buf = dma_loop_buffer!(2 * 16);
let mut config = Config::default();
config.clock_mode = ClockMode {
polarity: Polarity::IdleLow,
phase: Phase::ShiftLow,
};
config.frequency = 16.MHz();
config.format = Format {
enable_2byte_mode: true,
..Default::default()
};
config.timing = FrameTiming {
horizontal_active_width: 480,
horizontal_total_width: 520,
horizontal_blank_front_porch: 10,
let config = Config::default()
.with_clock_mode(ClockMode {
polarity: Polarity::IdleLow,
phase: Phase::ShiftLow,
})
.with_frequency(16.MHz())
.with_format(Format {
enable_2byte_mode: true,
..Default::default()
})
.with_timing(FrameTiming {
horizontal_active_width: 480,
horizontal_total_width: 520,
horizontal_blank_front_porch: 10,
vertical_active_height: 480,
vertical_total_height: 510,
vertical_blank_front_porch: 10,
vertical_active_height: 480,
vertical_total_height: 510,
vertical_blank_front_porch: 10,
hsync_width: 10,
vsync_width: 10,
hsync_width: 10,
vsync_width: 10,
hsync_position: 0,
};
config.vsync_idle_level = Level::High;
config.hsync_idle_level = Level::High;
config.de_idle_level = Level::Low;
config.disable_black_region = false;
hsync_position: 0,
})
.with_vsync_idle_level(Level::High)
.with_hsync_idle_level(Level::High)
.with_de_idle_level(Level::Low)
.with_disable_black_region(false);
let mut dpi = Dpi::new(lcd_cam.lcd, tx_channel, config)
.unwrap()

View File

@ -69,11 +69,14 @@ fn main() -> ! {
);
let lcd_cam = LcdCam::new(peripherals.LCD_CAM);
let mut i8080_config = Config::default();
i8080_config.frequency = 20.MHz();
let i8080 = I8080::new(lcd_cam.lcd, peripherals.DMA_CH0, tx_pins, i8080_config)
.unwrap()
.with_ctrl_pins(lcd_rs, lcd_wr);
let i8080 = I8080::new(
lcd_cam.lcd,
peripherals.DMA_CH0,
tx_pins,
Config::default().with_frequency(20.MHz()),
)
.unwrap()
.with_ctrl_pins(lcd_rs, lcd_wr);
// Note: This isn't provided in the HAL since different drivers may require
// different considerations, like how to manage the CS pin, the CD pin,