/** * ENSICAEN * 6 Boulevard Maréchal Juin * F-14050 Caen Cedex * * This file is owned by ENSICAEN students. No portion of this * document may be reproduced, copied or revised without written * permission of the authors. */ /** * @author Dimitri Boudier * @author * @author * @version 1.0.0 - 2023-07-23 * * @todo Nothing. * @bug None. * @note This driver can be used with only ONE Weather click at once. */ /** * @file weather_click.c * @brief Driver for the MIKROE "Weather click" board * * Ref. https://www.mikroe.com/weather-click */ #include "main.h" #include "weather_click.h" /***************************************************************************** * GLOBAL VARIABLE DEFINITIONS *****************************************************************************/ //! UART2 peripheral handle for debug purpose extern UART_HandleTypeDef huart2; //! Declared as global in order to be seen by all the driver functions. WEATHER_handle_t weather_handle; CalibParams_t calib; /***************************************************************************** * STATIC FUNCTION DECLARATIONS *****************************************************************************/ /** * Reads the calibration parameters from the Weather click memory * * Fills the field of a CalibParams_t struct declared as a global variable * * Ref. BME280 datasheet - Section 4.2.2 Trimming parameter readout */ static void WEATHER_getCalibParams(void); /** * Compensates the device temperature value with the device calibration parameters * * @param[in] raw_temp raw temperature value, output of the weather click registers * @param[out] fine_temp pointer to the temperature value after calibration * * The fine_temp is a 32-bit signed integer, expressed in 0.01 degC. */ static void WEATHER_compensateTemp(int32_t raw_temp, int32_t* fine_temp); /** * Compensates the device pressure value with the device calibration parameters * * @param[in] raw_press raw pressure value, output of the weather click registers * @param[out] fine_press pointer to the pressure value after calibration * * Returns pressure in Pa as unsigned 32 bit integer in Q24.8 format * (24 integer bits and 8 fractional bits). * Output value of “24674867” represents 24674867/256 = 96386.2 Pa = 963.862 hPa * * @note This compensation needs the temperature compensation first */ static void WEATHER_compensatePress(uint32_t raw_press, uint32_t* fine_press); /** * Compensates the device humidity value with the device calibration parameters * * @param[in] raw_hum raw humidity value, output of the weather click registers * @param[out] fine_hum pointer to the humidity value after calibration * * Returns humidity in %RH as unsigned 32 bit integer in Q22.10 format * (22 integer and 10 fractional bits). * Output value of “47445” represents 47445/1024 = 46.333 %RH * * @note This compensation needs the temperature compensation first */ static void WEATHER_compensateHum(uint32_t raw_hum, uint32_t* fine_hum); /***************************************************************************** * FUNCTION DEFINITIONS *****************************************************************************/ void WEATHER_init(I2C_HandleTypeDef *hi2c) { uint8_t reply; /** * @TODO (1) Weather handle initialization * - Attach the I2C peripheral handle to the Weather handle */ weather_handle.hi2c = hi2c; /** * @TODO (2) Weather click identification * Read the id register. If the received value is wrong: * - send an error message to UART2 * - stay blocked in this function (waiting for the user to reset the MCU) */ WEATHER_readReg(WEATHER_REG_ID, &reply, 1); if( reply != WEATHER_CHIP_ID ) { HAL_UART_Transmit(&huart2, (uint8_t*)"Failed to identify Weather\n\r", 30, HAL_MAX_DELAY); while(1); //!< Trap if device not found. User must reset the MCU } /** * @TODO (3) Weather click configuration * - measure the pressure, temperature and humidity * - no oversampling at all * - measures must be performed every 500 ms * - no IIR filter */ /** * @warning from datasheet: * Writes to the “config” register in normal mode may be ignored. * In sleep mode writes are not ignored. */ WEATHER_writeReg(WEATHER_REG_CONFIG, WEATHER_STANDBY_500ms + WEATHER_IIR_OFF + WEATHER_SPI_3WIRE_OFF); /** * @warning from datasheet: * Changes to this register only become effective after a write operation to “ctrl_meas”. */ WEATHER_writeReg(WEATHER_REG_CTRL_HUM, WEATHER_HUM_OVERSAMPLEx1); WEATHER_writeReg(WEATHER_REG_CTRL_MEAS, WEATHER_TEMP_OVERSAMPLEx1 + WEATHER_PRESS_OVERSAMPLEx1 + WEATHER_MODE_NORMAL); /** * @TODO (4) Weather click calibration parameters * Read the calibration parameters, which are unique to every single BME280 */ WEATHER_getCalibParams(); } void WEATHER_readReg(uint8_t reg_addr, uint8_t* reg_value, uint8_t n_bytes) { //! @TODO HAL_I2C_Master_Transmit(weather_handle.hi2c, WEATHER_I2C_ADDR, ®_addr, 1, HAL_MAX_DELAY); HAL_I2C_Master_Receive( weather_handle.hi2c, WEATHER_I2C_ADDR, reg_value, n_bytes, HAL_MAX_DELAY); } void WEATHER_writeReg(uint8_t reg_addr, uint8_t reg_value) { //! @TODO uint8_t sent_data[2]; sent_data[0] = reg_addr; sent_data[1] = reg_value; HAL_I2C_Master_Transmit(weather_handle.hi2c, WEATHER_I2C_ADDR, sent_data, 2, HAL_MAX_DELAY); } void WEATHER_getTemperature(float* temp_degC) { //! @TODO uint8_t reg_values[3]; int32_t temp_raw, temp_fine; WEATHER_readReg(WEATHER_REG_TEMP, reg_values, 3); temp_raw = ((int32_t)reg_values[0] << 12) + ((uint32_t)reg_values[1] << 4) + ((uint32_t)reg_values[2] >> 4); if( temp_raw == WEATHER_TEMP_MEAS_SKIPPED ) { *temp_degC = 0.0; return; } WEATHER_compensateTemp(temp_raw, &temp_fine); *temp_degC = (float)(temp_fine); *temp_degC /= 100.0; } void WEATHER_getPressure(float* press_hPa) { //! @TODO uint8_t reg_values[3]; uint32_t press_raw, press_fine; WEATHER_readReg(WEATHER_REG_PRESS, reg_values, 3); press_raw = ((uint32_t)reg_values[0] << 12) + ((uint32_t)reg_values[1] << 4) + ((uint32_t)reg_values[2] >> 4); if(press_raw == WEATHER_PRESS_MEAS_SKIPPED) { *press_hPa = 0.0; return; } WEATHER_compensatePress(press_raw, &press_fine); *press_hPa = (float)press_fine; *press_hPa /= 256.0; *press_hPa /= 100.0; } void WEATHER_getHumidity(float* hum_rh) { //! @TODO uint8_t reg_values[2]; uint32_t hum_raw, hum_fine; WEATHER_readReg(WEATHER_REG_HUM, reg_values, 2); hum_raw = (reg_values[0] << 8) + reg_values[1]; if(hum_raw == WEATHER_TEMP_MEAS_SKIPPED) { *hum_rh = 0; return; } WEATHER_compensateHum(hum_raw, &hum_fine); *hum_rh = (float)hum_fine; *hum_rh /= 1024; } void WEATHER_getAll(float* press_hPa, float* temp_degC, float* hum_rh) { //! @TODO WEATHER_getTemperature(temp_degC); WEATHER_getPressure(press_hPa); WEATHER_getHumidity(hum_rh); } /***************************************************************************** * STATIC FUNCTION DEFINITIONS *****************************************************************************/ static void WEATHER_getCalibParams(void) { uint8_t calib_buf[26]; WEATHER_readReg(WEATHER_REG_CAL_T, calib_buf, 26); calib.dig_T1 = (uint16_t)(calib_buf[ 1] << 8) + calib_buf[ 0]; calib.dig_T2 = (int16_t) (calib_buf[ 3] << 8) + calib_buf[ 2]; calib.dig_T3 = (int16_t) (calib_buf[ 5] << 8) + calib_buf[ 4]; calib.dig_P1 = (uint16_t)(calib_buf[ 7] << 8) + calib_buf[ 6]; calib.dig_P2 = (int16_t) (calib_buf[ 9] << 8) + calib_buf[ 8]; calib.dig_P3 = (int16_t) (calib_buf[11] << 8) + calib_buf[10]; calib.dig_P4 = (int16_t) (calib_buf[13] << 8) + calib_buf[12]; calib.dig_P5 = (int16_t) (calib_buf[15] << 8) + calib_buf[14]; calib.dig_P6 = (int16_t) (calib_buf[17] << 8) + calib_buf[16]; calib.dig_P7 = (int16_t) (calib_buf[19] << 8) + calib_buf[18]; calib.dig_P8 = (int16_t) (calib_buf[21] << 8) + calib_buf[20]; calib.dig_P9 = (int16_t) (calib_buf[23] << 8) + calib_buf[22]; calib.dig_H1 = (uint8_t) (calib_buf[25]); WEATHER_readReg(WEATHER_REG_CAL_H, calib_buf, 7); calib.dig_H2 = (int16_t) (calib_buf[ 1] << 8) + calib_buf[ 0]; calib.dig_H3 = (uint8_t) (calib_buf[ 2]); calib.dig_H4 = (int16_t) (calib_buf[ 3] << 4) + (calib_buf[ 4] & 0x0F); calib.dig_H5 = (int16_t) (calib_buf[ 5] << 4) + (calib_buf[ 4] >> 4); calib.dig_H6 = (int8_t) (calib_buf[ 6]); } static void WEATHER_compensatePress(uint32_t raw_press, uint32_t* fine_press) { int64_t var1, var2, p; if( raw_press == WEATHER_PRESS_MEAS_SKIPPED ) { *fine_press = WEATHER_PRESS_MEAS_SKIPPED; return; } //! See "4.2.3 Compensation formulas" of the BME280 datasheet var1 = ((int64_t)calib.t_fine) - 128000; var2 = var1 * var1 * (int64_t)calib.dig_P6; var2 = var2 + ((var1*(int64_t)calib.dig_P5)<<17); var2 = var2 + (((int64_t)calib.dig_P4)<<35); var1 = ((var1 * var1 * (int64_t)calib.dig_P3)>>8) + ((var1 * (int64_t)calib.dig_P2)<<12); var1 = (((((int64_t)1)<<47)+var1))*((int64_t)calib.dig_P1)>>33; if (var1 == 0) { *fine_press = WEATHER_PRESS_MEAS_SKIPPED; return; // avoid exception caused by division by zero } p = 1048576-raw_press; p = (((p<<31)-var2)*3125)/var1; var1 = (((int64_t)calib.dig_P9) * (p>>13) * (p>>13)) >> 25; var2 = (((int64_t)calib.dig_P8) * p) >> 19; p = ((p + var1 + var2) >> 8) + (((int64_t)calib.dig_P7)<<4); *fine_press = (uint32_t)p; } static void WEATHER_compensateTemp(int32_t raw_temp, int32_t* fine_temp) { int32_t var1, var2; if( raw_temp == WEATHER_TEMP_MEAS_SKIPPED ) { *fine_temp = WEATHER_TEMP_MEAS_SKIPPED; return; } //! See "4.2.3 Compensation formulas" of the BME280 datasheet var1 = ((((raw_temp>>3) - ((int32_t)calib.dig_T1<<1))) * ((int32_t)calib.dig_T2)) >> 11; var2 = (((((raw_temp>>4) - ((int32_t)calib.dig_T1)) * ((raw_temp>>4) - ((int32_t)calib.dig_T1))) >> 12) * ((int32_t)calib.dig_T3)) >> 14; calib.t_fine = (var1 + var2); *fine_temp = (calib.t_fine * 5 + 128) >> 8; } static void WEATHER_compensateHum(uint32_t raw_hum, uint32_t* fine_hum) { int32_t v_x1_u32r; if( raw_hum == WEATHER_HUM_MEAS_SKIPPED ) { *fine_hum = WEATHER_HUM_MEAS_SKIPPED; return; } //! See "4.2.3 Compensation formulas" of the BME280 datasheet v_x1_u32r = (calib.t_fine - ((int32_t)76800)); v_x1_u32r = (((((raw_hum << 14) - (((int32_t)calib.dig_H4) << 20) - (((int32_t)calib.dig_H5) * v_x1_u32r)) + ((int32_t)16384)) >> 15) * (((((((v_x1_u32r * ((int32_t)calib.dig_H6)) >> 10) * (((v_x1_u32r * ((int32_t)calib.dig_H3)) >> 11) + ((int32_t)32768))) >> 10) + ((int32_t)2097152)) * ((int32_t)calib.dig_H2) + 8192) >> 14)); v_x1_u32r = (v_x1_u32r - (((((v_x1_u32r >> 15) * (v_x1_u32r >> 15)) >> 7) * ((int32_t)calib.dig_H1)) >> 4)); v_x1_u32r = (v_x1_u32r < 0 ? 0 : v_x1_u32r); v_x1_u32r = (v_x1_u32r > 419430400 ? 419430400 : v_x1_u32r); *fine_hum = (uint32_t)(v_x1_u32r>>12); }