Skip to content
Snippets Groups Projects
Commit 70adabc6 authored by michael.divia's avatar michael.divia
Browse files

I2C & SPI Working

parent 70decd77
No related branches found
No related tags found
No related merge requests found
{
"cortex-debug.variableUseNaturalFormat": false
}
\ No newline at end of file
#![no_std]
#![no_main]
extern crate panic_halt;
use cortex_m_rt::entry;
use cortex_m_semihosting::hprintln;
use stm32l4xx_hal as hal;
use crate::hal::i2c;
use crate::hal::i2c::I2c;
use crate::hal::prelude::*;
#[entry]
fn main() -> ! {
hprintln! {"Nucleo-l476RG is alive !"};
/*
setup peripherals
*/
let periphs = hal::stm32::Peripherals::take().unwrap();
let mut flash = periphs.FLASH.constrain(); // .constrain();
let mut rcc = periphs.RCC.constrain();
let mut pwr = periphs.PWR.constrain(&mut rcc.apb1r1);
// let mut gpioa = periphs.GPIOA.split(&mut rcc.ahb2);
let mut gpiob = periphs.GPIOB.split(&mut rcc.ahb2);
let clocks = rcc.cfgr.freeze(&mut flash.acr, &mut pwr);
/*
setup i2c
*/
let mut scl = gpiob.pb13.into_alternate_open_drain::<4>(
&mut gpiob.moder,
&mut gpiob.otyper,
&mut gpiob.afrh,
);
scl.internal_pull_up(&mut gpiob.pupdr, true);
let mut sda = gpiob.pb14.into_alternate_open_drain::<4>(
&mut gpiob.moder,
&mut gpiob.otyper,
&mut gpiob.afrh,
);
sda.internal_pull_up(&mut gpiob.pupdr, true);
let mut i2c = I2c::i2c2(
periphs.I2C2,
(scl, sda),
i2c::Config::new(100.kHz(), clocks),
&mut rcc.apb1r1,
);
let mut buffer = [0u8; 4];
const MPL115A2_ADDR_WRITE: u8 = 0xC0;
const MPL115A2_ADDR_READ: u8 = 0xC1;
i2c.write_read(MPL115A2_ADDR_WRITE, &[0x04], &mut buffer)
.unwrap();
hprintln!("TEST: {}", buffer[0]);
loop {
continue;
}
}
......@@ -3,77 +3,451 @@
extern crate panic_halt;
use cortex_m::asm;
use cortex_m_rt::entry;
use cortex_m_semihosting::hprint;
use cortex_m_semihosting::hprintln;
use hal::gpio::Alternate;
use stm32l4xx_hal as hal;
use crate::hal::i2c;
use crate::hal::i2c::I2c;
use crate::hal::prelude::*;
use stm32l4xx_hal::{
gpio::{PushPull, Speed},
hal::spi::{Mode, Phase, Polarity},
prelude::*,
spi::Spi,
};
struct Coefficient {
a0: f32,
b1: f32,
b2: f32,
c12: f32,
}
struct Data {
pression: f32,
temperature: f32,
luminosity: i32,
}
#[entry]
fn main() -> ! {
// Clean terminal and acknowledge the boot
hprint! {"{}[2J", 27 as char};
hprintln! {"Nucleo-l476RG is alive !"};
/*
setup peripherals
*/
// **** Setup Peripherals ****
let periphs = hal::stm32::Peripherals::take().unwrap();
let mut flash = periphs.FLASH.constrain(); // .constrain();
let mut flash = periphs.FLASH.constrain();
let mut rcc = periphs.RCC.constrain();
let mut pwr = periphs.PWR.constrain(&mut rcc.apb1r1);
// let mut gpioa = periphs.GPIOA.split(&mut rcc.ahb2);
let mut gpioc = periphs.GPIOC.split(&mut rcc.ahb2);
let mut gpiob = periphs.GPIOB.split(&mut rcc.ahb2);
let mut gpioa = periphs.GPIOA.split(&mut rcc.ahb2);
let clocks = rcc.cfgr.freeze(&mut flash.acr, &mut pwr);
// **** Setup SPI ****
/*
setup gpios
VCC -> 5V
GND -> GND
SCK -> PC_10
SDO -> PC_11
NC -> X
CS -> PB_6
*/
// let mut led = gpioa
// .pa5
// .into_push_pull_output(&mut gpioa.moder, &mut gpioa.otyper);
// Pins
let sclk = gpioc
.pc10
.into_alternate(&mut gpioc.moder, &mut gpioc.otyper, &mut gpioc.afrh)
.set_speed(Speed::High);
let miso = gpioc
.pc11
.into_alternate(&mut gpioc.moder, &mut gpioc.otyper, &mut gpioc.afrh)
.set_speed(Speed::High);
let mosi_dummy = gpioc
.pc12
.into_alternate(&mut gpioc.moder, &mut gpioc.otyper, &mut gpioc.afrh)
.set_speed(Speed::High);
let mut cs = gpiob
.pb6
.into_push_pull_output(&mut gpiob.moder, &mut gpiob.otyper);
// SPI
let mut spi = Spi::spi3(
periphs.SPI3,
(sclk, miso, mosi_dummy),
Mode {
phase: Phase::CaptureOnFirstTransition,
polarity: Polarity::IdleLow,
},
1000.kHz(),
clocks,
&mut rcc.apb1r1,
);
// **** Setup I2C ****
/*
setup i2c
VDD -> 5V
GND -> GND
SCL -> PB_13
SDA -> PB_14
RST -> X
SDWN -> X
*/
// Pins
let mut scl = gpiob.pb13.into_alternate_open_drain::<4>(
&mut gpiob.moder,
&mut gpiob.otyper,
&mut gpiob.afrh,
);
scl.internal_pull_up(&mut gpiob.pupdr, true);
let mut sda = gpiob.pb14.into_alternate_open_drain::<4>(
&mut gpiob.moder,
&mut gpiob.otyper,
&mut gpiob.afrh,
);
// Set pull up
scl.internal_pull_up(&mut gpiob.pupdr, true);
sda.internal_pull_up(&mut gpiob.pupdr, true);
let mut i2c = I2c::i2c2(
// Create I2C
let mut i2c_a = I2c::i2c2(
periphs.I2C2,
(scl, sda),
i2c::Config::new(100.kHz(), clocks),
&mut rcc.apb1r1,
);
let mut buffer = [0u8; 4];
// Create Data Struct
let mut data = Data {
pression: 0.0,
temperature: 0.0,
luminosity: 0,
};
// **** Main Loop ****
loop {
// Get Data
mpl115_a2(&mut i2c_a, &mut data);
pb200_286(&mut spi, &mut cs, &mut data);
// Display Data
hprintln!("{}[2J", 27 as char);
hprintln!(
"Pression: {} [kPa]\nTempérature: {} [°C] (±5 °C)\nLuminosité: {} / 125",
data.pression,
data.temperature,
data.luminosity
);
// Wait
asm::delay(2_000_000);
}
}
// **** pb200_286 Function ****
/// Retrieve luminosity data from a Pmod ALS: Ambient Light Sensor
/// INPUT :
/// 1: spi -> The SPI module with which you would like to communicate
/// 2: cs -> The corresponding Chip Select for this SPI module
/// 3: data -> The reference in which the data will be written
/// OUTPUT :
/// NONE
fn pb200_286(
spi: &mut Spi<
crate::hal::pac::SPI3,
(
crate::hal::gpio::Pin<Alternate<PushPull, 6>, crate::hal::gpio::H8, 'C', 10>,
crate::hal::gpio::Pin<Alternate<PushPull, 6>, crate::hal::gpio::H8, 'C', 11>,
crate::hal::gpio::Pin<Alternate<PushPull, 6>, crate::hal::gpio::H8, 'C', 12>,
),
>,
cs: &mut crate::hal::gpio::Pin<
crate::hal::gpio::Output<PushPull>,
crate::hal::gpio::L8,
'B',
6,
>,
data: &mut Data,
) {
// Set Chip Select to low to start data exchange
cs.set_low();
// Retrieve the data
let mut buffer = [0x00, 0x00];
spi.transfer(&mut buffer).unwrap();
// Set Chip Select to high to stop data exchange
cs.set_high();
// Data is send in 2 bytes, with 3 leading and 4 trailing zeros
let mut lux = buffer[0] << 3;
lux |= buffer[1] >> 4;
// Store Data in struct
data.luminosity = lux as i32;
}
/// **** mpl115_a2 Function ****
/// Retrieve Pressure and Temperature data from a MPL115A2 - I2C Barometric Pressure/Temperature Sensor
/// INPUT :
/// 1: i2c -> The I2C module with which you would like to communicate
/// 2: data -> The reference in which the data will be written
/// OUTPUT :
/// NONE
fn mpl115_a2(
i2c: &mut I2c<
crate::hal::pac::I2C2,
(
crate::hal::gpio::Pin<
Alternate<crate::hal::gpio::OpenDrain, 4>,
crate::hal::gpio::H8,
'B',
13,
>,
crate::hal::gpio::Pin<
Alternate<crate::hal::gpio::OpenDrain, 4>,
crate::hal::gpio::H8,
'B',
14,
>,
),
>,
data: &mut Data,
) {
// Create our buffers
let mut buffer_4 = [0u8; 4];
let mut buffer_8 = [0u8; 8];
// Set the address of our card
const MPL115A2_ADDR: u8 = 0x60;
/*
Main loop
*/
// Get Coefficients
i2c.write_read(MPL115A2_ADDR, &[0x04], &mut buffer_8)
.unwrap();
let coeffs = coeff(buffer_8);
asm::delay(1_000_000);
i2c.write_read(MPL115A2_ADDR, &[0x04], &mut buffer).unwrap();
hprintln!("TEST: {}", buffer[0]);
// Start Pressure and Temperature Conversion
i2c.write(MPL115A2_ADDR, &[0x12, 0x00]).unwrap();
loop{
continue;
asm::delay(1_000_000);
// Get Temperature & Pressure
i2c.write_read(MPL115A2_ADDR, &[0x00], &mut buffer_4)
.unwrap();
asm::delay(1_000_000);
// Calculate Pressure Compensation
let pcomp = pcomp(buffer_4, coeffs);
// Calculate Pressure
let press = pcomp * ((115.0 - 50.0) / 1023.0) + 50.0;
// Calculate non accurate and non calibrated temperature
let temp =
((((((buffer_4[2] as u16) << 8) + buffer_4[3] as u16) >> 6) as f32) - 605.75) / -5.35;
// Store Data in struct
data.pression = press;
data.temperature = temp;
}
/// **** pcomp Function ****
/// Calculates the Pressure Compensation with the Pressure ADC and the Temperature ADC
/// INPUT :
/// 1: buffer -> Buffer with the Pressure ADC and the Temperature ADC
/// 2: coeffs -> Struct with all Coefficients
/// OUTPUT :
/// Pressure Compensation
fn pcomp(buffer: [u8; 4], coeffs: Coefficient) -> f32 {
// Retrieve Pressure ADC and Temperature ADC
let padc = ((((buffer[0] as u16) << 8) + buffer[1] as u16) >> 6) as f32;
let tdac = ((((buffer[2] as u16) << 8) + buffer[3] as u16) >> 6) as f32;
//https://cdn-shop.adafruit.com/datasheets/MPL115A2.pdf
// For TEST
//let padc = (0x6680 >> 6) as f32;
//let tdac = (0x7EC0 >> 6) as f32;
// Pcomp = a0 + (b1 + c12 * Tadc) * Padc + b2 * Tadc
let c12_2 = coeffs.c12 * tdac;
let a1 = coeffs.b1 + c12_2;
let a1_1 = a1 * padc;
let y1 = coeffs.a0 + a1_1;
let a2_2 = coeffs.b2 * tdac;
return y1 + a2_2;
}
/// **** coeff Function ****
/// Calculates all the coefficients
/// INPUT :
/// 1: buffer -> Buffer with all coefficients data
/// OUTPUT :
/// Coefficients
fn coeff(buffer: [u8; 8]) -> Coefficient {
// Get the HEX of all coefficients
let a0_coeff_hex = ((buffer[0] as u16) << 8) + buffer[1] as u16;
let b1_coeff_hex = ((buffer[2] as u16) << 8) + buffer[3] as u16;
let b2_coeff_hex = ((buffer[4] as u16) << 8) + buffer[5] as u16;
let c12_coeff_hex = ((buffer[6] as u16) << 8) + buffer[7] as u16 >> 2;
//https://cdn-shop.adafruit.com/datasheets/MPL115A2.pdf
// For TEST
//let a0_coeff_hex: u16 = 0x3ECE;
//let b1_coeff_hex: u16 = 0xB3F9;
//let b2_coeff_hex: u16 = 0xC517;
//let c12_coeff_hex: u16 = 0x33C8 >> 2;
// Apply Q-Format on all coefficients
let a0 = q_format_to_float(a0_coeff_hex, 3, 13, 0);
let b1 = q_format_to_float(b1_coeff_hex, 13, 3, 0);
let b2 = q_format_to_float(b2_coeff_hex, 14, 2, 0);
let c12 = q_format_to_float(c12_coeff_hex, 13, 0, 9);
return Coefficient { a0, b1, b2, c12 };
}
/// **** q_format_to_float Function ****
/// Converts Hex to f32 with custom Q-Format
/// INPUT :
/// 1: hex -> coefficient data
/// 2: fractional_bits -> Number of fractional bits
/// 3: int_bits -> Number of int bits
/// 4: decimal_padding -> Padding for the fractional bits
/// OUTPUT :
/// Coefficient
fn q_format_to_float(hex: u16, fractional_bits: i64, int_bits: i64, decimal_padding: i64) -> f32 {
// Mask to isolate the fractional part
let bitmask = (1 << fractional_bits) - 1;
// Get the fractional part
let frac_part = frac_to_float(hex & bitmask, fractional_bits, decimal_padding);
// Get the signed part
let signed_part = shex_to_float(hex >> fractional_bits, int_bits);
// Add fractional part to signed part by making sure that if the signed part was negative, the fractional part must also be negative
if signed_part.is_sign_positive() {
return signed_part + frac_part;
} else {
return signed_part - (1.0 - frac_part);
}
}
/// **** custom_pow Function ****
/// Custom Power function
/// INPUT :
/// 1: base -> base of the power
/// 2: exponent -> exponent of the power
/// OUTPUT :
/// Result of power
fn custom_pow(base: f32, exponent: i64) -> f32 {
// Any number raised to the power of 0 is 1
if exponent == 0 {
return 1.0;
}
let mut result = 1.0;
let mut exp = exponent;
let mut base_val = base;
// Handle negative exponents by inverting the base and exponent, ensuring a positive calculation
if exponent < 0 {
base_val = 1.0 / base_val;
exp = -exp;
}
// Loop to calculate the power iteratively by multiplying base_val with itself 'exp' times
for _ in 0..exp {
result *= base_val;
}
return result;
}
/// **** frac_to_float Function ****
/// Converts the fractional bites to a normal fractional number
/// INPUT :
/// 1: hex -> coefficient data
/// 2: fractional_bits -> Number of fractional bits
/// 2: decimal_padding -> Padding for the fractional bits
/// OUTPUT :
/// Fractional part
fn frac_to_float(hex: u16, fractional_bits: i64, decimal_padding: i64) -> f32 {
// Fractional bites are calculated this way :
// Hex => 0xA / number of fractional bytes => 5
// Binary => 01010
// Fractional part => 2^-2 + 2^-4 = 0.3125
let mut frac = 0.0;
let mut exp = fractional_bits;
// Knowing that we will travel from right to left, we will start with the smallest power and go to the biggest
for i in 0..fractional_bits {
// Mask to isolate 1 bit
let mask = 1 << i;
// Check if the bit is a 1
if (hex & mask) != 0 {
// Calculate the fraction with our custom power function and without forgetting to add the decimal padding
frac += custom_pow(2.0, (exp + decimal_padding) * -1);
}
// 'exp' is going inverted from 'i'
exp -= 1;
}
return frac;
}
/// **** shex_to_float Function ****
/// Converts the Signed integer bites to a normal integer number
/// INPUT :
/// 1: hex -> coefficient data
/// 2: int_bits -> Number of integer bits
/// OUTPUT :
/// Signed Integer part
fn shex_to_float(hex: u16, int_bits: i64) -> f32 {
// No need to do everything if the hex is already 0
if hex != 0 {
// Get the sign bit
let sign_bit = 1 << (int_bits - 1);
// If the int is negative
if (hex & sign_bit) != 0 {
// Create a mask to get the Integer part without the sign bit
let mask = (1u16 << int_bits) - 1;
// Invert every bits and multiply by -1
let negative_value = ((hex ^ mask) as f32) * -1.0;
return negative_value;
} else {
// Simply convert the value
return hex as f32;
}
} else {
return 0.0;
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment