/** library to communicate with MLX90614 infra-red thermometer * @file * @author King Kévin * @copyright SPDX-License-Identifier: GPL-3.0-or-later * @date 2020 * @note peripherals used: GPIO @ref sensor_mlx90614_gpio, I2C @ref i2c_master_i2c * @note this library only uses the I²C interface, not the PWM output */ /* standard libraries */ #include // standard integer types #include // boolean type #include // general utilities #include // NaN definition /* STM32 (including CM3) libraries */ #include // real-time control clock library #include // general purpose input output library #include // I²C library /* own libraries */ #include "global.h" // global utilities #include "sensor_mlx90614.h" // own definitions #include "smbus_master.h" // SMBus utilities /** @defgroup sensor_mlx90614_gpio GPIO pin used to control the MLX90614 * @{ */ #define SENSOR_MLX90614_SCL PA8 /**< GPIO pin for SMBus SCL, use to send request condition to switch mode from PWM to SMBus (must be the same as used for SMBus communication) */ /** @} */ /** MLX90614 I²C slave address */ static uint8_t sensor_mlx90614_slave_addr = 0x5a; bool sensor_mlx90614_setup(uint8_t slave_addr) { if (!smbus_master_check_signals()) { // check if there are pull-ups to operate I²C return false; } // send request condition to switch to I²C mode rcc_periph_clock_enable(GPIO_RCC(SENSOR_MLX90614_SCL)); // enable clock for GPIO port domain gpio_mode_setup(GPIO_PORT(SENSOR_MLX90614_SCL), GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN(SENSOR_MLX90614_SCL)); // set pin as output gpio_set_output_options(GPIO_PORT(SENSOR_MLX90614_SCL), GPIO_OTYPE_OD, GPIO_OSPEED_2MHZ, GPIO_PIN(SENSOR_MLX90614_SCL)); // set pin output as open-drain (should be pulled up by external resistors required for I²C communication gpio_clear(GPIO_PORT(SENSOR_MLX90614_SCL), GPIO_PIN(SENSOR_MLX90614_SCL)); // set low to send request condition sleep_us(1024 + 500); // SMBus Request is min. 1.024 ms gpio_set(GPIO_PORT(SENSOR_MLX90614_SCL), GPIO_PIN(SENSOR_MLX90614_SCL)); // set back high to end request condition if (slave_addr < 0x80) { // only the least 7-bit are valid (use default address else) sensor_mlx90614_slave_addr = slave_addr; // save I²C slave address of MLX90614 } smbus_master_setup(100, true); // setup SMBus with PEC uint8_t response[2]; // response is always 2 bytes // test if the device is present smbus_master_command_read(sensor_mlx90614_slave_addr, 0x20 | 0x0e, response, LENGTH(response)); // I don't know whey, but the PEC for the first transfer is erroneous, maybe because it has some old bits in the calculation buffer const enum smbus_master_rc rc = smbus_master_command_read(sensor_mlx90614_slave_addr, 0x20 | 0x0e, response, LENGTH(response)); // read its own slave address if (SMBUS_MASTER_RC_NONE != rc) { // read failed return false; // could not read from SMBus device } if (sensor_mlx90614_slave_addr != response[0]) { // the used slave address does not match the one in the ROM return false; // this is probably not a MLX90614 } return true; } float sensor_mlx90614_temperature_ambient(void) { uint8_t response[2]; // response is always 2 bytes const enum smbus_master_rc rc = smbus_master_command_read(sensor_mlx90614_slave_addr, 0x00 | 0x06, response, LENGTH(response)); // read Ta from RAM if (SMBUS_MASTER_RC_NONE != rc) { // read failed return NAN; } const uint16_t temp = response[0] + (response[1] << 8); // low byte is transferred first return temp * 0.02 - 273.15; // calculated temperature according to specification 7.7.1 } float sensor_mlx90614_temperature_object(void) { uint8_t response[2]; // response is always 2 bytes const enum smbus_master_rc rc = smbus_master_command_read(sensor_mlx90614_slave_addr, 0x00 | 0x07, response, LENGTH(response)); // read Tobj1 from RAM if (SMBUS_MASTER_RC_NONE != rc) { // read failed return NAN; } const uint16_t temp = response[0] + (response[1] << 8); // low byte is transferred first return temp * 0.02 - 273.15; // calculated temperature according to specification 7.7.1 }