Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support using the built-in UART peripherials of a Raspberry Pi #234

Merged
merged 1 commit into from
Sep 26, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/rpi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ jobs:
echo "CARGO_TARGET_ARMV7_UNKNOWN_LINUX_GNUEABIHF_LINKER=arm-linux-gnueabihf-gcc" >> $GITHUB_ENV

- name: Build binary
run: cargo build --release --all --target=${{ inputs.target }}
run: cargo build --release --all --target=${{ inputs.target }} --features=raspberry

- uses: papeloto/action-zip@v1
with:
Expand Down
11 changes: 11 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion cargo-espflash/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ fn flash(
if args.flash_args.monitor {
let pid = flasher.get_usb_pid()?;
monitor(
flasher.into_serial(),
flasher.into_interface(),
Some(&elf_data),
pid,
args.connect_args.monitor_baud.unwrap_or(115_200),
Expand Down
2 changes: 2 additions & 0 deletions espflash/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ md5 = "0.7.0"
miette = { version = "5.3.0", features = ["fancy"] }
parse_int = "0.6.0"
regex = "1.6.0"
rppal = { version = "0.13", optional = true }
serde = { version = "1.0.144", features = ["derive"] }
serde-hex = "0.1.0"
serde_json = "1.0.85"
Expand All @@ -70,3 +71,4 @@ xmas-elf = "0.8.0"
[features]
default = ["cli"]
cli = ["clap", "crossterm", "dialoguer", "update-informer"]
raspberry = ["rppal"]
6 changes: 6 additions & 0 deletions espflash/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@ SUBCOMMANDS:
write-bin Writes a binary file to a specific address in the chip's flash
```

## Compile-time features

- `raspberry`: enables configuring DTR and RTS GPIOs which are necessary to use a Raspberry Pi's
internal UART peripherals. This feature is optional (external USB <-> UART converters work
without it) and adds a dependency on [`rppal`](https://crates.io/crates/rppal).

## Configuration

You can also specify the serial port and/or expected VID/PID values by setting them in the configuration file. This file is in different locations depending on your operating system:
Expand Down
2 changes: 1 addition & 1 deletion espflash/src/bin/espflash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ fn flash(mut args: FlashArgs, config: &Config) -> Result<()> {
let pid = flasher.get_usb_pid()?;

monitor(
flasher.into_serial(),
flasher.into_interface(),
Some(&elf_data),
pid,
args.connect_args.monitor_baud.unwrap_or(115_200),
Expand Down
4 changes: 4 additions & 0 deletions espflash/src/cli/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ pub struct Config {
#[derive(Debug, Deserialize, Serialize, Default, Clone)]
pub struct Connection {
pub serial: Option<String>,
#[cfg(feature = "raspberry")]
pub rts: Option<u8>,
#[cfg(feature = "raspberry")]
pub dtr: Option<u8>,
}

#[derive(Debug, Deserialize, Serialize, Default, Clone)]
Expand Down
26 changes: 18 additions & 8 deletions espflash/src/cli/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,16 @@ use std::{
use clap::Args;
use config::Config;
use miette::{IntoDiagnostic, Result, WrapErr};
use serialport::{FlowControl, SerialPortType, UsbPortInfo};
use serialport::{SerialPortType, UsbPortInfo};
use strum::VariantNames;

use crate::{
cli::{monitor::monitor, serial::get_serial_port_info},
elf::ElfFirmwareImage,
error::{Error, NoOtadataError},
error::NoOtadataError,
flasher::{FlashFrequency, FlashMode, FlashSize},
image_format::ImageFormatType,
interface::Interface,
partition_table, Chip, Flasher, ImageFormatId, InvalidPartitionTable, MissingPartitionTable,
PartitionTable,
};
Expand All @@ -40,6 +41,17 @@ pub struct ConnectArgs {
/// Serial port connected to target device
#[clap(short = 'p', long)]
pub port: Option<String>,

/// DTR pin to use for the internal UART hardware. Uses BCM numbering.
#[cfg(feature = "raspberry")]
#[cfg_attr(feature = "raspberry", clap(long))]
pub dtr: Option<u8>,

/// RTS pin to use for the internal UART hardware. Uses BCM numbering.
#[cfg(feature = "raspberry")]
#[cfg_attr(feature = "raspberry", clap(long))]
pub rts: Option<u8>,

/// Use RAM stub for loading
#[clap(long)]
pub use_stub: bool,
Expand Down Expand Up @@ -126,10 +138,8 @@ pub fn connect(args: &ConnectArgs, config: &Config) -> Result<Flasher> {
// Attempt to open the serial port and set its initial baud rate.
println!("Serial port: {}", port_info.port_name);
println!("Connecting...\n");
let serial = serialport::new(&port_info.port_name, 115_200)
.flow_control(FlowControl::None)
.open()
.map_err(Error::from)

let interface = Interface::new(&port_info, args, config)
.wrap_err_with(|| format!("Failed to open serial port {}", port_info.port_name))?;

// NOTE: since `get_serial_port_info` filters out all non-USB serial ports, we
Expand All @@ -150,7 +160,7 @@ pub fn connect(args: &ConnectArgs, config: &Config) -> Result<Flasher> {
};

Ok(Flasher::connect(
serial,
interface,
port_info,
args.baud,
args.use_stub,
Expand All @@ -169,7 +179,7 @@ pub fn serial_monitor(args: ConnectArgs, config: &Config) -> Result<()> {
let pid = flasher.get_usb_pid()?;

monitor(
flasher.into_serial(),
flasher.into_interface(),
None,
pid,
args.monitor_baud.unwrap_or(115_200),
Expand Down
23 changes: 12 additions & 11 deletions espflash/src/cli/monitor.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use std::{
io::{stdout, ErrorKind, Read, Write},
io::{stdout, ErrorKind},
time::Duration,
};

Expand All @@ -9,9 +9,8 @@ use crossterm::{
};
use espmonitor::{handle_serial, load_bin_context, SerialState};
use miette::{IntoDiagnostic, Result};
use serialport::SerialPort;

use crate::connection::reset_after_flash;
use crate::{connection::reset_after_flash, interface::Interface};

/// Converts key events from crossterm into appropriate character/escape
/// sequences which are then sent over the serial connection.
Expand Down Expand Up @@ -84,7 +83,7 @@ impl Drop for RawModeGuard {
}

pub fn monitor(
mut serial: Box<dyn SerialPort>,
mut serial: Interface,
elf: Option<&[u8]>,
pid: u16,
baud: u32,
Expand All @@ -96,8 +95,10 @@ pub fn monitor(

// Explicitly set the baud rate when starting the serial monitor, to allow using
// different rates for flashing.
serial.set_baud_rate(baud)?;
serial.set_timeout(Duration::from_millis(5))?;
serial.serial_port_mut().set_baud_rate(baud)?;
serial
.serial_port_mut()
.set_timeout(Duration::from_millis(5))?;

let _raw_mode = RawModeGuard::new();

Expand All @@ -109,12 +110,12 @@ pub fn monitor(
serial_state = SerialState::new(symbols);
} else {
serial_state = SerialState::new(None);
reset_after_flash(&mut *serial, pid)?;
reset_after_flash(&mut serial, pid)?;
}

let mut buff = [0; 1024];
loop {
let read_count = match serial.read(&mut buff) {
let read_count = match serial.serial_port_mut().read(&mut buff) {
Ok(count) => Ok(count),
Err(e) if e.kind() == ErrorKind::TimedOut => Ok(0),
Err(e) if e.kind() == ErrorKind::Interrupted => continue,
Expand All @@ -131,16 +132,16 @@ pub fn monitor(
match key.code {
KeyCode::Char('c') => break,
KeyCode::Char('r') => {
reset_after_flash(&mut *serial, pid)?;
reset_after_flash(&mut serial, pid)?;
continue;
}
_ => {}
}
}

if let Some(bytes) = handle_key_event(key) {
serial.write_all(&bytes)?;
serial.flush()?;
serial.serial_port_mut().write_all(&bytes)?;
serial.serial_port_mut().flush()?;
}
}
}
Expand Down
46 changes: 26 additions & 20 deletions espflash/src/connection.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,15 @@
use std::{
io::{BufWriter, Write},
thread::sleep,
time::Duration,
};
use std::{io::BufWriter, thread::sleep, time::Duration};

use binread::{io::Cursor, BinRead, BinReaderExt};
use bytemuck::{Pod, Zeroable};
use serialport::{SerialPort, UsbPortInfo};
use serialport::UsbPortInfo;
use slip_codec::SlipDecoder;

use crate::{
command::{Command, CommandType},
encoder::SlipEncoder,
error::{ConnectionError, Error, ResultExt, RomError, RomErrorKind},
interface::Interface,
};

const DEFAULT_CONNECT_ATTEMPTS: usize = 7;
Expand All @@ -29,7 +26,7 @@ pub struct CommandResponse {
}

pub struct Connection {
serial: Box<dyn SerialPort>,
serial: Interface,
port_info: UsbPortInfo,
decoder: SlipDecoder,
}
Expand All @@ -44,7 +41,7 @@ struct WriteRegParams {
}

impl Connection {
pub fn new(serial: Box<dyn SerialPort>, port_info: UsbPortInfo) -> Self {
pub fn new(serial: Interface, port_info: UsbPortInfo) -> Self {
Connection {
serial,
port_info,
Expand Down Expand Up @@ -119,7 +116,7 @@ impl Connection {

pub fn reset(&mut self) -> Result<(), Error> {
let pid = self.port_info.pid;
Ok(reset_after_flash(&mut *self.serial, pid)?)
Ok(reset_after_flash(&mut self.serial, pid)?)
}

pub fn reset_to_flash(&mut self, extra_delay: bool) -> Result<(), Error> {
Expand Down Expand Up @@ -161,29 +158,36 @@ impl Connection {
}

pub fn set_timeout(&mut self, timeout: Duration) -> Result<(), Error> {
self.serial.set_timeout(timeout)?;
self.serial.serial_port_mut().set_timeout(timeout)?;
Ok(())
}

pub fn set_baud(&mut self, speed: u32) -> Result<(), Error> {
self.serial.set_baud_rate(speed)?;
self.serial.serial_port_mut().set_baud_rate(speed)?;

Ok(())
}

pub fn get_baud(&self) -> Result<u32, Error> {
Ok(self.serial.baud_rate()?)
Ok(self.serial.serial_port().baud_rate()?)
}

pub fn with_timeout<T, F: FnMut(&mut Connection) -> Result<T, Error>>(
&mut self,
timeout: Duration,
mut f: F,
) -> Result<T, Error> {
let old_timeout = self.serial.timeout();
self.serial.set_timeout(timeout)?;
let old_timeout = {
let serial = self.serial.serial_port_mut();
let old_timeout = serial.timeout();
serial.set_timeout(timeout)?;
old_timeout
};

let result = f(self);
self.serial.set_timeout(old_timeout)?;

self.serial.serial_port_mut().set_timeout(old_timeout)?;

result
}

Expand All @@ -199,8 +203,10 @@ impl Connection {
}

pub fn write_command(&mut self, command: Command) -> Result<(), Error> {
self.serial.clear(serialport::ClearBuffer::Input)?;
let mut writer = BufWriter::new(&mut self.serial);
let serial = self.serial.serial_port_mut();

serial.clear(serialport::ClearBuffer::Input)?;
let mut writer = BufWriter::new(serial);
let mut encoder = SlipEncoder::new(&mut writer)?;
command.write(&mut encoder)?;
encoder.finish()?;
Expand Down Expand Up @@ -261,11 +267,11 @@ impl Connection {
}

pub fn flush(&mut self) -> Result<(), Error> {
self.serial.flush()?;
self.serial.serial_port_mut().flush()?;
Ok(())
}

pub fn into_serial(self) -> Box<dyn SerialPort> {
pub fn into_interface(self) -> Interface {
self.serial
}

Expand All @@ -274,7 +280,7 @@ impl Connection {
}
}

pub fn reset_after_flash(serial: &mut dyn SerialPort, pid: u16) -> Result<(), serialport::Error> {
pub fn reset_after_flash(serial: &mut Interface, pid: u16) -> Result<(), serialport::Error> {
sleep(Duration::from_millis(100));

if pid == USB_SERIAL_JTAG_PID {
Expand Down
13 changes: 13 additions & 0 deletions espflash/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use crate::{
command::CommandType,
flasher::{FlashFrequency, FlashMode, FlashSize},
image_format::ImageFormatId,
interface::SerialConfigError,
partition_table::{CoreType, SubType, Type},
Chip,
};
Expand Down Expand Up @@ -90,6 +91,12 @@ https://github.com/espressif/esp32c3-direct-boot-example"
help("Make sure the correct device is connected to the host system")
)]
SerialNotFound(String),
#[error("Incorrect serial port configuration")]
#[diagnostic(
code(espflash::serial_config),
help("Make sure you have specified the DTR signal if you are using an internal UART peripherial")
)]
SerialConfiguration(SerialConfigError),
#[error("Canceled by user")]
Canceled,
#[error("The flash mode '{0}' is not valid")]
Expand Down Expand Up @@ -245,6 +252,12 @@ impl From<binread::Error> for Error {
}
}

impl From<SerialConfigError> for Error {
fn from(err: SerialConfigError) -> Self {
Self::SerialConfiguration(err)
}
}

#[derive(Copy, Clone, Debug, Error, Diagnostic)]
#[allow(dead_code)]
#[repr(u8)]
Expand Down
Loading