From dea2a2addb719e6096233b831d10b58776254619 Mon Sep 17 00:00:00 2001 From: Petr Malanik Date: Mon, 6 Mar 2023 15:26:18 +0100 Subject: [PATCH] Sun_sensor:FW add new class MLX75306 which could read data from photo sensor --- modules/Sun_sensor/fw/MLX75306/MLX75306.cpp | 78 ++++++++ modules/Sun_sensor/fw/MLX75306/MLX75306.hpp | 186 ++++++++++++++++++++ 2 files changed, 264 insertions(+) create mode 100644 modules/Sun_sensor/fw/MLX75306/MLX75306.cpp create mode 100644 modules/Sun_sensor/fw/MLX75306/MLX75306.hpp diff --git a/modules/Sun_sensor/fw/MLX75306/MLX75306.cpp b/modules/Sun_sensor/fw/MLX75306/MLX75306.cpp new file mode 100644 index 0000000..8d888d7 --- /dev/null +++ b/modules/Sun_sensor/fw/MLX75306/MLX75306.cpp @@ -0,0 +1,78 @@ +#include "MLX75306.hpp" + +MLX75306::MLX75306(SPI_HandleTypeDef SPI_handle, Chip_select_pin SPI_CS) : + SPI_handle(SPI_handle), SPI_CS(SPI_CS){ } + +void MLX75306::Init(){ + Reset(); +} + +void MLX75306::Reset(){ + Command(Commands::Chip_reset, { 0, 0 }); +} + +void MLX75306::Wake_up(){ + Command(Commands::Wake_up, { 0, 0 }); +} + +void MLX75306::Zebra_pattern_1(){ + Command(Commands::Test_zebra_pattern_1, { 0, 0 }); +} + +array MLX75306::Read_all_8bit(){ + // Command is set to read all pixels + return Command<159>(Commands::Read_out_8b, { 0x02, 0x8f }); +} + +void MLX75306::Integrate(double time_us){ + const unsigned int f_RCO = 10000000; + const double min_time_us = 0.1; + const double max_time_us = 100000; + int64_t integration_register = 0; + + // Cap values + if (time_us < min_time_us) { + time_us = min_time_us; + } + if (time_us > max_time_us) { + time_us = max_time_us; + } + + // Calculate value for short integration + integration_register = ((time_us / 1000000) * f_RCO) + 4; + + // If integration time is longer then maximal short integration time used long integration command + if (integration_register > ((1 << 16) - (11 * 16))) { + integration_register = (((time_us / 1000000) * f_RCO) - 11) / 16; + + + // Handle overflow and underflow of integration register + if (integration_register > (1 << 16)) { + integration_register = (1 << 16) - 1; + } else if (integration_register < 0) { + integration_register = 1; + } + // Long integration + Command(Commands::Start_integration_long, + { + static_cast((integration_register & 0xff00) >> 8), + static_cast(integration_register & 0xff) + } + ); + } else { + // Short integration + Command(Commands::Start_integration, + { + static_cast((integration_register & 0xff00) >> 8), + static_cast(integration_register & 0xff) + } + ); + } +} // MLX75306::Integrate + +MLX75306::Status_byte MLX75306::Status(){ + array payload = { 0, 0 }; + auto status = Command(Commands::Idle, payload)[0]; + Status_byte status_struct = *((Status_byte *) &(status)); + return status_struct; +} diff --git a/modules/Sun_sensor/fw/MLX75306/MLX75306.hpp b/modules/Sun_sensor/fw/MLX75306/MLX75306.hpp new file mode 100644 index 0000000..729bcd3 --- /dev/null +++ b/modules/Sun_sensor/fw/MLX75306/MLX75306.hpp @@ -0,0 +1,186 @@ +/** + * @file SPI_camera.hpp + * @author Petr MalanĂ­k (TheColonelYoung(at)gmail(dot)com) + * @brief + * @version 0.1 + * @date 1.03.2023 + */ + +#pragma once + +#include "stm32l4xx_hal.h" + +#include + +using namespace std; +typedef unsigned int uint; + +/** + * @brief Linear optical sensor array, including a 142 x 1 array of photodiodes + * associated charge amplifier circuitry and a pixel data-hold function that + * provides simultaneous integration start and stop times for all pixels. + */ +class MLX75306 +{ +public: + + /** + * @brief Describes GPIO which serves as SPI chip select pin + */ + struct Chip_select_pin { + GPIO_TypeDef *port; + uint16_t pin; + }; + + /** + * @brief Command for MLX75306, whole command composes of 3 bytes, first byte is from enum below + * Other two bytes are payload which could be command specific or empty + */ + enum class Commands: uint8_t { + Idle = 0b00000000, + Chip_reset = 0b11110000, + Read_thresholds = 0b11011000, + Write_thresholds = 0b11001100, + Start_integration = 0b10111000, + Start_integration_long = 0b10110100, + Read_out_1b = 0b10011100, + Read_out_1b5 = 0b10010110, + Read_out_4b = 0b10010011, + Read_out_8b = 0b10011001, + Test_zebra_pattern_1 = 0b11101000, + Test_zebra_pattern_2 = 0b11100100, + Test_zebra_pattern_12 = 0b11100010, + Test_zebra_pattern_0 = 0b11100001, + Sleep_mode = 0b11000110, + Wake_up = 0b11000011, + }; + + /** + * @brief Structure of MLX75306 status byte + */ + struct __attribute__((packed)) __attribute__((__may_alias__)) Status_byte{ + uint8_t command_counter : 5; // Counter of valid commands + uint8_t device_mode : 1; // Device mode: 0-Test 1-User + uint8_t power_up_in_progress : 1; // Set after first Chip_reset command, clear after power-up + uint8_t operational_mode : 1; // Device operational mode: 0-Sleep 1-Normal + }; + +protected: + + /* + * @brief HAL handle of SPI to which is ArduChip connected + */ + SPI_HandleTypeDef SPI_handle; + + /** + * @brief GPIO description which serves as SPI Chip select + */ + Chip_select_pin SPI_CS; + +public: + + /** + * @brief Construct a new MLX75306 object + * + * @param SPI_handle HAL handle of SPI to which is sensor connected + * @param SPI_CS GPIO description which serves as SPI Chip select + */ + MLX75306(SPI_HandleTypeDef SPI_handle, Chip_select_pin SPI_CS); + + /** + * @brief Initialize sensor by reset + */ + void Init(); + + /** + * @brief Reset sensor, reset must be done after power-up, reset all registers + */ + void Reset(); + + /** + * @brief Change operational mode of sensor to Normal + * During normal mode an integration and readout could be performed. + */ + void Wake_up(); + + /** + * @brief Change operational mode of sensor to Sleep + * During sleep mode an integration and readout could not be performed. + * But power draw of sensor is reduced. + */ + void Sleep_mode(); + + /** + * @brief Sensor will charge photodiodes to defined levels to create test pattern + * Every odd pixel is charged to high level of charge, even pixel to low level of charge + * This command is used instead of Integration start + */ + void Zebra_pattern_1(); + + /** + * @brief Time in micro second to integrate charge photodiodes + * The shortest time is 0.1 us, the longest is 100 ms, values above or below are capped + */ + void Integrate(double time_us); + + /** + * @brief Reads Status byte of sensor by using Idle command + * + * @return Status_byte Structured status byte of sensor + */ + Status_byte Status(); + + /** + * @brief Perform readout of all output registers and pixels from sensor + * + * @return array Output registers of sensor containing metadata and pixels + */ + array Read_all_8bit(); + + +protected: + + /** + * @brief Send command to sensor and return answer (mostly status byte and empty bytes) + * + * @tparam array_size Size of returned answer on bytes, default is 3 + * @param command Command from available Commands of sensor + * @param payload Payload (parameters) of command, mostly empty but could contain integration time, atc. + * @return array Answer to command, example: status byte, readout bytes, etc. + */ + template + array Command(Commands command, array payload){ + array byte_stream = { 0 }; + byte_stream[0] = (uint8_t) command; + byte_stream[1] = payload[0]; + byte_stream[2] = payload[1]; + + return Transmit_and_receive(byte_stream); + } + + /** + * @brief Transmit bytes to sensor and receive answer + * + * @tparam array_size Amount of bytes to transfer and receive + * @param byte_stream Array of bytes to transmit + * @return array Array of received bytes + */ + template + array Transmit_and_receive(array &byte_stream){ + array received = { 0 }; + CS_enable(); + HAL_SPI_TransmitReceive(&SPI_handle, byte_stream.data(), received.data(), byte_stream.size(), byte_stream.size()); + CS_disable(); + return received; + } + + /** + * @brief Enables communication with sensor via SPI, CS signal is active low + */ + void CS_enable(){ HAL_GPIO_WritePin(SPI_CS.port, SPI_CS.pin, GPIO_PIN_RESET); }; + + /** + * @brief Disables communication with sensor via SPI, CS signal is active low + */ + void CS_disable(){ HAL_GPIO_WritePin(SPI_CS.port, SPI_CS.pin, GPIO_PIN_SET); }; +};