Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ repository = "https://github.com/dsvensson/cc1101"
edition = "2021"

[dependencies]
embedded-hal = "0.2.3"
embedded-hal = "1.0.0"

[features]
std = []
82 changes: 34 additions & 48 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@ extern crate embedded_hal as hal;
extern crate std;

use core::fmt::{self, Display, Formatter};
use hal::blocking::spi::{Transfer, Write};
use hal::digital::v2::OutputPin;
use hal::spi::SpiDevice;

#[macro_use]
pub mod lowlevel;
Expand All @@ -20,57 +19,47 @@ use rssi::rssi_to_dbm;

/// CC1101 errors.
#[derive(Debug)]
pub enum Error<SpiE, GpioE> {
pub enum Error<SpiE> {
/// The RX FIFO buffer overflowed, too small buffer for configured packet length.
RxOverflow,
/// Corrupt packet received with invalid CRC.
CrcMismatch,
/// Platform-dependent SPI-errors, such as IO errors.
Spi(SpiE),
/// Platform-dependent GPIO-errors, such as IO errors.
Gpio(GpioE),
}

impl<SpiE, GpioE> From<lowlevel::Error<SpiE, GpioE>> for Error<SpiE, GpioE> {
fn from(e: lowlevel::Error<SpiE, GpioE>) -> Self {
match e {
lowlevel::Error::Spi(inner) => Error::Spi(inner),
lowlevel::Error::Gpio(inner) => Error::Gpio(inner),
}
impl<SpiE> From<SpiE> for Error<SpiE> {
fn from(e: SpiE) -> Self {
Error::Spi(e)
}
}

impl<SpiE: Display, GpioE: Display> Display for Error<SpiE, GpioE> {
impl<SpiE: Display> Display for Error<SpiE> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
Self::RxOverflow => write!(f, "RX FIFO buffer overflowed"),
Self::CrcMismatch => write!(f, "CRC mismatch"),
Self::Spi(e) => write!(f, "SPI error: {}", e),
Self::Gpio(e) => write!(f, "GPIO error: {}", e),
}
}
}

#[cfg(feature = "std")]
impl<SpiE: Display + core::fmt::Debug, GpioE: Display + core::fmt::Debug> std::error::Error
for Error<SpiE, GpioE>
{
}
impl<SpiE: Display + core::fmt::Debug> std::error::Error for Error<SpiE> {}

/// High level API for interacting with the CC1101 radio chip.
pub struct Cc1101<SPI, CS>(lowlevel::Cc1101<SPI, CS>);
pub struct Cc1101<SPI>(lowlevel::Cc1101<SPI>);

impl<SPI, CS, SpiE, GpioE> Cc1101<SPI, CS>
impl<SPI, SpiE> Cc1101<SPI>
where
SPI: Transfer<u8, Error = SpiE> + Write<u8, Error = SpiE>,
CS: OutputPin<Error = GpioE>,
SPI: SpiDevice<u8, Error = SpiE>,
{
pub fn new(spi: SPI, cs: CS) -> Result<Self, Error<SpiE, GpioE>> {
Ok(Cc1101(lowlevel::Cc1101::new(spi, cs)?))
pub fn new(spi: SPI) -> Result<Self, Error<SpiE>> {
Ok(Cc1101(lowlevel::Cc1101::new(spi)?))
}

/// Sets the carrier frequency (in Hertz).
pub fn set_frequency(&mut self, hz: u64) -> Result<(), Error<SpiE, GpioE>> {
pub fn set_frequency(&mut self, hz: u64) -> Result<(), Error<SpiE>> {
let (freq0, freq1, freq2) = from_frequency(hz);
self.0.write_register(Config::FREQ0, freq0)?;
self.0.write_register(Config::FREQ1, freq1)?;
Expand All @@ -79,14 +68,14 @@ where
}

/// Sets the frequency synthesizer intermediate frequency (in Hertz).
pub fn set_synthesizer_if(&mut self, hz: u64) -> Result<(), Error<SpiE, GpioE>> {
pub fn set_synthesizer_if(&mut self, hz: u64) -> Result<(), Error<SpiE>> {
self.0
.write_register(Config::FSCTRL1, FSCTRL1::default().freq_if(from_freq_if(hz)).bits())?;
Ok(())
}

/// Sets the target value for the averaged amplitude from the digital channel filter.
pub fn set_agc_target(&mut self, target: TargetAmplitude) -> Result<(), Error<SpiE, GpioE>> {
pub fn set_agc_target(&mut self, target: TargetAmplitude) -> Result<(), Error<SpiE>> {
self.0.modify_register(Config::AGCCTRL2, |r| {
AGCCTRL2(r).modify().magn_target(target.into()).bits()
})?;
Expand All @@ -97,25 +86,22 @@ where
pub fn set_agc_filter_length(
&mut self,
filter_length: FilterLength,
) -> Result<(), Error<SpiE, GpioE>> {
) -> Result<(), Error<SpiE>> {
self.0.modify_register(Config::AGCCTRL0, |r| {
AGCCTRL0(r).modify().filter_length(filter_length.into()).bits()
})?;
Ok(())
}

/// Configures when to run automatic calibration.
pub fn set_autocalibration(
&mut self,
autocal: AutoCalibration,
) -> Result<(), Error<SpiE, GpioE>> {
pub fn set_autocalibration(&mut self, autocal: AutoCalibration) -> Result<(), Error<SpiE>> {
self.0.modify_register(Config::MCSM0, |r| {
MCSM0(r).modify().fs_autocal(autocal.into()).bits()
})?;
Ok(())
}

pub fn set_deviation(&mut self, deviation: u64) -> Result<(), Error<SpiE, GpioE>> {
pub fn set_deviation(&mut self, deviation: u64) -> Result<(), Error<SpiE>> {
let (mantissa, exponent) = from_deviation(deviation);
self.0.write_register(
Config::DEVIATN,
Expand All @@ -125,7 +111,7 @@ where
}

/// Sets the data rate (in bits per second).
pub fn set_data_rate(&mut self, baud: u64) -> Result<(), Error<SpiE, GpioE>> {
pub fn set_data_rate(&mut self, baud: u64) -> Result<(), Error<SpiE>> {
let (mantissa, exponent) = from_drate(baud);
self.0
.modify_register(Config::MDMCFG4, |r| MDMCFG4(r).modify().drate_e(exponent).bits())?;
Expand All @@ -134,33 +120,33 @@ where
}

/// Sets the channel bandwidth (in Hertz).
pub fn set_chanbw(&mut self, bandwidth: u64) -> Result<(), Error<SpiE, GpioE>> {
pub fn set_chanbw(&mut self, bandwidth: u64) -> Result<(), Error<SpiE>> {
let (mantissa, exponent) = from_chanbw(bandwidth);
self.0.modify_register(Config::MDMCFG4, |r| {
MDMCFG4(r).modify().chanbw_m(mantissa).chanbw_e(exponent).bits()
})?;
Ok(())
}

pub fn get_hw_info(&mut self) -> Result<(u8, u8), Error<SpiE, GpioE>> {
pub fn get_hw_info(&mut self) -> Result<(u8, u8), Error<SpiE>> {
let partnum = self.0.read_register(Status::PARTNUM)?;
let version = self.0.read_register(Status::VERSION)?;
Ok((partnum, version))
}

/// Received Signal Strength Indicator is an estimate of the signal power level in the chosen channel.
pub fn get_rssi_dbm(&mut self) -> Result<i16, Error<SpiE, GpioE>> {
pub fn get_rssi_dbm(&mut self) -> Result<i16, Error<SpiE>> {
Ok(rssi_to_dbm(self.0.read_register(Status::RSSI)?))
}

/// The Link Quality Indicator metric of the current quality of the received signal.
pub fn get_lqi(&mut self) -> Result<u8, Error<SpiE, GpioE>> {
pub fn get_lqi(&mut self) -> Result<u8, Error<SpiE>> {
let lqi = self.0.read_register(Status::LQI)?;
Ok(lqi & !(1u8 << 7))
}

/// Configure the sync word to use, and at what level it should be verified.
pub fn set_sync_mode(&mut self, sync_mode: SyncMode) -> Result<(), Error<SpiE, GpioE>> {
pub fn set_sync_mode(&mut self, sync_mode: SyncMode) -> Result<(), Error<SpiE>> {
let reset: u16 = (SYNC1::default().bits() as u16) << 8 | (SYNC0::default().bits() as u16);

let (mode, word) = match sync_mode {
Expand All @@ -178,7 +164,7 @@ where
}

/// Configure signal modulation.
pub fn set_modulation(&mut self, format: Modulation) -> Result<(), Error<SpiE, GpioE>> {
pub fn set_modulation(&mut self, format: Modulation) -> Result<(), Error<SpiE>> {
use lowlevel::types::ModFormat as MF;

let value = match format {
Expand All @@ -195,7 +181,7 @@ where
}

/// Configure device address, and address filtering.
pub fn set_address_filter(&mut self, filter: AddressFilter) -> Result<(), Error<SpiE, GpioE>> {
pub fn set_address_filter(&mut self, filter: AddressFilter) -> Result<(), Error<SpiE>> {
use lowlevel::types::AddressCheck as AC;

let (mode, addr) = match filter {
Expand All @@ -212,7 +198,7 @@ where
}

/// Configure packet mode, and length.
pub fn set_packet_length(&mut self, length: PacketLength) -> Result<(), Error<SpiE, GpioE>> {
pub fn set_packet_length(&mut self, length: PacketLength) -> Result<(), Error<SpiE>> {
use lowlevel::types::LengthConfig as LC;

let (format, pktlen) = match length {
Expand All @@ -228,7 +214,7 @@ where
}

/// Set radio in Receive/Transmit/Idle/Calibrate mode.
pub fn set_radio_mode(&mut self, radio_mode: RadioMode) -> Result<(), Error<SpiE, GpioE>> {
pub fn set_radio_mode(&mut self, radio_mode: RadioMode) -> Result<(), Error<SpiE>> {
let target = match radio_mode {
RadioMode::Receive => {
self.set_radio_mode(RadioMode::Idle)?;
Expand All @@ -254,14 +240,14 @@ where
}

/// Resets the chip.
pub fn reset(&mut self) -> Result<(), Error<SpiE, GpioE>> {
pub fn reset(&mut self) -> Result<(), Error<SpiE>> {
self.0.write_strobe(Command::SRES)?;
Ok(())
}

/// Configure some default settings, to be removed in the future.
#[rustfmt::skip]
pub fn set_defaults(&mut self) -> Result<(), Error<SpiE, GpioE>> {
pub fn set_defaults(&mut self) -> Result<(), Error<SpiE, >> {
self.0.write_strobe(Command::SRES)?;

self.0.write_register(Config::PKTCTRL0, PKTCTRL0::default()
Expand All @@ -283,7 +269,7 @@ where
Ok(())
}

fn await_machine_state(&mut self, target: MachineState) -> Result<(), Error<SpiE, GpioE>> {
fn await_machine_state(&mut self, target: MachineState) -> Result<(), Error<SpiE>> {
loop {
let marcstate = MARCSTATE(self.0.read_register(Status::MARCSTATE)?);
if target.value() == marcstate.marc_state() {
Expand All @@ -293,7 +279,7 @@ where
Ok(())
}

fn rx_bytes_available(&mut self) -> Result<u8, Error<SpiE, GpioE>> {
fn rx_bytes_available(&mut self) -> Result<u8, Error<SpiE>> {
let mut last = 0;

loop {
Expand All @@ -315,7 +301,7 @@ where
// Should also be able to configure MCSM1.RXOFF_MODE to declare what state
// to enter after fully receiving a packet.
// Possible targets: IDLE, FSTON, TX, RX
pub fn receive(&mut self, addr: &mut u8, buf: &mut [u8]) -> Result<u8, Error<SpiE, GpioE>> {
pub fn receive(&mut self, addr: &mut u8, buf: &mut [u8]) -> Result<u8, Error<SpiE>> {
match self.rx_bytes_available() {
Ok(_nbytes) => {
let mut length = 0u8;
Expand All @@ -337,7 +323,7 @@ where
}

/// Configures raw data to be passed through, without any packet handling.
pub fn set_raw_mode(&mut self) -> Result<(), Error<SpiE, GpioE>> {
pub fn set_raw_mode(&mut self) -> Result<(), Error<SpiE>> {
// Serial data output.
self.0.write_register(Config::IOCFG0, 0x0d)?;
// Disable data whitening and CRC, fixed packet length, asynchronous serial mode.
Expand Down
71 changes: 17 additions & 54 deletions src/lowlevel.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
//! Low level unrestricted access to the CC1101 radio chip.

use core::fmt::{self, Display, Formatter};
use hal::blocking::spi::{Transfer, Write};
use hal::digital::v2::OutputPin;
use hal::spi::{Operation, SpiDevice};

#[macro_use]
mod macros;
Expand All @@ -17,95 +15,60 @@ use self::registers::*;

pub const FXOSC: u64 = 26_000_000;

pub struct Cc1101<SPI, CS> {
pub struct Cc1101<SPI> {
pub(crate) spi: SPI,
pub(crate) cs: CS,
// gdo0: GDO0,
// gdo2: GDO2,
}

#[derive(Debug)]
pub enum Error<SpiE, GpioE> {
Spi(SpiE),
Gpio(GpioE),
}

impl<SpiE: Display, GpioE: Display> Display for Error<SpiE, GpioE> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
Self::Spi(e) => write!(f, "SPI error: {}", e),
Self::Gpio(e) => write!(f, "GPIO error: {}", e),
}
}
}

#[cfg(feature = "std")]
impl<SpiE: Display + core::fmt::Debug, GpioE: Display + core::fmt::Debug> std::error::Error
for Error<SpiE, GpioE>
{
}

impl<SPI, CS, SpiE, GpioE> Cc1101<SPI, CS>
impl<SPI, SpiE> Cc1101<SPI>
where
SPI: Transfer<u8, Error = SpiE> + Write<u8, Error = SpiE>,
CS: OutputPin<Error = GpioE>,
SPI: SpiDevice<u8, Error = SpiE>,
{
pub fn new(spi: SPI, cs: CS) -> Result<Self, Error<SpiE, GpioE>> {
pub fn new(spi: SPI) -> Result<Self, SpiE> {
let cc1101 = Cc1101 {
spi,
cs,
};
Ok(cc1101)
}

pub fn read_register<R>(&mut self, reg: R) -> Result<u8, Error<SpiE, GpioE>>
pub fn read_register<R>(&mut self, reg: R) -> Result<u8, SpiE>
where
R: Into<Register>,
{
self.cs.set_low().map_err(Error::Gpio)?;
let mut buffer = [reg.into().raddr(), 0u8];
self.spi.transfer(&mut buffer).map_err(Error::Spi)?;
self.cs.set_high().map_err(Error::Gpio)?;
self.spi.transfer_in_place(&mut buffer)?;
Ok(buffer[1])
}

pub fn read_fifo(
&mut self,
addr: &mut u8,
len: &mut u8,
buf: &mut [u8],
) -> Result<(), Error<SpiE, GpioE>> {
pub fn read_fifo(&mut self, addr: &mut u8, len: &mut u8, buf: &mut [u8]) -> Result<(), SpiE> {
let mut buffer = [Command::FIFO.addr() | 0xC0, 0, 0];

self.cs.set_low().map_err(Error::Gpio)?;
self.spi.transfer(&mut buffer).map_err(Error::Spi)?;
self.spi.transfer(buf).map_err(Error::Spi)?;
self.cs.set_high().map_err(Error::Gpio)?;
self.spi.transaction(&mut [
Operation::TransferInPlace(&mut buffer),
Operation::TransferInPlace(buf),
])?;

*len = buffer[1];
*addr = buffer[2];

Ok(())
}

pub fn write_strobe(&mut self, com: Command) -> Result<(), Error<SpiE, GpioE>> {
self.cs.set_low().map_err(Error::Gpio)?;
self.spi.write(&[com.addr()]).map_err(Error::Spi)?;
self.cs.set_high().map_err(Error::Gpio)?;
pub fn write_strobe(&mut self, com: Command) -> Result<(), SpiE> {
self.spi.write(&[com.addr()])?;
Ok(())
}

pub fn write_register<R>(&mut self, reg: R, byte: u8) -> Result<(), Error<SpiE, GpioE>>
pub fn write_register<R>(&mut self, reg: R, byte: u8) -> Result<(), SpiE>
where
R: Into<Register>,
{
self.cs.set_low().map_err(Error::Gpio)?;
self.spi.write(&[reg.into().waddr(), byte]).map_err(Error::Spi)?;
self.cs.set_high().map_err(Error::Gpio)?;
self.spi.write(&[reg.into().waddr(), byte])?;
Ok(())
}

pub fn modify_register<R, F>(&mut self, reg: R, f: F) -> Result<(), Error<SpiE, GpioE>>
pub fn modify_register<R, F>(&mut self, reg: R, f: F) -> Result<(), SpiE>
where
R: Into<Register> + Copy,
F: FnOnce(u8) -> u8,
Expand Down