From fc14436e9a63f4ef0340ca7b68e0a64747a07e89 Mon Sep 17 00:00:00 2001 From: Eric Schubert Date: Tue, 10 Mar 2026 00:30:46 -0600 Subject: [PATCH 1/5] Add RP2040 single-wire UART write path for TMC2209 stepper drivers --- src/source/TMC2208Stepper.cpp | 70 +++++++++++++++++++++++++++-------- 1 file changed, 54 insertions(+), 16 deletions(-) diff --git a/src/source/TMC2208Stepper.cpp b/src/source/TMC2208Stepper.cpp index 8e2c9ab7..20dd3cdd 100644 --- a/src/source/TMC2208Stepper.cpp +++ b/src/source/TMC2208Stepper.cpp @@ -2,6 +2,10 @@ #include "TMC_MACROS.h" #include "SERIAL_SWITCH.h" +#if defined(ARDUINO_ARCH_RP2040) && defined(RP2040_SINGLE_WIRE_UART_PIN) +#include +#endif + // Protected // addr needed for TMC2209 TMC2208Stepper::TMC2208Stepper(Stream * SerialPort, float RS, uint8_t addr) : @@ -201,8 +205,41 @@ void TMC2208Stepper::write(uint8_t addr, uint32_t regVal) { delay(replyDelay); } +#if defined(ARDUINO_ARCH_RP2040) && defined(RP2040_SINGLE_WIRE_UART_PIN) +#include +// Bit-bang one 8N1 UART byte on the single-wire PDN_UART pin. +// Temporarily switches GPIO to SIO output for TX, then restores UART RX mode. +// 9 µs/bit ≈ 111 kbaud (-3.7% vs 115200) — within the ±6.25% 8N1 tolerance. +static void _rp2040_sw_uart_write_byte(uint8_t byte) { + constexpr uint32_t T = 9; // µs per bit + gpio_set_function(RP2040_SINGLE_WIRE_UART_PIN, GPIO_FUNC_SIO); + gpio_set_dir(RP2040_SINGLE_WIRE_UART_PIN, GPIO_OUT); + gpio_put(RP2040_SINGLE_WIRE_UART_PIN, 1); // idle HIGH before start bit + // Start bit + gpio_put(RP2040_SINGLE_WIRE_UART_PIN, 0); + delayMicroseconds(T); + // 8 data bits, LSB first + for (int i = 0; i < 8; i++) { + gpio_put(RP2040_SINGLE_WIRE_UART_PIN, (byte >> i) & 1); + delayMicroseconds(T); + } + // Stop bit + gpio_put(RP2040_SINGLE_WIRE_UART_PIN, 1); + delayMicroseconds(T); + // Restore UART1 RX function on GPIO9 + gpio_set_function(RP2040_SINGLE_WIRE_UART_PIN, GPIO_FUNC_UART); +} +#endif + uint64_t TMC2208Stepper::_sendDatagram(uint8_t datagram[], const uint8_t len, uint16_t timeout) { - while (available() > 0) serial_read(); // Flush + // === STEP 0: flush stale RX bytes === + { + uint32_t _flush_t0 = micros(); + while (available() > 0) { + serial_read(); + if ((micros() - _flush_t0) > (uint32_t)timeout * 1000UL) break; + } + } #if defined(ARDUINO_ARCH_AVR) if (RXTX_pin > 0) { @@ -211,7 +248,14 @@ uint64_t TMC2208Stepper::_sendDatagram(uint8_t datagram[], const uint8_t len, ui } #endif + // === STEP 1: write request === + #if defined(ARDUINO_ARCH_RP2040) && defined(RP2040_SINGLE_WIRE_UART_PIN) + noInterrupts(); + for(int i=0; i<=len; i++) _rp2040_sw_uart_write_byte(datagram[i]); + interrupts(); + #else for(int i=0; i<=len; i++) serial_write(datagram[i]); + #endif #if defined(ARDUINO_ARCH_AVR) if (RXTX_pin > 0) { @@ -219,21 +263,18 @@ uint64_t TMC2208Stepper::_sendDatagram(uint8_t datagram[], const uint8_t len, ui } #endif + // === STEP 2: reply delay === delay(this->replyDelay); - // scan for the rx frame and read it - uint32_t ms = millis(); + // === STEP 3: sync scan with micros() timeout === + uint32_t _sync_t0 = micros(); uint32_t sync_target = (static_cast(datagram[0])<<16) | 0xFF00 | datagram[2]; uint32_t sync = 0; do { - uint32_t ms2 = millis(); - if (ms2 != ms) { - // 1ms tick - ms = ms2; - timeout--; + if ((micros() - _sync_t0) > (uint32_t)timeout * 1000UL) { + return 0; } - if (!timeout) return 0; int16_t res = serial_read(); if (res < 0) continue; @@ -244,18 +285,15 @@ uint64_t TMC2208Stepper::_sendDatagram(uint8_t datagram[], const uint8_t len, ui } while (sync != sync_target); + // === STEP 4: body read === uint64_t out = sync; - ms = millis(); + uint32_t _body_t0 = micros(); timeout = this->abort_window; for(uint8_t i=0; i<5;) { - uint32_t ms2 = millis(); - if (ms2 != ms) { - // 1ms tick - ms = ms2; - timeout--; + if ((micros() - _body_t0) > (uint32_t)timeout * 1000UL) { + return 0; } - if (!timeout) return 0; int16_t res = serial_read(); if (res < 0) continue; From d602abf31cd7845b67deecc1f8c908e6686e1427 Mon Sep 17 00:00:00 2001 From: Scott Lahteine Date: Mon, 6 Apr 2026 15:41:57 -0500 Subject: [PATCH 2/5] =?UTF-8?q?=F0=9F=A7=91=E2=80=8D=F0=9F=92=BB=20Ignore?= =?UTF-8?q?=20project=20files?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index e43b0f98..3786e617 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ .DS_Store +*.sublime-workspace From 9cb35792a53d986db61cc79c87f792a1225a7730 Mon Sep 17 00:00:00 2001 From: Scott Lahteine Date: Mon, 6 Apr 2026 16:12:56 -0500 Subject: [PATCH 3/5] cleanup for merge --- src/source/TMC2208Stepper.cpp | 69 ++++++++++++++++++----------------- 1 file changed, 35 insertions(+), 34 deletions(-) diff --git a/src/source/TMC2208Stepper.cpp b/src/source/TMC2208Stepper.cpp index 20dd3cdd..e7b2129f 100644 --- a/src/source/TMC2208Stepper.cpp +++ b/src/source/TMC2208Stepper.cpp @@ -197,7 +197,7 @@ void TMC2208Stepper::write(uint8_t addr, uint32_t regVal) { preWriteCommunication(); - for(uint8_t i=0; i<=len; i++) { + for (uint8_t i = 0; i<= len; i++) { bytesWritten += serial_write(datagram[i]); } postWriteCommunication(); @@ -206,30 +206,33 @@ void TMC2208Stepper::write(uint8_t addr, uint32_t regVal) { } #if defined(ARDUINO_ARCH_RP2040) && defined(RP2040_SINGLE_WIRE_UART_PIN) -#include -// Bit-bang one 8N1 UART byte on the single-wire PDN_UART pin. -// Temporarily switches GPIO to SIO output for TX, then restores UART RX mode. -// 9 µs/bit ≈ 111 kbaud (-3.7% vs 115200) — within the ±6.25% 8N1 tolerance. -static void _rp2040_sw_uart_write_byte(uint8_t byte) { - constexpr uint32_t T = 9; // µs per bit - gpio_set_function(RP2040_SINGLE_WIRE_UART_PIN, GPIO_FUNC_SIO); - gpio_set_dir(RP2040_SINGLE_WIRE_UART_PIN, GPIO_OUT); - gpio_put(RP2040_SINGLE_WIRE_UART_PIN, 1); // idle HIGH before start bit - // Start bit - gpio_put(RP2040_SINGLE_WIRE_UART_PIN, 0); - delayMicroseconds(T); - // 8 data bits, LSB first - for (int i = 0; i < 8; i++) { - gpio_put(RP2040_SINGLE_WIRE_UART_PIN, (byte >> i) & 1); - delayMicroseconds(T); - } - // Stop bit - gpio_put(RP2040_SINGLE_WIRE_UART_PIN, 1); - delayMicroseconds(T); - // Restore UART1 RX function on GPIO9 - gpio_set_function(RP2040_SINGLE_WIRE_UART_PIN, GPIO_FUNC_UART); -} -#endif + + #include + + // Bit-bang one 8N1 UART byte on the single-wire PDN_UART pin. + // Temporarily switches GPIO to SIO output for TX, then restores UART RX mode. + // 9 µs/bit ≈ 111 kbaud (-3.7% vs 115200) — within the ±6.25% 8N1 tolerance. + static void _rp2040_sw_uart_write_byte(uint8_t byte) { + constexpr uint32_t T = 9; // µs per bit + gpio_set_function(RP2040_SINGLE_WIRE_UART_PIN, GPIO_FUNC_SIO); + gpio_set_dir(RP2040_SINGLE_WIRE_UART_PIN, GPIO_OUT); + gpio_put(RP2040_SINGLE_WIRE_UART_PIN, 1); // idle HIGH before start bit + // Start bit + gpio_put(RP2040_SINGLE_WIRE_UART_PIN, 0); + delayMicroseconds(T); + // 8 data bits, LSB first + for (int i = 0; i < 8; i++) { + gpio_put(RP2040_SINGLE_WIRE_UART_PIN, (byte >> i) & 1); + delayMicroseconds(T); + } + // Stop bit + gpio_put(RP2040_SINGLE_WIRE_UART_PIN, 1); + delayMicroseconds(T); + // Restore UART1 RX function on GPIO9 + gpio_set_function(RP2040_SINGLE_WIRE_UART_PIN, GPIO_FUNC_UART); + } + +#endif // ARDUINO_ARCH_RP2040 && RP2040_SINGLE_WIRE_UART_PIN uint64_t TMC2208Stepper::_sendDatagram(uint8_t datagram[], const uint8_t len, uint16_t timeout) { // === STEP 0: flush stale RX bytes === @@ -241,7 +244,7 @@ uint64_t TMC2208Stepper::_sendDatagram(uint8_t datagram[], const uint8_t len, ui } } - #if defined(ARDUINO_ARCH_AVR) + #ifdef ARDUINO_ARCH_AVR if (RXTX_pin > 0) { digitalWrite(RXTX_pin, HIGH); pinMode(RXTX_pin, OUTPUT); @@ -250,14 +253,14 @@ uint64_t TMC2208Stepper::_sendDatagram(uint8_t datagram[], const uint8_t len, ui // === STEP 1: write request === #if defined(ARDUINO_ARCH_RP2040) && defined(RP2040_SINGLE_WIRE_UART_PIN) - noInterrupts(); - for(int i=0; i<=len; i++) _rp2040_sw_uart_write_byte(datagram[i]); - interrupts(); + noInterrupts(); + for (int i = 0; i <= len; i++) _rp2040_sw_uart_write_byte(datagram[i]); + interrupts(); #else - for(int i=0; i<=len; i++) serial_write(datagram[i]); + for (int i = 0; i <= len; i++) serial_write(datagram[i]); #endif - #if defined(ARDUINO_ARCH_AVR) + #ifdef ARDUINO_ARCH_AVR if (RXTX_pin > 0) { pinMode(RXTX_pin, INPUT_PULLUP); } @@ -290,7 +293,7 @@ uint64_t TMC2208Stepper::_sendDatagram(uint8_t datagram[], const uint8_t len, ui uint32_t _body_t0 = micros(); timeout = this->abort_window; - for(uint8_t i=0; i<5;) { + for (uint8_t i = 0; i < 5; i++) { if ((micros() - _body_t0) > (uint32_t)timeout * 1000UL) { return 0; } @@ -300,8 +303,6 @@ uint64_t TMC2208Stepper::_sendDatagram(uint8_t datagram[], const uint8_t len, ui out <<= 8; out |= res & 0xFF; - - i++; } #if defined(ARDUINO_ARCH_AVR) From 79c84bdd621c083f70da866a6bc74edac1e1181c Mon Sep 17 00:00:00 2001 From: Scott Lahteine Date: Mon, 6 Apr 2026 16:35:01 -0500 Subject: [PATCH 4/5] Hide project files --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..3786e617 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.DS_Store +*.sublime-workspace From 2170543f0fb6df9fcfdf5ca363f36f5af07c2f4e Mon Sep 17 00:00:00 2001 From: Scott Lahteine Date: Mon, 6 Apr 2026 16:37:09 -0500 Subject: [PATCH 5/5] post merge --- src/source/TMC2208Stepper.cpp | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/source/TMC2208Stepper.cpp b/src/source/TMC2208Stepper.cpp index 95c0856b..6e0aa006 100644 --- a/src/source/TMC2208Stepper.cpp +++ b/src/source/TMC2208Stepper.cpp @@ -8,7 +8,7 @@ #include "SERIAL_SWITCH.h" #if defined(ARDUINO_ARCH_RP2040) && defined(RP2040_SINGLE_WIRE_UART_PIN) -#include + #include #endif // Protected @@ -268,12 +268,10 @@ void TMC2208Stepper::write(uint8_t addr, uint32_t regVal) { uint64_t TMC2208Stepper::_sendDatagram(uint8_t datagram[], const uint8_t len, uint16_t timeout) { // === STEP 0: flush stale RX bytes === - { - uint32_t _flush_t0 = micros(); - while (available() > 0) { - serial_read(); - if ((micros() - _flush_t0) > (uint32_t)timeout * 1000UL) break; - } + const uint32_t _flush_t0 = micros(); + while (available() > 0) { + serial_read(); + if ((micros() - _flush_t0) > (uint32_t)timeout * 1000UL) break; } #if HAS_HALF_DUPLEX_MODE