mirror of
https://github.com/esp-rs/esp-hal.git
synced 2025-09-26 20:00:32 +00:00
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:
parent
bb1ad1485a
commit
af6ee36d93
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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,
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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();
|
||||
|
@ -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 {
|
||||
|
@ -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)))]
|
||||
|
@ -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,
|
||||
}
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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();
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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"]
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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()
|
||||
|
@ -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,
|
||||
|
Loading…
x
Reference in New Issue
Block a user