#![no_std]
#![deny(warnings, missing_docs)]
use embedded_hal::{
blocking::spi::Transfer,
digital::v2::OutputPin,
};
use bit_field::BitField;
use core::ops::RangeInclusive;
const THERMOCOUPLE_BITS: RangeInclusive<usize> = 2..=15;
const FAULT_BIT: usize = 0;
const INTERNAL_BITS: RangeInclusive<usize> = 4..=15;
const FAULT_VCC_SHORT_BIT: usize = 2;
const FAULT_GROUND_SHORT_BIT: usize = 1;
const FAULT_NO_THERMOCOUPLE_BIT: usize = 0;
#[derive(Debug)]
pub enum Error<SpiE, CsE> {
SpiError(SpiE),
ChipSelectError(CsE),
Fault,
VccShortFault,
GroundShortFault,
MissingThermocoupleFault,
}
#[derive(Clone, Copy, Debug)]
pub enum Unit {
Celsius,
Fahrenheit,
Kelvin,
}
impl Unit {
pub fn convert(&self, celsius: f32) -> f32 {
match self {
Unit::Celsius => celsius,
Unit::Fahrenheit => celsius * 1.8 + 32.,
Unit::Kelvin => celsius + 273.15,
}
}
}
pub enum Reading {
Thermocouple,
Internal,
}
impl Reading {
pub fn convert(self, count: i16) -> f32 {
let count = count as f32;
match self {
Reading::Thermocouple => count * 0.25,
Reading::Internal => count * 0.0625,
}
}
}
enum CsState {
High,
Low,
}
use CsState::*;
fn set_cs<CS, SpiE, CsE>(cs: &mut CS, state: CsState) -> Result<(), Error<SpiE, CsE>>
where
CS: OutputPin<Error = CsE>,
{
let result = match state {
CsState::High => cs.set_high(),
CsState::Low => cs.set_low(),
};
result.map_err(|e| Error::ChipSelectError(e))
}
fn transfer<CS, SPI, SpiE, CsE>(spi: &mut SPI, chip_select: &mut CS, buffer: &mut [u8]) -> Result<(), Error<SpiE, CsE>>
where
CS: OutputPin<Error = CsE>,
SPI: Transfer<u8, Error = SpiE>,
{
set_cs(chip_select, Low)?;
spi.transfer(buffer).map_err(|e| Error::SpiError(e))?;
set_cs(chip_select, High)
}
fn bits_to_i16(bits: u16, len: usize, divisor: i16, shift: usize) -> i16 {
let negative = bits.get_bit(len - 1);
if negative {
(bits << shift) as i16 / divisor
} else {
bits as i16
}
}
#[derive(Debug)]
pub struct FullResultRaw {
pub thermocouple: i16,
pub internal: i16,
}
impl FullResultRaw {
pub fn convert(self, unit: Unit) -> FullResult {
let thermocouple = unit.convert(Reading::Thermocouple.convert(self.thermocouple));
let internal = unit.convert(Reading::Internal.convert(self.internal));
FullResult {
thermocouple,
internal,
unit,
}
}
}
#[derive(Debug)]
pub struct FullResult {
pub thermocouple: f32,
pub internal: f32,
pub unit: Unit,
}
pub trait Max31855<SpiE, CsE, CS> {
fn read_thermocouple_raw(&mut self, chip_select: &mut CS) -> Result<i16, Error<SpiE, CsE>>;
fn read_thermocouple(&mut self, chip_select: &mut CS, unit: Unit) -> Result<f32, Error<SpiE, CsE>>;
fn read_all_raw(&mut self, chip_select: &mut CS) -> Result<FullResultRaw, Error<SpiE, CsE>>;
fn read_all(&mut self, chip_select: &mut CS, unit: Unit) -> Result<FullResult, Error<SpiE, CsE>>;
}
impl<CS, SPI, SpiE, CsE> Max31855<SpiE, CsE, CS> for SPI
where
CS: OutputPin<Error = CsE>,
SPI: Transfer<u8, Error = SpiE>,
{
fn read_thermocouple_raw(&mut self, chip_select: &mut CS) -> Result<i16, Error<SpiE, CsE>> {
let mut buffer = [0; 2];
transfer(self, chip_select, &mut buffer)?;
if buffer[1].get_bit(FAULT_BIT) {
Err(Error::Fault)?
}
let raw = (buffer[0] as u16) << 8 |
(buffer[1] as u16) << 0;
let thermocouple = bits_to_i16(raw.get_bits(THERMOCOUPLE_BITS), 14, 4, 2);
Ok(thermocouple)
}
fn read_thermocouple(&mut self, chip_select: &mut CS, unit: Unit) -> Result<f32, Error<SpiE, CsE>> {
self
.read_thermocouple_raw(chip_select)
.map(|r| unit.convert(Reading::Thermocouple.convert(r)))
}
fn read_all_raw(&mut self, chip_select: &mut CS) -> Result<FullResultRaw, Error<SpiE, CsE>> {
let mut buffer = [0; 4];
transfer(self, chip_select, &mut buffer)?;
let fault = buffer[1].get_bit(0);
if fault {
let raw = (buffer[2] as u16) << 8 |
(buffer[3] as u16) << 0;
if raw.get_bit(FAULT_NO_THERMOCOUPLE_BIT) {
Err(Error::MissingThermocoupleFault)?
} else if raw.get_bit(FAULT_GROUND_SHORT_BIT) {
Err(Error::GroundShortFault)?
} else if raw.get_bit(FAULT_VCC_SHORT_BIT) {
Err(Error::VccShortFault)?
} else {
Err(Error::Fault)?
}
}
let first_u16 = (buffer[0] as u16) << 8 |
(buffer[1] as u16) << 0;
let second_u16 = (buffer[2] as u16) << 8 |
(buffer[3] as u16) << 0;
let thermocouple = bits_to_i16(first_u16.get_bits(THERMOCOUPLE_BITS), 14, 4, 2);
let internal = bits_to_i16(second_u16.get_bits(INTERNAL_BITS), 12, 16, 4);
Ok(FullResultRaw {
thermocouple,
internal,
})
}
fn read_all(&mut self, chip_select: &mut CS, unit: Unit) -> Result<FullResult, Error<SpiE, CsE>> {
self
.read_all_raw(chip_select)
.map(|r| r.convert(unit))
}
}