Skip to content

Commit 3af1236

Browse files
committed
Add support for Raspberry Pi's internal UART peripherals
1 parent 812749e commit 3af1236

File tree

14 files changed

+242
-48
lines changed

14 files changed

+242
-48
lines changed

.github/workflows/rpi.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ jobs:
6767
echo "CARGO_TARGET_ARMV7_UNKNOWN_LINUX_GNUEABIHF_LINKER=arm-linux-gnueabihf-gcc" >> $GITHUB_ENV
6868
6969
- name: Build binary
70-
run: cargo build --release --all --target=${{ inputs.target }}
70+
run: cargo build --release --all --target=${{ inputs.target }} --features=raspberry
7171

7272
- uses: papeloto/action-zip@v1
7373
with:

Cargo.lock

+11
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cargo-espflash/src/main.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@ fn flash(
222222
if args.flash_args.monitor {
223223
let pid = flasher.get_usb_pid()?;
224224
monitor(
225-
flasher.into_serial(),
225+
flasher.into_interface(),
226226
Some(&elf_data),
227227
pid,
228228
args.connect_args.monitor_baud.unwrap_or(115_200),

espflash/Cargo.toml

+2
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ md5 = "0.7.0"
5353
miette = { version = "5.3.0", features = ["fancy"] }
5454
parse_int = "0.6.0"
5555
regex = "1.6.0"
56+
rppal = { version = "0.13", optional = true }
5657
serde = { version = "1.0.144", features = ["derive"] }
5758
serde-hex = "0.1.0"
5859
serde_json = "1.0.85"
@@ -70,3 +71,4 @@ xmas-elf = "0.8.0"
7071
[features]
7172
default = ["cli"]
7273
cli = ["clap", "crossterm", "dialoguer", "update-informer"]
74+
raspberry = ["rppal"]

espflash/README.md

+12
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ OPTIONS:
3636
--bootloader <BOOTLOADER>
3737
Path to a binary (.bin) bootloader file
3838
39+
--dtr <DTR> [only available when built with the "raspberry" feature]
40+
DTR pin to use for the internal UART hardware. Uses BCM numbering
41+
3942
--erase-otadata
4043
Erase the OTADATA partition This is useful when using multiple OTA partitions and still
4144
wanting to be able to reflash via espflash
@@ -67,6 +70,9 @@ OPTIONS:
6770
--ram
6871
Load the application to RAM instead of Flash
6972
73+
--rts <RTS> [only available when built with the "raspberry" feature]
74+
RTS pin to use for the internal UART hardware. Uses BCM numbering
75+
7076
-s, --flash-size <SIZE>
7177
Flash size of the target [possible values: 256KB, 512KB, 1MB, 2MB, 4MB, 8MB, 16MB, 32MB,
7278
64MB, 128MB]
@@ -90,6 +96,12 @@ SUBCOMMANDS:
9096
write-bin-to-flash Writes a binary file to a specific address in the chip's flash
9197
```
9298

99+
## Compile-time features
100+
101+
- `raspberry`: enables configuring DTR and RTS GPIOs which are necessary to use a Raspberry Pi's
102+
internal UART peripherals. This feature is optional (external USB <-> UART converters work
103+
without it) and adds a dependency on [`rppal`](https://crates.io/crates/rppal).
104+
93105
## Configuration
94106

95107
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:

espflash/src/bin/espflash.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ fn flash(mut args: FlashArgs, config: &Config) -> Result<()> {
159159
let pid = flasher.get_usb_pid()?;
160160

161161
monitor(
162-
flasher.into_serial(),
162+
flasher.into_interface(),
163163
Some(&elf_data),
164164
pid,
165165
args.connect_args.monitor_baud.unwrap_or(115_200),

espflash/src/cli/config.rs

+4
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ pub struct Config {
1212
pub connection: Connection,
1313
#[serde(default)]
1414
pub usb_device: Vec<UsbDevice>,
15+
#[cfg(feature = "raspberry")]
16+
pub rts: Option<u8>,
17+
#[cfg(feature = "raspberry")]
18+
pub dtr: Option<u8>,
1519
#[serde(skip)]
1620
save_path: PathBuf,
1721
}

espflash/src/cli/mod.rs

+18-8
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,16 @@ use std::{
1111
use clap::Args;
1212
use config::Config;
1313
use miette::{IntoDiagnostic, Result, WrapErr};
14-
use serialport::{FlowControl, SerialPortType, UsbPortInfo};
14+
use serialport::{SerialPortType, UsbPortInfo};
1515
use strum::VariantNames;
1616

1717
use crate::{
1818
cli::{monitor::monitor, serial::get_serial_port_info},
1919
elf::ElfFirmwareImage,
20-
error::{Error, NoOtadataError},
20+
error::NoOtadataError,
2121
flasher::{FlashFrequency, FlashMode, FlashSize},
2222
image_format::ImageFormatType,
23+
interface::Interface,
2324
partition_table, Chip, Flasher, ImageFormatId, InvalidPartitionTable, MissingPartitionTable,
2425
PartitionTable,
2526
};
@@ -40,6 +41,17 @@ pub struct ConnectArgs {
4041
/// Serial port connected to target device
4142
#[clap(short = 'p', long)]
4243
pub port: Option<String>,
44+
45+
/// DTR pin to use for the internal UART hardware. Uses BCM numbering.
46+
#[cfg(feature = "raspberry")]
47+
#[cfg_attr(feature = "raspberry", clap(long))]
48+
pub dtr: Option<u8>,
49+
50+
/// RTS pin to use for the internal UART hardware. Uses BCM numbering.
51+
#[cfg(feature = "raspberry")]
52+
#[cfg_attr(feature = "raspberry", clap(long))]
53+
pub rts: Option<u8>,
54+
4355
/// Use RAM stub for loading
4456
#[clap(long)]
4557
pub use_stub: bool,
@@ -126,10 +138,8 @@ pub fn connect(args: &ConnectArgs, config: &Config) -> Result<Flasher> {
126138
// Attempt to open the serial port and set its initial baud rate.
127139
println!("Serial port: {}", port_info.port_name);
128140
println!("Connecting...\n");
129-
let serial = serialport::new(&port_info.port_name, 115_200)
130-
.flow_control(FlowControl::None)
131-
.open()
132-
.map_err(Error::from)
141+
142+
let interface = Interface::new(&port_info, config)
133143
.wrap_err_with(|| format!("Failed to open serial port {}", port_info.port_name))?;
134144

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

152162
Ok(Flasher::connect(
153-
serial,
163+
interface,
154164
port_info,
155165
args.baud,
156166
args.use_stub,
@@ -169,7 +179,7 @@ pub fn serial_monitor(args: ConnectArgs, config: &Config) -> Result<()> {
169179
let pid = flasher.get_usb_pid()?;
170180

171181
monitor(
172-
flasher.into_serial(),
182+
flasher.into_interface(),
173183
None,
174184
pid,
175185
args.monitor_baud.unwrap_or(115_200),

espflash/src/cli/monitor.rs

+12-11
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use std::{
2-
io::{stdout, ErrorKind, Read, Write},
2+
io::{stdout, ErrorKind},
33
time::Duration,
44
};
55

@@ -9,9 +9,8 @@ use crossterm::{
99
};
1010
use espmonitor::{handle_serial, load_bin_context, SerialState};
1111
use miette::{IntoDiagnostic, Result};
12-
use serialport::SerialPort;
1312

14-
use crate::connection::reset_after_flash;
13+
use crate::{connection::reset_after_flash, interface::Interface};
1514

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

8685
pub fn monitor(
87-
mut serial: Box<dyn SerialPort>,
86+
mut serial: Interface,
8887
elf: Option<&[u8]>,
8988
pid: u16,
9089
baud: u32,
@@ -96,8 +95,10 @@ pub fn monitor(
9695

9796
// Explicitly set the baud rate when starting the serial monitor, to allow using
9897
// different rates for flashing.
99-
serial.set_baud_rate(baud)?;
100-
serial.set_timeout(Duration::from_millis(5))?;
98+
serial.serial_port_mut().set_baud_rate(baud)?;
99+
serial
100+
.serial_port_mut()
101+
.set_timeout(Duration::from_millis(5))?;
101102

102103
let _raw_mode = RawModeGuard::new();
103104

@@ -109,12 +110,12 @@ pub fn monitor(
109110
serial_state = SerialState::new(symbols);
110111
} else {
111112
serial_state = SerialState::new(None);
112-
reset_after_flash(&mut *serial, pid)?;
113+
reset_after_flash(&mut serial, pid)?;
113114
}
114115

115116
let mut buff = [0; 1024];
116117
loop {
117-
let read_count = match serial.read(&mut buff) {
118+
let read_count = match serial.serial_port_mut().read(&mut buff) {
118119
Ok(count) => Ok(count),
119120
Err(e) if e.kind() == ErrorKind::TimedOut => Ok(0),
120121
Err(e) if e.kind() == ErrorKind::Interrupted => continue,
@@ -131,16 +132,16 @@ pub fn monitor(
131132
match key.code {
132133
KeyCode::Char('c') => break,
133134
KeyCode::Char('r') => {
134-
reset_after_flash(&mut *serial, pid)?;
135+
reset_after_flash(&mut serial, pid)?;
135136
continue;
136137
}
137138
_ => {}
138139
}
139140
}
140141

141142
if let Some(bytes) = handle_key_event(key) {
142-
serial.write_all(&bytes)?;
143-
serial.flush()?;
143+
serial.serial_port_mut().write_all(&bytes)?;
144+
serial.serial_port_mut().flush()?;
144145
}
145146
}
146147
}

espflash/src/connection.rs

+26-20
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,15 @@
1-
use std::{
2-
io::{BufWriter, Write},
3-
thread::sleep,
4-
time::Duration,
5-
};
1+
use std::{io::BufWriter, thread::sleep, time::Duration};
62

73
use binread::{io::Cursor, BinRead, BinReaderExt};
84
use bytemuck::{Pod, Zeroable};
9-
use serialport::{SerialPort, UsbPortInfo};
5+
use serialport::UsbPortInfo;
106
use slip_codec::SlipDecoder;
117

128
use crate::{
139
command::{Command, CommandType},
1410
encoder::SlipEncoder,
1511
error::{ConnectionError, Error, ResultExt, RomError, RomErrorKind},
12+
interface::Interface,
1613
};
1714

1815
const DEFAULT_CONNECT_ATTEMPTS: usize = 7;
@@ -29,7 +26,7 @@ pub struct CommandResponse {
2926
}
3027

3128
pub struct Connection {
32-
serial: Box<dyn SerialPort>,
29+
serial: Interface,
3330
port_info: UsbPortInfo,
3431
decoder: SlipDecoder,
3532
}
@@ -44,7 +41,7 @@ struct WriteRegParams {
4441
}
4542

4643
impl Connection {
47-
pub fn new(serial: Box<dyn SerialPort>, port_info: UsbPortInfo) -> Self {
44+
pub fn new(serial: Interface, port_info: UsbPortInfo) -> Self {
4845
Connection {
4946
serial,
5047
port_info,
@@ -119,7 +116,7 @@ impl Connection {
119116

120117
pub fn reset(&mut self) -> Result<(), Error> {
121118
let pid = self.port_info.pid;
122-
Ok(reset_after_flash(&mut *self.serial, pid)?)
119+
Ok(reset_after_flash(&mut self.serial, pid)?)
123120
}
124121

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

163160
pub fn set_timeout(&mut self, timeout: Duration) -> Result<(), Error> {
164-
self.serial.set_timeout(timeout)?;
161+
self.serial.serial_port_mut().set_timeout(timeout)?;
165162
Ok(())
166163
}
167164

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

171168
Ok(())
172169
}
173170

174171
pub fn get_baud(&self) -> Result<u32, Error> {
175-
Ok(self.serial.baud_rate()?)
172+
Ok(self.serial.serial_port().baud_rate()?)
176173
}
177174

178175
pub fn with_timeout<T, F: FnMut(&mut Connection) -> Result<T, Error>>(
179176
&mut self,
180177
timeout: Duration,
181178
mut f: F,
182179
) -> Result<T, Error> {
183-
let old_timeout = self.serial.timeout();
184-
self.serial.set_timeout(timeout)?;
180+
let old_timeout = {
181+
let serial = self.serial.serial_port_mut();
182+
let old_timeout = serial.timeout();
183+
serial.set_timeout(timeout)?;
184+
old_timeout
185+
};
186+
185187
let result = f(self);
186-
self.serial.set_timeout(old_timeout)?;
188+
189+
self.serial.serial_port_mut().set_timeout(old_timeout)?;
190+
187191
result
188192
}
189193

@@ -199,8 +203,10 @@ impl Connection {
199203
}
200204

201205
pub fn write_command(&mut self, command: Command) -> Result<(), Error> {
202-
self.serial.clear(serialport::ClearBuffer::Input)?;
203-
let mut writer = BufWriter::new(&mut self.serial);
206+
let serial = self.serial.serial_port_mut();
207+
208+
serial.clear(serialport::ClearBuffer::Input)?;
209+
let mut writer = BufWriter::new(serial);
204210
let mut encoder = SlipEncoder::new(&mut writer)?;
205211
command.write(&mut encoder)?;
206212
encoder.finish()?;
@@ -261,11 +267,11 @@ impl Connection {
261267
}
262268

263269
pub fn flush(&mut self) -> Result<(), Error> {
264-
self.serial.flush()?;
270+
self.serial.serial_port_mut().flush()?;
265271
Ok(())
266272
}
267273

268-
pub fn into_serial(self) -> Box<dyn SerialPort> {
274+
pub fn into_interface(self) -> Interface {
269275
self.serial
270276
}
271277

@@ -274,7 +280,7 @@ impl Connection {
274280
}
275281
}
276282

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

280286
if pid == USB_SERIAL_JTAG_PID {

0 commit comments

Comments
 (0)