Compare commits

..

13 Commits

7 changed files with 58 additions and 21 deletions

1
.gitignore vendored
View File

@@ -1 +1,2 @@
/target
tulflash

View File

@@ -2,29 +2,36 @@
## Installation instructions
No need to clone this repo manually. You'll just need to install the Rust toolchain and SDCC.
No need to clone this repo manually. You'll just need to install the Rust toolchain.
```bash
# All OSes
cargo install --git https://git.zumepro.cz/michal.prochazka/tulflash
```
> [!NOTE]
> For macOS/Linux users: remember to add `$HOME/.cargo/bin` to your `$PATH` if you have not done so already!
## Running the example program
To compile the example program using tulflash, you will need to install SDCC.
```bash
# Generate the example program (only needed to run once):
tulflash example
# Compile, flash and monitor the example program:
tulflash run example.c:
tulflash run example.c
# note: `tulflash run example.c` is the thing as `tulflash write --cmd "sdcc example.c" --monitor example.ihx`
# note: `tulflash run example.c` is the same thing as `mkdir "tulflash/" && tulflash write --cmd "sdcc example.c" --cmd-path "tulflash/" --monitor example.ihx`
```
If the flash fails, try to add `--slow` before `run` (should not be needed though).
## Manually flashing Intel HEX binaries
If you use an external toolchain (such as MCU8051IDE with SDCC) and only want to use this program just for flashing the generated HEX file, just run the following:
```bash
# All OSes, automatically detects the correct serial port
tulflash write <path-to-hex>

View File

@@ -298,7 +298,7 @@ void main(void) {
while(1) {
lcd_clear();
printf("hellOwOrld! %d\n", i);
printf("Hellorld! %d\n", i);
led_bar_set(i);
LED_RED = !(i & 1);

View File

@@ -57,14 +57,14 @@ pub struct ArgWrite {
#[argp(description = "Path to the source Intel HEX file.")]
pub path: OsString,
#[argp(option)]
#[argp(description = "Where to run the command. Will be created if it doesn't exist.")]
pub cmd_path: Option<std::path::PathBuf>,
#[argp(option)]
#[argp(description = "Command to run before writing the file. Handy for compiling the program and running it at the same time.")]
pub cmd: Option<OsString>,
#[argp(option)]
#[argp(description = "Where to run the command. If no command is specified, this option is ignored.")]
pub cmd_path: Option<std::path::PathBuf>,
#[argp(option, default = "0")]
#[argp(description = "Start address where to write from. Only for raw binaries, defaults to 0.")]
pub start: usize,
@@ -82,7 +82,7 @@ pub struct ArgWrite {
pub skip_verify: bool,
#[argp(switch)]
#[argp(description = "Write individual sections to Flash rather than performing a single write. Only applies to Intel HEX.")]
#[argp(description = "Write individual sections to Flash rather than performing a single write. Only applies to Intel HEX. Can in some very specific cases speed up the writing process.")]
pub write_individual: bool,
#[argp(switch)]
@@ -101,7 +101,7 @@ pub struct ArgErase {}
pub struct ArgMonitor {}
#[derive(FromArgs)]
#[argp(description = "Handy shorthand for `write --cmd \"sdcc <srcpath>\" --monitor <hexpath>`.")]
#[argp(description = "Handy shorthand for `write --cmd \"sdcc <srcpath>\" --cmd-path \"<srcparentpath>/tulflash\" --monitor <hexpath>`.")]
#[argp(subcommand, name = "run")]
pub struct ArgRun {
#[argp(positional)]

View File

@@ -270,11 +270,10 @@ fn run_chip(args: ArgRun, target: &mut PhysicalTarget) -> Result<(), Error> {
let source_file = build_dir.join(&source_file_name);
let output_file = source_file.with_extension("ihx");
let cmd = Some(OsString::from(format!("sdcc \"{}\"", source_file_name.to_string_lossy())));
let cmd = Some(OsString::from(format!("sdcc \"{}\"", args.path.canonicalize()?.to_string_lossy())));
let cmd_path = Some(build_dir.clone());
std::fs::create_dir_all(build_dir)?;
std::fs::copy(args.path, &source_file)?;
write_chip(ArgWrite {
path: output_file.into_os_string(),
@@ -318,9 +317,21 @@ fn main() {
let start = SystemTime::now();
let port = args.port.or_else(|| {
for port in serial_enumerator::get_serial_list() {
if let Some(vendor) = port.vendor && vendor.as_str() == "TUL" {
return Some(port.name.into())
let ports = serial_enumerator::get_serial_list();
// First try TUL (works on Linux and macOS)...
for port in &ports {
if let Some(vendor) = &port.vendor && vendor.as_str() == "TUL" {
return Some(port.name.clone().into())
}
}
// ...then try FTDI (which Windows overrides)
for port in &ports {
if let Some(vendor) = &port.vendor && vendor.as_str() == "FTDI" {
return Some(port.name.clone().into())
}
}
@@ -330,6 +341,25 @@ fn main() {
let Some(port) = port else {
println!("Could not find any compatible serial port. Please make sure that the target is connected.");
println!("If the issue persists, please specify the port manually with the `--port ...` argument.");
println!();
let ports = serial_enumerator::get_serial_list();
if ports.is_empty() {
println!("No usable ports detected.");
}
if !ports.is_empty() {
println!("Possible ports:");
for port in serial_enumerator::get_serial_list() {
println!(" - {} ({}, {})",
port.name,
port.vendor.unwrap_or_else(|| "unknown vendor".to_string()),
port.product.unwrap_or_else(|| "unknown product".to_string()));
}
}
std::process::exit(1);
};

View File

@@ -92,10 +92,9 @@ impl Monitor {
if key == KeyCode::Char('r') && mods == KeyModifiers::CONTROL {
stdout.write_all(b"\r\n*** Reboot ***\r\n")?;
port.set_dtr(true)?;
port.set_rts(true)?;
port.set_rts(false)?;
std::thread::sleep(Duration::from_millis(10));
port.set_dtr(false)?;
port.set_rts(false)?;
continue
}

View File

@@ -37,7 +37,6 @@ impl PhysicalTarget {
}
pub fn resync_target(&mut self) -> Result<(), Error> {
self.port.discard_buffers()?;
self.port.set_dtr(true)?;
self.port.set_rts(true)?;
std::thread::sleep(Duration::from_millis(10));
@@ -45,10 +44,12 @@ impl PhysicalTarget {
std::thread::sleep(Duration::from_millis(100));
for i in (0..5).rev() {
self.port.discard_buffers()?;
std::thread::sleep(Duration::from_millis(10));
self.port.write(b"U")?;
let mut buf = [0u8; 1];
self.port.read(&mut buf)?;
let _ = self.port.read(&mut buf);
if buf[0] == b'U' { break }
@@ -243,10 +244,9 @@ impl Target for PhysicalTarget {
impl Drop for PhysicalTarget {
fn drop(&mut self) {
let _ = self.port.set_dtr(true);
let _ = self.port.set_rts(true);
let _ = self.port.set_rts(false);
std::thread::sleep(Duration::from_millis(100));
let _ = self.port.set_dtr(false);
let _ = self.port.set_rts(false);
}
}