Skip to main content

Sx1302

Struct Sx1302 

Source
pub struct Sx1302<SPI, RESET, D> { /* private fields */ }
Expand description

A concentrator on an SPI device, with its reset line.

Implementations§

Source§

impl<SPI, RESET, D> Sx1302<SPI, RESET, D>

Source

pub const fn new(spi: SPI, reset: RESET, delay: D) -> Sx1302<SPI, RESET, D>

Wraps a concentrator’s SPI device and reset line. Nothing is sent until it is used.

§Arguments
  • spi - the SPI device the concentrator answers on.
  • reset - the line its reset pin is on, which the host drives.
  • delay - how the driver waits.
§Returns

The concentrator.

Source

pub fn release(self) -> (SPI, RESET, D)

Gives the SPI device and the lines back.

§Returns

What was handed over, so a caller can drive something else with them.

Source§

impl<SPI, RESET, D> Sx1302<SPI, RESET, D>
where SPI: SpiDevice, RESET: OutputPin, D: DelayNs,

Source

pub fn reset(&mut self) -> Result<(), ConcentratorError<SPI::Error>>

Pulses the reset line.

The concentrator does not reset itself. On a gateway board the line is a host pin, and this drives it the way the reference platform script does.

§Returns

Ok(()) once the chip has been released and given time to come up.

§Errors

Returns ConcentratorError::Pin if the line cannot be driven.

Source

pub fn version(&mut self) -> Result<u8, ConcentratorError<SPI::Error>>

Reads the version register.

§Returns

What it answered with, which is EXPECTED_VERSION on a concentrator that is powered and wired the right way round.

§Errors

Returns ConcentratorError::Spi if the transfer fails.

Source

pub fn check(&mut self) -> Result<(), ConcentratorError<SPI::Error>>

Checks that a concentrator is answering at all.

§Returns

Ok(()) when the version register reads what it should.

§Errors

Returns ConcentratorError::NotAnswering carrying what it read instead, which separates an unpowered board (zero) from a bus nothing is driving (all ones).

Source

pub fn identify(&mut self) -> Result<Model, ConcentratorError<SPI::Error>>

Reads the part number out of the one-time programmable memory.

§Returns

Which concentrator this is.

§Errors

Returns ConcentratorError::Spi if a transfer fails.

Source

pub fn reset_front_end( &mut self, chain: Chain, front_end: FrontEnd, ) -> Result<(), ConcentratorError<SPI::Error>>

Resets the front end on a chain, through the concentrator.

The concentrator is clocked from the host while it does this, so it is still listening on the bus while the radio it drives is held down.

§Arguments
  • chain - which chain’s front end to reset.
  • front_end - which front end it is, since the SX1250 calibrates itself afterwards.
§Returns

Ok(()) once it is out of reset and settled.

§Errors

Returns ConcentratorError::Spi if a transfer fails.

Source

pub fn load_firmware( &mut self, mcu: Mcu, image: &[u8], ) -> Result<(), ConcentratorError<SPI::Error>>

Gives a microcontroller its firmware, and checks that it took.

The image is written, read back, and compared before the microcontroller is released, and the chip is asked afterwards whether it accepted it. Both checks matter, and they fail differently: a read-back mismatch means the bus is not carrying the bytes, while a parity failure means they arrived and the image itself is wrong.

§Arguments
  • mcu - which microcontroller to load.
  • image - the firmware, which is FIRMWARE_LEN bytes.
§Returns

Ok(()) once it is loaded and the chip has accepted it.

§Errors

Returns ConcentratorError::Firmware for an image of the wrong size, a read-back that differs, or a parity check the chip refused.

Source

pub fn read_register( &mut self, register: Register, ) -> Result<u8, ConcentratorError<SPI::Error>>

Reads one register.

§Arguments
  • register - which register.
§Returns

Its value, taken out of the byte it shares with its neighbors.

§Errors

Returns ConcentratorError::Spi if the transfer fails.

Source

pub fn write_register( &mut self, register: Register, value: u8, ) -> Result<(), ConcentratorError<SPI::Error>>

Writes one register, leaving the rest of the byte it shares as it was.

§Arguments
  • register - which register.
  • value - what to put there.
§Returns

Ok(()) once written.

§Errors

Returns ConcentratorError::Spi if a transfer fails. A register narrower than a byte is read first, so this can cost two transfers.

Source

pub fn write_memory( &mut self, address: u16, data: &[u8], ) -> Result<(), ConcentratorError<SPI::Error>>

Writes a run of bytes into the chip’s memory.

§Arguments
  • address - where the first byte lands.
  • data - the bytes.
§Returns

Ok(()) once every chunk has gone out.

§Errors

Returns ConcentratorError::Spi if a transfer fails.

Source

pub fn read_memory( &mut self, address: u16, into: &mut [u8], ) -> Result<(), ConcentratorError<SPI::Error>>

Reads a run of bytes out of the chip’s memory.

§Arguments
  • address - where to start.
  • into - where to put them.
§Returns

Ok(()) once it is filled.

§Errors

Returns ConcentratorError::Spi if a transfer fails.

Source

pub fn read_fifo( &mut self, address: u16, into: &mut [u8], ) -> Result<(), ConcentratorError<SPI::Error>>

Reads a port rather than a window, so every chunk comes from the same address.

The receive buffer is a port: reading it moves the chip pointer along by itself. A reader that moves the address as well walks past the packets it came for, which only shows once there is more waiting than one chunk holds.

§Arguments
  • address - the port to read.
  • into - where the bytes go, filled to its length.
§Returns

Ok(()) once it is filled.

§Errors

Returns ConcentratorError::Spi if a transfer fails.

Source

pub fn waiting(&mut self) -> Result<u16, ConcentratorError<SPI::Error>>

How many bytes the receive buffer is holding.

The count is read twice and the larger kept. A read of the pair can answer with less than is really there, and the reference guards against it the same way.

§Returns

The number of bytes waiting, which is zero when nothing has been heard.

§Errors

Returns ConcentratorError::Spi if a transfer fails.

Source

pub fn receive<'a>( &mut self, into: &'a mut [u8], ) -> Result<&'a [u8], ConcentratorError<SPI::Error>>

Takes everything the receive buffer is holding.

What comes back is packets back to back, each wrapped in the metadata the chip writes around it, which packets walks.

§Arguments
  • into - where the bytes go. BUFFER_LEN always fits.
§Returns

What was read, which is empty when nothing has been heard.

§Errors

Returns ConcentratorError::Overrun if more is waiting than there is room for, having read nothing, so a larger buffer still gets it. Returns ConcentratorError::Spi if a transfer fails.

Source

pub fn transmit( &mut self, chain: Chain, transmit: &Transmit<'_>, trigger: Trigger, start_delay: u16, ) -> Result<(), ConcentratorError<SPI::Error>>

Loads a packet and arms the trigger that sends it.

The chip is not handed the packet and the moment together. The payload goes into the buffer of the chain, the delay it starts early by is programmed, and only then is the trigger armed. steps holds that order and this walks it.

§Arguments
  • chain - which transmit chain sends it.
  • transmit - the packet and everything the modulator has to be told about it. A chain given a payload without this sends it on whatever it was last set to.
  • trigger - what the send waits for.
  • start_delay - what start_delay worked out.
§Returns

Ok(()) once the trigger is armed. The packet leaves when the trigger says.

§Errors

Returns ConcentratorError::Spi if a transfer fails.

Source

pub fn send( &mut self, chain: Chain, payload: &[u8], trigger: Trigger, start_delay: u16, ) -> Result<(), ConcentratorError<SPI::Error>>

Loads a payload into a chain that is already configured, and arms its trigger.

transmit describes a packet and then calls this. A caller answering on settings the chain already holds can call it directly, which is every downlink after the first on one channel.

§Arguments
  • chain - which transmit chain sends it.
  • payload - the bytes the chain sends. For frequency shift keying the length byte goes first, as the chip reads it out of the buffer.
  • trigger - what the send waits for.
  • start_delay - what start_delay worked out.
§Returns

Ok(()) once the trigger is armed. The packet leaves when the trigger says.

§Errors

Returns ConcentratorError::Spi if a transfer fails.

Source

pub fn checked_transmission( &mut self, chain: Chain, ) -> Result<bool, ConcentratorError<SPI::Error>>

Waits for a transmission a carrier check guards to start, and reports whether the check let it go out.

Semtech’s lgw_lbt_tx_status: poll the gain control until it reports the chain started, read whether the check held it back, clear the status through the first mailbox, wait for it to read clear, and acknowledge. The status is cleared even when the start never came, so the next transmission is not read against this one.

§Arguments
  • chain - the chain the packet was armed on.
§Returns

true if the packet went out, false if the channel was busy.

§Errors

Returns ConcentratorError::Stalled if the transmission never started or its status never cleared, and ConcentratorError::Spi if a transfer fails.

Source

pub fn abort( &mut self, chain: Chain, ) -> Result<(), ConcentratorError<SPI::Error>>

Takes back a packet armed on a chain and waits for the chain to come free.

Semtech’s sx1302_tx_abort: every trigger reset, then the status polled for up to a second.

§Arguments
  • chain - the chain to free.
§Errors

Returns ConcentratorError::Stalled if the chain does not come free, and ConcentratorError::Spi if a transfer fails.

Source

pub fn tx_status( &mut self, chain: Chain, ) -> Result<TxStatus, ConcentratorError<SPI::Error>>

What a transmit chain is doing.

§Arguments
  • chain - the chain to ask.
§Returns

Its state, which is TxStatus::Free when it will take another packet.

§Errors

Returns ConcentratorError::Spi if a transfer fails.

Source

pub fn configure_channels( &mut self, plan: &Plan, ) -> Result<(), ConcentratorError<SPI::Error>>

Points a concentrator at the channels it is to listen on.

A concentrator that has been reset and given firmware still hears nothing until this has run. Its receivers have been given no frequencies, no radio to take samples from, no spreading factors to look for, and no permission to run at all. steps holds that order and this walks it.

§Arguments
  • plan - what to listen for.
§Returns

Ok(()) once the receivers are running.

§Errors

Returns ConcentratorError::Spi if a transfer fails.

Source

pub fn select_clock( &mut self, chain: Chain, ) -> Result<(), ConcentratorError<SPI::Error>>

Takes the concentrator clock from the front end on a chain.

Until this runs the chip is clocked by the bus, which is enough to answer registers and nothing else. The front end it takes the clock from has to be in receive, which is where setup_front_end leaves it.

§Arguments
  • chain - the chain whose front end carries the clock.
§Returns

Ok(()) once the chip is running on the radio clock.

§Errors

Returns ConcentratorError::Spi if a transfer fails.

Source

pub fn release_front_ends( &mut self, ) -> Result<(), ConcentratorError<SPI::Error>>

Hands the front ends back to the gain control.

The host drives them while they are being reset and tuned. From here on the gain control owns them, which is what lets it move gains while packets are arriving.

§Returns

Ok(()) once the host has let go.

§Errors

Returns ConcentratorError::Spi if a transfer fails.

Source

pub fn start_gain_control( &mut self, front_end: FrontEnd, listen_before_talk: bool, ) -> Result<(), ConcentratorError<SPI::Error>>

Configures the gain control microcontroller and lets it run.

Loading its firmware is only half of bringing it up: it comes out of reset waiting to be told what gains to use, one group of settings at a time, and a concentrator whose gain control was never configured hears very little. start_gain_control holds that exchange and this walks it against the mailboxes.

§Arguments
  • front_end - which front end the board carries, which decides the gains and the firmware version expected.
  • listen_before_talk - whether the SX1261 beside the concentrator checks a channel before the gateway talks on it.
§Returns

Ok(()) once the microcontroller has taken every group and been told there is no more to come.

§Errors

Returns ConcentratorError::WrongFirmware if it is running an image this crate does not drive, ConcentratorError::Stalled if it stops acknowledging, and ConcentratorError::Spi if a transfer fails.

Source

pub fn start_arbiter( &mut self, dual_demodulation: u8, ) -> Result<(), ConcentratorError<SPI::Error>>

Configures the arbiter microcontroller and lets it run.

The arbiter shares the receivers between the channels. It comes up halted and stays halted until it is told to resume.

§Arguments
  • dual_demodulation - which spreading factors are demodulated twice over, one bit each counting from SF5. Demodulating twice buys a finer timestamp and costs capacity, so a gateway that does not need one passes zero.
§Returns

Ok(()) once the arbiter reports it is running again.

§Errors

Returns ConcentratorError::WrongFirmware if it is running an image this crate does not drive, ConcentratorError::Stalled if it never resumes, and ConcentratorError::Spi if a transfer fails.

Source

pub fn counter( &mut self, counter: &mut Counter, ) -> Result<(u32, u32), ConcentratorError<SPI::Error>>

Reads the counter packets are stamped against.

Both counters come back in one burst, and the burst is read twice, because a read can catch a counter mid-step. When the two agree the first is kept; when a high byte has moved between them a third read settles it. That is what the reference does, and it is a different guard from the one on the receive byte count, where the larger of two readings is kept instead.

§Arguments
  • counter - the state carried between readings, which is what widens a counter of 27 bits past its rollover.
§Returns

The free running counter and then the pulse one, in microseconds.

§Errors

Returns ConcentratorError::Spi if a transfer fails.

Source

pub fn radio_command( &mut self, chain: Chain, opcode: u8, data: &[u8], ) -> Result<(), ConcentratorError<SPI::Error>>

Sends a command to the front end on a chain.

A front end answers on the same bus as the concentrator, with the first byte of the frame naming it rather than the concentrator. It gives the host no line to watch, so this waits WAIT_BUSY_US first, as the reference does.

§Arguments
  • chain - the chain the front end is on.
  • opcode - the command.
  • data - what the command carries.
§Returns

Ok(()) once it has been sent.

§Errors

Returns ConcentratorError::Spi if the transfer fails.

Source

pub fn radio_query( &mut self, chain: Chain, opcode: u8, into: &mut [u8], ) -> Result<(), ConcentratorError<SPI::Error>>

Asks the front end on a chain for an answer.

§Arguments
  • chain - the chain the front end is on.
  • opcode - the command.
  • into - where the answer goes, filled to its length.
§Returns

Ok(()) once it is filled.

§Errors

Returns ConcentratorError::Spi if the transfer fails.

Source

pub fn setup_front_end( &mut self, chain: Chain, hertz: u32, single_input: bool, ) -> Result<(), ConcentratorError<SPI::Error>>

Brings the front end on a chain up and leaves it listening.

A concentrator hears nothing until this has run, because the carrier is set here rather than on the concentrator. It also matters beyond receiving: the concentrator takes its clock from a front end that is in receive, so one left idle stops the chip beside it. setup holds the order and this walks it.

§Arguments
  • chain - the chain the front end is on.
  • hertz - the carrier to tune to.
  • single_input - whether the board wires the front end single ended rather than differential.
§Returns

Ok(()) once it is tuned and listening.

§Errors

Returns ConcentratorError::FrontEndMode if it does not settle into a standby it was put in, which is what an unpowered or unclocked front end looks like, and ConcentratorError::Spi if a transfer fails.

Source

pub fn calibrate_front_end( &mut self, chain: Chain, hertz: u32, ) -> Result<(), ConcentratorError<SPI::Error>>

Calibrates the front end on a chain over the band a carrier falls in.

The chip calibrates a band rather than a frequency, and it knows five of them. This is the SX1250 half of radio calibration: it runs once the front ends have been reset and while the host still drives them, before the receivers are given their channels. The front ends that came before calibrate through a firmware image instead.

§Arguments
  • chain - the chain the front end is on.
  • hertz - the carrier it will be tuned to.
§Returns

Ok(()) once the chip reports no fault.

§Errors

Returns ConcentratorError::UnsupportedBand for a carrier outside every band, without sending anything, ConcentratorError::ImageCalibration if the chip reports the calibration failed, and ConcentratorError::Spi if a transfer fails.

Auto Trait Implementations§

§

impl<SPI, RESET, D> Freeze for Sx1302<SPI, RESET, D>
where SPI: Freeze, RESET: Freeze, D: Freeze,

§

impl<SPI, RESET, D> RefUnwindSafe for Sx1302<SPI, RESET, D>
where SPI: RefUnwindSafe, RESET: RefUnwindSafe, D: RefUnwindSafe,

§

impl<SPI, RESET, D> Send for Sx1302<SPI, RESET, D>
where SPI: Send, RESET: Send, D: Send,

§

impl<SPI, RESET, D> Sync for Sx1302<SPI, RESET, D>
where SPI: Sync, RESET: Sync, D: Sync,

§

impl<SPI, RESET, D> Unpin for Sx1302<SPI, RESET, D>
where SPI: Unpin, RESET: Unpin, D: Unpin,

§

impl<SPI, RESET, D> UnsafeUnpin for Sx1302<SPI, RESET, D>
where SPI: UnsafeUnpin, RESET: UnsafeUnpin, D: UnsafeUnpin,

§

impl<SPI, RESET, D> UnwindSafe for Sx1302<SPI, RESET, D>
where SPI: UnwindSafe, RESET: UnwindSafe, D: UnwindSafe,

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.