ChanServ changed the topic of #rust-embedded to: Welcome to the Rust Embedded IRC channel! Bridged to #rust-embedded:matrix.org and logged at https://libera.irclog.whitequark.org/rust-embedded, code of conduct at https://www.rust-lang.org/conduct.html
<re_irc> <@henrik_alser:matrix.org> "unsafe { (&*hal::peripherals::I2C0::ptr()).to.write(|w| w.time_out().bits(0xfffff)) };"
<re_irc> <@henrik_alser:matrix.org> ...or something like that
<re_irc> <@henrik_alser:matrix.org> Gotta get some sleep! Good luck Luis Roel
IlPalazzo-ojiisa has quit [Remote host closed the connection]
<re_irc> <@firefrommoonlight:matrix.org> Luis Roel: Here's an impl. Caveat: For M10, and UART. Probalby p similar tho:
<re_irc> //! This module contains code for U-BLOX M10 GNSS modules.
<re_irc> //! It used the UBX protocol, although others are available. It uses USART, although
<re_irc> //! See the Ublox M10 interface manual for how this is set up.
<re_irc> //!
<re_irc> //! I2C is also available.
<re_irc> use core::sync::atomic::AtomicBool;
<re_irc> use num_enum::TryFromPrimitiveError;
<re_irc> use stm32_hal2::{
<re_irc> clocks::Clocks,
<re_irc> usart::{self, UsartInterrupt},
<re_irc> };
<re_irc> use ahrs::{Fix, FixType};
<re_irc> use chrono::NaiveDate;
<re_irc> use defmt::println;
<re_irc> use crate::setup::UartGnss;
<re_irc> // UBX messages always start with these 2 preamble characters.
<re_irc> const PREAMBLE_1: u8 = 0xb5;
<re_irc> const PREAMBLE_2: u8 = 0x62;
<re_irc> // Max Baud, per DS, is 921,600
<re_irc> // The peripheral is initialized at 9.6kbps. Once a higher update rate is configured on the GNSS,
<re_irc> // we update the UART peripheral. We try each at startup, since we don't know if the GNSS has had it's
<re_irc> // power interrupted, eg during debug runs. In operations, we expect the MCU and GNSS to power
<re_irc> // reset at the same time.
<re_irc> pub const BAUD_AT_RESET: u32 = 9_600;
<re_irc> pub const BAUD: u32 = 691_200;
<re_irc> // Maximum of 18Hz with a single constellation. Lower rates with fused data. For example,
<re_irc> // GPS + GAL is 10Hz max.
<re_irc> pub const MEASUREMENT_RATE: f32 = 10.; // Measurement rate in Hz.
<re_irc> // Includes start bytes, class, id, payload length, and CRC.
<re_irc> const MSG_SIZE_WITHOUT_PAYLOAD: usize = 8;
<re_irc> // Position, velocity, time data payload side: UBX-NAV-PVT.
<re_irc> const PAYLOAD_LEN_PVT: usize = 92;
<re_irc> const PAYLOAD_LEN_DOP: usize = 18;
<re_irc> const PAYLOAD_LEN_COVARIANCE: usize = 64;
<re_irc> // Payload length for an acknowledgement message.
<re_irc> const PAYLOAD_LEN_ACK_NAK: usize = 2;
<re_irc> // The first few messages of config are reserved or used to set RAM vs FLASH
<re_irc> const CFG_PAYLOAD_START_I: usize = 4;
<re_irc> // We use this max length for our DMA read buffer.
<re_irc> // We pad this due to the possibility of shifted data.
<re_irc> pub const MAX_BUF_LEN: usize = PAYLOAD_LEN_PVT + MSG_SIZE_WITHOUT_PAYLOAD + 4;
<re_irc> pub static mut RX_BUFFER: [u8; MAX_BUF_LEN] = [0; MAX_BUF_LEN];
<re_irc> pub static TRANSFER_IN_PROG: AtomicBool = AtomicBool::new(false);
<re_irc> // todo: Dedicated lib for these helpers:
<re_irc> /// Helper function; keeps syntax terser on repeated calls.
<re_irc> fn u16_from_le(buf: &[u8]) -> u16 {
<re_irc> u16::from_le_bytes(buf.try_into().unwrap())
<re_irc> }
<re_irc> /// Helper function; keeps syntax terser on repeated calls.
<re_irc> fn i32_from_le(buf: &[u8]) -> i32 {
<re_irc> i32::from_le_bytes(buf.try_into().unwrap())
<re_irc> }
<re_irc> /// Helper function; keeps syntax terser on repeated calls.
<re_irc> fn f32_from_le(buf: &[u8]) -> f32 {
<re_irc> f32::from_le_bytes(buf.try_into().unwrap())
<re_irc> }
<re_irc> /// Get position, velocity, and time data, assuming it has already been transsferred into the reception
<re_irc> /// buffer. Run this after a packet has been completedly received, eg as indicated by the UART idle
<re_irc> /// interrupt.
<re_irc> ///
<re_irc> /// The input buffer includes the entire packet, including CRC, message length, Class and ID etc.
<re_irc> ///
<re_irc> /// Timestamp is seconds since program start.
<re_irc> pub fn fix_from_payload(payload: &[u8], timestamp: f32) -> Result<Fix, GnssError> {
<re_irc> if payload.len() < PAYLOAD_LEN_PVT {
<re_irc> println!("Incorrect PVT payload.");
<re_irc> return Err(GnssError::MessageData);
<re_irc> }
<re_irc> let flags = payload[21];
<re_irc> let fix_ok = (flags & 1) != 0;
<re_irc> let heading_valid = (flags & 0b10_0000) != 0;
<re_irc> let date = NaiveDate::from_ymd_opt(
<re_irc> u16_from_le(&payload[4..6]) as i32,
<re_irc> payload[6] as u32,
<re_irc> payload[7] as u32,
<re_irc> );
<re_irc> if date.is_none() {
<re_irc> return Err(GnssError::MessageData);
<re_irc> }
<re_irc> let date = date.unwrap(); // eg invalid values.
<re_irc> let ns = i32_from_le(&payload[16..20]);
<re_irc> let datetime = date.and_hms_nano_opt(
<re_irc> payload[8] as u32,
<re_irc> payload[9] as u32,
<re_irc> payload[10] as u32,
<re_irc> ns as u32,
<re_irc> );
<re_irc> if datetime.is_none() {
<re_irc> return Err(GnssError::MessageData);
<re_irc> }
<re_irc> let datetime = datetime.unwrap();
<re_irc> let lat_e7 = i32_from_le(&payload[28..32]);
<re_irc> let lon_e7 = i32_from_le(&payload[24..28]);
<re_irc> let heading = if heading_valid {
<re_irc> Some(i32_from_le(&payload[84..88]) as f32)
<re_irc> } else {
<re_irc> None
<re_irc> };
<re_irc> let type_ = if fix_ok {
<re_irc> payload[20].try_into()?
<re_irc> } else {
<re_irc> FixType::NoFix
<re_irc> };
<re_irc> Ok(Fix {
<re_irc> timestamp_s: timestamp,
<re_irc> datetime,
<re_irc> type_,
<re_irc> lat_e7,
<re_irc> lon_e7,
<re_irc> elevation_hae: i32_from_le(&payload[32..36]),
<re_irc> elevation_msl: i32_from_le(&payload[36..40]),
<re_irc> ground_speed: i32_from_le(&payload[60..64]),
<re_irc> ned_velocity: [
<re_irc> i32_from_le(&payload[48..52]),
<re_irc> i32_from_le(&payload[52..56]),
<re_irc> i32_from_le(&payload[56..60]),
<re_irc> ],
<re_irc> heading,
<re_irc> sats_used: payload[23],
<re_irc> pdop: u16_from_le(&payload[76..78]),
<re_irc> })
<re_irc> }
<re_irc> // pub fn print(&self) {
<re_irc> // println!(
<re_irc> // "Fix data: Timestamp: {}, {}:{}:{}, type: {}, \nlat: {}, lon: {}, \n\
<re_irc> // HAE: {}, MSL: {}, sats used: {}",
<re_irc> // self.datetime.day(),
<re_irc> // self.datetime.hour(),
<re_irc> // self.datetime.minute(),
<re_irc> // self.datetime.second(),
<re_irc> // self.type_ as u8,
<re_irc> // self.lat as f32 / 10_000_000.,
<re_irc> // self.lon as f32 / 10_000_000.,
<re_irc> // self.elevation_hae as f32 / 1_000.,
<re_irc> // self.elevation_msl as f32 / 1_000.,
<re_irc> // self.sats_used,
<re_irc> // );
<re_irc> // }
<re_irc> #[derive(Default)]
<re_irc> /// Dilution of precision (DOP) Eg parsed from UBX-NAV-DOP.
<re_irc> /// DOP values are dimensionless.
<re_irc> /// All DOP values are scaled by a factor of 100. If the unit transmits a value of e.g. 156,
<re_irc> // the DOP value is 1.56.
<re_irc> pub struct DilutionOfPrecision {
<re_irc> /// GPS time of week of the navigation epoch; reported by the GNSS.
<re_irc> // pub timestamp: u32,
<re_irc> // pub datetime: NaiveDateTime,
<re_irc> pub geometric: u16,
<re_irc> pub position: u16,
<re_irc> pub time: u16,
<re_irc> pub vertical: u16,
<re_irc> pub horizontal: u16,
<re_irc> pub northing: u16,
<re_irc> pub easting: u16,
<re_irc> }
<re_irc> impl DilutionOfPrecision {
<re_irc> pub fn from_payload(payload: &[u8]) -> Result<Self, GnssError> {
<re_irc> if payload.len() < PAYLOAD_LEN_DOP {
<re_irc> println!("Incorrect DOP payload.");
<re_irc> return Err(GnssError::MessageData);
<re_irc> }
<re_irc> Ok(Self {
<re_irc> geometric: u16_from_le(&payload[4..6]),
<re_irc> position: u16_from_le(&payload[6..8]),
<re_irc> time: u16_from_le(&payload[8..10]),
<re_irc> vertical: u16_from_le(&payload[10..12]),
<re_irc> horizontal: u16_from_le(&payload[12..14]),
<re_irc> northing: u16_from_le(&payload[14..16]),
<re_irc> easting: u16_from_le(&payload[16..18]),
<re_irc> })
<re_irc> }
<re_irc> }
<re_irc> /// UBX-NAV-COV
<re_irc> /// "This message outputs the covariance matrices for the position and velocity solutions in the topocentric
<re_irc> /// coordinate system defined as the local-level North (N), East (E), Down (D) frame. As the covariance matrices
<re_irc> /// are symmetric, only the upper triangular part is output."
<re_irc> #[derive(Default)]
<re_irc> pub struct Covariance {
<re_irc> pub posit_valid: bool,
<re_irc> pub velocity_valid: bool,
<re_irc> pub pos_nn: f32,
<re_irc> pub pos_ne: f32,
<re_irc> pub pos_nd: f32,
<re_irc> pub pos_ee: f32,
<re_irc> pub pos_ed: f32,
<re_irc> pub pos_dd: f32,
<re_irc> pub vel_nn: f32,
<re_irc> pub vel_ne: f32,
<re_irc> pub vel_nd: f32,
<re_irc> pub vel_ee: f32,
<re_irc> pub vel_ed: f32,
<re_irc> pub vel_dd: f32,
<re_irc> }
<re_irc> impl Covariance {
<re_irc> pub fn from_payload(payload: &[u8]) -> Result<Self, GnssError> {
<re_irc> if payload.len() < PAYLOAD_LEN_COVARIANCE {
<re_irc> println!("Incorrect DOP payload.");
<re_irc> return Err(GnssError::MessageData);
<re_irc> }
<re_irc> Ok(Self {
<re_irc> posit_valid: payload[5] != 0,
<re_irc> velocity_valid: payload[6] != 0,
<re_irc> pos_nn: f32_from_le(&payload[16..20]),
<re_irc> pos_ne: f32_from_le(&payload[20..24]),
<re_irc> pos_nd: f32_from_le(&payload[24..28]),
<re_irc> pos_ee: f32_from_le(&payload[28..32]),
<re_irc> pos_ed: f32_from_le(&payload[32..36]),
<re_irc> pos_dd: f32_from_le(&payload[36..40]),
<re_irc> vel_nn: f32_from_le(&payload[40..44]),
<re_irc> vel_ne: f32_from_le(&payload[44..48]),
<re_irc> vel_nd: f32_from_le(&payload[48..52]),
<re_irc> vel_ee: f32_from_le(&payload[52..56]),
<re_irc> vel_ed: f32_from_le(&payload[56..60]),
<re_irc> vel_dd: f32_from_le(&payload[60..64]),
<re_irc> })
<re_irc> }
<re_irc> }
<re_irc> #[derive(Clone, Copy, PartialEq)]
<re_irc> /// See Interface manual, section 3.8: UBX messages overview
<re_irc> ///
<re_irc> /// todo: Class enum, and id-per-class enum?
<re_irc> pub enum MsgClassId {
<re_irc> /// Message acknowledged (Output)
<re_irc> AckAck,
<re_irc> /// Message not acknowledged (Output)
<re_irc> AckNak,
<re_irc> /// Clear, save and load configurations (Command)
<re_irc> CfgCfg,
<re_irc> /// Reset receiver / Clear backup data structures (Command)
<re_irc> CfgRst,
<re_irc> /// Delete configuration item values (Set)
<re_irc> /// Delete configuration item values (with transaction) (Set)
<re_irc> CfgValDel,
<re_irc> CfgValGet,
<re_irc> CfgValSet,
<re_irc> InfDebug,
<re_irc> InfError,
<re_irc> InfNotice,
<re_irc> InfTest,
<re_irc> InfWarning,
<re_irc> LogBatch,
<re_irc> LogRetrieveBath,
<re_irc> MgaAck,
<re_irc> MgaAno,
<re_irc> MgaBds,
<re_irc> MgaDbd,
<re_irc> MgaGal,
<re_irc> MgaGlo,
<re_irc> MgaGps,
<re_irc> MgaIni,
<re_irc> MgaQzss,
<re_irc> MonBatch,
<re_irc> MonComms,
<re_irc> MonGnss,
<re_irc> MonHw3,
<re_irc> MonPatch,
<re_irc> MonRf,
<re_irc> MonRxr,
<re_irc> MonSpan,
<re_irc> MonVer,
<re_irc> /// AssistNow Autonomous status (Periodic/polled)
<re_irc> NavAopstatus,
<re_irc> /// Clock solution (Periodic/polled)
<re_irc> NavClock,
<re_irc> NavCov,
<re_irc> NavDop,
<re_irc> NavEoe,
<re_irc> NavOdo,
<re_irc> NavOrb,
<re_irc> NavPl,
<re_irc> NavPosecef,
<re_irc> NavPosllh,
<re_irc> NavPvt,
<re_irc> NavResetOdo,
<re_irc> /// Satellite information (Periodic/polled)
<re_irc> NavSat,
<re_irc> /// SBAS status data (Periodic/polled)
<re_irc> NavSbas,
<re_irc> /// Signal information (Periodic/polled)
<re_irc> NavSig,
<re_irc> NavSlas,
<re_irc> NavStatus,
<re_irc> NavTimeBds,
<re_irc> NavTimeGal,
<re_irc> NavTimeGlo,
<re_irc> NavTimeGps,
<re_irc> NavTimeLs,
<re_irc> NavTimeQzss,
<re_irc> /// UTC time solution (Periodic/polled)
<re_irc> NavTimeUtc,
<re_irc> /// Velocity solution in ECEF (Periodic/polled)
<re_irc> NavVelecef,
<re_irc> /// Velocity solution in NED frame (Periodic/polled)
<re_irc> NavVelned,
<re_irc> RxmMeas20,
<re_irc> RxmMeas50,
<re_irc> RxmMeasc12,
<re_irc> RxmMeasd12,
<re_irc> RxmMeasx,
<re_irc> RxmPmreq,
<re_irc> RxmRlm,
<re_irc> RxmSfrbx,
<re_irc> SecUniqid,
<re_irc> TimTm2,
<re_irc> TimTp,
<re_irc> TimVrfy,
<re_irc> UpdSos,
<re_irc> }
<re_irc> impl MsgClassId {
<re_irc> /// Return a (class, id) tuple.
<re_irc> pub fn to_vals(&self) -> (u8, u8) {
<re_irc> match self {
<re_irc> Self::AckAck => (0x05, 0x01),
<re_irc> Self::AckNak => (0x05, 0x00),
<re_irc> Self::CfgCfg => (0x06, 0x09),
<re_irc> Self::CfgRst => (0x06, 0x04),
<re_irc> Self::CfgValDel => (0x06, 0x8c),
<re_irc> Self::CfgValGet => (0x06, 0x8b),
<re_irc> Self::CfgValSet => (0x06, 0x8a),
<re_irc> Self::InfDebug => (0x04, 0x04),
<re_irc> Self::InfError => (0x04, 0x00),
<re_irc> Self::InfNotice => (0x04, 0x02),
<re_irc> Self::InfTest => (0x04, 0x03),
<re_irc> Self::InfWarning => (0x04, 0x01),
<re_irc> Self::LogBatch => (0x21, 0x11),
<re_irc> Self::LogRetrieveBath => (0x21, 0x10),
<re_irc> Self::MgaAck => (0x13, 0x60),
<re_irc> Self::MgaAno => (0x13, 0x20),
<re_irc> Self::MgaBds => (0x13, 0x03),
<re_irc> Self::MgaDbd => (0x13, 0x80),
<re_irc> Self::MgaGal => (0x13, 0x02),
<re_irc> Self::MgaGlo => (0x13, 0x06),
<re_irc> Self::MgaGps => (0x13, 0x00),
<re_irc> Self::MgaIni => (0x13, 0x40),
<re_irc> Self::MgaQzss => (0x13, 0x05),
<re_irc> Self::MonBatch => (0x0a, 0x32),
<re_irc> Self::MonComms => (0x0a, 0x36),
<re_irc> Self::MonGnss => (0x0a, 0x28),
<re_irc> Self::MonHw3 => (0x0a, 0x37),
<re_irc> Self::MonPatch => (0x0a, 0x27),
<re_irc> Self::MonRf => (0x0a, 0x38),
<re_irc> Self::MonRxr => (0x0a, 0x21),
<re_irc> Self::MonSpan => (0x0a, 0x31),
<re_irc> Self::MonVer => (0x0a, 0x04),
<re_irc> Self::NavAopstatus => (0x01, 0x60),
<re_irc> Self::NavClock => (0x01, 0x22),
<re_irc> Self::NavCov => (0x01, 0x36),
<re_irc> Self::NavDop => (0x01, 0x04),
<re_irc> Self::NavEoe => (0x01, 0x61),
<re_irc> Self::NavOdo => (0x01, 0x09),
<re_irc> Self::NavOrb => (0x01, 0x34),
<re_irc> Self::NavPl => (0x01, 0x62),
<re_irc> Self::NavPosecef => (0x01, 0x01),
<re_irc> Self::NavPosllh => (0x01, 0x02),
<re_irc> Self::NavPvt => (0x01, 0x07),
<re_irc> Self::NavResetOdo => (0x01, 0x10),
<re_irc> Self::NavSat => (0x01, 0x35),
<re_irc> Self::NavSbas => (0x01, 0x32),
<re_irc> Self::NavSig => (0x01, 0x43),
<re_irc> Self::NavSlas => (0x01, 0x42),
<re_irc> Self::NavStatus => (0x01, 0x03),
<re_irc> Self::NavTimeBds => (0x01, 0x24),
<re_irc> Self::NavTimeGal => (0x01, 0x25),
<re_irc> Self::NavTimeGlo => (0x01, 0x23),
<re_irc> Self::NavTimeGps => (0x01, 0x20),
<re_irc> Self::NavTimeLs => (0x01, 0x26),
<re_irc> Self::NavTimeQzss => (0x01, 0x27),
<re_irc> Self::NavTimeUtc => (0x01, 0x21),
<re_irc> Self::NavVelecef => (0x01, 0x11),
<re_irc> Self::NavVelned => (0x01, 0x12),
<re_irc> Self::RxmMeas20 => (0x02, 0x84),
<re_irc> Self::RxmMeas50 => (0x02, 0x86),
<re_irc> Self::RxmMeasc12 => (0x02, 0x82),
<re_irc> Self::RxmMeasd12 => (0x02, 0x80),
<re_irc> Self::RxmMeasx => (0x02, 0x14),
<re_irc> Self::RxmPmreq => (0x02, 0x41),
<re_irc> Self::RxmRlm => (0x02, 0x59),
<re_irc> Self::RxmSfrbx => (0x02, 0x13),
<re_irc> Self::SecUniqid => (0x27, 0x03),
<re_irc> Self::TimTm2 => (0x0d, 0x03),
<re_irc> Self::TimTp => (0x0d, 0x001),
<re_irc> Self::TimVrfy => (0xd0, 0x06),
<re_irc> Self::UpdSos => (0x09, 0x14),
<re_irc> }
<re_irc> }
<re_irc> /// Construct a message given (class, id) numerical values.
<re_irc> pub fn from_vals(vals: (u8, u8)) -> Result<Self, GnssError> {
<re_irc> Ok(match vals {
<re_irc> (0x05, 0x01) => Self::AckAck,
<re_irc> (0x05, 0x00) => Self::AckNak,
<re_irc> (0x06, 0x09) => Self::CfgCfg,
<re_irc> (0x06, 0x04) => Self::CfgRst,
<re_irc> (0x06, 0x8c) => Self::CfgValDel,
<re_irc> (0x06, 0x8b) => Self::CfgValGet,
<re_irc> (0x06, 0x8a) => Self::CfgValSet,
<re_irc> (0x04, 0x04) => Self::InfDebug,
<re_irc> (0x04, 0x00) => Self::InfError,
<re_irc> (0x04, 0x02) => Self::InfNotice,
<re_irc> (0x04, 0x03) => Self::InfTest,
<re_irc> (0x04, 0x01) => Self::InfWarning,
<re_irc> (0x21, 0x11) => Self::LogBatch,
<re_irc> (0x21, 0x10) => Self::LogRetrieveBath,
<re_irc> (0x13, 0x60) => Self::MgaAck,
<re_irc> (0x13, 0x20) => Self::MgaAno,
<re_irc> (0x13, 0x03) => Self::MgaBds,
<re_irc> (0x13, 0x80) => Self::MgaDbd,
<re_irc> (0x13, 0x02) => Self::MgaGal,
<re_irc> (0x13, 0x06) => Self::MgaGlo,
<re_irc> (0x13, 0x00) => Self::MgaGps,
<re_irc> (0x13, 0x40) => Self::MgaIni,
<re_irc> (0x13, 0x05) => Self::MgaQzss,
<re_irc> (0x0a, 0x32) => Self::MonBatch,
<re_irc> (0x0a, 0x36) => Self::MonComms,
<re_irc> (0x0a, 0x28) => Self::MonGnss,
<re_irc> (0x0a, 0x37) => Self::MonHw3,
<re_irc> (0x0a, 0x27) => Self::MonPatch,
<re_irc> (0x0a, 0x38) => Self::MonRf,
<re_irc> (0x0a, 0x21) => Self::MonRxr,
<re_irc> (0x0a, 0x31) => Self::MonSpan,
<re_irc> (0x0a, 0x04) => Self::MonVer,
<re_irc> (0x01, 0x60) => Self::NavAopstatus,
<re_irc> (0x01, 0x22) => Self::NavClock,
<re_irc> (0x01, 0x36) => Self::NavCov,
<re_irc> (0x01, 0x04) => Self::NavDop,
<re_irc> (0x01, 0x61) => Self::NavEoe,
<re_irc> (0x01, 0x09) => Self::NavOdo,
<re_irc> (0x01, 0x34) => Self::NavOrb,
<re_irc> (0x01, 0x62) => Self::NavPl,
<re_irc> (0x01, 0x01) => Self::NavPosecef,
<re_irc> (0x01, 0x02) => Self::NavPosllh,
<re_irc> (0x01, 0x07) => Self::NavPvt,
<re_irc> (0x01, 0x10) => Self::NavResetOdo,
<re_irc> (0x01, 0x35) => Self::NavSat,
<re_irc> (0x01, 0x32) => Self::NavSbas,
<re_irc> (0x01, 0x43) => Self::NavSig,
<re_irc> (0x01, 0x42) => Self::NavSlas,
<re_irc> (0x01, 0x03) => Self::NavStatus,
<re_irc> (0x01, 0x24) => Self::NavTimeBds,
<re_irc> (0x01, 0x25) => Self::NavTimeGal,
<re_irc> (0x01, 0x23) => Self::NavTimeGlo,
<re_irc> (0x01, 0x20) => Self::NavTimeGps,
<re_irc> (0x01, 0x26) => Self::NavTimeLs,
<re_irc> (0x01, 0x27) => Self::NavTimeQzss,
<re_irc> (0x01, 0x21) => Self::NavTimeUtc,
<re_irc> (0x01, 0x11) => Self::NavVelecef,
<re_irc> (0x01, 0x12) => Self::NavVelned,
<re_irc> (0x02, 0x84) => Self::RxmMeas20,
<re_irc> (0x02, 0x86) => Self::RxmMeas50,
<re_irc> (0x02, 0x82) => Self::RxmMeasc12,
<re_irc> (0x02, 0x80) => Self::RxmMeasd12,
<re_irc> (0x02, 0x14) => Self::RxmMeasx,
<re_irc> (0x02, 0x41) => Self::RxmPmreq,
<re_irc> (0x02, 0x59) => Self::RxmRlm,
<re_irc> (0x02, 0x13) => Self::RxmSfrbx,
<re_irc> (0x27, 0x03) => Self::SecUniqid,
<re_irc> (0x0d, 0x03) => Self::TimTm2,
<re_irc> (0x0d, 0x01) => Self::TimTp,
<re_irc> (0xd0, 0x06) => Self::TimVrfy,
<re_irc> (0x09, 0x14) => Self::UpdSos,
<re_irc> _ => {
<re_irc> return Err(GnssError::MsgType);
<re_irc> }
<re_irc> })
<re_irc> }
<re_irc> }
<re_irc> pub enum GnssError {
<re_irc> Bus,
<re_irc> Fix,
<re_irc> /// CRC validation failed for a recieved message.
<re_irc> Crc,
<re_irc> MsgType,
<re_irc> /// Received Nak, or did not receive Ack.
<re_irc> NoAck,
<re_irc> MessageData,
<re_irc> }
<re_irc> impl From<usart::Error> for GnssError {
<re_irc> fn from(_e: usart::Error) -> Self {
<re_irc> Self::Bus
<re_irc> }
<re_irc> }
<re_irc> impl From<TryFromPrimitiveError<FixType>> for GnssError {
<re_irc> fn from(_e: TryFromPrimitiveError<FixType>) -> Self {
<re_irc> Self::Fix
<re_irc> }
<re_irc> }
<re_irc> pub struct Message<'a> {
<re_irc> /// A 1-byte message class field follows. A class is a group of messages that are related to each
<re_irc> /// other.
<re_irc> /// A 1-byte message ID field defines the message that is to follow.
<re_irc> pub class_id: MsgClassId,
<re_irc> /// A 2-byte length field follows. The length is defined as being that of the payload only. It does not
<re_irc> /// include the preamble, message class, message ID, length, or UBX checksum fields. The number
<re_irc> /// format of the length field is an unsigned little-endian 16-bit integer (a "U2" in UBX data types).
<re_irc> pub payload_len: u16,
<re_irc> pub payload: &'a [u8],
<re_irc> }
<re_irc> /// See interface manual, section 3.4: UBX checksum
<re_irc> /// "The checksum is calculated over the message, starting and including the class field up until, but
<re_irc> /// excluding, the checksum fields (see the figure UBX frame structure).
<re_irc> /// The checksum algorithm used is the 8-bit Fletcher algorithm, which is used in the TCP standard
<re_irc> /// RFC 1145)."
<re_irc> ///
<re_irc> /// This is a standalone fn due to needing a ref to the buffer in question.
<re_irc> fn calc_checksum(buffer: &[u8]) -> (u8, u8) {
<re_irc> // This code is taken directly from the interface manual.
<re_irc> let mut ck_a = 0;
<re_irc> let mut ck_b = 0;
<re_irc> for val in buffer {
<re_irc> ck_a += val;
<re_irc> ck_b += ck_a;
<re_irc> }
<re_irc> (ck_a, ck_b)
<re_irc> }
<re_irc> impl<'a> Message<'a> {
<re_irc> pub fn to_buf(&self, buf: &mut [u8]) {
<re_irc> let payload_end = 6 + self.payload_len as usize;
<re_irc> let (class, id) = self.class_id.to_vals();
<re_irc> buf[0] = PREAMBLE_1;
<re_irc> buf[1] = PREAMBLE_2;
<re_irc> buf[2] = class;
<re_irc> buf[3] = id;
<re_irc> buf[4..6].clone_from_slice(&self.payload_len.to_le_bytes());
<re_irc> buf[6..payload_end].clone_from_slice(self.payload);
<re_irc> // "The checksum is calculated over the message, starting and including the class field up until, but
<re_irc> // excluding, the checksum fields"
<re_irc> let (checksum_a, checksum_b) = calc_checksum(&buf[2..payload_end]);
<Darius> I really wish people wouldn't paste stuff into here
<re_irc> buf[payload_end] = checksum_a;
<re_irc> buf[payload_end + 1] = checksum_b;
<re_irc> }
<re_irc> pub fn from_buf(buf: &'a [u8]) -> Result<Self, GnssError> {
<re_irc> let mut shift: isize = 0;
<re_irc> // Check if the message has been shifted left due to jitter;
<re_irc> // if so, shift right accordingly.
<Darius> absolutely nukes the IRC bridge..
<re_irc> if buf[0] == PREAMBLE_2 {
<re_irc> shift = -1;
<re_irc> // println!("GNSS L shift")
<re_irc> } else if buf[0] != PREAMBLE_1 {
<re_irc> return Err(GnssError::MessageData);
<re_irc> // We can't prove this, but it's possible
<re_irc> // shift = 1;
<re_irc> // println!("Trying a right shift");
<re_irc> // println!("BUf first 5: {:?}", buf[0 ..5]);
<re_irc> }
<re_irc> let class = buf[(2 + shift) as usize];
<re_irc> let id = buf[(3 + shift) as usize];
<re_irc> let class_id = MsgClassId::from_vals((class, id))?;
<re_irc> let payload_len = u16_from_le(&buf[(4 + shift) as usize..(6 + shift) as usize]);
<re_irc> let payload_end = (6 + shift) as usize + payload_len as usize;
<re_irc> if payload_end > buf.len() {
<re_irc> // This can come up while reading the acknowledgement if attempting to set up while already
<re_irc> // set up, at a high data rate, eg if the read happens during PVT reception.
<re_irc> return Err(GnssError::MessageData);
<re_irc> }
<re_irc> let result = Self {
<re_irc> class_id,
<re_irc> payload_len,
<re_irc> payload: &buf[(6 + shift) as usize..payload_end],
<re_irc> };
<re_irc> let crc_received = (buf[payload_end], buf[payload_end + 1]);
<re_irc> if crc_received != calc_checksum(&buf[(2 + shift as usize)..payload_end]) {
<re_irc> return Err(GnssError::Crc);
<re_irc> }
<re_irc> Ok(result)
<re_irc> }
<re_irc> }
<re_irc> /// Configure the UART interrupts, and GNSS configuration settings.
<re_irc> /// Configure the Char match and idle interrupts, which will allow the initial UART ISR to run
<re_irc> /// upon receiving data. Run this once, on initial firmware setup.
<re_irc> /// We alternate between char matching the flight controller destination address, and
<re_irc> /// line idle, to indicate we're received, or stopped receiving a message respectively.
<re_irc> /// Additionally, configure several settings on the GNSS module itself.
<re_irc> /// After this is run, the module will periodically transmit Position, Velocity, and Time (PVT)
<re_irc> /// packets.
<re_irc> pub fn setup(uart: &mut UartGnss, clock_cfg: &Clocks) -> Result<(), GnssError> {
<re_irc> // todo: You should enable sensor fusion mode, eg to get heading?
<re_irc> // todo: Enable dead-recoking.
<re_irc> uart.enable_interrupt(UsartInterrupt::CharDetect(Some(PREAMBLE_1)));
<re_irc> uart.enable_interrupt(UsartInterrupt::Idle);
<re_irc> // Note: Fix mode defaults to auto, which allows fixes from multiple constellations.
<re_irc> // CFG-UART1_BAUDRATE
<re_irc> let key_id_baud: u32 = 0x4052_0001;
<re_irc> let val_baud: u32 = BAUD;
<re_irc> // Output rate of the UBX-NAV-PVT message on
<re_irc> // port UART1. By default, no fix messages are output. It appears this is a divisor of the
<re_irc> // measurement rate. So, a value of 1 means 1 PVT output on UART1 per measurement.
<re_irc> // CFG-MSGOUT-UBX_NAV_PVT_UART1
<re_irc> let key_id_pvt_rate: u32 = 0x2091_0007;
<re_irc> let val_pvt_rate: u8 = 1;
<re_irc> // CFG-RATE-MEAS
<re_irc> let key_id_rate_meas: u32 = 0x3021_0001;
<re_irc> let val_rate_meas = (1_000. / MEASUREMENT_RATE) as u16;
<re_irc> // CFG-MSGOUT-UBX_NAV_DOP_UART1
<re_irc> let key_id_dop_rate: u32 = 0x2091_0039;
<re_irc> let val_dop_rate: u8 = 1;
<re_irc> // CFG-MSGOUT-UBX_NAV_COV_UART1
<re_irc> let key_id_cov_rate: u32 = 0x2091_0084;
<re_irc> let val_cov_rate: u8 = 1;
<re_irc> // "Configuration data is the binary representation of a list of Key ID and Value pairs. It is formed by
<re_irc> // concatenating keys (U4 values) and values (variable type) without any padding. This format is used
<re_irc> // in the UBX-CFG-VALSET and UBX-CFG-VALGET messages."
<re_irc> const PAYLOAD_LEN_CFG: u16 = 33; // Adjust this based on which items you configure.
<re_irc> let mut cfg_payload = [0; PAYLOAD_LEN_CFG as usize];
<re_irc> // Bytes 0 and 1 are CFG metadata, prior to the key and value pairs. We use this to set the layer
<re_irc> // as RAM. Bytes 2-3 are reserved.
<re_irc> cfg_payload[1] = 0b001;
<re_irc> cfg_payload[CFG_PAYLOAD_START_I..CFG_PAYLOAD_START_I + 4]
<re_irc> .clone_from_slice(&key_id_baud.to_le_bytes());
<re_irc> cfg_payload[8..12].clone_from_slice(&val_baud.to_le_bytes());
<re_irc> cfg_payload[12..16].clone_from_slice(&key_id_pvt_rate.to_le_bytes());
<re_irc> cfg_payload[16] = val_pvt_rate;
<re_irc> cfg_payload[17..21].clone_from_slice(&key_id_rate_meas.to_le_bytes());
<re_irc> cfg_payload[21..23].clone_from_slice(&val_rate_meas.to_le_bytes());
<re_irc> cfg_payload[23..27].clone_from_slice(&key_id_dop_rate.to_le_bytes());
<re_irc> cfg_payload[27] = val_dop_rate;
<re_irc> cfg_payload[28..32].clone_from_slice(&key_id_cov_rate.to_le_bytes());
<re_irc> cfg_payload[32] = val_cov_rate;
<re_irc> let cfg_msg = Message {
<re_irc> class_id: MsgClassId::CfgValSet,
<re_irc> payload_len: PAYLOAD_LEN_CFG,
<re_irc> payload: &cfg_payload,
<re_irc> };
<re_irc> const CFG_BUF_SIZE: usize = MSG_SIZE_WITHOUT_PAYLOAD + PAYLOAD_LEN_CFG as usize;
<re_irc> let mut cfg_write_buf = [0; CFG_BUF_SIZE];
<re_irc> cfg_msg.to_buf(&mut cfg_write_buf);
<re_irc> uart.write(&cfg_write_buf)?;
<re_irc> // The GNSS sends its ack at the new baud, so set it before reading.
<re_irc> uart.set_baud(BAUD, clock_cfg)?;
<re_irc> // Due to the delay on possible responses, don't check here.
<re_irc> Ok(())
<re_irc> }
<re_irc> <@firefrommoonlight:matrix.org> *I guess this probably doesn't help much since your issue is I2C-centric
<re_irc> <@firefrommoonlight:matrix.org> Any reason for I2C here? Of note, I think Uart works better here since the data is in long packets. I tend to think of I2C as for register-style APIs
<re_irc> <@firefrommoonlight:matrix.org> * message
<re_irc> <@firefrommoonlight:matrix.org> But obv either is fine
<re_irc> <@firefrommoonlight:matrix.org> So, I will say this re one of your suspicions. If I don't delay for ~300ms between power on and setup, it doesn't work
<re_irc> <@firefrommoonlight:matrix.org> I don't know why
<re_irc> <@firefrommoonlight:matrix.org> Easy enough to test
<re_irc> <@firefrommoonlight:matrix.org> But the more important question is this: How the hell di dyou get that device?
<re_irc> <@firefrommoonlight:matrix.org> * did you
<re_irc> <@firefrommoonlight:matrix.org> If you have the UART pins exposed, it may be worth trying them
<re_irc> <@whitequark:matrix.org> I'm going to try and bridge using catircservices.org again soonish
<re_irc> <@whitequark:matrix.org> (after matrix-appservice-irc is upgraded to 1.0.1 in nixos... so at least tomorrow, probably)
<re_irc> <@whitequark:matrix.org> that would fix it
crabbedhaloablut has joined #rust-embedded
duderonomy has joined #rust-embedded
<re_irc> <@xobs:matrix.org> Hey, I've got a question. I'm trying to upstream support for my OS, "riscv32imac-unknown-xous-elf". However, I notice that the RISC-V foundation renamed the arch, and it's now "riscv32imac_zicsr-unknown-xous-elf".
<re_irc> Have there been any other embedded targets that are going to rename their architectures? The issue I'm facing is that code compiled with "cc-rs" doesn't have access to CSRs, so for example I can't compile "libunwind".
<re_irc> <@whitequark:matrix.org> : so.. libunwind is using "csrr" in exactly one place: https://github.com/llvm/llvm-project/blob/beb89e7fc0026bd04ce1da2d2d0f171a8293eecc/libunwind/src/Registers.hpp#L4106
<re_irc> <@whitequark:matrix.org> and this seems to only matter if you have the vector extension enabled
<re_irc> <@whitequark:matrix.org> why not add a conditional there?
<re_irc> <@whitequark:matrix.org> then you don't actually have to rename anything
<re_irc> <@whitequark:matrix.org> it was added here: https://reviews.llvm.org/D136264
<re_irc> <@whitequark:matrix.org> it looks like you can detect the vector extension (idk how... presumably riscv has an intrinsic for it?) and gate the entire block if it's not found
<re_irc> <@whitequark:matrix.org> returning 0 otherwis
<re_irc> <@whitequark:matrix.org> this is a libunwind bug
<re_irc> <@whitequark:matrix.org> returning 0 otherwise
<re_irc> <@whitequark:matrix.org> https://reviews.llvm.org/D136264#4545515
<re_irc> <@xobs:matrix.org> I was considering just hardcoding the opcode for "csrr" as a ".word", which could work. I've been patching "cc-rs" for now, which works, but isn't pretty at all.
<re_irc> <@whitequark:matrix.org> I'm a libunwind reviewer, if you add the conditional, I'll commit it for you
<re_irc> <@xobs:matrix.org> I don't know that there is a "#define" to look for when vector extensions are enabled.
<re_irc> <@xobs:matrix.org> But I'll check!
<re_irc> <@whitequark:matrix.org> "__riscv_v"
<re_irc> <@whitequark:matrix.org> but it's easier to branch on "__riscv_zicsr" which I suggested in the comment
<re_irc> <@whitequark:matrix.org> actually, thinking about it more, branching on "__riscv_v" is more correct
<re_irc> <@whitequark:matrix.org> this way you won't end up with a broken libunwind if by some accident you end up with +v -zicsr
<re_irc> <@whitequark:matrix.org> "__riscv_vector" also works and looks nicer
<re_irc> <@xobs:matrix.org> That seems like a fantastic solution.
<re_irc> <@whitequark:matrix.org> (I'm looking at the output of "riscv64-unknown-elf-gcc -dM -E -march=rv64gv - </dev/null|grep -i risc")
<re_irc> <@whitequark:matrix.org> IMO people are way too hesitant to patch LLVM components when there are obvious bugs
<re_irc> <@whitequark:matrix.org> LLVM bugs happen all the time, especially off the trodden path
<re_irc> <@whitequark:matrix.org> for posterity:
<re_irc> #define __riscv 1
<re_irc> $ riscv64-unknown-elf-gcc -dM -E -march=rv64gv - </dev/null|grep -i risc
<re_irc> #define __riscv_atomic 1
<re_irc> #define __riscv_v_elen_fp 64
<re_irc> #define __riscv_zvl32b 1000000
<re_irc> #define __riscv_cmodel_medlow 1
<re_irc> #define __riscv_fdiv 1
<re_irc> #define __riscv_float_abi_double 1
<re_irc> #define __riscv_zve64d 1000000
<re_irc> #define __riscv_zve64f 1000000
<re_irc> #define __riscv_zve64x 1000000
<re_irc> #define __riscv_mul 1
<re_irc> #define __riscv_muldiv 1
<re_irc> #define __riscv_xlen 64
<re_irc> #define __riscv_zve32f 1000000
<re_irc> #define __riscv_zve32x 1000000
<re_irc> #define __riscv_zvl128b 1000000
<re_irc> #define __riscv_fsqrt 1
<re_irc> #define __riscv_v_min_vlen 128
<re_irc> #define __riscv_v_elen 64
<re_irc> #define __riscv_m 2000000
<re_irc> #define __riscv_a 2001000
<re_irc> #define __riscv_d 2002000
<re_irc> #define __riscv_f 2002000
<re_irc> #define __riscv_i 2001000
<re_irc> #define __riscv_v 1000000
<re_irc> #define __riscv_zicsr 2000000
<re_irc> #define __riscv_vector 1
<re_irc> #define __riscv_flen 64
<re_irc> #define __riscv_arch_test 1
<re_irc> #define __riscv_zvl64b 1000000
<re_irc> #define __riscv_div 1
<re_irc> #define __riscv_zifencei 2000000
<re_irc> <@whitequark:matrix.org> oh gods, I'm so sorry for anyone on the IRC side
<re_irc> <@xobs:matrix.org> I'm pretending I'm Sgx so I'm using "UnwindRustSgx.c". How welcome do you think they'd be to modifying and reusing that? It seems like a bit of a hack, but it's a hack that's perfect for my target. Mostly I include "#include <link.h>" to get "alloca()", and I disable the body of "vwrite_err()" since I have no output handy.
<re_irc> <@whitequark:matrix.org> what's Sgx?
<re_irc> <@whitequark:matrix.org> UnwindRustSgx isn't an upstream thing in LLVM
<re_irc> <@whitequark:matrix.org> so you'd be talking to someone in the Rust project, not the LLVM project (i.e. not me)
<re_irc> <@xobs:matrix.org> My mistake, I saw it in https://github.com/rust-lang/llvm-project/blob/a7d11c453784a3f258c7269b5108c58592d27e1a/libunwind/src/UnwindRustSgx.c and I thought that Rust used upstream llvm without modifications, and requested all modifications be pushed upstream.
<re_irc> <@whitequark:matrix.org> nope
<re_irc> <@xobs:matrix.org> Alright, let me submit this to phabricator
hmw has quit [Quit: Bye.]
<re_irc> <@xobs:matrix.org> Okay, that's nice. I didn't realise llvm decided to ignore it when instructions were removed from the base spec. That's incredibly helpful, and explains why RISC-V "asm!()" code didn't break: https://llvm.org/docs/RISCVUsage.html#id5
hmw has joined #rust-embedded
<re_irc> <@sourcebox:matrix.org> Hi. I have a bunch of things to address regarding interfacing with C/C++ code. I did some basic setup using the "cc" crate and that generally works. What does not work out the box is linking to functions from libc or libm because there's no newlib in the game, so there has to be some solution for that. For the math functions, I did a test with the libm crate and wrap the functions to give them extern C linkage. Don't know...
<re_irc> ... if that's a good idea.
<re_irc> <@sourcebox:matrix.org> I also found "tinylibc" from . That is possibly something I should use.
<re_irc> <@9names:matrix.org> > there's no newlib in the game, so there has to be some solution for that.
<re_irc> well one solution is to link in newlib. have you considered it?
<re_irc> <@sourcebox:matrix.org> Yes, but I want to avoid that if possible. I think it requires to use the linker from the ARM toolchain.
<re_irc> <@sourcebox:matrix.org> The C/C++ code I'm talking about is mainly DSP code done by someone else. I expect that there will be some calls to math functions.
<re_irc> <@sourcebox:matrix.org> Making printf work would also not be a bad thing.
<re_irc> <@9names:matrix.org> how about trying picolibc?
<re_irc> i don't think there's precompiled binaries for it, but it's pretty simple to compile if you've got a linux dev environment, and there are scripts for building with clang for thumbv6m and thumbv7m targets.
<re_irc> if nothing else, building your own libc and linking it in yourself should help demystify the process somewhat
<re_irc> <@9names:matrix.org> - edit: precompiled binaries built by clang. there are gcc built ones, and a gcc toolchain, but the goal here is to avoid tools that magically solve your problems.
<re_irc> <@sourcebox:matrix.org> Ok, picolibc is (at least partly) GPL, so I don't need to read any further.
<re_irc> <@sourcebox:matrix.org> Maybe I should ask the other way around: is there some reason not to wrap libm functions like this:
<re_irc> #[no_mangle]
<re_irc> extern "C" fn sinf(x: f32) -> f32 {
<re_irc> libm::sinf(x)
<re_irc> }
<re_irc> <@diondokter:matrix.org> If that's what you need, why not just link in the 'normal' C libm?
<re_irc> <@ryan-summers:matrix.org> : I haven't been following closely, but if you want free, fast sin/cos functions, check out https://github.com/quartiq/idsp :)
<re_irc> <@sourcebox:matrix.org> Because I think the number of functions used is manageable.
<re_irc> <@9names:matrix.org> : did you read the licence section of the readme? it seems to me that you didn't.
<re_irc> <@sourcebox:matrix.org> : No, I just saw it on the meta info on GitHub.
<re_irc> <@sourcebox:matrix.org> : I don't need that functions in Rust, it's for the part written in C/C++ by someone else.
<re_irc> <@sourcebox:matrix.org> But you're right, licensing is ok if I read the details.
<re_irc> <@sourcebox:matrix.org> It's only because GH says "GPL-2.0 and 2 other licenses found".
<re_irc> <@sourcebox:matrix.org> Regarding performance, there will be LUTs used, I'm quite sure. But even if calculated via "constexpr", the functions have to be working.
<re_irc> <@sourcebox:matrix.org> Another things is how to deal with "malloc". If I'm going to provide that, it should use the "embedded-alloc" crate under the hood. Mainly because my setup is multi-core.
nex8192 has joined #rust-embedded
nex8192 is now known as Guest4814
GenTooMan has quit [Ping timeout: 246 seconds]
Guest4814 has quit [Changing host]
Guest4814 has joined #rust-embedded
GenTooMan has joined #rust-embedded
<re_irc> <@dirbaio:matrix.org> πŸ™ƒ https://github.com/rust-embedded/embedded-hal/issues/478
<re_irc> <@dirbaio:matrix.org> nothing new, we already knew we were sacrificing these use cases when we switched SpiDevice to the operation-list API
<re_irc> <@dirbaio:matrix.org> but still interesting, they make some strong points
<re_irc> <@diondokter:matrix.org> Just add an operation that takes a closure ;)
<re_irc> <@dirbaio:matrix.org> Box<dyn Fn>? ew :D
<re_irc> <@diondokter:matrix.org> Function pointer would work better :P
<re_irc> <@dirbaio:matrix.org> jokes aside, that defeats the point of the operation-list, which is to make SpiDevice implementable on top of linux spidev
<re_irc> <@diondokter:matrix.org> I know, just joking
<re_irc> <@dirbaio:matrix.org> we could just add an operation that contains an eBPF binary πŸ€ͺ
<re_irc> <@dirbaio:matrix.org> kernel-side closures! πŸš€
Foxyloxy has quit [Ping timeout: 252 seconds]
<re_irc> <@diondokter:matrix.org> Oh lol, somebody already asked about closures in that issue haha
IlPalazzo-ojiisa has joined #rust-embedded
<re_irc> <@lulf_:matrix.org> : Isn't this example dealing with the DC pin as discussed in that issue? https://github.com/embassy-rs/embassy/blob/main/examples/rp/src/bin/spi_display.rs#L180 or is it required to change the dc within the transaction?
<re_irc> <@dirbaio:matrix.org> no, that's doing separate CS toggles for each write
<re_irc> <@dirbaio:matrix.org> the ST7789 didn't seem to care, I don't know if others do
<re_irc> <@dirbaio:matrix.org> * other displays do
<re_irc> <@dirbaio:matrix.org> like, that code does
<re_irc> - DC low
<re_irc> - CS low
<re_irc> - write command
<re_irc> - CS high
<re_irc> - DC high
<re_irc> - CS low
<re_irc> - write data
<re_irc> - CS high
<re_irc> <@dirbaio:matrix.org> which is perfectly doable with SpiDevice
<re_irc> <@dirbaio:matrix.org> vs the "right" way would be
<re_irc> <@dirbaio:matrix.org> like, that code does
<re_irc> - DC low
<re_irc> - DC high
<re_irc> - write command
<re_irc> - CS low
<re_irc> - write data
<re_irc> - CS high
<re_irc> <@dirbaio:matrix.org> - DC low
<re_irc> - write command
<re_irc> - CS low
<re_irc> - DC high
<re_irc> - write data
<re_irc> - CS high
<re_irc> <@bugadani:matrix.org> : why does this example copy some of the display-interface-spi code?
<re_irc> <@dirbaio:matrix.org> because display-interface-spi is still on eh0.2 which doesn't have SpiDevice
<re_irc> <@bugadani:matrix.org> ah yeah that thing needs to be cleaned up and released :(
<re_irc> <@bugadani:matrix.org> +right
kenrendell[m] has quit [Write error: Connection reset by peer]
whitequark has quit [Remote host closed the connection]
<re_irc> <@dirbaio:matrix.org> it can't use SpiDevice thoguh due to the DC thing
<re_irc> <@dirbaio:matrix.org> unless it's guaranteed that all displays are OK with separate CS toggles?
<re_irc> <@dirbaio:matrix.org> in that case the discussion on the e-h issue is a bit moot πŸ˜‚
<re_irc> <@bugadani:matrix.org> I'd think nothing is guaranteed, but unless someone finds a device that cares about it...
<re_irc> <@bugadani:matrix.org> well I've removed the "SPIInterface" that togged CS but even that drew the line between data and command: each had their own transaction. I don't think there was any feedback that this is incorrect
<re_irc> <@dirbaio:matrix.org> that's never toggling CS though?
<re_irc> <@dirbaio:matrix.org> vs that embassy example is toggling CS "too much"
<re_irc> <@bugadani:matrix.org> okay I'll need to read back for context
kenrendell[m] has joined #rust-embedded
<re_irc> <@dhylands:matrix.org> I can give you a data sheet of a device Epson Gs370 IMU) that doesn’t want its CS toggled for transfers in burst mode.
whitequark has joined #rust-embedded
<re_irc> <@dirbaio:matrix.org> we're talking about SPI displays with a DC pin :)
<re_irc> <@dhylands:matrix.org> Ahhh. In that case I have no experience to offer.
<re_irc> <@bugadani:matrix.org> oh they are trying to read from that display, that's new πŸ˜… but the datasheet seems to be fine with keeping D/C low in this specific case
<re_irc> <@vollbrecht:matrix.org> the e-hal traits just need support for quad spi than we could misuses one of the lines as a dc line 🀯
<re_irc> <@bugadani:matrix.org> and also how do I start an I2C read after the 3rd bit of the second byte has been read?
Guest4814 has left #rust-embedded [Error from remote client]
<re_irc> <@dirbaio:matrix.org> what?
<re_irc> <@bugadani:matrix.org> I'm just trolling, asking where e-h should draw the line: a completely generic interface is impossible because linux doesn't like to work with callbacks, so how many exceptions does e-h want to define around SPI?
<re_irc> <@jamesmunns:beeper.com> personal request not to go to trolling, both for not being mean, and particularly as we have folks with varying levels of english, and sarcasm and similar is fairly hard to parse in a language you are not native to.
<re_irc> <@bugadani:matrix.org> : apologies
<re_irc> <@jamesmunns:beeper.com> it's also _really_ hard for anyone over text to tell the difference between "I'm joking with you" and "im making fun of you"
<re_irc> <@jamesmunns:beeper.com> (IM/chat lacks a lot of subtlety and context clues that'd be okay in person)
<re_irc> <@bugadani:matrix.org> Yeah I understand, and I'm always trying to get better but I've started pretty low on the communication skill scale, unfortunately. I'm never trying to make fun of anyone, except maybe myself.
<re_irc> <@bugadani:matrix.org> With that said
<re_irc> <@bugadani:matrix.org> My serious take is that there will always be exceptions. Some displays will not work with the mipidsi crate just becasue a minute detail, some will not work with display-interface. I feel like it's okay for embedded-hal to draw the line at one point, there will always be hardware, or combinations of hardware that it will not be able to support.
<re_irc> <@jamesmunns:beeper.com> I think this is very true, but I think you will find everyone _wants_ that line to be drawn somewhere different :)
<re_irc> <@jamesmunns:beeper.com> but yeah, e-hal maintainers are always allowed to say "no".
<re_irc> <@bugadani:matrix.org> : also true, but we have a specific issue where I think it is not necessary :)
<re_irc> <@bugadani:matrix.org> +to move the line
lightningwright has quit [Quit: ZNC - https://znc.in]
lightningwright has joined #rust-embedded
xnor has joined #rust-embedded
emerent has quit [Ping timeout: 245 seconds]
emerent has joined #rust-embedded
<re_irc> <@almindor:matrix.org> CS in most displays is really just a enabler (e.g. if CS is high, the device just ignores any inputs) and AFAICS (for my list of displays) it doesn't impact state (e.g. switching CS between DC doesn't change the logical outcome)
<re_irc> <@almindor:matrix.org> i didn't do any reading yet though to see if it becomes an issue there
<re_irc> <@almindor:matrix.org> as for sharing displays on one SPI, it's doable and I have a working demo
dc740 has joined #rust-embedded
<re_irc> <@sourcebox:matrix.org> When writing a wrapper for redirecting C's "malloc" to Rust, I think something like this will do it:
<re_irc> #[no_mangle]
<re_irc> extern "C" fn malloc(size: usize) -> *mut u8 {
<re_irc> }
<re_irc> But I think doing similar for "free" or "realloc" is not possible because these functions do not provide the size allocated.
<re_irc> unsafe { HEAP.alloc(Layout::from_size_align(size, 4).unwrap()) }
<Shell> hold up, no. malloc specifically returns a pointer that is aligned to alignof(max_align_t)
<re_irc> <@sourcebox:matrix.org> Shell: What does that mean in practice. I'm on an ARM Cortex-A7, so alignment is typically 4.
<re_irc> <@sourcebox:matrix.org> * practice?
<Shell> ah, fair.
<re_irc> <@ryan-summers:matrix.org> : For free etc, you'd have to do pointer math to figure out the memory locations to dealloc, just like C does I think
<re_irc> <@sourcebox:matrix.org> : That's obvious, but the free() function does not give me any size information of what the pointer was allocated before.
<re_irc> <@sourcebox:matrix.org> I could easily do a malloc(1024) but free is not knowing that.
<re_irc> <@sourcebox:matrix.org> But the problem of leaking is more a hypothetical one because memory is typically allocated once on startup when doing embedded.
<re_irc> <@sourcebox:matrix.org> And it's never freed.
Guest4814 has joined #rust-embedded
<re_irc> <@sourcebox:matrix.org> Shell: But I think there's some constant in the core library that is set to the target's alignment, so it would be a good practice to use that instead of hardcoding it.
<Shell> exactly
<re_irc> <@almindor:matrix.org> does anyone know what causes rust-analyzer to keel over with tons of errors such as
<re_irc> can't find crate for `std`
<re_irc> the `riscv32imac-unknown-none-elf` target may not support the standard library
<re_irc> ? This seems to come deep from dependency tree, in this case "byteorder" crate, but "cargo check" (and others) work fine for the project. The project is "!#[no_std]" of course.
<re_irc> <@almindor:matrix.org> nvm, solved. It seems including tinygif in my workspace caused this somehow
Guest4814 has left #rust-embedded [Error from remote client]
dc740 has quit [Remote host closed the connection]
dc740 has joined #rust-embedded
IlPalazzo-ojiisa has quit [Quit: Leaving.]
<re_irc> <@ryan-summers:matrix.org> Just recently saw https://github.com/hecatia-elegua/bilge on github discovery, looks very cool
<re_irc> <@ryan-summers:matrix.org> Looks to be pretty new too
IlPalazzo-ojiisa has joined #rust-embedded
<re_irc> <@2:0x2c.org> nice
Guest4814 has joined #rust-embedded
<re_irc> <@dngrs:matrix.org> : maybe because of its "dev-dependencies"
crabbedhaloablut has quit []
exark has quit [Quit: quit]
exark has joined #rust-embedded
<re_irc> <@gussius:matrix.org> I have been wondering lately, if there is a way, maybe with probe-rs or something where you can check that the board you are flashing to is the correct one. When you are developing on a multi-board system for instance, if you flash the wrong firmware to a power board, then you can blow FETs pretty quickly. Not sure how this would work, but it you could interrogate the program memory first before flashing the new firmware,...
<re_irc> ... then you could check some version variable or something.
<re_irc> <@jamesmunns:beeper.com> For chip specific support, I think there might be some kind of chip/manufacturer ID that is visible from SWD, though if it's not part of the SWD spec, then it's probably not portable.
<re_irc> For projects I've worked on in the past where you have the SAME IC but in different system parts, if you can spare a few GPIOs as "strapping lines", you can encode "board ID" by pulling those pins on every board high or low in a unique combination, or using resistors and an ADC to set a specific value
<re_irc> <@jamesmunns:beeper.com> so for that, it's possible to do something like:
<re_irc> - boot
<re_irc> - read 3 pins, if the value is NOT "my" special value, like "high low high", then just halt or go back to bootloader mode
<re_irc> <@jamesmunns:beeper.com> so on the "motor board" you could put "low low low", and on the "sensor board" you could do "low low high", and so on.
<re_irc> <@jamesmunns:beeper.com> I know for example the nrf family has registers you can read to figure out what chip it is, but I think that's specific to their family, and stm32 probably has something similar, but might be at a different location.
<re_irc> <@jamesmunns:beeper.com> but, you could probably write a small tool to "pre-check it" by using the probe-rs API to manually read those peripheral registers, and check the USB device before flashing, as a project specific tool.
<re_irc> <@grantm11235:matrix.org> It is common for a bootloader/firmware updater to check that the new firmware matches the current board, but that won't save you from flashing the wrong bootloader, and it won't save you if you are flashing the firmware directly during development
<re_irc> <@gussius:matrix.org> : Ok, thanks for the insight James.
dc740 has quit [Remote host closed the connection]
<re_irc> <@gussius:matrix.org> : Maybe you could run a validate with the old firmware before compiling and flashing the new firmware.
<re_irc> <@gussius:matrix.org> Is it possible to do just a validate, without flashing first?
dc740 has joined #rust-embedded
<re_irc> <@shakencodes:matrix.org> I'm trying to pass a *const c_char (a pointer to a C string) into a function that takes &[u8]... and am stuck at how to cast between the two. My Googling has not found anything that makes sense. The function in question checks for null termination (https://doc.rust-lang.org/stable/core/ffi/struct.CStr.html#method.from_bytes_with_nul).
<re_irc> The signature of the function receiving the C data is:
<re_irc> extern "C" fn myfunc(item: *const ::core::ffi::c_char)
<re_irc> <@jamesmunns:beeper.com> You could use https://doc.rust-lang.org/stable/core/ffi/struct.CStr.html#method.from_ptr ?
dc740 has quit [Remote host closed the connection]
<re_irc> <@shakencodes:matrix.org> That is what my code is using now as a workaround, but it does not check for null-termination. It looks like from_bytes_nul is the function you _should_ use, but the data types are not helpful. (We need from_ptr_nul()...)
<re_irc> <@jamesmunns:beeper.com> if you ONLY get a pointer, then there's really no _safer_ way to calculate len and/or test null termination
<re_irc> <@jamesmunns:beeper.com> from_ptr does calculate the len by running the ptr until it hits a nul
<re_irc> <@jamesmunns:beeper.com> IF you got a ptr+len, you could do "core::slice::from_raw_parts(ptr.cast::<u8>(), len)" to get a slice
<re_irc> <@jamesmunns:beeper.com> (in both cases you should test that "ptr" itself is not null)
<re_irc> <@shakencodes:matrix.org> Thank you, James. I think I see what you are saying. Would it make sense to combine the "from_raw_parts" with "from_byte_with_nul" and use an arbitrary "my string will never be more than NNN bytes as the length? It seems like this would ensure the thing doesn't run away in the case of an invalid string being passed in, and the case that is not null-terminated before NNN with give an error.
<re_irc> <@jamesmunns:beeper.com> Yeah, so that would be like subbing strnlen for strlen
<re_irc> <@jamesmunns:beeper.com> which I agree would be "harm reduction"
<re_irc> <@jamesmunns:beeper.com> though you could do the same, BUT tbh if you aren't getting a properly null terminated string passed in, you're PROBABLY walking across invalid memory, which is sorta already in the "oops UB" territory
<re_irc> <@jamesmunns:beeper.com> You could write your own version of https://doc.rust-lang.org/stable/src/core/ffi/c_str.rs.html#272-281 with an upper iteration count
kenrendell[m] has quit [Ping timeout: 245 seconds]
whitequark has quit [Ping timeout: 245 seconds]
<re_irc> <@shakencodes:matrix.org> Much appreciated, James. I feel like my skill level at Rust is making good progress and might be "passable" at the moment. It is really good having someone else to discuss this with. (I'm taking another engineer through the Exercism Rust track now, and they may join my team -- currently of one -- in the next few months. Which will be nice.)
<re_irc> <@jamesmunns:beeper.com> Very cool!