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