Compare commits
26 Commits
b2ab748ffb
...
master
Author | SHA1 | Date | |
---|---|---|---|
aae8970584 | |||
a80db635cc | |||
073f6e2549 | |||
7d40995934 | |||
276ae61783 | |||
62dadc65d0 | |||
6d8b9bc65e | |||
c9c31ebd23 | |||
75664c433e | |||
3cec3eacc6 | |||
7ca0c605b8 | |||
664e402080 | |||
329f7e1284 | |||
a637486847 | |||
04b642776d | |||
882cd55ec1 | |||
9f4a61a7ec
|
|||
d35c88e38f | |||
cf04037f9d | |||
2a88835da5 | |||
aa84a2c03b | |||
52a232f1e9 | |||
ab9078ab3f | |||
37f15bf187 | |||
e4c4a7f14a | |||
d7b70b7629 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1 +1,2 @@
|
||||
/target
|
||||
tulflash
|
||||
|
325
Cargo.lock
generated
325
Cargo.lock
generated
@@ -44,6 +44,12 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.9.4"
|
||||
@@ -56,6 +62,15 @@ version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9"
|
||||
|
||||
[[package]]
|
||||
name = "convert_case"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bb402b8d4c85569410425650ce3eddc7d698ed96d39a73f941b08fb63082f1e7"
|
||||
dependencies = [
|
||||
"unicode-segmentation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-channel"
|
||||
version = "0.5.15"
|
||||
@@ -71,12 +86,79 @@ version = "0.8.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
|
||||
|
||||
[[package]]
|
||||
name = "crossterm"
|
||||
version = "0.29.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d8b9f2e4c67f833b660cdb0a3523065869fb35570177239812ed4c905aeff87b"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"crossterm_winapi",
|
||||
"derive_more",
|
||||
"document-features",
|
||||
"mio",
|
||||
"parking_lot",
|
||||
"rustix",
|
||||
"signal-hook",
|
||||
"signal-hook-mio",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossterm_winapi"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "defer-lite"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d6a1ef0750c0fcdb9bd00e7829fcad58e9a0a2ab7078b57c39743b973f3878ce"
|
||||
|
||||
[[package]]
|
||||
name = "derive_more"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678"
|
||||
dependencies = [
|
||||
"derive_more-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_more-impl"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3"
|
||||
dependencies = [
|
||||
"convert_case",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "document-features"
|
||||
version = "0.2.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95249b50c6c185bee49034bcb378a49dc2b5dff0be90ff6616d31d64febab05d"
|
||||
dependencies = [
|
||||
"litrs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.3.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.61.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getopts"
|
||||
version = "0.2.24"
|
||||
@@ -93,16 +175,33 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543"
|
||||
|
||||
[[package]]
|
||||
name = "libredox"
|
||||
version = "0.1.10"
|
||||
name = "linux-raw-sys"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "416f7e718bdb06000964960ffa43b4335ad4012ae8b99060261aa4a8088d5ccb"
|
||||
checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039"
|
||||
|
||||
[[package]]
|
||||
name = "litrs"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f5e54036fe321fd421e10d732f155734c4e4afd610dd556d9a82833ab3ee0bed"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"autocfg",
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432"
|
||||
|
||||
[[package]]
|
||||
name = "mach"
|
||||
version = "0.1.2"
|
||||
@@ -124,6 +223,18 @@ version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
||||
|
||||
[[package]]
|
||||
name = "mio"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
"wasi",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nom"
|
||||
version = "7.1.3"
|
||||
@@ -135,10 +246,27 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "numtoa"
|
||||
version = "0.2.4"
|
||||
name = "parking_lot"
|
||||
version = "0.12.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6aa2c4e539b869820a2b82e1aef6ff40aa85e65decdd5185e83fb4b1249cd00f"
|
||||
checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13"
|
||||
dependencies = [
|
||||
"lock_api",
|
||||
"parking_lot_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
version = "0.9.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"smallvec",
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pbr"
|
||||
@@ -191,10 +319,23 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_termios"
|
||||
version = "0.1.3"
|
||||
name = "rustix"
|
||||
version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "20145670ba436b55d91fc92d25e71160fbfbdd57831631c8d7d36377a476f1cb"
|
||||
checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys 0.61.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||
|
||||
[[package]]
|
||||
name = "serial2"
|
||||
@@ -228,6 +369,42 @@ version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook"
|
||||
version = "0.3.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d881a16cf4426aa584979d30bd82cb33429027e42122b169753d6ef1085ed6e2"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"signal-hook-registry",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-mio"
|
||||
version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34db1a06d485c9142248b7a054f034b349b212551f3dfd19c94d45a754a217cd"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"mio",
|
||||
"signal-hook",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-registry"
|
||||
version = "1.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.15.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.106"
|
||||
@@ -239,28 +416,16 @@ dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termion"
|
||||
version = "4.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3669a69de26799d6321a5aa713f55f7e2cd37bd47be044b50f2acafc42c122bb"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"libredox",
|
||||
"numtoa",
|
||||
"redox_termios",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tulflash"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"argp",
|
||||
"crossterm",
|
||||
"pbr",
|
||||
"serial2",
|
||||
"serial_enumerator",
|
||||
"shlex",
|
||||
"termion",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -275,12 +440,24 @@ version = "1.0.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-segmentation"
|
||||
version = "1.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4a1a07cc7db3810833284e8d372ccdc6da29741639ecc70c9ec107df0fa6154c"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.1+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
@@ -309,39 +486,127 @@ version = "0.29.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aac7fef12f4b59cd0a29339406cc9203ab44e440ddff6b3f5a41455349fa9cf3"
|
||||
dependencies = [
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_msvc",
|
||||
"windows_aarch64_msvc 0.29.0",
|
||||
"windows_i686_gnu 0.29.0",
|
||||
"windows_i686_msvc 0.29.0",
|
||||
"windows_x86_64_gnu 0.29.0",
|
||||
"windows_x86_64_msvc 0.29.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-link"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.59.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.61.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e201184e40b2ede64bc2ea34968b28e33622acdbbf37104f0e4a33f7abe657aa"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc 0.52.6",
|
||||
"windows_i686_gnu 0.52.6",
|
||||
"windows_i686_gnullvm",
|
||||
"windows_i686_msvc 0.52.6",
|
||||
"windows_x86_64_gnu 0.52.6",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.29.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3d027175d00b01e0cbeb97d6ab6ebe03b12330a35786cbaca5252b1c4bf5d9b"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.29.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8793f59f7b8e8b01eda1a652b2697d87b93097198ae85f823b969ca5b89bba58"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.29.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8602f6c418b67024be2996c512f5f995de3ba417f4c75af68401ab8756796ae4"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.29.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f3d615f419543e0bd7d2b3323af0d86ff19cbc4f816e6453f36a2c2ce889c354"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.29.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "11d95421d9ed3672c280884da53201a5c46b7b2765ca6faf34b0d71cf34a3561"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
|
@@ -3,10 +3,14 @@ name = "tulflash"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[profile.release]
|
||||
opt-level = "s"
|
||||
strip = true
|
||||
|
||||
[dependencies]
|
||||
argp = "0.4.0"
|
||||
crossterm = "0.29.0"
|
||||
pbr = "1.1.1"
|
||||
serial2 = "0.2.32"
|
||||
serial_enumerator = "0.2.12"
|
||||
shlex = "1.3.0"
|
||||
termion = "4.0.5"
|
||||
|
37
README.md
37
README.md
@@ -9,24 +9,33 @@ No need to clone this repo manually. You'll just need to install the Rust toolch
|
||||
cargo install --git https://git.zumepro.cz/michal.prochazka/tulflash
|
||||
```
|
||||
|
||||
## Flashing Intel HEX binaries
|
||||
> [!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
|
||||
# All OSes, automatically detects all ports
|
||||
# Generate the example program (only needed to run once):
|
||||
tulflash example
|
||||
|
||||
# Compile, flash and monitor the example program:
|
||||
tulflash run example.c
|
||||
|
||||
# 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>
|
||||
```
|
||||
|
||||
If the flash fails, try to add `--slow` before `write` (should not be needed though).
|
||||
|
||||
The tool also allows reading and erasing the chip, figure that out on your own if you need that (although `tulflash --help` might guide you if you ask it nicely).
|
||||
|
||||
## Example program
|
||||
|
||||
You'll need to clone this repo (or just [download the file](example.c)) and install SDCC:
|
||||
|
||||
```bash
|
||||
tulflash run example.c
|
||||
|
||||
# Same thing as `tulflash write --cmd "sdcc example.c" --monitor example.ihx`
|
||||
```
|
||||
|
||||
|
33
example.c
33
example.c
@@ -8,19 +8,12 @@
|
||||
//**********************************************************************
|
||||
//**********************************************************************
|
||||
|
||||
#include <at89c55.h>
|
||||
#include <at89c51ed2.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define TICK_RATE 100
|
||||
#define NPER (0x10000 - (20000000 / 12 / TICK_RATE))
|
||||
|
||||
// Missing port definitions
|
||||
__sbit __at (0xC4) P4_4;
|
||||
__sbit __at (0xC3) P4_3;
|
||||
__sbit __at (0xC2) P4_2;
|
||||
__sbit __at (0xC1) P4_1;
|
||||
__sbit __at (0xC0) P4_0;
|
||||
#define NPER (0x10000 - (20000000 / 6 / TICK_RATE))
|
||||
|
||||
// I/O definitions
|
||||
#define BUTTON_1 P3_2
|
||||
@@ -71,8 +64,7 @@ uint8_t lcd_pos;
|
||||
|
||||
// Quick'n'dirty CPU pause, just enough for the LCD to stabilize.
|
||||
void lcd_pause(void) {
|
||||
int i = 30;
|
||||
while(i--);
|
||||
__asm__ ("nop");
|
||||
}
|
||||
|
||||
// Reads the status register from the LCD.
|
||||
@@ -170,11 +162,8 @@ void lcd_init(void) {
|
||||
// Puts a character to the LCD. Correctly handles '\n'.
|
||||
void lcd_putchar(char c) {
|
||||
if(c == 10) {
|
||||
while((lcd_pos != 0) && (lcd_pos != 40)) {
|
||||
lcd_send_data(32);
|
||||
lcd_pos++;
|
||||
if(lcd_pos == 80) lcd_pos=0;
|
||||
}
|
||||
if(lcd_pos < 40) lcd_pos = 40;
|
||||
else lcd_pos = 0;
|
||||
} else {
|
||||
lcd_send_data(c);
|
||||
lcd_pos++;
|
||||
@@ -184,6 +173,8 @@ void lcd_putchar(char c) {
|
||||
|
||||
// Initializes the background tick timer (for delays).
|
||||
void timer_init(void) {
|
||||
CKCON0 = X2; // Enable 2x clock mode
|
||||
|
||||
// Initialize timer 0 as 16-bit countdown, timer 1 as 8-bit auto-reload countdown
|
||||
|
||||
TMOD = 0x21;
|
||||
@@ -196,7 +187,8 @@ void timer_init(void) {
|
||||
|
||||
// Enable interrupts only for timer 0
|
||||
|
||||
IE = 0x82;
|
||||
EA = 1;
|
||||
ET0 = 1;
|
||||
}
|
||||
|
||||
// Initializes the serial port (along with timer 2).
|
||||
@@ -204,7 +196,7 @@ void serial_init(void) {
|
||||
// Initialize timer 2 as 312500 Hz as the baud rate generator (for tulflash)
|
||||
|
||||
RCAP2H = 0xFF;
|
||||
RCAP2L = 0xFE;
|
||||
RCAP2L = 0xFC;
|
||||
T2CON = 0x34;
|
||||
|
||||
// Initialize serial I/O
|
||||
@@ -217,6 +209,7 @@ void serial_init(void) {
|
||||
void serial_putchar(char c) {
|
||||
while(TI == 0);
|
||||
SBUF = c;
|
||||
TI = 0;
|
||||
}
|
||||
|
||||
__bit stdout_to_lcd = 0;
|
||||
@@ -296,6 +289,8 @@ void main(void) {
|
||||
lcd_init();
|
||||
serial_init();
|
||||
|
||||
delay(1);
|
||||
|
||||
stdout_to_serial = 1;
|
||||
stdout_to_lcd = 1;
|
||||
|
||||
@@ -303,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);
|
||||
|
28
src/args.rs
28
src/args.rs
@@ -24,7 +24,8 @@ pub enum ArgCommand {
|
||||
Write(ArgWrite),
|
||||
Erase(ArgErase),
|
||||
Monitor(ArgMonitor),
|
||||
Run(ArgRun)
|
||||
Run(ArgRun),
|
||||
Example(ArgExample)
|
||||
}
|
||||
|
||||
#[derive(FromArgs)]
|
||||
@@ -60,6 +61,10 @@ pub struct ArgWrite {
|
||||
#[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,
|
||||
@@ -77,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)]
|
||||
@@ -91,18 +96,27 @@ pub struct ArgWrite {
|
||||
pub struct ArgErase {}
|
||||
|
||||
#[derive(FromArgs)]
|
||||
#[argp(description = "Handy shorthand for `write --cmd \"sdcc <srcpath>\" --monitor <hexpath>`.")]
|
||||
#[argp(description = "Opens the serial monitor.")]
|
||||
#[argp(subcommand, name = "monitor")]
|
||||
pub struct ArgMonitor {}
|
||||
|
||||
#[derive(FromArgs)]
|
||||
#[argp(description = "Handy shorthand for `write --cmd \"sdcc <srcpath>\" --cmd-path \"<srcparentpath>/tulflash\" --monitor <hexpath>`.")]
|
||||
#[argp(subcommand, name = "run")]
|
||||
pub struct ArgRun {
|
||||
#[argp(positional)]
|
||||
#[argp(description = "Path to the source C file.")]
|
||||
pub path: OsString
|
||||
pub path: std::path::PathBuf
|
||||
}
|
||||
|
||||
#[derive(FromArgs)]
|
||||
#[argp(description = "Opens the serial monitor.")]
|
||||
#[argp(subcommand, name = "monitor")]
|
||||
pub struct ArgMonitor {}
|
||||
#[argp(description = "Generates a \"stub\" C file for the TUL výukový přípravek™.")]
|
||||
#[argp(subcommand, name = "example")]
|
||||
pub struct ArgExample {
|
||||
#[argp(positional)]
|
||||
#[argp(description = "Path to the source C file to be generated.")]
|
||||
pub path: Option<OsString>
|
||||
}
|
||||
|
||||
pub fn parse() -> Args {
|
||||
let args: Args = argp::parse_args_or_exit(argp::DEFAULT);
|
||||
|
97
src/main.rs
97
src/main.rs
@@ -3,6 +3,7 @@ use std::io::{Cursor, Error, Write};
|
||||
use std::path::PathBuf;
|
||||
use std::time::{Duration, SystemTime};
|
||||
|
||||
use crossterm::event::{KeyCode, KeyModifiers};
|
||||
use pbr::ProgressBar;
|
||||
|
||||
mod args;
|
||||
@@ -16,7 +17,6 @@ use monitor::Monitor;
|
||||
|
||||
mod target;
|
||||
use target::{PhysicalTarget, Target};
|
||||
use termion::event::Key;
|
||||
|
||||
mod utils;
|
||||
|
||||
@@ -204,16 +204,23 @@ fn write_chip_data(args: &ArgWrite, target: &mut PhysicalTarget) -> Result<(), E
|
||||
fn write_chip(args: ArgWrite, target: &mut PhysicalTarget) -> Result<(), Error> {
|
||||
loop {
|
||||
if let Some(cmd) = args.cmd.as_ref() {
|
||||
let cmd_path = &args.cmd_path;
|
||||
let mut args = shlex::bytes::split(cmd.as_encoded_bytes())
|
||||
.ok_or(Error::other("Error parsing commandline arguments"))?
|
||||
.into_iter()
|
||||
.map(|x| unsafe { OsString::from_encoded_bytes_unchecked(x) });
|
||||
|
||||
if let Some(cmd_path) = cmd_path {
|
||||
if !cmd_path.is_dir() {
|
||||
Err(Error::other("invalid cmd path (not a dir)"))?
|
||||
}
|
||||
}
|
||||
let Some(name) = args.next() else {
|
||||
Err(Error::other("No command provided"))?
|
||||
};
|
||||
|
||||
let out = std::process::Command::new(name)
|
||||
.current_dir(cmd_path.as_ref().map(|p| p.as_path()).unwrap_or(&std::path::Path::new(".")))
|
||||
.args(args)
|
||||
.output()?;
|
||||
|
||||
@@ -230,11 +237,13 @@ fn write_chip(args: ArgWrite, target: &mut PhysicalTarget) -> Result<(), Error>
|
||||
|
||||
if !args.monitor { break }
|
||||
|
||||
let cause = Monitor::new()
|
||||
.add_hook(Key::Ctrl('f'), if args.cmd.is_none() { "Re-flash target" } else { "Re-run command and flash target" })
|
||||
.run(target.port())?;
|
||||
let mut mon = Monitor::new();
|
||||
|
||||
if cause != Key::Ctrl('f') { break }
|
||||
let reflash_handle = mon.add_hook(KeyCode::Char('f'), KeyModifiers::CONTROL, if args.cmd.is_none() { "Re-flash target" } else { "Re-run command and flash target" });
|
||||
|
||||
let cause = mon.run(target.port())?;
|
||||
|
||||
if cause != reflash_handle { break }
|
||||
|
||||
target.resync_target()?;
|
||||
}
|
||||
@@ -255,11 +264,20 @@ fn monitor_chip(_args: ArgMonitor, target: &mut PhysicalTarget) -> Result<(), Er
|
||||
}
|
||||
|
||||
fn run_chip(args: ArgRun, target: &mut PhysicalTarget) -> Result<(), Error> {
|
||||
let path = PathBuf::from(&args.path).with_extension("ihx").into_os_string();
|
||||
let cmd = Some(OsString::from(format!("sdcc \"{}\"", unsafe { String::from_utf8_unchecked(args.path.into_encoded_bytes()) })));
|
||||
let build_dir = args.path.parent().unwrap().join("tulflash");
|
||||
|
||||
let source_file_name = args.path.file_name().ok_or(Error::other("invalid path to compilation source"))?;
|
||||
let source_file = build_dir.join(&source_file_name);
|
||||
let output_file = source_file.with_extension("ihx");
|
||||
|
||||
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)?;
|
||||
|
||||
write_chip(ArgWrite {
|
||||
path,
|
||||
path: output_file.into_os_string(),
|
||||
cmd_path,
|
||||
cmd,
|
||||
start: 0,
|
||||
end: 65535,
|
||||
@@ -270,15 +288,50 @@ fn run_chip(args: ArgRun, target: &mut PhysicalTarget) -> Result<(), Error> {
|
||||
}, target)
|
||||
}
|
||||
|
||||
fn gen_example(args: ArgExample) -> Result<(), Error> {
|
||||
let path = PathBuf::from(args.path.unwrap_or(OsString::from("example.c")));
|
||||
|
||||
if path.try_exists()? {
|
||||
Err(Error::other("File already exists"))?
|
||||
}
|
||||
|
||||
std::fs::write(&path, include_bytes!("../example.c"))?;
|
||||
|
||||
let path = path.to_string_lossy();
|
||||
println!("Example source code generated to `{}`.", path);
|
||||
println!("Execute `tulflash run \"{}\"` to run it.", path);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let args = args::parse();
|
||||
|
||||
if let ArgCommand::Example(args) = args.command {
|
||||
if let Err(err) = gen_example(args) {
|
||||
println!("Error generating example source code: {}", err);
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
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())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -288,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);
|
||||
};
|
||||
|
||||
@@ -298,7 +370,8 @@ fn main() {
|
||||
ArgCommand::Write(args) => write_chip(args, &mut target),
|
||||
ArgCommand::Erase(args) => erase_chip(args, &mut target),
|
||||
ArgCommand::Monitor(args) => monitor_chip(args, &mut target),
|
||||
ArgCommand::Run(args) => run_chip(args, &mut target)
|
||||
ArgCommand::Run(args) => run_chip(args, &mut target),
|
||||
ArgCommand::Example(_) => unreachable!()
|
||||
};
|
||||
|
||||
if let Err(err) = res {
|
||||
|
@@ -1,32 +1,46 @@
|
||||
use std::io::{Error, ErrorKind, Write};
|
||||
use std::time::Duration;
|
||||
|
||||
use crossterm::event::{Event, KeyCode, KeyModifiers};
|
||||
use serial2::SerialPort;
|
||||
|
||||
use termion::raw::IntoRawMode;
|
||||
use termion::event::Key;
|
||||
use termion::input::TermRead;
|
||||
struct RawMode {}
|
||||
|
||||
impl RawMode {
|
||||
fn init() -> Result<Self, Error> {
|
||||
crossterm::terminal::enable_raw_mode()?;
|
||||
Ok(Self {})
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for RawMode {
|
||||
fn drop(&mut self) {
|
||||
let _ = crossterm::terminal::disable_raw_mode();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub struct Monitor {
|
||||
hooks: Vec<(Key, &'static str)>
|
||||
hooks: Vec<(KeyCode, KeyModifiers, &'static str)>
|
||||
}
|
||||
|
||||
impl Monitor {
|
||||
pub fn new() -> Self {
|
||||
let mut hooks = Vec::new();
|
||||
|
||||
hooks.push((Key::Ctrl('c'), "Exit monitor"));
|
||||
hooks.push((Key::Ctrl('r'), "Reboot target"));
|
||||
hooks.push((KeyCode::Char('c'), KeyModifiers::CONTROL, "Exit monitor"));
|
||||
hooks.push((KeyCode::Char('r'), KeyModifiers::CONTROL, "Reboot target"));
|
||||
|
||||
Self { hooks }
|
||||
}
|
||||
|
||||
pub fn add_hook(&mut self, key: Key, desc: &'static str) -> &mut Self {
|
||||
self.hooks.push((key, desc));
|
||||
self
|
||||
pub fn add_hook(&mut self, key: KeyCode, mods: KeyModifiers, desc: &'static str) -> usize {
|
||||
let idx = self.hooks.len();
|
||||
self.hooks.push((key, mods, desc));
|
||||
idx
|
||||
}
|
||||
|
||||
pub fn run(&mut self, port: &mut SerialPort) -> Result<Key, Error> {
|
||||
pub fn run(&mut self, port: &mut SerialPort) -> Result<usize, Error> {
|
||||
port.set_dtr(true)?;
|
||||
port.set_rts(true)?;
|
||||
std::thread::sleep(Duration::from_millis(100));
|
||||
@@ -36,10 +50,10 @@ impl Monitor {
|
||||
println!();
|
||||
println!("Monitor controls:");
|
||||
|
||||
for (key, desc) in &self.hooks {
|
||||
let key = match key {
|
||||
Key::Char(c) => format!("{}", c.to_uppercase()),
|
||||
Key::Ctrl(c) => format!("Ctrl+{}", c.to_uppercase()),
|
||||
for (key, mods, desc) in &self.hooks {
|
||||
let key = match (key, *mods) {
|
||||
(KeyCode::Char(c), KeyModifiers::NONE) => format!("{}", c.to_uppercase()),
|
||||
(KeyCode::Char(c), KeyModifiers::CONTROL) => format!("Ctrl+{}", c.to_uppercase()),
|
||||
key => format!("{:?}", key)
|
||||
};
|
||||
|
||||
@@ -48,9 +62,8 @@ impl Monitor {
|
||||
|
||||
println!();
|
||||
|
||||
let mut stdout = std::io::stdout().into_raw_mode().unwrap();
|
||||
|
||||
let mut stdin = termion::async_stdin().keys();
|
||||
let _raw = RawMode::init()?;
|
||||
let mut stdout = std::io::stdout();
|
||||
|
||||
'main: loop {
|
||||
let mut buf = [0u8; 1];
|
||||
@@ -70,21 +83,25 @@ impl Monitor {
|
||||
Err(err)?
|
||||
}
|
||||
|
||||
if let Some(Ok(key)) = stdin.next() {
|
||||
if let Key::Ctrl('r') = key {
|
||||
if crossterm::event::poll(Duration::ZERO)? {
|
||||
let (key, mods) = match crossterm::event::read()? {
|
||||
Event::Key(ev) => (ev.code, ev.modifiers),
|
||||
_ => continue
|
||||
};
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
for (hook_key, _) in &self.hooks {
|
||||
if hook_key == &key {
|
||||
for (idx, (hook_key, hook_mods, _)) in self.hooks.iter().enumerate() {
|
||||
if hook_key == &key && hook_mods == &mods {
|
||||
stdout.write_all(b"\r\n")?;
|
||||
break 'main Ok(key)
|
||||
break 'main Ok(idx)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -31,13 +31,12 @@ impl PhysicalTarget {
|
||||
let name = new.read_info(0x00, 0x02).unwrap();
|
||||
let rev = new.read_info(0x00, 0x03).unwrap();
|
||||
|
||||
println!("Detected chip ID: {manufacturer:02X} {family:02X} {name:02X} {rev:02X} (whatever the fuck that means)");
|
||||
println!("Detected chip ID: {manufacturer:02X} {family:02X} {name:02X} {rev:02X} (whatever that means)");
|
||||
|
||||
Ok(new)
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user