initial commit
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/target
|
||||
16
Cargo.lock
generated
Normal file
16
Cargo.lock
generated
Normal file
@@ -0,0 +1,16 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "as7265x-rust"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"embedded-hal",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "embedded-hal"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "361a90feb7004eca4019fb28352a9465666b24f840f5c3cddf0ff13920590b89"
|
||||
7
Cargo.toml
Normal file
7
Cargo.toml
Normal file
@@ -0,0 +1,7 @@
|
||||
[package]
|
||||
name = "as7265x-rust"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
embedded-hal = "1.0.0"
|
||||
597
src/lib.rs
Normal file
597
src/lib.rs
Normal file
@@ -0,0 +1,597 @@
|
||||
#![no_std]
|
||||
|
||||
use embedded_hal::{
|
||||
delay::DelayNs,
|
||||
i2c::{self},
|
||||
};
|
||||
|
||||
const AS7265X_ADDR: u8 = 0x49;
|
||||
const STATUS_REG: u8 = 0x00;
|
||||
const WRITE_REG: u8 = 0x01;
|
||||
const READ_REG: u8 = 0x02;
|
||||
|
||||
const TX_VALID: u8 = 0x02;
|
||||
const RX_VALID: u8 = 0x01;
|
||||
|
||||
const HW_VERSION_HIGH_ADDR: u8 = 0x00;
|
||||
const HW_VERSION_LOW_ADDR: u8 = 0x01;
|
||||
const FW_VERSION_HIGH_ADDR: u8 = 0x02;
|
||||
const FW_VERSION_LOW_ADDR: u8 = 0x03;
|
||||
const CONFIG_ADDR: u8 = 0x04;
|
||||
const INTEGRATION_ADDR: u8 = 0x05;
|
||||
const TEMP_ADDR: u8 = 0x06;
|
||||
const LED_ADDR: u8 = 0x07;
|
||||
const DEV_SELECT_ADDR: u8 = 0x4F;
|
||||
|
||||
const SOFT_RESET: u8 = 0b10000000;
|
||||
const ENABLE_INTERRUPT: u8 = 0b01000000;
|
||||
|
||||
const CONFIG_GAIN_MASK: u8 = 0x30;
|
||||
const CONFIG_GAIN_SHIFT: u8 = 4;
|
||||
const CONFIG_BANK_MASK: u8 = 0x0C;
|
||||
const CONFIG_BANK_SHIFT: u8 = 2;
|
||||
const CONFIG_DATA_RDY: u8 = 0x02;
|
||||
|
||||
const LED_DRV_ICL_MASK: u8 = 0x30;
|
||||
const LED_DRV_ICL_SHIFT: u8 = 4;
|
||||
const LED_DRV_ENABLE: u8 = 0x08;
|
||||
const LED_IND_ICL_MASK: u8 = 0x06;
|
||||
const LED_IND_ICL_SHIFT: u8 = 1;
|
||||
const LED_IND_ENABLE: u8 = 0x01;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum Channel {
|
||||
/// 410nm - AS72653
|
||||
A,
|
||||
/// 435nm - AS72653
|
||||
B,
|
||||
/// 460nm - AS72653
|
||||
C,
|
||||
/// 485nm - AS72653
|
||||
D,
|
||||
/// 510nm - AS72653
|
||||
E,
|
||||
/// 535nm - AS72653
|
||||
F,
|
||||
/// 560nm - AS72652
|
||||
G,
|
||||
/// 585nm - AS72652
|
||||
H,
|
||||
/// 610nm - AS72651
|
||||
R,
|
||||
/// 645nm - AS72652
|
||||
I,
|
||||
/// 680nm - AS72651
|
||||
S,
|
||||
/// 705nm - AS72652
|
||||
J,
|
||||
/// 730nm - AS72651
|
||||
T,
|
||||
/// 760nm - AS72651
|
||||
U,
|
||||
/// 810nm - AS72651
|
||||
V,
|
||||
/// 860nm - AS72651
|
||||
W,
|
||||
/// 900nm - AS72652
|
||||
K,
|
||||
/// 940nm - AS72652
|
||||
L,
|
||||
}
|
||||
|
||||
impl Channel {
|
||||
pub fn get_device_for_chanel(&self) -> Device {
|
||||
match self {
|
||||
Channel::A | Channel::B | Channel::C | Channel::D | Channel::E | Channel::F => {
|
||||
Device::Uv
|
||||
}
|
||||
Channel::G | Channel::H | Channel::I | Channel::J | Channel::K | Channel::L => {
|
||||
Device::Visable
|
||||
}
|
||||
Channel::R | Channel::S | Channel::T | Channel::U | Channel::V | Channel::W => {
|
||||
Device::Nir
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_raw_address(&self) -> u8 {
|
||||
match self {
|
||||
Channel::A | Channel::G | Channel::R => 0x08,
|
||||
Channel::B | Channel::H | Channel::S => 0x0A,
|
||||
Channel::C | Channel::I | Channel::T => 0x0C,
|
||||
Channel::D | Channel::J | Channel::U => 0x0E,
|
||||
Channel::E | Channel::K | Channel::V => 0x10,
|
||||
Channel::F | Channel::L | Channel::W => 0x12,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_cal_address(&self) -> u8 {
|
||||
let raw = self.get_raw_address();
|
||||
((raw - 0x08) / 2) * 4 + 0x14
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum Device {
|
||||
Visable = 0x00,
|
||||
Nir = 0x01,
|
||||
Uv = 0x02,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum Led {
|
||||
White,
|
||||
Ir,
|
||||
Uv,
|
||||
}
|
||||
|
||||
impl Led {
|
||||
fn get_device(&self) -> Device {
|
||||
match self {
|
||||
Led::White => Device::Nir,
|
||||
Led::Ir => Device::Visable,
|
||||
Led::Uv => Device::Uv,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum GainConfig {
|
||||
/// x1
|
||||
Gain1x,
|
||||
|
||||
/// x3.7
|
||||
Gain3_7x,
|
||||
|
||||
/// x16
|
||||
Gain16x,
|
||||
|
||||
/// x64
|
||||
Gain64x,
|
||||
}
|
||||
|
||||
impl GainConfig {
|
||||
fn to_bits(self) -> u8 {
|
||||
match self {
|
||||
GainConfig::Gain1x => 0b00,
|
||||
GainConfig::Gain3_7x => 0b01,
|
||||
GainConfig::Gain16x => 0b10,
|
||||
GainConfig::Gain64x => 0b11,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Measurement mode options
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum MeasurementMode {
|
||||
/// 4 channels
|
||||
Mode0,
|
||||
|
||||
/// 4 channels
|
||||
Mode1,
|
||||
|
||||
/// All 6 channels
|
||||
Mode2Continuous,
|
||||
|
||||
/// One-Shot operation of mode 2
|
||||
Mode3OneShot,
|
||||
}
|
||||
|
||||
impl MeasurementMode {
|
||||
fn to_bits(self) -> u8 {
|
||||
match self {
|
||||
MeasurementMode::Mode0 => 0b00,
|
||||
MeasurementMode::Mode1 => 0b01,
|
||||
MeasurementMode::Mode2Continuous => 0b10,
|
||||
MeasurementMode::Mode3OneShot => 0b11,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct AS7265XConfig {
|
||||
pub enable_interrupt: bool,
|
||||
pub gain: GainConfig,
|
||||
pub measurement_mode: MeasurementMode,
|
||||
pub integration_time: u8,
|
||||
}
|
||||
|
||||
impl Default for AS7265XConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
enable_interrupt: false,
|
||||
gain: GainConfig::Gain64x,
|
||||
measurement_mode: MeasurementMode::Mode3OneShot,
|
||||
integration_time: 20,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AS7265XConfig {
|
||||
fn to_config_byte(&self) -> u8 {
|
||||
let mut byte = 0u8;
|
||||
|
||||
if self.enable_interrupt {
|
||||
byte |= ENABLE_INTERRUPT;
|
||||
}
|
||||
|
||||
byte |= (self.gain.to_bits() << CONFIG_GAIN_SHIFT) & CONFIG_GAIN_MASK;
|
||||
byte |= (self.measurement_mode.to_bits() << CONFIG_BANK_SHIFT) & CONFIG_BANK_MASK;
|
||||
|
||||
byte
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum LedCurrent {
|
||||
/// 12.5 mA
|
||||
Ma12_5,
|
||||
|
||||
/// 25 mA
|
||||
Ma25,
|
||||
|
||||
/// 50 mA
|
||||
Ma50,
|
||||
|
||||
/// 100 mA
|
||||
Ma100,
|
||||
}
|
||||
|
||||
impl LedCurrent {
|
||||
fn to_bits(self) -> u8 {
|
||||
match self {
|
||||
LedCurrent::Ma12_5 => 0b00,
|
||||
LedCurrent::Ma25 => 0b01,
|
||||
LedCurrent::Ma50 => 0b10,
|
||||
LedCurrent::Ma100 => 0b11,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum LedIndicatorCurrent {
|
||||
/// 1 mA
|
||||
Ma1,
|
||||
/// 2 mA
|
||||
Ma2,
|
||||
/// 4 mA
|
||||
Ma4,
|
||||
/// 8 mA
|
||||
Ma8,
|
||||
}
|
||||
|
||||
impl LedIndicatorCurrent {
|
||||
fn to_bits(self) -> u8 {
|
||||
match self {
|
||||
LedIndicatorCurrent::Ma1 => 0b00,
|
||||
LedIndicatorCurrent::Ma2 => 0b01,
|
||||
LedIndicatorCurrent::Ma4 => 0b10,
|
||||
LedIndicatorCurrent::Ma8 => 0b11,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct LedConfig {
|
||||
pub drv_enabled: bool,
|
||||
pub drv_current: LedCurrent,
|
||||
pub ind_enabled: bool,
|
||||
pub ind_current: LedIndicatorCurrent,
|
||||
}
|
||||
|
||||
impl LedConfig {
|
||||
fn config_byte(&self) -> u8 {
|
||||
let mut byte = 0u8;
|
||||
|
||||
if self.drv_enabled {
|
||||
byte |= LED_DRV_ENABLE;
|
||||
}
|
||||
byte |= (self.drv_current.to_bits() << LED_DRV_ICL_SHIFT) & LED_DRV_ICL_MASK;
|
||||
|
||||
if self.ind_enabled {
|
||||
byte |= LED_IND_ENABLE;
|
||||
}
|
||||
byte |= (self.ind_current.to_bits() << LED_IND_ICL_SHIFT) & LED_IND_ICL_MASK;
|
||||
|
||||
byte
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for LedConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
drv_enabled: false,
|
||||
drv_current: LedCurrent::Ma12_5,
|
||||
ind_enabled: false,
|
||||
ind_current: LedIndicatorCurrent::Ma8,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error<E> {
|
||||
I2c(E),
|
||||
Timeout,
|
||||
}
|
||||
|
||||
impl<E> From<E> for Error<E> {
|
||||
fn from(e: E) -> Self {
|
||||
Error::I2c(e)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AS7265X<I, D>
|
||||
where
|
||||
I: i2c::I2c,
|
||||
D: DelayNs,
|
||||
{
|
||||
device: I,
|
||||
delay: D,
|
||||
config: AS7265XConfig,
|
||||
}
|
||||
|
||||
impl<I, D> AS7265X<I, D>
|
||||
where
|
||||
I: i2c::I2c,
|
||||
D: DelayNs,
|
||||
{
|
||||
pub fn new(device: I, config: AS7265XConfig, delay: D) -> Self {
|
||||
Self {
|
||||
device,
|
||||
config,
|
||||
delay,
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn init(&mut self) -> Result<(), Error<I::Error>> {
|
||||
self.write_config().await?;
|
||||
|
||||
self.set_integration_time(self.config.integration_time)
|
||||
.await?;
|
||||
|
||||
self.set_led_config(
|
||||
Led::White,
|
||||
LedConfig {
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
|
||||
self.set_led_config(
|
||||
Led::Ir,
|
||||
LedConfig {
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
|
||||
self.set_led_config(
|
||||
Led::Uv,
|
||||
LedConfig {
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn soft_reset(&mut self) -> Result<(), Error<I::Error>> {
|
||||
self.write_virtual_register(CONFIG_ADDR, SOFT_RESET).await?;
|
||||
self.delay.delay_ms(100);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn write_config(&mut self) -> Result<(), Error<I::Error>> {
|
||||
let config_byte = self.config.to_config_byte();
|
||||
self.write_virtual_register(CONFIG_ADDR, config_byte).await
|
||||
}
|
||||
|
||||
pub async fn set_integration_time(&mut self, time: u8) -> Result<(), Error<I::Error>> {
|
||||
self.write_virtual_register(INTEGRATION_ADDR, time).await
|
||||
}
|
||||
|
||||
async fn set_led_config(&mut self, led: Led, config: LedConfig) -> Result<(), Error<I::Error>> {
|
||||
self.select_device(led.get_device()).await?;
|
||||
let config_byte = config.config_byte();
|
||||
self.write_virtual_register(LED_ADDR, config_byte).await
|
||||
}
|
||||
|
||||
pub async fn get_device_type(&mut self) -> Result<u8, Error<I::Error>> {
|
||||
self.read_virtual_register(HW_VERSION_HIGH_ADDR).await
|
||||
}
|
||||
|
||||
pub async fn get_hardware_version(&mut self) -> Result<u8, Error<I::Error>> {
|
||||
self.read_virtual_register(HW_VERSION_LOW_ADDR).await
|
||||
}
|
||||
|
||||
pub async fn get_firmware_version(&mut self) -> Result<(u8, u8), Error<I::Error>> {
|
||||
let high = self.read_virtual_register(FW_VERSION_HIGH_ADDR).await?;
|
||||
let low = self.read_virtual_register(FW_VERSION_LOW_ADDR).await?;
|
||||
Ok((high, low))
|
||||
}
|
||||
|
||||
async fn select_device(&mut self, device: Device) -> Result<(), Error<I::Error>> {
|
||||
self.write_virtual_register(DEV_SELECT_ADDR, device as u8)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn read_temperature(&mut self) -> Result<i8, Error<I::Error>> {
|
||||
let temp = self.read_virtual_register(TEMP_ADDR).await?;
|
||||
Ok(temp as i8)
|
||||
}
|
||||
|
||||
pub async fn is_data_ready(&mut self) -> Result<bool, Error<I::Error>> {
|
||||
let config = self.read_virtual_register(CONFIG_ADDR).await?;
|
||||
Ok((config & CONFIG_DATA_RDY) != 0)
|
||||
}
|
||||
|
||||
pub async fn measure(&mut self) -> Result<(), Error<I::Error>> {
|
||||
self.write_config().await?;
|
||||
let wait_time = (self.config.integration_time as u32) * 28 / 10 * 2;
|
||||
self.delay.delay_ms(wait_time);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn mesure_with_bulb(&mut self) -> Result<(), Error<I::Error>> {
|
||||
self.set_led_config(
|
||||
Led::White,
|
||||
LedConfig {
|
||||
drv_enabled: true,
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
|
||||
self.set_led_config(
|
||||
Led::Ir,
|
||||
LedConfig {
|
||||
drv_enabled: true,
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
|
||||
self.set_led_config(
|
||||
Led::Uv,
|
||||
LedConfig {
|
||||
drv_enabled: true,
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
|
||||
self.measure().await?;
|
||||
|
||||
self.set_led_config(
|
||||
Led::White,
|
||||
LedConfig {
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
|
||||
self.set_led_config(
|
||||
Led::Ir,
|
||||
LedConfig {
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
|
||||
self.set_led_config(
|
||||
Led::Uv,
|
||||
LedConfig {
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn read_raw_measurement(&mut self, channel: Channel) -> Result<u16, Error<I::Error>> {
|
||||
let dev = channel.get_device_for_chanel();
|
||||
self.select_device(dev).await?;
|
||||
self.read_raw_channel(channel.get_raw_address()).await
|
||||
}
|
||||
|
||||
pub async fn read_calibrated_messurement(
|
||||
&mut self,
|
||||
channel: Channel,
|
||||
) -> Result<f32, Error<I::Error>> {
|
||||
let dev = channel.get_device_for_chanel();
|
||||
self.select_device(dev).await?;
|
||||
|
||||
self.read_calibrated_channel(channel.get_cal_address())
|
||||
.await
|
||||
}
|
||||
|
||||
async fn write_virtual_register(
|
||||
&mut self,
|
||||
virtual_addr: u8,
|
||||
data: u8,
|
||||
) -> Result<(), Error<I::Error>> {
|
||||
const MAX_RETRIES: u8 = 100;
|
||||
|
||||
for _ in 0..MAX_RETRIES {
|
||||
let mut status = [0u8; 1];
|
||||
self.device
|
||||
.write_read(AS7265X_ADDR, &[STATUS_REG], &mut status)?;
|
||||
if (status[0] & TX_VALID) == 0 {
|
||||
break;
|
||||
}
|
||||
self.delay.delay_ms(5);
|
||||
}
|
||||
|
||||
self.device
|
||||
.write(AS7265X_ADDR, &[WRITE_REG, 0x80 | virtual_addr])?;
|
||||
|
||||
for _ in 0..MAX_RETRIES {
|
||||
let mut status = [0u8; 1];
|
||||
self.device
|
||||
.write_read(AS7265X_ADDR, &[STATUS_REG], &mut status)?;
|
||||
if (status[0] & TX_VALID) == 0 {
|
||||
break;
|
||||
}
|
||||
self.delay.delay_ms(5);
|
||||
}
|
||||
|
||||
self.device.write(AS7265X_ADDR, &[WRITE_REG, data])?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn read_virtual_register(&mut self, virtual_addr: u8) -> Result<u8, Error<I::Error>> {
|
||||
const MAX_RETRIES: u8 = 100;
|
||||
|
||||
for _ in 0..MAX_RETRIES {
|
||||
let mut status = [0u8; 1];
|
||||
self.device
|
||||
.write_read(AS7265X_ADDR, &[STATUS_REG], &mut status)?;
|
||||
if (status[0] & TX_VALID) == 0 {
|
||||
break;
|
||||
}
|
||||
self.delay.delay_ms(5);
|
||||
}
|
||||
|
||||
self.device
|
||||
.write(AS7265X_ADDR, &[WRITE_REG, virtual_addr])?;
|
||||
|
||||
for _ in 0..MAX_RETRIES {
|
||||
let mut status = [0u8; 1];
|
||||
self.device
|
||||
.write_read(AS7265X_ADDR, &[STATUS_REG], &mut status)?;
|
||||
if (status[0] & RX_VALID) != 0 {
|
||||
break;
|
||||
}
|
||||
self.delay.delay_ms(5);
|
||||
}
|
||||
|
||||
let mut data = [0u8; 1];
|
||||
self.device
|
||||
.write_read(AS7265X_ADDR, &[READ_REG], &mut data)?;
|
||||
|
||||
Ok(data[0])
|
||||
}
|
||||
|
||||
async fn read_raw_channel(&mut self, high_byte_addr: u8) -> Result<u16, Error<I::Error>> {
|
||||
let high = self.read_virtual_register(high_byte_addr).await?;
|
||||
let low = self.read_virtual_register(high_byte_addr + 1).await?;
|
||||
|
||||
Ok(((high as u16) << 8) | (low as u16))
|
||||
}
|
||||
|
||||
async fn read_calibrated_channel(
|
||||
&mut self,
|
||||
high_byte_addr: u8,
|
||||
) -> Result<f32, Error<I::Error>> {
|
||||
let b0 = self.read_virtual_register(high_byte_addr).await?;
|
||||
let b1 = self.read_virtual_register(high_byte_addr + 1).await?;
|
||||
let b2 = self.read_virtual_register(high_byte_addr + 2).await?;
|
||||
let b3 = self.read_virtual_register(high_byte_addr + 3).await?;
|
||||
|
||||
let bits = u32::from_be_bytes([b0, b1, b2, b3]);
|
||||
Ok(f32::from_bits(bits))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user