diff --git a/src/drivers/distance_sensor/broadcom/afbrs50/AFBRS50.cpp b/src/drivers/distance_sensor/broadcom/afbrs50/AFBRS50.cpp index b66eeeba3d..4419530515 100644 --- a/src/drivers/distance_sensor/broadcom/afbrs50/AFBRS50.cpp +++ b/src/drivers/distance_sensor/broadcom/afbrs50/AFBRS50.cpp @@ -33,6 +33,7 @@ /* Include Files */ #include "AFBRS50.hpp" +#include "argus_hal_test.h" #include @@ -42,9 +43,6 @@ /*! Define the SPI baud rate (to be used in the SPI module). */ #define SPI_BAUD_RATE 5000000 -#define LONG_RANGE_MODE_HZ 25 -#define SHORT_RANGE_MODE_HZ 50 - #include "s2pi.h" #include "timer.h" #include "argus_hal_test.h" @@ -52,6 +50,7 @@ AFBRS50 *g_dev{nullptr}; AFBRS50::AFBRS50(uint8_t device_orientation): + ModuleParams(nullptr), ScheduledWorkItem(MODULE_NAME, px4::wq_configurations::hp_default), _px4_rangefinder(0, device_orientation) { @@ -61,6 +60,7 @@ AFBRS50::AFBRS50(uint8_t device_orientation): device_id.devid_s.devtype = DRV_DIST_DEVTYPE_AFBRS50; _px4_rangefinder.set_device_id(device_id.devid); + _px4_rangefinder.set_device_type(distance_sensor_s::MAV_DISTANCE_SENSOR_LASER); } AFBRS50::~AFBRS50() @@ -70,12 +70,12 @@ AFBRS50::~AFBRS50() perf_free(_sample_perf); } -status_t AFBRS50::measurement_ready_callback(status_t status, void *data) +status_t AFBRS50::measurement_ready_callback(status_t status, argus_hnd_t *hnd) { if (!up_interrupt_context()) { if (status == STATUS_OK) { if (g_dev) { - g_dev->ProcessMeasurement(data); + g_dev->ProcessMeasurement(hnd); } } else { @@ -86,35 +86,33 @@ status_t AFBRS50::measurement_ready_callback(status_t status, void *data) return status; } -void AFBRS50::ProcessMeasurement(void *data) +void AFBRS50::ProcessMeasurement(argus_hnd_t *hnd) { - if (data != nullptr) { - perf_count(_sample_perf); + perf_count(_sample_perf); - argus_results_t res{}; - status_t evaluate_status = Argus_EvaluateData(_hnd, &res, data); + argus_results_t res{}; + status_t evaluate_status = Argus_EvaluateData(hnd, &res); - if ((evaluate_status == STATUS_OK) && (res.Status == STATUS_OK)) { - uint32_t result_mm = res.Bin.Range / (Q9_22_ONE / 1000); - float result_m = static_cast(result_mm) / 1000.f; - int8_t quality = res.Bin.SignalQuality; + if ((evaluate_status == STATUS_OK) && (res.Status == STATUS_OK)) { + uint32_t result_mm = res.Bin.Range / (Q9_22_ONE / 1000); + float result_m = static_cast(result_mm) / 1000.f; + int8_t quality = res.Bin.SignalQuality; - // Signal quality indicates 100% for good signals, 50% and lower for weak signals. - // 1% is an errored signal (not reliable). Signal Quality of 0% is unknown. - if (quality == 1) { - quality = 0; - } - - // distance quality check - if (result_m > _max_distance) { - result_m = 0.0; - quality = 0; - } - - _current_distance = result_m; - _current_quality = quality; - _px4_rangefinder.update(((res.TimeStamp.sec * 1000000ULL) + res.TimeStamp.usec), result_m, quality); + // Signal quality indicates 100% for good signals, 50% and lower for weak signals. + // 1% is an errored signal (not reliable). Signal Quality of 0% is unknown. + if (quality == 1) { + quality = 0; } + + // distance quality check + if (result_m > _max_distance) { + result_m = 0.0; + quality = 0; + } + + _current_distance = result_m; + _current_quality = quality; + _px4_rangefinder.update(((res.TimeStamp.sec * 1000000ULL) + res.TimeStamp.usec), result_m, quality); } } @@ -137,7 +135,37 @@ int AFBRS50::init() // Initialize the S2PI hardware required by the API. S2PI_Init(BROADCOM_AFBR_S50_S2PI_SPI_BUS, SPI_BAUD_RATE); - status_t status = Argus_Init(_hnd, BROADCOM_AFBR_S50_S2PI_SPI_BUS); + int32_t mode_param = _p_sens_afbr_mode.get(); + + if (mode_param < 0 || mode_param > 3) { + PX4_ERR("Invalid mode parameter: %li", mode_param); + return PX4_ERROR; + } + + argus_mode_t mode = ARGUS_MODE_LONG_RANGE; + + switch (mode_param) { + case 0: + mode = ARGUS_MODE_SHORT_RANGE; + break; + + case 1: + mode = ARGUS_MODE_LONG_RANGE; + break; + + case 2: + mode = ARGUS_MODE_HIGH_SPEED_SHORT_RANGE; + break; + + case 3: + mode = ARGUS_MODE_HIGH_SPEED_LONG_RANGE; + break; + + default: + break; + } + + status_t status = Argus_InitMode(_hnd, BROADCOM_AFBR_S50_S2PI_SPI_BUS, mode); if (status == STATUS_OK) { uint32_t id = Argus_GetChipID(_hnd); @@ -148,7 +176,6 @@ int AFBRS50::init() PX4_INFO_RAW("AFBR-S50 Chip ID: %u, API Version: %u v%d.%d.%d\n", (uint)id, (uint)value, a, b, c); argus_module_version_t mv = Argus_GetModuleVersion(_hnd); - argus_laser_type_t lt = Argus_GetLaserType(_hnd); switch (mv) { case AFBR_S50MV85G_V1: @@ -168,19 +195,20 @@ int AFBRS50::init() case AFBR_S50LV85D_V1: _min_distance = 0.08f; - - if (lt == LASER_H_V2X) { - _max_distance = 50.f; - PX4_INFO_RAW("AFBR-S50LX85D (v2)\n"); - - } else { - _max_distance = 30.f; - PX4_INFO_RAW("AFBR-S50LV85D (v1)\n"); - } - + _max_distance = 30.f; _px4_rangefinder.set_min_distance(_min_distance); _px4_rangefinder.set_max_distance(_max_distance); _px4_rangefinder.set_fov(math::radians(6.f)); + PX4_INFO_RAW("AFBR-S50LV85D\n"); + break; + + case AFBR_S50LX85D_V1: + _min_distance = 0.08f; + _max_distance = 50.f; + _px4_rangefinder.set_min_distance(_min_distance); + _px4_rangefinder.set_max_distance(_max_distance); + _px4_rangefinder.set_fov(math::radians(6.f)); + PX4_INFO_RAW("AFBR-S50LX85D\n"); break; case AFBR_S50MV68B_V1: @@ -223,6 +251,9 @@ int AFBRS50::init() ScheduleDelayed(_measure_interval); return PX4_OK; + + } else { + PX4_ERR("Argus_InitMode failed: %ld", status); } return PX4_ERROR; @@ -230,6 +261,15 @@ int AFBRS50::init() void AFBRS50::Run() { + if (_parameter_update_sub.updated()) { + // clear update + parameter_update_s param_update; + _parameter_update_sub.copy(¶m_update); + + // update parameters from storage + ModuleParams::updateParams(); + } + switch (_state) { case STATE::TEST: { if (_testing) { @@ -243,7 +283,8 @@ void AFBRS50::Run() break; case STATE::CONFIGURE: { - status_t status = set_rate(SHORT_RANGE_MODE_HZ); + _current_rate = (uint32_t)_p_sens_afbr_s_rate.get(); + status_t status = set_rate(_current_rate); if (status != STATUS_OK) { PX4_ERR("CONFIGURE status not okay: %i", (int)status); @@ -251,24 +292,18 @@ void AFBRS50::Run() ScheduleNow(); } - status = Argus_SetConfigurationDFMMode(_hnd, ARGUS_MODE_B, DFM_MODE_8X); + status = Argus_SetConfigurationDFMMode(_hnd, DFM_MODE_8X); if (status != STATUS_OK) { PX4_ERR("Argus_SetConfigurationDFMMode status not okay: %i", (int)status); + _state = STATE::STOP; + ScheduleNow(); } - status = Argus_SetConfigurationDFMMode(_hnd, ARGUS_MODE_A, DFM_MODE_8X); + status = Argus_SetConfigurationSmartPowerSaveEnabled(_hnd, false); if (status != STATUS_OK) { - PX4_ERR("Argus_SetConfigurationDFMMode status not okay: %i", (int)status); - } - - // start in short range mode - _mode = ARGUS_MODE_B; - set_mode(_mode); - - if (status != STATUS_OK) { - PX4_ERR("CONFIGURE status not okay: %i", (int)status); + PX4_ERR("Argus_SetConfigurationSmartPowerSaveEnabled status not okay: %i", (int)status); ScheduleNow(); } else { @@ -288,7 +323,7 @@ void AFBRS50::Run() } } - UpdateMode(); + Evaluate_rate(); } break; @@ -306,49 +341,41 @@ void AFBRS50::Run() ScheduleDelayed(_measure_interval); } -void AFBRS50::UpdateMode() +void AFBRS50::Evaluate_rate() { - // only update mode if _current_distance is a valid measurement - if ((_current_distance > 0) && (_current_quality > 0)) { + // only update mode if _current_distance is a valid measurement and if the last rate switch was more than 1 second ago + if ((_current_distance > 0) && (_current_quality > 0) && ((hrt_absolute_time() - _last_rate_switch) > 1_s)) { - if ((_current_distance >= _long_range_threshold) && (_mode != ARGUS_MODE_A)) { - // change to long range mode - argus_mode_t mode = ARGUS_MODE_A; - status_t status = set_mode(mode); + status_t status = STATUS_OK; - if (status != STATUS_OK) { - PX4_ERR("set_mode status not okay: %i", (int)status); - } + if ((_current_distance >= (_p_sens_afbr_thresh.get() + _p_sens_afbr_hyster.get())) + && (_current_rate != (uint32_t)_p_sens_afbr_l_rate.get())) { - status = set_rate(LONG_RANGE_MODE_HZ); + _current_rate = (uint32_t)_p_sens_afbr_l_rate.get(); + status = set_rate(_current_rate); if (status != STATUS_OK) { PX4_ERR("set_rate status not okay: %i", (int)status); + + } else { + PX4_INFO("switched to long range rate: %i", (int)_current_rate); + _last_rate_switch = hrt_absolute_time(); } - status = set_rate(LONG_RANGE_MODE_HZ); + } else if ((_current_distance <= (_p_sens_afbr_thresh.get() - _p_sens_afbr_hyster.get())) + && (_current_rate != (uint32_t)_p_sens_afbr_s_rate.get())) { + + _current_rate = (uint32_t)_p_sens_afbr_s_rate.get(); + status = set_rate(_current_rate); if (status != STATUS_OK) { PX4_ERR("set_rate status not okay: %i", (int)status); - } - } else if ((_current_distance <= _short_range_threshold) && (_mode != ARGUS_MODE_B)) { - // change to short range mode - argus_mode_t mode = ARGUS_MODE_B; - status_t status = set_mode(mode); - - if (status != STATUS_OK) { - PX4_ERR("set_mode status not okay: %i", (int)status); - } - - status = set_rate(SHORT_RANGE_MODE_HZ); - - if (status != STATUS_OK) { - PX4_ERR("set_rate status not okay: %i", (int)status); + } else { + PX4_INFO("switched to short range rate: %i", (int)_current_rate); + _last_rate_switch = hrt_absolute_time(); } } - - ScheduleDelayed(1000_ms); // don't switch again for at least 1 second } } @@ -373,33 +400,6 @@ void AFBRS50::print_info() get_info(); } -status_t AFBRS50::set_mode(argus_mode_t mode) -{ - while (Argus_GetStatus(_hnd) != STATUS_IDLE) { - px4_usleep(1_ms); - } - - status_t status = Argus_SetConfigurationMeasurementMode(_hnd, mode); - - if (status != STATUS_OK) { - PX4_ERR("Argus_SetConfigurationMeasurementMode status not okay: %i", (int)status); - return status; - } - - argus_mode_t current_mode; - status = Argus_GetConfigurationMeasurementMode(_hnd, ¤t_mode); - - if (status != STATUS_OK) { - PX4_ERR("Argus_GetConfigurationMeasurementMode status not okay: %i", (int)status); - return status; - - } else { - _mode = current_mode; - } - - return status; -} - status_t AFBRS50::set_rate(uint32_t rate_hz) { while (Argus_GetStatus(_hnd) != STATUS_IDLE) { @@ -429,13 +429,10 @@ status_t AFBRS50::set_rate(uint32_t rate_hz) void AFBRS50::get_info() { - argus_mode_t current_mode; argus_dfm_mode_t dfm_mode; - Argus_GetConfigurationMeasurementMode(_hnd, ¤t_mode); - Argus_GetConfigurationDFMMode(_hnd, current_mode, &dfm_mode); + Argus_GetConfigurationDFMMode(_hnd, &dfm_mode); PX4_INFO_RAW("distance: %.3fm\n", (double)_current_distance); - PX4_INFO_RAW("mode: %d\n", current_mode); PX4_INFO_RAW("dfm mode: %d\n", dfm_mode); PX4_INFO_RAW("rate: %u Hz\n", (uint)(1000000 / _measure_interval)); } diff --git a/src/drivers/distance_sensor/broadcom/afbrs50/AFBRS50.hpp b/src/drivers/distance_sensor/broadcom/afbrs50/AFBRS50.hpp index f7503b321b..2ad767b2fa 100644 --- a/src/drivers/distance_sensor/broadcom/afbrs50/AFBRS50.hpp +++ b/src/drivers/distance_sensor/broadcom/afbrs50/AFBRS50.hpp @@ -44,13 +44,16 @@ #include #include #include -#include #include +#include +#include #include +#include +#include using namespace time_literals; -class AFBRS50 : public px4::ScheduledWorkItem +class AFBRS50 : public ModuleParams, public px4::ScheduledWorkItem { public: AFBRS50(const uint8_t device_orientation = distance_sensor_s::ROTATION_DOWNWARD_FACING); @@ -75,18 +78,16 @@ public: private: void Run() override; - void UpdateMode(); + void Evaluate_rate(); - void ProcessMeasurement(void *data); + void ProcessMeasurement(argus_hnd_t *hnd); - static status_t measurement_ready_callback(status_t status, void *data); + static status_t measurement_ready_callback(status_t status, argus_hnd_t *hnd); void get_info(); - status_t set_mode(argus_mode_t mode); status_t set_rate(uint32_t rate_hz); argus_hnd_t *_hnd{nullptr}; - argus_mode_t _mode{ARGUS_MODE_B}; // Short-Range enum class STATE : uint8_t { TEST, @@ -98,14 +99,24 @@ private: PX4Rangefinder _px4_rangefinder; hrt_abstime _measurement_time{0}; + hrt_abstime _last_rate_switch{0}; perf_counter_t _sample_perf{perf_alloc(PC_INTERVAL, MODULE_NAME": sample interval")}; uint32_t _measure_interval{1000000 / 50}; // 50Hz float _current_distance{0}; int8_t _current_quality{0}; - const float _short_range_threshold = 4.0; //meters - const float _long_range_threshold = 6.0; //meters float _max_distance; float _min_distance; + uint32_t _current_rate{0}; + + uORB::Subscription _parameter_update_sub{ORB_ID(parameter_update)}; + + DEFINE_PARAMETERS( + (ParamInt) _p_sens_afbr_mode, + (ParamInt) _p_sens_afbr_s_rate, + (ParamInt) _p_sens_afbr_l_rate, + (ParamInt) _p_sens_afbr_thresh, + (ParamInt) _p_sens_afbr_hyster + ); }; diff --git a/src/drivers/distance_sensor/broadcom/afbrs50/API/Src/s2pi.c b/src/drivers/distance_sensor/broadcom/afbrs50/API/Src/s2pi.c index 61950a12b8..015249f859 100644 --- a/src/drivers/distance_sensor/broadcom/afbrs50/API/Src/s2pi.c +++ b/src/drivers/distance_sensor/broadcom/afbrs50/API/Src/s2pi.c @@ -85,6 +85,8 @@ static int gpio_falling_edge(int irq, void *context, void *arg) status_t S2PI_Init(s2pi_slave_t defaultSlave, uint32_t baudRate_Bps) { + (void)defaultSlave; + px4_arch_configgpio(BROADCOM_AFBR_S50_S2PI_CS); s2pi_.spidev = px4_spibus_initialize(BROADCOM_AFBR_S50_S2PI_SPI_BUS); @@ -107,11 +109,24 @@ status_t S2PI_Init(s2pi_slave_t defaultSlave, uint32_t baudRate_Bps) * - #STATUS_BUSY: An SPI transfer is in progress. * - #STATUS_S2PI_GPIO_MODE: The module is in GPIO mode. *****************************************************************************/ -status_t S2PI_GetStatus(void) +status_t S2PI_GetStatus(s2pi_slave_t slave) { + (void)slave; + return s2pi_.Status; } +status_t S2PI_TryGetMutex(s2pi_slave_t slave) +{ + (void) slave; + return STATUS_OK; +} + +void S2PI_ReleaseMutex(s2pi_slave_t slave) +{ + (void) slave; +} + /*!*************************************************************************** * @brief Sets the SPI baud rate in bps. * @param baudRate_Bps The default SPI baud rate in bauds-per-second. @@ -135,8 +150,10 @@ status_t S2PI_SetBaudRate(uint32_t baudRate_Bps) * switch back to ordinary SPI functionality. * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). *****************************************************************************/ -status_t S2PI_CaptureGpioControl(void) +status_t S2PI_CaptureGpioControl(s2pi_slave_t slave) { + (void)slave; + /* Check if something is ongoing. */ IRQ_LOCK(); status_t status = s2pi_.Status; @@ -165,8 +182,10 @@ status_t S2PI_CaptureGpioControl(void) * the #S2PI_CaptureGpioControl function. * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). *****************************************************************************/ -status_t S2PI_ReleaseGpioControl(void) +status_t S2PI_ReleaseGpioControl(s2pi_slave_t slave) { + (void)slave; + /* Check if something is ongoing. */ IRQ_LOCK(); status_t status = s2pi_.Status; @@ -202,6 +221,8 @@ status_t S2PI_ReleaseGpioControl(void) *****************************************************************************/ status_t S2PI_WriteGpioPin(s2pi_slave_t slave, s2pi_pin_t pin, uint32_t value) { + (void)slave; + /* Check if pin is valid. */ if (pin > S2PI_IRQ || value > 1) { return ERROR_INVALID_ARGUMENT; @@ -228,6 +249,8 @@ status_t S2PI_WriteGpioPin(s2pi_slave_t slave, s2pi_pin_t pin, uint32_t value) *****************************************************************************/ status_t S2PI_ReadGpioPin(s2pi_slave_t slave, s2pi_pin_t pin, uint32_t *value) { + (void)slave; + /* Check if pin is valid. */ if (pin > S2PI_IRQ || !value) { return ERROR_INVALID_ARGUMENT; @@ -255,6 +278,8 @@ status_t S2PI_ReadGpioPin(s2pi_slave_t slave, s2pi_pin_t pin, uint32_t *value) *****************************************************************************/ status_t S2PI_CycleCsPin(s2pi_slave_t slave) { + (void)slave; + /* Check the driver status. */ IRQ_LOCK(); status_t status = s2pi_.Status; @@ -372,8 +397,10 @@ status_t S2PI_TransferFrame(s2pi_slave_t spi_slave, uint8_t const *txData, uint8 * invoked with the #ERROR_ABORTED error byte. * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). *****************************************************************************/ -status_t S2PI_Abort(void) +status_t S2PI_Abort(s2pi_slave_t slave) { + (void)slave; + status_t status = s2pi_.Status; /* Check if something is ongoing. */ @@ -405,6 +432,8 @@ status_t S2PI_Abort(void) *****************************************************************************/ status_t S2PI_SetIrqCallback(s2pi_slave_t slave, s2pi_irq_callback_t callback, void *callbackData) { + (void)slave; + s2pi_.IrqCallback = callback; s2pi_.IrqCallbackData = callbackData; @@ -430,5 +459,7 @@ status_t S2PI_SetIrqCallback(s2pi_slave_t slave, s2pi_irq_callback_t callback, v *****************************************************************************/ uint32_t S2PI_ReadIrqPin(s2pi_slave_t slave) { + (void)slave; + return px4_arch_gpioread(s2pi_.GPIOs[S2PI_IRQ]); } diff --git a/src/drivers/distance_sensor/broadcom/afbrs50/Inc/api/argus_api.h b/src/drivers/distance_sensor/broadcom/afbrs50/Inc/api/argus_api.h index a933976370..44cdc92168 100644 --- a/src/drivers/distance_sensor/broadcom/afbrs50/Inc/api/argus_api.h +++ b/src/drivers/distance_sensor/broadcom/afbrs50/Inc/api/argus_api.h @@ -1,12 +1,12 @@ /*************************************************************************//** * @file - * @brief This file is part of the AFBR-S50 API. - * @details This file provides generic functionality belonging to all - * devices from the AFBR-S50 product family. + * @brief This file is part of the AFBR-S50 API. + * @details This file provides generic functionality belonging to all + * devices from the AFBR-S50 product family. * * @copyright * - * Copyright (c) 2021, Broadcom Inc + * Copyright (c) 2023, Broadcom Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,21 +37,21 @@ #ifndef ARGUS_API_H #define ARGUS_API_H - #ifdef __cplusplus extern "C" { #endif /*!*************************************************************************** - * @defgroup argusapi AFBR-S50 API + * @defgroup argus_api AFBR-S50 API + * @ingroup argus * - * @brief The main module of the API from the AFBR-S50 SDK. + * @brief The main module of the API from the AFBR-S50 SDK. * - * @details General API for the AFBR-S50 time-of-flight sensor device family.\n - * See the \ref getting_started Guide for a detailed description - * on how to use the module/API. + * @details General API for the AFBR-S50 time-of-flight sensor device family.\n + * See the \ref getting_started Guide for a detailed description + * on how to use the module/API. * - * @addtogroup argusapi + * @addtogroup argus_api * @{ *****************************************************************************/ @@ -61,213 +61,301 @@ extern "C" { #include "argus_dfm.h" #include "argus_snm.h" #include "argus_xtalk.h" - -/*! The data structure for the API representing a AFBR-S50 device instance. */ -typedef void argus_hnd_t; +#include "argus_offset.h" /*! The S2PI slave identifier. */ typedef int32_t s2pi_slave_t; /*!*************************************************************************** - * @brief Initializes the API modules and the device with default parameters. + * @brief Initializes the device with default measurement mode. * * @details The function that needs to be called once after power up to - * initialize the modules state (i.e. the corresponding handle) and the - * dedicated Time-of-Flight device. In order to obtain a handle, - * reference the #Argus_CreateHandle method. + * initialize the modules state (i.e. the corresponding handle) and the + * dedicated Time-of-Flight device. In order to obtain a handle, + * reference the #Argus_CreateHandle method. * - * Prior to calling the function, the required peripherals (i.e. S2PI, - * GPIO w/ IRQ and Timers) must be initialized and ready to use. + * Prior to calling the function, the required peripherals (i.e. S2PI, + * GPIO w/ IRQ and Timers) must be initialized and ready to use. * - * The function executes the following tasks: - * - Initialization of the internal state represented by the handle - * object. - * - Setup the device such that an safe configuration is present in - * the registers. - * - Initialize sub modules such as calibration or measurement modules. - * . + * The function executes the following tasks: + * - Initialization of the internal state represented by the handle + * object. + * - Setup the device such that an safe configuration is present in + * the registers. + * - Initialize sub modules such as calibration or measurement modules. + * . * - * The modules configuration is initialized with reasonable default values. + * The modules configuration is initialized with reasonable default + * values. Note that the default measurement mode depends on the + * given device. * - * @param hnd The API handle; contains all internal states and data. + * Also refer to #Argus_InitMode, which uses an specified measurement + * mode instead of the dedicated default measurement mode. * - * @param spi_slave The SPI hardware slave, i.e. the specified CS and IRQ - * lines. This is actually just a number that is passed - * to the SPI interface to distinct for multiple SPI slave - * devices. Note that the slave must be not equal to 0, - * since is reserved for error handling. + * @param hnd The API handle; contains all internal states and data. * - * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). + * @param spi_slave The SPI hardware slave, i.e. the specified CS and IRQ + * lines. This is actually just a number that is passed + * to the SPI interface to distinct for multiple SPI slave + * devices. Note that the slave must be not equal to 0, + * since is reserved for error handling. + * + * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). *****************************************************************************/ status_t Argus_Init(argus_hnd_t *hnd, s2pi_slave_t spi_slave); /*!*************************************************************************** - * @brief Reinitializes the API modules and the device with default parameters. + * @brief Initializes the device with specified measurement mode. * - * @details The function reinitializes the device with default configuration. - * Can be used as reset sequence for the device. See #Argus_Init for - * more information on the initialization. + * @details The function that needs to be called once after power up to + * initialize the modules state (i.e. the corresponding handle) and the + * dedicated Time-of-Flight device. In order to obtain a handle, + * reference the #Argus_CreateHandle method. * - * Note that the #Argus_Init function must be called first! Otherwise, - * the function will return an error if it is called for an yet - * uninitialized device/handle. + * Prior to calling the function, the required peripherals (i.e. S2PI, + * GPIO w/ IRQ and Timers) must be initialized and ready to use. * - * @param hnd The API handle; contains all internal states and data. + * The function executes the following tasks: + * - Initialization of the internal state represented by the handle + * object. + * - Setup the device such that an safe configuration is present in + * the registers. + * - Initialize sub modules such as calibration or measurement modules. + * . * - * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). + * The modules configuration is initialized with reasonable default values. + * + * Also refer to #Argus_Init, which uses the dedicated default measurement + * mode instead of an user specified measurement mode. + * + * @param hnd The API handle; contains all internal states and data. + * + * @param spi_slave The SPI hardware slave, i.e. the specified CS and IRQ + * lines. This is actually just a number that is passed + * to the SPI interface to distinct for multiple SPI slave + * devices. Note that the slave must be not equal to 0, + * since is reserved for error handling. + * + * @param mode The specified measurement mode to be initialized. + * Pass 0 as special value to select default measurement mode + * (see #Argus_Init). + * + * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). + *****************************************************************************/ +status_t Argus_InitMode(argus_hnd_t *hnd, s2pi_slave_t spi_slave, argus_mode_t mode); + +/*!*************************************************************************** + * @brief Reinitializes the device with the current measurement mode. + * + * @details The function reinitializes the device with the currently active + * measurement mode. + * + * This can be used as a soft reset for the device and API. + * See #Argus_Init for more information on the initialization. + * + * Note that the #Argus_Init or #Argus_InitMode function must be called + * first! Otherwise, the function will return an error if it is called + * for an yet uninitialized device/handle. + * + * Also refer to #Argus_ReinitMode, which uses a specified measurement + * mode instead of the currently active measurement mode. + * + * @param hnd The API handle; contains all internal states and data. + * + * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). *****************************************************************************/ status_t Argus_Reinit(argus_hnd_t *hnd); /*!*************************************************************************** - * @brief Deinitializes the API modules and the device. + * @brief Reinitializes the device with a specified measurement mode. + * + * @details The function reinitializes the device with a specified (/p mode) + * measurement mode. + * + * This can be used as a soft reset for the device and API. + * See #Argus_InitMode for more information on the initialization. + * + * Note that the #Argus_Init or #Argus_InitMode function must be called + * first! Otherwise, the function will return an error if it is called + * for an yet uninitialized device/handle. + * + * Also refer to #Argus_Reinit, which re-uses the currently active + * measurement mode instead of an user specified measurement mode. + * + * @param hnd The API handle; contains all internal states and data. + * + * @param mode The specified measurement mode to be initialized. + * Pass 0 as special value to select the current measurement mode + * (see #Argus_Init). + * + * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). + *****************************************************************************/ +status_t Argus_ReinitMode(argus_hnd_t *hnd, argus_mode_t mode); + +/*!*************************************************************************** + * @brief Deinitializes the API modules and the device. * * @details The function deinitializes the device and clear all internal states. - * Can be used to cleanup before releasing the memory. The device - * can not be used any more and must be initialized again prior to next - * usage. + * Can be used to cleanup before releasing the memory. The device + * can not be used any more and must be initialized again prior to next + * usage. * - * Note that the #Argus_Init function must be called first! Otherwise, - * the function will return an error if it is called for an yet - * uninitialized device/handle. + * Note that the #Argus_Init function must be called first! Otherwise, + * the function will return an error if it is called for an yet + * uninitialized device/handle. * - * @param hnd The API handle; contains all internal states and data. + * @param hnd The API handle; contains all internal states and data. * - * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). + * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). *****************************************************************************/ status_t Argus_Deinit(argus_hnd_t *hnd); /*!*************************************************************************** - * @brief Creates a new device data handle object to store all internal states. + * @brief Creates a new device data handle object to store all internal states. * * @details The function must be called to obtain a new device handle object. - * The handle is basically an abstract object in memory that contains - * all the internal states and settings of the API module. The handle - * is passed to all the API methods in order to address the specified - * device. This allows to use the API with more than a single measurement - * device. + * The handle is basically an abstract object in memory that contains + * all the internal states and settings of the API module. The handle + * is passed to all the API methods in order to address the specified + * device. This allows to use the API with more than a single measurement + * device. * - * The handler is created by calling the memory allocation method from - * the standard library: @code void * malloc(size_t size) @endcode - * In order to implement an individual memory allocation method, - * define and implement the following weakly binded method and return - * a pointer to the newly allocated memory. * + * The handler is created by calling the memory allocation method from + * the standard library: @code void * malloc(size_t size) @endcode + * In order to implement an individual memory allocation method, + * define and implement the following weakly binded method and return + * a pointer to the newly allocated memory. * * - * @code void * Argus_Malloc (size_t size) @endcode + * @code void * Argus_Malloc (size_t size) @endcode * - * Also see the #Argus_DestroyHandle method for the corresponding - * deallocation of the allocated memory. + * Also see the #Argus_DestroyHandle method for the corresponding + * deallocation of the allocated memory. * - * @note Although the method is using memory allocated on the heap, it - * is eventually no dynamic memory allocation, since the block of - * memory is kept all the time and no memory blocks are dynamically - * freed and re-allocated. If the usage of heap must be avoided, one - * can always implement its own version of the `Argus_Malloc` function - * to create the memory elsewhere. + * @note Although the method is using memory allocated on the heap, it + * is eventually no dynamic memory allocation, since the block of + * memory is kept all the time and no memory blocks are dynamically + * freed and re-allocated. If the usage of heap must be avoided, one + * can always implement its own version of the `Argus_Malloc` function + * to create the memory elsewhere. * - * @return Returns a pointer to the newly allocated device handler object. - * Returns a null pointer if the allocation failed! + * @return Returns a pointer to the newly allocated device handler object. + * Returns a null pointer if the allocation failed! *****************************************************************************/ argus_hnd_t *Argus_CreateHandle(void); /*!*************************************************************************** - * @brief Destroys a given device data handle object. + * @brief Destroys a given device data handle object. * * @details The function can be called to free the previously created device - * data handle object in order to save memory when the device is not - * used any more. + * data handle object in order to save memory when the device is not + * used any more. * - * Please refer to the #Argus_CreateHandle method for the corresponding - * allocation of the memory. + * Note that the handle must be deinitialized before it can be + * destroyed. The function returns #ERROR_FAIL if the handle is not + * yet deinitialized. * - * The handler is destroyed by freeing the corresponding memory with the - * method from the standard library, @code void free(void * ptr) @endcode. - * In order to implement an individual memory deallocation method, define - * and implement the following weakly binded method and free the memory - * object passed to the method by a pointer. + * Please refer to the #Argus_CreateHandle method for the corresponding + * allocation of the memory. * - * @code void Argus_Free (void * ptr) @endcode + * The handler is destroyed by freeing the corresponding memory with the + * method from the standard library, @code void free(void * ptr) @endcode. + * In order to implement an individual memory deallocation method, define + * and implement the following weakly binded method and free the memory + * object passed to the method by a pointer. * - * Also see the #Argus_CreateHandle method for the corresponding - * allocation of the required memory. + * @code void Argus_Free (void * ptr) @endcode * - * @param hnd The device handle object to be deallocated. + * Also see the #Argus_CreateHandle method for the corresponding + * allocation of the required memory. + * + * @param hnd The device handle object to be deallocated. + * + * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). *****************************************************************************/ -void Argus_DestroyHandle(argus_hnd_t *hnd); +status_t Argus_DestroyHandle(argus_hnd_t *hnd); /*!************************************************************************** * Generic API ****************************************************************************/ /*!*************************************************************************** - * @brief Gets the version number of the current API library. + * @brief Gets the version number of the current API library. * - * @details The version is compiled of a major (a), minor (b) and bugfix (c) - * number: a.b.c. + * @details The version is compiled of a major (a), minor (b) and bugfix (c) + * number: a.b.c. * - * The values are encoded into a 32-bit value: + * The values are encoded into a 32-bit value: * - * - [ 31 .. 24 ] - Major Version Number - * - [ 23 .. 16 ] - Minor Version Number - * - [ 15 .. 0 ] - Bugfix Version Number - * . + * - [ 31 .. 24 ] - Major Version Number + * - [ 23 .. 16 ] - Minor Version Number + * - [ 15 .. 0 ] - Bugfix Version Number + * . * - * To obtain the parts from the returned uin32_t value: + * To obtain the parts from the returned uin32_t value: * - * @code - * uint32_t value = Argus_GetAPIVersion(); - * uint8_t a = (value >> 24) & 0xFFU; - * uint8_t b = (value >> 16) & 0xFFU; - * uint8_t c = value & 0xFFFFU; - * @endcode + * @code + * uint32_t value = Argus_GetAPIVersion(); + * uint8_t a = (value >> 24) & 0xFFU; + * uint8_t b = (value >> 16) & 0xFFU; + * uint8_t c = value & 0xFFFFU; + * @endcode * - * @return Returns the current version number. + * @return Returns the current version number. *****************************************************************************/ uint32_t Argus_GetAPIVersion(void); /*!*************************************************************************** - * @brief Gets the build number of the current API library. + * @brief Gets the build number of the current API library. * - * @return Returns the current build number as a C-string. + * @return Returns the current build number as a C-string. *****************************************************************************/ char const *Argus_GetBuildNumber(void); /*!*************************************************************************** - * @brief Gets the version/variant of the module. + * @brief Gets the version/variant of the module. * - * @param hnd The API handle; contains all internal states and data. - * @return Returns the current module number. + * @param hnd The API handle; contains all internal states and data. + * @return Returns the current module number. *****************************************************************************/ argus_module_version_t Argus_GetModuleVersion(argus_hnd_t *hnd); /*!*************************************************************************** - * @brief Gets the version number of the chip. + * @brief Gets the name string of the module. * - * @param hnd The API handle; contains all internal states and data. - * @return Returns the current version number. + * @param hnd The API handle; contains all internal states and data. + * @return Returns the current module name. + *****************************************************************************/ +char const *Argus_GetModuleName(argus_hnd_t *hnd); + +/*!*************************************************************************** + * @brief Gets the version number of the chip. + * + * @param hnd The API handle; contains all internal states and data. + * @return Returns the current version number. *****************************************************************************/ argus_chip_version_t Argus_GetChipVersion(argus_hnd_t *hnd); /*!*************************************************************************** - * @brief Gets the type number of the device laser. + * @brief Gets the type number of the device laser. * - * @param hnd The API handle; contains all internal states and data. - * @return Returns the current device laser type number. + * @param hnd The API handle; contains all internal states and data. + * @return Returns the current device laser type number. *****************************************************************************/ argus_laser_type_t Argus_GetLaserType(argus_hnd_t *hnd); /*!*************************************************************************** - * @brief Gets the unique identification number of the chip. + * @brief Gets the unique identification number of the chip. * - * @param hnd The API handle; contains all internal states and data. - * @return Returns the unique identification number. + * @param hnd The API handle; contains all internal states and data. + * @return Returns the unique identification number. *****************************************************************************/ uint32_t Argus_GetChipID(argus_hnd_t *hnd); /*!*************************************************************************** - * @brief Gets the SPI hardware slave identifier. + * @brief Gets the SPI hardware slave identifier. * - * @param hnd The API handle; contains all internal states and data. - * @return The SPI hardware slave identifier. + * @param hnd The API handle; contains all internal states and data. + * @return The SPI hardware slave identifier. *****************************************************************************/ s2pi_slave_t Argus_GetSPISlave(argus_hnd_t *hnd); @@ -276,323 +364,425 @@ s2pi_slave_t Argus_GetSPISlave(argus_hnd_t *hnd); /*!************************************************************************** * Measurement/Device Operation **************************************************************************** - * @addtogroup argusmeas + * @addtogroup argus_meas * @{ ****************************************************************************/ /*!*************************************************************************** - * @brief Starts the timer based measurement cycle asynchronously. + * @brief Starts the timer based measurement cycle asynchronously. * * @details This function starts a timer based measurement cycle asynchronously - * in the background. A periodic timer interrupt triggers the measurement - * frames on the ASIC and the data readout afterwards. + * in the background. A periodic timer interrupt triggers the measurement + * frames on the ASIC and the data readout afterwards. * - * When the measurement has finished, a callback (which is passed as - * a parameter to the function) is invoked in order to inform the - * main thread to call the \link #Argus_EvaluateData data evaluation - * method\endlink. This call is mandatory to release the data buffer - * for the next measurement and it must not be invoked directly from - * the callback since it is currently within an interrupt service - * routine. Rather a flag should inform the main thread or task - * scheduler to invoke the evaluation as soon as possible in order - * to not introduce any unwanted delays to the next measurement frame. + * When the measurement has finished, a callback (which is passed as + * a parameter to the function) is invoked in order to inform the + * main thread to call the \link #Argus_EvaluateData data evaluation + * method\endlink. This call is mandatory to release the data buffer + * for the next measurement and it must not be invoked directly from + * the callback since it is currently within an interrupt service + * routine. Rather a flag should inform the main thread or task + * scheduler to invoke the evaluation as soon as possible in order + * to not introduce any unwanted delays to the next measurement frame. * - * The next measurement frame will be started as soon as the pre- - * conditions are meet. These are: - * 1. timer flag set (i.e. a certain time has passed since the last - * measurement in order to fulfill eye-safety), - * 2. device idle (i.e. no measurement currently ongoing) and - * 3. data buffer ready (i.e. the previous data has been evaluated). + * The next measurement frame will be started as soon as the pre- + * conditions are meet. These are: + * 1. timer flag set (i.e. a certain time has passed since the last + * measurement in order to fulfill eye-safety), + * 2. device idle (i.e. no measurement currently ongoing) and + * 3. data buffer ready (i.e. the previous data has been evaluated). * - * Usually, the device idle and data buffer ready conditions are met - * before the timer tick occurs and thus the timer dictates the frame - * rate. + * Usually, the device idle and data buffer ready conditions are met + * before the timer tick occurs and thus the timer dictates the frame + * rate. * - * The callback function pointer will be invoked when the measurement - * frame has finished successfully or whenever an error, that cannot - * be handled internally, occurs. + * The callback function pointer will be invoked when the measurement + * frame has finished successfully or whenever an error, that cannot + * be handled internally, occurs. * - * The periodic timer interrupts are used to check the measurement status - * for timeouts. An error is invoked when a measurement cycle have not - * finished within the specified time. + * The periodic timer interrupts are used to check the measurement status + * for timeouts. An error is invoked when a measurement cycle have not + * finished within the specified time. * - * Use #Argus_StopMeasurementTimer to stop the measurements. + * Use #Argus_StopMeasurementTimer to stop the measurements. * - * @note In order to use this function, the periodic interrupt timer module - * (see @ref argus_timer) must be implemented! + * @note In order to use this function, the periodic interrupt timer module + * (see @ref argus_timer) must be implemented! * - * @param hnd The API handle; contains all internal states and data. - * @param cb Callback function that will be invoked when the measurement - * is completed. Its parameters are the \link #status_t status - * \endlink and a pointer to the \link #argus_results_t results - * \endlink structure. If an error occurred, the status differs - * from #STATUS_OK and the second parameter is null. - * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). + * @param hnd The API handle; contains all internal states and data. + * + * @param cb Callback function that will be invoked when the measurement + * is completed. Its parameters are the \link #status_t status + * \endlink of the finished measurement cycle and the pointer to + * the calling \link #argus_hnd_t API handle\endlink, i.e. the + * /p hnd value. The latter must be passed to the + * #Argus_EvaluateData function. + * If an error occurred, the status differs from #STATUS_OK. + * + * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). *****************************************************************************/ -status_t Argus_StartMeasurementTimer(argus_hnd_t *hnd, argus_callback_t cb); +status_t Argus_StartMeasurementTimer(argus_hnd_t *hnd, + argus_measurement_ready_callback_t cb); /*!*************************************************************************** - * @brief Stops the timer based measurement cycle. + * @brief Stops the timer based measurement cycle. * * @details This function stops the ongoing timer based measurement cycles - * that have been started using the #Argus_StartMeasurementTimer - * function. + * that have been started using the #Argus_StartMeasurementTimer + * function. * - * @param hnd The API handle; contains all internal states and data. - * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). + * @param hnd The API handle; contains all internal states and data. + * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). *****************************************************************************/ status_t Argus_StopMeasurementTimer(argus_hnd_t *hnd); /*!*************************************************************************** - * @brief Triggers a single measurement frame asynchronously. + * @brief Triggers a single measurement frame asynchronously. * * @details This function immediately triggers a single measurement frame - * asynchronously if all the pre-conditions are met. Otherwise it - * returns with a corresponding status (e.g. #STATUS_BUSY or - * #STATUS_ARGUS_POWERLIMIT). + * asynchronously if all the pre-conditions are met. Otherwise it + * returns with a corresponding status (e.g. #STATUS_BUSY or + * #STATUS_ARGUS_POWERLIMIT). * - * When the measurement has finished, a callback (which is passed as - * a parameter to the function) is invoked in order to inform the - * main thread to call the \link #Argus_EvaluateData data evaluation - * method\endlink. This call is mandatory to release the data buffer - * for the next measurement and it must not be invoked directly from - * the callback since it is currently within an interrupt service - * routine. Rather a flag should inform the main thread or task - * scheduler to invoke the evaluation task. + * When the measurement has finished, a callback (which is passed as + * a parameter to the function) is invoked in order to inform the + * main thread to call the \link #Argus_EvaluateData data evaluation + * method\endlink. This call is mandatory to release the data buffer + * for the next measurement and it must not be invoked directly from + * the callback since it is currently within an interrupt service + * routine. Rather a flag should inform the main thread or task + * scheduler to invoke the evaluation task. * - * The pre-conditions for starting a measurement frame are: - * 1. timer flag set (i.e. a certain time has passed since the last - * measurement in order to fulfill eye-safety), - * 2. device idle (i.e. no measurement currently ongoing) and - * 3. data buffer ready (i.e. the previous data has been evaluated). + * The pre-conditions for starting a measurement frame are: + * 1. timer flag set (i.e. a certain time has passed since the last + * measurement in order to fulfill eye-safety), + * 2. device idle (i.e. no measurement currently ongoing) and + * 3. data buffer ready (i.e. the previous data has been evaluated). * - * The callback function pointer will be invoked when the measurement - * frame has finished successfully or whenever an error, that cannot - * be handled internally, occurs. + * The callback function pointer will be invoked when the measurement + * frame has finished successfully or whenever an error, that cannot + * be handled internally, occurs. * - * The successful finishing of the measurement frame is not checked - * for timeouts! Instead, the user can call the #Argus_GetStatus() - * function on a regular function to do so. + * The successful finishing of the measurement frame is not checked + * for timeouts! Instead, the user can call the #Argus_GetStatus() + * function on a regular function to do so. * - * @param hnd The API handle; contains all internal states and data. - * @param cb Callback function that will be invoked when the measurement - * is completed. Its parameters are the \link #status_t status - * \endlink and a pointer to the \link #argus_results_t results - * \endlink structure. If an error occurred, the status differs - * from #STATUS_OK and the second parameter is null. - * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). + * @note Despite this function triggers a new measurement cycle upon its + * invocation, the frame time parameter is still active for this + * measurement mode. Basically, the first pre-condition mentioned + * above is controlled via the frame time parameter. This means + * that measurements cannot be triggered faster than the frame + * timer parameters specifies. The maximum integration time (i.e. + * exposure time) is also determined by the frame time such that + * new measurements are finished with the specified frame time and + * the device is ready to trigger a new measurement after the + * frame time has elapse. + * See #Argus_SetConfigurationFrameTime function for more information + * on the frame time. + * + * @param hnd The API handle; contains all internal states and data. + * + * @param cb Callback function that will be invoked when the measurement + * is completed. Its parameters are the \link #status_t status + * \endlink of the finished measurement cycle and the pointer to + * the calling \link #argus_hnd_t API handle\endlink, i.e. the + * /p hnd value. The latter must be passed to the + * #Argus_EvaluateData function. + * If an error occurred, the status differs from #STATUS_OK. + * + * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). *****************************************************************************/ -status_t Argus_TriggerMeasurement(argus_hnd_t *hnd, argus_callback_t cb); +status_t Argus_TriggerMeasurement(argus_hnd_t *hnd, + argus_measurement_ready_callback_t cb); /*!*************************************************************************** - * @brief Stops the currently ongoing measurements and SPI activity immediately. + * @brief Determines whether a data evaluation is pending. * - * @param hnd The API handle; contains all internal states and data. - * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). + * @details If the function returns true, a raw buffer is required to be + * evaluated to the #Argus_EvaluateData function. The raw data buffer + * is filled with raw data from the measurement tasks which need to + * be evaluated and the buffer must be freed in order to restart a + * new measurement task. + * + * Note that no configuration parameters can be update until all raw + * buffers are evaluated. + * + * @note See also the #Argus_GetStatus function to obtain the current device + * status and error code if any. + * + * @param hnd The API handle; contains all internal states and data. + * @return True if any raw buffer is filled with data that must be evaluated. + *****************************************************************************/ +bool Argus_IsDataEvaluationPending(argus_hnd_t *hnd); + +/*!*************************************************************************** + * @brief Determines if the device if active with timer based measurements. + * @details If the function returns true, the device is active with timer + * scheduled measurements that have been started via the + * #Argus_StartMeasurementTimer. + * + * Note that the active state is independent of the busy state that + * is set when the device is actually busy. The active state is also + * true if the device is currently idle but waits for the next timer + * event to trigger a new measurement cycle. + * + * @note See also the #Argus_GetStatus function to obtain the current device + * status and error code if any. + * + * @param hnd The API handle; contains all internal states and data. + * @return True if the device is operating in timer triggered measurement mode. + *****************************************************************************/ +bool Argus_IsTimerMeasurementActive(argus_hnd_t *hnd); + +/*!*************************************************************************** + * @brief Stops the currently ongoing measurements and SPI activity immediately. + * + * @param hnd The API handle; contains all internal states and data. + * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). *****************************************************************************/ status_t Argus_Abort(argus_hnd_t *hnd); /*!*************************************************************************** - * @brief Checks the state of the device/driver. + * @brief Checks the state of the device/driver. * - * @details Returns the current module status or error if any. - * See the following for a list of errors: + * @details Returns the current module status or error if any. * - * Status: - * - Idle/OK: Device and SPI interface are idle (== #STATUS_IDLE). - * - Busy: Device or SPI interface are busy (== #STATUS_BUSY). - * - Initializing: The modules and devices are currently initializing - * (== #STATUS_INITIALIZING). - * . + * See the following for a list of errors: * - * Error: - * - Not Initialized: The modules (or any submodule) has not been - * initialized yet (== #ERROR_NOT_INITIALIZED). - * - Not Connected: No device has been connected (or connection errors - * have occurred) (== #ERROR_ARGUS_NOT_CONNECTED). - * - Timeout: A previous frame measurement has not finished within a - * specified time (== #ERROR_TIMEOUT). - * . + * Status: + * - Idle/OK: Device and SPI interface are idle (== #STATUS_IDLE). + * - Busy: Device or SPI interface are busy (== #STATUS_BUSY). + * - Initializing: The modules and devices are currently initializing + * (== #STATUS_INITIALIZING). + * . * - * @param hnd The API handle; contains all internal states and data. - * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). + * Error: + * - Not Initialized: The modules (or any submodule) has not been + * initialized yet (== #ERROR_NOT_INITIALIZED). + * - Not Connected: No device has been connected (or connection errors + * have occurred) (== #ERROR_ARGUS_NOT_CONNECTED). + * - Timeout: A previous frame measurement has not finished within a + * specified time (== #ERROR_TIMEOUT). + * . + * + * @note Note that this function returns the actual busy state. This means + * that it will return #STATUS_IDLE during the pause between two + * consecutive measurement frames. If the device is active with timer + * based measurements (i.e. started via the #Argus_StartMeasurementTimer + * function), the return state switches from idle to busy and back + * periodically. Use the #Argus_IsTimerMeasurementActive function in + * order to determine if the device is active with timer based + * measurements. + * + * @note Note also that the device might reject configuration parameter + * update despite the status is #STATUS_IDLE. This is due to the fact + * that the internal raw data buffers are still busy and require to + * be freed by passing them to the #Argus_EvaluateData function. Use + * the #Argus_IsDataEvaluationPending function to see whether any of + * the raw data buffers is busy or the configuration can be changed. + * + * @param hnd The API handle; contains all internal states and data. + * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). *****************************************************************************/ status_t Argus_GetStatus(argus_hnd_t *hnd); /*!***************************************************************************** - * @brief Tests the connection to the device by sending a ping message. + * @brief Tests the connection to the device by sending a ping message. * - * @details A ping is transfered to the device in order to check the device and - * SPI connection status. Returns #STATUS_OK on success and - * #ERROR_ARGUS_NOT_CONNECTED elsewise. + * @details A ping is transferred to the device in order to check the device and + * SPI connection status. Returns #STATUS_OK on success and + * #ERROR_ARGUS_NOT_CONNECTED else-wise. * - * @param hnd The API handle; contains all internal states and data. - * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). + * @param hnd The API handle; contains all internal states and data. + * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). ******************************************************************************/ status_t Argus_Ping(argus_hnd_t *hnd); /*!*************************************************************************** - * @brief Evaluate useful information from the raw measurement data. + * @brief Evaluates measurement data from the raw sensor readout data. * - * @details This function is called with a pointer to the raw results obtained - * from the measurement cycle. It evaluates this data and creates - * useful information from it. Furthermore, calibration is applied to - * the data. Finally, the results are used in order to adapt the device - * configuration to the ambient conditions in order to achieve optimal - * device performance. + * @details This function must be called after each completion of a measurement + * cycle. The completion of a measurement cycle is communicated by the + * API via the invocation of the measurement data ready callback. The + * callback is installed in the API when new measurements are started + * either via the #Argus_TriggerMeasurement or via the + * #Argus_StartMeasurementTimer functions. * - * Therefore, it consists of the following sub-functions: - * - Apply pre-calibration: Applies calibration steps before evaluating - * the data, i.e. calculations that are to the integration results - * directly. - * - Evaluate data: Calculates measurement parameters such as range, - * amplitude or ambient light intensity, depending on the configurations. - * - Apply post-calibration: Applies calibrations after evaluation of - * measurement data, i.e. calibrations applied to the calculated - * values such as range. - * - Dynamic Configuration Adaption: checks if the configuration needs - * to be adjusted before the next measurement cycle in order to - * achieve optimum performance. Note that the configuration might not - * applied directly but before the next measurement starts. This is - * due to the fact that the device could be busy measuring already - * the next frame and thus no SPI activity is allowed. - * . - * However, if the device is idle, the configuration will be written - * immediately. + * This function evaluates measurement values like distances, amplitudes + * states and auxiliary values like temperature or voltage values from + * the raw sensor readout data obtained from the device during the + * measurement cycle. A pointer to a #argus_results_t data structure + * must be passed where all the evaluated values will be written to. + * The structure must persist during the whole execution of the + * #Argus_EvaluateData function. * - * @param hnd The API handle; contains all internal states and data. - * @param res A pointer to the results structure that will be populated - * with evaluated data. - * @param raw The pointer to the raw data that has been obtained by the - * measurement finished callback. - * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). + * In addition to the evaluation of measurement data, the function + * feeds back the obtained information to the device in order to + * optimize its performance with respect to the ambient conditions, + * utilizing the so called Dynamic Configuration Adaption (DCA) + * feature. + * + * Furthermore, several calibration algorithm are applied to the data. + * + * If the function is called without any data ready to be evaluated + * from the measurement module, the error code #ERROR_ARGUS_BUFFER_EMPTY + * is returned and not data is written to the passed #argus_results_t + * data structure. + * + * @note The call to this function is mandatory for each finished measurement + * cycle, i.e. for each call to the measurement data ready callback. + * If the function is not called, the data is not evaluated and the + * internal raw data buffers are not freed. In that case, they can not + * be reused for the next measurement and the API can not start new + * measurements. + * There are up to two internal buffers available, the to callback + * is called twice before the API must wait for the data evaluation + * to finish. + * + * @param hnd The API handle; contains all internal states and data. + * @param res A pointer to the results structure that will be populated + * with evaluated data. + * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). *****************************************************************************/ -status_t Argus_EvaluateData(argus_hnd_t *hnd, argus_results_t *res, void *raw); +status_t Argus_EvaluateData(argus_hnd_t *hnd, argus_results_t *res); /*!*************************************************************************** - * @brief Executes a crosstalk calibration measurement. + * @brief Evaluates measurement data from the raw sensor readout data. + * + * @details This function enhances the #Argus_EvaluateData by adding additional + * debug data into a specified debug data structure (\p dbg). If the + * \p dbg is null, the function is eqivalent to the #Argus_EvaluateData + * function. This, see #Argus_EvaluateData for reference. + * + * @param hnd The API handle; contains all internal states and data. + * @param res A pointer to the results structure that will be populated + * with evaluated data. + * @param dbg An optional pointer (can be null) to the debug data structure. + * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). + *****************************************************************************/ +status_t Argus_EvaluateDataDebug(argus_hnd_t *hnd, argus_results_t *res, + argus_results_debug_t *dbg); + +/*!*************************************************************************** + * @brief Executes a crosstalk calibration measurement. * * @details This function immediately triggers a crosstalk vector calibration - * measurement sequence. The ordinary measurement activity is suspended - * while the calibration is ongoing. + * measurement sequence. The ordinary measurement activity is suspended + * while the calibration is ongoing. * - * In order to perform a crosstalk calibration, the reflection of the - * transmitted signal must be kept from the receiver side, by either - * covering the TX completely (or RX respectively) or by setting up - * an absorbing target at far distance. + * In order to perform a crosstalk calibration, the reflection of the + * transmitted signal must be kept from the receiver side, by either + * covering the TX completely (or RX respectively) or by setting up + * an absorbing target at far distance. * - * After calibration has finished successfully, the obtained data is - * applied immediately and can be read from the API using the - * #Argus_GetCalibrationCrosstalkVectorTable function. + * After calibration has finished successfully, the obtained data is + * applied immediately and can be read from the API using the + * #Argus_GetCalibrationCrosstalkVectorTable function. * - * @param hnd The API handle; contains all internal states and data. - * @param mode The targeted measurement mode. - * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). + * @param hnd The API handle; contains all internal states and data. + * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). *****************************************************************************/ -status_t Argus_ExecuteXtalkCalibrationSequence(argus_hnd_t *hnd, argus_mode_t mode); - +status_t Argus_ExecuteXtalkCalibrationSequence(argus_hnd_t *hnd); /*!*************************************************************************** - * @brief Executes a relative range offset calibration measurement. + * @brief Executes a relative range offset calibration measurement. * * @details This function immediately triggers a relative range offset calibration - * measurement sequence. The ordinary measurement activity is suspended - * while the calibration is ongoing. + * measurement sequence. The ordinary measurement activity is suspended + * while the calibration is ongoing. * - * In order to perform a relative range offset calibration, a flat - * calibration target must be setup perpendicular to the sensors - * field-of-view. + * In order to perform a relative range offset calibration, a flat + * calibration target must be setup perpendicular to the sensors + * field-of-view. * - * \code + * \code * AFBR-S50 ToF Sensor - * #| - * #| | - * #|-----+ | - * #| Rx | | - * Reference #|----++ | Calibration - * Plane #| Tx | | Target - * #|----+ | - * #| | - * #| <------- targetRange -----------------> | - * \endcode + * #| + * #| | + * #|-----+ | + * #| RX | | + * Reference #|----++ | Calibration + * Plane #| TX | | Target + * #|----+ | + * #| | + * #| <------- targetRange -----------------> | + * \endcode * - * There are two options to run the offset calibration: relative and - * absolute. - * - Relative (#Argus_ExecuteRelativeRangeOffsetCalibrationSequence): - * when the absolute distance is not essential or the distance to - * the calibration target is not known, the relative method can be - * used to compensate the relative pixel range offset w.r.t. the - * average range. The absolute or global range offset is not changed. - * - Absolute (#Argus_ExecuteAbsoluteRangeOffsetCalibrationSequence): - * when the absolute distance is essential and the distance to the - * calibration target is known, the absolute method can be used to - * calibrate the absolute measured distance. Additionally, the - * relative pixel offset w.r.t. the average range is also compensated. - * . + * There are two options to run the offset calibration: relative and + * absolute. * - * After calibration has finished successfully, the obtained data is - * applied immediately and can be read from the API using the - * #Argus_GetCalibrationPixelRangeOffsets or - * #Argus_GetCalibrationGlobalRangeOffset function. + * - Relative (#Argus_ExecuteRelativeRangeOffsetCalibrationSequence): + * when the absolute distance is not essential or the distance to + * the calibration target is not known, the relative method can be + * used to compensate the relative pixel range offset w.r.t. the + * average range. The absolute or global range offset is not changed. + * - Absolute (#Argus_ExecuteAbsoluteRangeOffsetCalibrationSequence): + * when the absolute distance is essential and the distance to the + * calibration target is known, the absolute method can be used to + * calibrate the absolute measured distance. Additionally, the + * relative pixel offset w.r.t. the average range is also compensated. + * . * - * @param hnd The API handle; contains all internal states and data. - * @param mode The targeted measurement mode. - * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). + * After calibration has finished successfully, the obtained data is + * applied immediately and can be read from the API using the + * #Argus_GetCalibrationPixelRangeOffsets or + * #Argus_GetCalibrationGlobalRangeOffset function. + * + * @param hnd The API handle; contains all internal states and data. + * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). *****************************************************************************/ -status_t Argus_ExecuteRelativeRangeOffsetCalibrationSequence(argus_hnd_t *hnd, - argus_mode_t mode); +status_t Argus_ExecuteRelativeRangeOffsetCalibrationSequence(argus_hnd_t *hnd); /*!*************************************************************************** - * @brief Executes an absolute range offset calibration measurement. + * @brief Executes an absolute range offset calibration measurement. * * @details This function immediately triggers an absolute range offset calibration - * measurement sequence. The ordinary measurement activity is suspended - * while the calibration is ongoing. + * measurement sequence. The ordinary measurement activity is suspended + * while the calibration is ongoing. * - * In order to perform a relative range offset calibration, a flat - * calibration target must be setup perpendicular to the sensors - * field-of-view. + * In order to perform a relative range offset calibration, a flat + * calibration target must be setup perpendicular to the sensors + * field-of-view. * - * \code + * \code * AFBR-S50 ToF Sensor - * #| - * #| | - * #|-----+ | - * #| Rx | | - * Reference #|----++ | Calibration - * Plane #| Tx | | Target - * #|----+ | - * #| | - * #| <------- targetRange -----------------> | - * \endcode + * #| + * #| | + * #|-----+ | + * #| RX | | + * Reference #|----++ | Calibration + * Plane #| TX | | Target + * #|----+ | + * #| | + * #| <------- targetRange -----------------> | + * \endcode * - * There are two options to run the offset calibration: relative and - * absolute. - * - Relative (#Argus_ExecuteRelativeRangeOffsetCalibrationSequence): - * when the absolute distance is not essential or the distance to - * the calibration target is not known, the relative method can be - * used to compensate the relative pixel range offset w.r.t. the - * average range. The absolute or global range offset is not changed. - * - Absolute (#Argus_ExecuteAbsoluteRangeOffsetCalibrationSequence): - * when the absolute distance is essential and the distance to the - * calibration target is known, the absolute method can be used to - * calibrate the absolute measured distance. Additionally, the - * relative pixel offset w.r.t. the average range is also compensated. - * . + * There are two options to run the offset calibration: relative and + * absolute. * - * After calibration has finished successfully, the obtained data is - * applied immediately and can be read from the API using the - * #Argus_GetCalibrationPixelRangeOffsets or - * #Argus_GetCalibrationGlobalRangeOffset function. + * - Relative (#Argus_ExecuteRelativeRangeOffsetCalibrationSequence): + * when the absolute distance is not essential or the distance to + * the calibration target is not known, the relative method can be + * used to compensate the relative pixel range offset w.r.t. the + * average range. The absolute or global range offset is not changed. + * - Absolute (#Argus_ExecuteAbsoluteRangeOffsetCalibrationSequence): + * when the absolute distance is essential and the distance to the + * calibration target is known, the absolute method can be used to + * calibrate the absolute measured distance. Additionally, the + * relative pixel offset w.r.t. the average range is also compensated. + * . * - * @param hnd The API handle; contains all internal states and data. - * @param mode The targeted measurement mode. - * @param targetRange The absolute range between the reference plane and the - * calibration target in meter an Q9.22 format. - * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). + * After calibration has finished successfully, the obtained data is + * applied immediately and can be read from the API using the + * #Argus_GetCalibrationPixelRangeOffsets or + * #Argus_GetCalibrationGlobalRangeOffset function. + * + * @param hnd The API handle; contains all internal states and data. + * @param targetRange The absolute range between the reference plane and the + * calibration target in meter an Q9.22 format. + * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). *****************************************************************************/ status_t Argus_ExecuteAbsoluteRangeOffsetCalibrationSequence(argus_hnd_t *hnd, - argus_mode_t mode, q9_22_t targetRange); /*! @} */ @@ -600,188 +790,245 @@ status_t Argus_ExecuteAbsoluteRangeOffsetCalibrationSequence(argus_hnd_t *hnd, /*!************************************************************************** * Configuration API **************************************************************************** - * @addtogroup arguscfg + * @addtogroup argus_cfg * @{ ****************************************************************************/ /*!*************************************************************************** - * @brief Sets the measurement mode to a specified device. + * @brief Gets the default measurement mode for a specified module type. * - * @param hnd The API handle; contains all internal states and data. - * @param value The new measurement mode. - * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). + * @param module The specified module type. + * @return Returns the default measurement mode for the specified module type. *****************************************************************************/ -status_t Argus_SetConfigurationMeasurementMode(argus_hnd_t *hnd, - argus_mode_t value); +argus_mode_t Argus_GetDefaultMeasurementMode(argus_module_version_t module); /*!*************************************************************************** - * @brief Gets the measurement mode from a specified device. + * @brief Sets the measurement mode to a specified device. * - * @param hnd The API handle; contains all internal states and data. - * @param value The current measurement mode. - * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). + * @details This generates a new default configuration and calibration for the + * specified measurement mode and applies it to the device. + * + * See #argus_mode_t for a list of all available measurement modes. + * + * @warning The function overwrites all made changes to the configuration or + * calibration parameters with the default values. So this function + * must be called before any other changes to the configuration or + * calibration parameters are made! + * + * @param hnd The API handle; contains all internal states and data. + * @param mode The new measurement mode. + * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). *****************************************************************************/ -status_t Argus_GetConfigurationMeasurementMode(argus_hnd_t *hnd, - argus_mode_t *value); +status_t Argus_SetMeasurementMode(argus_hnd_t *hnd, argus_mode_t mode); /*!*************************************************************************** - * @brief Sets the frame time to a specified device. + * @brief Resets the measurement mode to a specified device. * - * @param hnd The API handle; contains all internal states and data. - * @param value The measurement frame time in microseconds. - * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). + * @details This generates a new default configuration and calibration for the + * current measurement mode and applies it to the device. + * + * @warning The function overwrites all made changes to the configuration or + * calibration parameters with the default values. So this function + * must be called before any other changes to the configuration or + * calibration parameters are made! + * + * @param hnd The API handle; contains all internal states and data. + * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). + *****************************************************************************/ +status_t Argus_ResetMeasurementMode(argus_hnd_t *hnd); + +/*!*************************************************************************** + * @brief Gets the measurement mode from a specified device. + * + * @param hnd The API handle; contains all internal states and data. + * @param mode The current measurement mode. + * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). + *****************************************************************************/ +status_t Argus_GetMeasurementMode(argus_hnd_t *hnd, argus_mode_t *mode); + +/*!*************************************************************************** + * @brief Sets the frame time to a specified device. + * + * @details The frame time determines the measurement rate of the device. + * Usually, this controller the periodicity of measurements to be + * triggered via the timer based measurement mode that can be started + * via the #Argus_StartMeasurementTimer function. But also the + * behavior of the #Argus_TriggerMeasurement function is influenced + * by the frame rate parameter. + * + * The frame time parameter handles the maximum frame rate by limiting + * the trigger of a new measurement frame to the specified value. + * On the other hand, the accuracy of measurement results it also + * influenced since the frame time specifies the maximum integration + * depth (i.e. exposure time) along with the laser safety limitations. + * This means, the measurement speed can be increased by decreasing + * the frame time parameter and the accuracy can be improved by + * increasing the frame time parameter. + * + * Note the additional factor will limit the maximum frame rate on the + * one hand and the accuracy on the other hand: + * - High CPU load (or slow CPU in general) will lead to delays due + * to long data evaluation task (#Argus_EvaluateData) or long user + * application code. Reduce CPU load or increase CPU power to + * increase maximum frame rate. + * - The dual frequency mode (DFM, see #Argus_SetConfigurationDFMMode) + * will additionally limit the maximum frame rate to approximately + * 100 frames per second. Disable the DFM to increase maximum frame + * rates. + * - The smart power save (SPS, see + * #Argus_SetConfigurationSmartPowerSaveEnabled) mode will decrease + * the maximum possible frame rate slightly. Disable it to increase + * the maximum frame rate. + * - The dynamic configuration adaption with its specific power saving + * ratio parameter (see #Argus_SetConfigurationDynamicAdaption) + * will limit the maximum integration depth along with the laser + * safety limitations. Increase the power saving ratio to increase + * accuracy. Note that laser safety limitations might already limit + * the maximum integration depth such that the power saving ratio + * is ineffective. + * . + * + * @param hnd The API handle; contains all internal states and data. + * @param value The measurement frame time in microseconds. + * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). *****************************************************************************/ status_t Argus_SetConfigurationFrameTime(argus_hnd_t *hnd, uint32_t value); /*!*************************************************************************** - * @brief Gets the frame time from a specified device. + * @brief Gets the frame time from a specified device. * - * @param hnd The API handle; contains all internal states and data. - * @param value The current frame time in microseconds. - * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). + * @param hnd The API handle; contains all internal states and data. + * @param value The current frame time in microseconds. + * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). *****************************************************************************/ status_t Argus_GetConfigurationFrameTime(argus_hnd_t *hnd, uint32_t *value); /*!*************************************************************************** - * @brief Sets the smart power save enabled flag to a specified device. - * @param hnd The API handle; contains all internal states and data. - * @param mode The targeted measurement mode. - * @param value The new smart power save enabled flag. - * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). + * @brief Sets the smart power save enabled flag to a specified device. + * + * @param hnd The API handle; contains all internal states and data. + * @param value The new smart power save enabled flag. + * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). *****************************************************************************/ status_t Argus_SetConfigurationSmartPowerSaveEnabled(argus_hnd_t *hnd, - argus_mode_t mode, bool value); /*!*************************************************************************** - * @brief Gets the smart power save enabled flag from a specified device. - * @param hnd The API handle; contains all internal states and data. - * @param mode The targeted measurement mode. - * @param value The current smart power save enabled flag. - * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). + * @brief Gets the smart power save enabled flag from a specified device. + * + * @param hnd The API handle; contains all internal states and data. + * @param value The current smart power save enabled flag. + * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). *****************************************************************************/ status_t Argus_GetConfigurationSmartPowerSaveEnabled(argus_hnd_t *hnd, - argus_mode_t mode, bool *value); /*!*************************************************************************** - * @brief Sets the Dual Frequency Mode (DFM) to a specified device. - * @param hnd The API handle; contains all internal states and data. - * @param mode The targeted measurement mode. - * @param value The new DFM mode value. - * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). + * @brief Sets the Dual Frequency Mode (DFM) to a specified device. + * + * @param hnd The API handle; contains all internal states and data. + * @param value The new DFM mode value. + * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). *****************************************************************************/ status_t Argus_SetConfigurationDFMMode(argus_hnd_t *hnd, - argus_mode_t mode, argus_dfm_mode_t value); /*!*************************************************************************** - * @brief Gets the Dual Frequency Mode (DFM) from a specified device. - * @param hnd The API handle; contains all internal states and data. - * @param mode The targeted measurement mode. - * @param value The current DFM mode value. - * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). + * @brief Gets the Dual Frequency Mode (DFM) from a specified device. + * + * @param hnd The API handle; contains all internal states and data. + * @param value The current DFM mode value. + * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). *****************************************************************************/ status_t Argus_GetConfigurationDFMMode(argus_hnd_t *hnd, - argus_mode_t mode, argus_dfm_mode_t *value); /*!*************************************************************************** - * @brief Sets the Shot Noise Monitor (SNM) mode to a specified device. - * @param hnd The API handle; contains all internal states and data. - * @param mode The targeted measurement mode. - * @param value The new SNM mode value. - * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). + * @brief Sets the Shot Noise Monitor (SNM) mode to a specified device. + * + * @param hnd The API handle; contains all internal states and data. + * @param value The new SNM mode value. + * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). *****************************************************************************/ status_t Argus_SetConfigurationShotNoiseMonitorMode(argus_hnd_t *hnd, - argus_mode_t mode, argus_snm_mode_t value); /*!*************************************************************************** - * @brief Gets the Shot Noise Montor (SNM) mode from a specified device. - * @param hnd The API handle; contains all internal states and data. - * @param mode The targeted measurement mode. - * @param value The current SNM mode value. - * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). + * @brief Gets the Shot Noise Monitor (SNM) mode from a specified device. + * + * @param hnd The API handle; contains all internal states and data. + * @param value The current SNM mode value. + * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). *****************************************************************************/ status_t Argus_GetConfigurationShotNoiseMonitorMode(argus_hnd_t *hnd, - argus_mode_t mode, argus_snm_mode_t *value); -#if 0 -///*!*************************************************************************** -// * @brief Sets the Crosstalk Monitor (XTM) mode to a specified device. -// * @param hnd The API handle; contains all internal states and data. -// * @param mode The targeted measurement mode. -// * @param value The new XTM mode value (true: enabled; false: disabled). -// * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). -// *****************************************************************************/ -//status_t Argus_SetConfigurationCrosstalkMonitorMode(argus_hnd_t * hnd, -// argus_mode_t mode, -// bool value); -// -///*!*************************************************************************** -// * @brief Gets the Crosstalk Monitor (XTM) mode from a specified device. -// * @param hnd The API handle; contains all internal states and data. -// * @param mode The targeted measurement mode. -// * @param value The current XTM mode value (true: enabled; false: disabled). -// * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). -// *****************************************************************************/ -//status_t Argus_GetConfigurationCrosstalkMonitorMode(argus_hnd_t * hnd, -// argus_mode_t mode, -// bool * value); -#endif /*!*************************************************************************** - * @brief Sets the full DCA module configuration to a specified device. +* @brief Sets the Crosstalk Monitor (XTM) mode to a specified device. +* +* @param hnd The API handle; contains all internal states and data. +* @param value The new XTM mode value (true: enabled; false: disabled). +* @return Returns the \link #status_t status\endlink (#STATUS_OK on success). +*****************************************************************************/ +status_t Argus_SetConfigurationCrosstalkMonitorMode(argus_hnd_t *hnd, + bool value); + +/*!*************************************************************************** +* @brief Gets the Crosstalk Monitor (XTM) mode from a specified device. +* +* @param hnd The API handle; contains all internal states and data. +* @param value The current XTM mode value (true: enabled; false: disabled). +* @return Returns the \link #status_t status\endlink (#STATUS_OK on success). +*****************************************************************************/ +status_t Argus_GetConfigurationCrosstalkMonitorMode(argus_hnd_t *hnd, + bool *value); + +/*!*************************************************************************** + * @brief Sets the full DCA module configuration to a specified device. * - * @param hnd The API handle; contains all internal states and data. - * @param mode The targeted measurement mode. - * @param value The new DCA configuration set. - * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). + * @param hnd The API handle; contains all internal states and data. + * @param value The new DCA configuration set. + * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). *****************************************************************************/ status_t Argus_SetConfigurationDynamicAdaption(argus_hnd_t *hnd, - argus_mode_t mode, argus_cfg_dca_t const *value); /*!*************************************************************************** - * @brief Gets the # from a specified device. + * @brief Gets the # from a specified device. * - * @param hnd The API handle; contains all internal states and data. - * @param mode The targeted measurement mode. - * @param value The current DCA configuration set value. - * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). + * @param hnd The API handle; contains all internal states and data. + * @param value The current DCA configuration set value. + * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). *****************************************************************************/ status_t Argus_GetConfigurationDynamicAdaption(argus_hnd_t *hnd, - argus_mode_t mode, argus_cfg_dca_t *value); /*!*************************************************************************** - * @brief Sets the pixel binning configuration parameters to a specified device. - * @param hnd The API handle; contains all internal states and data. - * @param mode The targeted measurement mode. - * @param value The new pixel binning configuration parameters. - * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). + * @brief Sets the pixel binning configuration parameters to a specified device. + * + * @param hnd The API handle; contains all internal states and data. + * @param value The new pixel binning configuration parameters. + * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). *****************************************************************************/ status_t Argus_SetConfigurationPixelBinning(argus_hnd_t *hnd, - argus_mode_t mode, argus_cfg_pba_t const *value); /*!*************************************************************************** - * @brief Gets the pixel binning configuration parameters from a specified device. - * @param hnd The API handle; contains all internal states and data. - * @param mode The targeted measurement mode. - * @param value The current pixel binning configuration parameters. - * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). + * @brief Gets the pixel binning configuration parameters from a specified device. + * + * @param hnd The API handle; contains all internal states and data. + * @param value The current pixel binning configuration parameters. + * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). *****************************************************************************/ status_t Argus_GetConfigurationPixelBinning(argus_hnd_t *hnd, - argus_mode_t mode, argus_cfg_pba_t *value); /*!*************************************************************************** - * @brief Gets the current unambiguous range in mm. - * @param hnd The API handle; contains all internal states and data. - * @param range_mm The returned range in mm. - * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). + * @brief Gets the current unambiguous range in mm. + * + * @param hnd The API handle; contains all internal states and data. + * @param range_mm The returned range in mm. + * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). *****************************************************************************/ status_t Argus_GetConfigurationUnambiguousRange(argus_hnd_t *hnd, uint32_t *range_mm); @@ -791,458 +1038,398 @@ status_t Argus_GetConfigurationUnambiguousRange(argus_hnd_t *hnd, /*!************************************************************************** * Calibration API **************************************************************************** - * @addtogroup arguscal + * @addtogroup argus_cal * @{ ****************************************************************************/ /*!*************************************************************************** - * @brief Sets the global range offset value to a specified device. + * @brief Sets the global range offset value to a specified device. * - * @details The global range offset is subtracted from the raw range values. + * @details The global range offset is subtracted from the raw range values. * - * @param hnd The API handle; contains all internal states and data. - * @param mode The targeted measurement mode. - * @param value The new global range offset in meter and Q0.15 format. - * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). + * @param hnd The API handle; contains all internal states and data. + * @param value The new global range offset in meter and Q0.15 format. + * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). *****************************************************************************/ status_t Argus_SetCalibrationGlobalRangeOffset(argus_hnd_t *hnd, - argus_mode_t mode, q0_15_t value); /*!*************************************************************************** - * @brief Gets the global range offset value from a specified device. + * @brief Gets the global range offset value from a specified device. * - * @details The global range offset is subtracted from the raw range values. + * @details The global range offset is subtracted from the raw range values. * - * @param hnd The API handle; contains all internal states and data. - * @param mode The targeted measurement mode. - * @param value The current global range offset in meter and Q0.15 format. - * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). + * @param hnd The API handle; contains all internal states and data. + * @param value The current global range offset in meter and Q0.15 format. + * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). *****************************************************************************/ status_t Argus_GetCalibrationGlobalRangeOffset(argus_hnd_t *hnd, - argus_mode_t mode, q0_15_t *value); /*!*************************************************************************** - * @brief Sets the relative pixel offset table to a specified device. + * @brief Sets the relative pixel offset table to a specified device. * * @details The relative pixel offset values are subtracted from the raw range - * values for each individual pixel. Note that a global range offset - * is applied additionally. The relative pixel offset values are meant - * to be with respect to the average range of all pixels, i.e. the - * average of all relative offsets should be 0! + * values for each individual pixel. Note that a global range offset + * is applied additionally. The relative pixel offset values are meant + * to be with respect to the average range of all pixels, i.e. the + * average of all relative offsets should be 0! * - * The crosstalk vector table is a two dimensional array of type - * #q0_15_t. + * The crosstalk vector table is a two dimensional array of type + * #q0_15_t, wrapped within the #argus_cal_offset_table_t structure. * - * The dimensions are: - * - size(0) = #ARGUS_PIXELS_X (Pixel count in x-direction) - * - size(1) = #ARGUS_PIXELS_Y (Pixel count in y-direction) - * . + * The dimensions are: + * - size(0) = #ARGUS_PIXELS_X (Pixel count in x-direction) + * - size(1) = #ARGUS_PIXELS_Y (Pixel count in y-direction) + * . * - * Its recommended to use the built-in pixel offset calibration - * sequence (see #Argus_ExecuteRelativeRangeOffsetCalibrationSequence) - * to determine the offset table for the current device. + * Its recommended to use the built-in pixel offset calibration + * sequence (see #Argus_ExecuteRelativeRangeOffsetCalibrationSequence) + * to determine the offset table for the current device. * - * If a constant offset table for all device needs to be incorporated - * into the sources, the #Argus_GetExternalPixelRangeOffsets_Callback - * should be used. + * If a constant offset table for all device needs to be incorporated + * into the sources, the #Argus_GetPixelRangeOffsets_Callback + * should be used. * - * @param hnd The API handle; contains all internal states and data. - * @param mode The targeted measurement mode. - * @param value The new relative range offset in meter and Q0.15 format. - * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). + * @param hnd The API handle; contains all internal states and data. + * @param value The new relative range offset in meter and Q0.15 format. + * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). *****************************************************************************/ -status_t Argus_SetCalibrationPixelRangeOffsets(argus_hnd_t *hnd, argus_mode_t mode, - q0_15_t value[ARGUS_PIXELS_X][ARGUS_PIXELS_Y]); +status_t Argus_SetCalibrationPixelRangeOffsets(argus_hnd_t *hnd, + argus_cal_offset_table_t const *value); /*!*************************************************************************** - * @brief Gets the relative pixel offset table from a specified device. + * @brief Gets the relative pixel offset table from a specified device. * * @details The relative pixel offset values are subtracted from the raw range - * values for each individual pixel. Note that a global range offset - * is applied additionally. The relative pixel offset values are meant - * to be with respect to the average range of all pixels, i.e. the - * average of all relative offsets should be 0! + * values for each individual pixel. Note that a global range offset + * is applied additionally. The relative pixel offset values are meant + * to be with respect to the average range of all pixels, i.e. the + * average of all relative offsets should be 0! * - * The crosstalk vector table is a two dimensional array of type - * #q0_15_t. + * The crosstalk vector table is a two dimensional array of type + * #q0_15_t, wrapped within the #argus_cal_offset_table_t structure. * - * The dimensions are: - * - size(0) = #ARGUS_PIXELS_X (Pixel count in x-direction) - * - size(1) = #ARGUS_PIXELS_Y (Pixel count in y-direction) - * . + * The dimensions are: + * - size(0) = #ARGUS_PIXELS_X (Pixel count in x-direction) + * - size(1) = #ARGUS_PIXELS_Y (Pixel count in y-direction) + * . * - * @param hnd The API handle; contains all internal states and data. - * @param mode The targeted measurement mode. - * @param value The current relative range offset in meter and Q0.15 format. - * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). + * @param hnd The API handle; contains all internal states and data. + * @param value The current relative range offset in meter and Q0.15 format. + * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). *****************************************************************************/ -status_t Argus_GetCalibrationPixelRangeOffsets(argus_hnd_t *hnd, argus_mode_t mode, - q0_15_t value[ARGUS_PIXELS_X][ARGUS_PIXELS_Y]); +status_t Argus_GetCalibrationPixelRangeOffsets(argus_hnd_t *hnd, + argus_cal_offset_table_t *value); /*!*************************************************************************** - * @brief Gets the relative pixel offset table from a specified device. + * @brief Resets the relative pixel offset values for the specified device to + * the factory calibrated default values. * * @details The relative pixel offset values are subtracted from the raw range - * values for each individual pixel. Note that a global range offset - * is applied additionally. The relative pixel offset values are meant - * to be with respect to the average range of all pixels, i.e. the - * average of all relative offsets should be 0! + * values for each individual pixel. Note that a global range offset + * is applied additionally. * - * The crosstalk vector table is a two dimensional array of type - * #q0_15_t. + * The factory defaults are device specific values. * - * The dimensions are: - * - size(0) = #ARGUS_PIXELS_X (Pixel count in x-direction) - * - size(1) = #ARGUS_PIXELS_Y (Pixel count in y-direction) - * - * The total offset table consists of the custom pixel offset values - * (set via #Argus_SetCalibrationPixelRangeOffsets) and the internal, - * factory calibrated device specific offset values. - * This is informational only! - * - * @param hnd The API handle; contains all internal states and data. - * @param mode The targeted measurement mode. - * @param value The current total relative range offset in meter and Q0.15 format. - * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). + * @param hnd The API handle; contains all internal states and data. + * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). *****************************************************************************/ -status_t Argus_GetCalibrationTotalPixelRangeOffsets(argus_hnd_t *hnd, argus_mode_t mode, - q0_15_t value[ARGUS_PIXELS_X][ARGUS_PIXELS_Y]); - +status_t Argus_ResetCalibrationPixelRangeOffsets(argus_hnd_t *hnd); /*!*************************************************************************** - * @brief Resets the relative pixel offset values for the specified device to - * the factory calibrated default values. - * - * @details The relative pixel offset values are subtracted from the raw range - * values for each individual pixel. Note that a global range offset - * is applied additionally. - * - * The factory defaults are device specific values. - * - * @param hnd The API handle; contains all internal states and data. - * @param mode The targeted measurement mode. - * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). - *****************************************************************************/ -status_t Argus_ResetCalibrationPixelRangeOffsets(argus_hnd_t *hnd, argus_mode_t mode); - -/*!*************************************************************************** - * @brief A callback that returns the external pixel range offsets. + * @brief A callback that returns the external pixel range offsets. * * @details The function needs to be implemented by the host application in - * order to set the external pixel range offsets values upon system - * initialization. If not defined in user code, the default - * implementation will return an all zero offset table, assuming there - * is no (additional) external pixel range offset values. + * order to set the external pixel range offsets values upon system + * initialization. If not defined in user code, the default + * implementation will return an all zero offset table, assuming there + * is no (additional) external pixel range offset values. * - * If defined in user code, the function must fill all offset values - * in the provided \par offsets parameter with external range offset - * values. - * The values can be obtained by the calibration routine. + * If defined in user code, the function must fill all offset values + * in the provided \par offsets parameter with external range offset + * values. + * The values can be obtained by the calibration routine. * - * Example usage: + * Example usage: * - * @code - * status_t Argus_GetExternalPixelRangeOffsets_Callback(q0_15_t offsets[ARGUS_PIXELS_X][ARGUS_PIXELS_Y], - * argus_mode_t mode) - * { - * (void) mode; // Ignore mode; use same values for all modes. - * memset(offsets, 0, sizeof(q0_15_t) * ARGUS_PIXELS); + * @code + * status_t Argus_GetPixelRangeOffsets_Callback(argus_cal_offset_table_t offsets) + * { + * memset(offsets, 0, sizeof(argus_cal_offset_t)); * - * // Set offset values in meter and Q0.15 format. - * offsets[0][0].dS = -16384; offsets[0][0].dC = -32768; - * offsets[0][1].dS = -32768; offsets[0][1].dC = 0; - * offsets[0][2].dS = 16384; offsets[0][2].dC = -16384; - * // etc. - * } - * @endcode + * // Set offset values in meter and Q0.15 format. + * offsets.Table[0][0] = -3542; + * offsets.Table[0][1] = -4385; + * offsets.Table[0][2] = 2953; + * // etc. + * } + * @endcode * - * @param offsets The pixel range offsets in meter and Q0.15 format; to be - * filled with data. - * @param mode Determines the current measurement mode; can be ignored if - * only a single measurement mode is utilized. + * @param offsets The pixel range offsets in meter and Q0.15 format; to be + * filled with data. + * @param mode The current measurement mode. *****************************************************************************/ -void Argus_GetExternalPixelRangeOffsets_Callback(q0_15_t offsets[ARGUS_PIXELS_X][ARGUS_PIXELS_Y], - argus_mode_t mode); +void Argus_GetPixelRangeOffsets_Callback(argus_cal_offset_table_t *offsets, + argus_mode_t const mode); /*!*************************************************************************** - * @brief Sets the sample time for the range offset calibration sequence. + * @brief Sets the sample time for the range offset calibration sequence. * - * @details Gets the measurement sample acquisition time for executing the - * range offset calibration sequence and generate the offset data.\n - * Units: msec. + * @details Gets the measurement sample acquisition time for executing the + * range offset calibration sequence and generate the offset data.\n + * Units: msec. * - * @param hnd The API handle; contains all internal states and data. - * @param value The new range offset calibration sequence sample time. - * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). + * @param hnd The API handle; contains all internal states and data. + * @param value The new range offset calibration sequence sample time. + * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). *****************************************************************************/ status_t Argus_SetCalibrationRangeOffsetSequenceSampleTime(argus_hnd_t *hnd, uint16_t value); /*!*************************************************************************** - * @brief Gets the sample time for the range offset calibration sequence. + * @brief Gets the sample time for the range offset calibration sequence. * - * @details Gets the measurement sample acquisition time for executing the - * range offset calibration sequence and generate the ooffset data.\n - * Units: msec. + * @details Gets the measurement sample acquisition time for executing the + * range offset calibration sequence and generate the offset data.\n + * Units: msec. * - * @param hnd The API handle; contains all internal states and data. - * @param value The current range offset calibration sequence sample time. - * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). + * @param hnd The API handle; contains all internal states and data. + * @param value The current range offset calibration sequence sample time. + * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). *****************************************************************************/ status_t Argus_GetCalibrationRangeOffsetSequenceSampleTime(argus_hnd_t *hnd, uint16_t *value); /*!*************************************************************************** - * @brief Sets the pixel-to-pixel crosstalk compensation parameters to a specified device. + * @brief Sets the pixel-to-pixel crosstalk compensation parameters to a specified device. * - * @param hnd The API handle; contains all internal states and data. - * @param mode The targeted measurement mode. - * @param value The new pixel-to-pixel crosstalk compensation parameters. - * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). + * @param hnd The API handle; contains all internal states and data. + * @param value The new pixel-to-pixel crosstalk compensation parameters. + * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). *****************************************************************************/ status_t Argus_SetCalibrationCrosstalkPixel2Pixel(argus_hnd_t *hnd, - argus_mode_t mode, argus_cal_p2pxtalk_t const *value); /*!*************************************************************************** - * @brief Gets the pixel-to-pixel crosstalk compensation parameters from a specified device. + * @brief Gets the pixel-to-pixel crosstalk compensation parameters from a specified device. * - * @param hnd The API handle; contains all internal states and data. - * @param mode The targeted measurement mode. - * @param value The current pixel-to-pixel crosstalk compensation parameters. - * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). + * @param hnd The API handle; contains all internal states and data. + * @param value The current pixel-to-pixel crosstalk compensation parameters. + * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). *****************************************************************************/ status_t Argus_GetCalibrationCrosstalkPixel2Pixel(argus_hnd_t *hnd, - argus_mode_t mode, argus_cal_p2pxtalk_t *value); /*!*************************************************************************** - * @brief Sets the custom crosstalk vector table to a specified device. + * @brief Sets the custom crosstalk vector table to a specified device. * * @details The crosstalk vectors are subtracted from the raw sampling data - * in the data evaluation phase. + * in the data evaluation phase. * - * The crosstalk vector table is a three dimensional array of type - * #xtalk_t. + * The crosstalk vector table is a three dimensional array of type + * #xtalk_t. The #argus_cal_xtalk_table_t is the corresponding + * typedef for the required data. * - * The dimensions are: - * - size(0) = #ARGUS_DFM_FRAME_COUNT (Dual-frequency mode A- or B-frame) - * - size(1) = #ARGUS_PIXELS_X (Pixel count in x-direction) - * - size(2) = #ARGUS_PIXELS_Y (Pixel count in y-direction) - * . + * The dimensions are: + * - size(0) = #ARGUS_DFM_FRAME_COUNT (Dual-frequency mode A- or B-frame) + * - size(1) = #ARGUS_PIXELS_X (Pixel count in x-direction) + * - size(2) = #ARGUS_PIXELS_Y (Pixel count in y-direction) + * . * - * Its recommended to use the built-in crosstalk calibration sequence - * (see #Argus_ExecuteXtalkCalibrationSequence) to determine the - * crosstalk vector table. + * Its recommended to use the built-in crosstalk calibration sequence + * (see #Argus_ExecuteXtalkCalibrationSequence) to determine the + * crosstalk vector table. * - * If a constant table for all device needs to be incorporated into - * the sources, the #Argus_GetExternalCrosstalkVectorTable_Callback - * should be used. + * If a constant table for all device needs to be incorporated into + * the sources, the #Argus_GetCrosstalkVectorTable_Callback + * should be used. * - * @param hnd The API handle; contains all internal states and data. - * @param mode The targeted measurement mode. - * @param value The new crosstalk vector table. - * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). + * @param hnd The API handle; contains all internal states and data. + * @param value The new crosstalk vector table. + * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). *****************************************************************************/ status_t Argus_SetCalibrationCrosstalkVectorTable(argus_hnd_t *hnd, - argus_mode_t mode, - xtalk_t value[ARGUS_DFM_FRAME_COUNT][ARGUS_PIXELS_X][ARGUS_PIXELS_Y]); + argus_cal_xtalk_table_t const *value); /*!*************************************************************************** - * @brief Gets the custom crosstalk vector table from a specified device. + * @brief Gets the custom crosstalk vector table from a specified device. * * @details The crosstalk vectors are subtracted from the raw sampling data - * in the data evaluation phase. + * in the data evaluation phase. * - * The crosstalk vector table is a three dimensional array of type - * #xtalk_t. + * The crosstalk vector table is a three dimensional array of type + * #xtalk_t. The #argus_cal_xtalk_table_t is the corresponding + * typedef for the required data. * - * The dimensions are: - * - size(0) = #ARGUS_DFM_FRAME_COUNT (Dual-frequency mode A- or B-frame) - * - size(1) = #ARGUS_PIXELS_X (Pixel count in x-direction) - * - size(2) = #ARGUS_PIXELS_Y (Pixel count in y-direction) - * . + * The dimensions are: + * - size(0) = #ARGUS_DFM_FRAME_COUNT (Dual-frequency mode A- or B-frame) + * - size(1) = #ARGUS_PIXELS_X (Pixel count in x-direction) + * - size(2) = #ARGUS_PIXELS_Y (Pixel count in y-direction) + * . * - * @param hnd The API handle; contains all internal states and data. - * @param mode The targeted measurement mode. - * @param value The current crosstalk vector table. - * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). + * @param hnd The API handle; contains all internal states and data. + * @param value The current crosstalk vector table. + * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). *****************************************************************************/ status_t Argus_GetCalibrationCrosstalkVectorTable(argus_hnd_t *hnd, - argus_mode_t mode, - xtalk_t value[ARGUS_DFM_FRAME_COUNT][ARGUS_PIXELS_X][ARGUS_PIXELS_Y]); + argus_cal_xtalk_table_t *value); /*!*************************************************************************** - * @brief Gets the factory calibrated default crosstalk vector table for the - * specified device. + * @brief Resets the crosstalk vector table for the specified device to the + * factory calibrated default values. * * @details The crosstalk vectors are subtracted from the raw sampling data - * in the data evaluation phase. - * - * The crosstalk vector table is a three dimensional array of type - * #xtalk_t. - * - * The dimensions are: - * - size(0) = #ARGUS_DFM_FRAME_COUNT (Dual-frequency mode A- or B-frame) - * - size(1) = #ARGUS_PIXELS_X (Pixel count in x-direction) - * - size(2) = #ARGUS_PIXELS_Y (Pixel count in y-direction) - * . - * - * The total vector table consists of the custom crosstalk vector - * table (set via #Argus_SetCalibrationCrosstalkVectorTable) and - * an internal, factory calibrated device specific vector table. - * This is informational only! - * - * @param hnd The API handle; contains all internal states and data. - * @param mode The targeted measurement mode. - * @param value The current total crosstalk vector table. - * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). - *****************************************************************************/ -status_t Argus_GetCalibrationTotalCrosstalkVectorTable(argus_hnd_t *hnd, - argus_mode_t mode, - xtalk_t value[ARGUS_DFM_FRAME_COUNT][ARGUS_PIXELS_X][ARGUS_PIXELS_Y]); - -/*!*************************************************************************** - * @brief Resets the crosstalk vector table for the specified device to the - * factory calibrated default values. - * - * @details The crosstalk vectors are subtracted from the raw sampling data - * in the data evaluation phase. + * in the data evaluation phase. * * - * The factory defaults are device specific calibrated values. + * The factory defaults are device specific calibrated values. * - * @param hnd The API handle; contains all internal states and data. - * @param mode The targeted measurement mode. - * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). + * @param hnd The API handle; contains all internal states and data. + * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). *****************************************************************************/ -status_t Argus_ResetCalibrationCrosstalkVectorTable(argus_hnd_t *hnd, - argus_mode_t mode); +status_t Argus_ResetCalibrationCrosstalkVectorTable(argus_hnd_t *hnd); /*!*************************************************************************** - * @brief Sets the sample time for the crosstalk calibration sequence. + * @brief Sets the sample time for the crosstalk calibration sequence. * - * @details Sets the measurement sample acquisition time for executing the - * crosstalk calibration sequence and generate the crosstalk data.\n - * Units: msec. + * @details Sets the measurement sample acquisition time for executing the + * crosstalk calibration sequence and generate the crosstalk data.\n + * Units: msec. * - * @param hnd The API handle; contains all internal states and data. - * @param value The new crosstalk calibration sequence sample time. - * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). + * @param hnd The API handle; contains all internal states and data. + * @param value The new crosstalk calibration sequence sample time. + * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). *****************************************************************************/ status_t Argus_SetCalibrationCrosstalkSequenceSampleTime(argus_hnd_t *hnd, uint16_t value); /*!*************************************************************************** - * @brief Gets the sample time for the crosstalk calibration sequence. + * @brief Gets the sample time for the crosstalk calibration sequence. * - * @details Gets the measurement sample acquisition time for executing the - * crosstalk calibration sequence and generate the crosstalk data.\n - * Units: msec. + * @details Gets the measurement sample acquisition time for executing the + * crosstalk calibration sequence and generate the crosstalk data.\n + * Units: msec. * - * @param hnd The API handle; contains all internal states and data. - * @param value The current crosstalk calibration sequence sample time. - * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). + * @param hnd The API handle; contains all internal states and data. + * @param value The current crosstalk calibration sequence sample time. + * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). *****************************************************************************/ status_t Argus_GetCalibrationCrosstalkSequenceSampleTime(argus_hnd_t *hnd, uint16_t *value); /*!*************************************************************************** - * @brief Sets the max. amplitude threshold for the crosstalk calibration sequence. + * @brief Sets the max. amplitude threshold for the crosstalk calibration sequence. * - * @details The maximum amplitude threshold defines a maximum crosstalk vector - * amplitude before causing an error message. If the crosstalk is - * too high, there is usually an issue with the measurement setup, i.e. - * there is still a measurement signal detected. + * @details The maximum amplitude threshold defines a maximum crosstalk vector + * amplitude before causing an error message. If the crosstalk is + * too high, there is usually an issue with the measurement setup, i.e. + * there is still a measurement signal detected. * - * @param hnd The API handle; contains all internal states and data. - * @param value The new crosstalk calibration sequence maximum amplitude - * threshold value in UQ12.4 format. - * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). + * @param hnd The API handle; contains all internal states and data. + * @param value The new crosstalk calibration sequence maximum amplitude + * threshold value in UQ12.4 format. + * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). *****************************************************************************/ status_t Argus_SetCalibrationCrosstalkSequenceAmplitudeThreshold(argus_hnd_t *hnd, uq12_4_t value); /*!*************************************************************************** - * @brief Gets the max. amplitude threshold for the crosstalk calibration sequence. + * @brief Gets the max. amplitude threshold for the crosstalk calibration sequence. * - * @details The maximum amplitude threshold defines a maximum crosstalk vector - * amplitude before causing an error message. If the crosstalk is - * too high, there is usually an issue with the measurement setup, i.e. - * there is still a measurement signal detected. + * @details The maximum amplitude threshold defines a maximum crosstalk vector + * amplitude before causing an error message. If the crosstalk is + * too high, there is usually an issue with the measurement setup, i.e. + * there is still a measurement signal detected. * - * @param hnd The API handle; contains all internal states and data. - * @param value The current max. amplitude threshold value in UQ12.4 format. - * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). + * @param hnd The API handle; contains all internal states and data. + * @param value The current max. amplitude threshold value in UQ12.4 format. + * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). *****************************************************************************/ status_t Argus_GetCalibrationCrosstalkSequenceAmplitudeThreshold(argus_hnd_t *hnd, uq12_4_t *value); -/*!*************************************************************************** - * @brief Sets the sample count for the substrate voltage calibration sequence. - * - * @param hnd The API handle; contains all internal states and data. - * @param value The new substrate voltage calibration sequence sample count. - * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). - *****************************************************************************/ -status_t Argus_SetCalibrationVsubSequenceSampleCount(argus_hnd_t *hnd, - uint16_t value); /*!*************************************************************************** - * @brief Gets the sample count for the substrate voltage calibration sequence. + * @brief Clears all user calibration values from NVM for the specified device. * - * @param hnd The API handle; contains all internal states and data. - * @param value The current substrate voltage calibration sequence sample count. - * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). + * @details The user calibration values are stored in the non-volatile memory + * (NVM) if corresponding \link #argus_nvm NVM hardware layer\endlink + * is implemented. This method clears the user calibration data from + * the non-volatile memory. + * + * @warning This does not reset the currently set calibration values to + * factory defaults! + * + * @param hnd The API handle; contains all internal states and data. + * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). *****************************************************************************/ -status_t Argus_GetCalibrationVsubSequenceSampleCount(argus_hnd_t *hnd, - uint16_t *value); +status_t Argus_ClearUserCalibration(argus_hnd_t *hnd); + /*!*************************************************************************** - * @brief A callback that returns the external crosstalk vector table. + * @brief A callback that returns the external crosstalk vector table. * * @details The function needs to be implemented by the host application in - * order to set the external crosstalk vector table upon system - * initialization. If not defined in user code, the default - * implementation will return an all zero vector table, assuming there - * is no (additional) external crosstalk. + * order to set the external crosstalk vector table upon system + * initialization. If not defined in user code, the default + * implementation will return an all zero vector table, assuming there + * is no (additional) external crosstalk. * - * If defined in user code, the function must fill all vector values - * in the provided \par xtalk parameter with external crosstalk values. - * The values can be obtained by the calibration routine. + * If defined in user code, the function must fill all vector values + * in the provided \par crosstalk parameter with external crosstalk + * values. The values can be obtained by the calibration routine. * - * Example usage: + * Example usage: * - * @code - * status_t Argus_GetExternalCrosstalkVectorTable_Callback(xtalk_t xtalk[ARGUS_DFM_FRAME_COUNT][ARGUS_PIXELS_X][ARGUS_PIXELS_Y], - * argus_mode_t mode) - * { - * (void) mode; // Ignore mode; use same values for all modes. - * memset(&xtalk, 0, sizeof(xtalk)); + * @code + * status_t Argus_GetCrosstalkVectorTable_Callback( + * argus_cal_xtalk_table_t * xtalk) + * { + * memset(xtalk, 0, sizeof(argus_cal_xtalk_table_t)); * - * // Set crosstalk vectors in Q11.4 format. - * // Note on dual-frequency frame index: 0 = A-Frame; 1 = B-Frame - * xtalk[0][0][0].dS = -9; xtalk[0][0][0].dC = -11; - * xtalk[0][0][1].dS = -13; xtalk[0][0][1].dC = -16; - * xtalk[0][0][2].dS = 6; xtalk[0][0][2].dC = -18; - * // etc. - * } - * @endcode + * // Set crosstalk vectors in Q11.4 format. + * // Note on dual-frequency frame index: 0 = A-Frame; 1 = B-Frame + * xtalk.FrameA[0][0].dS = -9; xtalk.FrameB[0][0].dC = -11; + * xtalk.FrameA[0][1].dS = -13; xtalk.FrameB[0][1].dC = -16; + * xtalk.FrameA[0][2].dS = 6; xtalk.FrameB[0][2].dC = -18; + * // etc. + * } + * @endcode * - * @param xtalk The crosstalk vector array; to be filled with data. - * @param mode Determines the current measurement mode; can be ignored if - * only a single measurement mode is utilized. + * @param xtalk The crosstalk vector array; to be filled with data. + * @param mode The current measurement mode. *****************************************************************************/ -void Argus_GetExternalCrosstalkVectorTable_Callback(xtalk_t - xtalk[ARGUS_DFM_FRAME_COUNT][ARGUS_PIXELS_X][ARGUS_PIXELS_Y], - argus_mode_t mode); +void Argus_GetCrosstalkVectorTable_Callback(argus_cal_xtalk_table_t *xtalk, + argus_mode_t const mode); -#ifdef __cplusplus -} -#endif +/*!*************************************************************************** + * @brief Gets the currently calibrated Golden Pixel coordinates. + * + * @details The Golden Pixel is the pixel that is located at the center of the + * receiving light beam. Thus it it the one that receives the most + * signal and plays a central role in 1D measurement systems. + * + * The function fills the provided \p x and \p y parameters with + * the Golden Pixel coordinates. Typical values are x = 5 and y = 1 + * or 2. But the actual values depend on the specific sensor. + * + * Please also note the utility functions provided in the \ref argus_map + * module to convert between pixel coordinates and channel numbers or + * shift pixel maps by a position offset (#ShiftSelectedPixels) or + * generate pixel masks centered around the Golden Pixel + * (#FillPixelMask). + * + * @param hnd The API handle; contains all internal states and data. + * @param x The Golden Pixel x-coordinate. + * @param y The Golden Pixel y-coordinate. + * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). + *****************************************************************************/ +status_t Argus_GetCalibrationGoldenPixel(argus_hnd_t const *hnd, uint8_t *x, uint8_t *y); /*! @} */ +#ifdef __cplusplus +} // extern "C" +#endif #endif /* ARGUS_API_H */ diff --git a/src/drivers/distance_sensor/broadcom/afbrs50/Inc/api/argus_dca.h b/src/drivers/distance_sensor/broadcom/afbrs50/Inc/api/argus_dca.h index f23d117648..8f6b40bdc5 100644 --- a/src/drivers/distance_sensor/broadcom/afbrs50/Inc/api/argus_dca.h +++ b/src/drivers/distance_sensor/broadcom/afbrs50/Inc/api/argus_dca.h @@ -1,12 +1,12 @@ /*************************************************************************//** * @file - * @brief This file is part of the AFBR-S50 API. - * @details Defines the dynamic configuration adaption (DCA) setup parameters - * and data structure. + * @brief This file is part of the AFBR-S50 API. + * @details Defines the dynamic configuration adaption (DCA) setup parameters + * and data structure. * * @copyright * - * Copyright (c) 2021, Broadcom Inc + * Copyright (c) 2023, Broadcom Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,34 +37,37 @@ #ifndef ARGUS_DCA_H #define ARGUS_DCA_H +#ifdef __cplusplus +extern "C" { +#endif /*!*************************************************************************** - * @defgroup argusdca Dynamic Configuration Adaption - * @ingroup argusapi + * @defgroup argus_dca Dynamic Configuration Adaption + * @ingroup argus_api * - * @brief Dynamic Configuration Adaption (DCA) parameter definitions and API functions. + * @brief Dynamic Configuration Adaption (DCA) parameter definitions and API functions. * - * @details The DCA contains an algorithms that detect ambient conditions - * and adopt the device configuration to the changing parameters - * dynamically while operating the sensor. This is achieved by - * rating the currently received signal quality and changing the - * device configuration accordingly to the gathered information - * from the current measurement frame results before the next - * integration cycle starts. + * @details The DCA contains an algorithms that detect ambient conditions + * and adopt the device configuration to the changing parameters + * dynamically while operating the sensor. This is achieved by + * rating the currently received signal quality and changing the + * device configuration accordingly to the gathered information + * from the current measurement frame results before the next + * integration cycle starts. * - * The DCA consists of the following features: - * - Static or Dynamic mode. The first is utilizing the nominal - * values while the latter is dynamically adopting between min. - * and max. value and starting from the nominal values. - * - Analog Integration Depth Adaption (from multiple patterns down to single pulses) - * - Optical Output Power Adaption - * - Pixel Input Gain Adaption (w/ ambient light rejection) - * - ADC Sensitivity (i.e. ADC Range) Adaption - * - Power Saving Ratio (to decrease the average output power and thus the current consumption) - * - All that features are heeding the Laser Safety limits. - * . + * The DCA consists of the following features: + * - Static or Dynamic mode. The first is utilizing the nominal + * values while the latter is dynamically adopting between min. + * and max. value and starting from the nominal values. + * - Analog Integration Depth Adaption (from multiple patterns down to single pulses) + * - Optical Output Power Adaption + * - Pixel Input Gain Adaption (w/ ambient light rejection) + * - ADC Sensitivity (i.e. ADC Range) Adaption + * - Power Saving Ratio (to decrease the average output power and thus the current consumption) + * - All that features are heeding the Laser Safety limits. + * . * - * @addtogroup argusdca + * @addtogroup argus_dca * @{ *****************************************************************************/ @@ -73,39 +76,26 @@ /*! The minimum amplitude threshold value. */ -#define ARGUS_CFG_DCA_ATH_MIN (1U << 6U) +#define ARGUS_CFG_DCA_ATH_MIN (1U << 6U) /*! The maximum amplitude threshold value. */ -#define ARGUS_CFG_DCA_ATH_MAX (0xFFFFU) +#define ARGUS_CFG_DCA_ATH_MAX (0xFFFFU) /*! The minimum saturated pixel threshold value. */ -#define ARGUS_CFG_DCA_PXTH_MIN (1U) +#define ARGUS_CFG_DCA_PXTH_MIN (1U) /*! The maximum saturated pixel threshold value. */ -#define ARGUS_CFG_DCA_PXTH_MAX (33U) +#define ARGUS_CFG_DCA_PXTH_MAX (33U) /*! The maximum analog integration depth in UQ10.6 format, * i.e. the maximum pattern count per sample. */ -#define ARGUS_CFG_DCA_DEPTH_MAX ((uq10_6_t)(ADS_SEQCT_N_MASK << (6U - ADS_SEQCT_N_SHIFT))) +#define ARGUS_CFG_DCA_DEPTH_MAX ((uq10_6_t)(0xFFC0U)) /*! The minimum analog integration depth in UQ10.6 format, * i.e. the minimum pattern count per sample. */ -#define ARGUS_CFG_DCA_DEPTH_MIN ((uq10_6_t)(1U)) // 1/64, i.e. 1/2 nibble - - -/*! The maximum optical output power, i.e. the maximum VCSEL high current in LSB. */ -#define ARGUS_CFG_DCA_POWER_MAX_LSB (ADS_LASET_VCSEL_HC1_MASK >> ADS_LASET_VCSEL_HC1_SHIFT) - -/*! The minimum optical output power, i.e. the minimum VCSEL high current in mA. */ -#define ARGUS_CFG_DCA_POWER_MIN_LSB (1) - -/*! The maximum optical output power, i.e. the maximum VCSEL high current in LSB. */ -#define ARGUS_CFG_DCA_POWER_MAX (ADS0032_HIGH_CURRENT_LSB2MA(ARGUS_CFG_DCA_POWER_MAX_LSB + 1)) - -/*! The minimum optical output power, i.e. the minimum VCSEL high current in mA. */ -#define ARGUS_CFG_DCA_POWER_MIN (1) +#define ARGUS_CFG_DCA_DEPTH_MIN ((uq10_6_t)(1U)) // 1/64, i.e. 1/2 nibble /*! The dynamic configuration algorithm Pixel Input Gain stage count. */ @@ -139,9 +129,9 @@ /*!*************************************************************************** - * @brief The dynamic configuration algorithm enable flags. + * @brief The dynamic configuration algorithm enable flags. *****************************************************************************/ -typedef enum { +typedef enum argus_dca_enable_t { /*! @internal * * DCA is disabled and will be completely skipped. @@ -160,9 +150,9 @@ typedef enum { } argus_dca_enable_t; /*!*************************************************************************** - * @brief The DCA amplitude evaluation method. + * @brief The DCA amplitude evaluation method. *****************************************************************************/ -typedef enum { +typedef enum argus_dca_amplitude_mode_t { /*! Evaluate the DCA amplitude as the maximum of all valid amplitudes. */ DCA_AMPLITUDE_MAX = 1U, @@ -172,9 +162,9 @@ typedef enum { } argus_dca_amplitude_mode_t; /*!*************************************************************************** - * @brief The dynamic configuration algorithm Optical Output Power stages enumerator. + * @brief The dynamic configuration algorithm Optical Output Power stages enumerator. *****************************************************************************/ -typedef enum { +typedef enum argus_dca_power_t { /*! Use low output power stage. */ DCA_POWER_LOW = 0, @@ -187,9 +177,9 @@ typedef enum { } argus_dca_power_t; /*!*************************************************************************** - * @brief The dynamic configuration algorithm Pixel Input Gain stages enumerator. + * @brief The dynamic configuration algorithm Pixel Input Gain stages enumerator. *****************************************************************************/ -typedef enum { +typedef enum argus_dca_gain_t { /*! Low gain stage. */ DCA_GAIN_LOW = 0, @@ -206,113 +196,113 @@ typedef enum { /*!*************************************************************************** - * @brief State flags for the current frame. - * @details State flags determine the current state of the measurement frame: - * - [0]: #ARGUS_STATE_MEASUREMENT_MODE - * - [1]: #ARGUS_STATE_DUAL_FREQ_MODE - * - [2]: #ARGUS_STATE_MEASUREMENT_FREQ - * - [3]: #ARGUS_STATE_DEBUG_MODE - * - [4]: #ARGUS_STATE_WEAK_SIGNAL - * - [5]: #ARGUS_STATE_BGL_WARNING - * - [6]: #ARGUS_STATE_BGL_ERROR - * - [7]: #ARGUS_STATE_PLL_LOCKED - * - [8]: #ARGUS_STATE_LASER_WARNING - * - [9]: #ARGUS_STATE_LASER_ERROR - * - [10]: #ARGUS_STATE_HAS_DATA - * - [11]: #ARGUS_STATE_HAS_AUX_DATA - * - [12]: #ARGUS_STATE_DCA_MAX - * - [13]: DCA Power Stage - * - [14-15]: DCA Gain Stages - * . + * @brief State flags for the current frame. + * @details State flags determine the current state of the measurement frame: + * - [0]: #ARGUS_STATE_XTALK_MONITOR_ACTIVE + * - [1]: #ARGUS_STATE_DUAL_FREQ_MODE + * - [2]: #ARGUS_STATE_MEASUREMENT_FREQ + * - [3]: #ARGUS_STATE_DEBUG_MODE + * - [4]: #ARGUS_STATE_WEAK_SIGNAL + * - [5]: #ARGUS_STATE_BGL_WARNING + * - [6]: #ARGUS_STATE_BGL_ERROR + * - [7]: #ARGUS_STATE_PLL_LOCKED + * - [8]: #ARGUS_STATE_LASER_WARNING + * - [9]: #ARGUS_STATE_LASER_ERROR + * - [10]: #ARGUS_STATE_HAS_DATA + * - [11]: #ARGUS_STATE_HAS_AUX_DATA + * - [12]: #ARGUS_STATE_DCA_MAX + * - [13]: DCA Power Stage + * - [14-15]: DCA Gain Stages + * . *****************************************************************************/ -typedef enum { +typedef enum argus_state_t { /*! No state flag set. */ ARGUS_STATE_NONE = 0, - /*! 0x0001: Measurement Mode. - * - 0: Mode A: Long Range / Medium Precision - * - 1: Mode B: Short Range / High Precision */ - ARGUS_STATE_MEASUREMENT_MODE = 1U << 0U, + /*! 0x0001: Crosstalk Monitor is enabled and updating. + * - 0: Inactive: crosstalk monitor values are not updated, + * - 1: Active: crosstalk monitor values are updated. */ + ARGUS_STATE_XTALK_MONITOR_ACTIVE = 1U << 0U, /*! 0x0002: Dual Frequency Mode Enabled. - * - 0: Disabled: measurements with base frequency, - * - 1: Enabled: measurement with detuned frequency. */ + * - 0: Disabled: measurements with base frequency, + * - 1: Enabled: measurement with detuned frequency. */ ARGUS_STATE_DUAL_FREQ_MODE = 1U << 1U, /*! 0x0004: Measurement Frequency for Dual Frequency Mode * (only if #ARGUS_STATE_DUAL_FREQ_MODE flag is set). - * - 0: A-Frame w/ detuned frequency, - * - 1: B-Frame w/ detuned frequency */ + * - 0: A-Frame w/ detuned frequency, + * - 1: B-Frame w/ detuned frequency */ ARGUS_STATE_MEASUREMENT_FREQ = 1U << 2U, /*! 0x0008: Debug Mode. If set, the range value of erroneous pixels - * are not cleared or reset. - * - 0: Disabled (default). - * - 1: Enabled. */ + * are not cleared or reset. + * - 0: Disabled (default). + * - 1: Enabled. */ ARGUS_STATE_DEBUG_MODE = 1U << 3U, /*! 0x0010: Weak Signal Flag. - * Set whenever the Pixel Binning Algorithm is detecting a - * weak signal, i.e. if the amplitude dies not reach its - * (absolute) threshold. If the Golden Pixel is enabled, - * this also indicates that the Pixel Binning Algorithm - * falls back to the Golden Pixel. - * - 0: Normal Signal. - * - 1: Weak Signal or Golden Pixel Mode. */ + * Set whenever the Pixel Binning Algorithm is detecting a + * weak signal, i.e. if the amplitude dies not reach its + * (absolute) threshold. If the Golden Pixel is enabled, + * this also indicates that the Pixel Binning Algorithm + * falls back to the Golden Pixel. + * - 0: Normal Signal. + * - 1: Weak Signal or Golden Pixel Mode. */ ARGUS_STATE_WEAK_SIGNAL = 1U << 4U, /*! 0x0020: Background Light Warning Flag. - * Set whenever the background light is very high and the - * measurement data might be unreliable. - * - 0: No Warning: Background Light is within valid range. - * - 1: Warning: Background Light is very high. */ + * Set whenever the background light is very high and the + * measurement data might be unreliable. + * - 0: No Warning: Background Light is within valid range. + * - 1: Warning: Background Light is very high. */ ARGUS_STATE_BGL_WARNING = 1U << 5U, /*! 0x0040: Background Light Error Flag. - * Set whenever the background light is too high and the - * measurement data is unreliable or invalid. - * - 0: No Error: Background Light is within valid range. - * - 1: Error: Background Light is too high. */ + * Set whenever the background light is too high and the + * measurement data is unreliable or invalid. + * - 0: No Error: Background Light is within valid range. + * - 1: Error: Background Light is too high. */ ARGUS_STATE_BGL_ERROR = 1U << 6U, /*! 0x0080: PLL_LOCKED bit. - * - 0: PLL not locked at start of integration. - * - 1: PLL locked at start of integration. */ + * - 0: PLL not locked at start of integration. + * - 1: PLL locked at start of integration. */ ARGUS_STATE_PLL_LOCKED = 1U << 7U, /*! 0x0100: Laser Failure Warning Flag. - * Set whenever the an invalid system condition is detected. - * (i.e. DCA at max state but no amplitude on any (incl. reference) - * pixel, not amplitude but any saturated pixel). - * - 0: No Warning: Laser is operating properly. - * - 1: Warning: Invalid laser conditions detected. If the invalid - * condition stays, a laser malfunction error is raised. */ + * Set whenever the an invalid system condition is detected. + * (i.e. DCA at max state but no amplitude on any (incl. reference) + * pixel, not amplitude but any saturated pixel). + * - 0: No Warning: Laser is operating properly. + * - 1: Warning: Invalid laser conditions detected. If the invalid + * condition stays, a laser malfunction error is raised. */ ARGUS_STATE_LASER_WARNING = 1U << 8U, /*! 0x0200: Laser Failure Error Flag. - * Set whenever a laser malfunction error is raised and the - * system is put into a safe state. - * - 0: No Error: Laser is operating properly. - * - 1: Error: Invalid laser conditions are detected for a certain - * soak time and the system is put into a safe state. */ + * Set whenever a laser malfunction error is raised and the + * system is put into a safe state. + * - 0: No Error: Laser is operating properly. + * - 1: Error: Invalid laser conditions are detected for a certain + * soak time and the system is put into a safe state. */ ARGUS_STATE_LASER_ERROR = 1U << 9U, /*! 0x0400: Set if current frame has distance measurement data available. - * - 0: No measurement data available, all values are 0 or stalled. - * - 1: Measurement data is available and correctly evaluated. */ + * - 0: No measurement data available, all values are 0 or stalled. + * - 1: Measurement data is available and correctly evaluated. */ ARGUS_STATE_HAS_DATA = 1U << 10U, /*! 0x0800: Set if current frame has auxiliary measurement data available. - * - 0: No auxiliary data available, all values are 0 or stalled. - * - 1: Auxiliary data is available and correctly evaluated. */ + * - 0: No auxiliary data available, all values are 0 or stalled. + * - 1: Auxiliary data is available and correctly evaluated. */ ARGUS_STATE_HAS_AUX_DATA = 1U << 11U, /*! 0x1000: DCA Maximum State Flag. - * Set whenever the DCA has extended all its parameters to their - * maximum values and can not increase the integration energy any - * further. - * - 0: DCA has not yet reached its maximum state. - * - 1: DCA has reached its maximum state and can not increase any further. */ + * Set whenever the DCA has extended all its parameters to their + * maximum values and can not increase the integration energy any + * further. + * - 0: DCA has not yet reached its maximum state. + * - 1: DCA has reached its maximum state and can not increase any further. */ ARGUS_STATE_DCA_MAX = 1U << 12U, /*! 0x2000: DCA is in high Optical Output Power stage. */ @@ -333,20 +323,20 @@ typedef enum { } argus_state_t; /*!*************************************************************************** - * @brief Dynamic Configuration Adaption (DCA) Parameters. - * @details DCA contains: - * - Static or dynamic mode. The first is utilizing the nominal values - * while the latter is dynamically adopting between min. and max. - * value and starting form the nominal values. - * - Analog Integration Depth Adaption down to single pulses. - * - Optical Output Power Adaption - * - Pixel Input Gain Adaption - * - Digital Integration Depth Adaption - * - Dynamic Global Phase Shift Injection. - * - All that features are heeding the Laser Safety limits. - * . + * @brief Dynamic Configuration Adaption (DCA) Parameters. + * @details DCA contains: + * - Static or dynamic mode. The first is utilizing the nominal values + * while the latter is dynamically adopting between min. and max. + * value and starting form the nominal values. + * - Analog Integration Depth Adaption down to single pulses. + * - Optical Output Power Adaption + * - Pixel Input Gain Adaption + * - Digital Integration Depth Adaption + * - Dynamic Global Phase Shift Injection. + * - All that features are heeding the Laser Safety limits. + * . *****************************************************************************/ -typedef struct { +typedef struct argus_cfg_dca_t { /*! Enables the automatic configuration adaption features. * Enables the dynamic part if #DCA_ENABLE_DYNAMIC and the static only if * #DCA_ENABLE_STATIC. */ @@ -494,4 +484,7 @@ typedef struct { } argus_cfg_dca_t; /*! @} */ +#ifdef __cplusplus +} // extern "C" +#endif #endif /* ARGUS_DCA_H */ diff --git a/src/drivers/distance_sensor/broadcom/afbrs50/Inc/api/argus_def.h b/src/drivers/distance_sensor/broadcom/afbrs50/Inc/api/argus_def.h index c639922a7d..6d9dd3e9d3 100644 --- a/src/drivers/distance_sensor/broadcom/afbrs50/Inc/api/argus_def.h +++ b/src/drivers/distance_sensor/broadcom/afbrs50/Inc/api/argus_def.h @@ -1,12 +1,12 @@ /*************************************************************************//** * @file - * @brief This file is part of the AFBR-S50 hardware API. - * @details This file provides generic definitions belonging to all - * devices from the AFBR-S50 product family. + * @brief This file is part of the AFBR-S50 hardware API. + * @details This file provides generic definitions belonging to all + * devices from the AFBR-S50 product family. * * @copyright * - * Copyright (c) 2021, Broadcom Inc + * Copyright (c) 2023, Broadcom Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,6 +37,9 @@ #ifndef ARGUS_DEF_H #define ARGUS_DEF_H +#ifdef __cplusplus +extern "C" { +#endif /*!*************************************************************************** * Include files @@ -52,36 +55,41 @@ #include /*!*************************************************************************** - * @addtogroup argusapi + * @addtogroup argus_api * @{ *****************************************************************************/ /*!*************************************************************************** - * @brief Maximum number of phases per measurement cycle. - * @details The actual phase number is defined in the register configuration. - * However the software does only support a fixed value of 4 yet. + * @brief Maximum number of phases per measurement cycle. + * @details The actual phase number is defined in the register configuration. + * However the software does only support a fixed value of 4 yet. *****************************************************************************/ -#define ARGUS_PHASECOUNT 4U +#define ARGUS_PHASECOUNT 4 /*!*************************************************************************** - * @brief The device pixel field size in x direction (long edge). + * @brief The device pixel field size in x direction (long edge). *****************************************************************************/ -#define ARGUS_PIXELS_X 8U +#define ARGUS_PIXELS_X 8 /*!*************************************************************************** - * @brief The device pixel field size in y direction (short edge). + * @brief The device pixel field size in y direction (short edge). *****************************************************************************/ -#define ARGUS_PIXELS_Y 4U +#define ARGUS_PIXELS_Y 4 /*!*************************************************************************** - * @brief The total device pixel count. + * @brief The total device pixel count. *****************************************************************************/ -#define ARGUS_PIXELS ((ARGUS_PIXELS_X)*(ARGUS_PIXELS_Y)) +#define ARGUS_PIXELS ((ARGUS_PIXELS_X)*(ARGUS_PIXELS_Y)) /*!*************************************************************************** - * @brief The AFBR-S50 module types. + * @brief A flag indicating that the device is a extended range device. *****************************************************************************/ -typedef enum { +#define MODULE_EXTENDED_FLAG (0x40U) + +/*!*************************************************************************** + * @brief The AFBR-S50 module types. + *****************************************************************************/ +typedef enum argus_module_version_t { /*! No device connected or not recognized. */ MODULE_NONE = 0, @@ -89,54 +97,80 @@ typedef enum { * w/ 4x8 pixel matrix and infra-red, 850 nm, laser source for * medium range 3D applications. * Version 1 - legacy version! */ - AFBR_S50MV85G_V1 = 1, + AFBR_S50MV85G_V1 = 0x01, /*! AFBR-S50MV85G: an ADS0032 based multi-pixel range finder device * w/ 4x8 pixel matrix and infra-red, 850 nm, laser source for * medium range 3D applications. * Version 2 - legacy version! */ - AFBR_S50MV85G_V2 = 2, - - /*! AFBR-S50MV85G: an ADS0032 based multi-pixel range finder device - * w/ 4x8 pixel matrix and infra-red, 850 nm, laser source for - * medium range 3D applications. - * Version 7 - current version! */ - AFBR_S50MV85G_V3 = 7, + AFBR_S50MV85G_V2 = 0x02, /*! AFBR-S50LV85D: an ADS0032 based multi-pixel range finder device * w/ 4x8 pixel matrix and infra-red, 850 nm, laser source for * long range 1D applications. * Version 1 - current version! */ - AFBR_S50LV85D_V1 = 3, + AFBR_S50LV85D_V1 = 0x03, /*! AFBR-S50MV68B: an ADS0032 based multi-pixel range finder device * w/ 4x8 pixel matrix and red, 680 nm, laser source for * medium range 1D applications. * Version 1 - current version! */ - AFBR_S50MV68B_V1 = 4, + AFBR_S50MV68B_V1 = 0x04, /*! AFBR-S50MV85I: an ADS0032 based multi-pixel range finder device * w/ 4x8 pixel matrix and infra-red, 850 nm, laser source for * medium range 3D applications. * Version 1 - current version! */ - AFBR_S50MV85I_V1 = 5, + AFBR_S50MV85I_V1 = 0x05, /*! AFBR-S50MV85G: an ADS0032 based multi-pixel range finder device * w/ 4x8 pixel matrix and infra-red, 850 nm, laser source for * short range 3D applications. * Version 1 - current version! */ - AFBR_S50SV85K_V1 = 6, + AFBR_S50SV85K_V1 = 0x06, + /*! AFBR-S50MV85G: an ADS0032 based multi-pixel range finder device + * w/ 4x8 pixel matrix and infra-red, 850 nm, laser source for + * medium range 3D applications. + * Version 3 - current version! */ + AFBR_S50MV85G_V3 = 0x07, - /*! Reserved for future extensions. */ - Reserved = 0x3F + /*! AFBR-S50LX85D: an ADS0032 based multi-pixel range finder device + * w/ 4x8 pixel matrix and infra-red, 850 nm, laser source for + * extended long range 1D applications. + * Version 1 - current version! */ + AFBR_S50LX85D_V1 = AFBR_S50LV85D_V1 | MODULE_EXTENDED_FLAG, + + /*! AFBR-S50MX68B: an ADS0032 based multi-pixel range finder device + * w/ 4x8 pixel matrix and red, 680 nm, laser source for + * extended medium range 1D applications. + * Version 1 - current version! */ + AFBR_S50MX68B_V1 = AFBR_S50MV68B_V1 | MODULE_EXTENDED_FLAG, + + /*! AFBR-S50MX85I: an ADS0032 based multi-pixel range finder device + * w/ 4x8 pixel matrix and infra-red, 850 nm, laser source for + * extended medium range 3D applications. + * Version 1 - current version! */ + AFBR_S50MX85I_V1 = AFBR_S50MV85I_V1 | MODULE_EXTENDED_FLAG, + + /*! AFBR-S50MX85G: an ADS0032 based multi-pixel range finder device + * w/ 4x8 pixel matrix and infra-red, 850 nm, laser source for + * extended short range 3D applications. + * Version 1 - current version! */ + AFBR_S50SX85K_V1 = AFBR_S50SV85K_V1 | MODULE_EXTENDED_FLAG, + + /*! AFBR-S50MX85G: an ADS0032 based multi-pixel range finder device + * w/ 4x8 pixel matrix and infra-red, 850 nm, laser source for + * extended medium range 3D applications. + * Version 1 - current version! */ + AFBR_S50MX85G_V1 = AFBR_S50MV85G_V3 | MODULE_EXTENDED_FLAG, } argus_module_version_t; /*!*************************************************************************** - * @brief The AFBR-S50 laser configurations. + * @brief The AFBR-S50 laser configurations. *****************************************************************************/ -typedef enum { +typedef enum argus_laser_type_t { /*! No laser connected. */ LASER_NONE = 0, @@ -152,12 +186,15 @@ typedef enum { /*! 850nm Infra-Red VCSEL v2 /w extended mode. */ LASER_H_V2X = 4, + /*! 680nm Red VCSEL v1 w/ extended mode. */ + LASER_R_V1X = 5, + } argus_laser_type_t; /*!*************************************************************************** - * @brief The AFBR-S50 chip versions. + * @brief The AFBR-S50 chip versions. *****************************************************************************/ -typedef enum { +typedef enum argus_chip_version_t { /*! No device connected or not recognized. */ ADS0032_NONE = 0, @@ -178,37 +215,102 @@ typedef enum { } argus_chip_version_t; -/*!*************************************************************************** - * @brief The number of measurement modes with distinct configuration and - * calibration records. - *****************************************************************************/ -#define ARGUS_MODE_COUNT (2) /*!*************************************************************************** - * @brief The measurement modes. + * @brief The measurement mode flags. + * @details The measurement mode flags that can be combined to a measurement + * mode, e.g. high speed short range mode. See #argus_mode_t for + * a complete list of available measurement modes. + * + * - Bit 0: Short Range Mode + * - Bit 1: Long Range Mode + * - Bit 2: High Speed Mode + * + * Note that the Long and Short Range Flags are mutual exclusive but + * any of those 2 must be set. Thus the value 0 is invalid! + * All other flags enhance the base configurations, e.g. the High + * Speed flag create the high speed mode of the selected base + * measurement mode. *****************************************************************************/ -typedef enum { - /*! Measurement Mode A: Long Range Mode. */ - ARGUS_MODE_A = 1, +typedef enum argus_mode_flags_t { + /*! Measurement Mode Flag for Short Range Base Mode. */ + ARGUS_MODE_FLAG_SHORT_RANGE = 0x01 << 0, - /*! Measurement Mode B: Short Range Mode. */ - ARGUS_MODE_B = 2, + /*! Measurement Mode Flag for Long Range Base Mode. */ + ARGUS_MODE_FLAG_LONG_RANGE = 0x01 << 1, + + /*! Measurement Mode Flag for High Speed Mode. */ + ARGUS_MODE_FLAG_HIGH_SPEED = 0x01 << 2 + +} argus_mode_flags_t; + +/*!*************************************************************************** + * @brief The measurement modes. + * @details The measurement modes are composed in binary from of the flags + * define in #argus_mode_flags_t, i.e. each bit has a special meaning: + * + * - Bit 0: Short Range Mode + * - Bit 1: Long Range Mode + * - Bit 2: High Speed Mode + * + * Note that the Long and Short Range Bits are mutual exclusive but any + * of those 2 must be set. Thus the value 0 is invalid! + *****************************************************************************/ +typedef enum argus_mode_t { + /*! Measurement Mode: Short Range Mode. */ + ARGUS_MODE_SHORT_RANGE = // = 0x01 = 0b0001 + ARGUS_MODE_FLAG_SHORT_RANGE, + + /*! Measurement Mode: Long Range Mode. */ + ARGUS_MODE_LONG_RANGE = // = 0x02 = 0b0010 + ARGUS_MODE_FLAG_LONG_RANGE, + + /*! Measurement Mode: High Speed Short Range Mode. */ + ARGUS_MODE_HIGH_SPEED_SHORT_RANGE = // = 0x05 = 0b0101 + ARGUS_MODE_FLAG_SHORT_RANGE | ARGUS_MODE_FLAG_HIGH_SPEED, + + /*! Measurement Mode: High Speed Long Range Mode. */ + ARGUS_MODE_HIGH_SPEED_LONG_RANGE = // = 0x06 = 0b0110 + ARGUS_MODE_FLAG_LONG_RANGE | ARGUS_MODE_FLAG_HIGH_SPEED, } argus_mode_t; +/*! The data structure for the API representing a AFBR-S50 device instance. */ +typedef struct argus_hnd_t argus_hnd_t; + /*!*************************************************************************** - * @brief Generic API callback function. - * @details Invoked by the API. The content of the abstract data pointer - * depends upon the context. - * @param status The module status that caused the callback. #STATUS_OK if - * everything was as expected. - * @param data An abstract pointer to an user defined data. This will usually - * be passed to the function that also takes the callback as an - * parameter. Otherwise it has a special meaning such as - * configuration or calibration data. - * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). + * @brief Measurement Ready API callback function. + * + * @details Invoked by the API whenever a measurement cycle is finished and + * new data is ready to be evaluated via the #Argus_EvaluateData API + * function. + * The callback is passed to the API via the #Argus_TriggerMeasurement + * or #Argus_StartMeasurementTimer API functions. + * The API passes the status of the currently finished measurement + * cycle to the callback as first parameters. The second parameter is + * a pointer the API handle structure. The latter is used to identify + * the calling instance of the API in case of multiple devices. + * Further it can be passed to the #Argus_EvaluateData function. + * + * @warning Since the callback is called from an interrupt context, the + * callback execution must return as fast as possible. The usual task + * in the callback is to post an event to the main thread to inform it + * about the new data and that is must call the #Argus_EvaluateData + * function. + * + * @param status The module status that caused the callback. #STATUS_OK if + * everything was as expected. + * + * @param hnd The API handle pointer to the calling instance. Identifies the + * instance of the API that was invoking the callback and thus the + * instance that must call the #Argus_EvaluateData for. + * + * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). *****************************************************************************/ -typedef status_t (*argus_callback_t)(status_t status, void *data); +typedef status_t (*argus_measurement_ready_callback_t)(status_t status, argus_hnd_t *hnd); /*! @} */ +#ifdef __cplusplus +} // extern "C" +#endif #endif /* ARGUS_DEF_H */ diff --git a/src/drivers/distance_sensor/broadcom/afbrs50/Inc/api/argus_dfm.h b/src/drivers/distance_sensor/broadcom/afbrs50/Inc/api/argus_dfm.h index b2517182f8..4c0182a0b3 100644 --- a/src/drivers/distance_sensor/broadcom/afbrs50/Inc/api/argus_dfm.h +++ b/src/drivers/distance_sensor/broadcom/afbrs50/Inc/api/argus_dfm.h @@ -1,11 +1,11 @@ /*************************************************************************//** * @file - * @brief This file is part of the AFBR-S50 API. - * @details Defines the dual frequency mode (DFM) setup parameters. + * @brief This file is part of the AFBR-S50 API. + * @details Defines the dual frequency mode (DFM) setup parameters. * * @copyright * - * Copyright (c) 2021, Broadcom Inc + * Copyright (c) 2023, Broadcom Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -36,24 +36,27 @@ #ifndef ARGUS_DFM_H #define ARGUS_DFM_H +#ifdef __cplusplus +extern "C" { +#endif /*!*************************************************************************** - * @defgroup argusdfm Dual Frequency Mode - * @ingroup argusapi + * @defgroup argus_dfm Dual Frequency Mode + * @ingroup argus_api * - * @brief Dual Frequency Mode (DFM) parameter definitions and API functions. + * @brief Dual Frequency Mode (DFM) parameter definitions and API functions. * - * @details The DFM is an algorithm to extend the unambiguous range of the - * sensor by utilizing two detuned measurement frequencies. + * @details The DFM is an algorithm to extend the unambiguous range of the + * sensor by utilizing two detuned measurement frequencies. * - * The AFBR-S50 API provides three measurement modes: - * - 1X: Single Frequency Measurement - * - 4X: Dual Frequency Measurement w/ 4 times the unambiguous - * range of the Single Frequency Measurement - * - 8X: Dual Frequency Measurement w/ 8 times the unambiguous - * range of the Single Frequency Measurement + * The AFBR-S50 API provides three measurement modes: + * - 1X: Single Frequency Measurement + * - 4X: Dual Frequency Measurement w/ 4 times the unambiguous + * range of the Single Frequency Measurement + * - 8X: Dual Frequency Measurement w/ 8 times the unambiguous + * range of the Single Frequency Measurement * - * @addtogroup argusdfm + * @addtogroup argus_dfm * @{ *****************************************************************************/ @@ -61,10 +64,10 @@ #define ARGUS_DFM_FRAME_COUNT (2U) /*! The Dual Frequency Mode measurement modes count. Excluding the disabled mode. */ -#define ARGUS_DFM_MODE_COUNT (2U) // expect off-mode! +#define ARGUS_DFM_MODE_COUNT (2U) // except off-mode! /*! The Dual Frequency Mode measurement modes enumeration. */ -typedef enum { +typedef enum argus_dfm_mode_t { /*! Single Frequency Measurement Mode (w/ 1x Unambiguous Range). */ DFM_MODE_OFF = 0U, @@ -78,4 +81,7 @@ typedef enum { /*! @} */ +#ifdef __cplusplus +} // extern "C" +#endif #endif /* ARGUS_DFM_H */ diff --git a/src/drivers/distance_sensor/broadcom/afbrs50/Inc/api/argus_map.h b/src/drivers/distance_sensor/broadcom/afbrs50/Inc/api/argus_map.h index 64588d25f1..4ffa55656b 100644 --- a/src/drivers/distance_sensor/broadcom/afbrs50/Inc/api/argus_map.h +++ b/src/drivers/distance_sensor/broadcom/afbrs50/Inc/api/argus_map.h @@ -1,11 +1,11 @@ /*************************************************************************//** * @file - * @brief This file is part of the AFBR-S50 API. - * @details Defines macros to work with pixel and ADC channel masks. + * @brief This file is part of the AFBR-S50 API. + * @details Defines macros to work with pixel and ADC channel masks. * * @copyright * - * Copyright (c) 2021, Broadcom Inc + * Copyright (c) 2023, Broadcom Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,217 +37,409 @@ #ifndef ARGUS_MAP_H #define ARGUS_MAP_H +#ifdef __cplusplus +extern "C" { +#endif /*!*************************************************************************** - * @defgroup argusmap ADC Channel Mapping - * @ingroup argusres + * @defgroup argus_map Pixel Channel Mapping + * @ingroup argus_api * - * @brief Pixel ADC Channel Mapping + * @brief Pixel Channel Mapping * - * @details The ADC Channels of each pixel or auxiliary channel on the device - * are numbered in a way that is convenient on the chip architecture. - * The macros in this module are defined in order to map between the - * chip internal channel number (ch) to the two-dimensional - * x-y-indices or one-dimensional n-index representation. + * @details The ADC Channels of each pixel or auxiliary channel on the device + * are numbered in a way that is convenient on the chip architecture. + * The macros in this module are defined in order to map between the + * chip internal channel number (ch) to the two-dimensional + * x-y-indices or one-dimensional n-index representation. * - * @addtogroup argusmap + * @addtogroup argus_map * @{ *****************************************************************************/ -#include "api/argus_def.h" #include "utility/int_math.h" +#include +/*!*************************************************************************** + * @brief The device pixel field size in x direction (long edge). + *****************************************************************************/ +#define ARGUS_PIXELS_X 8 + +/*!*************************************************************************** + * @brief The device pixel field size in y direction (short edge). + *****************************************************************************/ +#define ARGUS_PIXELS_Y 4 + +/*!*************************************************************************** + * @brief The total device pixel count. + *****************************************************************************/ +#define ARGUS_PIXELS ((ARGUS_PIXELS_X)*(ARGUS_PIXELS_Y)) /*!***************************************************************************** - * @brief Macro to determine the pixel ADC channel number from the x-z-indices. - * @param x The x-index of the pixel. - * @param y The y-index of the pixel. - * @return The ADC channel number of the pixel. + * @brief Macro to determine the pixel ADC channel number from the x-z-indices. + * @param x The x-index of the pixel. + * @param y The y-index of the pixel. + * @return The ADC channel number of the pixel. ******************************************************************************/ #define PIXEL_XY2CH(x, y) ((((y) << 3U) & 0x10U) | (((x) ^ 0x07U) << 1U) | ((y) & 0x01U)) /*!***************************************************************************** - * @brief Macro to determine the pixel x-index from the ADC channel number. - * @param c The ADC channel number of the pixel. - * @return The x-index of the pixel. + * @brief Macro to determine the pixel x-index from the ADC channel number. + * @param c The ADC channel number of the pixel. + * @return The x-index of the pixel. ******************************************************************************/ #define PIXEL_CH2X(c) ((((c) >> 1U) ^ 0x07U) & 0x07U) /*!***************************************************************************** - * @brief Macro to determine the pixel y-index from the ADC channel number. - * @param c The ADC channel number of the pixel. - * @return The y-index of the pixel. + * @brief Macro to determine the pixel y-index from the ADC channel number. + * @param c The ADC channel number of the pixel. + * @return The y-index of the pixel. ******************************************************************************/ #define PIXEL_CH2Y(c) ((((c) >> 3U) & 0x02U) | ((c) & 0x01U)) /*!***************************************************************************** - * @brief Macro to determine the n-index from the x-y-indices. - * @param x The x-index of the pixel. - * @param y The y-index of the pixel. - * @return The n-index of the pixel. + * @brief Macro to determine the n-index from the x-y-indices. + * @param x The x-index of the pixel. + * @param y The y-index of the pixel. + * @return The n-index of the pixel. ******************************************************************************/ #define PIXEL_XY2N(x, y) (((x) << 2U) | (y)) /*!***************************************************************************** - * @brief Macro to determine the pixel x-index from the n-index. - * @param n The n-index of the pixel. - * @return The x-index number of the pixel. + * @brief Macro to determine the pixel x-index from the n-index. + * @param n The n-index of the pixel. + * @return The x-index number of the pixel. ******************************************************************************/ #define PIXEL_N2X(n) ((n) >> 2U) /*!***************************************************************************** - * @brief Macro to determine the pixel y-index from the n-index. - * @param n The n-index of the pixel. - * @return The y-index number of the pixel. + * @brief Macro to determine the pixel y-index from the n-index. + * @param n The n-index of the pixel. + * @return The y-index number of the pixel. ******************************************************************************/ #define PIXEL_N2Y(n) ((n) & 0x03U) /*!***************************************************************************** - * @brief Macro to determine the pixel n-index from the ADC channel number. - * @param n The n-index of the pixel. - * @return The ADC channel number of the pixel. + * @brief Macro to determine the pixel n-index from the ADC channel number. + * @param n The n-index of the pixel. + * @return The ADC channel number of the pixel. ******************************************************************************/ #define PIXEL_N2CH(n) ((((n) << 3U) & 0x10U) | ((((n) >> 1U) ^ 0x0EU) & 0x0EU) | ((n) & 0x01U)) /*!***************************************************************************** - * @brief Macro to determine the pixel - * @param c The ADC channel number of the pixel. - * @return The n-index of the pixel. + * @brief Macro to determine the pixel + * @param c The ADC channel number of the pixel. + * @return The n-index of the pixel. ******************************************************************************/ #define PIXEL_CH2N(c) (((((c) << 1U) ^ 0x1CU) & 0x1CU) | (((c) >> 3U) & 0x02U) | ((c) & 0x01U)) /*!***************************************************************************** - * @brief Macro to determine if a pixel given by the n-index is enabled in a pixel mask. - * @param msk 32-bit pixel mask - * @param n n-index of the pixel. - * @return True if the pixel (n) is enabled. + * @brief Macro to determine if a pixel given by the n-index is enabled in a pixel mask. + * @param msk 32-bit pixel mask + * @param n n-index of the pixel. + * @return True if the pixel (n) is enabled. ******************************************************************************/ #define PIXELN_ISENABLED(msk, n) (((msk) >> (n)) & 0x01U) /*!***************************************************************************** - * @brief Macro to enable a pixel given by the n-index in a pixel mask. - * @param msk 32-bit pixel mask - * @param n n-index of the pixel to enable. + * @brief Macro to enable a pixel given by the n-index in a pixel mask. + * @param msk 32-bit pixel mask + * @param n n-index of the pixel to enable. ******************************************************************************/ #define PIXELN_ENABLE(msk, n) ((msk) |= (0x01U << (n))) /*!***************************************************************************** - * @brief Macro disable a pixel given by the n-index in a pixel mask. - * @param msk 32-bit pixel mask - * @param n n-index of the pixel to disable. + * @brief Macro disable a pixel given by the n-index in a pixel mask. + * @param msk 32-bit pixel mask + * @param n n-index of the pixel to disable. ******************************************************************************/ #define PIXELN_DISABLE(msk, n) ((msk) &= (~(0x01U << (n)))) /*!***************************************************************************** - * @brief Macro to determine if an ADC pixel channel is enabled from a pixel mask. - * @param msk The 32-bit pixel mask - * @param c The ADC channel number of the pixel. - * @return True if the specified pixel ADC channel is enabled. + * @brief Macro to determine if an ADC pixel channel is enabled from a pixel mask. + * @param msk The 32-bit pixel mask + * @param c The ADC channel number of the pixel. + * @return True if the specified pixel ADC channel is enabled. ******************************************************************************/ #define PIXELCH_ISENABLED(msk, c) (PIXELN_ISENABLED(msk, PIXEL_CH2N(c))) /*!***************************************************************************** - * @brief Macro to enable an ADC pixel channel in a pixel mask. - * @param msk The 32-bit pixel mask - * @param c The pixel ADC channel number to enable. + * @brief Macro to enable an ADC pixel channel in a pixel mask. + * @param msk The 32-bit pixel mask + * @param c The pixel ADC channel number to enable. ******************************************************************************/ #define PIXELCH_ENABLE(msk, c) (PIXELN_ENABLE(msk, PIXEL_CH2N(c))) /*!***************************************************************************** - * @brief Macro to disable an ADC pixel channel in a pixel mask. - * @param msk The 32-bit pixel mask - * @param c The pixel ADC channel number to disable. + * @brief Macro to disable an ADC pixel channel in a pixel mask. + * @param msk The 32-bit pixel mask + * @param c The pixel ADC channel number to disable. ******************************************************************************/ #define PIXELCH_DISABLE(msk, c) (PIXELN_DISABLE(msk, PIXEL_CH2N(c))) /*!***************************************************************************** - * @brief Macro to determine if a pixel given by the x-y-indices is enabled in a pixel mask. - * @param msk 32-bit pixel mask - * @param x x-index of the pixel. - * @param y y-index of the pixel. - * @return True if the pixel (x,y) is enabled. + * @brief Macro to determine if a pixel given by the x-y-indices is enabled in a pixel mask. + * @param msk 32-bit pixel mask + * @param x x-index of the pixel. + * @param y y-index of the pixel. + * @return True if the pixel (x,y) is enabled. ******************************************************************************/ #define PIXELXY_ISENABLED(msk, x, y) (PIXELN_ISENABLED(msk, PIXEL_XY2N(x, y))) /*!***************************************************************************** - * @brief Macro to enable a pixel given by the x-y-indices in a pixel mask. - * @param msk 32-bit pixel mask - * @param x x-index of the pixel to enable. - * @param y y-index of the pixel to enable. + * @brief Macro to enable a pixel given by the x-y-indices in a pixel mask. + * @param msk 32-bit pixel mask + * @param x x-index of the pixel to enable. + * @param y y-index of the pixel to enable. ******************************************************************************/ #define PIXELXY_ENABLE(msk, x, y) (PIXELN_ENABLE(msk, PIXEL_XY2N(x, y))) /*!***************************************************************************** - * @brief Macro disable a pixel given by the x-y-indices in a pixel mask. - * @param msk 32-bit pixel mask - * @param x x-index of the pixel to disable. - * @param y y-index of the pixel to disable. + * @brief Macro disable a pixel given by the x-y-indices in a pixel mask. + * @param msk 32-bit pixel mask + * @param x x-index of the pixel to disable. + * @param y y-index of the pixel to disable. ******************************************************************************/ #define PIXELXY_DISABLE(msk, x, y) (PIXELN_DISABLE(msk, PIXEL_XY2N(x, y))) /*!***************************************************************************** - * @brief Macro to determine if an ADC channel is enabled in a channel mask. - * @param msk 32-bit channel mask - * @param ch channel number of the ADC channel. - * @return True if the ADC channel is enabled. + * @brief Macro to determine if an ADC channel is enabled in a channel mask. + * @param msk 32-bit channel mask + * @param ch channel number of the ADC channel. + * @return True if the ADC channel is enabled. ******************************************************************************/ #define CHANNELN_ISENABLED(msk, ch) (((msk) >> ((ch) - 32U)) & 0x01U) /*!***************************************************************************** - * @brief Macro to determine if an ADC channel is enabled in a channel mask. - * @param msk 32-bit channel mask - * @param ch channel number of the ADC channel to enabled. + * @brief Macro to determine if an ADC channel is enabled in a channel mask. + * @param msk 32-bit channel mask + * @param ch channel number of the ADC channel to enabled. ******************************************************************************/ #define CHANNELN_ENABLE(msk, ch) ((msk) |= (0x01U << ((ch) - 32U))) /*!***************************************************************************** - * @brief Macro to determine if an ADC channel is disabled in a channel mask. - * @param msk 32-bit channel mask - * @param ch channel number of the ADC channel to disable. + * @brief Macro to determine if an ADC channel is disabled in a channel mask. + * @param msk 32-bit channel mask + * @param ch channel number of the ADC channel to disable. ******************************************************************************/ #define CHANNELN_DISABLE(msk, ch) ((msk) &= (~(0x01U << ((ch) - 32U)))) /*!***************************************************************************** - * @brief Macro to determine the number of enabled pixel/channels in a mask - * via a popcount algorithm. - * @param pxmsk 32-bit pixel mask - * @return The count of enabled pixel channels. + * @brief Macro to determine the number of enabled pixel/channels in a mask + * via a popcount algorithm. + * @param pxmsk 32-bit pixel mask + * @return The count of enabled pixel channels. ******************************************************************************/ #define PIXEL_COUNT(pxmsk) popcount(pxmsk) /*!***************************************************************************** - * @brief Macro to determine the number of enabled channels via a popcount - * algorithm. - * @param pxmsk 32-bit pixel mask - * @param chmsk 32-bit channel mask - * @return The count of enabled ADC channels. + * @brief Macro to determine the number of enabled channels via a popcount + * algorithm. + * @param pxmsk 32-bit pixel mask + * @param chmsk 32-bit channel mask + * @return The count of enabled ADC channels. ******************************************************************************/ #define CHANNEL_COUNT(pxmsk, chmsk) (popcount(pxmsk) + popcount(chmsk)) /*!***************************************************************************** - * @brief Converts a raw ADC channel mask to a x-y-sorted pixel mask. - * @param msk The raw ADC channel mask to be converted. - * @return The converted x-y-sorted pixel mask. + * @brief Converts a raw ADC channel mask to a x-y-sorted pixel mask. + * @param msk The raw ADC channel mask to be converted. + * @return The converted x-y-sorted pixel mask. ******************************************************************************/ static inline uint32_t ChannelToPixelMask(uint32_t msk) { uint32_t res = 0; for (uint_fast8_t n = 0; n < 32; n += 2) { - res |= ((msk >> PIXEL_N2CH(n)) & 0x3U) << n; + res |= ((msk >> PIXEL_N2CH(n)) & 0x3U) << n; // sets 2 bits at once } return res; } +/*!***************************************************************************** + * @brief Converts a x-y-sorted pixel mask to a raw ADC channel mask. + * @param msk The x-y-sorted pixel channel mask to be converted. + * @return The converted raw ADC channel mask. + ******************************************************************************/ +static inline uint32_t PixelToChannelMask(uint32_t msk) +{ + uint32_t res = 0; + + for (uint_fast8_t ch = 0; ch < 32; ch += 2) { + res |= ((msk >> PIXEL_CH2N(ch)) & 0x3U) << ch; // sets 2 bits at once + } + + return res; +} + + +/*!***************************************************************************** + * @brief Shifts a pixel mask by a given offset. + * + * @details This moves the selected pixel pattern by a specified number of + * pixels in x and y direction. + * If the shift in y direction is odd (e.g +1), the pattern will be + * shifted by +0.5 or -0.5 in x direction due to the hexagonal shape + * of the pixel field. Thus, a center pixel (usually the Golden Pixel) + * is determined that is used to determine if the pattern is shifted + * by +0.5 or -0.5 pixels in x direction. The center pixel is then + * always shifted without changing the x index and the surrounding + * pixels are adopting its x index accordingly. + * + * Example: Consider the flower pattern, i.e. the Golden Pixel (e.g. + * 5/2) is selected and all is direct neighbors (i.e. 5/1, 6/1, 6/2, + * 6/3, 5/3, 4/2). If the pattern is shifted by -1 in y direction, the + * new Golden Pixel would be 5/1. Now all surrounding pixels are + * selected, namely 4/0, 4/1, 4/2, 5/0, 5/2, 6/1). This yields again + * the flower around the Golden Pixel. + * + * Thus, the pixels can not all be shifted by the same dx/dy values due + * to the hexagonal shape of the pixel field, e.g. the upper right + * neighbor of 5/2 is 5/1 but the upper right neighbor of 5/1 is NOT + * 5/0 but 4/0! + * This happens only if the shift in y direction is an odd number. + * The algorithm to determine new indices is as follows: + * - If the shift in y direction is even (e.g. +2, -2), no compensation + * of the hexagonal shape is needed; skip compensation, simply + * add/subtract indices. + * - If the center pixel y index is even, pixels that will have even y + * index after the shift will be additionally shifted by -1 in x + * direction. + * - If the center pixel y index is odd, pixel that will have odd y + * index after the shift will be additionally shifted by +1 in x + * direction. + * + * @see Please also refer to the function #Argus_GetCalibrationGoldenPixel + * to obtain the current Golden Pixel location. + * + * @param pixel_mask The x-y-sorted pixel mask to be shifted. + * @param dx The number of pixel to shift in x direction. + * @param dy The number of pixel to shift in y direction. + * @param center_y The center y index of the pattern that is shifted. + * @return The shifted pixel mask. + ******************************************************************************/ +static inline uint32_t ShiftSelectedPixels(const uint32_t pixel_mask, + const int8_t dx, + const int8_t dy, + const uint8_t center_y) +{ + if (dx == 0 && dy == 0) { return pixel_mask; } + + uint32_t shifted_mask = 0; + + for (uint8_t x = 0; x < ARGUS_PIXELS_X; ++x) { + for (uint8_t y = 0; y < ARGUS_PIXELS_Y; ++y) { + int8_t x_src = x - dx; + int8_t y_src = y - dy; + + if (dy & 0x1) { + /* Compensate for hexagonal pixel shape. */ + if ((center_y & 0x1) && (y & 0x1)) { + x_src--; + } + + if (!(center_y & 0x1) && !(y & 0x1)) { + x_src++; + } + } + + if (x_src < 0 || x_src >= ARGUS_PIXELS_X) { continue; } + + if (y_src < 0 || y_src >= ARGUS_PIXELS_Y) { continue; } + + if (PIXELXY_ISENABLED(pixel_mask, x_src, y_src)) { + PIXELXY_ENABLE(shifted_mask, x, y); + } + } + } + + return shifted_mask; +} + +/*!***************************************************************************** + * @brief Fills a pixel mask to a specified number of pixels around a center pixel. + * + * @details The pixel mask is iteratively filled with the nearest pixel to a + * specified center pixel until a specified number of pixels is achieved. + * The distance between two pixel is determined via a quadratic metric, + * i.e. dx^2 + dy^2. Pixels towards the lower x indices are preferred. + * + * Note that the distance of only calculated approximately, e.g. the + * y distance of pixels is considered to be 2 instead of cos(60)*2. + * + * Nothing is done if the number of pixels already exceeds the specified + * /p pixel_count parameter. + * + * @see Please also refer to the function #Argus_GetCalibrationGoldenPixel + * to obtain the current Golden Pixel location. + * + * @param pixel_mask The x-y-sorted pixel mask to be filled with pixels. + * @param pixel_count The final number of pixels in the pixel mask. + * @param center_x The center pixel x-index. + * @param center_y The center pixel y-index. + * @return The filled pixel mask with at least /p pixel_count pixels selected. + ******************************************************************************/ +static inline uint32_t FillPixelMask(uint32_t pixel_mask, + const uint8_t pixel_count, + const uint8_t center_x, + const uint8_t center_y) +{ + assert(pixel_count <= ARGUS_PIXELS); + assert(center_x < ARGUS_PIXELS_X); + assert(center_y < ARGUS_PIXELS_Y); + + if (pixel_count == ARGUS_PIXELS) { return 0xFFFFFFFFU; } + + /* If the pattern was shifted towards boundaries, the pixel count may have + * decreased. In this case, the pixels closest to the reference pixel are + * selected. Pixel towards lower x index are prioritized. */ + while (pixel_count > PIXEL_COUNT(pixel_mask)) { + int32_t min_dist = INT32_MAX; + int8_t min_x = -1; + int8_t min_y = -1; + + /* Find nearest not selected pixel. */ + for (uint8_t x = 0; x < ARGUS_PIXELS_X; ++x) { + for (uint8_t y = 0; y < ARGUS_PIXELS_Y; ++y) { + if (!PIXELXY_ISENABLED(pixel_mask, x, y)) { + int32_t distx = (x - center_x) << 1; + + if (!(y & 0x1)) { distx++; } + + if (!(center_y & 0x1)) { distx--; } + + const int32_t disty = (y - center_y) << 1; + int32_t dist = distx * distx + disty * disty; + + if (dist < min_dist) { + min_dist = dist; + min_x = x; + min_y = y; + } + } + } + } + + assert(min_x >= 0 && min_x < ARGUS_PIXELS_X); + assert(min_y >= 0 && min_y < ARGUS_PIXELS_Y); + assert(!PIXELXY_ISENABLED(pixel_mask, min_x, min_y)); + PIXELXY_ENABLE(pixel_mask, min_x, min_y); + } + + return pixel_mask; +} /*! @} */ +#ifdef __cplusplus +} // extern "C" +#endif #endif /* ARGUS_MAP_H */ diff --git a/src/drivers/distance_sensor/broadcom/afbrs50/Inc/api/argus_meas.h b/src/drivers/distance_sensor/broadcom/afbrs50/Inc/api/argus_meas.h index 0e074c6a8b..7a0fa31aa5 100644 --- a/src/drivers/distance_sensor/broadcom/afbrs50/Inc/api/argus_meas.h +++ b/src/drivers/distance_sensor/broadcom/afbrs50/Inc/api/argus_meas.h @@ -1,11 +1,11 @@ /*************************************************************************//** * @file - * @brief This file is part of the AFBR-S50 hardware API. - * @details Defines the generic measurement parameters and data structures. + * @brief This file is part of the AFBR-S50 hardware API. + * @details Defines the generic measurement parameters and data structures. * * @copyright * - * Copyright (c) 2021, Broadcom Inc + * Copyright (c) 2023, Broadcom Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -36,17 +36,20 @@ #ifndef ARGUS_MEAS_H #define ARGUS_MEAS_H +#ifdef __cplusplus +extern "C" { +#endif /*!*************************************************************************** - * @defgroup argusmeas Measurement/Device Control - * @ingroup argusapi + * @defgroup argus_meas Measurement/Device Control + * @ingroup argus_api * - * @brief Measurement/Device control module + * @brief Measurement/Device control module * - * @details This module contains measurement and device control specific - * definitions and methods. + * @details This module contains measurement and device control specific + * definitions and methods. * - * @addtogroup argusmeas + * @addtogroup argus_meas * @{ *****************************************************************************/ @@ -66,11 +69,11 @@ #define ARGUS_AUX_DATA_SIZE (3U * ARGUS_AUX_CHANNEL_COUNT) // 3 bytes * x channels * 1 phase /*!*************************************************************************** - * @brief The device measurement configuration structure. - * @details The portion of the configuration data that belongs to the - * measurement cycle. I.e. the data that defines a measurement frame. + * @brief The device measurement configuration structure. + * @details The portion of the configuration data that belongs to the + * measurement cycle. I.e. the data that defines a measurement frame. *****************************************************************************/ -typedef struct { +typedef struct argus_meas_frame_t { /*! Frame integration time in microseconds. * The integration time determines the measured time between * the start signal and the IRQ. Note that this value will be @@ -82,13 +85,13 @@ typedef struct { /*! Pixel enabled mask for the 32 pixels sorted * by x-y-indices. - * See [pixel mapping](@ref argusmap) for more + * See [pixel mapping](@ref argus_map) for more * details on the pixel mask. */ uint32_t PxEnMask; /*! ADS channel enabled mask for the remaining * channels 31 .. 63 (miscellaneous values). - * See [pixel mapping](@ref argusmap) for more + * See [pixel mapping](@ref argus_map) for more * details on the ADC channel mask. */ uint32_t ChEnMask; @@ -113,9 +116,6 @@ typedef struct { * Determines the optical output power. */ uq12_4_t OutputPower; - /*! The amplitude that is evaluated and used in the DCA module. */ - uq12_4_t DCAAmplitude; - /*! Laser Bias Current Settings in LSB. */ uint8_t BiasCurrent; @@ -133,4 +133,7 @@ typedef struct { } argus_meas_frame_t; /*! @} */ +#ifdef __cplusplus +} // extern "C" +#endif #endif /* ARGUS_MEAS_H */ diff --git a/src/drivers/distance_sensor/broadcom/afbrs50/Inc/api/argus_offset.h b/src/drivers/distance_sensor/broadcom/afbrs50/Inc/api/argus_offset.h new file mode 100644 index 0000000000..7a41440f39 --- /dev/null +++ b/src/drivers/distance_sensor/broadcom/afbrs50/Inc/api/argus_offset.h @@ -0,0 +1,59 @@ +/*************************************************************************//** + * @file + * @brief This file is part of the AFBR-S50 hardware API. + * @details Defines the generic device calibration API. + * + * @copyright + * + * Copyright (c) 2023, Broadcom Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef ARGUS_OFFSET_H +#define ARGUS_OFFSET_H + +/*!*************************************************************************** + * @addtogroup argus_cal + * @{ + *****************************************************************************/ + +#include "argus_def.h" + +/*!*************************************************************************** + * @brief Pixel Range Offset Table. + * @details Contains pixel range offset values for all 32 active pixels. + *****************************************************************************/ +typedef struct argus_cal_offset_table_t { + /*! The offset values per pixel in meter and Q0.15 format. */ + q0_15_t Table[ARGUS_PIXELS_X][ARGUS_PIXELS_Y]; + +} argus_cal_offset_table_t; + + +/*! @} */ +#endif /* ARGUS_OFFSET_T */ diff --git a/src/drivers/distance_sensor/broadcom/afbrs50/Inc/api/argus_pba.h b/src/drivers/distance_sensor/broadcom/afbrs50/Inc/api/argus_pba.h index 07b4853bda..f28576500d 100644 --- a/src/drivers/distance_sensor/broadcom/afbrs50/Inc/api/argus_pba.h +++ b/src/drivers/distance_sensor/broadcom/afbrs50/Inc/api/argus_pba.h @@ -1,12 +1,12 @@ /*************************************************************************//** * @file - * @brief This file is part of the AFBR-S50 API. - * @details Defines the pixel binning algorithm (PBA) setup parameters and - * data structure. + * @brief This file is part of the AFBR-S50 API. + * @details Defines the pixel binning algorithm (PBA) setup parameters and + * data structure. * * @copyright * - * Copyright (c) 2021, Broadcom Inc + * Copyright (c) 2023, Broadcom Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,86 +37,98 @@ #ifndef ARGUS_PBA_H #define ARGUS_PBA_H +#ifdef __cplusplus +extern "C" { +#endif /*!*************************************************************************** - * @defgroup arguspba Pixel Binning Algorithm - * @ingroup argusapi + * @defgroup argus_pba Pixel Binning Algorithm + * @ingroup argus_api * - * @brief Pixel Binning Algorithm (PBA) parameter definitions and API functions. + * @brief Pixel Binning Algorithm (PBA) parameter definitions and API functions. * - * @details Defines the generic pixel binning algorithm (PBA) setup parameters - * and data structure. + * @details Defines the generic Pixel Binning Algorithm (PBA) setup parameters + * and data structure. * - * The PBA module contains filter algorithms that determine the - * pixels with the best signal quality and extract an 1d distance - * information from the filtered pixels. + * The PBA module contains filter algorithms that determine the + * pixels with the best signal quality and extract an 1D distance + * information from the filtered pixels by averaging them in a + * specified way. * - * The pixel filter algorithm is a three-stage filter with a - * fallback value: + * The Pixel Binning Algorithm is a three-stage filter with a + * fallback value: * - * -# A fixed pre-filter mask is applied to statically disable - * specified pixels. - * -# A relative and absolute amplitude filter is applied in the - * second stage. The relative filter is determined by a ratio - * of the maximum amplitude off all available (i.e. not filtered - * in stage 1) pixels. Pixels that have an amplitude below the - * relative threshold are dismissed. The same holds true for - * the absolute amplitude threshold. All pixel with smaller - * amplitude are dismissed.\n - * The relative threshold is useful to setup a distance - * measurement scenario. All well illuminated pixels are - * selected and considered for the final 1d distance. The - * absolute threshold is used to dismiss pixels that are below - * the noise level. The latter would be considered for the 1d - * result if the maximum amplitude is already very low. - * -# A distance filter is used to distinguish pixels that target - * the actual object from pixels that see the brighter background, - * e.g. white walls. Thus, the pixel with the minimum distance - * is referenced and all pixel that have a distance between - * the minimum and the given minimum distance scope are selected - * for the 1d distance result. The minimum distance scope is - * determined by an relative (to the current minimum distance) - * and an absolute value. The larger scope value is the - * relevant one, i.e. the relative distance scope can be used - * to heed the increasing noise at larger distances. - * -# If all of the above filters fail to determine a single valid - * pixel, the golden pixel is used as a fallback value. The - * golden pixel is the pixel that sits right at the focus point - * of the optics at large distances. - * . + * -# A fixed pre-filter mask is applied to statically disable + * specified pixels. + * -# A relative and absolute amplitude filter is applied in the + * second stage. The relative filter is determined by a ratio + * of the maximum amplitude off all available (i.e. not filtered + * in stage 1) pixels. Pixels that have an amplitude below the + * relative threshold are dismissed. The same holds true for + * the absolute amplitude threshold. All pixel with smaller + * amplitude are dismissed.\n + * Note that the absolute amplitude threshold is disabled if + * the Golden Pixel (see below) is also disabled in order to + * prevent invalid filtering for multi-pixel devices.\n + * The relative threshold is useful to setup a distance + * measurement scenario. All well illuminated pixels are + * selected and considered for the final 1D distance. The + * absolute threshold is used to dismiss pixels that are below + * the noise level. The latter would be considered for the 1D + * result if the maximum amplitude is already very low. + * -# An absolute minimum distance filter is applied in addition + * to the amplitude filter. This removes all pixel that have + * a lower distance than the specified threshold. This is used + * to remove invalid pixels that can be detected by a physically + * not correct negative distance. + * -# A distance filter is used to distinguish pixels that target + * the actual object from pixels that see the brighter background, + * e.g. white walls. Thus, the pixel with the minimum distance + * is referenced and all pixel that have a distance between + * the minimum and the given minimum distance scope are selected + * for the 1D distance result. The minimum distance scope is + * determined by an relative (to the current minimum distance) + * and an absolute value. The larger scope value is the + * relevant one, i.e. the relative distance scope can be used + * to heed the increasing noise at larger distances. + * -# If all of the above filters fail to determine a single valid + * pixel, the Golden Pixel is used as a fallback value. The + * Golden Pixel is the pixel that sits right at the focus point + * of the optics at large distances. + * . * - * After filtering is done, there may be more than a single pixel - * left to determine the 1d signal. Therefore several averaging - * methods are implemented to obtain the best 1d result from many - * pixels. See #argus_pba_averaging_mode_t for details. + * After filtering is done, there may be more than a single pixel + * left to determine the 1D signal. Therefore several averaging + * methods are implemented to obtain the best 1D result from many + * pixels. See #argus_pba_averaging_mode_t for details. * * - * @addtogroup arguspba + * @addtogroup argus_pba * @{ *****************************************************************************/ #include "argus_def.h" /*!*************************************************************************** - * @brief Enable flags for the pixel binning algorithm. + * @brief Enable flags for the pixel binning algorithm. * * @details Determines the pixel binning algorithm feature enable status. - * - [0]: #PBA_ENABLE: Enables the pixel binning feature. - * - [1]: reserved - * - [2]: reserved - * - [3]: reserved - * - [4]: reserved - * - [5]: #PBA_ENABLE_GOLDPX: Enables the golden pixel feature. - * - [6]: #PBA_ENABLE_MIN_DIST_SCOPE: Enables the minimum distance scope - * feature. - * - [7]: reserved - * . + * - [0]: #PBA_ENABLE: Enables the pixel binning feature. + * - [1]: reserved + * - [2]: reserved + * - [3]: reserved + * - [4]: reserved + * - [5]: #PBA_ENABLE_GOLDPX: Enables the Golden Pixel feature. + * - [6]: #PBA_ENABLE_MIN_DIST_SCOPE: Enables the minimum distance scope + * feature. + * - [7]: reserved + * . *****************************************************************************/ -typedef enum { +typedef enum argus_pba_flags_t { /*! Enables the pixel binning feature. */ PBA_ENABLE = 1U << 0U, - /*! Enables the golden pixel. */ + /*! Enables the Golden Pixel. */ PBA_ENABLE_GOLDPX = 1U << 5U, /*! Enables the minimum distance scope filter. */ @@ -125,9 +137,9 @@ typedef enum { } argus_pba_flags_t; /*!*************************************************************************** - * @brief The averaging modes for the pixel binning algorithm. + * @brief The averaging modes for the pixel binning algorithm. *****************************************************************************/ -typedef enum { +typedef enum argus_pba_averaging_mode_t { /*! Evaluate the 1D range from all available pixels using * a simple average. */ PBA_SIMPLE_AVG = 1U, @@ -140,11 +152,12 @@ typedef enum { } argus_pba_averaging_mode_t; /*!*************************************************************************** - * @brief The pixel binning algorithm settings data structure. - * @details Describes the pixel binning algorithm settings. + * @brief The pixel binning algorithm settings data structure. + * @details Describes the pixel binning algorithm settings. *****************************************************************************/ typedef struct { - /*! Enables the pixel binning features. + /*! Enables the Pixel Binning Algorithm. + * * Each bit may enable a different feature. See #argus_pba_flags_t * for details about the enabled flags. */ argus_pba_flags_t Enabled; @@ -156,6 +169,7 @@ typedef struct { argus_pba_averaging_mode_t AveragingMode; /*! The Relative amplitude threshold value (in %) of the max. amplitude. + * * Pixels with amplitude below this threshold value are dismissed. * * All available values from the 8-bit representation are valid. @@ -165,22 +179,27 @@ typedef struct { uq0_8_t RelAmplThreshold; /*! The relative minimum distance scope value in %. - * Pixels that have a range value within [x0, x0 + dx] are considered - * for the pixel binning, where x0 is the minimum distance of all - * amplitude picked pixels and dx is the minimum distance scope value. - * The minimum distance scope value will be the maximum of relative - * and absolute value. + * + * Pixels that have a range value within [x0, x0 + dx] are considered + * for the pixel binning, where x0 is the minimum distance of all + * amplitude picked pixels and dx is the minimum distance scope value. + * The minimum distance scope value will be the maximum of relative + * and absolute value. * * All available values from the 8-bit representation are valid. * The actual percentage value is determined by 100%/256*x. * - * Special values: + * Special values: * - 0: Use 0 for absolute value only or to choose the pixel with the - * minimum distance only (of also the absolute value is 0)! */ + * minimum distance only (of also the absolute value is 0)! */ uq0_8_t RelMinDistanceScope; - /*! The Absolute amplitude threshold value in LSB. - * Pixels with amplitude below this threshold value are dismissed. + /*! The absolute amplitude threshold value in LSB. + * + * Pixels with amplitude below this threshold value are dismissed. + * + * The absolute amplitude threshold is only valid if the Golden Pixel + * mode is enabled. Otherwise, the threshold is set to 0 LSB internally. * * All available values from the 16-bit representation are valid. * The actual LSB value is determined by x/16. @@ -189,33 +208,42 @@ typedef struct { uq12_4_t AbsAmplThreshold; /*! The absolute minimum distance scope value in m. - * Pixels that have a range value within [x0, x0 + dx] are considered - * for the pixel binning, where x0 is the minimum distance of all - * amplitude picked pixels and dx is the minimum distance scope value. - * The minimum distance scope value will be the maximum of relative - * and absolute value. + * + * Pixels that have a range value within [x0, x0 + dx] are considered + * for the pixel binning, where x0 is the minimum distance of all + * amplitude picked pixels and dx is the minimum distance scope value. + * The minimum distance scope value will be the maximum of relative + * and absolute value. * * All available values from the 16-bit representation are valid. * The actual LSB value is determined by x/2^15. * - * Special values: + * Special values: * - 0: Use 0 for relative value only or to choose the pixel with the - * minimum distance only (of also the relative value is 0)! */ + * minimum distance only (of also the relative value is 0)! */ uq1_15_t AbsMinDistanceScope; - /*! The pre-filter pixel mask determines the pixel channels that are - * statically excluded from the pixel binning (i.e. 1D distance) result. + /*! The absolute minimum distance threshold value in m. * - * The pixel enabled mask is an 32-bit mask that determines the - * device internal channel number. It is recommended to use the - * - #PIXELXY_ISENABLED(msk, x, y) - * - #PIXELXY_ENABLE(msk, x, y) - * - #PIXELXY_DISABLE(msk, x, y) - * . - * macros to work with the pixel enable masks. */ + * Pixels with distance below this threshold value are dismissed. */ + q9_22_t AbsMinDistanceThreshold; + + /*! The pre-filter pixel mask determines the pixel channels that are + * statically excluded from the pixel binning (i.e. 1D distance) result. + * + * The pixel enabled mask is an 32-bit mask that determines the + * device internal channel number. It is recommended to use the + * - #PIXELXY_ISENABLED(msk, x, y) + * - #PIXELXY_ENABLE(msk, x, y) + * - #PIXELXY_DISABLE(msk, x, y) + * . + * macros to work with the pixel enable masks. */ uint32_t PrefilterMask; } argus_cfg_pba_t; /*! @} */ +#ifdef __cplusplus +} // extern "C" +#endif #endif /* ARGUS_PBA_H */ diff --git a/src/drivers/distance_sensor/broadcom/afbrs50/Inc/api/argus_px.h b/src/drivers/distance_sensor/broadcom/afbrs50/Inc/api/argus_px.h index faa031aeb7..a739cea7f3 100644 --- a/src/drivers/distance_sensor/broadcom/afbrs50/Inc/api/argus_px.h +++ b/src/drivers/distance_sensor/broadcom/afbrs50/Inc/api/argus_px.h @@ -1,11 +1,11 @@ /*************************************************************************//** * @file - * @brief This file is part of the AFBR-S50 API. - * @details Defines the device pixel measurement results data structure. + * @brief This file is part of the AFBR-S50 API. + * @details Defines the device pixel measurement results data structure. * * @copyright * - * Copyright (c) 2021, Broadcom Inc + * Copyright (c) 2023, Broadcom Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -36,34 +36,40 @@ #ifndef ARGUS_PX_H #define ARGUS_PX_H +#ifdef __cplusplus +extern "C" { +#endif /*!*************************************************************************** - * @addtogroup argusres + * @addtogroup argus_res * @{ *****************************************************************************/ +#include "argus_def.h" + + /*! Maximum amplitude value in UQ12.4 format. */ -#define ARGUS_AMPLITUDE_MAX (0xFFF0U) +#define ARGUS_AMPLITUDE_MAX (0xFFF0U) /*! Maximum range value in Q9.22 format. * Also used as a special value to determine no object detected or infinity range. */ #define ARGUS_RANGE_MAX (Q9_22_MAX) /*!*************************************************************************** - * @brief Status flags for the evaluated pixel structure. + * @brief Status flags for the evaluated pixel structure. * * @details Determines the pixel status. 0 means OK (#PIXEL_OK). - * - [0]: #PIXEL_OFF: Pixel was disabled and not read from the device. - * - [1]: #PIXEL_SAT: The pixel was saturated. - * - [2]: #PIXEL_BIN_EXCL: The pixel was excluded from the 1D result. - * - [3]: #PIXEL_AMPL_MIN: The pixel amplitude has evaluated to 0. - * - [4]: #PIXEL_PREFILTERED: The was pre-filtered by static mask. - * - [5]: #PIXEL_NO_SIGNAL: The pixel has no valid signal. - * - [6]: #PIXEL_OUT_OF_SYNC: The pixel has lost signal trace. - * - [7]: #PIXEL_STALLED: The pixel value is stalled due to errors. - * . + * - [0]: #PIXEL_OFF: Pixel was disabled and not read from the device. + * - [1]: #PIXEL_SAT: The pixel was saturated. + * - [2]: #PIXEL_BIN_EXCL: The pixel was excluded from the 1D result. + * - [3]: #PIXEL_INVALID: The pixel data is invalid. + * - [4]: #PIXEL_PREFILTERED: The was pre-filtered by static mask. + * - [5]: #PIXEL_NO_SIGNAL: The pixel has no valid signal. + * - [6]: #PIXEL_OUT_OF_SYNC: The pixel has lost signal trace. + * - [7]: #PIXEL_STALLED: The pixel value is stalled due to errors. + * . *****************************************************************************/ -typedef enum { +typedef enum argus_px_status_t { /*! 0x00: Pixel status OK. */ PIXEL_OK = 0, @@ -77,43 +83,45 @@ typedef enum { /*! 0x04: Pixel is excluded from the pixel binning (1d) result. */ PIXEL_BIN_EXCL = 1U << 2U, - /*! 0x08: Pixel amplitude minimum underrun - * (i.e. the amplitude calculation yields 0). */ - PIXEL_AMPL_MIN = 1U << 3U, + /*! 0x08: Pixel has invalid data due to miscellaneous reasons, e.g. + * - Amplitude calculates to 0 (i.e. division by 0) + * - Golden Pixel is invalid due to other saturated pixel. + * - Range/distance is negative. */ + PIXEL_INVALID = 1U << 3U, /*! 0x10: Pixel is pre-filtered by the static pixel binning pre-filter mask, - * i.e. the pixel is disabled by software. */ + * i.e. the pixel is disabled by software. */ PIXEL_PREFILTERED = 1U << 4U, /*! 0x20: Pixel amplitude is below its threshold value. The received signal - * strength is too low to evaluate a valid signal. The range value is - * set to the maximum possible value (approx. 512 m). */ + * strength is too low to evaluate a valid signal. The range value is + * set to the maximum possible value (approx. 512 m). */ PIXEL_NO_SIGNAL = 1U << 5U, /*! 0x40: Pixel is not in sync with respect to the dual frequency algorithm. - * I.e. the pixel may have a correct value but is estimated into the - * wrong unambiguous window. */ + * I.e. the pixel may have a correct value but is estimated into the + * wrong unambiguous window. */ PIXEL_OUT_OF_SYNC = 1U << 6U, /*! 0x80: Pixel is stalled due to one of the following reasons: - * - #PIXEL_SAT - * - #PIXEL_AMPL_MIN - * - #PIXEL_OUT_OF_SYNC - * - Global Measurement Error - * . - * A stalled pixel does not update its measurement data and keeps the - * previous values. If the issue is resolved, the stall disappears and - * the pixel is updating again. */ + * - #PIXEL_SAT + * - #PIXEL_INVALID + * - #PIXEL_OUT_OF_SYNC + * - Global Measurement Error + * . + * A stalled pixel does not update its measurement data and keeps the + * previous values. If the issue is resolved, the stall disappears and + * the pixel is updating again. */ PIXEL_STALLED = 1U << 7U } argus_px_status_t; /*!*************************************************************************** - * @brief The evaluated measurement results per pixel. - * @details This structure contains the evaluated data for a single pixel.\n - * If the amplitude is 0, the pixel is turned off or has invalid data. + * @brief The evaluated measurement results per pixel. + * @details This structure contains the evaluated data for a single pixel.\n + * If the amplitude is 0, the pixel is turned off or has invalid data. *****************************************************************************/ -typedef struct { +typedef struct argus_pixel_t { /*! Range Values from the device in meter. It is the actual distance before * software adjustments/calibrations. */ q9_22_t Range; @@ -141,14 +149,23 @@ typedef struct { /*!*************************************************************************** * @brief Representation of a correlation vector containing sine/cosine components. *****************************************************************************/ -typedef struct { - /*! The sine component. */ - q15_16_t S; +typedef struct argus_vector_t { + union { + /*! The sine [0] and cosine [1] components. */ + q15_16_t SC[2]; - /*! The cosine component. */ - q15_16_t C; + struct { + /*! The sine component. */ + q15_16_t S; + /*! The cosine component. */ + q15_16_t C; + }; + }; } argus_vector_t; /*! @} */ +#ifdef __cplusplus +} // extern "C" +#endif #endif /* ARGUS_PX_H */ diff --git a/src/drivers/distance_sensor/broadcom/afbrs50/Inc/api/argus_res.h b/src/drivers/distance_sensor/broadcom/afbrs50/Inc/api/argus_res.h index f59d817634..7cb81bfcf1 100644 --- a/src/drivers/distance_sensor/broadcom/afbrs50/Inc/api/argus_res.h +++ b/src/drivers/distance_sensor/broadcom/afbrs50/Inc/api/argus_res.h @@ -1,11 +1,11 @@ /*************************************************************************//** * @file - * @brief This file is part of the AFBR-S50 API. - * @details Defines the generic measurement results data structure. + * @brief This file is part of the AFBR-S50 API. + * @details Defines the generic measurement results data structure. * * @copyright * - * Copyright (c) 2021, Broadcom Inc + * Copyright (c) 2023, Broadcom Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -36,35 +36,39 @@ #ifndef ARGUS_RES_H #define ARGUS_RES_H +#ifdef __cplusplus +extern "C" { +#endif /*!*************************************************************************** - * @defgroup argusres Measurement Data - * @ingroup argusapi + * @defgroup argus_res Measurement Data + * @ingroup argus_api * - * @brief Measurement results data structures. + * @brief Measurement results data structures. * - * @details The interface defines all data structures that correspond to - * the AFBR-S50 measurement results, e.g. - * - 1D distance and amplitude values, - * - 3D distance and amplitude values (i.e. per pixel), - * - Auxiliary channel measurement results (VDD, IAPD, temperature, ...) - * - Device and result status - * - ... - * . + * @details The interface defines all data structures that correspond to + * the AFBR-S50 measurement results, e.g. + * - 1D distance and amplitude values, + * - 3D distance and amplitude values (i.e. per pixel), + * - Auxiliary channel measurement results (VDD, IAPD, temperature, ...) + * - Device and result status + * - ... + * . * - * @addtogroup argusres + * @addtogroup argus_res * @{ *****************************************************************************/ -#include "argus_def.h" #include "argus_px.h" +#include "argus_def.h" #include "argus_meas.h" +#include "argus_xtalk.h" /*!*************************************************************************** - * @brief The 1d measurement results data structure. + * @brief The 1d measurement results data structure. * @details The 1d measurement results obtained by the Pixel Binning Algorithm. *****************************************************************************/ -typedef struct { +typedef struct argus_results_bin_t { /*! Raw 1D range value in meter (Q9.22 format). The distance obtained by * the Pixel Binning Algorithm from the current measurement frame. */ q9_22_t Range; @@ -83,11 +87,11 @@ typedef struct { } argus_results_bin_t; /*!*************************************************************************** - * @brief The auxiliary measurement results data structure. - * @details The auxiliary measurement results obtained by the auxiliary task.\n - * Special values, i.e. 0xFFFFU, indicate no readout value available. + * @brief The auxiliary measurement results data structure. + * @details The auxiliary measurement results obtained by the auxiliary task.\n + * Special values, i.e. 0xFFFFU, indicate no readout value available. *****************************************************************************/ -typedef struct { +typedef struct argus_results_aux_t { /*! VDD ADC channel readout value.\n * Special Value if no value has been measured:\n * Invalid/NotAvailable = 0xFFFFU (UQ12_4_MAX) */ @@ -129,32 +133,66 @@ typedef struct { } argus_results_aux_t; /*!*************************************************************************** - * @brief The measurement results data structure. + * @brief The debug data of measurement results data structure. + * @details This data structure will be filled with API internal data for + * debugging purposes. + *****************************************************************************/ +typedef struct argus_results_debug_t { + /*! The amplitude that is evaluated and used in the DCA module. */ + uq12_4_t DCAAmplitude; + + /*! Raw x-y-sorted ADC results from the device.\n + * Data is arranged as 32-bit values in following order: + * index > phase; where index is pixel number n and auxiliary ADC channel.\n + * Note that disabled pixels are skipped.\n + * e.g. [n=0,p=0][n=0,p=1]..[n=0,p=3][n=1,p=0]...[n=1,p=3]...[n=31,p=3] */ + uint32_t Data[ARGUS_RAW_DATA_VALUES]; + + /*! The current crosstalk correction values as determined by the + * crosstalk predictor algorithm. This is basically the temperature + * dependent portion of the crosstalk correction.\n + * Note that there are two values for the upper and lower two rows + * respectively. */ + xtalk_t XtalkPredictor[ARGUS_PIXELS_Y / 2U]; + + /*! The current crosstalk correction values as determined by the + * crosstalk monitor algorithm. This is a dynamic portion of the + * crosstalk correction that is determined by monitoring passive + * pixels.\n + * Note that the values are valid row-wise. */ + xtalk_t XtalkMonitor[ARGUS_PIXELS_Y]; + +} argus_results_debug_t; + +/*!*************************************************************************** + * @brief The measurement results data structure. * @details This structure contains all information obtained by a single - * distance measurement on the device: - * - The measurement status can be read from the #Status. - * - A timing information is given via the #TimeStamp. - * - Information about the frame state is in the #Frame structure. - * - The 1D distance results are gathered under #Bin. - * - The 3D distance results for each pixel is at #Pixels or #Pixel. - * - Auxiliary values such as temperature can be found at #Auxiliary. - * - Raw data from the device is stored in the #Data array. - * . + * distance measurement on the device: + * - The measurement status can be read from the #Status. + * - A timing information is given via the #TimeStamp. + * - Information about the frame state is in the #Frame structure. + * - The 1D distance results are gathered under #Bin. + * - The 3D distance results for each pixel is at #Pixels or #Pixel. + * - Auxiliary values such as temperature can be found at #Auxiliary. + * - Raw data and debug information from the device and API is stored + * in the optional #Debug data structure. Note that this points to + * an optional structure and can be null! + * . * - * The pixel x-y orientation is sketched in the following graph. Note that - * the laser source would be on the right side beyond the reference pixel. - * See also \link argusmap ADC Channel Mapping\endlink + * The pixel x-y orientation is sketched in the following graph. Note that + * the laser source would be on the right side beyond the reference pixel. + * See also \link argus_map ADC Channel Mapping\endlink * @code - * // Pixel Field: Pixel[x][y] - * // - * // 0 -----------> x - * // | O O O O O O O O - * // | O O O O O O O O - * // | O O O O O O O O O (ref. Px) - * // y O O O O O O O O + * // Pixel Field: Pixel[x][y] + * // + * // 0 -----------> x + * // | O O O O O O O O + * // | O O O O O O O O + * // | O O O O O O O O O (ref. Px) + * // y O O O O O O O O * @endcode *****************************************************************************/ -typedef struct { +typedef struct argus_results_t { /*! The \link #status_t status\endlink of the current measurement frame. * - 0 (i.e. #STATUS_OK) for a good measurement signal. * - > 0 for warnings and weak measurement signal. @@ -168,13 +206,6 @@ typedef struct { /*! The configuration for the current measurement frame. */ argus_meas_frame_t Frame; - /*! Raw x-y-sorted ADC results from the device.\n - * Data is arranged as 32-bit values in following order: - * index > phase; where index is pixel number n and auxiliary ADC channel.\n - * Note that disabled pixels are skipped.\n - * e.g. [n=0,p=0][n=0,p=1]..[n=0,p=3][n=1,p=0]...[n=1,p=3]...[n=31,p=3] */ - uint32_t Data[ARGUS_RAW_DATA_VALUES]; - union { /*! Pixel data indexed by channel number n.\n * Contains calibrated range, amplitude and pixel status among others. @@ -183,14 +214,14 @@ typedef struct { * - 0..31: active pixels * - 32: reference pixel * - * See also \link argusmap ADC Channel Mapping\endlink */ + * See also \link argus_map ADC Channel Mapping\endlink */ argus_pixel_t Pixels[ARGUS_PIXELS + 1U]; struct { /*! Pixel data indexed by x-y-indices.\n * The pixels are ordered in a two dimensional array that represent * the x and y indices of the pixel.\n - * See also \link argusmap ADC Channel Mapping\endlink + * See also \link argus_map ADC Channel Mapping\endlink * * Contains calibrated range, amplitude and pixel status among others. */ argus_pixel_t Pixel[ARGUS_PIXELS_X][ARGUS_PIXELS_Y]; @@ -213,8 +244,17 @@ typedef struct { /*! The auxiliary ADC channel data, e.g. sensor temperature. */ argus_results_aux_t Auxiliary; + /*! Optional Debug Data. + * If the pointer is set to a #argus_results_debug_t data structure before + * passing it to the #Argus_EvaluateData function, the data structure is + * filled with internal parameters for debugging purposes. */ + argus_results_debug_t *Debug; + } argus_results_t; /*! @} */ +#ifdef __cplusplus +} // extern "C" +#endif #endif /* ARGUS_RES_H */ diff --git a/src/drivers/distance_sensor/broadcom/afbrs50/Inc/api/argus_snm.h b/src/drivers/distance_sensor/broadcom/afbrs50/Inc/api/argus_snm.h index 2776308143..2b77965bb0 100644 --- a/src/drivers/distance_sensor/broadcom/afbrs50/Inc/api/argus_snm.h +++ b/src/drivers/distance_sensor/broadcom/afbrs50/Inc/api/argus_snm.h @@ -1,11 +1,11 @@ /*************************************************************************//** * @file - * @brief This file is part of the AFBR-S50 API. - * @details Defines the Shot Noise Monitor (SNM) setup parameters. + * @brief This file is part of the AFBR-S50 API. + * @details Defines the Shot Noise Monitor (SNM) setup parameters. * * @copyright * - * Copyright (c) 2021, Broadcom Inc + * Copyright (c) 2023, Broadcom Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -36,30 +36,33 @@ #ifndef ARGUS_SNM_H #define ARGUS_SNM_H +#ifdef __cplusplus +extern "C" { +#endif /*!*************************************************************************** - * @defgroup argussnm Shot Noise Monitor - * @ingroup argusapi + * @defgroup argus_snm Shot Noise Monitor + * @ingroup argus_api * - * @brief Shot Noise Monitor (SNM) parameter definitions and API functions. + * @brief Shot Noise Monitor (SNM) parameter definitions and API functions. * - * @details The SNM is an algorithm to monitor and react on shot noise - * induced by harsh environment conditions like high ambient - * light. + * @details The SNM is an algorithm to monitor and react on shot noise + * induced by harsh environment conditions like high ambient + * light. * - * The AFBR-S50 API provides three modes: - * - Dynamic: Automatic mode, automatically adopts to current - * ambient conditions. - * - Static (Outdoor): Static mode, optimized for outdoor applications. - * - Static (Indoor): Static mode, optimized for indoor applications. - * . + * The AFBR-S50 API provides three modes: + * - Dynamic: Automatic mode, automatically adopts to current + * ambient conditions. + * - Static (Outdoor): Static mode, optimized for outdoor applications. + * - Static (Indoor): Static mode, optimized for indoor applications. + * . * - * @addtogroup argussnm + * @addtogroup argus_snm * @{ *****************************************************************************/ /*! The Shot Noise Monitor modes enumeration. */ -typedef enum { +typedef enum argus_snm_mode_t { /*! Static Shot Noise Monitoring Mode, optimized for indoor applications. * Assumes the best case scenario, i.e. no bad influence from ambient conditions. * Thus it uses a fixed setting that will result in the best performance. @@ -79,4 +82,7 @@ typedef enum { /*! @} */ +#ifdef __cplusplus +} // extern "C" +#endif #endif /* ARGUS_SNM_H */ diff --git a/src/drivers/distance_sensor/broadcom/afbrs50/Inc/api/argus_status.h b/src/drivers/distance_sensor/broadcom/afbrs50/Inc/api/argus_status.h index 244ad1beec..77cd856413 100644 --- a/src/drivers/distance_sensor/broadcom/afbrs50/Inc/api/argus_status.h +++ b/src/drivers/distance_sensor/broadcom/afbrs50/Inc/api/argus_status.h @@ -1,11 +1,11 @@ /*************************************************************************//** * @file - * @brief This file is part of the AFBR-S50 API. - * @details Provides status codes for the AFBR-S50 API. + * @brief This file is part of the AFBR-S50 API. + * @details Provides status codes for the AFBR-S50 API. * * @copyright * - * Copyright (c) 2021, Broadcom Inc + * Copyright (c) 2023, Broadcom Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -36,25 +36,32 @@ #ifndef ARGUS_STATUS_H #define ARGUS_STATUS_H +#ifdef __cplusplus +extern "C" { +#endif #include /*!*************************************************************************** - * @defgroup status Status Codes + * @defgroup argus_status Status Codes + * @ingroup argus + * * @brief Status and Error Code Definitions + * * @details Defines status and error codes for function return values. * Basic status number structure: * - 0 is OK or no error. * - negative values determine errors. * - positive values determine warnings or status information. * . - * @addtogroup status + * + * @addtogroup argus_status * @{ *****************************************************************************/ /*!*************************************************************************** - * @brief Type used for all status and error return values. - * @details Basic status number structure: + * @brief Type used for all status and error return values. + * @details Basic status number structure: * - 0 is OK or no error. * - negative values determine errors. * - positive values determine warnings or status information. @@ -138,8 +145,8 @@ enum Status { ********** NVM / Flash Layer Status ********************************************************* *********************************************************************************************/ - /*! -98: Flash Error: The version of the settings in the flash memory is not compatible. */ - ERROR_NVM_INVALID_FILE_VERSION = -98, + /*! -98: Flash Error: The read memory block was not written previously and contains no data. */ + ERROR_NVM_EMPTY = -98, /*! -99: Flash Error: The memory is out of range. */ ERROR_NVM_OUT_OF_RANGE = -99, @@ -183,6 +190,13 @@ enum Status { /*! -102: AFBR-S50 Error: Inconsistent configuration parameters. */ ERROR_ARGUS_INVALID_CFG = -102, + /*! -103: AFBR-S50 Error: The evaluation function has been called but no + * raw data is available yet. + * See also #Argus_EvaluateData for more information. */ + ERROR_ARGUS_BUFFER_EMPTY = -103, + + /*! -104: AFBR-S50 Error: Invalid slave identifier is passed to the module. */ + ERROR_ARGUS_INVALID_SLAVE = -104, /*! -105: AFBR-S50 Error: Invalid measurement mode configuration parameter. */ ERROR_ARGUS_INVALID_MODE = -105, @@ -191,7 +205,6 @@ enum Status { * The current measurement data set is invalid! */ ERROR_ARGUS_BIAS_VOLTAGE_REINIT = -107, - /*! -109: AFBR-S50 Error: The EEPROM readout has failed. The failure is detected * by three distinct read attempts, each resulting in invalid data. * Note: this state differs from that #STATUS_ARGUS_EEPROM_BIT_ERROR @@ -224,7 +237,6 @@ enum Status { * requested command. */ ERROR_ARGUS_BUSY = -191, - /*! -199: AFBR-S50 Error: Unknown module number. */ ERROR_ARGUS_UNKNOWN_MODULE = -199, @@ -235,24 +247,22 @@ enum Status { ERROR_ARGUS_UNKNOWN_LASER = -197, + /*! 191: AFBR-S50 Status (internal): The device is currently busy with testing the + * SPI connection to the device. */ + STATUS_ARGUS_BUSY_TEST = 191, - /*! 193: AFBR-S50 Status (internal): The device is currently busy with updating the - * configuration (i.e. with writing register values). */ - STATUS_ARGUS_BUSY_CFG_UPDATE = 193, - - /*! 194: AFBR-S50 Status (internal): The device is currently busy with updating the - * calibration data (i.e. writing to register values). */ - STATUS_ARGUS_BUSY_CAL_UPDATE = 194, + /*! 192: AFBR-S50 Status (internal): The device is currently busy with updating the + * settings parameter (i.e. with writing register values). */ + STATUS_ARGUS_BUSY_UPDATE = 192, /*! 195: AFBR-S50 Status (internal): The device is currently executing a calibration - * sequence. */ + * sequence. */ STATUS_ARGUS_BUSY_CAL_SEQ = 195, /*! 196: AFBR-S50 Status (internal): The device is currently executing a measurement * cycle. */ STATUS_ARGUS_BUSY_MEAS = 196, - /*! 100: AFBR-S50 Status (internal): The ASIC is initializing a new measurement, i.e. * a register value is written that starts an integration cycle on the ASIC. */ STATUS_ARGUS_STARTING = 100, @@ -260,9 +270,10 @@ enum Status { /*! 103: AFBR-S50 Status (internal): The ASIC is performing an integration cycle. */ STATUS_ARGUS_ACTIVE = 103, - - }; /*! @} */ +#ifdef __cplusplus +} // extern "C" +#endif #endif /* ARGUS_STATUS_H */ diff --git a/src/drivers/distance_sensor/broadcom/afbrs50/Inc/api/argus_version.h b/src/drivers/distance_sensor/broadcom/afbrs50/Inc/api/argus_version.h index ea16342c84..a1a2d878ac 100644 --- a/src/drivers/distance_sensor/broadcom/afbrs50/Inc/api/argus_version.h +++ b/src/drivers/distance_sensor/broadcom/afbrs50/Inc/api/argus_version.h @@ -1,11 +1,11 @@ /*************************************************************************//** * @file - * @brief This file is part of the AFBR-S50 API. - * @details This file contains the current API version number. + * @brief This file is part of the AFBR-S50 API. + * @details This file contains the current API version number. * * @copyright * - * Copyright (c) 2021, Broadcom Inc + * Copyright (c) 2023, Broadcom Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -36,16 +36,19 @@ #ifndef ARGUS_VERSION_H #define ARGUS_VERSION_H +#ifdef __cplusplus +extern "C" { +#endif /*!*************************************************************************** - * @defgroup version API Version - * @ingroup argusapi + * @defgroup argus_version API Version + * @ingroup argus_api * - * @brief API and library core version number + * @brief API and library core version number * - * @details Contains the AFBR-S50 API and Library Core Version Number. + * @details Contains the AFBR-S50 API and Library Core Version Number. * - * @addtogroup version + * @addtogroup argus_version * @{ *****************************************************************************/ @@ -53,13 +56,13 @@ #define ARGUS_API_VERSION_MAJOR 1 /*! Minor version number of the AFBR-S50 API. */ -#define ARGUS_API_VERSION_MINOR 3 +#define ARGUS_API_VERSION_MINOR 4 /*! Bugfix version number of the AFBR-S50 API. */ -#define ARGUS_API_VERSION_BUGFIX 5 +#define ARGUS_API_VERSION_BUGFIX 4 -/*! Build version nunber of the AFBR-S50 API. */ -#define ARGUS_API_VERSION_BUILD "20210812171515" +/*! Build version number of the AFBR-S50 API. */ +#define ARGUS_API_VERSION_BUILD "20230327150535" /*****************************************************************************/ @@ -73,4 +76,7 @@ (ARGUS_API_VERSION_BUGFIX)) /*! @} */ +#ifdef __cplusplus +} // extern "C" +#endif #endif /* ARGUS_VERSION_H */ diff --git a/src/drivers/distance_sensor/broadcom/afbrs50/Inc/api/argus_xtalk.h b/src/drivers/distance_sensor/broadcom/afbrs50/Inc/api/argus_xtalk.h index 5613706267..6f3d40b49a 100644 --- a/src/drivers/distance_sensor/broadcom/afbrs50/Inc/api/argus_xtalk.h +++ b/src/drivers/distance_sensor/broadcom/afbrs50/Inc/api/argus_xtalk.h @@ -1,11 +1,11 @@ /*************************************************************************//** * @file - * @brief This file is part of the AFBR-S50 hardware API. - * @details Defines the generic device calibration API. + * @brief This file is part of the AFBR-S50 hardware API. + * @details Defines the generic device calibration API. * * @copyright * - * Copyright (c) 2021, Broadcom Inc + * Copyright (c) 2023, Broadcom Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -36,44 +36,71 @@ #ifndef ARGUS_XTALK_H #define ARGUS_XTALK_H +#ifdef __cplusplus +extern "C" { +#endif /*!*************************************************************************** - * @addtogroup arguscal + * @addtogroup argus_cal * @{ *****************************************************************************/ -#include "api/argus_def.h" +#include "argus_def.h" +#include "argus_dfm.h" /*!*************************************************************************** - * @brief Pixel Crosstalk Compensation Vector. - * @details Contains calibration data (per pixel) that belongs to the - * RX-TX-Crosstalk compensation feature. + * @brief Pixel Crosstalk Compensation Vector. + * @details Contains calibration data (per pixel) that belongs to the + * RX-TX-Crosstalk compensation feature. + * The crosstalk vector consists of a Sine and Cosine component in LSB. *****************************************************************************/ - -/*! Pixel Crosstalk Vector */ -typedef struct { +typedef struct xtalk_t { /*! Crosstalk Vector - Sine component. + * Units: LSB * Special Value: Q11_4_MIN == not available */ q11_4_t dS; /*! Crosstalk Vector - Cosine component. + * Units: LSB * Special Value: Q11_4_MIN == not available */ q11_4_t dC; } xtalk_t; /*!*************************************************************************** - * @brief Pixel-To-Pixel Crosstalk Compensation Parameters. - * @details Contains calibration data that belongs to the pixel-to-pixel - * crosstalk compensation feature. + * @brief Pixel Crosstalk Vector Table. + * @details Contains crosstalk vector values for all 32 active pixels, + * separated for A/B-Frames. *****************************************************************************/ -typedef struct { +typedef struct argus_cal_xtalk_table_t { + union { + struct { + /*! The crosstalk vector table for A-Frames. */ + xtalk_t FrameA[ARGUS_PIXELS_X][ARGUS_PIXELS_Y]; + + /*! The crosstalk vector table for B-Frames. */ + xtalk_t FrameB[ARGUS_PIXELS_X][ARGUS_PIXELS_Y]; + }; + + /*! The crosstalk vector table for A/B-Frames of all 32 pixels.*/ + xtalk_t Table[ARGUS_DFM_FRAME_COUNT][ARGUS_PIXELS_X][ARGUS_PIXELS_Y]; + }; + +} argus_cal_xtalk_table_t; + + +/*!*************************************************************************** + * @brief Pixel-To-Pixel Crosstalk Compensation Parameters. + * @details Contains calibration data that belongs to the pixel-to-pixel + * crosstalk compensation feature. + *****************************************************************************/ +typedef struct argus_cal_p2pxtalk_t { /*! Pixel-To-Pixel Compensation on/off. */ bool Enabled; /*! The relative threshold determines when the compensation is active for * each individual pixel. The value determines the ratio of the individual - * pixel signal is with respect to the overall average signal. If the + * pixel signal with respect to the overall average signal. If the * ratio is smaller than the value, the compensation is active. Absolute * and relative conditions are connected with AND logic. */ uq0_8_t RelativeThreshold; @@ -111,4 +138,7 @@ typedef struct { /*! @} */ +#ifdef __cplusplus +} // extern "C" +#endif #endif /* ARGUS_XTALK_H */ diff --git a/src/drivers/distance_sensor/broadcom/afbrs50/Inc/argus.h b/src/drivers/distance_sensor/broadcom/afbrs50/Inc/argus.h index dcea881d02..79cf0ede58 100644 --- a/src/drivers/distance_sensor/broadcom/afbrs50/Inc/argus.h +++ b/src/drivers/distance_sensor/broadcom/afbrs50/Inc/argus.h @@ -1,11 +1,11 @@ /*************************************************************************//** * @file - * @brief This file is part of the AFBR-S50 API. - * @details This file the main header of the AFBR-S50 API. + * @brief This file is part of the AFBR-S50 API. + * @details This file the main header of the AFBR-S50 API. * * @copyright * - * Copyright (c) 2021, Broadcom Inc + * Copyright (c) 2023, Broadcom Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -36,7 +36,6 @@ #ifndef ARGUS_H #define ARGUS_H - #ifdef __cplusplus extern "C" { #endif @@ -44,7 +43,6 @@ extern "C" { #include "api/argus_api.h" #ifdef __cplusplus -} +} // extern "C" #endif - #endif /* ARGUS_H */ diff --git a/src/drivers/distance_sensor/broadcom/afbrs50/Inc/platform/argus_irq.h b/src/drivers/distance_sensor/broadcom/afbrs50/Inc/platform/argus_irq.h index 3eb7d2cfd4..d55097cd8a 100644 --- a/src/drivers/distance_sensor/broadcom/afbrs50/Inc/platform/argus_irq.h +++ b/src/drivers/distance_sensor/broadcom/afbrs50/Inc/platform/argus_irq.h @@ -1,11 +1,11 @@ /*************************************************************************//** * @file - * @brief This file is part of the AFBR-S50 API. - * @details This file provides an interface for enabling/disabling interrupts. + * @brief This file is part of the AFBR-S50 API. + * @details This file provides an interface for enabling/disabling interrupts. * * @copyright * - * Copyright (c) 2021, Broadcom Inc + * Copyright (c) 2023, Broadcom Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -36,96 +36,94 @@ #ifndef ARGUS_IRQ_H #define ARGUS_IRQ_H - #ifdef __cplusplus extern "C" { #endif /*!*************************************************************************** - * @defgroup argus_irq IRQ: Global Interrupt Control Layer - * @ingroup argus_platform + * @defgroup argus_irq IRQ: Global Interrupt Control Layer + * @ingroup argus_hal * - * @brief Global Interrupt Control Layer + * @brief Global Interrupt Control Layer * - * @details This module provides functionality to globally enable/disable - * interrupts in a nested way. + * @details This module provides functionality to globally enable/disable + * interrupts in a nested way. * - * Here is a simple example implementation using the CMSIS functions - * "__enable_irq()" and "__disable_irq()". An integer counter is - * used to achieve nested interrupt disabling: + * Here is a simple example implementation using the CMSIS functions + * "__enable_irq()" and "__disable_irq()". An integer counter is + * used to achieve nested interrupt disabling: * - * @code + * @code * - * // Global lock level counter value. - * static volatile int g_irq_lock_ct; + * // Global lock level counter value. + * static volatile int g_irq_lock_ct; * - * // Global unlock all interrupts using CMSIS function "__enable_irq()". - * void IRQ_UNLOCK(void) - * { - * assert(g_irq_lock_ct > 0); - * if (--g_irq_lock_ct <= 0) - * { - * g_irq_lock_ct = 0; - * __enable_irq(); - * } - * } + * // Global unlock all interrupts using CMSIS function "__enable_irq()". + * void IRQ_UNLOCK(void) + * { + * assert(g_irq_lock_ct > 0); + * if (--g_irq_lock_ct <= 0) + * { + * g_irq_lock_ct = 0; + * __enable_irq(); + * } + * } * - * // Global lock all interrupts using CMSIS function "__disable_irq()". - * void IRQ_LOCK(void) - * { - * __disable_irq(); - * g_irq_lock_ct++; - * } + * // Global lock all interrupts using CMSIS function "__disable_irq()". + * void IRQ_LOCK(void) + * { + * __disable_irq(); + * g_irq_lock_ct++; + * } * - * @endcode + * @endcode * - * @note The IRQ locking mechanism is used to create atomic sections - * (within the scope of the AFBR-S50 API) that are very few processor - * instruction only. It does NOT lock interrupts for considerable - * amounts of time. + * @note The IRQ locking mechanism is used to create atomic sections + * (within the scope of the AFBR-S50 API) that are very few processor + * instruction only. It does NOT lock interrupts for considerable + * amounts of time. * - * @note The IRQ_LOCK might get called multiple times. Therefore, the - * API expects that the IRQ_UNLOCK must be called as many times as - * the IRQ_LOCK was called before the interrupts are enabled. + * @note The IRQ_LOCK might get called multiple times. Therefore, the + * API expects that the IRQ_UNLOCK must be called as many times as + * the IRQ_LOCK was called before the interrupts are enabled. * - * @note The interrupts utilized by the AFBR-S50 API can be interrupted - * by other, higher prioritized interrupts, e.g. some system - * critical interrupts. In this case, the IRQ_LOCK/IRQ_UNLOCK - * mechanism can be implemented such that only the interrupts - * required for the AFBR-S50 API are locked. The above example is - * dedicated to a ARM Corex-M0 architecture, where interrupts - * can only disabled at a global scope. Other architectures like - * ARM Cortex-M4 allow selective disabling of interrupts. + * @note The interrupts utilized by the AFBR-S50 API can be interrupted + * by other, higher prioritized interrupts, e.g. some system + * critical interrupts. In this case, the IRQ_LOCK/IRQ_UNLOCK + * mechanism can be implemented such that only the interrupts + * required for the AFBR-S50 API are locked. The above example is + * dedicated to a ARM Corex-M0 architecture, where interrupts + * can only disabled at a global scope. Other architectures like + * ARM Cortex-M4 allow selective disabling of interrupts. * - * @addtogroup argus_irq + * @addtogroup argus_irq * @{ *****************************************************************************/ /*!*************************************************************************** - * @brief Enable IRQ Interrupts + * @brief Enable IRQ Interrupts * - * @details Enables IRQ interrupts and enters an atomic or critical section. + * @details Enables IRQ interrupts and enters an atomic or critical section. * - * @note The IRQ_LOCK might get called multiple times. Therefore, the - * API expects that the IRQ_UNLOCK must be called as many times as - * the IRQ_LOCK was called before the interrupts are enabled. + * @note The IRQ_LOCK might get called multiple times. Therefore, the + * API expects that the IRQ_UNLOCK must be called as many times as + * the IRQ_LOCK was called before the interrupts are enabled. *****************************************************************************/ void IRQ_UNLOCK(void); /*!*************************************************************************** - * @brief Disable IRQ Interrupts + * @brief Disable IRQ Interrupts * - * @details Disables IRQ interrupts and leaves the atomic or critical section. + * @details Disables IRQ interrupts and leaves the atomic or critical section. * - * @note The IRQ_LOCK might get called multiple times. Therefore, the - * API expects that the IRQ_UNLOCK must be called as many times as - * the IRQ_LOCK was called before the interrupts are enabled. + * @note The IRQ_LOCK might get called multiple times. Therefore, the + * API expects that the IRQ_UNLOCK must be called as many times as + * the IRQ_LOCK was called before the interrupts are enabled. *****************************************************************************/ void IRQ_LOCK(void); -#ifdef __cplusplus -} -#endif - /*! @} */ +#ifdef __cplusplus +} // extern "C" +#endif #endif // ARGUS_IRQ_H diff --git a/src/drivers/distance_sensor/broadcom/afbrs50/Inc/platform/argus_nvm.h b/src/drivers/distance_sensor/broadcom/afbrs50/Inc/platform/argus_nvm.h index 69939b7759..b8150f0281 100644 --- a/src/drivers/distance_sensor/broadcom/afbrs50/Inc/platform/argus_nvm.h +++ b/src/drivers/distance_sensor/broadcom/afbrs50/Inc/platform/argus_nvm.h @@ -1,11 +1,11 @@ /*************************************************************************//** * @file - * @brief This file is part of the AFBR-S50 API. - * @details This file provides an interface for the optional non-volatile memory. + * @brief This file is part of the AFBR-S50 API. + * @details This file provides an interface for the optional non-volatile memory. * * @copyright * - * Copyright (c) 2021, Broadcom Inc + * Copyright (c) 2023, Broadcom Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -36,99 +36,113 @@ #ifndef ARGUS_NVM_H #define ARGUS_NVM_H - #ifdef __cplusplus extern "C" { #endif /*!*************************************************************************** - * @defgroup argus_nvm NVM: Non-Volatile Memory Layer - * @ingroup argus_platform + * @defgroup argus_nvm NVM: Non-Volatile Memory Layer + * @ingroup argus_hal * - * @brief Non-Volatile Memory Layer + * @brief Non-Volatile Memory Layer * - * @details This module provides functionality to access the non-volatile - * memory (e.g. flash) on the underlying platform. + * @details This module provides functionality to access the non-volatile + * memory (e.g. flash) on the underlying platform. * - * This module is optional and only required if calibration data - * needs to be stored within the API. + * This module is optional and only required if calibration data + * needs to be stored within the API. * - * @note The implementation of this module is optional for the correct - * execution of the API. If not implemented, a weak implementation - * within the API will be used that disables the NVM feature. + * @note The implementation of this module is optional for the correct + * execution of the API. If not implemented, a weak implementation + * within the API will be used that disables the NVM feature. * - * @addtogroup argus_nvm + * @addtogroup argus_nvm * @{ *****************************************************************************/ -#include "argus.h" +#include "api/argus_def.h" + +/*! The NVM block size in the non-volatile memory. */ +#define ARGUS_NVM_BLOCK_SIZE 0x300 // 768 bytes /*!*************************************************************************** - * @brief Initializes the non-volatile memory unit and reserves a chunk of memory. + * @brief Write a block of data to the non-volatile memory. * - * @details The function is called upon API initialization sequence. If available, - * the non-volatile memory module reserves a chunk of memory with the - * provides number of bytes (size) and returns with #STATUS_OK. + * @details The function is called whenever the API wants to write data into + * non-volatile memory, e.g. flash. Later, the API reads the written + * data via the #NVM_ReadBlock function. * - * If not implemented, the function should return #ERROR_NOT_IMPLEMENTED - * in oder to inform the API to not use the NVM module. + * The data shall be written to a specified memory block that is + * uniquely dedicated to each individual device. The /p id parameter + * is passed to the function that identifies the device. The /p id + * is composed of the device ID and module type, i.e. it is unique + * among all devices. If only a single device is used anyway, the + * /p id parameter can be ignored. * - * After initialization, the API calls the #NVM_Write and #NVM_Read - * methods to write within the reserved chunk of memory. + * If no NVM module is available, the function can return with error + * #ERROR_NOT_IMPLEMENTED and the API ignores the NVM. * - * @note The implementation of this function is optional for the correct - * execution of the API. If not implemented, a weak implementation - * within the API will be used that disables the NVM feature. + * If write fails, e.g. due to lack of memory, a negative status + * must be returned, e.g. #ERROR_NVM_OUT_OF_RANGE. * - * @param size The required size of NVM to store all parameters. - * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). + * The block size is fixed for a single device. The actual block size + * is defined with #ARGUS_NVM_BLOCK_SIZE. + * + * @note The implementation of this function is optional for the correct + * execution of the API. If not implemented, a weak implementation + * within the API will be used that disables the NVM feature. + * + * @param id The 32-bit ID number to identify the corresponding memory block. + * @param block_size The number of bytes to be written. Note that this value + * is fixed, i.e. the API always writes the same data size. + * The size is defined here: #ARGUS_NVM_BLOCK_SIZE. + * @param buf The pointer to the data buffer of size /p block_size that needs + * to be written to the NVM. + * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). *****************************************************************************/ -status_t NVM_Init(uint32_t size); +status_t NVM_WriteBlock(uint32_t id, uint32_t block_size, uint8_t const *buf); /*!*************************************************************************** - * @brief Write a block of data to the non-volatile memory. + * @brief Reads a block of data from the non-volatile memory. * - * @details The function is called whenever the API wants to write data into - * the previously reserved (#NVM_Init) memory block. The data shall - * be written at a given offset and with a given size. + * @details The function is called whenever the API wants to read data from + * non-volatile memory, e.g. flash. The data will be previously + * stored using the #NVM_WriteBlock function. Otherwise, the function + * must return a corresponding error code, namely #ERROR_NVM_EMPTY. * - * If no NVM module is available, the function can return with error - * #ERROR_NOT_IMPLEMENTED. + * The data shall be read from a specified memory block that is + * uniquely dedicated to each individual device. The /p id parameter + * is passed to the function that identifies the device. The /p id + * is composed of the device ID and module type, i.e. it is unique + * among all devices. If only a single device is used anyway, the + * /p id parameter can be ignored. * - * @note The implementation of this function is optional for the correct - * execution of the API. If not implemented, a weak implementation - * within the API will be used that disables the NVM feature. + * If no NVM module is available, the function can return with error + * #ERROR_NOT_IMPLEMENTED and the API ignores the NVM. * - * @param offset The index offset where the first byte needs to be written. - * @param size The number of bytes to be written. - * @param buf The pointer to the data buffer with the data to be written. - * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). + * If read fails, e.g. if data has not been written previously, + * a negative status must be returned, e.g. #ERROR_NVM_EMPTY if no + * data has been written yet or any other negative error else-wise. + * + * The block size is fixed for a single device. The actual block size + * is defined with #ARGUS_NVM_BLOCK_SIZE. + * + * @note The implementation of this function is optional for the correct + * execution of the API. If not implemented, a weak implementation + * within the API will be used that disables the NVM feature. + * + * @param id The 32-bit ID number to identify the corresponding memory block. + * @param block_size The number of bytes to be read. Note that this value + * is fixed, i.e. the API always reads the same data size. + * The size is defined here: #ARGUS_NVM_BLOCK_SIZE. + * @param buf The pointer to the data buffer of size /p block_size to copy + * the data to. + * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). *****************************************************************************/ -status_t NVM_Write(uint32_t offset, uint32_t size, uint8_t const *buf); - -/*!*************************************************************************** - * @brief Reads a block of data from the non-volatile memory. - * - * @details The function is called whenever the API wants to read data from - * the previously reserved (#NVM_Init) memory block. The data shall - * be read at a given offset and with a given size. - * - * If no NVM module is available, the function can return with error - * #ERROR_NOT_IMPLEMENTED. - * - * @note The implementation of this function is optional for the correct - * execution of the API. If not implemented, a weak implementation - * within the API will be used that disables the NVM feature. - * - * @param offset The index offset where the first byte needs to be read. - * @param size The number of bytes to be read. - * @param buf The pointer to the data buffer to copy the data to. - * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). - *****************************************************************************/ -status_t NVM_Read(uint32_t offset, uint32_t size, uint8_t *buf); -#ifdef __cplusplus -} -#endif +status_t NVM_ReadBlock(uint32_t id, uint32_t block_size, uint8_t *buf); /*! @} */ +#ifdef __cplusplus +} // extern "C" +#endif #endif // ARGUS_NVM_H diff --git a/src/drivers/distance_sensor/broadcom/afbrs50/Inc/platform/argus_print.h b/src/drivers/distance_sensor/broadcom/afbrs50/Inc/platform/argus_print.h index 0ca49f858c..9a76dbe485 100644 --- a/src/drivers/distance_sensor/broadcom/afbrs50/Inc/platform/argus_print.h +++ b/src/drivers/distance_sensor/broadcom/afbrs50/Inc/platform/argus_print.h @@ -1,11 +1,11 @@ /*************************************************************************//** * @file - * @brief This file is part of the AFBR-S50 API. - * @details This file provides an interface for the optional debug module. + * @brief This file is part of the AFBR-S50 API. + * @details This file provides an interface for the optional debug module. * * @copyright * - * Copyright (c) 2021, Broadcom Inc + * Copyright (c) 2023, Broadcom Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -36,48 +36,55 @@ #ifndef ARGUS_PRINT_H #define ARGUS_PRINT_H +#ifdef __cplusplus +extern "C" { +#endif /*!*************************************************************************** - * @defgroup argus_log Debug: Logging Interface - * @ingroup argus_platform + * @defgroup argus_log Debug: Logging Interface + * @ingroup argus_hal * - * @brief Logging interface for the AFBR-S50 API. + * @brief Logging interface for the AFBR-S50 API. * - * @details This interface provides logging utility functions. - * Defines a printf-like function that is used to print error and - * log messages. + * @details This interface provides logging utility functions. + * Defines a printf-like function that is used to print error and + * log messages. * - * @addtogroup argus_log + * @addtogroup argus_log * @{ *****************************************************************************/ #include "api/argus_def.h" /*!*************************************************************************** - * @brief A printf-like function to print formatted data to an debugging interface. + * @brief A printf-like function to print formatted data to an debugging interface. * * @details Writes the C string pointed by fmt_t to an output. If format - * includes format specifiers (subsequences beginning with %), the - * additional arguments following fmt_t are formatted and inserted in - * the resulting string replacing their respective specifiers. + * includes format specifiers (subsequences beginning with %), the + * additional arguments following fmt_t are formatted and inserted in + * the resulting string replacing their respective specifiers. * - * To enable the print functionality, an implementation of the function - * must be provided that maps the output to an interface like UART or - * a debugging console, e.g. by forwarding to standard printf() method. + * To enable the print functionality, an implementation of the function + * must be provided that maps the output to an interface like UART or + * a debugging console, e.g. by forwarding to standard printf() method. * - * @note The implementation of this function is optional for the correct - * execution of the API. If not implemented, a weak implementation - * within the API will be used that does nothing. This will improve - * the performance but no error messages are logged. + * @note The implementation of this function is optional for the correct + * execution of the API. If not implemented, a weak implementation + * within the API will be used that does nothing. This will improve + * the performance but no error messages are logged. * - * @note The naming is different from the standard printf() on purpose to - * prevent builtin compiler optimizations. + * @note The naming is different from the standard printf() on purpose to + * prevent builtin compiler optimizations. * - * @param fmt_s The usual print() format string. - * @param ... The usual print() parameters. * - * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). + * @param fmt_s The usual print() format string. + * @param ... The usual print() parameters. + * + * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). *****************************************************************************/ status_t print(const char *fmt_s, ...); /*! @} */ +#ifdef __cplusplus +} // extern "C" +#endif #endif /* ARGUS_PRINT_H */ diff --git a/src/drivers/distance_sensor/broadcom/afbrs50/Inc/platform/argus_s2pi.h b/src/drivers/distance_sensor/broadcom/afbrs50/Inc/platform/argus_s2pi.h index 4b8f5331b5..8d912afc22 100644 --- a/src/drivers/distance_sensor/broadcom/afbrs50/Inc/platform/argus_s2pi.h +++ b/src/drivers/distance_sensor/broadcom/afbrs50/Inc/platform/argus_s2pi.h @@ -1,11 +1,11 @@ /*************************************************************************//** * @file - * @brief This file is part of the AFBR-S50 API. - * @details This file provides an interface for the required S2PI module. + * @brief This file is part of the AFBR-S50 API. + * @details This file provides an interface for the required S2PI module. * * @copyright * - * Copyright (c) 2021, Broadcom Inc + * Copyright (c) 2023, Broadcom Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -36,95 +36,94 @@ #ifndef ARGUS_S2PI_H #define ARGUS_S2PI_H - #ifdef __cplusplus extern "C" { #endif /*!*************************************************************************** - * @defgroup argus_s2pi S2PI: Serial Peripheral Interface - * @ingroup argus_platform + * @defgroup argus_s2pi S2PI: Serial Peripheral Interface + * @ingroup argus_hal * - * @brief S2PI: SPI incl. GPIO Hardware Layer Module + * @brief S2PI: SPI incl. GPIO Hardware Layer Module * - * @details The S2PI module consists of a standard SPI interface plus a - * single GPIO interrupt line. Furthermore, the SPI pins are - * accessible via GPIO control to allow a software emulation of - * additional protocols using the same pins. + * @details The S2PI module consists of a standard SPI interface plus a + * single GPIO interrupt line. Furthermore, the SPI pins are + * accessible via GPIO control to allow a software emulation of + * additional protocols using the same pins. * - * **SPI interface:** + * **SPI interface:** * - * The SPI interface is based around a single functionality: + * The SPI interface is based around a single functionality: * - * #S2PI_TransferFrame. This function transfers a specified number - * of bytes via the interfaces MOSI line and simultaneously reads - * the incoming data on the MOSI line. The read can also be skipped. - * The transfer happen asynchronously, e.g. via a DMA request. After - * finishing the transfer, the provided callback is invoked with - * the status of the transfer and the provided abstract parameter. - * Furthermore, the functions receives a slave parameter that can - * be used to connect multiple slaves, each with its individual - * chip select line. + * #S2PI_TransferFrame. This function transfers a specified number + * of bytes via the interfaces MOSI line and simultaneously reads + * the incoming data on the MOSI line. The read can also be skipped. + * The transfer happen asynchronously, e.g. via a DMA request. After + * finishing the transfer, the provided callback is invoked with + * the status of the transfer and the provided abstract parameter. + * Furthermore, the functions receives a slave parameter that can + * be used to connect multiple slaves, each with its individual + * chip select line. * - * The interface also provides functionality to change the SPI - * baud rate. An additional abort method is used to cancel the - * ongoing transfer. + * The interface also provides functionality to change the SPI + * baud rate. An additional abort method is used to cancel the + * ongoing transfer. * - * **GPIO interface:** + * **GPIO interface:** * - * The GPIO part of the S2PI interface has two distinct concerns: + * The GPIO part of the S2PI interface has two distinct concerns: * - * First, the GPIO interface handles the measurement finished interrupt - * from the device. When the device invokes the interrupt, it pulls - * the interrupt line to low. Thus the interrupt must trigger when - * a transition from high to low occurs on the interrupt line. + * First, the GPIO interface handles the measurement finished interrupt + * from the device. When the device invokes the interrupt, it pulls + * the interrupt line to low. Thus the interrupt must trigger when + * a transition from high to low occurs on the interrupt line. * - * The module simply invokes a callback when this interrupt occurs. - * The #S2PI_SetIrqCallback method is used to install the callback - * for a specified slave. Each slave will have its own interrupt - * line. An additional callback parameter can be set that would be - * passed to the callback function. + * The module simply invokes a callback when this interrupt occurs. + * The #S2PI_SetIrqCallback method is used to install the callback + * for a specified slave. Each slave will have its own interrupt + * line. An additional callback parameter can be set that would be + * passed to the callback function. * - * In addition to the interrupt, all SPI pins need to be accessible - * as GPIO pins through this interface. This is required to read - * the EEPROM memory on the device hat is connected to the SPI - * pins but requires a different protocol that is not compatible - * to any standard SPI interface. Therefore, the interface provides - * the possibility to switch to GPIO control mode that allows to - * emulate the EEPROM protocol via software bit banging. + * In addition to the interrupt, all SPI pins need to be accessible + * as GPIO pins through this interface. This is required to read + * the EEPROM memory on the device hat is connected to the SPI + * pins but requires a different protocol that is not compatible + * to any standard SPI interface. Therefore, the interface provides + * the possibility to switch to GPIO control mode that allows to + * emulate the EEPROM protocol via software bit banging. * - * Two methods are provided to switch forth and back between SPI - * and GPIO control. In GPIO mode, several functions are used to - * read and write the individual GPIO pins. + * Two methods are provided to switch forth and back between SPI + * and GPIO control. In GPIO mode, several functions are used to + * read and write the individual GPIO pins. * - * Note that the GPIO mode is only required to readout the EEPROM - * upon initialization of the device, i.e. during execution of the - * #Argus_Init or #Argus_Reinit methods. The GPIO mode is not used - * during measurements. + * Note that the GPIO mode is only required to readout the EEPROM + * upon initialization of the device, i.e. during execution of the + * #Argus_Init or #Argus_Reinit methods. The GPIO mode is not used + * during measurements. * * - * @addtogroup argus_s2pi + * @addtogroup argus_s2pi * @{ *****************************************************************************/ #include "api/argus_def.h" /*!*************************************************************************** - * @brief S2PI layer callback function type for the SPI transfer completed event. + * @brief S2PI layer callback function type for the SPI transfer completed event. * - * @param status The \link #status_t status\endlink of the completed + * @param status The \link #status_t status\endlink of the completed * transfer (#STATUS_OK on success). * - * @param param The provided (optional, can be null) callback parameter. + * @param param The provided (optional, can be null) callback parameter. * - * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). + * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). *****************************************************************************/ typedef status_t (*s2pi_callback_t)(status_t status, void *param); /*!*************************************************************************** - * @brief S2PI layer callback function type for the GPIO interrupt event. + * @brief S2PI layer callback function type for the GPIO interrupt event. * - * @param param The provided (optional, can be null) callback parameter. + * @param param The provided (optional, can be null) callback parameter. *****************************************************************************/ typedef void (*s2pi_irq_callback_t)(void *param); @@ -132,8 +131,8 @@ typedef void (*s2pi_irq_callback_t)(void *param); * can be used to identify the slave within the SPI module. */ typedef int32_t s2pi_slave_t; -/*! The enumeration of S2PI pins. */ -typedef enum { +/*! The enumeration of S2PI pins. */ +typedef enum s2pi_pin_t { /*! The SPI clock pin. */ S2PI_CLK, @@ -153,64 +152,141 @@ typedef enum { /*!*************************************************************************** - * @brief Returns the status of the SPI module. + * @brief Returns the status of the SPI module. * - * @return Returns the \link #status_t status\endlink: - * - #STATUS_IDLE: No SPI transfer or GPIO access is ongoing. - * - #STATUS_BUSY: An SPI transfer is in progress. - * - #STATUS_S2PI_GPIO_MODE: The module is in GPIO mode. + * @param slave The specified S2PI slave. Note that the slave information is + * only required if multiple SPI instances are used in order to + * map to the correct SPI instance. + * + * @return Returns the \link #status_t status\endlink: + * - #STATUS_IDLE: No SPI transfer or GPIO access is ongoing. + * - #STATUS_BUSY: An SPI transfer is in progress. + * - #STATUS_S2PI_GPIO_MODE: The module is in GPIO mode. *****************************************************************************/ -status_t S2PI_GetStatus(void); +status_t S2PI_GetStatus(s2pi_slave_t slave); /*!*************************************************************************** - * @brief Transfers a single SPI frame asynchronously. + * @brief Tries to grab the SPI interface mutex for the next transfer. + * + * @details This mutex prevents new asynchronous SPI requests to interfere + * with transfers already in progress for this interface. + * + * Note that this is only required if multiple device are connected to + * a single SPI interface. If only operating a single device per SPI, + * the function can simply always return #STATUS_OK. + * + * There must be a dedicated mutex object per SPI interface if + * multiple SPI interfaces are used. + * + * The mutex will be released in the #S2PI_ReleaseMutex function. + * See #S2PI_ReleaseMutex for additional information. + * + * Here is a simple example implementation for the multiple devices on + * a single SPI interface case. Note that the SpiMutexBlocked must be + * defined per SPI interface if multiple SPI interfaces are used. + * + * @code + * static volatile bool SpiMutexBlocked = false; + * status_t S2PI_TryGetMutex(s2pi_slave_t slave) + * { + * (void) slave; // not used in this implementation as all + * // SPI slaves are on the same SPI interface + * + * status_t status = STATUS_BUSY; + * IRQ_LOCK(); + * if (!SpiMutexBlocked) + * { + * SpiMutexBlocked = true; + * status = STATUS_OK; + * } + * IRQ_UNLOCK(); + * return status; + * } + * void S2PI_ReleaseMutex(s2pi_slave_t slave) + * { + * (void) slave; // not used in this implementation + * SpiMutexBlocked = false; + * } + * @endcode + * + * @param slave The specified S2PI slave. Note that the slave information is + * only required if multiple SPI instances are used in order to + * map to the correct SPI instance. + * + * @return Returns the \link #status_t status\endlink: + * - #STATUS_OK: the SPI interface was successfully reserved for the caller + * - #STATUS_BUSY: another transfer is ongoing, the caller must not access the bus + *****************************************************************************/ +status_t S2PI_TryGetMutex(s2pi_slave_t slave); + +/*!*************************************************************************** + * @brief Releases the SPI interface mutex. + * + * @details Once the mutex is captured, only a single thread (the one that + * captured it) will call this release function, so there is no + * need for any test or thread safe barriers. Also there is no + * side effect of calling this function when the Mutex is not + * taken so this function can be really simple and doesn't need + * to return anything. + * + * See #S2PI_TryGetMutex on more information and an example + * implementation for the single SPI interface case. + * + * @param slave The specified S2PI slave. Note that the slave information is + * only required if multiple SPI instances are used in order to + * map to the correct SPI instance. + *****************************************************************************/ +void S2PI_ReleaseMutex(s2pi_slave_t slave); + +/*!*************************************************************************** + * @brief Transfers a single SPI frame asynchronously. * * @details Transfers a single SPI frame in asynchronous manner. The Tx data - * buffer is written to the device via the MOSI line. - * Optionally, the data on the MISO line is written to the provided - * Rx data buffer. If null, the read data is dismissed. Note that - * Rx and Tx buffer can be identical. I.e. the same buffer is used - * for writing and reading data. First, a byte is transmitted and - * the received byte overwrites the previously send value. + * buffer is written to the device via the MOSI line. + * Optionally, the data on the MISO line is written to the provided + * Rx data buffer. If null, the read data is dismissed. Note that + * Rx and Tx buffer can be identical. I.e. the same buffer is used + * for writing and reading data. First, a byte is transmitted and + * the received byte overwrites the previously send value. * - * The transfer of a single frame requires to not toggle the chip - * select line to high in between the data frame. The maximum - * number of bytes transfered in a single SPI transfer is given by - * the data value register of the device, which is 396 data bytes - * plus a single address byte: 397 bytes. + * The transfer of a single frame requires to not toggle the chip + * select line to high in between the data frame. The maximum + * number of bytes transferred in a single SPI transfer is given by + * the data value register of the device, which is 396 data bytes + * plus a single address byte: 397 bytes. * - * An optional callback is invoked when the asynchronous transfer - * is finished. If the \p callback parameter is a null pointer, - * no callback is provided. Note that the provided buffer must not - * change while the transfer is ongoing. + * An optional callback is invoked when the asynchronous transfer + * is finished. If the \p callback parameter is a null pointer, + * no callback is provided. Note that the provided buffer must not + * change while the transfer is ongoing. * - * Use the slave parameter to determine the corresponding slave via the - * given chip select line. + * Use the slave parameter to determine the corresponding slave via the + * given chip select line. * - * Usually, two distinct interrupts are required to handle the RX and - * TX ready events. The callback must be invoked from whichever - * interrupt comes after the SPI transfer has been finished. Note - * that new SPI transfers are invoked from within the callback function - * (i.e. from within the interrupt service routine of same priority). + * Usually, two distinct interrupts are required to handle the RX and + * TX ready events. The callback must be invoked from whichever + * interrupt comes after the SPI transfer has been finished. Note + * that new SPI transfers are invoked from within the callback function + * (i.e. from within the interrupt service routine of same priority). * - * @param slave The specified S2PI slave. - * @param txData The 8-bit values to write to the SPI bus MOSI line. - * @param rxData The 8-bit values received from the SPI bus MISO line + * @param slave The specified S2PI slave. + * @param txData The 8-bit values to write to the SPI bus MOSI line. + * @param rxData The 8-bit values received from the SPI bus MISO line * (pass a null pointer if the data don't need to be read). - * @param frameSize The number of 8-bit values to be sent/received. - * @param callback A callback function to be invoked when the transfer is - * finished. Pass a null pointer if no callback is required. - * @param callbackData A pointer to a state that will be passed to the + * @param frameSize The number of 8-bit values to be sent/received. + * @param callback A callback function to be invoked when the transfer is + * finished. Pass a null pointer if no callback is required. + * @param callbackData A pointer to a state that will be passed to the * callback. Pass a null pointer if not used. * - * @return Returns the \link #status_t status\endlink: - * - #STATUS_OK: Successfully invoked the transfer. - * - #ERROR_INVALID_ARGUMENT: An invalid parameter has been passed. - * - #ERROR_S2PI_INVALID_SLAVE: A wrong slave identifier is provided. - * - #STATUS_BUSY: An SPI transfer is already in progress. The - * transfer was not started. - * - #STATUS_S2PI_GPIO_MODE: The module is in GPIO mode. The transfer - * was not started. + * @return Returns the \link #status_t status\endlink: + * - #STATUS_OK: Successfully invoked the transfer. + * - #ERROR_INVALID_ARGUMENT: An invalid parameter has been passed. + * - #ERROR_S2PI_INVALID_SLAVE: A wrong slave identifier is provided. + * - #STATUS_BUSY: An SPI transfer is already in progress. The + * transfer was not started. + * - #STATUS_S2PI_GPIO_MODE: The module is in GPIO mode. The transfer + * was not started. *****************************************************************************/ status_t S2PI_TransferFrame(s2pi_slave_t slave, uint8_t const *txData, @@ -220,136 +296,158 @@ status_t S2PI_TransferFrame(s2pi_slave_t slave, void *callbackData); /*!*************************************************************************** - * @brief Terminates a currently ongoing asynchronous SPI transfer. + * @brief Terminates a currently ongoing asynchronous SPI transfer. * - * @details When a callback is set for the current ongoing activity, it is - * invoked with the #ERROR_ABORTED error byte. + * @details When a callback is set for the current ongoing activity, it is + * invoked with the #ERROR_ABORTED error byte. * - * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). + * @param slave The specified S2PI slave. Note that the slave information is + * only required if multiple SPI instances are used in order to + * map to the correct SPI instance. + * + * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). *****************************************************************************/ -status_t S2PI_Abort(void); +status_t S2PI_Abort(s2pi_slave_t slave); /*!*************************************************************************** - * @brief Set a callback for the GPIO IRQ for a specified S2PI slave. + * @brief Set a callback for the GPIO IRQ for a specified S2PI slave. * - * @param slave The specified S2PI slave. - * @param callback A callback function to be invoked when the specified - * S2PI slave IRQ occurs. Pass a null pointer to disable - * the callback. - * @param callbackData A pointer to a state that will be passed to the + * @param slave The specified S2PI slave. + * @param callback A callback function to be invoked when the specified + * S2PI slave IRQ occurs. Pass a null pointer to disable + * the callback. + * @param callbackData A pointer to a state that will be passed to the * callback. Pass a null pointer if not used. * - * @return Returns the \link #status_t status\endlink: - * - #STATUS_OK: Successfully installation of the callback. - * - #ERROR_S2PI_INVALID_SLAVE: A wrong slave identifier is provided. + * @return Returns the \link #status_t status\endlink: + * - #STATUS_OK: Successfully installation of the callback. + * - #ERROR_S2PI_INVALID_SLAVE: A wrong slave identifier is provided. *****************************************************************************/ status_t S2PI_SetIrqCallback(s2pi_slave_t slave, s2pi_irq_callback_t callback, void *callbackData); /*!*************************************************************************** - * @brief Reads the current status of the IRQ pin. + * @brief Reads the current interrupt pending status of the IRQ pin. * * @details In order to keep a low priority for GPIO IRQs, the state of the - * IRQ pin must be read in order to reliable check for chip timeouts. + * IRQ pin must be read in order to reliable check for chip timeouts. * - * The execution of the interrupt service routine for the data-ready - * interrupt from the corresponding GPIO pin might be delayed due to - * priority issues. The delayed execution might disable the timeout - * for the eye-safety checker too late causing false error messages. - * In order to overcome the issue, the state of the IRQ GPIO input - * pin is read before raising a timeout error in order to check if - * the device has already finished but the IRQ is still pending to be - * executed! + * The execution of the interrupt service routine for the data-ready + * interrupt from the corresponding GPIO pin might be delayed due to + * priority issues. The delayed execution might disable the timeout + * for the eye-safety checker too late causing false error messages. + * In order to overcome the issue, the interrupt state of the IRQ + * GPIO input pin is read before raising a timeout error in order to + * check if the device has already finished and the IRQ is still + * pending to be executed! + * + * Note: an easy implementation is to simply return the state of the + * IRQ line, i.e. 0 if there is a low input state and 1 if there is + * a high input state on the IRQ input pin. However, this + * implementation is not fully reliable since the GPIO interrupt + * (triggered on the falling edge) might be missed and the callback + * is never invoked while the IRQ line is correctly asserted to low + * state by the device. In that case, the API is waiting forever + * until the callback is invoked which might never happen. Therefore, + * it is better if the implementation checks the state of the IRQ + * pending status register or even combines both variations. - * @param slave The specified S2PI slave. - * @return Returns 1U if the IRQ pin is high (IRQ not pending) and 0U if the - * devices pulls the pin to low state (IRQ pending). + * @param slave The specified S2PI slave. + * + * @return Returns 1U if the IRQ is NOT pending (pin is in high state) and + * 0U if the IRQ is pending (pin is pulled to low state by the device). *****************************************************************************/ uint32_t S2PI_ReadIrqPin(s2pi_slave_t slave); /*!*************************************************************************** - * @brief Cycles the chip select line. + * @brief Cycles the chip select line. * * @details In order to cancel the integration on the ASIC, a fast toggling - * of the chip select pin of the corresponding SPI slave is required. - * Therefore, this function toggles the CS from high to low and back. - * The SPI instance for the specified S2PI slave must be idle, - * otherwise the status #STATUS_BUSY is returned. + * of the chip select pin of the corresponding SPI slave is required. + * Therefore, this function toggles the CS from high to low and back. + * The SPI instance for the specified S2PI slave must be idle, + * otherwise the status #STATUS_BUSY is returned. * - * @param slave The specified S2PI slave. - * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). + * @param slave The specified S2PI slave. + * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). *****************************************************************************/ status_t S2PI_CycleCsPin(s2pi_slave_t slave); - - /*!***************************************************************************** - * @brief Captures the S2PI pins for GPIO usage. + * @brief Captures the S2PI pins for GPIO usage. * - * @details The SPI is disabled (module status: #STATUS_S2PI_GPIO_MODE) and the - * pins are configured for GPIO operation. The GPIO control must be - * release with the #S2PI_ReleaseGpioControl function in order to - * switch back to ordinary SPI functionality. + * @details The SPI is disabled (module status: #STATUS_S2PI_GPIO_MODE) and the + * pins are configured for GPIO operation. The GPIO control must be + * release with the #S2PI_ReleaseGpioControl function in order to + * switch back to ordinary SPI functionality. * - * @note This function is only called during device initialization! + * @note This function is only called during device initialization! * - * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). + * @param slave The specified S2PI slave. Note that the slave information is + * only required if multiple SPI instances are used in order to + * map to the correct SPI instance. + * + * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). *****************************************************************************/ -status_t S2PI_CaptureGpioControl(void); +status_t S2PI_CaptureGpioControl(s2pi_slave_t slave); /*!***************************************************************************** - * @brief Releases the S2PI pins from GPIO usage and switches back to SPI mode. + * @brief Releases the S2PI pins from GPIO usage and switches back to SPI mode. * - * @details The GPIO pins are configured for SPI operation and the GPIO mode is - * left. Must be called if the pins are captured for GPIO operation via - * the #S2PI_CaptureGpioControl function. + * @details The GPIO pins are configured for SPI operation and the GPIO mode is + * left. Must be called if the pins are captured for GPIO operation via + * the #S2PI_CaptureGpioControl function. * - * @note This function is only called during device initialization! + * @note This function is only called during device initialization! * - * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). + * @param slave The specified S2PI slave. Note that the slave information is + * only required if multiple SPI instances are used in order to + * map to the correct SPI instance. + * + * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). *****************************************************************************/ -status_t S2PI_ReleaseGpioControl(void); +status_t S2PI_ReleaseGpioControl(s2pi_slave_t slave); /*!***************************************************************************** - * @brief Writes the output for a specified SPI pin in GPIO mode. + * @brief Writes the output for a specified SPI pin in GPIO mode. * * @details This function writes the value of an SPI pin if the SPI pins are - * captured for GPIO operation via the #S2PI_CaptureGpioControl previously. + * captured for GPIO operation via the #S2PI_CaptureGpioControl previously. * - * @note Since some GPIO peripherals switch the GPIO pins very fast a delay - * must be added after each GBIO access (i.e. right before returning - * from the #S2PI_WriteGpioPin method) in order to decrease the baud - * rate of the software EEPROM protocol. Increase the delay if timing - * issues occur while reading the EERPOM. For example: - * Delay = 10 µsec => Baud Rate < 100 kHz + * @note Since some GPIO peripherals switch the GPIO pins very fast a delay + * must be added after each GBIO access (i.e. right before returning + * from the #S2PI_WriteGpioPin method) in order to decrease the baud + * rate of the software EEPROM protocol. Increase the delay if timing + * issues occur while reading the EERPOM. For example: + * Delay = 10 µsec => Baud Rate < 100 kHz * - * @note This function is only called during device initialization! + * @note This function is only called during device initialization! * - * @param slave The specified S2PI slave. - * @param pin The specified S2PI pin. - * @param value The GPIO pin state to write (0 = low, 1 = high). - * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). + * @param slave The specified S2PI slave. + * @param pin The specified S2PI pin. + * @param value The GPIO pin state to write (0 = low, 1 = high). + * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). *****************************************************************************/ status_t S2PI_WriteGpioPin(s2pi_slave_t slave, s2pi_pin_t pin, uint32_t value); /*!***************************************************************************** - * @brief Reads the input from a specified SPI pin in GPIO mode. + * @brief Reads the input from a specified SPI pin in GPIO mode. * * @details This function reads the value of an SPI pin if the SPI pins are - * captured for GPIO operation via the #S2PI_CaptureGpioControl previously. + * captured for GPIO operation via the #S2PI_CaptureGpioControl previously. * - * @note This function is only called during device initialization! + * @note This function is only called during device initialization! * - * @param slave The specified S2PI slave. - * @param pin The specified S2PI pin. - * @param value The GPIO pin state to read (0 = low, GND level, 1 = high, VCC level). - * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). + * @param slave The specified S2PI slave. + * @param pin The specified S2PI pin. + * @param value The GPIO pin state to read (0 = low, GND level, 1 = high, VCC level). + * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). *****************************************************************************/ status_t S2PI_ReadGpioPin(s2pi_slave_t slave, s2pi_pin_t pin, uint32_t *value); -#ifdef __cplusplus -} -#endif /*! @} */ +#ifdef __cplusplus +} // extern "C" +#endif #endif // ARGUS_S2PI_H diff --git a/src/drivers/distance_sensor/broadcom/afbrs50/Inc/platform/argus_timer.h b/src/drivers/distance_sensor/broadcom/afbrs50/Inc/platform/argus_timer.h index 80deb42016..b736010682 100644 --- a/src/drivers/distance_sensor/broadcom/afbrs50/Inc/platform/argus_timer.h +++ b/src/drivers/distance_sensor/broadcom/afbrs50/Inc/platform/argus_timer.h @@ -1,11 +1,11 @@ /*************************************************************************//** * @file - * @brief This file is part of the AFBR-S50 API. - * @details This file provides an interface for the required timer modules. + * @brief This file is part of the AFBR-S50 API. + * @details This file provides an interface for the required timer modules. * * @copyright * - * Copyright (c) 2021, Broadcom Inc + * Copyright (c) 2023, Broadcom Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -36,118 +36,117 @@ #ifndef ARGUS_TIMER_H #define ARGUS_TIMER_H - #ifdef __cplusplus extern "C" { #endif /*!*************************************************************************** - * @defgroup argus_timer Timer: Hardware Timer Interface - * @ingroup argus_platform + * @defgroup argus_timer Timer: Hardware Timer Interface + * @ingroup argus_hal * - * @brief Timer implementations for lifetime counting as well as periodic - * callback. + * @brief Timer implementations for lifetime counting as well as periodic + * callback. * - * @details The module provides an interface to the timing utilities that - * are required by the AFBR-S50 time-of-flight sensor API. + * @details The module provides an interface to the timing utilities that + * are required by the AFBR-S50 time-of-flight sensor API. * - * Two essential features have to be provided by the user code: - * 1. Time Measurement Capability: In order to keep track of outgoing - * signals, the API needs to measure elapsed time. In order to - * provide optimum device performance, the granularity should be - * around 10 to 100 microseconds. - * 2. Periodic Callback: The API provides an automatic starting of - * measurement cycles at a fixed frame rate via a periodic - * interrupt timer. If this feature is not used, implementation - * of the periodic interrupts can be skipped. An weak default - * implementation is provide in the API. - * . + * Two essential features have to be provided by the user code: + * 1. Time Measurement Capability: In order to keep track of outgoing + * signals, the API needs to measure elapsed time. In order to + * provide optimum device performance, the granularity should be + * around 10 to 100 microseconds. + * 2. Periodic Callback: The API provides an automatic starting of + * measurement cycles at a fixed frame rate via a periodic + * interrupt timer. If this feature is not used, implementation + * of the periodic interrupts can be skipped. An weak default + * implementation is provide in the API. + * . * - * The time measurement feature is simply implemented by the function - * #Timer_GetCounterValue. Whenever the function is called, the - * provided counter values must be written with the values obtained - * by the current time. + * The time measurement feature is simply implemented by the function + * #Timer_GetCounterValue. Whenever the function is called, the + * provided counter values must be written with the values obtained + * by the current time. * - * The periodic interrupt timer is a simple callback interface. - * After installing the callback function pointer via #Timer_SetCallback, - * the timer can be started by setting interval via #Timer_SetInterval. - * From then, the callback is invoked periodically as the corresponding - * interval may specify. The timer is stopped by setting the interval - * to 0 using the #Timer_SetInterval function. The interval can be - * updated at any time by updating the interval via the #Timer_SetInterval - * function. To any of these functions, an abstract parameter pointer - * must be passed. This parameter is passed back to the callback any - * time it is invoked. + * The periodic interrupt timer is a simple callback interface. + * After installing the callback function pointer via #Timer_SetCallback, + * the timer can be started by setting interval via #Timer_SetInterval. + * From then, the callback is invoked periodically as the corresponding + * interval may specify. The timer is stopped by setting the interval + * to 0 using the #Timer_SetInterval function. The interval can be + * updated at any time by updating the interval via the #Timer_SetInterval + * function. To any of these functions, an abstract parameter pointer + * must be passed. This parameter is passed back to the callback any + * time it is invoked. * - * In order to provide the usage of multiple devices, an mechanism is - * introduced to allow the installation of multiple callback interval - * at the same time. Therefore, the abstract parameter pointer is used - * to identify the corresponding callback interval. For example, there - * are two callbacks for two intervals, t1 and t2, required. The user - * can start two timers by calling the #Timer_SetInterval method twice, - * but with an individual parameter pointer, ptr1 and ptr2, each: - * \code - * Timer_SetInterval(100000, ptr1); // 10 ms callback w/ parameter ptr1 - * Timer_SetInterval(200000, ptr2); // 20 ms callback w/ parameter ptr1 - * \endcode + * In order to provide the usage of multiple devices, an mechanism is + * introduced to allow the installation of multiple callback interval + * at the same time. Therefore, the abstract parameter pointer is used + * to identify the corresponding callback interval. For example, there + * are two callbacks for two intervals, t1 and t2, required. The user + * can start two timers by calling the #Timer_SetInterval method twice, + * but with an individual parameter pointer, ptr1 and ptr2, each: + * \code + * Timer_SetInterval(100000, ptr1); // 10 ms callback w/ parameter ptr1 + * Timer_SetInterval(200000, ptr2); // 20 ms callback w/ parameter ptr1 + * \endcode * - * Note that the implemented timer module must therefore support - * as many different intervals as instances of the AFBR-S50 device are - * used. + * Note that the implemented timer module must therefore support + * as many different intervals as instances of the AFBR-S50 device are + * used. * - * @addtogroup argus_timer + * @addtogroup argus_timer * @{ *****************************************************************************/ -#include "api/argus_def.h" +#include "utility/status.h" /******************************************************************************* * Lifetime Counter Timer Interface ******************************************************************************/ /*!*************************************************************************** - * @brief Obtains the lifetime counter value from the timers. + * @brief Obtains the lifetime counter value from the timers. * * @details The function is required to get the current time relative to any - * point in time, e.g. the startup time. The returned values \p hct and - * \p lct are given in seconds and microseconds respectively. The current - * elapsed time since the reference time is then calculated from: + * point in time, e.g. the startup time. The returned values \p hct and + * \p lct are given in seconds and microseconds respectively. The current + * elapsed time since the reference time is then calculated from: * - * t_now [µsec] = hct * 1000000 µsec + lct * 1 µsec + * t_now [µsec] = hct * 1000000 µsec + lct * 1 µsec * - * Note that the accuracy/granularity of the lifetime counter does - * not need to be 1 µsec. Usually, a granularity of approximately - * 100 µsec is sufficient. However, in case of very high frame rates - * (above 1000 frames per second), it is recommended to implement - * an even lower granularity (somewhere in the 10 µsec regime). + * Note that the accuracy/granularity of the lifetime counter does + * not need to be 1 µsec. Usually, a granularity of approximately + * 100 µsec is sufficient. However, in case of very high frame rates + * (above 1000 frames per second), it is recommended to implement + * an even lower granularity (somewhere in the 10 µsec regime). * - * It must be guaranteed, that each call of the #Timer_GetCounterValue - * function must provide a value that is greater or equal, but never lower, - * than the value returned from the previous call. + * It must be guaranteed, that each call of the #Timer_GetCounterValue + * function must provide a value that is greater or equal, but never lower, + * than the value returned from the previous call. * - * A hardware based implementation of the lifetime counter functionality - * would be to chain two distinct timers such that counter 2 increases - * its value when counter 1 wraps to 0. The easiest way is to setup - * counter 1 to wrap exactly every second. Counter 1 would than count - * the sub-seconds (i.e. µsec) value (\p lct) and counter 2 the seconds - * (\p hct) value. A 16-bit counter is sufficient in case of counter 1 - * while counter 2 must be a 32-bit version. + * A hardware based implementation of the lifetime counter functionality + * would be to chain two distinct timers such that counter 2 increases + * its value when counter 1 wraps to 0. The easiest way is to setup + * counter 1 to wrap exactly every second. Counter 1 would than count + * the sub-seconds (i.e. µsec) value (\p lct) and counter 2 the seconds + * (\p hct) value. A 16-bit counter is sufficient in case of counter 1 + * while counter 2 must be a 32-bit version. * - * In case of a lack of available hardware timers, a software solution - * can be used that requires only a 16-bit timer. In a simple scenario, - * the timer is configured to wrap around every second and increase - * a software counter value in its interrupt service routine (triggered - * with the wrap around event) every time the wrap around occurs. + * In case of a lack of available hardware timers, a software solution + * can be used that requires only a 16-bit timer. In a simple scenario, + * the timer is configured to wrap around every second and increase + * a software counter value in its interrupt service routine (triggered + * with the wrap around event) every time the wrap around occurs. * * - * @note The implementation of this function is mandatory for the correct - * execution of the API. + * @note The implementation of this function is mandatory for the correct + * execution of the API. * - * @param hct A pointer to the high counter value bits representing current - * time in seconds. + * @param hct A pointer to the high counter value bits representing current + * time in seconds. * - * @param lct A pointer to the low counter value bits representing current - * time in microseconds. Range: 0, .., 999999 µsec + * @param lct A pointer to the low counter value bits representing current + * time in microseconds. Range: 0, .., 999999 µsec *****************************************************************************/ void Timer_GetCounterValue(uint32_t *hct, uint32_t *lct); @@ -156,68 +155,70 @@ void Timer_GetCounterValue(uint32_t *hct, uint32_t *lct); ******************************************************************************/ /*!*************************************************************************** - * @brief The callback function type for periodic interrupt timer. + * @brief The callback function type for periodic interrupt timer. * - * @details The function that is invoked every time a specified interval elapses. - * An abstract parameter is passed to the function whenever it is called. + * @details The function that is invoked every time a specified interval elapses. + * An abstract parameter is passed to the function whenever it is called. * - * @param param An abstract parameter to be passed to the callback. This is - * also the identifier of the given interval. + * @param param An abstract parameter to be passed to the callback. This is + * also the identifier of the given interval. *****************************************************************************/ typedef void (*timer_cb_t)(void *param); /*!*************************************************************************** - * @brief Installs an periodic timer callback function. + * @brief Installs an periodic timer callback function. * - * @details Installs an periodic timer callback function that is invoked whenever - * an interval elapses. The callback is the same for any interval, - * however, the single intervals can be identified by the passed - * parameter. - * Passing a zero-pointer removes and disables the callback. + * @details Installs an periodic timer callback function that is invoked whenever + * an interval elapses. The callback is the same for any interval, + * however, the single intervals can be identified by the passed + * parameter. + * Passing a zero-pointer removes and disables the callback. * - * @note The implementation of this function is optional for the correct - * execution of the API. If not implemented, a weak implementation - * within the API will be used that disable the periodic timer callback - * and thus the automatic starting of measurements from the background. + * @note The implementation of this function is optional for the correct + * execution of the API. If not implemented, a weak implementation + * within the API will be used that disable the periodic timer callback + * and thus the automatic starting of measurements from the background. * - * @param f The timer callback function. + * @param f The timer callback function. * - * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). + * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). *****************************************************************************/ status_t Timer_SetCallback(timer_cb_t f); /*!*************************************************************************** - * @brief Sets the timer interval for a specified callback parameter. + * @brief Sets the timer interval for a specified callback parameter. * - * @details Sets the callback interval for the specified parameter and starts - * the timer with a new interval. If there is already an interval with - * the given parameter, the timer is restarted with the given interval. - * If the same time interval as already set is passed, nothing happens. - * Passing a interval of 0 disables the timer. + * @details Sets the callback interval for the specified parameter and starts + * the timer with a new interval. If there is already an interval with + * the given parameter, the timer is restarted with the given interval. + * If the same time interval as already set is passed, nothing happens. + * Passing a interval of 0 disables the timer. * - * Note that a microsecond granularity for the timer interrupt period is - * not required. Usually a microseconds granularity is sufficient. - * The required granularity depends on the targeted frame rate, e.g. in - * case of more than 1 kHz measurement rate, a granularity of less than - * a microsecond is required to achieve the given frame rate. + * When enabling the timer (or resetting by applying another interval), + * the first timer interrupt must happen after the specified interval. * - * @note The implementation of this function is optional for the correct - * execution of the API. If not implemented, a weak implementation - * within the API will be used that disable the periodic timer callback - * and thus the automatic starting of measurements from the background. + * Note that a microsecond granularity for the timer interrupt period is + * not required. Usually a milliseconds granularity is sufficient. + * The required granularity depends on the targeted frame rate, e.g. in + * case of more than 1 kHz measurement rate, a granularity of less than + * a millisecond is required to achieve the given frame rate. * - * @param dt_microseconds The callback interval in microseconds. + * @note The implementation of this function is optional for the correct + * execution of the API. If not implemented, a weak implementation + * within the API will be used that disable the periodic timer callback + * and thus the automatic starting of measurements from the background. * - * @param param An abstract parameter to be passed to the callback. This is - * also the identifier of the given interval. + * @param dt_microseconds The callback interval in microseconds. * - * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). + * @param param An abstract parameter to be passed to the callback. This is + * also the identifier of the given interval. + * + * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). *****************************************************************************/ status_t Timer_SetInterval(uint32_t dt_microseconds, void *param); -#ifdef __cplusplus -} -#endif - /*! @} */ +#ifdef __cplusplus +} // extern "C" +#endif #endif /* ARGUS_TIMER_H */ diff --git a/src/drivers/distance_sensor/broadcom/afbrs50/Inc/utility/fp_def.h b/src/drivers/distance_sensor/broadcom/afbrs50/Inc/utility/fp_def.h index ae7422ada5..8d5406360b 100644 --- a/src/drivers/distance_sensor/broadcom/afbrs50/Inc/utility/fp_def.h +++ b/src/drivers/distance_sensor/broadcom/afbrs50/Inc/utility/fp_def.h @@ -1,11 +1,11 @@ /*************************************************************************//** * @file - * @brief This file is part of the AFBR-S50 API. - * @details Provides definitions and basic macros for fixed point data types. + * @brief This file is part of the AFBR-S50 API. + * @details Provides definitions and basic macros for fixed point data types. * * @copyright * - * Copyright (c) 2021, Broadcom Inc + * Copyright (c) 2023, Broadcom Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -36,23 +36,30 @@ #ifndef FP_DEF_H #define FP_DEF_H +#ifdef __cplusplus +extern "C" { +#endif /*!*************************************************************************** - * @defgroup fixedpoint Fixed Point Math - * @ingroup argusutil - * @brief A basic math library for fixed point number in the Qx.y fomat. - * @details This module contains common fixed point type definitions as - * well as some basic math algorithms. All types are based on - * integer types. The number are defined with the Q number format. + * @defgroup argus_fp Fixed Point Math + * @ingroup argus_util * - * - For a description of the Q number format refer to: - * https://en.wikipedia.org/wiki/Q_(number_format) - * - Another resource for fixed point math in C might be found at - * http://www.eetimes.com/author.asp?section_id=36&doc_id=1287491 - * . - * @warning This definitions are not portable and work only with - * little-endian systems! - * @addtogroup fixedpoint + * @brief A basic math library for fixed point number in the Qx.y fomat. + * + * @details This module contains common fixed point type definitions as + * well as some basic math algorithms. All types are based on + * integer types. The number are defined with the Q number format. + * + * - For a description of the Q number format refer to: + * https://en.wikipedia.org/wiki/Q_(number_format) + * - Another resource for fixed point math in C might be found at + * http://www.eetimes.com/author.asp?section_id=36&doc_id=1287491 + * . + * + * @warning This definitions are not portable and work only with + * little-endian systems! + * + * @addtogroup argus_fp * @{ *****************************************************************************/ @@ -66,11 +73,11 @@ ***** UQ6.2 ******************************************************************************/ /*!*************************************************************************** - * @brief Unsigned fixed point number: UQ6.2 - * @details An unsigned fixed point number format based on the 8-bit unsigned - * integer type with 6 integer and 2 fractional bits. - * - Range: 0 .. 63.75 - * - Granularity: 0.25 + * @brief Unsigned fixed point number: UQ6.2 + * @details An unsigned fixed point number format based on the 8-bit unsigned + * integer type with 6 integer and 2 fractional bits. + * - Range: 0 .. 63.75 + * - Granularity: 0.25 *****************************************************************************/ typedef uint8_t uq6_2_t; @@ -86,11 +93,11 @@ typedef uint8_t uq6_2_t; ***** UQ4.4 ******************************************************************************/ /*!*************************************************************************** - * @brief Unsigned fixed point number: UQ4.4 - * @details An unsigned fixed point number format based on the 8-bit unsigned - * integer type with 4 integer and 4 fractional bits. - * - Range: 0 .. 15.9375 - * - Granularity: 0.0625 + * @brief Unsigned fixed point number: UQ4.4 + * @details An unsigned fixed point number format based on the 8-bit unsigned + * integer type with 4 integer and 4 fractional bits. + * - Range: 0 .. 15.9375 + * - Granularity: 0.0625 *****************************************************************************/ typedef uint8_t uq4_4_t; @@ -106,11 +113,11 @@ typedef uint8_t uq4_4_t; ***** UQ2.6 ******************************************************************************/ /*!*************************************************************************** - * @brief Unsigned fixed point number: UQ2.6 - * @details An unsigned fixed point number format based on the 8-bit unsigned - * integer type with 2 integer and 6 fractional bits. - * - Range: 0 .. 3.984375 - * - Granularity: 0.015625 + * @brief Unsigned fixed point number: UQ2.6 + * @details An unsigned fixed point number format based on the 8-bit unsigned + * integer type with 2 integer and 6 fractional bits. + * - Range: 0 .. 3.984375 + * - Granularity: 0.015625 *****************************************************************************/ typedef uint8_t uq2_6_t; @@ -126,11 +133,11 @@ typedef uint8_t uq2_6_t; ***** UQ1.7 ******************************************************************************/ /*!*************************************************************************** - * @brief Unsigned fixed point number: UQ1.7 - * @details An unsigned fixed point number format based on the 8-bit unsigned - * integer type with 1 integer and 7 fractional bits. - * - Range: 0 .. 1.9921875 - * - Granularity: 0.0078125 + * @brief Unsigned fixed point number: UQ1.7 + * @details An unsigned fixed point number format based on the 8-bit unsigned + * integer type with 1 integer and 7 fractional bits. + * - Range: 0 .. 1.9921875 + * - Granularity: 0.0078125 *****************************************************************************/ typedef uint8_t uq1_7_t; @@ -146,11 +153,11 @@ typedef uint8_t uq1_7_t; ***** UQ0.8 ******************************************************************************/ /*!*************************************************************************** - * @brief Unsigned fixed point number: UQ0.8 - * @details An unsigned fixed point number format based on the 8-bit unsigned - * integer type with 1 integer and 7 fractional bits. - * - Range: 0 .. 0.99609375 - * - Granularity: 0.00390625 + * @brief Unsigned fixed point number: UQ0.8 + * @details An unsigned fixed point number format based on the 8-bit unsigned + * integer type with 1 integer and 7 fractional bits. + * - Range: 0 .. 0.99609375 + * - Granularity: 0.00390625 *****************************************************************************/ typedef uint8_t uq0_8_t; @@ -167,11 +174,11 @@ typedef uint8_t uq0_8_t; ***** Q3.4 ******************************************************************************/ /*!*************************************************************************** - * @brief Signed fixed point number: Q3.4 - * @details A signed fixed point number format based on the 8-bit signed - * integer type with 3 integer and 4 fractional bits. - * - Range: -8 ... 7.9375 - * - Granularity: 0.0625 + * @brief Signed fixed point number: Q3.4 + * @details A signed fixed point number format based on the 8-bit signed + * integer type with 3 integer and 4 fractional bits. + * - Range: -8 ... 7.9375 + * - Granularity: 0.0625 *****************************************************************************/ typedef int8_t q3_4_t; @@ -189,11 +196,11 @@ typedef int8_t q3_4_t; ***** Q1.6 ******************************************************************************/ /*!*************************************************************************** - * @brief Signed fixed point number: Q1.6 - * @details A signed fixed point number format based on the 8-bit signed - * integer type with 1 integer and 6 fractional bits. - * - Range: -2 ... 1.984375 - * - Granularity: 0.015625 + * @brief Signed fixed point number: Q1.6 + * @details A signed fixed point number format based on the 8-bit signed + * integer type with 1 integer and 6 fractional bits. + * - Range: -2 ... 1.984375 + * - Granularity: 0.015625 *****************************************************************************/ typedef int8_t q1_6_t; @@ -215,11 +222,11 @@ typedef int8_t q1_6_t; ***** UQ12.4 ******************************************************************************/ /*!*************************************************************************** - * @brief Unsigned fixed point number: UQ12.4 - * @details An unsigned fixed point number format based on the 16-bit unsigned - * integer type with 12 integer and 4 fractional bits. - * - Range: 0 ... 4095.9375 - * - Granularity: 0.0625 + * @brief Unsigned fixed point number: UQ12.4 + * @details An unsigned fixed point number format based on the 16-bit unsigned + * integer type with 12 integer and 4 fractional bits. + * - Range: 0 ... 4095.9375 + * - Granularity: 0.0625 *****************************************************************************/ typedef uint16_t uq12_4_t; @@ -235,11 +242,11 @@ typedef uint16_t uq12_4_t; ***** UQ10.6 ******************************************************************************/ /*!*************************************************************************** - * @brief Unsigned fixed point number: UQ10.6 - * @details An unsigned fixed point number format based on the 16-bit unsigned - * integer type with 10 integer and 6 fractional bits. - * - Range: 0 ... 1023.984375 - * - Granularity: 0.015625 + * @brief Unsigned fixed point number: UQ10.6 + * @details An unsigned fixed point number format based on the 16-bit unsigned + * integer type with 10 integer and 6 fractional bits. + * - Range: 0 ... 1023.984375 + * - Granularity: 0.015625 *****************************************************************************/ typedef uint16_t uq10_6_t; @@ -255,11 +262,11 @@ typedef uint16_t uq10_6_t; ***** UQ1.15 ******************************************************************************/ /*!*************************************************************************** - * @brief Unsigned fixed point number: UQ1.15 - * @details An unsigned fixed point number format based on the 16-bit unsigned - * integer type with 1 integer and 15 fractional bits. - * - Range: 0 .. 1.999969 - * - Granularity: 0.000031 + * @brief Unsigned fixed point number: UQ1.15 + * @details An unsigned fixed point number format based on the 16-bit unsigned + * integer type with 1 integer and 15 fractional bits. + * - Range: 0 .. 1.999969 + * - Granularity: 0.000031 *****************************************************************************/ typedef uint16_t uq1_15_t; @@ -275,11 +282,11 @@ typedef uint16_t uq1_15_t; ***** UQ0.16 ******************************************************************************/ /*!*************************************************************************** - * @brief Unsigned fixed point number: UQ0.16 - * @details An unsigned fixed point number format based on the 16-bit unsigned - * integer type with 0 integer and 16 fractional bits. - * - Range: 0 .. 0.9999847412109375 - * - Granularity: 1.52587890625e-5 + * @brief Unsigned fixed point number: UQ0.16 + * @details An unsigned fixed point number format based on the 16-bit unsigned + * integer type with 0 integer and 16 fractional bits. + * - Range: 0 .. 0.9999847412109375 + * - Granularity: 1.52587890625e-5 *****************************************************************************/ typedef uint16_t uq0_16_t; @@ -296,11 +303,11 @@ typedef uint16_t uq0_16_t; ***** Q11.4 ******************************************************************************/ /*!*************************************************************************** - * @brief Signed fixed point number: Q11.4 - * @details A signed fixed point number format based on the 16-bit signed - * integer type with 11 integer and 4 fractional bits. - * - Range: -2048 ... 2047.9375 - * - Granularity: 0.0625 + * @brief Signed fixed point number: Q11.4 + * @details A signed fixed point number format based on the 16-bit signed + * integer type with 11 integer and 4 fractional bits. + * - Range: -2048 ... 2047.9375 + * - Granularity: 0.0625 *****************************************************************************/ typedef int16_t q11_4_t; @@ -319,11 +326,11 @@ typedef int16_t q11_4_t; ***** Q7.8 ******************************************************************************/ /*!*************************************************************************** - * @brief Signed fixed point number: Q7.8 - * @details A signed fixed point number format based on the 16-bit signed - * integer type with 7 integer and 8 fractional bits. - * - Range: -128 .. 127.99609375 - * - Granularity: 0.00390625 + * @brief Signed fixed point number: Q7.8 + * @details A signed fixed point number format based on the 16-bit signed + * integer type with 7 integer and 8 fractional bits. + * - Range: -128 .. 127.99609375 + * - Granularity: 0.00390625 *****************************************************************************/ typedef int16_t q7_8_t; @@ -342,11 +349,11 @@ typedef int16_t q7_8_t; ***** Q3.12 ******************************************************************************/ /*!*************************************************************************** - * @brief Signed fixed point number: Q3.12 - * @details A signed fixed point number format based on the 16-bit integer - * type with 3 integer and 12 fractional bits. - * - Range: -8 .. 7.99975586 - * - Granularity: 0.00024414 + * @brief Signed fixed point number: Q3.12 + * @details A signed fixed point number format based on the 16-bit integer + * type with 3 integer and 12 fractional bits. + * - Range: -8 .. 7.99975586 + * - Granularity: 0.00024414 *****************************************************************************/ typedef int16_t q3_12_t; @@ -365,11 +372,11 @@ typedef int16_t q3_12_t; ***** Q0.15 ******************************************************************************/ /*!*************************************************************************** - * @brief Signed fixed point number: Q0.15 - * @details A signed fixed point number format based on the 16-bit integer - * type with 0 integer and 15 fractional bits. - * - Range: -1 .. 0.999969482 - * - Granularity: 0.000030518 + * @brief Signed fixed point number: Q0.15 + * @details A signed fixed point number format based on the 16-bit integer + * type with 0 integer and 15 fractional bits. + * - Range: -1 .. 0.999969482 + * - Granularity: 0.000030518 *****************************************************************************/ typedef int16_t q0_15_t; @@ -389,11 +396,11 @@ typedef int16_t q0_15_t; ***** UQ28.4 ******************************************************************************/ /*!*************************************************************************** - * @brief Unsigned fixed point number: UQ28.4 - * @details An unsigned fixed point number format based on the 32-bit unsigned - * integer type with 28 integer and 4 fractional bits. - * - Range: 0 ... 268435455.9375 - * - Granularity: 0.0625 + * @brief Unsigned fixed point number: UQ28.4 + * @details An unsigned fixed point number format based on the 32-bit unsigned + * integer type with 28 integer and 4 fractional bits. + * - Range: 0 ... 268435455.9375 + * - Granularity: 0.0625 *****************************************************************************/ typedef uint32_t uq28_4_t; @@ -409,11 +416,11 @@ typedef uint32_t uq28_4_t; ***** UQ16.16 ******************************************************************************/ /*!*************************************************************************** - * @brief Unsigned fixed point number: UQ16.16 - * @details An unsigned fixed point number format based on the 32-bit unsigned - * integer type with 16 integer and 16 fractional bits. - * - Range: 0 ... 65535.999984741 - * - Granularity: 0.000015259 + * @brief Unsigned fixed point number: UQ16.16 + * @details An unsigned fixed point number format based on the 32-bit unsigned + * integer type with 16 integer and 16 fractional bits. + * - Range: 0 ... 65535.999984741 + * - Granularity: 0.000015259 *****************************************************************************/ typedef uint32_t uq16_16_t; @@ -432,11 +439,11 @@ typedef uint32_t uq16_16_t; ***** UQ10.22 ******************************************************************************/ /*!*************************************************************************** - * @brief Unsigned fixed point number: UQ10.22 - * @details An unsigned fixed point number format based on the 32-bit unsigned - * integer type with 10 integer and 22 fractional bits. - * - Range: 0 ... 1023.99999976158 - * - Granularity: 2.38418579101562E-07 + * @brief Unsigned fixed point number: UQ10.22 + * @details An unsigned fixed point number format based on the 32-bit unsigned + * integer type with 10 integer and 22 fractional bits. + * - Range: 0 ... 1023.99999976158 + * - Granularity: 2.38418579101562E-07 *****************************************************************************/ typedef uint32_t uq10_22_t; @@ -456,11 +463,11 @@ typedef uint32_t uq10_22_t; ***** Q27.4 ******************************************************************************/ /*!*************************************************************************** - * @brief Signed fixed point number: Q27.4 - * @details A signed fixed point number format based on the 32-bit signed - * integer type with 27 integer and 4 fractional bits. - * - Range: -134217728 ... 134217727.9375 - * - Granularity: 0.0625 + * @brief Signed fixed point number: Q27.4 + * @details A signed fixed point number format based on the 32-bit signed + * integer type with 27 integer and 4 fractional bits. + * - Range: -134217728 ... 134217727.9375 + * - Granularity: 0.0625 *****************************************************************************/ typedef int32_t q27_4_t; @@ -475,15 +482,35 @@ typedef int32_t q27_4_t; +/******************************************************************************* + ***** Q16.15 + ******************************************************************************/ +/*!*************************************************************************** + * @brief Signed fixed point number: Q16.15 + * @details A signed fixed point number format based on the 32-bit integer + * type with 16 integer and 15 fractional bits. + * - Range: -65536 .. 65536.999969482 + * - Granularity: 0.000030518 + *****************************************************************************/ +typedef int32_t q16_15_t; + +/*! Minimum value of Q16.15 number format. */ +#define Q16_15_MIN ((q16_15_t)INT32_MIN) + +/*! Maximum value of Q16.15 number format. */ +#define Q16_15_MAX ((q16_15_t)INT32_MAX) + + + /******************************************************************************* ***** Q15.16 ******************************************************************************/ /*!*************************************************************************** - * @brief Signed fixed point number: Q15.16 - * @details A signed fixed point number format based on the 32-bit integer - * type with 15 integer and 16 fractional bits. - * - Range: -32768 .. 32767.99998 - * - Granularity: 1.52588E-05 + * @brief Signed fixed point number: Q15.16 + * @details A signed fixed point number format based on the 32-bit integer + * type with 15 integer and 16 fractional bits. + * - Range: -32768 .. 32767.99998 + * - Granularity: 1.52588E-05 *****************************************************************************/ typedef int32_t q15_16_t; @@ -502,11 +529,11 @@ typedef int32_t q15_16_t; ***** Q9.22 ******************************************************************************/ /*!*************************************************************************** - * @brief Signed fixed point number: Q9.22 - * @details A signed fixed point number format based on the 32-bit integer - * type with 9 integer and 22 fractional bits. - * - Range: -512 ... 511.9999998 - * - Granularity: 2.38418579101562E-07 + * @brief Signed fixed point number: Q9.22 + * @details A signed fixed point number format based on the 32-bit integer + * type with 9 integer and 22 fractional bits. + * - Range: -512 ... 511.9999998 + * - Granularity: 2.38418579101562E-07 *****************************************************************************/ typedef int32_t q9_22_t; @@ -522,4 +549,7 @@ typedef int32_t q9_22_t; /*! @} */ +#ifdef __cplusplus +} // extern "C" +#endif #endif /* FP_DEF_H */ diff --git a/src/drivers/distance_sensor/broadcom/afbrs50/Inc/utility/fp_div.h b/src/drivers/distance_sensor/broadcom/afbrs50/Inc/utility/fp_div.h new file mode 100644 index 0000000000..60b75a164a --- /dev/null +++ b/src/drivers/distance_sensor/broadcom/afbrs50/Inc/utility/fp_div.h @@ -0,0 +1,173 @@ +/*************************************************************************//** + * @file + * @brief This file is part of the AFBR-S50 API. + * @details Provides definitions and basic macros for fixed point data types. + * + * @copyright + * + * Copyright (c) 2023, Broadcom Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef FP_DIV_H +#define FP_DIV_H +#ifdef __cplusplus +extern "C" { +#endif + +/*!*************************************************************************** + * @addtogroup argus_fp + * @{ + *****************************************************************************/ + +#include "fp_def.h" +#include "int_math.h" + +/*!*************************************************************************** + * Set to use hardware division (Cortex-M3/4) over software division (Cortex-M0/1). + *****************************************************************************/ +#ifndef USE_HW_DIV +#define USE_HW_DIV 0 +#endif + +/*!*************************************************************************** + * @brief 32-bit implementation of an Q15.16 division. + * + * @details Algorithm to evaluate a/b, where b is in Q15.16 format, on a 32-bit + * architecture with maximum precision. + * The result is correctly rounded and given as the input format. + * Division by 0 yields max. values determined by signa of numerator. + * Too high/low results are truncated to max/min values. + * + * Depending on the architecture, the division is implemented with a 64-bit + * division and shifting (Cortex-M3/4) or as a fast software algorithm + * (Cortex-M0/1) wich runs fast on processors without hardware division. + * + * @see https://code.google.com/archive/p/libfixmath + * + * @param a Numerator in any Qx.y format + * @param b Denominator in Q15.16 format + * @return Result = a/b in the same Qx.y format as the input parameter a. + *****************************************************************************/ +inline int32_t fp_div16(int32_t a, q15_16_t b) +{ + //assert(b); + if (b == 0) { return a < 0 ? INT32_MIN : INT32_MAX; } + +#if USE_HW_DIV + // Tested on Cortex-M4, it takes approx. 75% of the + // software algorithm below. + int64_t c = ((int64_t) a) << 30U; + + if ((uint32_t)(a ^ b) & 0x80000000U) { + c = (((-c) / b) + (1 << 13U)) >> 14U; + + if (c > 0x80000000U) { return INT32_MIN; } + + return -c; + + } else { + c = ((c / b) + (1 << 13U)) >> 14U; + + if (c > (int64_t)INT32_MAX) { return INT32_MAX; } + + return c; + } + +#else + // This uses the basic binary restoring division algorithm. + // It appears to be faster to do the whole division manually than + // trying to compose a 64-bit divide out of 32-bit divisions on + // platforms without hardware divide. + // Tested on Cortex-M0, it takes approx. 33% of the time of the + // 64-bit version above. + + uint32_t remainder = absval(a); + uint32_t divider = absval(b); + + uint32_t quotient = 0; + uint32_t bit = 0x10000U; + + /* The algorithm requires D >= R */ + while (divider < remainder) { + divider <<= 1U; + bit <<= 1U; + } + + if (!bit) { + if ((uint32_t)(a ^ b) & 0x80000000U) { // return truncated values + return INT32_MIN; + + } else { + return INT32_MAX; + } + } + + if (divider & 0x80000000U) { + // Perform one step manually to avoid overflows later. + // We know that divider's bottom bit is 0 here. + if (remainder >= divider) { + quotient |= bit; + remainder -= divider; + } + + divider >>= 1U; + bit >>= 1U; + } + + /* Main division loop */ + while (bit && remainder) { + if (remainder >= divider) { + quotient |= bit; + remainder -= divider; + } + + remainder <<= 1U; + bit >>= 1U; + } + + if (remainder >= divider) { + quotient++; + } + + uint32_t result = quotient; + + /* Figure out the sign of result */ + if ((uint32_t)(a ^ b) & 0x80000000U) { + result = -result; + } + + return (int32_t)result; +#endif +} + +/*! @} */ +#ifdef __cplusplus +} // extern "C" +#endif +#endif /* FP_DIV_H */ diff --git a/src/drivers/distance_sensor/broadcom/afbrs50/Inc/utility/fp_ema.h b/src/drivers/distance_sensor/broadcom/afbrs50/Inc/utility/fp_ema.h new file mode 100644 index 0000000000..f8c494a630 --- /dev/null +++ b/src/drivers/distance_sensor/broadcom/afbrs50/Inc/utility/fp_ema.h @@ -0,0 +1,204 @@ +/*************************************************************************//** + * @file + * @brief This file is part of the AFBR-S50 API. + * @details Provides averaging algorithms for fixed point data types. + * + * @copyright + * + * Copyright (c) 2023, Broadcom Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef FP_EMA_H +#define FP_EMA_H +#ifdef __cplusplus +extern "C" { +#endif + +/*!*************************************************************************** + * @addtogroup argus_fp + * @{ + *****************************************************************************/ + +#include "fp_def.h" + +#include "utility/fp_rnd.h" +#include "utility/fp_mul.h" + +/*!*************************************************************************** + * @brief Circular exponentially weighted moving average using UQ1.15 format. + * + * @details Evaluates the moving average (exponentially weighted) for circular + * data in UQ1.15 format. + * Circular data is that MAX_VALUE + 1 == MIN_VALUE. For example the + * usual phase information. + * + * Problem: Due to circularity of phase values, i.e. 0+x and 2PI+x are + * the same, the usual EMA has issues with the wrap around effect. + * Especially for vectors with phase around 0 (or 2PI), two values + * like 0 + x and PI - y are averaged to something around PI instead + * of 0 which would be more correct. + * + * Solution: Assume that phase jumps of more than PI are not allowed + * or possible. If a deviation of the new value to the smoothed signal + * occurs, it is clear that this stems from the wrap around effect and + * can be caught and correctly handled by the smoothing algorithm. + * + * Caution: If a target comes immediately into the field of view, phase + * jumps of > PI are indeed possible and volitional. However, the + * averaging break there anyway since the smoothed signal approaches + * only with delay to the correct values. The error made here is, that + * the smoothed signal approaches from the opposite direction. However, + * is approaches even faster since it always takes the shortest + * direction. + * + * @param mean The previous mean value in UQ1.15 format. + * @param x The current value to be added to the average UQ1.15 format. + * @param weight The EMA weight in UQ0.7 format. + * @return The new mean value in UQ1.15 format. + *****************************************************************************/ +inline uq1_15_t fp_ema15c(uq1_15_t mean, uq1_15_t x, uq0_8_t weight) +{ + if (weight == 0) { return x; } + + // Heeds the wrap around effect by casting dx to int16: + const int16_t dx = (int16_t)(x - mean); + const int32_t diff = weight * dx; + return (uq1_15_t)fp_rnds((mean << 8U) + diff, 8U); +} + +/*!*************************************************************************** + * @brief Exponentially weighted moving average using the Q11.4 format. + * + * @details Evaluates the moving average (exponentially weighted) for data in + * Q11.4 format. + * + * @param mean The previous mean value in Q11.4 format. + * @param x The current value to be added to the average Q11.4 format. + * @param weight The EMA weight in UQ0.7 format. + * @return The new mean value in Q11.4 format. + *****************************************************************************/ +inline q11_4_t fp_ema4(q11_4_t mean, q11_4_t x, uq0_8_t weight) +{ + if (weight == 0) { return x; } + + const int32_t dx = x - mean; + const int32_t diff = weight * dx; + return (q11_4_t)fp_rnds((mean << 8U) + diff, 8U); +} + +/*!*************************************************************************** + * @brief Exponentially weighted moving average using the Q7.8 format. + * + * @details Evaluates the moving average (exponentially weighted) for data in + * Q7.8 format. + * + * @param mean The previous mean value in Q7.8 format. + * @param x The current value to be added to the average Q7.8 format. + * @param weight The EMA weight in UQ0.7 format. + * @return The new mean value in Q7.8 format. + *****************************************************************************/ +inline q7_8_t fp_ema8(q7_8_t mean, q7_8_t x, uq0_8_t weight) +{ + return (q7_8_t)fp_ema4(mean, x, weight); +} + +/*!*************************************************************************** + * @brief Exponentially weighted moving average using the Q15.16 format. + * + * @details Evaluates the moving average (exponentially weighted) for data in + * Q15.16 format. + * + * @param mean The previous mean value in Q15.16 format. + * @param x The current value to be added to the average Q15.16 format. + * @param weight The EMA weight in UQ0.7 format. + * @return The new mean value in Q15.16 format. + *****************************************************************************/ +inline uint32_t uint_ema32(uint32_t mean, uint32_t x, uq0_8_t weight) +{ + if (weight == 0) { return x; } + + if (x > mean) { + const uint32_t dx = x - mean; + const uint32_t diff = fp_mulu(weight, dx, 8U); + return mean + diff; + + } else { + const uint32_t dx = mean - x; + const uint32_t diff = fp_mulu(weight, dx, 8U); + return mean - diff; + } +} +/*!*************************************************************************** + * @brief Exponentially weighted moving average using the Q15.16 format. + * + * @details Evaluates the moving average (exponentially weighted) for data in + * Q15.16 format. + * + * @param mean The previous mean value in Q15.16 format. + * @param x The current value to be added to the average Q15.16 format. + * @param weight The EMA weight in UQ0.7 format. + * @return The new mean value in Q15.16 format. + *****************************************************************************/ +inline int32_t int_ema32(int32_t mean, int32_t x, uq0_8_t weight) +{ + if (weight == 0) { return x; } + + if (x > mean) { + const uint32_t dx = x - mean; + const uint32_t diff = fp_mulu(weight, dx, 8U); + return mean + diff; + + } else { + const uint32_t dx = mean - x; + const uint32_t diff = fp_mulu(weight, dx, 8U); + return mean - diff; + } +} + +/*!*************************************************************************** + * @brief Exponentially weighted moving average using the Q15.16 format. + * + * @details Evaluates the moving average (exponentially weighted) for data in + * Q15.16 format. + * + * @param mean The previous mean value in Q15.16 format. + * @param x The current value to be added to the average Q15.16 format. + * @param weight The EMA weight in UQ0.7 format. + * @return The new mean value in Q15.16 format. + *****************************************************************************/ +inline q15_16_t fp_ema16(q15_16_t mean, q15_16_t x, uq0_8_t weight) +{ + return (q15_16_t)int_ema32(mean, x, weight); +} + +/*! @} */ +#ifdef __cplusplus +} // extern "C" +#endif +#endif /* FP_EMA_H */ diff --git a/src/drivers/distance_sensor/broadcom/afbrs50/Inc/utility/fp_exp.h b/src/drivers/distance_sensor/broadcom/afbrs50/Inc/utility/fp_exp.h new file mode 100644 index 0000000000..f845ca6e00 --- /dev/null +++ b/src/drivers/distance_sensor/broadcom/afbrs50/Inc/utility/fp_exp.h @@ -0,0 +1,69 @@ +/*************************************************************************//** + * @file + * @brief This file is part of the AFBR-S50 API. + * @details This file provides an exponential function for fixed point type. + * + * @copyright + * + * Copyright (c) 2023, Broadcom Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef FP_EXP_H +#define FP_EXP_H +#ifdef __cplusplus +extern "C" { +#endif + +/*!*************************************************************************** + * @addtogroup argus_fp + * @{ + *****************************************************************************/ + +#include "fp_def.h" + +/*!*************************************************************************** + * @brief Calculates the exponential of an fixed point number Q15.16 format. + * + * @details Calculates y = exp(x) in fixed point representation. + * + * Note that the result might not be 100 % accurate and might contain + * a small error! + * + * @see https://www.quinapalus.com/efunc.html + * + * @param x The input parameter in unsigned fixed point format Q15.16. + * @return Result y = exp(x) in the UQ16.16 format. + *****************************************************************************/ +uq16_16_t fp_exp16(q15_16_t x); + +/*! @} */ +#ifdef __cplusplus +} // extern "C" +#endif +#endif /* FP_DIV_H */ diff --git a/src/drivers/distance_sensor/broadcom/afbrs50/Inc/utility/fp_log.h b/src/drivers/distance_sensor/broadcom/afbrs50/Inc/utility/fp_log.h new file mode 100644 index 0000000000..6bc42b2ec7 --- /dev/null +++ b/src/drivers/distance_sensor/broadcom/afbrs50/Inc/utility/fp_log.h @@ -0,0 +1,69 @@ +/*************************************************************************//** + * @file + * @brief This file is part of the AFBR-S50 API. + * @details This file provides an logarithm function for fixed point type. + * + * @copyright + * + * Copyright (c) 2023, Broadcom Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef FP_LOG_H +#define FP_LOG_H +#ifdef __cplusplus +extern "C" { +#endif + +/*!*************************************************************************** + * @addtogroup argus_fp + * @{ + *****************************************************************************/ + +#include "fp_def.h" + +/*!*************************************************************************** + * @brief Calculates the natural logarithm (base e) of an fixed point number. + * + * @details Calculates y = ln(x) = log_e(x) in fixed point representation. + * + * Note that the result might not be 100 % accurate and might contain + * a small error! + * + * @see https://www.quinapalus.com/efunc.html + * + * @param x The input parameter in unsigned fixed point format Q15.16. + * @return Result y = ln(x) in the UQ16.16 format. + *****************************************************************************/ +q15_16_t fp_log16(uq16_16_t x); + +/*! @} */ +#ifdef __cplusplus +} // extern "C" +#endif +#endif /* FP_DIV_H */ diff --git a/src/drivers/distance_sensor/broadcom/afbrs50/Inc/utility/fp_mul.h b/src/drivers/distance_sensor/broadcom/afbrs50/Inc/utility/fp_mul.h new file mode 100644 index 0000000000..78db582644 --- /dev/null +++ b/src/drivers/distance_sensor/broadcom/afbrs50/Inc/utility/fp_mul.h @@ -0,0 +1,235 @@ +/*************************************************************************//** + * @file + * @brief This file is part of the AFBR-S50 API. + * @details Provides definitions and basic macros for fixed point data types. + * + * @copyright + * + * Copyright (c) 2023, Broadcom Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef FP_MUL_H +#define FP_MUL_H +#ifdef __cplusplus +extern "C" { +#endif + +/*!*************************************************************************** + * @addtogroup argus_fp + * @{ + *****************************************************************************/ + +#include "fp_def.h" +#include "utility/fp_rnd.h" + +/*!*************************************************************************** + * Set to use hardware division (Cortex-M3/4) over software division (Cortex-M0/1). + *****************************************************************************/ +#ifndef USE_64BIT_MUL +#define USE_64BIT_MUL 0 +#endif + +#if !USE_64BIT_MUL +/*!*************************************************************************** + * @brief Long multiplication of two unsigned 32-bit into an 64-bit value on + * 32-bit architecture. + * + * @details w (two words) gets the product of u and v (one word each). + * w[0] is the most significant word of the result, w[1] the least. + * (The words are in big-endian order). + * It is Knuth's Algorithm M from [Knu2] section 4.3.1. + * * + * @see http://www.hackersdelight.org/hdcodetxt/muldwu.c.txt + * + * @param w The result (u * v) value given as two unsigned 32-bit numbers: + * w[0] is the most significant word of the result, w[1] the least. + * (The words are in big-endian order). + * @param u Left hand side of the multiplication. + * @param v Right hand side of the multiplication. + *****************************************************************************/ +inline void muldwu(uint32_t w[], uint32_t u, uint32_t v) +{ + const uint32_t u0 = u >> 16U; + const uint32_t u1 = u & 0xFFFFU; + const uint32_t v0 = v >> 16U; + const uint32_t v1 = v & 0xFFFFU; + + uint32_t t = u1 * v1; + const uint32_t w3 = t & 0xFFFFU; + uint32_t k = t >> 16U; + + t = u0 * v1 + k; + const uint32_t w2 = t & 0xFFFFU; + const uint32_t w1 = t >> 16U; + + t = u1 * v0 + w2; + k = t >> 16U; + + w[0] = u0 * v0 + w1 + k; + w[1] = (t << 16U) + w3; +} +#endif + +/*!*************************************************************************** + * @brief 64-bit implementation of an unsigned multiplication with fixed point format. + * + * @details Algorithm to evaluate a*b, where a and b are arbitrary fixed point + * number of 32-bit width. The multiplication is done in 64-bit and + * the result is shifted down by the passed shift parameter in order + * to return a 32-bit value. + * The shift is executed with correct rounding. + * + * Note that the result must fit into the 32-bit value. An assertion + * error occurs otherwise (or undefined behavior of no assert available). + * + * @param u The left parameter in UQx1.y1 format + * @param v The right parameter in UQx2.y2 format + * @param shift The final right shift (rounding) value. + * @return Result = (a*b)>>shift in UQx.(y1+y2-shift) format. + *****************************************************************************/ +inline uint32_t fp_mulu(uint32_t u, uint32_t v, uint_fast8_t shift) +{ + assert(shift <= 32); +#if USE_64BIT_MUL + const uint64_t w = (uint64_t)u * (uint64_t)v; + return (w >> shift) + ((w >> (shift - 1)) & 1U); +#else + uint32_t tmp[2] = { 0 }; + muldwu(tmp, u, v); + + assert(shift ? tmp[0] <= (UINT32_MAX >> (32 - shift)) : tmp[0] == 0); + + if (32 - shift) { + return ((tmp[0] << (32 - shift)) + fp_rndu(tmp[1], shift)); + + } else { + return tmp[1] > (UINT32_MAX >> 1) ? tmp[0] + 1 : tmp[0]; + } + +#endif +} + +/*!*************************************************************************** + * @brief 64-bit implementation of a signed multiplication with fixed point format. + * + * @details Algorithm to evaluate a*b, where a and b are arbitrary fixed point + * number of 32-bit width. The multiplication is done in 64-bit and + * the result is shifted down by the passed shift parameter in order + * to return a 32-bit value. + * The shift is executed with correct rounding. + * + * Note that the result must fit into the 32-bit value. An assertion + * error occurs otherwise (or undefined behavior of no assert available). + * + * @param u The left parameter in Qx1.y1 format + * @param v The right parameter in Qx2.y2 format + * @param shift The final right shift (rounding) value. + * @return Result = (a*b)>>shift in Qx.(y1+y2-shift) format. + *****************************************************************************/ +inline int32_t fp_muls(int32_t u, int32_t v, uint_fast8_t shift) +{ + int_fast8_t sign = 1; + + uint32_t u2, v2; + + if (u < 0) { u2 = -u; sign = -sign; } else { u2 = u; } + + if (v < 0) { v2 = -v; sign = -sign; } else { v2 = v; } + + const uint32_t res = fp_mulu(u2, v2, shift); + + assert(sign > 0 ? res <= 0x7FFFFFFFU : res <= 0x80000000U); + + return sign > 0 ? res : -res; +} + + +/*!*************************************************************************** + * @brief 48-bit implementation of a unsigned multiplication with fixed point format. + * + * @details Algorithm to evaluate a*b, where a and b are arbitrary fixed point + * numbers with 32-bit unsigned and 16-bit unsigned format respectively. + * The multiplication is done in two 16x16-bit operations and the + * result is shifted down by the passed shift parameter in order to + * return a 32-bit value. + * + * Note that the result must fit into the 32-bit value. An assertion + * error occurs otherwise (or undefined behavior of no assert available). + * + * @param u The left parameter in Qx1.y1 format + * @param v The right parameter in Qx2.y2 format + * @param shift The final right shift (rounding) value. + * @return Result = (a*b)>>shift in Qx.(y1+y2-shift) format. + *****************************************************************************/ +inline uint32_t fp_mul_u32_u16(uint32_t u, uint16_t v, uint_fast8_t shift) +{ + assert(shift <= 48); + + if (shift > 16) { + uint32_t msk = 0xFFFFU; + uint32_t a = (u >> 16U) * v; + uint32_t b = (msk & u) * v; + return fp_rndu(a, shift - 16) + fp_rndu(b, shift); + + } else { + uint32_t msk = ~(0xFFFFFFFFU << shift); + uint32_t a = (u >> shift) * v; + uint32_t b = fp_rndu((msk & u) * v, shift); + return a + b; + } +} + +/*!*************************************************************************** + * @brief 48-bit implementation of an unsigned/signed multiplication with fixed point format. + * + * @details Algorithm to evaluate a*b, where a and b are arbitrary fixed point + * numbers with 32-bit signed and 16-bit unsigned format respectively. + * The multiplication is done in two 16x16-bit operations and the + * result is shifted down by the passed shift parameter in order to + * return a 32-bit value. + * The shift is executed with correct rounding. + * + * Note that the result must fit into the 32-bit value. An assertion + * error occurs otherwise (or undefined behavior of no assert available). + * + * @param u The left parameter in Qx1.y1 format + * @param v The right parameter in Qx2.y2 format + * @param shift The final right shift (rounding) value. + * @return Result = (a*b)>>shift in Qx.(y1+y2-shift) format. + *****************************************************************************/ +inline int32_t fp_mul_s32_u16(int32_t u, uint16_t v, uint_fast8_t shift) +{ + return u >= 0 ? fp_mul_u32_u16(u, v, shift) : - fp_mul_u32_u16(-u, v, shift); +} + +/*! @} */ +#ifdef __cplusplus +} // extern "C" +#endif +#endif /* FP_MUL_H */ diff --git a/src/drivers/distance_sensor/broadcom/afbrs50/Inc/utility/fp_rnd.h b/src/drivers/distance_sensor/broadcom/afbrs50/Inc/utility/fp_rnd.h new file mode 100644 index 0000000000..ad6f71e09c --- /dev/null +++ b/src/drivers/distance_sensor/broadcom/afbrs50/Inc/utility/fp_rnd.h @@ -0,0 +1,118 @@ +/*************************************************************************//** + * @file + * @brief This file is part of the AFBR-S50 API. + * @details Provides definitions and basic macros for fixed point data types. + * + * @copyright + * + * Copyright (c) 2023, Broadcom Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef FP_RND_H +#define FP_RND_H +#ifdef __cplusplus +extern "C" { +#endif + +/*!*************************************************************************** + * @addtogroup argus_fp + * @{ + *****************************************************************************/ + +#include "fp_def.h" +#include + +/*!*************************************************************************** + * @brief Converting with rounding from UQx.n1 to UQx.n2. + * @details Equivalent to dividing by 2^n with correct rounding to unsigned + * integer values. + * @param Q The number in (U)Qx.n1 fixed point format to be rounded. + * @param n The number of bits to be rounded, + * e.g. UQ8.8 -> UQ12.4 => n = 8 - 4 = 4. + * @return The rounded value in (U)Qx.n2 format. + *****************************************************************************/ +inline uint32_t fp_rndu(uint32_t Q, uint_fast8_t n) +{ + if (n == 0) { return Q; } + + else if (n > 32U) { return 0; } + + // Shift by n>=32 yields undefined behavior! Thus, this extra first + // step is essential to prevent issues. + Q >>= n - 1; + return (Q >> 1) + (Q & 1U); +} + +/*!*************************************************************************** + * @brief Converting with rounding from Qx.n1 to Qx.n2. + * @details Equivalent to dividing by 2^n with correct rounding to integer + * values. + * @param Q The number in (U)Qx.n1 fixed point format to be rounded. + * @param n The number of bits to be rounded, + * e.g. Q7.8 -> Q11.4 => n = 8 - 4 = 4. + * @return The rounded value in (U)Qx.n2 format. + *****************************************************************************/ +inline int32_t fp_rnds(int32_t Q, uint_fast8_t n) +{ + return (Q < 0) ? -fp_rndu(-Q, n) : fp_rndu(Q, n); +} + +/*!*************************************************************************** + * @brief Converting with truncation from UQx.n1 to UQx.n2. + * @details Equivalent to dividing by 2^n with truncating (throw away) the + * fractional part, resulting in an unsigned integer/fixed-point value. + * @param Q The number in (U)Qx.n1 fixed point format to be truncated. + * @param n The number of bits to be truncated, + * e.g. UQ8.8 -> UQ12.4 => n = 8 - 4 = 4. + * @return The truncated value in (U)Qx.n2 format. + *****************************************************************************/ +inline uint32_t fp_truncu(uint32_t Q, uint_fast8_t n) +{ + return (n < 32U) ? (Q >> n) : 0; +} + +/*!*************************************************************************** + * @brief Converting with truncation from Qx.n1 to Qx.n2. + * @details Equivalent to dividing by 2^n with truncating (throw away) the + * fractional part, resulting in a signed integer/fixed-point value. + * @param Q The number in (U)Qx.n1 fixed point format to be truncated. + * @param n The number of bits to be truncated, + * e.g. Q7.8 -> Q11.4 => n = 8 - 4 = 4. + * @return The truncated value in (U)Qx.n2 format. + *****************************************************************************/ +inline int32_t fp_truncs(int32_t Q, uint_fast8_t n) +{ + return (Q < 0) ? -fp_truncu(-Q, n) : fp_truncu(Q, n); +} + +/*! @} */ +#ifdef __cplusplus +} // extern "C" +#endif +#endif /* FP_RND_H */ diff --git a/src/drivers/distance_sensor/broadcom/afbrs50/Inc/utility/int_math.h b/src/drivers/distance_sensor/broadcom/afbrs50/Inc/utility/int_math.h new file mode 100644 index 0000000000..27de8cc688 --- /dev/null +++ b/src/drivers/distance_sensor/broadcom/afbrs50/Inc/utility/int_math.h @@ -0,0 +1,281 @@ +/*************************************************************************//** + * @file + * @brief This file is part of the AFBR-S50 API. + * @details Provides algorithms applied to integer values. + * + * @copyright + * + * Copyright (c) 2023, Broadcom Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef INT_MATH +#define INT_MATH +#ifdef __cplusplus +extern "C" { +#endif + +/*!*************************************************************************** + * @addtogroup argus_misc + * @{ + *****************************************************************************/ + +#include +#include + +/*! Enables the integer square root function. */ +#ifndef INT_SQRT +#define INT_SQRT 0 +#endif + +/*!*************************************************************************** + * @brief Integer Base-2 Logarithm. + * + * @details Calculates the base-2 logarithm for unsigned integer values. The + * result is the integer equivalent of floor(log2(x)). + * + * @param x Input parameter. + * @return The floor of the base-2 logarithm. + *****************************************************************************/ +inline uint32_t log2i(uint32_t x) +{ + assert(x != 0); +#if 1 + return 31 - __builtin_clz(x); +#else +#define S(k) if (x >= (1 << k)) { i += k; x >>= k; } + int i = 0; S(16); S(8); S(4); S(2); S(1); return i; +#undef S +#endif +} + +/*!*************************************************************************** + * @brief Integer Base-2 Logarithm with rounded result. + * + * @details Calculates the base-2 logarithm for unsigned integer values and + * returns the rounded result. The result is the integer equivalent + * of round(log2(x)). + * + * It is finding the nearest power-of-two value s.t. |x - 2^n| becomes + * minimum for all n. + * + * @param x Input parameter. + * @return The rounded value of the base-2 logarithm. + *****************************************************************************/ +inline uint32_t log2_round(uint32_t x) +{ + assert(x != 0); +#if 0 + const uint32_t y = x; + const uint32_t i = 0; + + while (y >>= 1) { i++; } + +#else + const uint32_t i = log2i(x); +#endif + return (i + ((x >> (i - 1U)) == 3U)); +} + +/*!*************************************************************************** + * @brief Finding the nearest power-of-two value. + * + * @details Implemented s.t. |x - 2^n| becomes minimum for all n. + * Special case 0: returns 0; + * Maximum input: 3037000499; higher number result in overflow! (returns 0) + * + * @param x Input parameter. + * @return Nearest power-of-two number, i.e. 2^n. + *****************************************************************************/ +inline uint32_t binary_round(uint32_t x) +{ + assert(x != 0); + const uint32_t shift = log2_round(x); + return (shift > 31U) ? 0 : 1U << shift; +} + +/*!*************************************************************************** + * @brief Counting bits set in a 32-bit unsigned integer. + * + * @details @see http://graphics.stanford.edu/~seander/bithacks.html + * + * @param x Input parameter. + * @return Number of bits set in input value. + *****************************************************************************/ +inline uint32_t popcount(uint32_t x) +{ + // http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel + x = x - ((x >> 1) & 0x55555555); + x = (x & 0x33333333) + ((x >> 2) & 0x33333333); + return (((x + (x >> 4)) & 0xF0F0F0F) * 0x1010101) >> 24; +} + +/*!*************************************************************************** + * @brief Determining if an integer is a power of 2 + * + * @details @see http://graphics.stanford.edu/~seander/bithacks.html + * + * @param x Input parameter. + * @return True if integer is power of 2. + *****************************************************************************/ +inline uint32_t ispowoftwo(uint32_t x) +{ + return x && !(x & (x - 1)); +} + +/*!*************************************************************************** + * @brief Calculates the absolute value. + * + * @param x Input parameter. + * @return The absolute value of x. + *****************************************************************************/ +inline uint32_t absval(int32_t x) +{ + // Note: special case of INT32_MIN must be handled correctly: + return x < 0 ? ((~(uint32_t)(x)) + 1) : (uint32_t)x; + + /* alternative with equal performance:*/ +// int32_t y = x >> 31; +// return (x ^ y) - y; + /* wrong implementation: + * does not correctly return abs(INT32_MIN) on 32-bit platform */ +// return x < 0 ? (uint32_t)(-x) : (uint32_t)x; +} + +/*!*************************************************************************** + * @brief Calculates the floor division by a factor of 2: floor(x / 2^n). + * + * @param x Input parameter. + * @param n The shift value, maximum is 31. + * @return The floor division by 2^n result. + *****************************************************************************/ +inline uint32_t floor2(uint32_t x, uint_fast8_t n) +{ + assert(n < 32); + return x >> n; +} + +/*!*************************************************************************** + * @brief Calculates the ceildiv division by a factor of 2: ceildiv(x / 2^n). + * + * @param x Input parameter. + * @param n The shift value, maximum is 31. + * @return The ceildiv division by 2^n result. + *****************************************************************************/ +inline uint32_t ceiling2(uint32_t x, uint_fast8_t n) +{ + assert(n < 32); + return x ? (1 + ((x - 1) >> n)) : 0; +} + +/*!*************************************************************************** + * @brief Calculates the ceildiv division: ceildiv(x / y). + * + * @param x Numerator + * @param y Denominator + * @return The result of the ceildiv division ceildiv(x / y). + *****************************************************************************/ +inline uint32_t ceildiv(uint32_t x, uint32_t y) +{ + assert(y != 0); + return x ? (1 + ((x - 1) / y)) : 0; +} + +/*!*************************************************************************** + * @brief Calculates the maximum of two values. + * + * @param a Input parameter. + * @param b Input parameter. + * @return The maximum value of the input parameters. + *****************************************************************************/ +#define MAX(a, b) ((a) > (b) ? (a) : (b)) + +/*!*************************************************************************** + * @brief Calculates the minimum of two values. + * + * @param a Input parameter. + * @param b Input parameter. + * @return The minimum value of the input parameters. + *****************************************************************************/ +#define MIN(a, b) ((a) < (b) ? (a) : (b)) + +/*!*************************************************************************** + * @brief Clamps a value between a minimum and maximum boundary. + * + * @details Clamps the values such that the condition min <= x <= max is true. + * + * @note The condition \p min <= \p max must hold!!! + * + * @param x The input parameter to be clamped. + * @param min The minimum or lower boundary. + * @param max The maximum or upper boundary. + * @return The clamped value of the input parameter within [min,max]. + *****************************************************************************/ +#define CLAMP(x, min, max) (MIN(MAX((x), (min)), (max))) + +#if INT_SQRT +/*!*************************************************************************** + * @brief Calculates the integer square root of x. + * + * @details The integer square root is defined as: + * isqrt(x) = (int)sqrt(x) + * + * @see https://en.wikipedia.org/wiki/Integer_square_root + * @see https://github.com/chmike/fpsqrt/blob/master/fpsqrt.c + * + * @param x Input parameter. + * @return isqrt(x) + *****************************************************************************/ +inline uint32_t isqrt(uint32_t v) +{ + unsigned t, q, b, r; + r = v; // r = v - x² + b = 0x40000000; // a² + q = 0; // 2ax + + while (b > 0) { + t = q + b; // t = 2ax + a² + q >>= 1; // if a' = a/2, then q' = q/2 + + if (r >= t) { // if (v - x²) >= 2ax + a² + r -= t; // r' = (v - x²) - (2ax + a²) + q += b; // if x' = (x + a) then ax' = ax + a², thus q' = q' + b + } + + b >>= 2; // if a' = a/2, then b' = b / 4 + } + + return q; +} +#endif // INT_SQRT + +/*! @} */ +#ifdef __cplusplus +} // extern "C" +#endif +#endif /* INT_MATH */ diff --git a/src/drivers/distance_sensor/broadcom/afbrs50/Inc/utility/status.h b/src/drivers/distance_sensor/broadcom/afbrs50/Inc/utility/status.h new file mode 100644 index 0000000000..ae9a4648f5 --- /dev/null +++ b/src/drivers/distance_sensor/broadcom/afbrs50/Inc/utility/status.h @@ -0,0 +1,49 @@ +/*************************************************************************//** + * @file + * @brief This file is part of the AFBR-S50 API. + * @details This file contains status codes for all platform specific + * functions. + * + * @copyright + * + * Copyright (c) 2023, Broadcom Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef STATUS_H +#define STATUS_H +#ifdef __cplusplus +extern "C" { +#endif + +#include "api/argus_status.h" + +#ifdef __cplusplus +} // extern "C" +#endif +#endif /* STATUS_H */ diff --git a/src/drivers/distance_sensor/broadcom/afbrs50/Inc/utility/time.h b/src/drivers/distance_sensor/broadcom/afbrs50/Inc/utility/time.h index 9c5f930351..1c2fe480e8 100644 --- a/src/drivers/distance_sensor/broadcom/afbrs50/Inc/utility/time.h +++ b/src/drivers/distance_sensor/broadcom/afbrs50/Inc/utility/time.h @@ -1,11 +1,11 @@ /*************************************************************************//** * @file - * @brief This file is part of the AFBR-S50 API. - * @details This file provides utility functions for timing necessities. + * @brief This file is part of the AFBR-S50 API. + * @details This file provides utility functions for timing necessities. * * @copyright * - * Copyright (c) 2021, Broadcom Inc + * Copyright (c) 2023, Broadcom Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -36,255 +36,563 @@ #ifndef TIME_H #define TIME_H +#ifdef __cplusplus +extern "C" { +#endif /*!*************************************************************************** - * @defgroup time Time Utility - * @ingroup argusutil - * @brief Timer utilities for time measurement duties. - * @details This module provides time measurement utility functions like - * delay or time measurement methods, or time math functions. - * @addtogroup time + * @defgroup argus_time Time Utility + * @ingroup argus_util + * + * @brief Timer utilities for time measurement duties. + * + * @details This module provides time measurement utility functions like + * delay or time measurement methods, or time math functions. + * + * @addtogroup argus_time * @{ *****************************************************************************/ +#include "platform/argus_timer.h" +#include #include #include /*!*************************************************************************** - * @brief A data structure to represent current time. + * @brief A data structure to represent current time. * - * @details Value is obtained from the PIT time which must be configured as - * lifetime counter. + * @details Value is obtained from the PIT time which must be configured as + * lifetime counter. + * + * Range: [0.000000, 4294967296.999999] seconds *****************************************************************************/ -typedef struct { - /*! Seconds. */ +typedef struct ltc_t { + /*! Seconds; + * Range: [0, UINT32_MAX] seconds */ uint32_t sec; - /*! Microseconds. */ + /*! Microseconds; + * Range: [0, 999999] microseconds */ uint32_t usec; } ltc_t; /*!*************************************************************************** - * @brief Obtains the elapsed time since MCU startup. - * @param t_now returned current time + * @brief Converts #ltc_t to microseconds (uint32_t). + * @details The specified time value (type #ltc_t) is converted to microseconds. + * The value is truncated to UINT32_MAX value if the result would + * exceed UINT32_MAX microseconds. + * @param t Input #ltc_t structure. + * @return Time value in microseconds. *****************************************************************************/ -void Time_GetNow(ltc_t *t_now); +inline uint32_t Time_ToUSec(ltc_t const *t) +{ + assert(t != 0); + + // max. value to convert correctly is 4294.967295 sec (UINT32_MAX/1000000) + return ((t->sec < 4294U) || (t->sec == 4294U && t->usec < 967295U)) ? + t->usec + t->sec * 1000000U : UINT32_MAX; +} /*!*************************************************************************** - * @brief Obtains the elapsed microseconds since MCU startup. - * @details Wrap around effect due to uint32_t result format!! - * @param - - * @return Elapsed microseconds since MCU startup as uint32_t. + * @brief Converts #ltc_t to milliseconds (uint32_t). + * @details The specified time value (type #ltc_t) is converted to milliseconds. + * The value is truncated to UINT32_MAX value if the result would + * exceed UINT32_MAX milliseconds. + * The returned value is correctly rounded to the nearest value. + * @param t Input #ltc_t structure. + * @return Time value in milliseconds. *****************************************************************************/ -uint32_t Time_GetNowUSec(void); +inline uint32_t Time_ToMSec(ltc_t const *t) +{ + assert(t != 0); + + // max. value to convert correctly is 4294967.295499 sec (UINT32_MAX/1000) + return ((t->sec < 4294967U) || (t->sec == 4294967U && t->usec < 295500U)) ? + (t->usec + 500U) / 1000U + t->sec * 1000U : UINT32_MAX; +} /*!*************************************************************************** - * @brief Obtains the elapsed milliseconds since MCU startup. - * @details Wrap around effect due to uint32_t result format!! - * @param - - * @return Elapsed milliseconds since MCU startup as uint32_t. + * @brief Converts #ltc_t to seconds (uint32_t). + * @details The specified time value (type #ltc_t) is converted to seconds. + * The returned value is correctly rounded to the nearest value. + * @param t Input #ltc_t structure. + * @return Time value in seconds. *****************************************************************************/ -uint32_t Time_GetNowMSec(void); +inline uint32_t Time_ToSec(ltc_t const *t) +{ + assert(t != 0); + + // max. value to convert correctly is 4294967295.499999 sec (UINT32_MAX/1000) + return (t->sec < 4294967295U || t->usec < 500000U) ? + (t->usec + 500000U) / 1000000U + t->sec : UINT32_MAX; +} /*!*************************************************************************** - * @brief Obtains the elapsed seconds since MCU startup. - * @param - - * @return Elapsed seconds since MCU startup as uint32_t. + * @brief Converts microseconds (uint32_t) to #ltc_t. + * @details The specified time value in microseconds is converted to type #ltc_t. + * @param t Output #ltc_t structure. + * @param t_usec Input time in microseconds. *****************************************************************************/ -uint32_t Time_GetNowSec(void); +inline void Time_FromUSec(ltc_t *t, uint32_t t_usec) +{ + assert(t != 0); + t->sec = t_usec / 1000000U; + t->usec = t_usec % 1000000U; +} /*!*************************************************************************** - * @brief Obtains the elapsed time since a given time point. - * @param t_elapsed Returns the elapsed time since t_start. - * @param t_start Start time point. + * @brief Converts milliseconds (uint32_t) to #ltc_t. + * @details The specified time value in milliseconds is converted to type #ltc_t. + * @param t Output #ltc_t structure. + * @param t_msec Input time in milliseconds. *****************************************************************************/ -void Time_GetElapsed(ltc_t *t_elapsed, ltc_t const *t_start); +inline void Time_FromMSec(ltc_t *t, uint32_t t_msec) +{ + assert(t != 0); + t->sec = t_msec / 1000U; + t->usec = (t_msec % 1000U) * 1000U; +} /*!*************************************************************************** - * @brief Obtains the elapsed microseconds since a given time point. - * @details Wrap around effect due to uint32_t result format!! - * @param t_start Start time point. - * @return Elapsed microseconds since t_start as uint32_t. + * @brief Converts seconds (uint32_t) to #ltc_t. + * @details The specified time value in seconds is converted to type #ltc_t. + * @param t Output #ltc_t structure. + * @param t_sec Input time in seconds. *****************************************************************************/ -uint32_t Time_GetElapsedUSec(ltc_t const *t_start); +inline void Time_FromSec(ltc_t *t, uint32_t t_sec) +{ + assert(t != 0); + t->usec = 0; + t->sec = t_sec; +} + /*!*************************************************************************** - * @brief Obtains the elapsed milliseconds since a given time point. - * @details Wrap around effect due to uint32_t result format!! - * @param t_start Start time point. - * @return Elapsed milliseconds since t_start as uint32_t. + * @brief Checks if /p t1 is greater or equal that /p t2. + * @details Handles overflow. + * @param t1 1st operand. + * @param t2 2nd operand. + * @return Returns (t1 >= t2); *****************************************************************************/ -uint32_t Time_GetElapsedMSec(ltc_t const *t_start); +inline bool Time_GreaterEqual(ltc_t const *t1, ltc_t const *t2) +{ + assert(t1 != 0); + assert(t2 != 0); + return (t1->sec == t2->sec) ? (t1->usec >= t2->usec) : (t1->sec > t2->sec); +} + /*!*************************************************************************** - * @brief Obtains the elapsed seconds since a given time point. - * @param t_start Start time point. - * @return Elapsed seconds since t_start as uint32_t. + * @brief Obtains the elapsed time since MCU startup. + * @param t_now returned current time *****************************************************************************/ -uint32_t Time_GetElapsedSec(ltc_t const *t_start); +inline void Time_GetNow(ltc_t *t_now) +{ + assert(t_now != 0); + Timer_GetCounterValue(&(t_now->sec), &(t_now->usec)); + assert(t_now->usec < 1000000U); +} /*!*************************************************************************** - * @brief Obtains the time difference between two given time points. - * @details Result is defined as t_diff = t_end - t_start. - * Note: since no negative time differences are supported, t_end has - * to be later/larger than t_start. Otherwise, the result won't be - * a negative time span but given by max value. - * @param t_diff Returned time difference. - * @param t_start Start time point. - * @param t_end End time point. + * @brief Obtains the elapsed time since MCU startup. + * @return Returns the current time. *****************************************************************************/ -void Time_Diff(ltc_t *t_diff, ltc_t const *t_start, ltc_t const *t_end); +inline ltc_t Time_Now(void) +{ + ltc_t t_now; + Time_GetNow(&t_now); + return t_now; +} /*!*************************************************************************** - * @brief Obtains the time difference between two given time points in - * microseconds. - * @details Result is defined as t_diff = t_end - t_start. - * Refers to Time_Diff() and handles overflow such that to large - * values are limited by 0xFFFFFFFF µs. - * @param t_start Start time point. - * @param t_end End time point. - * @return Time difference in microseconds. + * @brief Obtains the elapsed microseconds since MCU startup. + * @details Wrap around effect due to uint32_t result format!! + * @return Elapsed microseconds since MCU startup as uint32_t. *****************************************************************************/ -uint32_t Time_DiffUSec(ltc_t const *t_start, ltc_t const *t_end); +inline uint32_t Time_GetNowUSec(void) +{ + ltc_t t_now = Time_Now(); + return Time_ToUSec(&t_now); +} /*!*************************************************************************** - * @brief Obtains the time difference between two given time points in - * milliseconds. - * @details Result is defined as t_diff = t_end - t_start. - * Refers to Time_Diff() and handles overflow. - * Wrap around effect due to uint32_t result format!! - * @param t_start Start time point. - * @param t_end End time point. - * @return Time difference in milliseconds. + * @brief Obtains the elapsed milliseconds (rounded) since MCU startup. + * @details Wrap around effect due to uint32_t result format!! + * @return Elapsed milliseconds since MCU startup as uint32_t. *****************************************************************************/ -uint32_t Time_DiffMSec(ltc_t const *t_start, ltc_t const *t_end); +inline uint32_t Time_GetNowMSec(void) +{ + ltc_t t_now = Time_Now(); + return Time_ToMSec(&t_now); +} /*!*************************************************************************** - * @brief Obtains the time difference between two given time points in - * seconds. - * @details Result is defined as t_diff = t_end - t_start. - * Refers to Time_Diff() and handles overflow. - * @param t_start Start time point. - * @param t_end End time point. - * @return Time difference in seconds. + * @brief Obtains the elapsed seconds (rounded) since MCU startup. + * @return Elapsed seconds since MCU startup as uint32_t. *****************************************************************************/ -uint32_t Time_DiffSec(ltc_t const *t_start, ltc_t const *t_end); +inline uint32_t Time_GetNowSec(void) +{ + ltc_t t_now = Time_Now(); + return Time_ToSec(&t_now); +} + /*!*************************************************************************** - * @brief Time delay for a given time period. - * @param dt Delay time. + * @brief Obtains the time difference between two given time points. + * @details Result is defined as t_diff = t_end - t_start. + * Note: since no negative time differences are supported, t_end has + * to be later/larger than t_start. Otherwise, the result is undefined! + * @param t_diff Returned time difference. + * @param t_start Start time point. + * @param t_end End time point. *****************************************************************************/ -void Time_Delay(ltc_t const *dt); +inline void Time_Diff(ltc_t *t_diff, ltc_t const *t_start, ltc_t const *t_end) +{ + assert(t_diff != 0); + assert(t_start != 0); + assert(t_end != 0); + assert(t_diff != t_start); + assert(t_diff != t_end); + assert(Time_GreaterEqual(t_end, t_start)); + + if (t_start->usec <= t_end->usec) { // no carry over + t_diff->sec = t_end->sec - t_start->sec; + t_diff->usec = t_end->usec - t_start->usec; + + } else { // with carry over + t_diff->sec = t_end->sec - 1 - t_start->sec; + t_diff->usec = (1000000U - t_start->usec) + t_end->usec; + } +} /*!*************************************************************************** - * @brief Time delay for a given time period in microseconds. - * @param dt_usec Delay time in microseconds. + * @brief Obtains the time difference between two given time points in + * microseconds. + * @details Result is defined as t_diff = t_end - t_start. + * Refers to Time_Diff() and handles overflow such that to large + * values are limited by 0xFFFFFFFF µs. + * @param t_start Start time point. + * @param t_end End time point. + * @return Time difference in microseconds. *****************************************************************************/ -void Time_DelayUSec(uint32_t dt_usec); +inline uint32_t Time_DiffUSec(ltc_t const *t_start, ltc_t const *t_end) +{ + ltc_t t_diff; + Time_Diff(&t_diff, t_start, t_end); + return Time_ToUSec(&t_diff); +} /*!*************************************************************************** - * @brief Time delay for a given time period in milliseconds. - * @param dt_msec Delay time in milliseconds. + * @brief Obtains the time difference between two given time points in + * milliseconds. + * @details Result is defined as t_diff = t_end - t_start. + * Refers to Time_Diff() and handles overflow. + * Wrap around effect due to uint32_t result format!! + * @param t_start Start time point. + * @param t_end End time point. + * @return Time difference in milliseconds. *****************************************************************************/ -void Time_DelayMSec(uint32_t dt_msec); +inline uint32_t Time_DiffMSec(ltc_t const *t_start, ltc_t const *t_end) +{ + ltc_t t_diff; + Time_Diff(&t_diff, t_start, t_end); + return Time_ToMSec(&t_diff); +} /*!*************************************************************************** - * @brief Time delay for a given time period in seconds. - * @param dt_sec Delay time in seconds. + * @brief Obtains the time difference between two given time points in + * seconds. + * @details Result is defined as t_diff = t_end - t_start. + * Refers to Time_Diff() and handles overflow. + * @param t_start Start time point. + * @param t_end End time point. + * @return Time difference in seconds. *****************************************************************************/ -void Time_DelaySec(uint32_t dt_sec); +inline uint32_t Time_DiffSec(ltc_t const *t_start, ltc_t const *t_end) +{ + ltc_t t_diff; + Time_Diff(&t_diff, t_start, t_end); + return Time_ToSec(&t_diff); +} + /*!*************************************************************************** - * @brief Checks if timeout is reached from a given starting time. - * @details Handles overflow. - * @param t_start Start time. - * @param t_timeout Timeout period. - * @return Timeout elapsed? True/False (boolean value) + * @brief Obtains the elapsed time since a given time point. + * @details Calculates the currently elapsed time since a specified start time + * (/p t_start). + * + * Note that /p t_start must be in the past! Otherwise, the behavior is + * undefined! + * + * @param t_elapsed Returns the elapsed time since /p t_start. + * @param t_start Start time point. *****************************************************************************/ -bool Time_CheckTimeout(ltc_t const *t_start, ltc_t const *t_timeout); +inline void Time_GetElapsed(ltc_t *t_elapsed, ltc_t const *t_start) +{ + assert(t_elapsed != 0); + assert(t_start != 0); + assert(t_elapsed != t_start); + ltc_t t_now = Time_Now(); + Time_Diff(t_elapsed, t_start, &t_now); +} /*!*************************************************************************** - * @brief Checks if timeout is reached from a given starting time. - * @details Handles overflow. - * @param t_start Start time. - * @param t_timeout_usec Timeout period in microseconds. - * @return Timeout elapsed? True/False (boolean value) + * @brief Obtains the elapsed microseconds since a given time point. + * @details Wrap around effect due to uint32_t result format!! + * @param t_start Start time point. + * @return Elapsed microseconds since t_start as uint32_t. *****************************************************************************/ -bool Time_CheckTimeoutUSec(ltc_t const *t_start, uint32_t const t_timeout_usec); +inline uint32_t Time_GetElapsedUSec(ltc_t const *t_start) +{ + assert(t_start != 0); + ltc_t t_now = Time_Now(); + return Time_DiffUSec(t_start, &t_now); +} /*!*************************************************************************** - * @brief Checks if timeout is reached from a given starting time. - * @details Handles overflow. - * @param t_start Start time. - * @param t_timeout_msec Timeout period in milliseconds. - * @return Timeout elapsed? True/False (boolean value) + * @brief Obtains the elapsed milliseconds since a given time point. + * @details Wrap around effect due to uint32_t result format!! + * @param t_start Start time point. + * @return Elapsed milliseconds since t_start as uint32_t. *****************************************************************************/ -bool Time_CheckTimeoutMSec(ltc_t const *t_start, uint32_t const t_timeout_msec); +inline uint32_t Time_GetElapsedMSec(ltc_t const *t_start) +{ + assert(t_start != 0); + ltc_t t_now = Time_Now(); + return Time_DiffMSec(t_start, &t_now); +} /*!*************************************************************************** - * @brief Checks if timeout is reached from a given starting time. - * @details Handles overflow. - * @param t_start Start time. - * @param t_timeout_sec Timeout period in seconds. - * @return Timeout elapsed? True/False (boolean value) + * @brief Obtains the elapsed seconds since a given time point. + * @param t_start Start time point. + * @return Elapsed seconds since t_start as uint32_t. *****************************************************************************/ -bool Time_CheckTimeoutSec(ltc_t const *t_start, uint32_t const t_timeout_sec); +inline uint32_t Time_GetElapsedSec(ltc_t const *t_start) +{ + assert(t_start != 0); + ltc_t t_now = Time_Now(); + return Time_DiffSec(t_start, &t_now); +} + /*!*************************************************************************** - * @brief Adds two ltc_t values. - * @details Result is defined as t = t1 + t2. Results are wrapped around at - * maximum values just like integers. - * @param t Return value: t = t1 + t2. - * @param t1 1st operand. - * @param t2 2nd operand. + * @brief Adds two #ltc_t values. + * @details Result is defined as t = t1 + t2. + * The results are wrapped around at maximum values just like integers. + * The references for t, t1 and t2 may point to the same instance(s). + * + * @param t Return value: t = t1 + t2. + * @param t1 1st operand. + * @param t2 2nd operand. *****************************************************************************/ -void Time_Add(ltc_t *t, ltc_t const *t1, ltc_t const *t2); +inline void Time_Add(ltc_t *t, ltc_t const *t1, ltc_t const *t2) +{ + assert(t != 0); + assert(t1 != 0); + assert(t2 != 0); + + t->sec = t1->sec + t2->sec; + t->usec = t1->usec + t2->usec; + + if (t->usec > 999999U) { + t->sec += 1U; + t->usec -= 1000000U; + } +} /*!*************************************************************************** - * @brief Adds a given time in microseconds to an ltc_t value. - * @param t Return value: t = t1 + t2. - * @param t1 1st operand. - * @param t2_usec 2nd operand in microseconds. + * @brief Adds a given time in microseconds to an #ltc_t value. + * @details Result is defined as t = t1 + t2. + * The results are wrapped around at maximum values just like integers. + * The references for t and t1 may point to the same instance. + * + * @param t Return value: t = t1 + t2. + * @param t1 1st operand. + * @param t2_usec 2nd operand in microseconds. *****************************************************************************/ -void Time_AddUSec(ltc_t *t, ltc_t const *t1, uint32_t t2_usec); +inline void Time_AddUSec(ltc_t *t, ltc_t const *t1, uint32_t t2_usec) +{ + assert(t != 0); + assert(t1 != 0); + ltc_t t2; + Time_FromUSec(&t2, t2_usec); + Time_Add(t, t1, &t2); +} /*!*************************************************************************** - * @brief Adds a given time in milliseconds to an ltc_t value. - * @param t Return value: t = t1 + t2. - * @param t1 1st operand. - * @param t2_msec 2nd operand in milliseconds. + * @brief Adds a given time in milliseconds to an #ltc_t value. + * @details Result is defined as t = t1 + t2. + * The results are wrapped around at maximum values just like integers. + * The references for t and t1 may point to the same instance. + * + * @param t Return value: t = t1 + t2. + * @param t1 1st operand. + * @param t2_msec 2nd operand in milliseconds. *****************************************************************************/ -void Time_AddMSec(ltc_t *t, ltc_t const *t1, uint32_t t2_msec); +inline void Time_AddMSec(ltc_t *t, ltc_t const *t1, uint32_t t2_msec) +{ + assert(t != 0); + assert(t1 != 0); + ltc_t t2; + Time_FromMSec(&t2, t2_msec); + Time_Add(t, t1, &t2); +} /*!*************************************************************************** - * @brief Adds a given time in seconds to an ltc_t value. - * @param t Return value: t = t1 + t2. - * @param t1 1st operand. - * @param t2_sec 2nd operand in seconds. + * @brief Adds a given time in seconds to an #ltc_t value. + * @details Result is defined as t = t1 + t2. + * The results are wrapped around at maximum values just like integers. + * The references for t and t1 may point to the same instance. + * + * @param t Return value: t = t1 + t2. + * @param t1 1st operand. + * @param t2_sec 2nd operand in seconds. *****************************************************************************/ -void Time_AddSec(ltc_t *t, ltc_t const *t1, uint32_t t2_sec); +inline void Time_AddSec(ltc_t *t, ltc_t const *t1, uint32_t t2_sec) +{ + assert(t != 0); + assert(t1 != 0); + ltc_t t2; + Time_FromSec(&t2, t2_sec); + Time_Add(t, t1, &t2); +} + /*!*************************************************************************** - * @brief Converts ltc_t to microseconds (uint32_t). - * @param t Input ltc_t struct. - * @return Time value in microseconds. + * @brief Checks if /p t is within the time interval /p t_start and /p t_end. + * @details The interval is from /p t_start to /p t_end. + * The function returns true if /p t >= /p t_start AND /p t < /p t_end. + * If /p t_end is before /p t_start, /p t_end is consider to be wrapped + * around and the condition inverts (i.e. the function returns true if + * /p < /p t_end OR /p t >= t_start. + * @param t_start The start of the time interval. + * @param t_end The end of the time interval. + * @param t The time to be checked if it is with the interval. + * @return True if t is within t_start and t_stop. *****************************************************************************/ -uint32_t Time_ToUSec(ltc_t const *t); +inline bool Time_CheckWithin(ltc_t const *t_start, ltc_t const *t_end, ltc_t const *t) +{ + if (Time_GreaterEqual(t_end, t_start)) { + return Time_GreaterEqual(t, t_start) && !Time_GreaterEqual(t, t_end); + + } else { + return Time_GreaterEqual(t, t_start) || !Time_GreaterEqual(t, t_end); + } +} + /*!*************************************************************************** - * @brief Converts ltc_t to milliseconds (uint32_t). - * @param t Input ltc_t struct. - * @return Time value in milliseconds. + * @brief Checks if timeout is reached from a given starting time. + * @details Checks if a specified time (/p t_timeout) has elapsed since a + * specified start time (/p t_start). + * Handles overflow/wraparound of time values at the maximum value. + * @param t_start Start time. + * @param t_timeout Timeout period. + * @return Timeout elapsed? True/False (boolean value) *****************************************************************************/ -uint32_t Time_ToMSec(ltc_t const *t); +inline bool Time_CheckTimeout(ltc_t const *t_start, ltc_t const *t_timeout) +{ + assert(t_start != 0); + assert(t_timeout != 0); + + ltc_t t_end; + ltc_t t_now = Time_Now(); + Time_Add(&t_end, t_start, t_timeout); + return !Time_CheckWithin(t_start, &t_end, &t_now); +} /*!*************************************************************************** - * @brief Converts ltc_t to seconds (uint32_t). - * @param t Input ltc_t struct. - * @return Time value in seconds. + * @brief Checks if timeout is reached from a given starting time. + * @details Handles overflow. + * @param t_start Start time. + * @param t_timeout_usec Timeout period in microseconds. + * @return Timeout elapsed? True/False (boolean value) *****************************************************************************/ -uint32_t Time_ToSec(ltc_t const *t); +inline bool Time_CheckTimeoutUSec(ltc_t const *t_start, uint32_t const t_timeout_usec) +{ + ltc_t t_timeout; + Time_FromUSec(&t_timeout, t_timeout_usec); + return Time_CheckTimeout(t_start, &t_timeout); +} + +/*!*************************************************************************** + * @brief Checks if timeout is reached from a given starting time. + * @details Handles overflow. + * @param t_start Start time. + * @param t_timeout_msec Timeout period in milliseconds. + * @return Timeout elapsed? True/False (boolean value) + *****************************************************************************/ +inline bool Time_CheckTimeoutMSec(ltc_t const *t_start, uint32_t const t_timeout_msec) +{ + ltc_t t_timeout; + Time_FromMSec(&t_timeout, t_timeout_msec); + return Time_CheckTimeout(t_start, &t_timeout); +} + +/*!*************************************************************************** + * @brief Checks if timeout is reached from a given starting time. + * @details Handles overflow. + * @param t_start Start time. + * @param t_timeout_sec Timeout period in seconds. + * @return Timeout elapsed? True/False (boolean value) + *****************************************************************************/ +inline bool Time_CheckTimeoutSec(ltc_t const *t_start, uint32_t const t_timeout_sec) +{ + ltc_t t_timeout; + Time_FromSec(&t_timeout, t_timeout_sec); + return Time_CheckTimeout(t_start, &t_timeout); +} + + +/*!*************************************************************************** + * @brief Time delay for a given time period. + * @param dt Delay time. + *****************************************************************************/ +inline void Time_Delay(ltc_t const *dt) +{ + assert(dt != 0); + ltc_t t_start = Time_Now(); + + while (!Time_CheckTimeout(&t_start, dt)); +} + +/*!*************************************************************************** + * @brief Time delay for a given time period in microseconds. + * @param dt_usec Delay time in microseconds. + *****************************************************************************/ +inline void Time_DelayUSec(uint32_t dt_usec) +{ + ltc_t t_start = Time_Now(); + + while (!Time_CheckTimeoutUSec(&t_start, dt_usec)); +} + +/*!*************************************************************************** + * @brief Time delay for a given time period in milliseconds. + * @param dt_msec Delay time in milliseconds. + *****************************************************************************/ +inline void Time_DelayMSec(uint32_t dt_msec) +{ + ltc_t t_start = Time_Now(); + + while (!Time_CheckTimeoutMSec(&t_start, dt_msec)); +} + +/*!*************************************************************************** + * @brief Time delay for a given time period in seconds. + * @param dt_sec Delay time in seconds. + *****************************************************************************/ +inline void Time_DelaySec(uint32_t dt_sec) +{ + ltc_t t_start = Time_Now(); + + while (!Time_CheckTimeoutSec(&t_start, dt_sec)); +} + /*! @} */ +#ifdef __cplusplus +} // extern "C" +#endif #endif /* TIME_H */ diff --git a/src/drivers/distance_sensor/broadcom/afbrs50/Lib/libafbrs50_m4_fpu.a b/src/drivers/distance_sensor/broadcom/afbrs50/Lib/libafbrs50_m4_fpu.a index c646ab3116..e4e6dc0c52 100644 Binary files a/src/drivers/distance_sensor/broadcom/afbrs50/Lib/libafbrs50_m4_fpu.a and b/src/drivers/distance_sensor/broadcom/afbrs50/Lib/libafbrs50_m4_fpu.a differ diff --git a/src/drivers/distance_sensor/broadcom/afbrs50/Lib/libafbrs50_m4_fpu_os.a b/src/drivers/distance_sensor/broadcom/afbrs50/Lib/libafbrs50_m4_fpu_os.a index 905b2fa66d..23bcd6fbea 100644 Binary files a/src/drivers/distance_sensor/broadcom/afbrs50/Lib/libafbrs50_m4_fpu_os.a and b/src/drivers/distance_sensor/broadcom/afbrs50/Lib/libafbrs50_m4_fpu_os.a differ diff --git a/src/drivers/distance_sensor/broadcom/afbrs50/argus_hal_test.c b/src/drivers/distance_sensor/broadcom/afbrs50/argus_hal_test.c index 5f8fd4b1f1..2e0e8dff16 100644 --- a/src/drivers/distance_sensor/broadcom/afbrs50/argus_hal_test.c +++ b/src/drivers/distance_sensor/broadcom/afbrs50/argus_hal_test.c @@ -1,10 +1,10 @@ /*************************************************************************//** - * @file argus_hal_test.c - * @brief Tests for the AFBR-S50 API hardware abstraction layer. + * @file + * @brief Tests for the AFBR-S50 API hardware abstraction layer. * * @copyright * - * Copyright (c) 2021, Broadcom, Inc. + * Copyright (c) 2023, Broadcom Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -34,6 +34,10 @@ * POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ +/*!*************************************************************************** + * @addtogroup argus_test + * @{ + *****************************************************************************/ /******************************************************************************* * Include Files @@ -46,44 +50,44 @@ #include "platform/argus_nvm.h" #include "platform/argus_irq.h" -#include -#include -#include - /******************************************************************************* * Definitions ******************************************************************************/ -/*! An error log message via print(); */ +/*! An error log message via #print function. */ #define error_log(fmt, ...) print("ERROR: " fmt "\n", ##__VA_ARGS__) /******************************************************************************* * Prototypes ******************************************************************************/ +static status_t VerifyHALImplementation(s2pi_slave_t spi_slave); + static status_t TimerPlausibilityTest(void); static status_t TimerWraparoundTest(void); static status_t SpiConnectionTest(s2pi_slave_t slave); -static status_t SpiInterruptTest(s2pi_slave_t slave); +static status_t SpiMaxLengthTest(s2pi_slave_t slave); +//static status_t SpiInterruptTest(s2pi_slave_t slave); +static status_t GpioInterruptTest(s2pi_slave_t slave); static status_t GpioModeTest(s2pi_slave_t slave); static status_t TimerTest(s2pi_slave_t slave); static status_t PITTest(void); +static status_t SpiTransferFromInterruptTest(s2pi_slave_t slave); static status_t CheckTimerCounterValues(uint32_t hct, uint32_t lct); -static status_t SPITransferSync(s2pi_slave_t slave, uint8_t *data, uint8_t size); +static status_t SPITransferSync(s2pi_slave_t slave, uint8_t *data, size_t size); static status_t ConfigureDevice(s2pi_slave_t slave, int8_t rcoTrim); -static status_t TriggerMeasurement(s2pi_slave_t slave, uint16_t samples); -static status_t AwaitDataReady(s2pi_slave_t slave, uint32_t timeout_ms); +static status_t TriggerMeasurement(s2pi_slave_t slave, uint16_t samples, s2pi_callback_t callback, void *callbackData); static status_t ReadEEPROM(s2pi_slave_t slave, uint8_t *eeprom); static status_t ReadRcoTrim(s2pi_slave_t slave, int8_t *RcoTrim); static status_t RunMeasurement(s2pi_slave_t slave, uint16_t samples); static status_t RunPITTest(uint32_t exp_dt_us, uint32_t n); static void PIT_Callback(void *param); -static void DataReadyCallback(void *param); +static void GPIO_Callback(void *param); /// @cond EXTERN extern uint32_t EEPROM_ReadChipId(uint8_t const *eeprom); -extern argus_module_version_t EEPROM_ReadModule(uint8_t const *eeprom); +extern uint8_t EEPROM_ReadModule(uint8_t const *eeprom); extern status_t EEPROM_Read(s2pi_slave_t slave, uint8_t address, uint8_t *data); extern uint8_t hamming_decode(uint8_t const *code, uint8_t *data); /// @endcond @@ -98,99 +102,129 @@ extern uint8_t hamming_decode(uint8_t const *code, uint8_t *data); status_t Argus_VerifyHALImplementation(s2pi_slave_t spi_slave) { - status_t status = STATUS_OK; + print("########################################################\n"); + print("# Running HAL Verification Test - " HAL_TEST_VERSION "\n"); + print("########################################################\n"); + print("- SPI Slave: %d \n\n", spi_slave); - PX4_INFO_RAW("########################################################\n"); - PX4_INFO_RAW("# Running HAL Verification Test - " HAL_TEST_VERSION "\n"); - PX4_INFO_RAW("########################################################\n\n"); + const status_t status = VerifyHALImplementation(spi_slave); - PX4_INFO_RAW("1 > Timer Plausibility Test\n"); - status = TimerPlausibilityTest(); - - if (status != STATUS_OK) { goto summary; } - - PX4_INFO_RAW("1 > PASS\n\n"); - - PX4_INFO_RAW("2 > Timer Wraparound Test\n"); - status = TimerWraparoundTest(); - - if (status != STATUS_OK) { goto summary; } - - PX4_INFO_RAW("2 > PASS\n\n"); - - PX4_INFO_RAW("3 > SPI Connection Test\n"); - status = SpiConnectionTest(spi_slave); - - if (status != STATUS_OK) { goto summary; } - - PX4_INFO_RAW("3 > PASS\n\n"); - - PX4_INFO_RAW("4 > SPI Interrupt Test\n"); - status = SpiInterruptTest(spi_slave); - - if (status != STATUS_OK) { goto summary; } - - PX4_INFO_RAW("4 > PASS\n\n"); - - PX4_INFO_RAW("5 > GPIO Mode Test\n"); - status = GpioModeTest(spi_slave); - - if (status != STATUS_OK) { goto summary; } - - PX4_INFO_RAW("5 > PASS\n\n"); - - PX4_INFO_RAW("6 > Lifetime Counter Timer (LTC) Test\n"); - status = TimerTest(spi_slave); - - if (status != STATUS_OK) { goto summary; } - - PX4_INFO_RAW("6 > PASS\n\n"); - - PX4_INFO_RAW("7 > Periodic Interrupt Timer (PIT) Test\n"); - status = PITTest(); - - if (status == ERROR_NOT_IMPLEMENTED) { - PX4_INFO_RAW("7 > SKIPPED (PIT is not implemented)\n\n"); - - } else { - if (status != STATUS_OK) { goto summary; } - - PX4_INFO_RAW("7 > PASS\n\n"); - } - - -summary: - PX4_INFO_RAW("########################################################\n"); + print("########################################################\n"); if (status != STATUS_OK) { - PX4_INFO_RAW("# FAIL: HAL Verification Test finished with error %d!\n", (int)status); + print("# FAIL: HAL Verification Test finished with error %d!\n", status); } else { - PX4_INFO_RAW("# PASS: HAL Verification Test finished successfully!\n"); + print("# PASS: HAL Verification Test finished successfully!\n"); } - PX4_INFO_RAW("########################################################\n\n"); + print("########################################################\n\n"); + return status; } /*!*************************************************************************** - * @brief Checks the validity of timer counter values. + * @brief Executes a series of tests in order to verify the HAL implementation. * - * @details This verifies that the counter values returned from the - * #Timer_GetCounterValue function are valid. This means, the low - * counter value \p lct is within 0 and 999999 µs. + * @details See #Argus_VerifyHALImplementation for details. * - * @return Returns the \link #status_t status\endlink: - * - #STATUS_OK on success. - * - #ERROR_FAIL on failure (check the error log for more information). + * @param spi_slave The SPI hardware slave. + * + * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). + *****************************************************************************/ +static status_t VerifyHALImplementation(s2pi_slave_t spi_slave) +{ + status_t status = STATUS_OK; + + print("1 > Timer Plausibility Test\n"); + status = TimerPlausibilityTest(); + + if (status != STATUS_OK) { return status; } + + print("1 > PASS\n\n"); + + print("2 > Timer Wraparound Test\n"); + status = TimerWraparoundTest(); + + if (status != STATUS_OK) { return status; } + + print("2 > PASS\n\n"); + + print("3 > SPI Connection Test\n"); + status = SpiConnectionTest(spi_slave); + + if (status != STATUS_OK) { return status; } + + print("3 > PASS\n\n"); + + print("4 > SPI Maximum Data Length Test\n"); + status = SpiMaxLengthTest(spi_slave); + + if (status != STATUS_OK) { return status; } + + print("4 > PASS\n\n"); + + print("5 > GPIO Interrupt Test\n"); + status = GpioInterruptTest(spi_slave); + + if (status != STATUS_OK) { return status; } + + print("5 > PASS\n\n"); + + print("6 > GPIO Mode Test\n"); + status = GpioModeTest(spi_slave); + + if (status != STATUS_OK) { return status; } + + print("6 > PASS\n\n"); + + print("7 > Lifetime Counter Timer (LTC) Test\n"); + status = TimerTest(spi_slave); + + if (status != STATUS_OK) { return status; } + + print("7 > PASS\n\n"); + + print("8 > Periodic Interrupt Timer (PIT) Test\n"); + status = PITTest(); + + if (status == ERROR_NOT_IMPLEMENTED) { + print("8 > SKIPPED (PIT is not implemented)\n\n"); + + } else { + if (status != STATUS_OK) { return status; } + + print("8 > PASS\n\n"); + } + + print("9 > SPI Interrupt Test\n"); + status = SpiTransferFromInterruptTest(spi_slave); + + if (status != STATUS_OK) { return status; } + + print("9 > PASS\n\n"); + + return status; +} + +/*!*************************************************************************** + * @brief Checks the validity of timer counter values. + * + * @details This verifies that the counter values returned from the + * #Timer_GetCounterValue function are valid. This means, the low + * counter value \p lct is within 0 and 999999 µs. + * + * @return Returns the \link #status_t status\endlink: + * - #STATUS_OK on success. + * - #ERROR_FAIL on failure (check the error log for more information). *****************************************************************************/ static status_t CheckTimerCounterValues(uint32_t hct, uint32_t lct) { if (lct > 999999) { - PX4_INFO_RAW("Timer plausibility check:\n" - "The parameter \"lct\" of Timer_GetCounterValue() must always " - "be within 0 and 999999.\n" - "Current Values: hct = %d, lct = %d", (uint)hct, (uint)lct); + error_log("Timer plausibility check:\n" + "The parameter \"lct\" of Timer_GetCounterValue() must always " + "be within 0 and 999999.\n" + "Current Values: hct = %d, lct = %d", hct, lct); return ERROR_FAIL; } @@ -198,24 +232,24 @@ static status_t CheckTimerCounterValues(uint32_t hct, uint32_t lct) } /*!*************************************************************************** - * @brief Plausibility Test for Timer HAL Implementation. + * @brief Plausibility Test for Timer HAL Implementation. * - * @details Rudimentary tests the lifetime counter (LTC) implementation. - * This verifies that the LTC is running by checking if the returned - * values of two consecutive calls to the #Timer_GetCounterValue - * function are ascending. An artificial delay using the NOP operation - * is induced such that the timer is not read to fast. + * @details Rudimentary tests the lifetime counter (LTC) implementation. + * This verifies that the LTC is running by checking if the returned + * values of two consecutive calls to the #Timer_GetCounterValue + * function are ascending. An artificial delay using the NOP operation + * is induced such that the timer is not read to fast. * * @warning If using an ultra-fast processor with a rather low timer granularity, - * the test may fail! In this case, it could help to increase the delay - * by increasing the for-loop exit criteria. + * the test may fail! In this case, it could help to increase the delay + * by increasing the for-loop exit criteria. * - * @warning This test does not test yet verify if the timing is correct at all! - * This it done in later test... + * @warning This test does not test yet verify if the timing is correct at all! + * This it done in later test... * - * @return Returns the \link #status_t status\endlink: - * - #STATUS_OK on success. - * - #ERROR_FAIL on failure (check the error log for more information). + * @return Returns the \link #status_t status\endlink: + * - #STATUS_OK on success. + * - #ERROR_FAIL on failure (check the error log for more information). *****************************************************************************/ static status_t TimerPlausibilityTest(void) { @@ -230,7 +264,7 @@ static status_t TimerPlausibilityTest(void) /* Check max value is not exceeded for LCT timer (us) */ status_t status = CheckTimerCounterValues(hct0, lct0); - if (status < STATUS_OK) { return status; } + if (status != STATUS_OK) { return status; } /* Adding a delay. Depending on MCU speed, this takes any time. * However, the Timer should be able to solve this on any MCU. */ @@ -242,18 +276,18 @@ static status_t TimerPlausibilityTest(void) /* Check max value is not exceeded for LCT timer (us) */ status = CheckTimerCounterValues(hct1, lct1); - if (status < STATUS_OK) { return status; } + if (status != STATUS_OK) { return status; } /* Either the hct value must have been increased or the lct value if the hct * value is still the same. */ if (!((hct1 > hct0) || ((hct1 == hct0) && (lct1 > lct0)))) { - PX4_INFO_RAW("Timer plausibility check: the elapsed time could not be " - "measured with the Timer_GetCounterValue() function; no time " - "has elapsed!\n" - "The delay was induced by the following code:\n" - "for (volatile uint32_t i = 0; i < 100000; ++i) __asm(\"nop\");\n" - "Current Values: hct0 = %d, lct0 = %d, hct1 = %d, lct1 = %d", - (uint)hct0, (uint)lct0, (uint)hct1, (uint)lct1); + error_log("Timer plausibility check: the elapsed time could not be " + "measured with the Timer_GetCounterValue() function; no time " + "has elapsed!\n" + "The delay was induced by the following code:\n" + "for (volatile uint32_t i = 0; i < 100000; ++i) __asm(\"nop\");\n", + "Current Values: hct0 = %d, lct0 = %d, hct1 = %d, lct1 = %d", + hct0, lct0, hct1, lct1); return ERROR_FAIL; } @@ -261,29 +295,29 @@ static status_t TimerPlausibilityTest(void) } /*!*************************************************************************** - * @brief Wraparound Test for the Timer HAL Implementation. + * @brief Wraparound Test for the Timer HAL Implementation. * * @details The LTC values must wrap from 999999 µs to 0 µs and increase the * seconds counter accordingly. This test verifies the correct wrapping * by consecutively calling the #Timer_GetCounterValue function until * at least 2 wraparound events have been occurred. * - * @note This test requires the timer to basically run and return ascending - * values. Also, if the timer is too slow, this may take very long! - * Usually, the test takes 2 seconds, since 2 wraparound events are - * verified. + * @note This test requires the timer to basically run and return ascending + * values. Also, if the timer is too slow, this may take very long! + * Usually, the test takes 2 seconds, since 2 wraparound events are + * verified. * - * @warning This test does not test yet verify if the timing is correct at all! - * This it done in later test... + * @warning This test does not test yet verify if the timing is correct at all! + * This it done in later test... * - * @return Returns the \link #status_t status\endlink: - * - #STATUS_OK on success. - * - #ERROR_FAIL on failure (check the error log for more information). + * @return Returns the \link #status_t status\endlink: + * - #STATUS_OK on success. + * - #ERROR_FAIL on failure (check the error log for more information). *****************************************************************************/ static status_t TimerWraparoundTest(void) { /* Test parameter configuration: *****************************************/ - const int8_t n = 2; // The number of wraparounds to test. + const uint8_t n = 2; // The number of wraparounds to test. /*************************************************************************/ uint32_t hct0 = 0; @@ -297,14 +331,12 @@ static status_t TimerWraparoundTest(void) /* Check max value is not exceeded for LCT timer (us) */ status_t status = CheckTimerCounterValues(hct0, lct0); - if (status < STATUS_OK) { return status; } + if (status != STATUS_OK) { return status; } /* Set end after 2 seconds, i.e. 2 wrap around events. */ uint32_t hct2 = hct0 + n; uint32_t lct2 = lct0; - px4_usleep(20000); - /* Periodically read timer values. From previous tests we * already know the timer value is increasing. */ while (hct0 < hct2 || lct0 < lct2) { @@ -315,69 +347,92 @@ static status_t TimerWraparoundTest(void) /* Check max value is not exceeded for LCT timer (us) */ status = CheckTimerCounterValues(hct0, lct0); - if (status < STATUS_OK) { return status; } + if (status != STATUS_OK) { return status; } /* Testing if calls to Timer_GetCounterValue are equal or increasing. * Also testing if wraparound is correctly handled. * Assumption here is that two sequential calls to the get functions are - * only a few µs appart! I.e. if hct wraps, the new lct must be smaller + * only a few µs apart! I.e. if hct wraps, the new lct must be smaller * than previous one. */ if (!(((hct1 == hct0 + 1) && (lct1 < lct0)) || ((hct1 == hct0) && (lct1 >= lct0)))) { - PX4_INFO_RAW("Timer plausibility check: the wraparound of \"lct\" or " - "\"hct\" parameters of the Timer_GetCounterValue() " - "function was not handled correctly!\n" - "Current Values: hct0 = %d, lct0 = %d, hct1 = %d, lct1 = %d", - (uint)hct0, (uint)lct0, (uint)hct1, (uint)lct1); + error_log("Timer plausibility check: the wraparound of \"lct\" or " + "\"hct\" parameters of the Timer_GetCounterValue() " + "function was not handled correctly!\n" + "Current Values: hct0 = %d, lct0 = %d, hct1 = %d, lct1 = %d", + hct0, lct0, hct1, lct1); return ERROR_FAIL; } hct0 = hct1; lct0 = lct1; - - px4_usleep(20000); } return STATUS_OK; } /*!*************************************************************************** - * @brief Helper function for transfer data to SPI in blocking mode. + * @brief SPI interrupt callback function for the SPI transfer interrupt test. + * + * @details The interrupt callback is invoked from the S2PI module upon + * finishing the SPI transfer. The callback is used by the + * #SPITransferSync helper function to retrieve the status of the + * SPI transfer. + * + * @param status The S2PI module status passed to the callback. + * @param param The abstract interrupt callback parameter. + * + * @return Returns #STATUS_OK. + *****************************************************************************/ +static status_t SpiTransferInterruptCallback(status_t status, void *param) +{ + *((status_t *)param) = status; + return STATUS_OK; +} + +/*!*************************************************************************** + * @brief Helper function for transfer data to SPI in blocking mode. * * @details Calls the #S2PI_TransferFrame function and waits until the transfer - * has been finished by checking the #S2PI_GetStatus return code to - * become #STATUS_IDLE (or #STATUS_OK). + * has been finished by checking the #S2PI_GetStatus return code to + * become #STATUS_IDLE (or #STATUS_OK). * - * @warning The test utilizes already the timer HAL in order to implement a - * rudimentary timeout. However, at this time, only some basic - * plausibility checks are performed on the timer HAL. I.e. if there - * is an issue in the time HAL, e.g. too fast or too slow time - * counting, the test may fail with an #ERROR_TIMEOUT. In this case, - * one also needs to verify the timer HAL, especially the - * #Timer_GetCounterValue function. + * @warning The test utilizes already the timer HAL in order to implement a + * rudimentary timeout. However, at this time, only some basic + * plausibility checks are performed on the timer HAL. I.e. if there + * is an issue in the time HAL, e.g. too fast or too slow time + * counting, the test may fail with an #ERROR_TIMEOUT. In this case, + * one also needs to verify the timer HAL, especially the + * #Timer_GetCounterValue function. * - * @param slave The S2PI slave parameter passed to the S2PI HAL functions. - * @param data The data array to be transfered. - * @param size The size of the data array to be transfered. + * @param slave The S2PI slave parameter passed to the S2PI HAL functions. + * @param data The data array to be transferred. + * @param size The size of the data array to be transferred. * - * @return Returns the \link #status_t status\endlink: - * - #STATUS_OK on success. - * - #ERROR_TIMEOUT if the operation did not finished within a specified - * time (check also timer HAL implementation). - * - The S2PI layer error code if #S2PI_TransferFrame or #S2PI_GetStatus - * return any negative status. + * @return Returns the \link #status_t status\endlink: + * - #STATUS_OK on success. + * - #ERROR_TIMEOUT if the operation did not finished within a specified + * time (check also timer HAL implementation). + * - The S2PI layer error code if #S2PI_TransferFrame or #S2PI_GetStatus + * return any negative status. *****************************************************************************/ -static status_t SPITransferSync(s2pi_slave_t slave, uint8_t *data, uint8_t size) +static status_t SPITransferSync(s2pi_slave_t slave, uint8_t *data, size_t size) { /* Test parameter configuration: *****************************************/ - const uint32_t timeout_ms = 100; // The transfer timeout in ms. + const uint32_t timeout_ms = 100; // The transfer timeout in ms. /*************************************************************************/ - status_t status = S2PI_TransferFrame(slave, data, data, size, 0, 0); + /* The status will be changed in the SPI callback. */ + volatile status_t callbackStatus = STATUS_BUSY; - if (status < STATUS_OK) { - PX4_INFO_RAW("SPI transfer failed! The call to S2PI_TransferFrame " - "yielded error code: %d", (int)status); + status_t status = S2PI_TransferFrame(slave, data, data, size, + SpiTransferInterruptCallback, + (void *)&callbackStatus); + + if (status != STATUS_OK) { + error_log("SPI transfer failed! The call to S2PI_TransferFrame " + "yielded error code: %d", + status); return status; } @@ -388,53 +443,67 @@ static status_t SPITransferSync(s2pi_slave_t slave, uint8_t *data, uint8_t size) Time_GetNow(&start); do { - status = S2PI_GetStatus(); + status = S2PI_GetStatus(slave); if (status < STATUS_OK) { - PX4_INFO_RAW("SPI transfer failed! The call to S2PI_GetStatus " - "yielded error code: %d", (int)status); - S2PI_Abort(); + error_log("SPI transfer failed! The call to S2PI_GetStatus " + "yielded error code: %d", status); + S2PI_Abort(slave); return status; } if (Time_CheckTimeoutMSec(&start, timeout_ms)) { - PX4_INFO_RAW("SPI transfer failed! The operation did not finished " - "within %u ms. This may also be caused by an invalid " - "timer implementation!", (uint)timeout_ms); + error_log("SPI transfer failed! The operation did not finished " + "within %d ms. This may also be caused by an invalid " + "timer implementation!", timeout_ms); return ERROR_TIMEOUT; } } while (status == STATUS_BUSY); + if (callbackStatus != STATUS_OK) { + error_log("Invocation of the SPI callback failed! The SPI transfer " + "callback yielded error code: %d", callbackStatus); + return callbackStatus; + } + return status; } /*!*************************************************************************** - * @brief SPI Connection Test for S2PI HAL Implementation. + * @brief SPI Connection Test for S2PI HAL Implementation. * * @details This test verifies the basic functionality of the SPI interface. - * The test utilizes the devices laser pattern register, which can - * be freely programmed by any 128-bit pattern. Thus, it writes a byte - * sequence and reads back the written values on the consecutive SPI - * access. * - * @warning The test utilizes already the timer HAL in order to implement a - * rudimentary timeout. However, at this time, only some basic - * plausibility checks are performed on the timer HAL. I.e. if there - * is an issue in the time HAL, e.g. too fast or too slow time - * counting, the test may fail with an #ERROR_TIMEOUT. In this case, - * one also needs to verify the timer HAL, especially the - * #Timer_GetCounterValue function. + * The test utilizes the devices laser pattern register, which can + * be freely programmed by any 128-bit pattern. Thus, it writes a byte + * sequence and reads back the written values on the consecutive SPI + * access. * - * @param slave The S2PI slave parameter passed to the S2PI HAL functions. + * Note: The test verifies the SPI interface transfer functionality + * in blocking mode and also verifies the interrupt callback. + * In order to wait for the transfer to finish, it reads the S2PI + * status in a loop. If the status does not change to #STATUS_IDLE, + * the test will fail with an #ERROR_TIMEOUT. Finally, the test will + * verify the SPI transfer callback status. * - * @return Returns the \link #status_t status\endlink: - * - #STATUS_OK on success. - * - #ERROR_TIMEOUT if the operation did not finished within a specified - * time (check also timer HAL implementation). - * - #ERROR_FAIL if the device access failed and the read data did not - * match the expected values. - * - The S2PI layer error code if #S2PI_TransferFrame or #S2PI_GetStatus - * return any negative status. + * @warning The test utilizes already the timer HAL in order to implement a + * rudimentary timeout. However, at this time, only some basic + * plausibility checks are performed on the timer HAL. I.e. if there + * is an issue in the time HAL, e.g. too fast or too slow time + * counting, the test may fail with an #ERROR_TIMEOUT. In this case, + * one also needs to verify the timer HAL, especially the + * #Timer_GetCounterValue function. + * + * @param slave The S2PI slave parameter passed to the S2PI HAL functions. + * + * @return Returns the \link #status_t status\endlink: + * - #STATUS_OK on success. + * - #ERROR_TIMEOUT if the operation did not finished within a specified + * time (check also timer HAL implementation). + * - #ERROR_FAIL if the device access failed and the read data did not + * match the expected values. + * - The S2PI layer error code if #S2PI_TransferFrame, #S2PI_GetStatus + * or the SPI callback yield any negative status. *****************************************************************************/ static status_t SpiConnectionTest(s2pi_slave_t slave) { @@ -448,8 +517,8 @@ static status_t SpiConnectionTest(s2pi_slave_t slave) status = SPITransferSync(slave, data, 17U); - if (status < STATUS_OK) { - PX4_INFO_RAW("SPI connection test failed!"); + if (status != STATUS_OK) { + error_log("SPI connection test failed!"); return status; } @@ -460,18 +529,18 @@ static status_t SpiConnectionTest(s2pi_slave_t slave) status = SPITransferSync(slave, data, 17U); - if (status < STATUS_OK) { - PX4_INFO_RAW("SPI connection test failed!"); + if (status != STATUS_OK) { + error_log("SPI connection test failed!"); return status; } /* Verify the read pattern. */ for (uint8_t i = 1; i < 17U; ++i) { if (data[i] != i) { - PX4_INFO_RAW("SPI connection test failed!\n" - "Verification of read data is invalid!\n" - "read_data[%d] = %d, but expected was %d", - i, data[i], i); + error_log("SPI connection test failed!\n" + "Verification of read data is invalid!\n" + "read_data[%d] = %d, but expected was %d", + i, data[i], i); return ERROR_FAIL; } } @@ -479,142 +548,236 @@ static status_t SpiConnectionTest(s2pi_slave_t slave) return STATUS_OK; } - /*!*************************************************************************** - * @brief The data ready callback invoked by the API. + * @brief Maximum SPI Data Size Test for S2PI HAL Implementation. * - * @details The callback is invoked by the API when the device GPIO IRQ is - * pending after a measurement has been executed and data is ready to - * be read from the device. + * @details This test verifies the maximum data transfer length of the SPI + * interface. The test sends and receives up to 396 data bytes plus + * a single address byte over the SPI interface and verifies that no + * data get lost. * - * @param param The abstract pointer to the boolean value that determines if - * the callback is invoked. + * The test utilizes the channel select register which is 3 bytes plus + * address. This register can be repeatedly written with any pattern + * using the DMA mode. The register is written 100 times in a row + * to verify that long data frames with up to 400 bytes can be + * transmitted. + * + * Note that this test was motivated by an invalid implementation that + * used uint8_t type for the frame length in the #S2PI_TransferFrame + * function instead of an uint16_t value. This resulted in a maximum + * data length of 141 bytes (367 & 0xFF = 141) when reading the + * data value register. + * + * @warning The test utilizes already the timer HAL in order to implement a + * rudimentary timeout. However, at this time, only some basic + * plausibility checks are performed on the timer HAL. I.e. if there + * is an issue in the time HAL, e.g. too fast or too slow time + * counting, the test may fail with an #ERROR_TIMEOUT. In this case, + * one also needs to verify the timer HAL, especially the + * #Timer_GetCounterValue function. + * + * @param slave The S2PI slave parameter passed to the S2PI HAL functions. + * + * @return Returns the \link #status_t status\endlink: + * - #STATUS_OK on success. + * - #ERROR_TIMEOUT if the operation did not finished within a specified + * time (check also timer HAL implementation). + * - #ERROR_FAIL if the device access failed and the read data did not + * match the expected values. + * - The S2PI layer error code if #S2PI_TransferFrame or #S2PI_GetStatus + * return any negative status. *****************************************************************************/ -static void DataReadyCallback(void *param) +static status_t SpiMaxLengthTest(s2pi_slave_t slave) { - IRQ_LOCK(); - *((bool *) param) = true; - IRQ_UNLOCK(); + status_t status = STATUS_OK; + uint8_t data[400U] = { 0 }; + + /* Setup device (enable DMA mode). */ + data[0] = 0x10; data[1] = 0x12; + status = SPITransferSync(slave, data, 2); + + if (status != STATUS_OK) { + error_log("Device configuration failed!"); + return status; + } + + data[0] = 0x12; data[1] = 0x00; data[2] = 0x2B; + status = SPITransferSync(slave, data, 3); + + if (status != STATUS_OK) { + error_log("Device configuration failed!"); + return status; + } + + /* Transfer a pattern to the register */ + for (uint32_t i = 0; i < sizeof(data); i += 4) { + data[i + 0] = 0x1E; // Address + data[i + 1] = (uint8_t)i; // Random Data Byte 0 + data[i + 2] = (uint8_t)(i + 1); // Random Data Byte 1 + data[i + 3] = (uint8_t)(i * 2); // Random Data Byte 2 + } + + status = SPITransferSync(slave, data, sizeof(data)); + + if (status != STATUS_OK) { + error_log("SPI maximum data length test failed!"); + return status; + } + + /* Repeat ... */ + for (uint32_t i = 0; i < sizeof(data); i += 4) { + data[i + 0] = 0x1E; // Address + data[i + 1] = (uint8_t)i; // Random Data Byte 0 + data[i + 2] = (uint8_t)(i + 1); // Random Data Byte 1 + data[i + 3] = (uint8_t)(i * 2); // Random Data Byte 2 + } + + status = SPITransferSync(slave, data, sizeof(data)); + + if (status != STATUS_OK) { + error_log("SPI maximum data length test failed!"); + return status; + } + + /* Verify the read pattern; skip all address bytes. */ + for (uint32_t i = 0; i < sizeof(data); i += 4) { + uint32_t j = (i + 4) % sizeof(data); + + if (data[j + 1] != (uint8_t)i + || data[j + 2] != (uint8_t)(i + 1) + || data[j + 3] != (uint8_t)(i * 2)) { + error_log("SPI maximum data length test failed!\n" + "Verification of read data is invalid at byte %d!\n" + " - expected: 0x%02X%02X%02X\n" + " - actual: 0x%02X%02X%02X", + i, (uint8_t)i, (uint8_t)(i + 1), (uint8_t)(i * 2), + data[j + 1], data[j + 2], data[j + 3]); + return ERROR_FAIL; + } + } + + return STATUS_OK; } /*!*************************************************************************** - * @brief Configures the device with a bare minimum setup to run the tests. + * @brief Configures the device with a bare minimum setup to run the tests. * - * @details This function applies a number of configuration values to the - * device, such that a pseudo measurement w/o laser output can be - * performed. + * @details This function applies a number of configuration values to the + * device, such that a pseudo measurement w/o laser output can be + * performed. * - * A \p rcoTrim parameter can be passed to adjust the actual clock - * setup. + * A \p rcoTrim parameter can be passed to adjust the actual clock + * setup. * - * @warning The test utilizes already the timer HAL in order to implement a - * rudimentary timeout. However, at this time, only some basic - * plausibility checks are performed on the timer HAL. I.e. if there - * is an issue in the time HAL, e.g. too fast or too slow time - * counting, the test may fail with an #ERROR_TIMEOUT. In this case, - * one also needs to verify the timer HAL, especially the - * #Timer_GetCounterValue function. + * @warning The test utilizes already the timer HAL in order to implement a + * rudimentary timeout. However, at this time, only some basic + * plausibility checks are performed on the timer HAL. I.e. if there + * is an issue in the time HAL, e.g. too fast or too slow time + * counting, the test may fail with an #ERROR_TIMEOUT. In this case, + * one also needs to verify the timer HAL, especially the + * #Timer_GetCounterValue function. * - * @param slave The S2PI slave parameter passed to the S2PI HAL functions. - * @param rcoTrim The RCO Trimming value added to the nominal RCO register - * value. Pass 0 if no fine tuning is required. + * @param slave The S2PI slave parameter passed to the S2PI HAL functions. + * @param rcoTrim The RCO Trimming value added to the nominal RCO register + * value. Pass 0 if no fine tuning is required. * - * @return Returns the \link #status_t status\endlink: - * - #STATUS_OK on success. - * - #ERROR_TIMEOUT if the SPI operation did not finished within a - * specified time (check also timer HAL implementation). - * - The S2PI layer error code if #S2PI_TransferFrame or #S2PI_GetStatus - * return any negative status. + * @return Returns the \link #status_t status\endlink: + * - #STATUS_OK on success. + * - #ERROR_TIMEOUT if the SPI operation did not finished within a + * specified time (check also timer HAL implementation). + * - The S2PI layer error code if #S2PI_TransferFrame or #S2PI_GetStatus + * return any negative status. *****************************************************************************/ static status_t ConfigureDevice(s2pi_slave_t slave, int8_t rcoTrim) { /* Setup Device and Trigger Measurement. */ - uint16_t v = 0x0010U | (((34 + rcoTrim) & 0x3F) << 6); - uint8_t d1[] = { 0x14, v >> 8, v & 0xFF, 0x21 }; + assert(rcoTrim >= -34 && rcoTrim < 0x3F - 34); + const uint16_t v = (uint16_t)(0x0010U | (((uint16_t)(34 + rcoTrim) & 0x3F) << 6U)); + uint8_t d1[] = { 0x14U, (uint8_t)(v >> 8U), v & 0xFFU, 0x21U }; status_t status = SPITransferSync(slave, d1, sizeof(d1)); - if (status < STATUS_OK) { - PX4_INFO_RAW("Device configuration failed!"); + if (status != STATUS_OK) { + error_log("Device configuration failed!"); return status; } uint8_t d2[] = { 0x16, 0x7F, 0xFF, 0x7F, 0xE9 }; status = SPITransferSync(slave, d2, sizeof(d2)); - if (status < STATUS_OK) { - PX4_INFO_RAW("Device configuration failed!"); + if (status != STATUS_OK) { + error_log("Device configuration failed!"); return status; } uint8_t d3[] = { 0x18, 0x00, 0x00, 0x03 }; status = SPITransferSync(slave, d3, sizeof(d3)); - if (status < STATUS_OK) { - PX4_INFO_RAW("Device configuration failed!"); + if (status != STATUS_OK) { + error_log("Device configuration failed!"); return status; } uint8_t d4[] = { 0x10, 0x12 }; status = SPITransferSync(slave, d4, sizeof(d4)); - if (status < STATUS_OK) { - PX4_INFO_RAW("Device configuration failed!"); + if (status != STATUS_OK) { + error_log("Device configuration failed!"); return status; } uint8_t d5[] = { 0x12, 0x00, 0x2B }; status = SPITransferSync(slave, d5, sizeof(d5)); - if (status < STATUS_OK) { - PX4_INFO_RAW("Device configuration failed!"); + if (status != STATUS_OK) { + error_log("Device configuration failed!"); return status; } uint8_t d6[] = { 0x08, 0x04, 0x84, 0x10 }; status = SPITransferSync(slave, d6, sizeof(d6)); - if (status < STATUS_OK) { - PX4_INFO_RAW("Device configuration failed!"); + if (status != STATUS_OK) { + error_log("Device configuration failed!"); return status; } uint8_t d7[] = { 0x0A, 0xFE, 0x51, 0x0F, 0x05 }; status = SPITransferSync(slave, d7, sizeof(d7)); - if (status < STATUS_OK) { - PX4_INFO_RAW("Device configuration failed!"); + if (status != STATUS_OK) { + error_log("Device configuration failed!"); return status; } uint8_t d8[] = { 0x0C, 0x00, 0x00, 0x00 }; status = SPITransferSync(slave, d8, sizeof(d8)); - if (status < STATUS_OK) { - PX4_INFO_RAW("Device configuration failed!"); + if (status != STATUS_OK) { + error_log("Device configuration failed!"); return status; } uint8_t d9[] = { 0x1E, 0x00, 0x00, 0x00 }; status = SPITransferSync(slave, d9, sizeof(d9)); - if (status < STATUS_OK) { - PX4_INFO_RAW("Device configuration failed!"); + if (status != STATUS_OK) { + error_log("Device configuration failed!"); return status; } uint8_t d10[] = { 0x20, 0x01, 0xFF, 0xFF }; status = SPITransferSync(slave, d10, sizeof(d10)); - if (status < STATUS_OK) { - PX4_INFO_RAW("Device configuration failed!"); + if (status != STATUS_OK) { + error_log("Device configuration failed!"); return status; } uint8_t d11[] = { 0x22, 0xFF, 0xFF, 0x04 }; status = SPITransferSync(slave, d11, sizeof(d11)); - if (status < STATUS_OK) { - PX4_INFO_RAW("Device configuration failed!"); + if (status != STATUS_OK) { + error_log("Device configuration failed!"); return status; } @@ -622,40 +785,55 @@ static status_t ConfigureDevice(s2pi_slave_t slave, int8_t rcoTrim) } /*!*************************************************************************** - * @brief Triggers a measurement on the device with specified sample count. + * @brief Triggers a measurement on the device with specified sample count. * * @details The function triggers a measurement cycle on the device. A - * \p sample count can be specified to setup individual number of - * digital averaging. + * \p sample count can be specified to setup individual number of + * digital averaging. * - * @warning The test utilizes already the timer HAL in order to implement a - * rudimentary timeout. However, at this time, only some basic - * plausibility checks are performed on the timer HAL. I.e. if there - * is an issue in the time HAL, e.g. too fast or too slow time - * counting, the test may fail with an #ERROR_TIMEOUT. In this case, - * one also needs to verify the timer HAL, especially the - * #Timer_GetCounterValue function. + * The measurement in triggered asynchronously without waiting + * for any event to finish. * - * @param slave The S2PI slave parameter passed to the S2PI HAL functions. - * @param samples The specified number of averaging samples for the measurement. + * @warning The test utilizes already the timer HAL in order to implement a + * rudimentary timeout. However, at this time, only some basic + * plausibility checks are performed on the timer HAL. I.e. if there + * is an issue in the time HAL, e.g. too fast or too slow time + * counting, the test may fail with an #ERROR_TIMEOUT. In this case, + * one also needs to verify the timer HAL, especially the + * #Timer_GetCounterValue function. * - * @return Returns the \link #status_t status\endlink: - * - #STATUS_OK on success. - * - #ERROR_TIMEOUT if the operation did not finished within a specified - * time (check also timer HAL implementation). - * - The S2PI layer error code if #S2PI_TransferFrame or #S2PI_GetStatus - * return any negative status. + * @param slave The S2PI slave parameter passed to the S2PI HAL functions. + * @param samples The specified number of averaging samples for the measurement. + * @param callback An optional SPI callback. + * @param callbackData The optional callback data parameter. + * + * @return Returns the \link #status_t status\endlink: + * - #STATUS_OK on success. + * - #ERROR_TIMEOUT if the operation did not finished within a specified + * time (check also timer HAL implementation). + * - The S2PI layer error code if #S2PI_TransferFrame or #S2PI_GetStatus + * return any negative status. *****************************************************************************/ -static status_t TriggerMeasurement(s2pi_slave_t slave, uint16_t samples) +static status_t TriggerMeasurement(s2pi_slave_t slave, uint16_t samples, + s2pi_callback_t callback, void *callbackData) { // samples is zero based, i.e. writing 0 yields 1 sample samples = samples > 0 ? samples - 1 : samples; - uint16_t v = 0x8000U | ((samples & 0x03FFU) << 5U); - uint8_t d[] = { 0x1C, v >> 8, v & 0xFFU }; - status_t status = SPITransferSync(slave, d, sizeof(d)); + const uint16_t v = (uint16_t)(0x8000U | ((samples & 0x03FFU) << 5U)); - if (status < STATUS_OK) { - PX4_INFO_RAW("Trigger measurement failed!"); + // data is static as the transfer is asynchronous and the buffer must persist. + static uint8_t data[] = { 0x1CU, 0x00U, 0x00U }; + data[0] = 0x1CU; + data[1] = (uint8_t)(v >> 8U); + data[2] = v & 0xFFU; + + status_t status = S2PI_TransferFrame(slave, data, data, sizeof(data), + callback, callbackData); + + if (status != STATUS_OK) { + error_log("SPI transfer failed to trigger measurements! " + "The call to S2PI_TransferFrame yielded error code: %d", + status); return status; } @@ -663,162 +841,263 @@ static status_t TriggerMeasurement(s2pi_slave_t slave, uint16_t samples) } /*!*************************************************************************** - * @brief Waits for the data ready interrupt to be pending. + * @brief Data structure for the GPIO interrupt test. * - * @details The function polls the current interrupt pending state of the data - * ready interrupt from the device, i.e. reads the IRQ GPIO pin until - * it is pulled to low by the device. - * - * - * @warning The test utilizes already the timer HAL in order to implement a - * rudimentary timeout. However, at this time, only some basic - * plausibility checks are performed on the timer HAL. I.e. if there - * is an issue in the time HAL, e.g. too fast or too slow time - * counting, the test may fail with an #ERROR_TIMEOUT. In this case, - * one also needs to verify the timer HAL, especially the - * #Timer_GetCounterValue function. - * - * @param slave The S2PI slave parameter passed to the S2PI HAL functions. - * @param timeout_ms The timeout to cancel waiting for the IRQ. - * - * @return Returns the \link #status_t status\endlink: - * - #STATUS_OK on success. - * - #ERROR_TIMEOUT if either the SPI operation did not finished - * or the IRQ was not detected within a specified time (check also - * timer HAL implementation). - * - The S2PI layer error code if #S2PI_TransferFrame, #S2PI_GetStatus - * or #S2PI_SetIrqCallback return any negative status. + * @details Contains data that is required by the GPIO interrupt test. *****************************************************************************/ -static status_t AwaitDataReady(s2pi_slave_t slave, uint32_t timeout_ms) -{ - ltc_t start; - Time_GetNow(&start); +typedef struct gpio_data_t { + /* The S2PI slave parameter passed to the S2PI HAL functions. */ + s2pi_slave_t Slave; - while (S2PI_ReadIrqPin(slave)) { - if (Time_CheckTimeoutMSec(&start, timeout_ms)) { - PX4_INFO_RAW("SPI interrupt test failed! The S2PI_ReadIrqPin did not " - "determine an pending interrupt within %u ms.", (uint)timeout_ms); - return ERROR_TIMEOUT; - } + /* The callback status. */ + volatile status_t Status; + + /* The GPIO timeout in milliseconds. */ + uint32_t Timeout_ms; + + /* A counter to determine how often the callback is invoked. */ + volatile uint32_t CallbackInvoked; + + /* The return value of the #S2PI_ReadIrqPin function. */ + volatile uint32_t ReadIrqPinValue; + +} gpio_data_t; + +/*!*************************************************************************** + * @brief The IRQ callback dedicated to the #GpioInterruptTest. + * + * @details The callback is invoked by the API when the device GPIO IRQ is + * pending after a measurement has been executed and data is ready to + * be read from the device. + * + * @param param The abstract pointer to the boolean value that determines if + * the callback is invoked. + *****************************************************************************/ +static void GPIO_Callback(void *param) +{ + if (param == NULL) { + error_log("GPIO interrupt test failed: callback parameter \"param\" was NULL!"); + return; } + gpio_data_t *data = (gpio_data_t *)param; + data->CallbackInvoked = 1; +} + +/*!*************************************************************************** + * @brief The SPI transfer callback dedicated to the #GpioInterruptTest. + * + * @details The callback is invoked by the S2PI layer when the SPI transfer + * finished IRQ is invoked. The callback is used to simulate a + * deferred GPIO interrupt by locking the interrupts until the + * #S2PI_ReadIrqPin detects an GPIO interrupt pending state and + * returns 0. + * + * @param status The status of the SPI transfer. + * @param param The abstract pointer to the boolean value that determines if + * the callback is invoked. + * + * @return Returns the \link #status_t status\endlink: + * - #STATUS_OK on success. + * - #ERROR_FAIL if the \p param parameter is NULL. + * - #ERROR_TIMEOUT if the #S2PI_ReadIrqPin does not return 0 after + * a specified time (check also timer HAL implementation). + * - The S2PI layer error code that may be received from the S2PI + * module via the \p status parameter. + *****************************************************************************/ +static status_t GPIO_SPI_Callback(status_t status, void *param) +{ + IRQ_LOCK(); // prevents GPIO interrupt to preempt if set to higher priority. + + if (param == NULL) { + IRQ_UNLOCK(); + error_log("GPIO interrupt test failed: callback parameter \"param\" was NULL!"); + return ERROR_FAIL; + } + + gpio_data_t *data = (gpio_data_t *)param; + + if (status != STATUS_OK) { + IRQ_UNLOCK(); + error_log("GPIO interrupt test failed: callback parameter \"status\" was %d!", + status); + data->Status = status; + return status; + } + + /* The S2PI_ReadIrqPin must correctly return the GPIO IRQ state if the GPIO + * interrupt is pending but deferred due to any higher priority or critical + * sections. Therefore, the SPI callback with the #IRQ_LOCK/#IRQ_UNLOCK is + * used to delay the GPIO callback and test the #S2PI_ReadIrqPin function. + * + * The purpose is to simulate a delayed GPIO interrupt that can in the + * production code happen due to any higher priority interrupts (such as + * the SPI interrupt in this test). In those cases, the API relies on the + * #S2PI_ReadIrqPin method to obtain if the device has finished in time and + * the interrupt is already pending. Otherwise, it would fail with an + * timeout due to the deferred GPIO interrupt callback event. */ + + ltc_t start; + Time_GetNow(&start); + data->ReadIrqPinValue = S2PI_ReadIrqPin(data->Slave); + + while (data->ReadIrqPinValue) { + if (Time_CheckTimeoutMSec(&start, data->Timeout_ms)) { + IRQ_UNLOCK(); + error_log("GPIO interrupt test failed! The IRQ pin did not assert " + "to low state when reading from the IRQ callback. " + "Elapsed %d ms.", data->Timeout_ms); + data->Status = ERROR_TIMEOUT; + return ERROR_TIMEOUT; + } + + data->ReadIrqPinValue = S2PI_ReadIrqPin(data->Slave); + } + + IRQ_UNLOCK(); + data->Status = STATUS_OK; return STATUS_OK; } /*!*************************************************************************** - * @brief SPI Interrupt Test for S2PI HAL Implementation. + * @brief SPI Interrupt Test for S2PI HAL Implementation. * - * @details This test verifies the correct implementation of the device - * integration finished interrupt callback. Therefore it configures - * the device with a minimal setup to run a pseudo measurement that - * does not emit any laser light. + * @details This test verifies the correct implementation of the device + * integration finished interrupt callback, a.k.a. the GPIO interrupt. + * Therefore it configures the device with a minimal setup to run a + * pseudo measurement that does not emit any laser light but triggers + * an GPIO interrupt once finished. * - * Note that this test does verify the GPIO interrupt that occurs - * whenever the device has finished the integration/measurement and - * new data is waiting to be read from the device. This does not test - * the interrupt that is triggered when the SPI transfer has finished. + * The data ready interrupt implies two S2PI layer functions that + * are tested in this test: The #S2PI_SetIrqCallback function installs + * a callback function that is invoked whenever the IRQ occurs and + * the #S2PI_ReadIrqPin function to obtain the pending interrupt state. * - * The data ready interrupt implies two S2PI layer functions that - * are tested in this test: The #S2PI_SetIrqCallback function installs - * a callback function that is invoked whenever the IRQ occurs. - * The IRQ can be delayed due to higher priority task, e.g. from the - * user code. It is essential for the laser safety timeout algorithm - * to determine the device ready signal as fast as possible, another - * method is implemented to read if the IRQ is pending but the - * callback has not been reset yet. This is what the #S2PI_ReadIrqPin - * function is for. + * The IRQ can be delayed due to higher priority task, e.g. from the + * user code. It is essential for the laser safety timeout algorithm + * to determine the device ready signal as fast as possible. Thus a + * method is required to obtain if the IRQ is currently pending but + * the callback has not been invoked yet. This is what the + * #S2PI_ReadIrqPin function is for. Note that the #S2PI_ReadIrqPin + * must return 0 if not interrupt is pending and 1 else. Just like + * the IRQ pin is active low. * + * The test simulate a delayed GPIO interrupt by locking the interrupts + * until the #S2PI_ReadIrqPin detects an GPIO interrupt pending state + * and returns 0. This is done by the #GPIO_SPI_Callback function. + * + * Note that this test does verify the GPIO interrupt that occurs + * whenever the device has finished the integration/measurement and + * new data is waiting to be read from the device. This does not test + * the interrupt that is triggered when the SPI transfer has finished. * * @warning The test assumes the device is in a fresh power on state and no - * additional reset is required. If the test fail, one may want to - * power cycle the device and try again. + * additional reset is required. If the test fail, one may want to + * power cycle the device and try again. * - * @warning The test utilizes already the timer HAL in order to implement a - * rudimentary timeout. However, at this time, only some basic - * plausibility checks are performed on the timer HAL. I.e. if there - * is an issue in the time HAL, e.g. too fast or too slow time - * counting, the test may fail with an #ERROR_TIMEOUT. In this case, - * one also needs to verify the timer HAL, especially the - * #Timer_GetCounterValue function. + * @warning The test locks the interrupts for a quite long period of time in + * order to simulate a delayed GPIO interrupt. This is not a good + * practice in production code. However, it is required to test the + * #S2PI_ReadIrqPin function. Please be aware of that when you run + * this test. * - * @param slave The S2PI slave parameter passed to the S2PI HAL functions. + * @warning The test utilizes already the timer HAL in order to implement a + * rudimentary timeout. However, at this time, only some basic + * plausibility checks are performed on the timer HAL. I.e. if there + * is an issue in the time HAL, e.g. too fast or too slow time + * counting, the test may fail with an #ERROR_TIMEOUT. In this case, + * one also needs to verify the timer HAL, especially the + * #Timer_GetCounterValue function. * - * @return Returns the \link #status_t status\endlink: - * - #STATUS_OK on success. - * - #ERROR_TIMEOUT if either the SPI operation did not finished - * or the IRQ was not detected within a specified time (check also - * timer HAL implementation). - * - #ERROR_FAIL if the IRQ pin readout failed and the no or invalid - * interrupt was detected. - * - The S2PI layer error code if #S2PI_TransferFrame, #S2PI_GetStatus - * or #S2PI_SetIrqCallback return any negative status. + * @param slave The S2PI slave parameter passed to the S2PI HAL functions. + * + * @return Returns the \link #status_t status\endlink: + * - #STATUS_OK on success. + * - #ERROR_TIMEOUT if either the SPI operation did not finished + * or the IRQ was not detected within a specified time (check also + * timer HAL implementation). + * - #ERROR_FAIL if the IRQ pin readout failed and the no or invalid + * interrupt was detected. + * - The S2PI layer error code if #S2PI_TransferFrame, #S2PI_GetStatus + * or #S2PI_SetIrqCallback return any negative status. *****************************************************************************/ -static status_t SpiInterruptTest(s2pi_slave_t slave) +static status_t GpioInterruptTest(s2pi_slave_t slave) { /* Test parameter configuration: *****************************************/ const uint32_t timeout_ms = 300; // timeout for measurement, might be increased.. /*************************************************************************/ - /* Install IRQ callback. */ - volatile bool isDataReady = false; - status_t status = S2PI_SetIrqCallback(slave, DataReadyCallback, (void *)&isDataReady); + gpio_data_t data = { .Slave = slave, + .Status = ERROR_FAIL, + .Timeout_ms = timeout_ms, + .ReadIrqPinValue = 12345, + .CallbackInvoked = 0 + }; - if (status < STATUS_OK) { - PX4_INFO_RAW("SPI interrupt test failed! The call to S2PI_SetIrqCallback " - "yielded error code: %d", (int)status); + /* Install IRQ callback. */ + status_t status = S2PI_SetIrqCallback(slave, GPIO_Callback, &data); + + if (status != STATUS_OK) { + error_log("GPIO interrupt test failed! The call to S2PI_SetIrqCallback " + "yielded error code: %d", status); + return status; + } + + /* Setup Device. */ + status = ConfigureDevice(slave, 0); + + if (status != STATUS_OK) { + error_log("GPIO interrupt test failed!"); return status; } /* Check if IRQ is not yet pending. */ if (S2PI_ReadIrqPin(slave) == 0) { - PX4_INFO_RAW("SPI interrupt test failed! The S2PI_ReadIrqPin did " - "return 0 but no interrupt is pending since no " - "measurements are executed yet!"); + error_log("GPIO interrupt test failed! The S2PI_ReadIrqPin did " + "return 0 but no interrupt is pending since no " + "measurements are executed yet!"); return ERROR_FAIL; }; - /* Setup Device. */ - status = ConfigureDevice(slave, 0); - - if (status < STATUS_OK) { - PX4_INFO_RAW("SPI interrupt test failed!"); - return status; - } - /* Trigger Measurement. */ - status = TriggerMeasurement(slave, 0); + status = TriggerMeasurement(slave, 0, GPIO_SPI_Callback, &data); - if (status < STATUS_OK) { - PX4_INFO_RAW("SPI interrupt test failed!"); - return status; - } - - ltc_t start; - Time_GetNow(&start); - - /* Wait for Interrupt using the S2PI_ReadIrqPin method. */ - status = AwaitDataReady(slave, timeout_ms); - - if (status < STATUS_OK) { - PX4_INFO_RAW("SPI interrupt test failed!"); + if (status != STATUS_OK) { + error_log("GPIO interrupt test failed!"); return status; } /* Wait for Interrupt using the callback method. */ - while (!isDataReady) { + ltc_t start; + Time_GetNow(&start); + + while (!data.CallbackInvoked) { if (Time_CheckTimeoutMSec(&start, timeout_ms)) { - PX4_INFO_RAW("SPI interrupt test failed! The IRQ callback was not " - "invoked within %u ms.", (uint)timeout_ms); + error_log("GPIO interrupt test failed! The IRQ callback was not " + "invoked within %d ms.", timeout_ms); return ERROR_TIMEOUT; } } + /* Verify ... */ + if (data.Status != STATUS_OK) { + error_log("GPIO interrupt test failed! The SPI IRQ callback yielded " + "an error status: %d (expected 0)", data.Status); + return ERROR_FAIL; + } + + if (data.ReadIrqPinValue != 0) { + error_log("GPIO interrupt test failed! The IRQ pin returned " + "the wrong value: %d (expected 0)", data.ReadIrqPinValue); + return ERROR_FAIL; + } + /* Remove callback. */ status = S2PI_SetIrqCallback(slave, 0, 0); - if (status < STATUS_OK) { - PX4_INFO_RAW("SPI interrupt test failed! The call to S2PI_SetIrqCallback " - "with null pointers yielded error code: %d", (int)status); + if (status != STATUS_OK) { + error_log("GPIO interrupt test failed! The call to S2PI_SetIrqCallback " + "with null pointers yielded error code: %d", status); return status; } @@ -826,28 +1105,28 @@ static status_t SpiInterruptTest(s2pi_slave_t slave) } /*!*************************************************************************** - * @brief Reads the EEPROM bytewise and applies Hamming weight. - * @details The EEPROM bytes are consecutevly read from the device via GPIO mode. - * The #EEPROM_Read function is an internal API function that enables - * the GPIO mode from the S2PI module and reads the data via a software - * bit-banging protocol. Finally it disables the GPIO mode and returns - * to SPI mode. + * @brief Reads the EEPROM byte-wise and applies Hamming weight. + * @details The EEPROM bytes are consecutively read from the device via GPIO mode. + * The EEPROM_Read function is an internal API function that enables + * the GPIO mode from the S2PI module and reads the data via a software + * bit-banging protocol. Finally it disables the GPIO mode and returns + * to SPI mode. * - * The calls to S2PI HAL module is as follows: - * 1. S2PI_CaptureGpioControl - * 2. multiple calls to S2PI_WriteGpioPin and S2PI_ReadGpioPin - * 3. S2PI_ReleaseGpioControl + * The calls to S2PI HAL module is as follows: + * 1. S2PI_CaptureGpioControl + * 2. multiple calls to S2PI_WriteGpioPin and S2PI_ReadGpioPin + * 3. S2PI_ReleaseGpioControl * - * @param slave The S2PI slave parameter passed to the S2PI HAL functions. - * @param eeprom The 16 byte array to be filled with EEPROM data. + * @param slave The S2PI slave parameter passed to the S2PI HAL functions. + * @param eeprom The 16 byte array to be filled with EEPROM data. * - * @return Returns the \link #status_t status\endlink: - * - #STATUS_OK on success. - * - #STATUS_ARGUS_EEPROM_BIT_ERROR if the Hamming weight fails. - * interrupt was detected. - * - The S2PI layer error code if #S2PI_CaptureGpioControl, - * #S2PI_ReleaseGpioControl, #S2PI_WriteGpioPin or - * #S2PI_ReadGpioPin return any negative status. + * @return Returns the \link #status_t status\endlink: + * - #STATUS_OK on success. + * - #STATUS_ARGUS_EEPROM_BIT_ERROR if the Hamming weight fails. + * interrupt was detected. + * - The S2PI layer error code if #S2PI_CaptureGpioControl, + * #S2PI_ReleaseGpioControl, #S2PI_WriteGpioPin or + * #S2PI_ReadGpioPin return any negative status. *****************************************************************************/ static status_t ReadEEPROM(s2pi_slave_t slave, uint8_t *eeprom) { @@ -855,9 +1134,9 @@ static status_t ReadEEPROM(s2pi_slave_t slave, uint8_t *eeprom) uint8_t d1[] = { 0x12, 0x00, 0x4B }; status_t status = SPITransferSync(slave, d1, sizeof(d1)); - if (status < STATUS_OK) { - PX4_INFO_RAW("EEPROM readout failed (enable EEPROM), " - "error code: %d", (int)status); + if (status != STATUS_OK) { + error_log("EEPROM readout failed (enable EEPROM), " + "error code: %d", status); return status; } @@ -868,8 +1147,8 @@ static status_t ReadEEPROM(s2pi_slave_t slave, uint8_t *eeprom) status = EEPROM_Read(slave, address, &data[address]); if (status != STATUS_OK) { - PX4_INFO_RAW("EEPROM readout failed @ address 0x%02x, " - "error code: %d!", address, (int)status); + error_log("EEPROM readout failed @ address 0x%02x, " + "error code: %d!", address, status); return status; } } @@ -878,9 +1157,9 @@ static status_t ReadEEPROM(s2pi_slave_t slave, uint8_t *eeprom) uint8_t d2[] = { 0x12, 0x00, 0x2B }; status = SPITransferSync(slave, d2, sizeof(d2)); - if (status < STATUS_OK) { - PX4_INFO_RAW("EEPROM readout failed (enable EEPROM), " - "error code: %d", (int)status); + if (status != STATUS_OK) { + error_log("EEPROM readout failed (enable EEPROM), " + "error code: %d", status); return status; } @@ -888,8 +1167,8 @@ static status_t ReadEEPROM(s2pi_slave_t slave, uint8_t *eeprom) uint8_t err = hamming_decode(data, eeprom); if (err != 0) { - PX4_INFO_RAW("EEPROM readout failed! Failed to decoding " - "Hamming weight (error: %d)!", err); + error_log("EEPROM readout failed! Failed to decoding " + "Hamming weight (Hamming parity error: %d)!", err); return STATUS_ARGUS_EEPROM_BIT_ERROR; } @@ -900,30 +1179,30 @@ static status_t ReadEEPROM(s2pi_slave_t slave, uint8_t *eeprom) } /*!*************************************************************************** - * @brief GPIO Mode Test for S2PI HAL Implementation. + * @brief GPIO Mode Test for S2PI HAL Implementation. * * @details This test verifies the GPIO mode of the S2PI HAL module. This is - * done by leveraging the EEPROM readout sequence that accesses the - * devices EEPROM via a software protocol that depends on the GPIO - * mode. + * done by leveraging the EEPROM readout sequence that accesses the + * devices EEPROM via a software protocol that depends on the GPIO + * mode. * - * This the requires several steps, most of them are already verified - * in previous tests: - * - Basic device configuration and enable EEPROM. - * - Read EERPOM via GPIO mode and apply Hamming weight - * - Repeat several times (to eliminate random readout issues). - * - Decode the EEPROM (using EEPROM_Decode in argus_cal_eeprom.c) - * - Check if Module Number and Chip ID is not 0 + * This the requires several steps, most of them are already verified + * in previous tests: + * - Basic device configuration and enable EEPROM. + * - Read EEPROM via GPIO mode and apply Hamming weight + * - Repeat several times (to eliminate random readout issues). + * - Decode the EEPROM (using EEPROM_Decode in argus_cal_eeprom.c) + * - Check if Module Number and Chip ID is not 0 * - * @param slave The S2PI slave parameter passed to the S2PI HAL functions. + * @param slave The S2PI slave parameter passed to the S2PI HAL functions. * - * @return Returns the \link #status_t status\endlink: - * - #STATUS_OK on success. - * - #ERROR_FAIL if the GPIO test fails. - * - #STATUS_ARGUS_EEPROM_BIT_ERROR if the Hamming weight fails. - * - The S2PI layer error code if #S2PI_CaptureGpioControl, - * #S2PI_ReleaseGpioControl, #S2PI_WriteGpioPin or - * #S2PI_ReadGpioPin return any negative status. + * @return Returns the \link #status_t status\endlink: + * - #STATUS_OK on success. + * - #ERROR_FAIL if the GPIO test fails. + * - #STATUS_ARGUS_EEPROM_BIT_ERROR if the Hamming weight fails. + * - The S2PI layer error code if #S2PI_CaptureGpioControl, + * #S2PI_ReleaseGpioControl, #S2PI_WriteGpioPin or + * #S2PI_ReadGpioPin return any negative status. *****************************************************************************/ static status_t GpioModeTest(s2pi_slave_t slave) { @@ -934,66 +1213,66 @@ static status_t GpioModeTest(s2pi_slave_t slave) status_t status = ReadEEPROM(slave, eeprom1); - if (status < STATUS_OK) { - PX4_INFO_RAW("GPIO mode test failed (1st attempt)!"); + if (status != STATUS_OK) { + error_log("GPIO mode test failed (1st attempt)!"); return status; } status = ReadEEPROM(slave, eeprom2); - if (status < STATUS_OK) { - PX4_INFO_RAW("GPIO mode test failed (2nd attempt)!"); + if (status != STATUS_OK) { + error_log("GPIO mode test failed (2nd attempt)!"); return status; } status = ReadEEPROM(slave, eeprom3); - if (status < STATUS_OK) { - PX4_INFO_RAW("GPIO mode test failed (3rd attempt)!"); + if (status != STATUS_OK) { + error_log("GPIO mode test failed (3rd attempt)!"); return status; } /* Verify EEPROM data. */ if ((memcmp(eeprom1, eeprom2, 16) != 0) || (memcmp(eeprom1, eeprom3, 16) != 0)) { - PX4_INFO_RAW("GPIO Mode test failed (data comparison)!\n" - "The data from 3 distinct EEPROM readout does not match!"); + error_log("GPIO Mode test failed (data comparison)!\n" + "The data from 3 distinct EEPROM readout does not match!"); return ERROR_FAIL; } /* Check EEPROM data for reasonable chip and module number (i.e. not 0) */ uint32_t chipID = EEPROM_ReadChipId(eeprom1); - argus_module_version_t module = EEPROM_ReadModule(eeprom1); + uint8_t module = EEPROM_ReadModule(eeprom1); if (chipID == 0 || module == 0) { - PX4_INFO_RAW("GPIO Mode test failed (data verification)!\n" - "Invalid EEPROM data: Module = %d; Chip ID = %u!", module, (uint)chipID); + error_log("GPIO Mode test failed (data verification)!\n" + "Invalid EEPROM data: Module = %d; Chip ID = %d!", module, chipID); return ERROR_FAIL; } - PX4_INFO_RAW("EEPROM Readout succeeded!\n"); - PX4_INFO_RAW("- Module: %d\n", module); - PX4_INFO_RAW("- Device ID: %u\n", (uint)chipID); + print("EEPROM Readout succeeded!\n"); + print("- Module: %d\n", module); + print("- Device ID: %d\n", chipID); return STATUS_OK; } /*!*************************************************************************** - * @brief Reads the RCO_TRIM value from the devices EEPROM. + * @brief Reads the RCO_TRIM value from the devices EEPROM. * * @details The function reads the devices EEPROM via GPIO mode and extracts - * the RCO_TRIM value from the EEPROM map. + * the RCO_TRIM value from the EEPROM map. * - * @param slave The S2PI slave parameter passed to the S2PI HAL functions. - * @param rcotrim The read RCO_TRIM value will be returned via this pointer. + * @param slave The S2PI slave parameter passed to the S2PI HAL functions. + * @param rcotrim The read RCO_TRIM value will be returned via this pointer. * - * @return Returns the \link #status_t status\endlink: - * - #STATUS_OK on success. - * - #STATUS_ARGUS_EEPROM_BIT_ERROR if the Hamming weight fails. - * - #ERROR_ARGUS_UNKNOWN_MODULE if the EEPROM module number is invalid. - * - The S2PI layer error code if #S2PI_CaptureGpioControl, - * #S2PI_ReleaseGpioControl, #S2PI_WriteGpioPin or - * #S2PI_ReadGpioPin return any negative status. + * @return Returns the \link #status_t status\endlink: + * - #STATUS_OK on success. + * - #STATUS_ARGUS_EEPROM_BIT_ERROR if the Hamming weight fails. + * - #ERROR_ARGUS_UNKNOWN_MODULE if the EEPROM module number is invalid. + * - The S2PI layer error code if #S2PI_CaptureGpioControl, + * #S2PI_ReleaseGpioControl, #S2PI_WriteGpioPin or + * #S2PI_ReadGpioPin return any negative status. *****************************************************************************/ static status_t ReadRcoTrim(s2pi_slave_t slave, int8_t *rcotrim) { @@ -1003,25 +1282,15 @@ static status_t ReadRcoTrim(s2pi_slave_t slave, int8_t *rcotrim) if (status != STATUS_OK) { return status; } - argus_module_version_t module = EEPROM_ReadModule(eeprom); - - switch (module) { - case AFBR_S50MV85G_V1: - case AFBR_S50MV85G_V2: - case AFBR_S50MV85G_V3: - case AFBR_S50LV85D_V1: - case AFBR_S50MV68B_V1: - case AFBR_S50MV85I_V1: - case AFBR_S50SV85K_V1: + uint8_t module = EEPROM_ReadModule(eeprom); + if (module > 0 && module < 8) { /* Read RCO Trim Value from EEPROM Map 1/2/3: */ *rcotrim = ((int8_t) eeprom[0]) >> 3; - break; - case MODULE_NONE: /* Uncalibrated module; use all 0 data. */ - default: - - PX4_INFO_RAW("EEPROM Readout failed! Unknown module number: %d", module); + } else { + /* Uncalibrated module; use all 0 data. */ + error_log("EEPROM Readout failed! Unknown module number: %d", module); return ERROR_ARGUS_UNKNOWN_MODULE; } @@ -1029,112 +1298,151 @@ static status_t ReadRcoTrim(s2pi_slave_t slave, int8_t *rcotrim) } /*!*************************************************************************** - * @brief Triggers a measurement on the device and waits for the data ready - * interrupt. + * @brief Callback function for the data ready interrupt. + * + * @details The function is called by the S2PI layer when the data ready + * interrupt is pending. The function sets the \p param to + * #STATUS_IDLE. + * + * @param param The parameter passed to the #S2PI_SetIrqCallback function as + * an abstract pointer to an #status_t type. + *****************************************************************************/ +static void MeasurementCallback(void *param) +{ + *(status_t *) param = STATUS_IDLE; +} + +/*!*************************************************************************** + * @brief Triggers a measurement on the device and waits for the data ready + * interrupt. * * @details The function triggers a measurement cycle on the device and waits - * until the measurement has been finished. A \p sample count can be - * specified to setup individual number of digital averaging. + * until the measurement has been finished. A \p sample count can be + * specified to setup individual number of digital averaging. * - * @warning The test utilizes already the timer HAL in order to implement a - * rudimentary timeout. However, at this time, only some basic - * plausibility checks are performed on the timer HAL. I.e. if there - * is an issue in the time HAL, e.g. too fast or too slow time - * counting, the test may fail with an #ERROR_TIMEOUT. In this case, - * one also needs to verify the timer HAL, especially the - * #Timer_GetCounterValue function. + * @warning The test utilizes already the timer HAL in order to implement a + * rudimentary timeout. However, at this time, only some basic + * plausibility checks are performed on the timer HAL. I.e. if there + * is an issue in the time HAL, e.g. too fast or too slow time + * counting, the test may fail with an #ERROR_TIMEOUT. In this case, + * one also needs to verify the timer HAL, especially the + * #Timer_GetCounterValue function. * - * @param slave The S2PI slave parameter passed to the S2PI HAL functions. - * @param samples The specified number of averaging samples for the measurement. + * @param slave The S2PI slave parameter passed to the S2PI HAL functions. + * @param samples The specified number of averaging samples for the measurement. * - * @return Returns the \link #status_t status\endlink: - * - #STATUS_OK on success. - * - #ERROR_TIMEOUT if either the SPI operation did not finished - * or the IRQ was not detected within a specified time (check also - * timer HAL implementation). - * - The S2PI layer error code if #S2PI_TransferFrame, #S2PI_GetStatus - * or #S2PI_SetIrqCallback return any negative status. + * @return Returns the \link #status_t status\endlink: + * - #STATUS_OK on success. + * - #ERROR_TIMEOUT if either the SPI operation did not finished + * or the IRQ was not detected within a specified time (check also + * timer HAL implementation). + * - The S2PI layer error code if #S2PI_TransferFrame, #S2PI_GetStatus + * or #S2PI_SetIrqCallback return any negative status. *****************************************************************************/ static status_t RunMeasurement(s2pi_slave_t slave, uint16_t samples) { - status_t status = TriggerMeasurement(slave, samples); + /* Test parameter configuration: *****************************************/ + const uint32_t timeout_ms = 300; // The transfer timeout in ms. + /*************************************************************************/ - if (status < STATUS_OK) { - PX4_INFO_RAW("Speed test failed!\n" - "Call to TransferFrame returned code: %d", - (int)status); + volatile status_t callbackStatus = STATUS_BUSY; + + status_t status = S2PI_SetIrqCallback(slave, MeasurementCallback, (void *)&callbackStatus); + + if (status != STATUS_OK) { + error_log("Failed to run a measurement!\n" + "Call to SetIrqCallback returned code: %d", status); + return status; + } + + status = TriggerMeasurement(slave, samples, 0, 0); + + if (status != STATUS_OK) { + error_log("Failed to run a measurement!\n" + "Call to TransferFrame returned code: %d", status); return status; } /* Wait until the transfer is finished using a timeout. */ - status = AwaitDataReady(slave, 300); - if (status < STATUS_OK) { - PX4_INFO_RAW("Speed test failed!\n" - "SPI Read IRQ pin didn't raised, timeout activated at 200ms, error code: %d", - (int)status); - return status; + ltc_t start; + Time_GetNow(&start); + + while (callbackStatus == STATUS_BUSY) { + if (Time_CheckTimeoutMSec(&start, timeout_ms)) { + error_log("Failed to run a measurement!\n" + "Timeout occurred while waiting for the SPI interrupt (%d ms).", + timeout_ms); + return ERROR_TIMEOUT; + } } - return status; + if (callbackStatus != STATUS_OK) { + error_log("Failed to run a measurement!\n" + "The SPI callback yielded returned code: %d", + callbackStatus); + return callbackStatus; + } + + return STATUS_OK; } /*!*************************************************************************** - * @brief Test for Timer HAL Implementation by comparing timings to the device. + * @brief Test for Timer HAL Implementation by comparing timings to the device. * - * @details The test verifies the timer HAL implementation by comparing the - * timings to the AFBR-S50 device as a reference. - * Therefore several measurement are executed on the device, each with - * a different averaging sample count. The elapsed time increases - * linearly with the number of averaging samples. In order to remove - * the time for software/setup, a linear regression fit is applied to - * the measurement results and only the slope is considered for the - * result. A delta of 102.4 microseconds per sample is expected. - * If the measured delta per sample is within an specified error range, - * the timer implementation is considered correct. + * @details The test verifies the timer HAL implementation by comparing the + * timings to the AFBR-S50 device as a reference. + * Therefore several measurement are executed on the device, each with + * a different averaging sample count. The elapsed time increases + * linearly with the number of averaging samples. In order to remove + * the time for software/setup, a linear regression fit is applied to + * the measurement results and only the slope is considered for the + * result. A delta of 102.4 microseconds per sample is expected. + * If the measured delta per sample is within an specified error range, + * the timer implementation is considered correct. * - * @param slave The S2PI slave parameter passed to the S2PI HAL functions. + * @param slave The S2PI slave parameter passed to the S2PI HAL functions. * - * @return Returns the \link #status_t status\endlink: - * - #STATUS_OK on success. - * - #ERROR_FAIL if the timer test fails. - * - #STATUS_ARGUS_EEPROM_BIT_ERROR if the EEPROM Hamming weight fails. - * - #ERROR_ARGUS_UNKNOWN_MODULE if the EEPROM module number is invalid. - * - #ERROR_TIMEOUT if either the SPI operation did not finished - * or the IRQ was not detected within a specified time (check also - * timer HAL implementation). - * - The S2PI layer error code if #S2PI_TransferFrame, #S2PI_GetStatus, - * #S2PI_SetIrqCallback, #S2PI_CaptureGpioControl, - * #S2PI_ReleaseGpioControl, #S2PI_WriteGpioPin or #S2PI_ReadGpioPin - * return any negative status. + * @return Returns the \link #status_t status\endlink: + * - #STATUS_OK on success. + * - #ERROR_FAIL if the timer test fails. + * - #STATUS_ARGUS_EEPROM_BIT_ERROR if the EEPROM Hamming weight fails. + * - #ERROR_ARGUS_UNKNOWN_MODULE if the EEPROM module number is invalid. + * - #ERROR_TIMEOUT if either the SPI operation did not finished + * or the IRQ was not detected within a specified time (check also + * timer HAL implementation). + * - The S2PI layer error code if #S2PI_TransferFrame, #S2PI_GetStatus, + * #S2PI_SetIrqCallback, #S2PI_CaptureGpioControl, + * #S2PI_ReleaseGpioControl, #S2PI_WriteGpioPin or #S2PI_ReadGpioPin + * return any negative status. *****************************************************************************/ static status_t TimerTest(s2pi_slave_t slave) { /* Test parameter configuration: *****************************************/ - const int8_t n = 10; // The number of measurements. - const uint32_t ds = 100; // The step size in averaging samples. - const float exp_slope = 102.4; // Expected slope is 102.4 µs / phase / sample - const float rel_slope_error = 3e-2; // Relative slope tolerance is 3%. + const int8_t n = 10; // The number of measurements. + const uint32_t ds = 100; // The step size in averaging samples. + const float exp_slope = 102.4f; // Expected slope is 102.4 µs / phase / sample + const float rel_slope_error = 3e-2f; // Relative slope tolerance is 3%. /*************************************************************************/ /* Read RCOTrim value from EEPROM*/ int8_t RcoTrim = 0; status_t status = ReadRcoTrim(slave, &RcoTrim); - if (status < STATUS_OK) { - PX4_INFO_RAW("Timer test failed!\n" - "EEPROM Read test returned code: %d", (int)status); + if (status != STATUS_OK) { + error_log("Timer test failed!\n" + "EEPROM Read test returned code: %d", status); return status; } - PX4_INFO_RAW("RCOTrim = %d\n", RcoTrim); + print("RCOTrim = %d\n", RcoTrim); /* Configure the device with calibrated RCO to 24MHz. */ status = ConfigureDevice(slave, RcoTrim); - if (status < STATUS_OK) { - PX4_INFO_RAW("Timer test failed!\n" - "Configuration test returned code: %d", (int)status); + if (status != STATUS_OK) { + error_log("Timer test failed!\n" + "Configuration test returned code: %d", status); return status; } @@ -1146,21 +1454,23 @@ static status_t TimerTest(s2pi_slave_t slave) float x2sum = 0; float xysum = 0; - PX4_INFO_RAW("+-------+---------+------------+\n"); - PX4_INFO_RAW("| count | samples | elapsed us |\n"); - PX4_INFO_RAW("+-------+---------+------------+\n"); + print("+-------+---------+------------+\n"); + print("| count | samples | elapsed us |\n"); + print("+-------+---------+------------+\n"); for (uint8_t i = 1; i <= n; ++i) { ltc_t start; Time_GetNow(&start); - int samples = ds * i; - status = RunMeasurement(slave, samples); + uint32_t samples = ds * i; + assert(samples < UINT16_MAX); - if (status < STATUS_OK) { - PX4_INFO_RAW("Timer test failed!\n" - "Run measurement returned code: %d", - (int)status); + status = RunMeasurement(slave, (uint16_t)samples); + + if (status != STATUS_OK) { + error_log("Timer test failed!\n" + "Run measurement returned code: %d", + status); return status; } @@ -1168,30 +1478,30 @@ static status_t TimerTest(s2pi_slave_t slave) xsum += (float) samples; ysum += (float) elapsed_usec; - x2sum += (float) samples * samples; - xysum += (float) samples * elapsed_usec; + x2sum += (float) samples * (float) samples; + xysum += (float) samples * (float) elapsed_usec; - PX4_INFO_RAW("| %5d | %7d | %10d |\n", i, samples, (uint)elapsed_usec); + print("| %5d | %7d | %10d |\n", i, samples, elapsed_usec); } - PX4_INFO_RAW("+-------+---------+------------+\n"); + print("+-------+---------+------------+\n"); const float slope = (n * xysum - xsum * ysum) / (n * x2sum - xsum * xsum); const float intercept = (ysum * x2sum - xsum * xysum) / (n * x2sum - xsum * xsum); - PX4_INFO_RAW("Linear Regression: y(x) = %dE-7 sec * x + %dE-7 sec\n", - (int)(10 * slope), (int)(10 * intercept)); + print("Linear Regression: y(x) = %dE-7 sec * x + %dE-7 sec\n", + (int)(10 * slope), (int)(10 * intercept)); /* Check the error of the slope. */ const float max_slope = exp_slope * (1.f + rel_slope_error); const float min_slope = exp_slope * (1.f - rel_slope_error); if (slope > max_slope || slope < min_slope) { - PX4_INFO_RAW("Time test failed!\n" - "The measured time slope does not match the expected value! " - "(actual: %dE-7, expected: %dE-7, min: %dE-7, max: %dE-7)\n", - (int)(10 * slope), (int)(10 * exp_slope), - (int)(10 * min_slope), (int)(10 * max_slope)); + error_log("Time test failed!\n" + "The measured time slope does not match the expected value! " + "(actual: %dE-7, expected: %dE-7, min: %dE-7, max: %dE-7)\n", + (int)(10 * slope), (int)(10 * exp_slope), + (int)(10 * min_slope), (int)(10 * max_slope)); return ERROR_FAIL; } @@ -1200,11 +1510,11 @@ static status_t TimerTest(s2pi_slave_t slave) /*!*************************************************************************** - * @brief Data structure for the PIT test. + * @brief Data structure for the PIT test. * - * @details Contains data that is required by the PIT timer test. + * @details Contains data that is required by the PIT timer test. *****************************************************************************/ -typedef struct { +typedef struct pit_data_t { /*! The number of PIT callback events. */ volatile uint32_t n; @@ -1216,90 +1526,113 @@ typedef struct { } pit_data_t; - - /*!*************************************************************************** - * @brief Callback function invoked by the PIT. + * @brief Callback function invoked by the PIT. * - * @details The function that is invoked every time a specified interval elapses. - * An abstract parameter is passed to the function whenever it is called. + * @details The function that is invoked every time a specified interval elapses. + * An abstract parameter is passed to the function whenever it is called. * - * This implementation collects callback time stamps and counts the - * number of callback events using the abstract parameter. + * This implementation collects callback time stamps and counts the + * number of callback events using the abstract parameter. * - * @param param An abstract parameter to be passed to the callback. This is - * also the identifier of the given interval. + * @param param An abstract parameter to be passed to the callback. This is + * also the identifier of the given interval. *****************************************************************************/ static void PIT_Callback(void *param) { - pit_data_t *data = (pit_data_t *) param; - - if (data->n == 0) { - Time_GetNow(&data->t_first); - data->t_last = data->t_first; + if (param == NULL) { + error_log("PIT interrupt test failed: callback parameter \"param\" was NULL!"); } else { - Time_GetNow(&data->t_last); - } + pit_data_t *data = (pit_data_t *)param; - data->n++; + if (data->n == 0) { + Time_GetNow(&data->t_first); + data->t_last = data->t_first; + + } else { + Time_GetNow(&data->t_last); + } + + data->n++; + } } /*!*************************************************************************** - * @brief Executes a PIT measurement and verifies the callback interval. + * @brief Executes a PIT measurement and verifies the callback interval. * * @details The function configures the PIT with a given interval and waits - * several callback events to happen. In each callback event, the - * elapsed time is measured and the number of calls are counted. - * Finally, the average interrupt period is compared with the - * lifetime timer that has been already verified in a previous test - * (see #Timer_Test). + * several callback events to happen. In each callback event, the + * elapsed time is measured and the number of calls are counted. + * Finally, the average interrupt period is compared with the + * lifetime timer that has been already verified in a previous test + * (see #TimerTest). The time until the first interrupt event is also + * verified. * - * @param exp_dt_us The expected timer interval in microseconds. - * @param n The number of PIT events to await. + * @param exp_dt_us The expected timer interval in microseconds. + * @param n The number of PIT events to await. * - * @return Returns the \link #status_t status\endlink: - * - #STATUS_OK on success. - * - #ERROR_FAIL if the measured interval does not match the - * expectations or the PIT was not disabled properly. - * - #ERROR_TIMEOUT if either the PIT events do not occur within the - * expected time. - * - The PIT layer error code if #Timer_SetInterval return any - * negative status. + * @return Returns the \link #status_t status\endlink: + * - #STATUS_OK on success. + * - #ERROR_FAIL if the measured interval does not match the + * expectations or the PIT was not disabled properly. + * - #ERROR_TIMEOUT if either the PIT events do not occur within the + * expected time. + * - The PIT layer error code if #Timer_SetInterval return any + * negative status. *****************************************************************************/ static status_t RunPITTest(uint32_t exp_dt_us, uint32_t n) { /* Test parameter configuration: *****************************************/ - const float rel_dt_error = 1e-3; // Relative timer interval tolerance is 0.1%. - const float abs_dt_error = 1.0; // Absolute timer interval tolerance is 1us. + const float rel_dt_error = 5e-3f; // Relative timer interval tolerance: 0.5 %. + const float abs_dt_error = 5.0f; // Absolute timer interval tolerance: 5.0 us. /*************************************************************************/ - float dt = exp_dt_us * rel_dt_error; + float dt = (float) exp_dt_us * rel_dt_error; if (dt < abs_dt_error) { dt = abs_dt_error; } - const float max_dt = exp_dt_us + dt; - const float min_dt = exp_dt_us - dt; + const float max_dt = (float) exp_dt_us + dt; + const float min_dt = (float) exp_dt_us - dt; + + if (dt < abs_dt_error * 3) { dt = abs_dt_error * 3; } + + const float t_first_max = (float) exp_dt_us + dt * 5; // use 5x tolerance for + const float t_first_min = (float) exp_dt_us - dt * 5; // the first interval /*************************************************************************/ + print("Run PIT Test (w/ %d us interval):\n" + " - expected event count: %d\n" + " - expected interval: %d us, min: %d us, max: %d us\n" + " - expected first event: %d us, min: %d us, max: %d us\n", + exp_dt_us, n, exp_dt_us, (int)min_dt, (int)max_dt, + exp_dt_us, (int)t_first_min, (int)t_first_max); + /* Setup the PIT callback with specified interval. */ pit_data_t data = { 0 }; status_t status = Timer_SetInterval(exp_dt_us, &data); - if (status < STATUS_OK) { - PX4_INFO_RAW("PIT test failed!\n" - "Timer_SetInterval returned status code: %d", (int)status); + if (status != STATUS_OK) { + error_log("PIT test failed!\n" + "Timer_SetInterval returned status code: %d", status); return status; } /* Wait until n PIT callback have been happened. */ - uint32_t timeout_us = (n + 1) * exp_dt_us; + const uint32_t timeout_us = (n + 1) * exp_dt_us; + ltc_t start; Time_GetNow(&start); while (data.n < n) { if (Time_CheckTimeoutUSec(&start, timeout_us)) { - PX4_INFO_RAW("PIT test failed!\n" - "Waiting for the PIT interrupt events yielded a timeout."); + const uint32_t elapsed_us = Time_GetElapsedUSec(&start); + const uint32_t t_first_us = Time_DiffUSec(&start, &data.t_first); + const uint32_t t_last_us = Time_DiffUSec(&start, &data.t_last); + error_log("PIT test failed!\n" + "Waiting for the PIT interrupt events yielded a timeout.\n" + "Timeout: %d us; Elapsed: %d us (%d of %d events).\n" + "First event @ %d us, last event @ %d us", + timeout_us, elapsed_us, data.n, n, t_first_us, t_last_us); status = ERROR_TIMEOUT; break; } @@ -1309,59 +1642,71 @@ static status_t RunPITTest(uint32_t exp_dt_us, uint32_t n) /* Disable the PIT timer callback. */ status = Timer_SetInterval(0, &data); - if (status < STATUS_OK) { - PX4_INFO_RAW("PIT test failed!\n" - "Timer_SetInterval returned status code: %d", (int)status); + if (status != STATUS_OK) { + error_log("PIT test failed!\n" + "Timer_SetInterval returned status code: %d", status); } } if (status == STATUS_OK) { /* Check if PIT callback is not invoked any more. */ - timeout_us = 2 * exp_dt_us; - Time_GetNow(&start); - - while (!Time_CheckTimeoutUSec(&start, timeout_us)) { __asm("nop"); } + Time_DelayUSec(3 * exp_dt_us); if (data.n > n) { - PX4_INFO_RAW("PIT test failed!\n" - "Timer_SetInterval has been called after it was disabled."); + const uint32_t elapsed_us = Time_GetElapsedUSec(&start); + error_log("PIT test failed!\n" + "Timer_SetInterval has been called again after it was disabled\n" + "(within %d us; %d of %d events in total).", + elapsed_us, data.n, n); status = ERROR_FAIL; } } /* Verify the measured average timer interval. */ - const float act_dt_us = Time_DiffUSec(&data.t_first, &data.t_last) / (n - 1); + const float act_dt_us = Time_DiffUSec(&data.t_first, &data.t_last) / (float)(n - 1); + const uint32_t t_first_us = Time_DiffUSec(&start, &data.t_first); + const uint32_t t_last_us = Time_DiffUSec(&start, &data.t_last); - if (status == STATUS_OK && (act_dt_us > max_dt || act_dt_us < min_dt)) { - PX4_INFO_RAW("PIT test failed!\n" - "The measured timer interval does not match the expected value!\n"); + print(" - actual event count: %d\n" + " - actual interval: %d us\n" + " - actual first event: %d us\n" + " - actual last event: %d us\n\n", + data.n, (int)act_dt_us, t_first_us, t_last_us); + + if (status == STATUS_OK && (t_first_us > t_first_max || t_first_us < t_first_min)) { + error_log("PIT test failed!\n" + "The first timer event did not occur after the expected interval!"); status = ERROR_FAIL; } - PX4_INFO_RAW("PIT Test Results:\n" - " - event count: %u\n" - " - actual interval: %d us\n" - " - expected interval: %d us, min: %d us, max: %d us\n", - (uint)data.n, (int)act_dt_us, (uint)exp_dt_us, (int)min_dt, (int)max_dt); + if (status == STATUS_OK && (act_dt_us > max_dt || act_dt_us < min_dt)) { + error_log("PIT test failed!\n" + "The measured timer interval does not match the expected value!"); + status = ERROR_FAIL; + } + + print(" - test status: %d\n\n", status); return status; } /*!*************************************************************************** - * @brief Test for PIT HAL Implementation by comparing timings to the device. + * @brief Test for PIT HAL Implementation by comparing timings to the device. * - * @details The test verifies the timer HAL implementation by comparing the + * @details The test verifies the timer HAL implementation by comparing the + * period between the interrupts with the lifetime timer values + * that has been already verified in a previous test (see #TimerTest). * - * @return Returns the \link #status_t status\endlink: - * - #STATUS_OK on success. - * - #ERROR_NOT_IMPLEMENTED if the PIT functionality is not - * implemented and the test is skipped. - * - #ERROR_FAIL if the measured interval does not match the - * expectations or the PIT was not disabled properly. - * - #ERROR_TIMEOUT if either the PIT events do not occur within the - * expected time. - * - The PIT layer error code if #Timer_SetInterval or - * #Timer_SetCallback return any negative status. + * @return Returns the \link #status_t status\endlink: + * - #STATUS_OK on success. + * - #ERROR_NOT_IMPLEMENTED if the PIT functionality is not + * implemented and the test is skipped. + * - #ERROR_FAIL if the measured interval does not match the + * expectations or the PIT was not disabled properly. + * - #ERROR_TIMEOUT if either the PIT events do not occur within the + * expected time. + * - The PIT layer error code if #Timer_SetInterval or + * #Timer_SetCallback return any negative status. *****************************************************************************/ static status_t PITTest(void) { @@ -1369,31 +1714,595 @@ static status_t PITTest(void) if (status == ERROR_NOT_IMPLEMENTED) { return status; } - if (status < STATUS_OK) { - PX4_INFO_RAW("PIT test failed!\n" - "Timer_SetCallback returned status code: %d", (int)status); + if (status != STATUS_OK) { + error_log("PIT test failed!\n" + "Timer_SetCallback returned status code: %d", status); return status; } + status = RunPITTest(200000, 5); + + if (status != STATUS_OK) { return status; } + status = RunPITTest(10000, 10); - if (status < STATUS_OK) { return status; } + if (status != STATUS_OK) { return status; } - status = RunPITTest(333, 1000); + /* High Speed Test down to 1000 microseconds. If this fails, just print + * a message that very high frame rates might have issues. */ + status = RunPITTest(1000, 500); - if (status < STATUS_OK) { return status; } + if (status != STATUS_OK) { + print("WARNING: PIT test failed for 1000 us interval!\n" + " This is only critical if high frame rates (up to 1000 fps)\n" + " need to be achieved. Otherwise, the error can be safely ignored.\n"); + status = STATUS_IGNORE; // ignore + } - status = RunPITTest(100000, 5); + if (status == STATUS_OK) { // only run if previous test succeeded! + /* High Speed Test down to 333 microseconds. If this fails, just print + * a message that very high frame rates might have issues. */ + status = RunPITTest(333, 500); - if (status < STATUS_OK) { return status; } + if (status != STATUS_OK) { + print("WARNING: PIT test failed for 333 us interval!\n" + " This is only critical if very high frame rates (up to 3000 fps)\n" + " need to be achieved. Otherwise, the error can be safely ignored.\n"); + status = STATUS_IGNORE; // ignore + } + } status = Timer_SetCallback(0); - if (status < STATUS_OK) { - PX4_INFO_RAW("PIT test failed!\n" - "Timer_SetCallback to 0 returned status code: %d", (int)status); + if (status != STATUS_OK) { + error_log("PIT test failed!\n" + "Timer_SetCallback to 0 returned status code: %d", status); return status; } return STATUS_OK; } + +/*!*************************************************************************** + * @brief Data structure for the S2PI transfer from interrupt tests. + * + * @details Contains data that is required by the S2PI transfer from interrupt + * test. The data structure is passed to the corresponding interrupt + * callback functions. + *****************************************************************************/ +typedef struct spi_irq_data_t { + /*! The status of the interrupt callback function. */ + volatile status_t Status; + + /*! The S2PI slave parameter passed to the S2PI HAL functions. */ + s2pi_slave_t Slave; + + /*! The data buffer to be transferred from/to the device for testing purposes. */ + uint8_t Data[17U]; + + /*! Set to true when all SPI transfers are finished. */ + volatile bool Finished; + + /*! Set to true when the second SPI transfers is started. + The second transfer is used to read-back the previously set values. */ + volatile bool ReadBack; + +} spi_irq_data_t; + + +/*!*************************************************************************** + * @brief SPI interrupt callback function for the SPI transfer from IRQ test. + * + * @details The interrupt callback is invoked from the S2PI module upon + * finishing the SPI transfer. The callback is used by the + * #SpiTransferFromSpiInterrupt test to trigger the second SPI transfer + * from the interrupt callback context. + * + * @note The callback also utilizes the #print functionality. This requires + * a correct implementation of the corresponding function such that it + * can be invoked from the given interrupt context. This usually + * requires the underlying send (e.g. UART or USB send functions) to + * have higher priority that this interrupt in order to finished the + * print statement asynchronously. + * + * @param status The S2PI module status passed to the callback. + * @param param The abstract interrupt callback parameter. + * + * @return Returns the \link #status_t status\endlink: + * - #STATUS_OK on success. + * - #ERROR_INVALID_ARGUMENT if the \p param is NULL. + * - The S2PI layer error code if any is passed to the callback function. + * - The S2PI layer error code if #S2PI_TransferFrame return any. + *****************************************************************************/ +static status_t SpiTransferFromSpiInterruptCallback(status_t status, void *param) +{ + if (param == NULL) { + error_log("SPI transfer from SPI interrupt test failed\n" + "callback parameter \"param\" was NULL!"); + return ERROR_INVALID_ARGUMENT; + } + + spi_irq_data_t *data = (spi_irq_data_t *) param; + + if (status != STATUS_OK) { + error_log("SPI transfer from SPI interrupt test failed:\n" + "callback received error! Error code: %d", status); + data->Status = status; + return status; + } + + if (!data->ReadBack) { + print("Invoking SPI transfer from SPI interrupt callback...\n"); + + /* Clear the laser pattern and read back previous values. */ + data->Data[0] = 0x04; // Laser Pattern Register Address + + for (uint8_t i = 1; i < 17U; ++i) { data->Data[i] = 0; } + + status = S2PI_TransferFrame(data->Slave, data->Data, data->Data, 17U, + SpiTransferFromSpiInterruptCallback, param); + + if (status != STATUS_OK) { + error_log("SPI transfer from SPI interrupt test failed:\n" + "Calling S2PI_TransferFrame from SPI interrupt " + "returned error code: %d", status); + data->Status = status; + return status; + } + + data->ReadBack = true; + + } else { + data->Finished = true; + } + + return STATUS_OK; +} + +/*!*************************************************************************** + * @brief SPI transfer from SPI interrupt callback test. + * + * @details This test verifies the interrupt functionality of the SPI interface. + * The test verifies that an SPI transfer can be triggered from the SPI + * interrupt service routine context. + * + * The test basically repeats the #SpiConnectionTest but this time it + * invokes the second SPI transfer from the SPI callback function. + * A very common error is that the callback is invoked while the SPI + * module is still busy which does not allow to invoke another SPI + * transfer from the callback. + * + * @param slave The S2PI slave parameter passed to the S2PI HAL functions. + * + * @return Returns the \link #status_t status\endlink: + * - #STATUS_OK on success. + * - #ERROR_TIMEOUT if the test did not finish within a specified time. + * - #ERROR_FAIL if the device access failed and the read data did not + * match the expected values. + * - The S2PI layer error code if #S2PI_TransferFrame or the SPI + * callback yield in any non-OK status. + *****************************************************************************/ +static status_t SpiTransferFromSpiInterrupt(s2pi_slave_t slave) +{ + /* Test parameter configuration: *****************************************/ + const uint32_t timeout_us = 100000; // timeout for SPI transfers to finish + /*************************************************************************/ + + status_t status = STATUS_OK; + spi_irq_data_t data = { .Slave = slave }; + + print("Invoking SPI transfer from task level...\n"); + + /* Transfer a pattern to the register */ + data.Data[0] = 0x04; // Laser Pattern Register Address + + for (uint8_t i = 1; i < 17U; ++i) { data.Data[i] = i; } + + status = S2PI_TransferFrame(slave, data.Data, data.Data, 17U, + SpiTransferFromSpiInterruptCallback, &data); + + if (status != STATUS_OK) { + error_log("SPI transfer from SPI interrupt test failed:\n" + "Failed to transfer a data frame! Error code: %d", status); + return status; + } + + /* Wait until transfers has finished. */ + ltc_t start; + Time_GetNow(&start); + + while (!data.Finished && (data.Status == STATUS_OK)) { + if (Time_CheckTimeoutUSec(&start, timeout_us)) { + const uint32_t elapsed_us = Time_GetElapsedUSec(&start); + error_log("SPI transfer from SPI interrupt test failed:\n" + "Waiting for the transfers to be finished yielded a timeout.\n" + "Timeout: %d us; Elapsed: %d us (%d of %d events).", + timeout_us, elapsed_us); + status = ERROR_TIMEOUT; + break; + } + } + + if (data.Status != STATUS_OK) { + error_log("SPI transfer from SPI interrupt test failed:\n" + "Waiting for the transfers to be finished yielded a error code: %d", + data.Status); + return data.Status; + } + + print("Verify read data...\n"); + + /* Verify the read pattern. */ + for (uint8_t i = 1; i < 17U; ++i) { + if (data.Data[i] != i) { + error_log("SPI transfer from SPI interrupt test failed:\n" + "Verification of read data is invalid!\n" + "read_data[%d] = %d, but expected was %d", + i, data.Data[i], i); + return ERROR_FAIL; + } + } + + return STATUS_OK; +} + +/*!*************************************************************************** + * @brief GPIO interrupt callback function for the SPI transfer from IRQ test. + * + * @details The interrupt callback is invoked from the S2PI module upon + * receiving an GPIO interrupt from the devices IRQ pin. The callback + * is used by the #SpiTransferFromGpioInterrupt test to trigger the + * first SPI transfer from the interrupt callback context. + * + * @note The callback also utilizes the #print functionality. This requires + * a correct implementation of the corresponding function such that it + * can be invoked from the given interrupt context. This usually + * requires the underlying send (e.g. UART or USB send functions) to + * have higher priority that this interrupt in order to finished the + * print statement asynchronously. + * + * @param param The abstract interrupt callback parameter. + *****************************************************************************/ +static void SpiTransferFromGpioInterruptCallback(void *param) +{ + if (param == NULL) { + error_log("SPI transfer from GPIO interrupt test failed:\n" + "callback parameter \"param\" was NULL!"); + return; + } + + print("Invoking SPI transfer from GPIO interrupt callback...\n"); + + /* Clear the laser pattern and read back previous values. */ + spi_irq_data_t *data = (spi_irq_data_t *) param; + data->Data[0] = 0x04; // Laser Pattern Register Address + + for (uint8_t i = 1; i < 17U; ++i) { data->Data[i] = i; } + + status_t status = S2PI_TransferFrame(data->Slave, data->Data, data->Data, 17U, + SpiTransferFromSpiInterruptCallback, param); + + if (status != STATUS_OK) { + error_log("SPI transfer from GPIO interrupt test failed:\n" + "Calling S2PI_TransferFrame from GPIO interrupt " + "returned error code: %d", status); + data->Status = status; + return; + } +} + +/*!*************************************************************************** + * @brief SPI transfer from GPIO interrupt callback test. + * + * @details This test verifies the interrupt functionality of the SPI interface. + * The test verifies that an SPI transfer can be triggered from the + * GPIO interrupt service routine context. + * + * The test basically repeats the #SpiTransferFromSpiInterrupt but + * this time it invokes the first SPI transfer from the GPIO callback + * function. In order to trigger a GPIO interrupt, the device is + * configured and a measurement is started (see #GpioInterruptTest). + * + * @param slave The S2PI slave parameter passed to the S2PI HAL functions. + * + * @return Returns the \link #status_t status\endlink: + * - #STATUS_OK on success. + * - #ERROR_TIMEOUT if the test did not finish within a specified time. + * - #ERROR_FAIL if the device access failed and the read data did not + * match the expected values. + * - The S2PI layer error code if #S2PI_TransferFrame or the GPIO or + * SPI callback yield in any non-OK status. + *****************************************************************************/ +static status_t SpiTransferFromGpioInterrupt(s2pi_slave_t slave) +{ + /* Test parameter configuration: *****************************************/ + const uint32_t timeout_ms = 300; // timeout for measurement, might be increased.. + /*************************************************************************/ + + spi_irq_data_t data = { .Slave = slave }; + + /* Install IRQ callback. */ + status_t status = S2PI_SetIrqCallback(slave, SpiTransferFromGpioInterruptCallback, &data); + + if (status != STATUS_OK) { + error_log("SPI transfer from GPIO interrupt test failed:\n" + "The call to S2PI_SetIrqCallback returned error code: %d", status); + return status; + } + + /* Setup Device for invoking GPIO interrupt. */ + status = ConfigureDevice(slave, 0); + + if (status != STATUS_OK) { + error_log("SPI transfer from GPIO interrupt test failed."); + return status; + } + + /* Trigger Measurement and invoke GPIO interrupt. */ + status = TriggerMeasurement(slave, 0, 0, 0); + + if (status != STATUS_OK) { + error_log("GPIO interrupt test failed!"); + return status; + } + + ltc_t start; + Time_GetNow(&start); + + /* Wait for Interrupt using the callback method. */ + while (!data.Finished) { + if (Time_CheckTimeoutMSec(&start, timeout_ms)) { + error_log("SPI transfer from GPIO interrupt test failed:\n" + "The IRQ callback was not invoked within %d ms.", + timeout_ms); + return ERROR_TIMEOUT; + } + } + + if (data.Status != STATUS_OK) { + error_log("SPI transfer from GPIO interrupt test failed:\n" + "Waiting for the transfers to be finished yielded a error code: %d", + data.Status); + return data.Status; + } + + print("Verify read data...\n"); + + /* Verify the read pattern. */ + for (uint8_t i = 1; i < 17U; ++i) { + if (data.Data[i] != i) { + error_log("SPI transfer from GPIO interrupt test failed:\n" + "Verification of read data is invalid!\n" + "read_data[%d] = %d, but expected was %d", + i, data.Data[i], i); + return ERROR_FAIL; + } + } + + return STATUS_OK; +} + +/*!*************************************************************************** + * @brief PIT interrupt callback function for the SPI transfer from IRQ test. + * + * @details The interrupt callback is invoked from the PIT module upon periodic + * timeout event. The callback is used by the + * #SpiTransferFromPitInterrupt test to trigger the first SPI transfer + * from the interrupt callback context. + * + * @note The callback also utilizes the #print functionality. This requires + * a correct implementation of the corresponding function such that it + * can be invoked from the given interrupt context. This usually + * requires the underlying send (e.g. UART or USB send functions) to + * have higher priority that this interrupt in order to finished the + * print statement asynchronously. + * + * @param param The abstract interrupt callback parameter. + *****************************************************************************/ +static void SpiTransferFromPitInterruptCallback(void *param) +{ + status_t status = Timer_SetInterval(0, param); // disable timer + + if (status != STATUS_OK) { + error_log("SPI transfer from PIT interrupt test failed:\n" + "Timer_SetCallback to 0 returned status code: %d", + status); + + if (param != NULL) { ((spi_irq_data_t *)param)->Status = status; } + + return; + } + + + if (param == NULL) { + error_log("SPI transfer from PIT interrupt test failed:\n" + "callback parameter \"param\" was NULL!"); + return; + } + + print("Invoking SPI transfer from PIT interrupt callback...\n"); + + /* Clear the laser pattern and read back previous values. */ + spi_irq_data_t *data = (spi_irq_data_t *) param; + data->Data[0] = 0x04; // Laser Pattern Register Address + + for (uint8_t i = 1; i < 17U; ++i) { data->Data[i] = i; } + + status = S2PI_TransferFrame(data->Slave, data->Data, data->Data, 17U, + SpiTransferFromSpiInterruptCallback, param); + + if (status != STATUS_OK) { + error_log("SPI transfer from PIT interrupt test failed:\n" + "Calling S2PI_TransferFrame from GPIO interrupt " + "returned error code: %d", status); + data->Status = status; + return; + } +} + +/*!*************************************************************************** + * @brief SPI transfer from PIT interrupt callback test. + * + * @details This test verifies the interrupt functionality of the SPI interface. + * The test verifies that an SPI transfer can be triggered from the + * PIT interrupt service routine context. + * + * The test basically repeats the #SpiTransferFromSpiInterrupt but + * this time it invokes the first SPI transfer from the PIT callback + * function. In order to trigger a PIT interrupt, the timer is + * configured with a small interval and immediately disabled upon the + * first event. + * + * Note that this test is only executed if the PIT module is actually + * implemented. + * + * @param slave The S2PI slave parameter passed to the S2PI HAL functions. + * + * @return Returns the \link #status_t status\endlink: + * - #STATUS_OK on success. + * - #ERROR_NOT_IMPLEMENTED if the PIT functionality is not + * implemented and the test is skipped. + * - #ERROR_TIMEOUT if the test did not finish within a specified time. + * - #ERROR_FAIL if the device access failed and the read data did not + * match the expected values. + * - The S2PI layer error code if #S2PI_TransferFrame or the SPI + * callback yield in any non-OK status. + * - The PIT layer error code if #Timer_SetCallback or the PIT + * callback yield in any non-OK status. + *****************************************************************************/ +static status_t SpiTransferFromPitInterrupt(s2pi_slave_t slave) +{ + /* Test parameter configuration: *****************************************/ + const uint32_t timeout_ms = 100; // timeout for test. + const uint32_t interval_us = 1000; // PIT interval for the first event. + /*************************************************************************/ + + spi_irq_data_t data = { .Slave = slave }; + + status_t status = Timer_SetCallback(SpiTransferFromPitInterruptCallback); + + if (status == ERROR_NOT_IMPLEMENTED) { return status; } + + if (status != STATUS_OK) { + error_log("SPI transfer from PIT interrupt test failed:\n" + "Timer_SetCallback returned status code: %d", status); + return status; + } + + /* Setup the PIT callback with specified interval. */ + status = Timer_SetInterval(interval_us, &data); + + if (status != STATUS_OK) { + error_log("SPI transfer from PIT interrupt test failed:\n" + "Timer_SetInterval returned status code: %d", status); + return status; + } + + ltc_t start; + Time_GetNow(&start); + + /* Wait for test to be finished. */ + while (!data.Finished) { + if (Time_CheckTimeoutMSec(&start, timeout_ms)) { + error_log("SPI transfer from PIT interrupt test failed:\n" + "The IRQ callback was not invoked within %d ms.", + timeout_ms); + return ERROR_TIMEOUT; + } + } + + if (data.Status != STATUS_OK) { + error_log("SPI transfer from PIT interrupt test failed:\n" + "Waiting for the transfers to be finished yielded a error code: %d", + data.Status); + return data.Status; + } + + status = Timer_SetCallback(0); + + if (status != STATUS_OK) { + error_log("SPI transfer from PIT interrupt test failed:\n" + "Timer_SetCallback to 0 returned status code: %d", status); + return status; + } + + print("Verify read data...\n"); + + /* Verify the read pattern. */ + for (uint8_t i = 1; i < 17U; ++i) { + if (data.Data[i] != i) { + error_log("SPI transfer from PIT interrupt test failed:\n" + "Verification of read data is invalid!\n" + "read_data[%d] = %d, but expected was %d", + i, data.Data[i], i); + return ERROR_FAIL; + } + } + + return STATUS_OK; +} + +/*!*************************************************************************** + * @brief SPI Transfer from Interrupt Test for S2PI HAL Implementation. + * + * @details This test verifies the interrupt functionality of the SPI interface. + * The test verifies that an SPI transfer can be triggered from the + * interrupt service routine context. I.e. the #S2PI_TransferFrame + * function is called from the following interrupts: + * - SPI interrupt + * - GPIO interrupt + * - PIT interrupt (optional, if PIT is implemented) + * + * @warning The test utilizes already the timer HAL in order to implement a + * rudimentary timeout. However, at this time, only some basic + * plausibility checks are performed on the timer HAL. I.e. if there + * is an issue in the time HAL, e.g. too fast or too slow time + * counting, the test may fail with an #ERROR_TIMEOUT. In this case, + * one also needs to verify the timer HAL, especially the + * #Timer_GetCounterValue function. + * + * @param slave The S2PI slave parameter passed to the S2PI HAL functions. + * + * @return Returns the \link #status_t status\endlink: + * - #STATUS_OK on success. + * - #ERROR_TIMEOUT if the operation did not finished within a specified + * time (check also timer HAL implementation). + * - #ERROR_FAIL if the device access failed and the read data did not + * match the expected values. + * - The S2PI layer error code if #S2PI_TransferFrame or #S2PI_GetStatus + * return any negative status. + *****************************************************************************/ +static status_t SpiTransferFromInterruptTest(s2pi_slave_t slave) +{ + status_t status = STATUS_OK; + + print(" .1 >> SPI Transfer from SPI Interrupt Test\n"); + status = SpiTransferFromSpiInterrupt(slave); + + if (status != STATUS_OK) { return status; } + + print(" .1 >> PASS\n\n"); + + print(" .2 >> SPI Transfer from GPIO Interrupt Test\n"); + status = SpiTransferFromGpioInterrupt(slave); + + if (status != STATUS_OK) { return status; } + + print(" .2 >> PASS\n\n"); + + print(" .3 >> SPI Transfer from PIT Interrupt Test\n"); + status = SpiTransferFromPitInterrupt(slave); + + if (status == ERROR_NOT_IMPLEMENTED) { + print(" .3 >> SKIPPED (PIT is not implemented)\n\n"); + + } else { + if (status != STATUS_OK) { return status; } + + print(" .3 >> PASS\n\n"); + } + + return STATUS_OK; +} + +/*! @} */ diff --git a/src/drivers/distance_sensor/broadcom/afbrs50/argus_hal_test.h b/src/drivers/distance_sensor/broadcom/afbrs50/argus_hal_test.h index 5af6660dfa..be9c0071e4 100644 --- a/src/drivers/distance_sensor/broadcom/afbrs50/argus_hal_test.h +++ b/src/drivers/distance_sensor/broadcom/afbrs50/argus_hal_test.h @@ -1,10 +1,10 @@ /*************************************************************************//** - * @file argus_hal_test.c - * @brief Tests for the AFBR-S50 API hardware abstraction layer. + * @file + * @brief Tests for the AFBR-S50 API hardware abstraction layer. * * @copyright * - * Copyright (c) 2021, Broadcom, Inc. + * Copyright (c) 2023, Broadcom Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -41,147 +41,192 @@ extern "C" { #endif - /*!*************************************************************************** - * @defgroup argustest HAL Self Test + * @defgroup argus_test HAL Self Test + * @ingroup argus * - * @brief A test module to verify implementation of the HAL. + * @brief A test module to verify implementation of the HAL. * - * @details A series of automated tests that can be executed on the target - * platform in order to verify the implementation of the HAL that - * are required by the API. + * @details A series of automated tests that can be executed on the target + * platform in order to verify the implementation of the HAL that + * are required by the API. * - * @addtogroup argustest + * See #Argus_VerifyHALImplementation for a detailed documentation. + * + * @addtogroup argus_test * @{ *****************************************************************************/ #include "argus.h" /*!*************************************************************************** - * @brief Version number of the HAL Self Test. + * @brief Version number of the HAL Self Test. * * @details Changes: - * * v1.0: - * - Initial release. - * * v1.1: - * - Added additional print output. - * - Increased tolerance for timer test to 3%. - * - Fixed callback issue by disabling it after IRQ test. - * * v1.1: - * - Added PIT test cases. + * * v1.0: + * - Initial release. + * * v1.1: + * - Added additional print output. + * - Increased tolerance for timer test to 3%. + * - Fixed callback issue by disabling it after IRQ test. + * * v1.2: + * - Added PIT test cases. + * * v1.3: + * - Added test case for SPI maximum data transfer size. + * - Added tests for SPI transfers invoked from all IRQ callbacks. + * - Added verification of first PIT event occurrence. + * - Relaxed PIT pass conditions (0.1% -> 0.5%) + * * v1.4: + * - Adopted to new multi-device HAL interface of API v1.4.4 release. + * - Added verification of SPI callback invocation. + * - Updated GPIO interrupt test to verify if delayed interrupt + * pending states can be detected via #S2PI_ReadIrqPin. + * *****************************************************************************/ -#define HAL_TEST_VERSION "v1.2" +#define HAL_TEST_VERSION "v1.4" /*!*************************************************************************** - * @brief Executes a series of tests in order to verify the HAL implementation. + * @brief Executes a series of tests in order to verify the HAL implementation. * * @details A series of automated tests are executed on the target platform in - * order to verify the implementation of the HAL that are required by - * the API. + * order to verify the implementation of the HAL that are required by + * the API. * - * Each test will write an error description via the print (i.e. UART) - * function that shows what went wrong. Also an corresponding status is - * returned in case no print functionality is available. + * Each test will write an error description via the print (i.e. UART) + * function that shows what went wrong. Also an corresponding status is + * returned in case no print functionality is available. * - * The following tests are executed: + * The following tests are executed: * - * **1) Timer Plausibility Test:** + * **1) Timer Plausibility Test:** * - * Rudimentary tests of the lifetime counter (LTC) implementation. - * This verifies that the LTC is running by checking if the returned - * values of two consecutive calls to the #Timer_GetCounterValue - * function are ascending. An artificial delay using the NOP operation - * is induced such that the timer is not read to fast. + * Rudimentary tests of the lifetime counter (LTC) implementation. + * This verifies that the LTC is running by checking if the returned + * values of two consecutive calls to the #Timer_GetCounterValue + * function are ascending. An artificial delay using the NOP operation + * is induced such that the timer is not read to fast. * - * **2) Timer Wraparound Test:** + * **2) Timer Wraparound Test:** * - * The LTC values must wrap from 999999 µs to 0 µs and increase the - * seconds counter accordingly. This test verifies the correct wrapping - * by consecutively calling the #Timer_GetCounterValue function until - * at least 2 wraparound events have been occurred. + * The LTC values must wrap from 999999 µs to 0 µs and increase the + * seconds counter accordingly. This test verifies the correct wrapping + * by consecutively calling the #Timer_GetCounterValue function until + * at least 2 wraparound events have been occurred. * - * **3) SPI Connection Test:** + * **3) SPI Connection Test:** * - * This test verifies the basic functionality of the SPI interface. - * The test utilizes the devices laser pattern register, which can - * be freely programmed by any 128-bit pattern. Thus, it writes a byte - * sequence and reads back the written values on the consecutive SPI - * access. + * This test verifies the basic functionality of the SPI interface. + * The test utilizes the devices laser pattern register, which can + * be freely programmed by any 128-bit pattern. Thus, it writes a byte + * sequence and reads back the written values on the consecutive SPI + * access. * - * **4) SPI Interrupt Test:** + * **4) SPI Maximum Data Length Test**: * - * This test verifies the correct implementation of the device - * integration finished interrupt callback. Therefore it configures - * the device with a minimal setup to run a pseudo measurement that - * does not emit any laser light. + * This test verifies the maximum data transfer length of the SPI + * interface. The test sends and receives up to 396 data bytes plus + * a single address byte over the SPI interface and verifies that no + * data get lost. * - * Note that this test does verify the GPIO interrupt that occurs - * whenever the device has finished the integration/measurement and - * new data is waiting to be read from the device. This does not test - * the interrupt that is triggered when the SPI transfer has finished. + * **5) SPI Interrupt Test:** * - * The data ready interrupt implies two S2PI layer functions that - * are tested in this test: The #S2PI_SetIrqCallback function installs - * a callback function that is invoked whenever the IRQ occurs. - * The IRQ can be delayed due to higher priority task, e.g. from the - * user code. It is essential for the laser safety timeout algorithm - * to determine the device ready signal as fast as possible, another - * method is implemented to read if the IRQ is pending but the - * callback has not been reset yet. This is what the #S2PI_ReadIrqPin - * function is for. + * This test verifies the correct implementation of the device + * integration finished interrupt callback. Therefore it configures + * the device with a minimal setup to run a pseudo measurement that + * does not emit any laser light. * - * **5) GPIO Mode Test:** + * Note that this test does verify the GPIO interrupt that occurs + * whenever the device has finished the integration/measurement and + * new data is waiting to be read from the device. This does not test + * the interrupt that is triggered when the SPI transfer has finished. * - * This test verifies the GPIO mode of the S2PI HAL module. This is - * done by leveraging the EEPROM readout sequence that accesses the - * devices EEPROM via a software protocol that depends on the GPIO - * mode. + * The data ready interrupt implies two S2PI layer functions that + * are tested in this test: The #S2PI_SetIrqCallback function installs + * a callback function that is invoked whenever the IRQ occurs. + * The IRQ can be delayed due to higher priority task, e.g. from the + * user code. It is essential for the laser safety timeout algorithm + * to determine the device ready signal as fast as possible, another + * method is implemented to read if the IRQ is pending but the + * callback has not been reset yet. This is what the #S2PI_ReadIrqPin + * function is for. * - * This the requires several steps, most of them are already verified - * in previous tests: + * **6) GPIO Mode Test:** * - * - Basic device configuration and enable EEPROM. - * - Read EERPOM via GPIO mode and apply Hamming weight. - * - Repeat several times (to eliminate random readout issues). - * - Decode the EEPROM (using EEPROM_Decode in argus_cal_eeprom.c). - * - Check if Module Number and Chip ID is not 0. + * This test verifies the GPIO mode of the S2PI HAL module. This is + * done by leveraging the EEPROM readout sequence that accesses the + * devices EEPROM via a software protocol that depends on the GPIO + * mode. * - * **6) Timer Test for Lifetime Counter:** + * This the requires several steps, most of them are already verified + * in previous tests: * - * The test verifies the lifetime counter timer HAL implementation by - * comparing the timings to the AFBR-S50 device as a reference. - * Therefore several measurement are executed on the device, each with - * a different averaging sample count. The elapsed time increases - * linearly with the number of averaging samples. In order to remove - * the time for software/setup, a linear regression fit is applied to - * the measurement results and only the slope is considered for the - * result. A delta of 102.4 microseconds per sample is expected. - * If the measured delta per sample is within an specified error range, - * the timer implementation is considered correct. + * - Basic device configuration and enable EEPROM. + * - Read EERPOM via GPIO mode and apply Hamming weight. + * - Repeat several times (to eliminate random readout issues). + * - Decode the EEPROM (using EEPROM_Decode in argus_cal_eeprom.c). + * - Check if Module Number and Chip ID is not 0. * - * **7) Timer Test for Periodic Interrupt Timer:** + * **7) Timer Test for Lifetime Counter:** * - * The test verifies the correct implementation of the periodic - * interrupt timer (PIT). It sets different intervals and waits for - * a certain number of interrupts to happen. Each interrupt event - * is counted and the time between the first and the last interrupt - * is measured. Finally, the measured interval is compared to the - * expectations. + * The test verifies the lifetime counter timer HAL implementation by + * comparing the timings to the AFBR-S50 device as a reference. + * Therefore several measurement are executed on the device, each with + * a different averaging sample count. The elapsed time increases + * linearly with the number of averaging samples. In order to remove + * the time for software/setup, a linear regression fit is applied to + * the measurement results and only the slope is considered for the + * result. A delta of 102.4 microseconds per sample is expected. + * If the measured delta per sample is within an specified error range, + * the timer implementation is considered correct. * + * **8) Timer Test for Periodic Interrupt Timer (optional):** * - * @param spi_slave The SPI hardware slave, i.e. the specified CS and IRQ - * lines. This is actually just a number that is passed - * to the SPI interface to distinct for multiple SPI slave - * devices. Note that the slave must be not equal to 0, - * since is reserved for error handling. + * The test verifies the correct implementation of the periodic + * interrupt timer (PIT). It sets different intervals and waits for + * a certain number of interrupts to happen. Each interrupt event + * is counted and the time between the first and the last interrupt + * is measured. Finally, the measured interval is compared to the + * expectations. * - * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). + * Note that this test is only executed if the PIT is actually + * implemented. Otherwise, the test is skipped. + * + * **9) SPI Transfer from Interrupt Callback Test:** + * + * The test verifies that the #S2PI_TransferFrame method of the + * S2PI layer can be invoked from a interrupt callback function too. + * Thus, it repeats the S2PI Connection Test but this time from + * different interrupt callback functions: + * + * - SPI Callback: The first transfer is invoked from thread level, + * the second transfer is invoke from the SPI interrupt callback + * function. + * + * - GPIO Callback: The device is setup to trigger an GPIO interrupt + * (see also the SPI Interrupt Test). The corresponding GPIO + * interrupt callback function will trigger the first transfer while + * the second one is triggered from the SPI callback function. + * + * - PIT Callback (optional): This test is only executed optional if + * the PIT interface is implemented. The test sequence is the same + * as for the GPIO callback, but the first transfer is triggered + * from the PIT callback function. + * + * @note See #HAL_TEST_VERSION for a version history and change log of + * the HAL self tests. + * + * @param spi_slave The SPI hardware slave, i.e. the specified CS and IRQ + * lines. This is actually just a number that is passed + * to the SPI interface to distinct for multiple SPI slave + * devices. Note that the slave must be not equal to 0, + * since is reserved for error handling. + * + * @return Returns the \link #status_t status\endlink (#STATUS_OK on success). *****************************************************************************/ status_t Argus_VerifyHALImplementation(s2pi_slave_t spi_slave); -#ifdef __cplusplus -} -#endif - /*! @} */ -#endif /* ARGUS_CAL_API_H */ +#ifdef __cplusplus +} // extern "C" +#endif +#endif /* ARGUS_HAL_TEST_H */ diff --git a/src/drivers/distance_sensor/broadcom/afbrs50/parameters.c b/src/drivers/distance_sensor/broadcom/afbrs50/parameters.c new file mode 100644 index 0000000000..28ef2a17c1 --- /dev/null +++ b/src/drivers/distance_sensor/broadcom/afbrs50/parameters.c @@ -0,0 +1,102 @@ +/**************************************************************************** + * + * Copyright (c) 2023 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/** + * AFBR Rangefinder Mode + * + * This parameter defines the mode of the AFBR Rangefinder. + * + * @reboot_required true + * @min 0 + * @max 3 + * @group Sensors + * + * @value 0 Short Range Mode + * @value 1 Long Range Mode + * @value 2 High Speed Short Range Mode + * @value 3 High Speed Long Range Mode + */ +PARAM_DEFINE_INT32(SENS_AFBR_MODE, 1); + +/** + * AFBR Rangefinder Short Range Rate + * + * This parameter defines measurement rate of the AFBR Rangefinder in short range mode. + * + * @min 1 + * @max 100 + * @group Sensors + * + */ +PARAM_DEFINE_INT32(SENS_AFBR_S_RATE, 50); + +/** + * AFBR Rangefinder Long Range Rate + * + * This parameter defines measurement rate of the AFBR Rangefinder in long range mode. + * + * @min 1 + * @max 100 + * @group Sensors + * + */ +PARAM_DEFINE_INT32(SENS_AFBR_L_RATE, 25); + +/** + * AFBR Rangefinder Short/Long Range Threshold + * + * This parameter defines the threshold for switching between short and long range mode. + * The mode will switch from short to long range when the distance is greater than the threshold plus the hysteresis. + * The mode will switch from long to short range when the distance is less than the threshold minus the hysteresis. + * + * @unit m + * @min 1 + * @max 50 + * @group Sensors + * + */ +PARAM_DEFINE_INT32(SENS_AFBR_THRESH, 5); + + +/** + * AFBR Rangefinder Short/Long Range Threshold Hysteresis + * + * This parameter defines the hysteresis for switching between short and long range mode. + * + * @unit m + * @min 1 + * @max 10 + * @group Sensors + * + */ +PARAM_DEFINE_INT32(SENS_AFBR_HYSTER, 1); diff --git a/src/drivers/uavcan/sensors/rangefinder.cpp b/src/drivers/uavcan/sensors/rangefinder.cpp index cff084292c..5e15b8d51f 100644 --- a/src/drivers/uavcan/sensors/rangefinder.cpp +++ b/src/drivers/uavcan/sensors/rangefinder.cpp @@ -108,14 +108,13 @@ void UavcanRangefinderBridge::range_sub_cb(const _inited = true; } - /* - * FIXME HACK - * This code used to rely on msg.getMonotonicTimestamp().toUSec() instead of HRT. - * It stopped working when the time sync feature has been introduced, because it caused libuavcan - * to use an independent time source (based on hardware TIM5) instead of HRT. - * The proper solution is to be developed. - */ - rangefinder->update(hrt_absolute_time(), msg.range); + int8_t quality = -1; + + if (msg.reading_type == uavcan::equipment::range_sensor::Measurement::READING_TYPE_VALID_RANGE) { + quality = 100; + } + + rangefinder->update(hrt_absolute_time(), msg.range, quality); } int UavcanRangefinderBridge::init_driver(uavcan_bridge::Channel *channel)