diff --git a/lib/sensor_mlx90614.c b/lib/sensor_mlx90614.c new file mode 100644 index 0000000..d079c2e --- /dev/null +++ b/lib/sensor_mlx90614.c @@ -0,0 +1,86 @@ +/** 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 +} diff --git a/lib/sensor_mlx90614.h b/lib/sensor_mlx90614.h new file mode 100644 index 0000000..7966c18 --- /dev/null +++ b/lib/sensor_mlx90614.h @@ -0,0 +1,27 @@ +/** 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 + */ +#pragma once + +/** setup I²C bus to communicated with MLX90614 Infra-Red thermometer + * @param[in] slave_addr I²C slave address of MLX90614 device (least significant 7-bit, 0xff for default) + * @return if the display setup is successful, else the display is probably not on the I²C bus + * @warning only one display on the I²C bus is currently supported + * @note I²C frequency is 100 kHz + */ +bool sensor_mlx90614_setup(uint8_t slave_addr); +/** get ambient temperature of sensor + * @return ambient temperature in °C + * @note uses internal thermistor + */ +float sensor_mlx90614_temperature_ambient(void); +/** get measured object temperature + * @return object temperature in °C + * @note uses IR sensor + */ +float sensor_mlx90614_temperature_object(void);