Compare commits

15 Commits

Author SHA1 Message Date
aae8970584 The default example is now _slightly_ less cringe 2025-09-26 19:24:50 +02:00
a80db635cc Bugfix: tulflash run hot reload broke after a637486 2025-09-26 19:23:39 +02:00
073f6e2549 Turns out I only need to do this (source: Martinec) 2025-09-26 16:21:25 +02:00
7d40995934 ...even more 2025-09-25 12:09:54 +02:00
276ae61783 Improved chances for successful sync 2025-09-25 12:06:30 +02:00
62dadc65d0 Added the tulflash directory to .gitignore 2025-09-25 12:02:43 +02:00
6d8b9bc65e Literally changed a single character 2025-09-25 12:02:01 +02:00
c9c31ebd23 Updated README 2025-09-25 12:00:44 +02:00
75664c433e jfdlsjflsajflsajflkajdslf 2025-09-25 11:57:25 +02:00
3cec3eacc6 Made the docs a bit more clear 2025-09-25 11:55:43 +02:00
7ca0c605b8 Made the commandline arguments a bit more clear 2025-09-25 11:50:38 +02:00
664e402080 Fixed port auto-detection on Windows 2025-09-24 13:45:53 +02:00
329f7e1284 Now printing the list of all available ports (Wokňous issue?) 2025-09-24 13:28:44 +02:00
a637486847 Merge pull request 'Compile in separate directory to keep out the bloat' (#1) from compilation_path into master
Reviewed-on: #1
2025-09-24 11:20:03 +00:00
882cd55ec1 Now building stripped binaries 2025-09-24 13:10:03 +02:00
8 changed files with 62 additions and 21 deletions

1
.gitignore vendored
View File

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

View File

@@ -3,6 +3,10 @@ name = "tulflash"
version = "0.1.0"
edition = "2024"
[profile.release]
opt-level = "s"
strip = true
[dependencies]
argp = "0.4.0"
crossterm = "0.29.0"

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);
}
}