From 4cc9926cf6410ca5005bf134a7edb55f9bdd88e5 Mon Sep 17 00:00:00 2001 From: Kenta Yonekura Date: Sat, 2 Aug 2025 00:57:08 +0900 Subject: [PATCH 1/3] Add an example to trasmit RS422 signal --- README.md | 1 + pio/CMakeLists.txt | 1 + pio/rs422_tx/CMakeLists.txt | 11 ++++++ pio/rs422_tx/rs422_tx.c | 48 +++++++++++++++++++++++ pio/rs422_tx/rs422_tx.pio | 77 +++++++++++++++++++++++++++++++++++++ 5 files changed, 138 insertions(+) create mode 100644 pio/rs422_tx/CMakeLists.txt create mode 100644 pio/rs422_tx/rs422_tx.c create mode 100644 pio/rs422_tx/rs422_tx.pio diff --git a/README.md b/README.md index f43a7339a..a2312c7e4 100644 --- a/README.md +++ b/README.md @@ -329,6 +329,7 @@ App|Description [pio_ws2812](pio/ws2812) | Example of driving a string of WS2812 addressable RGB LEDs. [pio_ws2812_parallel](pio/ws2812) | Examples of driving multiple strings of WS2812 addressable RGB LEDs efficiently. [pio_addition](pio/addition) | Add two integers together using PIO. Only around 8 billion times slower than Cortex-M0+. +[pio_rs422_tx](pio/rs422_tx) | Modifies the pio_uart_tx sample to send "Hello, world!" with RS-422 transmit functionality without an external IC. ### PWM diff --git a/pio/CMakeLists.txt b/pio/CMakeLists.txt index 023bd4ea8..6cc93b33d 100644 --- a/pio/CMakeLists.txt +++ b/pio/CMakeLists.txt @@ -14,6 +14,7 @@ if (TARGET hardware_pio) add_subdirectory_exclude_platforms(pwm) add_subdirectory_exclude_platforms(quadrature_encoder) add_subdirectory_exclude_platforms(quadrature_encoder_substep) + add_subdirectory_exclude_platforms(rs422_tx) add_subdirectory_exclude_platforms(spi) add_subdirectory_exclude_platforms(squarewave) add_subdirectory_exclude_platforms(st7789_lcd) diff --git a/pio/rs422_tx/CMakeLists.txt b/pio/rs422_tx/CMakeLists.txt new file mode 100644 index 000000000..cba7be988 --- /dev/null +++ b/pio/rs422_tx/CMakeLists.txt @@ -0,0 +1,11 @@ +add_executable(pio_rs422_tx) + +pico_generate_pio_header(pio_rs422_tx ${CMAKE_CURRENT_LIST_DIR}/rs422_tx.pio) + +target_sources(pio_rs422_tx PRIVATE rs422_tx.c) + +target_link_libraries(pio_rs422_tx PRIVATE pico_stdlib hardware_pio) +pico_add_extra_outputs(pio_rs422_tx) + +# add url via pico_set_program_url +example_auto_set_url(pio_rs422_tx) diff --git a/pio/rs422_tx/rs422_tx.c b/pio/rs422_tx/rs422_tx.c new file mode 100644 index 000000000..657cd218e --- /dev/null +++ b/pio/rs422_tx/rs422_tx.c @@ -0,0 +1,48 @@ +/** + * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "pico/stdlib.h" +#include "hardware/pio.h" +#include "rs422_tx.pio.h" + +// Defines the GPIO pins for PIO communication. +// PIO_TXP_PIN is the positive pin. +// PIO_TXM_PIN is the negative pin and must be the next pin in sequence. +#define PIO_TXP_PIN 0 +#define PIO_TXM_PIN 1 + +// Check the pin is compatible with the platform +#if (PIO_TXM_PIN) != (PIO_TXP_PIN + 1) +#error "PIO_TXM_PIN must be the next pin after PIO_TXP_PIN." +#endif +#if (PIO_TXM_PIN) >= NUM_BANK0_GPIOS +#error "Attempting to use a pin>=32 on a platform that does not support it" +#endif + +int main() { + // This is the same as the default UART baud rate on Pico + const uint SERIAL_BAUD = 115200; + + PIO pio; + uint sm; + uint offset; + + // This will find a free pio and state machine for our program and load it for us + // We use pio_claim_free_sm_and_add_program_for_gpio_range (for_gpio_range variant) + // so we will get a PIO instance suitable for addressing gpios >= 32 if needed and supported by the hardware + bool success = pio_claim_free_sm_and_add_program_for_gpio_range(&rs422_tx_program, &pio, &sm, &offset, PIO_TXP_PIN, 2, true); + hard_assert(success); + + rs422_tx_program_init(pio, sm, offset, PIO_TXP_PIN, SERIAL_BAUD); + + while (true) { + rs422_tx_program_puts(pio, sm, "Hello, world! (from PIO!)\r\n"); + sleep_ms(1000); + } + + // This will free resources and unload our program + pio_remove_program_and_unclaim_sm(&rs422_tx_program, pio, sm, offset); +} diff --git a/pio/rs422_tx/rs422_tx.pio b/pio/rs422_tx/rs422_tx.pio new file mode 100644 index 000000000..67bd089ee --- /dev/null +++ b/pio/rs422_tx/rs422_tx.pio @@ -0,0 +1,77 @@ +; +; Copyright (c) 2020 Raspberry Pi (Trading) Ltd. +; +; SPDX-License-Identifier: BSD-3-Clause +; +.pio_version 0 // only requires PIO version 0 + +.program rs422_tx +.side_set 2 opt + +; An 8n1 UART transmit program. +; OUT pin 0-1 are mapped to RS422 TX+ and TX- pin. + + pull side 0b01 [3] ; Assert stop bit, or stall with line in idle state + nop + nop + nop + nop + set x, 7 side 0b10 [3] ; Preload bit counter, assert start bit + nop + nop + nop +bitloop: ; This loop will run 8 times (8n1) + out y, 1 ; Shift 1 bit from OSR to the Y register + jmp !y put0 +put1: + jmp next side 0b01 [3] +put0: + nop side 0b10 [3] +next: + nop + jmp x-- bitloop + + +% c-sdk { +#include "hardware/clocks.h" + +static inline void rs422_tx_program_init(PIO pio, uint sm, uint offset, uint pin_tx, uint baud) { + // Tell PIO to initially drive output-high on the selected pin, then map PIO + // onto that pin with the IO muxes. + pio_sm_set_consecutive_pindirs(pio, sm, pin_tx, 2, true); + for ( uint i = 0;i<2;i++ ) { + pio_gpio_init(pio, pin_tx + i); + } + + pio_sm_config c = rs422_tx_program_get_default_config(offset); + + // OUT shifts to right, no autopull + sm_config_set_out_shift(&c, true, false, 32); + + // We are mapping both OUT and side-set to the same pin, because sometimes + // we need to assert user data onto the pin (with OUT) and sometimes + // assert constant values (start/stop bit) + sm_config_set_out_pins(&c, pin_tx, 2); + sm_config_set_sideset_pins(&c, pin_tx); + + // We only need TX, so get an 8-deep FIFO! + sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX); + + // SM transmits 1 bit per 8 execution cycles. + float div = (float)clock_get_hz(clk_sys) / (8 * baud); + sm_config_set_clkdiv(&c, div); + + pio_sm_init(pio, sm, offset, &c); + pio_sm_set_enabled(pio, sm, true); +} + +static inline void rs422_tx_program_putc(PIO pio, uint sm, char c) { + pio_sm_put_blocking(pio, sm, (uint32_t)c); +} + +static inline void rs422_tx_program_puts(PIO pio, uint sm, const char *s) { + while (*s) + rs422_tx_program_putc(pio, sm, *s++); +} + +%} From cad6c7b9e3fe306d8e04f7b47f58106287a784e8 Mon Sep 17 00:00:00 2001 From: Kenta Yonekura Date: Tue, 5 Aug 2025 14:39:25 +0900 Subject: [PATCH 2/3] fix --- pio/rs422_tx/rs422_tx.c | 8 +++++++- pio/rs422_tx/rs422_tx.pio | 6 ++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/pio/rs422_tx/rs422_tx.c b/pio/rs422_tx/rs422_tx.c index 657cd218e..6e203c973 100644 --- a/pio/rs422_tx/rs422_tx.c +++ b/pio/rs422_tx/rs422_tx.c @@ -19,7 +19,13 @@ #error "PIO_TXM_PIN must be the next pin after PIO_TXP_PIN." #endif #if (PIO_TXM_PIN) >= NUM_BANK0_GPIOS -#error "Attempting to use a pin>=32 on a platform that does not support it" +#error "Pin number exceeds available GPIOs (30 on RP2040/RP2350A, 48 on RP2350B)" +#endif +#if NUM_BANK0_GPIOS > 30 +// On RP2350B, ensure both pins are in the same PIO range +#if !((PIO_TXP_PIN >= 0 && PIO_TXM_PIN < 32) || (PIO_TXP_PIN >= 16 && PIO_TXM_PIN < 48)) +#error "On RP2350B, both pins must be in the same PIO range: [0-31] or [16-47]" +#endif #endif int main() { diff --git a/pio/rs422_tx/rs422_tx.pio b/pio/rs422_tx/rs422_tx.pio index 67bd089ee..e99c9582a 100644 --- a/pio/rs422_tx/rs422_tx.pio +++ b/pio/rs422_tx/rs422_tx.pio @@ -48,10 +48,8 @@ static inline void rs422_tx_program_init(PIO pio, uint sm, uint offset, uint pin // OUT shifts to right, no autopull sm_config_set_out_shift(&c, true, false, 32); - // We are mapping both OUT and side-set to the same pin, because sometimes - // we need to assert user data onto the pin (with OUT) and sometimes - // assert constant values (start/stop bit) - sm_config_set_out_pins(&c, pin_tx, 2); + // We use side-set to control the RS422 differential pins + // Data bits are determined by conditional jumps based on OUT to Y register sm_config_set_sideset_pins(&c, pin_tx); // We only need TX, so get an 8-deep FIFO! From 94e84426c7744954b1fdc2dea78ff0088aad2879 Mon Sep 17 00:00:00 2001 From: Kenta Yonekura Date: Tue, 5 Aug 2025 16:40:47 +0900 Subject: [PATCH 3/3] change pioasm label --- pio/rs422_tx/rs422_tx.pio | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pio/rs422_tx/rs422_tx.pio b/pio/rs422_tx/rs422_tx.pio index e99c9582a..a536ed5c6 100644 --- a/pio/rs422_tx/rs422_tx.pio +++ b/pio/rs422_tx/rs422_tx.pio @@ -24,10 +24,10 @@ bitloop: ; This loop will run 8 times (8n1) out y, 1 ; Shift 1 bit from OSR to the Y register jmp !y put0 put1: - jmp next side 0b01 [3] + jmp cont side 0b01 [3] put0: nop side 0b10 [3] -next: +cont: nop jmp x-- bitloop