From 70adabc65c1ac482e0c8535ec4731c20df5dad47 Mon Sep 17 00:00:00 2001
From: "michael.divia" <michael.divia@etu.hesge.ch>
Date: Wed, 25 Oct 2023 14:33:57 +0200
Subject: [PATCH] I2C & SPI Working

---
 .vscode/settings.json |   3 +
 src/i2c.rs            |  69 -------
 src/main.rs           | 422 +++++++++++++++++++++++++++++++++++++++---
 3 files changed, 401 insertions(+), 93 deletions(-)
 create mode 100644 .vscode/settings.json
 delete mode 100644 src/i2c.rs

diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 0000000..af1664e
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,3 @@
+{
+    "cortex-debug.variableUseNaturalFormat": false
+}
\ No newline at end of file
diff --git a/src/i2c.rs b/src/i2c.rs
deleted file mode 100644
index c07031d..0000000
--- a/src/i2c.rs
+++ /dev/null
@@ -1,69 +0,0 @@
-#![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;
-    }
-}
diff --git a/src/main.rs b/src/main.rs
index 548d75f..23445be 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -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);
+
+    // Start Pressure and Temperature Conversion
+    i2c.write(MPL115A2_ADDR, &[0x12, 0x00]).unwrap();
+
+    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;
 
-    
-    i2c.write_read(MPL115A2_ADDR, &[0x04], &mut buffer).unwrap();
-    hprintln!("TEST: {}", buffer[0]);
-    
-    loop{
-        continue;
+            return negative_value;
+        } else {
+            // Simply convert the value
+            return hex as f32;
+        }
+    } else {
+        return 0.0;
     }
 }
-- 
GitLab