diff --git a/.vscode/settings.json b/.vscode/settings.json index af1664ec1fa7835f9a46e622cd2c549570a39af6..0db2f7af33798b35bdcf4257f5180d0297f262a7 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,6 @@ { - "cortex-debug.variableUseNaturalFormat": false + "cortex-debug.variableUseNaturalFormat": false, + "rust-analyzer.linkedProjects": [ + "./Cargo.toml" + ] } \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 23445be6880554a475c4b833521be3c765cae88d..47862b097abe2db1b0ec112561fd99a637574789 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,35 +3,22 @@ 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; +mod mpl115_a2; +mod pb200_286; +mod rn_131rg; -use crate::hal::i2c; -use crate::hal::i2c::I2c; +use stm32l4xx_hal::{gpio::Speed, 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, -} +// use stm32l4xx_hal::delay::Delay; struct Data { pression: f32, temperature: f32, - luminosity: i32, + luminosity: u8, } #[entry] @@ -42,7 +29,8 @@ fn main() -> ! { // **** Setup Peripherals **** - let periphs = hal::stm32::Peripherals::take().unwrap(); + let periphs = stm32l4xx_hal::stm32::Peripherals::take().unwrap(); + // let c_periphs = cortex_m::Peripherals::take().unwrap(); let mut flash = periphs.FLASH.constrain(); let mut rcc = periphs.RCC.constrain(); let mut pwr = periphs.PWR.constrain(&mut rcc.apb1r1); @@ -51,403 +39,161 @@ fn main() -> ! { let mut gpioa = periphs.GPIOA.split(&mut rcc.ahb2); let clocks = rcc.cfgr.freeze(&mut flash.acr, &mut pwr); - // **** Setup SPI **** + // let mut timer = Delay::new(c_periphs.SYST, clocks); /* - VCC -> 5V - GND -> GND - SCK -> PC_10 - SDO -> PC_11 - NC -> X - CS -> PB_6 + + **** Setup pb200_286 (luminosity) **** + + VCC -> 5V + GND -> GND + SCK -> PC_10 + SDO -> PC_11 + NC -> X + CS -> PB_6 + */ - // Pins - let sclk = gpioc + let pb_sclk = gpioc .pc10 .into_alternate(&mut gpioc.moder, &mut gpioc.otyper, &mut gpioc.afrh) .set_speed(Speed::High); - let miso = gpioc + let pb_miso = gpioc .pc11 .into_alternate(&mut gpioc.moder, &mut gpioc.otyper, &mut gpioc.afrh) .set_speed(Speed::High); - let mosi_dummy = gpioc + let pb_mosi_dummy = gpioc .pc12 .into_alternate(&mut gpioc.moder, &mut gpioc.otyper, &mut gpioc.afrh) .set_speed(Speed::High); - let mut cs = gpiob + let mut pb_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(), + let mut pb200_286_conn = pb200_286::get_connexion( clocks, &mut rcc.apb1r1, + periphs.SPI3, + pb_sclk, + pb_miso, + pb_mosi_dummy, ); - // **** Setup I2C **** - /* - VDD -> 5V - GND -> GND - SCL -> PB_13 - SDA -> PB_14 - RST -> X - SDWN -> X + + **** Setup mpl115_a2 (pressure and temperature) **** + + 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>( + let mut mp_scl = gpiob.pb13.into_alternate_open_drain::<4>( &mut gpiob.moder, &mut gpiob.otyper, &mut gpiob.afrh, ); - let mut sda = gpiob.pb14.into_alternate_open_drain::<4>( + let mut mp_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); + mp_scl.internal_pull_up(&mut gpiob.pupdr, true); + mp_sda.internal_pull_up(&mut gpiob.pupdr, true); - // Create I2C - let mut i2c_a = I2c::i2c2( - periphs.I2C2, - (scl, sda), - i2c::Config::new(100.kHz(), clocks), - &mut rcc.apb1r1, - ); + let mut mp_con = + mpl115_a2::get_connexion(periphs.I2C2, clocks, &mut rcc.apb1r1, mp_scl, mp_sda); + + /* + + **** Setup rn-131rg **** + + ┌─────┬─────┐ + │ x │ GND │ + ├─────┼─────┤ + │ VCC │ x │ + ├─────┼─────┤ + │ x │ x │ + ├─────┼─────┤ ... [puce, leds, etc] + │ x │ x │ + ├─────┼─────┤ + ┌────────┐ │ x │ x │ + │TX -> RX│ └─────┴─────┘ + ├────────┤ + │RX -> TX│ (module -> micro controller) + └────────┘ + + */ + + let rn_tx = gpioa + .pa0 + .into_alternate(&mut gpioa.moder, &mut gpioa.otyper, &mut gpioa.afrl); + + let rn_rx = gpioa + .pa1 + .into_alternate(&mut gpioa.moder, &mut gpioa.otyper, &mut gpioa.afrl); + + let mut rn_con = rn_131rg::get_connexion(rn_tx, rn_rx, periphs.UART4, &mut rcc.apb1r1, clocks); + + // **** Main Loop **** - // Create Data Struct let mut data = Data { pression: 0.0, temperature: 0.0, luminosity: 0, }; - // **** Main Loop **** + const BUFFER_SIZE: usize = 1024; + let mut buffer: [u8; BUFFER_SIZE] = [0; BUFFER_SIZE]; loop { + rn_131rg::wait_http(&mut rn_con, &mut buffer, BUFFER_SIZE); + // Get Data - mpl115_a2(&mut i2c_a, &mut data); - pb200_286(&mut spi, &mut cs, &mut data); + mpl115_a2::get_data(&mut mp_con, &mut data.pression, &mut data.temperature); + pb200_286::get_data(&mut pb200_286_conn, &mut pb_cs, &mut data.luminosity); + + rn_131rg::send_fmt( + &mut rn_con, + format_args!( + "HTTP/1.1 200 OK +Content-Length: 286 +Content-Type: text/html +Connection: close + +<!DOCTYPE html> +<html lang='en'> +<head> + <meta charset='UTF-8'> + <meta name='viewport' content='width=device-width, initial-scale=1.0'> + <title>Document</title> +</head> +<body> +<h1>Ayo</h1> +<p>Pression: {:.3}</p> +<p>Température: {:.3}</p> +<p>Luminosité: {:0>3}</p> +</body> +</html> + +", data.pression, data.temperature, data.luminosity + ), + ); // Display Data - hprintln!("{}[2J", 27 as char); - hprintln!( - "Pression: {} [kPa]\nTempérature: {} [°C] (±5 °C)\nLuminosité: {} / 125", - data.pression, - data.temperature, - data.luminosity - ); + // 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; - - // 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; - - return negative_value; - } else { - // Simply convert the value - return hex as f32; - } - } else { - return 0.0; } } diff --git a/src/mpl115_a2.rs b/src/mpl115_a2.rs new file mode 100644 index 0000000000000000000000000000000000000000..db59020a94efc252edee729b4b99b0fc9ce78870 --- /dev/null +++ b/src/mpl115_a2.rs @@ -0,0 +1,281 @@ +use cortex_m::asm; + +use stm32l4xx_hal::gpio::OpenDrain; +use stm32l4xx_hal::gpio::*; +use stm32l4xx_hal::i2c::I2c; +use stm32l4xx_hal::pac::*; +use stm32l4xx_hal::prelude::*; + +use stm32l4xx_hal::i2c; +use stm32l4xx_hal::rcc::*; +/// Local struct to store values used in caculations +struct Coefficient { + a0: f32, + b1: f32, + b2: f32, + c12: f32, +} + +pub fn get_connexion( + i2c2: I2C2, + clocks: Clocks, + mut apb1r1: &mut APB1R1, + scl: Pin<Alternate<OpenDrain, 4>, H8, 'B', 13>, + sda: Pin<Alternate<OpenDrain, 4>, H8, 'B', 14>, +) -> I2c< + I2C2, + ( + Pin<Alternate<OpenDrain, 4>, H8, 'B', 13>, + Pin<Alternate<OpenDrain, 4>, H8, 'B', 14>, + ), +> { + I2c::i2c2( + i2c2, + (scl, sda), + i2c::Config::new(100.kHz(), clocks), + &mut apb1r1, + ) +} + +/// 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 + +pub fn get_data( + i2c: &mut I2c< + I2C2, + ( + Pin<Alternate<OpenDrain, 4>, H8, 'B', 13>, + Pin<Alternate<OpenDrain, 4>, H8, 'B', 14>, + ), + >, + pression: &mut f32, + temperature: &mut f32, +) { + // 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; + + // 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 + *pression = press; + *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; + } +} diff --git a/src/pb200_286.rs b/src/pb200_286.rs new file mode 100644 index 0000000000000000000000000000000000000000..c51ebc9054968651662aeb8a60ce42bee9b1c63f --- /dev/null +++ b/src/pb200_286.rs @@ -0,0 +1,80 @@ +extern crate panic_halt; + +use cortex_m::prelude::_embedded_hal_blocking_spi_Transfer; +use stm32l4xx_hal::gpio::*; +use stm32l4xx_hal::pac::*; +use stm32l4xx_hal::rcc::Clocks; +use stm32l4xx_hal::spi::Spi; + +use stm32l4xx_hal::rcc::*; +use stm32l4xx_hal::{ + hal::spi::{Mode, Phase, Polarity}, + prelude::*, +}; + +pub fn get_connexion( + clocks: Clocks, + mut apb1r1:&mut APB1R1, + + spi3: SPI3, + sclk: Pin<Alternate<PushPull, 6>, H8, 'C', 10>, + miso: Pin<Alternate<PushPull, 6>, H8, 'C', 11>, + mosi_dummy: Pin<Alternate<PushPull, 6>, H8, 'C', 12>, +) -> Spi< + SPI3, + ( + Pin<Alternate<PushPull, 6>, H8, 'C', 10>, + Pin<Alternate<PushPull, 6>, H8, 'C', 11>, + Pin<Alternate<PushPull, 6>, H8, 'C', 12>, + ), +> { + Spi::spi3( + spi3, + (sclk, miso, mosi_dummy), + Mode { + phase: Phase::CaptureOnFirstTransition, + polarity: Polarity::IdleLow, + }, + 1000.kHz(), + clocks, + &mut apb1r1, + ) +} + +/// 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 + +pub fn get_data( + spi: &mut Spi< + SPI3, + ( + Pin<Alternate<PushPull, 6>, H8, 'C', 10>, + Pin<Alternate<PushPull, 6>, H8, 'C', 11>, + Pin<Alternate<PushPull, 6>, H8, 'C', 12>, + ), + >, + cs: &mut Pin<Output<PushPull>, L8, 'B', 6>, + luminosity: &mut u8, +) { + // 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 + *luminosity = lux; +} diff --git a/src/rn_131rg.rs b/src/rn_131rg.rs new file mode 100644 index 0000000000000000000000000000000000000000..6f0d85fd0f70259d0eff84f4e647fea360e5e63f --- /dev/null +++ b/src/rn_131rg.rs @@ -0,0 +1,116 @@ +use stm32l4xx_hal::gpio::*; +use stm32l4xx_hal::rcc::*; +use stm32l4xx_hal::serial::*; +use stm32l4xx_hal::stm32::*; +use stm32l4xx_hal::prelude::*; +use core::fmt::Arguments; +// use cortex_m_semihosting::hprint; +// use core::str::from_utf8; +use core::fmt::Write; +use cortex_m::asm; +pub struct RnCon { + pub tx: Tx<stm32l4xx_hal::pac::UART4>, + pub rx: Rx<stm32l4xx_hal::pac::UART4>, +} + +pub fn get_connexion( + tx: Pin<Alternate<PushPull, 8>, L8, 'A', 0>, + rx: Pin<Alternate<PushPull, 8>, L8, 'A', 1>, + uart4: UART4, + apb1r1: &mut APB1R1, + clocks: Clocks, +) -> RnCon { + + // create connexion struct + + let (uart_tx, uart_rx) = Serial::uart4( + uart4, + (tx, rx), + 57600.bps(),// 9600.bps(), + clocks, + apb1r1, + ) + .split(); + + let mut uart = RnCon { tx: uart_tx, rx: uart_rx }; + + + // check communication between microcontroller and wifi module + + const BUFFER_SIZE: usize = 3; + let mut buffer: [u8; BUFFER_SIZE] = [0; BUFFER_SIZE]; + + send(&mut uart, "$$$"); + wait_serial(&mut uart, &mut buffer, BUFFER_SIZE); + assert_eq!(buffer, "CMD".as_bytes()); + + send(&mut uart, "set comm remote 0\r\n"); // necessary to avoid "*HELLO*" string at connexion start + asm::delay(1000000); + send(&mut uart, "apmode RustWeatherStation 5\r\n"); + asm::delay(1000000); + send(&mut uart, "exit\r\n"); + // + + return uart; +} + +pub fn send(con: &mut RnCon, s: &str) { + con.tx.write_str(s).unwrap(); +} + +pub fn send_fmt(con: &mut RnCon, args: Arguments<'_>) { + con.tx.write_fmt(args).unwrap(); +} + +// pub fn print(buffer: &[u8]) { +// hprint!("{}", from_utf8(buffer).unwrap()); +// } + + +/// read on the uart connexion +/// +pub fn wait_serial(con: &mut RnCon, buffer: &mut [u8], buffer_size: usize) { + let mut i = 0; // char index + + loop { + match con.rx.read() { + Ok(x) => { + buffer[i] = x; + + if i > 0 && buffer[i - 1] == 13 && buffer[i] == 10 { + // end of message, we flush + break; + } + i += 1; + + if i >= buffer_size { + break; + } + } + Err(_x) => {} + } + } +} + +pub fn wait_http(con: &mut RnCon, buffer: &mut [u8], buffer_size: usize) { + let mut i = 0; // char index + + loop { + match con.rx.read() { + Ok(x) => { + buffer[i] = x; + + if i > 3 && buffer[i - 3] == 13 && buffer[i] == 10 { + // end of message, we flush + break; + } + i += 1; + + if i >= buffer_size { + break; + } + } + Err(_x) => {} + } + } +}