embassy-rs / embassy

Modern embedded framework, using Rust and async.
https://embassy.dev
Apache License 2.0
5.65k stars 786 forks source link

nrf52832 UARTE read_until_idle not working #2817

Open lfdominguez opened 7 months ago

lfdominguez commented 7 months ago

Hi, I'm porting my firmware from Zephyr to Rust, and I'm stopped because the read_until_idle call is blocking forever. My task is:

#[embassy_executor::task]
pub async fn init_gps_event_controller(mut tx: UarteTx<'static, UARTE0>, mut force_on: Output<'static, AnyPin>) {
    loop {
        match EVENTS.receive().await {
            Event::StopGPS => {
                debug!("Sending Backup to GPS");

                force_on.set_low();
                Timer::after_millis(200).await;

                if tx.write(&gps_commands::SET_BACKUP_MODE).await.is_err() {
                    error!("Fail to send backup command to GPS");
                }

                Timer::after_secs(2).await;
            }

            Event::StartGPS => {
                debug!("Start GPS");

                force_on.set_high();
                Timer::after_millis(200).await;

                if tx.write(&gps_commands::SET_FULL_MODE).await.is_err() {
                    error!("Fail to send full mode command to GPS");
                }
            }
        }
    }
}

#[embassy_executor::task]
pub async fn init_gps_controller(gps_component: board::components::Gps) {
  let mut config = uarte::Config::default();

  config.baudrate = uarte::Baudrate::BAUD9600;

  let uart = uarte::Uarte::new(
      gps_component.uarte,
      Irqs,
      gps_component.rx,
      gps_component.tx,
      config,
  );
  let (mut tx, mut rx) = uart.split();

  unwrap!(Spawner::for_current_executor().await.spawn(init_gps_event_controller(tx, Output::new(gps_component.force_on, Level::High, OutputDrive::Standard))));

  info!("GPS initialized");

  let mut gps_line_buffer = [0u8; 256];
  let mut gps_line_buffer_index = 0;

  let mut read_buffer = [0u8; 8];

  let mut rx = rx.with_idle(gps_component.timer0, gps_component.ppi_ch0, gps_component.ppi_ch1);

  loop {
      rx.read_until_idle(&mut read_buffer).await.unwrap();

      info!("Read");

      for c in read_buffer {
          match c {
              b'\r' => {
                  gps_line_buffer[gps_line_buffer_index] = 0;

                  if let Ok(conv) = core::str::from_utf8(&gps_line_buffer[0..gps_line_buffer_index]) {
                      info!("read done, got {}", conv);
                  }

                  gps_line_buffer_index = 0;
              }

              b'\n' => {}

              _ => {
                  if gps_line_buffer_index >= gps_line_buffer.len() {
                      gps_line_buffer_index = 0;

                      if let Ok(conv) = core::str::from_utf8(&gps_line_buffer) {
                          warn!("Buffer overflow on GPS read line, buffer content: {}", conv);
                      } else {
                          warn!("Buffer overflow on GPS read line");
                      }
                  }

                  gps_line_buffer[gps_line_buffer_index] = c;
                  gps_line_buffer_index += 1
              }
          }
      }
  }
}

My problem is that when I send a command to the GPS component that is connected by UART, it's stop sending data over UART line, but the read_until_idle() get stuck.

If there is another better approach to do this please tell me... my final goal is when GPS is stopped, drop the UARTE instance because right now, on Zephyr my board is consuming 400uA, but on this is 5 mA.

phycrax commented 7 months ago

Maybe select? You could signal when GPS stops sending data and use select() to break the loop.

lfdominguez commented 7 months ago

Maybe select? You could signal when GPS stops sending data and use select() to break the loop.

But how I have known that GPS stop sending data? Can you post an example? thanks in advance

phycrax commented 7 months ago

I haven't worked with any GPS module so I don't know how they communicate.

If you are expecting some data from GPS asynchronously, then you need to keep UART alive. If you know that GPS will stop after some known command, you can also signal an event and handle that event via select().

loop {
    let either = select(rx.read_until_idle(&mut read_buffer), STOP_EVENT.receive()).await
    match either {
        Either::First(gps_msg) => handle_gps_msg(gps_msg.unwrap()),
        Either::Second(stop_evt) => break,
    }
}

How about with_timeout?

lfdominguez commented 7 months ago

I haven't worked with any GPS module so I don't know how they communicate.

If you are expecting some data from GPS asynchronously, then you need to keep UART alive. If you know that GPS will stop after some known command, you can also signal an event and handle that event via select().

loop {
    let either = select(rx.read_until_idle(&mut read_buffer), STOP_EVENT.receive()).await
    match either {
        Either::First(gps_msg) => handle_gps_msg(gps_msg.unwrap()),
        Either::Second(stop_evt) => break,
    }
}

How about with_timeout?

Great!! thanks, I will test it.