diff --git a/CHANGELOG.md b/CHANGELOG.md index 13dd6f20..f5315849 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Added +- Support loading flash size, frequency, and mode from the config file (#627) ### Fixed - Fixed help text for size parameter of read-flash subcommand diff --git a/cargo-espflash/README.md b/cargo-espflash/README.md index ea728766..addc4af8 100644 --- a/cargo-espflash/README.md +++ b/cargo-espflash/README.md @@ -135,6 +135,13 @@ The configuration file allows you to define various parameters for your applicat ```toml partition_table = "path/to/custom/partition-table.bin" ``` +- Flash settings + ```toml + [flash] + mode = "qio" + size = "8MB" + frequency = "80MHz" + ``` You can have a local and/or a global configuration file: diff --git a/cargo-espflash/src/main.rs b/cargo-espflash/src/main.rs index ebe6cfa6..10a30e78 100644 --- a/cargo-espflash/src/main.rs +++ b/cargo-espflash/src/main.rs @@ -9,13 +9,13 @@ use clap::{Args, CommandFactory, Parser, Subcommand}; use espflash::{ cli::{ self, board_info, checksum_md5, completions, config::Config, connect, erase_flash, - erase_partitions, erase_region, flash_elf_image, monitor::monitor, partition_table, - print_board_info, read_flash, save_elf_as_image, serial_monitor, ChecksumMd5Args, - CompletionsArgs, ConnectArgs, EraseFlashArgs, EraseRegionArgs, EspflashProgress, - FlashConfigArgs, MonitorArgs, PartitionTableArgs, ReadFlashArgs, + erase_partitions, erase_region, flash_elf_image, make_flash_data, monitor::monitor, + partition_table, print_board_info, read_flash, save_elf_as_image, serial_monitor, + ChecksumMd5Args, CompletionsArgs, ConnectArgs, EraseFlashArgs, EraseRegionArgs, + EspflashProgress, FlashConfigArgs, MonitorArgs, PartitionTableArgs, ReadFlashArgs, }, error::Error as EspflashError, - flasher::{parse_partition_table, FlashData, FlashSettings}, + flasher::parse_partition_table, logging::initialize_logger, targets::{Chip, XtalFrequency}, update::check_for_update, @@ -267,12 +267,14 @@ fn flash(args: FlashArgs, config: &Config) -> Result<()> { args.flash_args.no_verify, args.flash_args.no_skip, )?; - flasher.verify_minimum_revision(args.flash_args.min_chip_rev)?; + flasher.verify_minimum_revision(args.flash_args.image.min_chip_rev)?; - // If the user has provided a flash size via a command-line argument, we'll + // If the user has provided a flash size via a command-line argument or config, we'll // override the detected (or default) value with this. if let Some(flash_size) = args.build_args.flash_config_args.flash_size { flasher.set_flash_size(flash_size); + } else if let Some(flash_size) = config.flash.size { + flasher.set_flash_size(flash_size); } let chip = flasher.chip(); @@ -292,40 +294,12 @@ fn flash(args: FlashArgs, config: &Config) -> Result<()> { if args.flash_args.ram { flasher.load_elf_to_ram(&elf_data, Some(&mut EspflashProgress::default()))?; } else { - let bootloader = args - .flash_args - .bootloader - .as_deref() - .or(config.bootloader.as_deref()) - .or(build_ctx.bootloader_path.as_deref()); - - let partition_table = args - .flash_args - .partition_table - .as_deref() - .or(config.partition_table.as_deref()) - .or(build_ctx.partition_table_path.as_deref()); - - if let Some(path) = &bootloader { - println!("Bootloader: {}", path.display()); - } - if let Some(path) = &partition_table { - println!("Partition table: {}", path.display()); - } - - let flash_settings = FlashSettings::new( - args.build_args.flash_config_args.flash_mode, - args.build_args.flash_config_args.flash_size, - args.build_args.flash_config_args.flash_freq, - ); - - let flash_data = FlashData::new( - bootloader, - partition_table, - args.flash_args.partition_table_offset, - args.flash_args.target_app_partition, - flash_settings, - args.flash_args.min_chip_rev, + let flash_data = make_flash_data( + args.flash_args.image, + &args.build_args.flash_config_args, + config, + build_ctx.bootloader_path.as_deref(), + build_ctx.partition_table_path.as_deref(), )?; if args.flash_args.erase_parts.is_some() || args.flash_args.erase_data_parts.is_some() { @@ -532,47 +506,18 @@ fn save_image(args: SaveImageArgs, config: &Config) -> Result<()> { let build_ctx = build(&args.build_args, &cargo_config, args.save_image_args.chip)?; let elf_data = fs::read(build_ctx.artifact_path).into_diagnostic()?; - let bootloader = args - .save_image_args - .bootloader - .as_deref() - .or(config.bootloader.as_deref()) - .or(build_ctx.bootloader_path.as_deref()) - .map(|p| p.to_path_buf()); - - let partition_table = args - .save_image_args - .partition_table - .as_deref() - .or(config.partition_table.as_deref()) - .or(build_ctx.partition_table_path.as_deref()) - .map(|p| p.to_path_buf()); - // Since we have no `Flasher` instance and as such cannot print the board // information, we will print whatever information we _do_ have. println!("Chip type: {}", args.save_image_args.chip); println!("Merge: {}", args.save_image_args.merge); println!("Skip padding: {}", args.save_image_args.skip_padding); - if let Some(path) = &args.save_image_args.bootloader { - println!("Bootloader: {}", path.display()); - } - if let Some(path) = &args.save_image_args.partition_table { - println!("Partition table: {}", path.display()); - } - let flash_settings = FlashSettings::new( - args.build_args.flash_config_args.flash_mode, - args.build_args.flash_config_args.flash_size, - args.build_args.flash_config_args.flash_freq, - ); - - let flash_data = FlashData::new( - bootloader.as_deref(), - partition_table.as_deref(), - args.save_image_args.partition_table_offset, - args.save_image_args.target_app_partition, - flash_settings, - args.save_image_args.min_chip_rev, + let flash_data = make_flash_data( + args.save_image_args.image, + &args.build_args.flash_config_args, + config, + build_ctx.bootloader_path.as_deref(), + build_ctx.partition_table_path.as_deref(), )?; let xtal_freq = args diff --git a/espflash/README.md b/espflash/README.md index 31d3fd10..35edcb88 100644 --- a/espflash/README.md +++ b/espflash/README.md @@ -149,6 +149,13 @@ The configuration file allows you to define various parameters for your applicat ```toml partition_table = "path/to/custom/partition-table.bin" ``` +- Flash settings + ```toml + [flash] + mode = "qio" + size = "8MB" + frequency = "80MHz" + ``` You can have a local and/or a global configuration file: diff --git a/espflash/src/bin/espflash.rs b/espflash/src/bin/espflash.rs index 0bc6f87e..c115c9ea 100644 --- a/espflash/src/bin/espflash.rs +++ b/espflash/src/bin/espflash.rs @@ -8,13 +8,14 @@ use clap::{Args, CommandFactory, Parser, Subcommand}; use espflash::{ cli::{ self, board_info, checksum_md5, completions, config::Config, connect, erase_flash, - erase_partitions, erase_region, flash_elf_image, monitor::monitor, parse_uint32, - partition_table, print_board_info, read_flash, save_elf_as_image, serial_monitor, - ChecksumMd5Args, CompletionsArgs, ConnectArgs, EraseFlashArgs, EraseRegionArgs, - EspflashProgress, FlashConfigArgs, MonitorArgs, PartitionTableArgs, ReadFlashArgs, + erase_partitions, erase_region, flash_elf_image, make_flash_data, monitor::monitor, + parse_uint32, partition_table, print_board_info, read_flash, save_elf_as_image, + serial_monitor, ChecksumMd5Args, CompletionsArgs, ConnectArgs, EraseFlashArgs, + EraseRegionArgs, EspflashProgress, FlashConfigArgs, MonitorArgs, PartitionTableArgs, + ReadFlashArgs, }, error::Error, - flasher::{parse_partition_table, FlashData, FlashSettings}, + flasher::parse_partition_table, logging::initialize_logger, targets::{Chip, XtalFrequency}, update::check_for_update, @@ -161,7 +162,7 @@ fn main() -> Result<()> { // displayed. check_for_update(env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION")); - // Load any user configuraiton, if present. + // Load any user configuration, if present. let config = Config::load()?; // Execute the correct action based on the provided subcommand and its @@ -212,12 +213,14 @@ fn flash(args: FlashArgs, config: &Config) -> Result<()> { args.flash_args.no_verify, args.flash_args.no_skip, )?; - flasher.verify_minimum_revision(args.flash_args.min_chip_rev)?; + flasher.verify_minimum_revision(args.flash_args.image.min_chip_rev)?; // If the user has provided a flash size via a command-line argument, we'll // override the detected (or default) value with this. if let Some(flash_size) = args.flash_config_args.flash_size { flasher.set_flash_size(flash_size); + } else if let Some(flash_size) = config.flash.size { + flasher.set_flash_size(flash_size); } print_board_info(&mut flasher)?; @@ -232,37 +235,12 @@ fn flash(args: FlashArgs, config: &Config) -> Result<()> { if args.flash_args.ram { flasher.load_elf_to_ram(&elf_data, Some(&mut EspflashProgress::default()))?; } else { - let bootloader = args - .flash_args - .bootloader - .as_deref() - .or(config.bootloader.as_deref()); - let partition_table = args - .flash_args - .partition_table - .as_deref() - .or(config.partition_table.as_deref()); - - if let Some(path) = bootloader { - println!("Bootloader: {}", path.display()); - } - if let Some(path) = partition_table { - println!("Partition table: {}", path.display()); - } - - let flash_settings = FlashSettings::new( - args.flash_config_args.flash_mode, - args.flash_config_args.flash_size, - args.flash_config_args.flash_freq, - ); - - let flash_data = FlashData::new( - bootloader, - partition_table, - args.flash_args.partition_table_offset, - args.flash_args.target_app_partition, - flash_settings, - args.flash_args.min_chip_rev, + let flash_data = make_flash_data( + args.flash_args.image, + &args.flash_config_args, + config, + None, + None, )?; if args.flash_args.erase_parts.is_some() || args.flash_args.erase_data_parts.is_some() { @@ -306,42 +284,18 @@ fn save_image(args: SaveImageArgs, config: &Config) -> Result<()> { .into_diagnostic() .wrap_err_with(|| format!("Failed to open image {}", args.image.display()))?; - let bootloader = args - .save_image_args - .bootloader - .as_deref() - .or(config.bootloader.as_deref()); - let partition_table = args - .save_image_args - .partition_table - .as_deref() - .or(config.partition_table.as_deref()); - // Since we have no `Flasher` instance and as such cannot print the board // information, we will print whatever information we _do_ have. println!("Chip type: {}", args.save_image_args.chip); println!("Merge: {}", args.save_image_args.merge); println!("Skip padding: {}", args.save_image_args.skip_padding); - if let Some(path) = &bootloader { - println!("Bootloader: {}", path.display()); - } - if let Some(path) = &partition_table { - println!("Partition table: {}", path.display()); - } - let flash_settings = FlashSettings::new( - args.flash_config_args.flash_mode, - args.flash_config_args.flash_size, - args.flash_config_args.flash_freq, - ); - - let flash_data = FlashData::new( - bootloader, - partition_table, - args.save_image_args.partition_table_offset, - args.save_image_args.target_app_partition, - flash_settings, - args.save_image_args.min_chip_rev, + let flash_data = make_flash_data( + args.save_image_args.image, + &args.flash_config_args, + config, + None, + None, )?; let xtal_freq = args diff --git a/espflash/src/cli/config.rs b/espflash/src/cli/config.rs index 93103635..61a825cf 100644 --- a/espflash/src/cli/config.rs +++ b/espflash/src/cli/config.rs @@ -20,6 +20,7 @@ use serde::{Deserialize, Serialize}; use serialport::UsbPortInfo; use crate::error::Error; +use crate::flasher::FlashSettings; /// A configured, known serial connection #[derive(Debug, Deserialize, Serialize, Default, Clone)] @@ -89,6 +90,9 @@ pub struct Config { /// Preferred USB devices #[serde(default)] pub usb_device: Vec, + /// Flash settings + #[serde(default)] + pub flash: FlashSettings, /// Path of the file to save the configuration to #[serde(skip)] save_path: PathBuf, diff --git a/espflash/src/cli/mod.rs b/espflash/src/cli/mod.rs index feadef32..0105a15e 100644 --- a/espflash/src/cli/mod.rs +++ b/espflash/src/cli/mod.rs @@ -10,7 +10,13 @@ //! [cargo-espflash]: https://crates.io/crates/cargo-espflash //! [espflash]: https://crates.io/crates/espflash -use std::{collections::HashMap, fs, io::Write, num::ParseIntError, path::PathBuf}; +use std::{ + collections::HashMap, + fs, + io::Write, + num::ParseIntError, + path::{Path, PathBuf}, +}; use clap::Args; use clap_complete::Shell; @@ -31,8 +37,8 @@ use crate::{ elf::ElfFirmwareImage, error::{Error, MissingPartition, MissingPartitionTable}, flasher::{ - parse_partition_table, FlashData, FlashFrequency, FlashMode, FlashSize, Flasher, - ProgressCallbacks, + parse_partition_table, FlashData, FlashFrequency, FlashMode, FlashSettings, FlashSize, + Flasher, ProgressCallbacks, }, targets::{Chip, XtalFrequency}, }; @@ -124,9 +130,6 @@ pub struct FlashConfigArgs { #[non_exhaustive] #[group(skip)] pub struct FlashArgs { - /// Path to a binary (.bin) bootloader file - #[arg(long, value_name = "FILE")] - pub bootloader: Option, /// Erase partitions by label #[arg(long, value_name = "LABELS", value_delimiter = ',')] pub erase_parts: Option>, @@ -136,24 +139,12 @@ pub struct FlashArgs { /// Logging format. #[arg(long, short = 'L', default_value = "serial", requires = "monitor")] pub log_format: LogFormat, - /// Minimum chip revision supported by image, in format: major.minor - #[arg(long, default_value = "0.0", value_parser = parse_chip_rev)] - pub min_chip_rev: u16, /// Open a serial monitor after flashing #[arg(short = 'M', long)] pub monitor: bool, /// Baud rate at which to read console output #[arg(long, requires = "monitor", value_name = "BAUD")] pub monitor_baud: Option, - /// Path to a CSV file containing partition table - #[arg(long, value_name = "FILE")] - pub partition_table: Option, - /// Label of target app partition - #[arg(long, value_name = "LABEL")] - pub target_app_partition: Option, - /// Partition table offset - #[arg(long, value_name = "OFFSET")] - pub partition_table_offset: Option, /// Load the application to RAM instead of Flash #[arg(long)] pub ram: bool, @@ -163,6 +154,8 @@ pub struct FlashArgs { /// Don't skip flashing of parts with matching checksum #[arg(long)] pub no_skip: bool, + #[clap(flatten)] + pub image: ImageArgs, } /// Operations for partitions tables @@ -214,22 +207,33 @@ pub struct ReadFlashArgs { #[non_exhaustive] #[group(skip)] pub struct SaveImageArgs { - /// Custom bootloader for merging - #[arg(long, value_name = "FILE")] - pub bootloader: Option, /// Chip to create an image for #[arg(long, value_enum)] pub chip: Chip, /// File name to save the generated image to pub file: PathBuf, - /// Minimum chip revision supported by image, in format: major.minor - #[arg(long, default_value = "0.0", value_parser = parse_chip_rev)] - pub min_chip_rev: u16, /// Boolean flag to merge binaries into single binary #[arg(long)] pub merge: bool, - /// Custom partition table for merging - #[arg(long, short = 'T', requires = "merge", value_name = "FILE")] + /// Don't pad the image to the flash size + #[arg(long, short = 'P', requires = "merge")] + pub skip_padding: bool, + /// Cristal frequency of the target + #[arg(long, short = 'x')] + pub xtal_freq: Option, + #[clap(flatten)] + pub image: ImageArgs, +} + +#[derive(Debug, Args)] +#[non_exhaustive] +#[group(skip)] +pub struct ImageArgs { + /// Path to a binary (.bin) bootloader file + #[arg(long, value_name = "FILE")] + pub bootloader: Option, + /// Path to a CSV file containing partition table + #[arg(long, short = 'T', value_name = "FILE")] pub partition_table: Option, /// Partition table offset #[arg(long, value_name = "OFFSET")] @@ -237,12 +241,9 @@ pub struct SaveImageArgs { /// Label of target app partition #[arg(long, value_name = "LABEL")] pub target_app_partition: Option, - /// Don't pad the image to the flash size - #[arg(long, short = 'P', requires = "merge")] - pub skip_padding: bool, - /// Cristal frequency of the target - #[arg(long, short = 'x')] - pub xtal_freq: Option, + /// Minimum chip revision supported by image, in format: major.minor + #[arg(long, default_value = "0.0", value_parser = parse_chip_rev)] + pub min_chip_rev: u16, } /// Open the serial monitor without flashing @@ -798,3 +799,47 @@ fn pretty_print(table: PartitionTable) { pub fn parse_uint32(input: &str) -> Result { parse_int::parse(input) } + +pub fn make_flash_settings(flash_config_args: &FlashConfigArgs, config: &Config) -> FlashSettings { + FlashSettings::new( + flash_config_args.flash_mode.or(config.flash.mode), + flash_config_args.flash_size.or(config.flash.size), + flash_config_args.flash_freq.or(config.flash.freq), + ) +} + +pub fn make_flash_data( + image_args: ImageArgs, + flash_config_args: &FlashConfigArgs, + config: &Config, + default_bootloader: Option<&Path>, + default_partition_table: Option<&Path>, +) -> Result { + let bootloader = image_args + .bootloader + .as_deref() + .or(config.bootloader.as_deref()) + .or(default_bootloader); + let partition_table = image_args + .partition_table + .as_deref() + .or(config.partition_table.as_deref()) + .or(default_partition_table); + + if let Some(path) = &bootloader { + println!("Bootloader: {}", path.display()); + } + if let Some(path) = &partition_table { + println!("Partition table: {}", path.display()); + } + + let flash_settings = make_flash_settings(flash_config_args, config); + FlashData::new( + bootloader, + partition_table, + image_args.partition_table_offset, + image_args.target_app_partition, + flash_settings, + image_args.min_chip_rev, + ) +} diff --git a/espflash/src/flasher/mod.rs b/espflash/src/flasher/mod.rs index a16fd3f3..ac2d396c 100644 --- a/espflash/src/flasher/mod.rs +++ b/espflash/src/flasher/mod.rs @@ -60,27 +60,38 @@ pub(crate) mod stubs; #[repr(u8)] pub enum FlashFrequency { /// 12 MHz + #[serde(rename = "12MHz")] _12Mhz, /// 15 MHz + #[serde(rename = "15MHz")] _15Mhz, /// 16 MHz + #[serde(rename = "16MHz")] _16Mhz, /// 20 MHz + #[serde(rename = "20MHz")] _20Mhz, /// 24 MHz + #[serde(rename = "24MHz")] _24Mhz, /// 26 MHz + #[serde(rename = "26MHz")] _26Mhz, /// 30 MHz + #[serde(rename = "30MHz")] _30Mhz, /// 40 MHz + #[serde(rename = "40MHz")] #[default] _40Mhz, /// 48 MHz + #[serde(rename = "48MHz")] _48Mhz, /// 60 MHz + #[serde(rename = "60MHz")] _60Mhz, /// 80 MHz + #[serde(rename = "80MHz")] _80Mhz, } @@ -104,6 +115,7 @@ impl FlashFrequency { #[derive(Copy, Clone, Debug, Default, VariantNames, Serialize, Deserialize)] #[non_exhaustive] #[strum(serialize_all = "lowercase")] +#[serde(rename_all = "lowercase")] pub enum FlashMode { /// Quad I/O (4 pins used for address & data) Qio, @@ -139,27 +151,38 @@ pub enum FlashMode { #[doc(alias("esp_image_flash_size_t"))] pub enum FlashSize { /// 256 KB + #[serde(rename = "256KB")] _256Kb, /// 512 KB + #[serde(rename = "512KB")] _512Kb, /// 1 MB + #[serde(rename = "1MB")] _1Mb, /// 2 MB + #[serde(rename = "2MB")] _2Mb, /// 4 MB #[default] + #[serde(rename = "4MB")] _4Mb, /// 8 MB + #[serde(rename = "8MB")] _8Mb, /// 16 MB + #[serde(rename = "16MB")] _16Mb, /// 32 MB + #[serde(rename = "32MB")] _32Mb, /// 64 MB + #[serde(rename = "64MB")] _64Mb, /// 128 MB + #[serde(rename = "128MB")] _128Mb, /// 256 MB + #[serde(rename = "256MB")] _256Mb, } @@ -242,11 +265,12 @@ impl FromStr for FlashSize { } /// Flash settings to use when flashing a device -#[derive(Copy, Clone, Debug)] +#[derive(Copy, Clone, Debug, Serialize, Deserialize, Default)] #[non_exhaustive] pub struct FlashSettings { pub mode: Option, pub size: Option, + #[serde(rename = "frequency")] pub freq: Option, }