[Beginner] uart on the pico-2 with rp235x-hal

Hi Beginner in rust here, I just finished The Book, and am starting my journey In embedded rust with the pico-2 using the rp235x-hal.

However I have hit a problem in my project where the uart doesn't seem to work in rust, whereas the exact same pins work when using c.

for reference my pico 2 is not the A4 stepping so it has the chip erratums(could this be the issue).

and my project uses the tmc2209-uart crate.

for some reason even though I write to uart there is no echo coming back. kindly advise.

your question is too general to give an answer.

please show the code how you configured the clocks and uart, and the part that does not work as expected. it would also be useful if you can describe your project setup.

if you don't know how to configure the uart, check out the examples here, there are several examples using the uart:

the blinking led in the code is just a visual indicator to show the program has started to run

this bit below deals with setting up the function that handles the tmc2209-uart crate and uses it properly:-


use tmc2209_uart::{Gconf, MicrostepResolution, Tmc2209};

//#[cfg(feature = "blocking")]
pub fn basic_motor_control<U, E>(uart: U) -> Result<(), tmc2209_uart::Error<E>>
where
    U: embedded_io::Read<Error = E> + embedded_io::Write<Error = E>,
{
    let mut driver = Tmc2209::new(uart, 0);

    info!("starting motor control");

    let mut gconf = Gconf::new();

    gconf.set_pdn_disable(true);

    driver.write_register::<Gconf>(&mut gconf)?;

    info!("uart mode enabled");

    if !driver.is_connected() {
        info!("driver is not connected");
        return Err(tmc2209_uart::Error::NoResponse);
    } else {
        info!("driver is connected");
    }

    driver.clear_gstat()?;
    info!("gstat cleared");

    driver.set_microsteps(MicrostepResolution::M64)?;
    info!("microstep set 64");

    driver.set_interpolation(true)?;
    info!("mstep intpol set true");

    driver.set_velocity(4000)?;
    info!("motor velocity set 4000");

    let status = driver.drv_status()?;
    info!("driver status checked");

    if status.ot() {
        driver.stop()?;
        driver.set_enabled(false)?;
    }

    if status.otpw() {
        info!("Warning motor temperature is high");
    }

    driver.stop()?;
    info!("driver is stopped");
    Ok(())
}

and this is my rp235x-hal setup:-

#![no_std]
#![no_main]

use panic_probe as _;

use defmt::info;
use defmt_rtt as _;

use rp235x_hal::{self as hal, Clock};

use hal::fugit::RateExtU32;

//use hal::gpio;

use hal::uart::{DataBits, StopBits, UartConfig}; //, ValidatedPinRx, ValidatedPinTx};

use embedded_hal::delay::DelayNs;
use embedded_hal::digital::OutputPin;

pub mod tmc2209_test;

#[unsafe(link_section = ".start_block")]
#[used]
pub static IMAGE_DEF: hal::block::ImageDef = hal::block::ImageDef::secure_exe();

const XTAL_FREQ_HZ: u32 = 12_000_000u32;

#[hal::entry]
fn main() -> ! {
    let mut pac = hal::pac::Peripherals::take().unwrap();

    let mut watchdog = hal::Watchdog::new(pac.WATCHDOG);

    let clocks = hal::clocks::init_clocks_and_plls(
        XTAL_FREQ_HZ,
        pac.XOSC,
        pac.CLOCKS,
        pac.PLL_SYS,
        pac.PLL_USB,
        &mut pac.RESETS,
        &mut watchdog,
    )
    .unwrap();

    let mut timer = hal::Timer::new_timer0(pac.TIMER0, &mut pac.RESETS, &clocks);

    let sio = hal::Sio::new(pac.SIO);

    let pins = hal::gpio::Pins::new(
        pac.IO_BANK0,
        pac.PADS_BANK0,
        sio.gpio_bank0,
        &mut pac.RESETS,
    );

    let mut led_pin = pins.gpio25.into_push_pull_output();
    led_pin.set_high().unwrap();
    info!("led on");
    timer.delay_ms(500);
    led_pin.set_low().unwrap();
    info!("led off");
    timer.delay_ms(500);

    {
        let uart1_pins = (pins.gpio8.into_function(), pins.gpio9.into_function());
        let mut uart1 = hal::uart::UartPeripheral::new(pac.UART1, uart1_pins, &mut pac.RESETS)
            .enable(
                UartConfig::new(115200.Hz(), DataBits::Eight, None, StopBits::One),
                clocks.peripheral_clock.freq(),
            )
            .unwrap();

        uart1.set_fifos(true);

        tmc2209_test::basic_motor_control(uart1).unwrap();
    }

    loop {
        info!("finished program");
        timer.delay_ms(1000);
    }
}

#[unsafe(link_section = ".bi_entries")]
#[used]
pub static PICOTOOL_ENTRIES: [hal::binary_info::EntryAddr; 5] = [
    hal::binary_info::rp_cargo_bin_name!(),
    hal::binary_info::rp_cargo_version!(),
    hal::binary_info::rp_program_description!(c"Blinky Example"),
    hal::binary_info::rp_cargo_homepage_url!(),
    hal::binary_info::rp_program_build_attribute!(),
];

But I always get an error saying that there was a framing error when reading back the echo, or that there was no response. I used the exact same setup with some c code written using the raspberry pi c sdk and had no problems with it. could this have something to do with the rp235x-hal or even with my rust code.

kindly advise

I didn't see any log messages about framing error or no response, so I assume you got the error as a panic? what's the full error information, and any chance to locate which call failed? for example, did you see any of the log messages printed before the panic?

personally I have not used tmc2209, but the rp2350 initialization part looks good to me, so I assume it's probably some misconfiguration on the tmc2209 driver side.

I would also recommend to step into the driver code in a debugger. I assume you are using probe-rs, the easiest way to setup the debugger is using the vscode extension, but other visual debuggers are also supported via the DAP protocol. check the debug chapter of the probe-rs documentation website for detailed instructions.

I'll post the error or panic message that comes up

In my setup I use a pico 2w as the debugger with the uart connection going from the pico2w to the pico2's pins 0 and 1, so the pico probe uses uart0 and I have taken care to use uart1 for tmc2209.

however could having to deliver power to the pico2 from the pico2w's 5v out pin to the pico2's vsys pin. could a lack of sufficient current be causing this

Two things you probably aren't aware of because you're new to the forum:

  • Always post text, not screen shots.
  • When you have an error with a line number, show us which line that is in the code you posted. We would have no way to know that.

alright, I placed some info!() macros to mark points in the execution so the error happens in tmc2209_test file which is the bit of code that contains the function that deals with the tmc2209 crate for reference below:-> so it happens on the driver.set_microsteps(MicrostepResolution::M64)?; line

use tmc2209_uart::{Gconf, MicrostepResolution, Tmc2209};

//#[cfg(feature = "blocking")]
pub fn basic_motor_control<U, E>(uart: U) -> Result<(), tmc2209_uart::Error<E>>
where
    U: embedded_io::Read<Error = E> + embedded_io::Write<Error = E>,
{
    let mut driver = Tmc2209::new(uart, 0);

    info!("starting motor control");

    let mut gconf = Gconf::new();

    gconf.set_pdn_disable(true);

    driver.write_register::<Gconf>(&mut gconf)?;

    info!("uart mode enabled");

    if !driver.is_connected() {
        info!("driver is not connected");
        return Err(tmc2209_uart::Error::NoResponse);
    } else {
        info!("driver is connected");
    }

    driver.clear_gstat()?;
    info!("gstat cleared");

//The error happens right here on the line right below this comment

    driver.set_microsteps(MicrostepResolution::M64)?;
    info!("microstep set 64");

    driver.set_interpolation(true)?;
    info!("mstep intpol set true");

    driver.set_velocity(4000)?;
    info!("motor velocity set 4000");

    let status = driver.drv_status()?;
    info!("driver status checked");

    if status.ot() {
        driver.stop()?;
        driver.set_enabled(false)?;
    }

    if status.otpw() {
        info!("Warning motor temperature is high");
    }

    driver.stop()?;
    info!("driver is stopped");
    Ok(())
}

and here is the error in text form for you, im using probe-rs so :-

[INFO ] led on
[INFO ] led off
[INFO ] starting motor control
[INFO ] uart mode enabled
[INFO ] driver is connected
[INFO ] gstat cleared
[ERROR] panicked at src/main.rs:75:50:
called 'Result::unwrap()' on an 'Err' value: Uart(Framing)

Some thing that I cannot explain and fully understand just happened.

I replaced the my dev profile in cargo.toml with this:-

[profile.dev]
codegen-units=1

and now it suddenly works without any fault no errors and everything goes smoothly.

but the instant I use the previous dev profile I get the error mentioned above.

for reference here is my previous dev profile :-

[profile.dev]
debug = 2
debug-assertions = true
opt-level = 2
overflow-checks = true
codegen-units=1

why is this happening, does this have something to do with the rust compiler?

also for reference I used the rp235x_hal project template cargo generate wizard as suggested in the rp-rs GitHub page :- https://github.com/rp-rs/rp235x-project-template

the error Uart(Framing) indicates there's an uart receiving error, specifically, Framing is raised when the stop bit is missing:

my first suspect is the uart config is incorrect, but this doesn't explain why it failed at set_microsteps(), but the prevous commands like is_connected() and clear_gstat() all succeeded.

then the next possibility is maybe the hardware timing is a little bit off, causing the uart baudrate drifts from the nominal 115200. if the failure would happen randomly at different locations, then this could be the cause. however, if the error always happen at the exact same line of code, then it's probably not this.

what firmware are you running on the probe? why the probe uses uart on the device? I never used such setup. typically, just the SWD/JTAG connection is enough for probe-rs with rtt for defmt, no serial port is required.

that's a good guess. if the stepper motor draws more current than the power supply, it could cause the controller to send a bad serial signal, that would explain why you get an error of missing stop bit.

try use a stronger power source for the target board and see if the error happens again.

well, if we exclude the possibility of hardware faults, this usually only happens when some unsafe code has soundness issues. but, it's also possible, although very unlikely, that there's a bug in the compiler (or linker, for that matter).

the template is good, I have never had problems with the template.

btw, if you happen to have a logic analyzer (with uart decoder) in your lab, it would be much easier to figure out what's actually going on: just tap on the serial signals and record all the traffics over the wire.

I actually followed your advice about separate power supplies and removing the uart connection from the pico probe to the pico2. im using the official debug probe firmware that raspberry pi provides for the pico2 ( although im running it on my pico2w). and also the test with the different debug profiles was done with this setup that I just described. And also I never connected a stepper motor to the driver I only wanted to check if uart works fine.

Thanks for the advice.

So what do you think is happening with the different profiles giving different results.

The tmc2209-uart crate I'm using doesn't have any unsafe blocks or sections, so it may be something else

have you figured out if the error is determinstic or does it happen randomly? for example, if you reset the board re-run the program multiple times, do you always see the same error happening at the same line, or it could change between runs?

it's hard to say. in theory, the codegen-units option should only affect the compilation speed, it should NOT change the behavior of the program, at least when the code is UB free.

but that's the theory. when it does change, basically there are two possible explanations, either:

a) it's a compiler bug, the code is mis-compiled, or
b) there's UB in the code, the codegen-units option happens to expose the UB which were hidden by chance.

both are possible, and both are very very hard to diagnostic, unfortunately.

yeah, if the problem was indeed caused by UB, it could be literally anywhere. it would be very hard to guess, but I would try to test small pieces of the application in isolation which should hopefully narrow it down progressively.

It always happens on the line with the set_microsteps and if I comment that out it happens on every other line. so it is deterministic. so should I not use codegen-units

so does this also mean that if there are any unsafe blocks in any of the crates i used, like perhaps the rp235x-hal or embedded-has or cortex-m crate then that could be causing the error ?

I don't think codegen-units=1 is the problem here though, because the only common thing in the previous and current dev profile is the codegen-units=1 and that works with the current profile.

what doesn't work is adding all the other stuff back into the profile