LCOV - code coverage report
Current view: top level - AP_RangeFinder - AP_RangeFinder_LightWare_GRF_I2C.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 79 92 85.9 %
Date: 2026-09-09 11:02:08 Functions: 7 7 100.0 %

          Line data    Source code
       1             : #include "AP_RangeFinder_config.h"
       2             : 
       3             : #if AP_RANGEFINDER_LIGHTWARE_GRF_I2C_ENABLED
       4             : 
       5             : #include "AP_RangeFinder_LightWare_GRF_I2C.h"
       6             : 
       7             : #include <AP_HAL/AP_HAL.h>
       8             : #include <AP_HAL/utility/sparse-endian.h>
       9             : #include <GCS_MAVLink/GCS.h>
      10             : 
      11             : extern const AP_HAL::HAL& hal;
      12             : 
      13             : #define GRF_I2C_PRODUCT_NAME_PAYLOAD_LEN 16
      14             : #define GRF_I2C_U32_PAYLOAD_LEN          4
      15             : #define GRF_I2C_DISTANCE_PAYLOAD_LEN     8
      16             : #define GRF_I2C_MAX_WRITE_PAYLOAD_LEN    16
      17             : #define GRF_I2C_HANDSHAKE_ATTEMPTS       4
      18             : #define GRF_I2C_HANDSHAKE_RETRY_MS       30
      19             : #define GRF_I2C_POLL_HZ                  50
      20             : 
      21             : const AP_Param::GroupInfo AP_RangeFinder_LightWare_GRF_I2C::var_info[] = {
      22             :     // @Group: GRF_
      23             :     // @Path: AP_RangeFinder_LightWare_GRF_Common.cpp
      24             :     AP_SUBGROUPINFO(return_selection, "GRF_", 12, AP_RangeFinder_LightWare_GRF_I2C, AP_RangeFinder_LightWare_GRF_Common),
      25             : 
      26             :     AP_GROUPEND
      27             : };
      28             : 
      29           3 : AP_RangeFinder_LightWare_GRF_I2C::AP_RangeFinder_LightWare_GRF_I2C(
      30             :     RangeFinder::RangeFinder_State &_state,
      31             :     AP_RangeFinder_Params &_params,
      32           3 :     AP_HAL::I2CDevice &_dev)
      33           3 :     : AP_RangeFinder_Backend_I2C(_state, _params, _dev)
      34             : {
      35           3 :     state.var_info = var_info;
      36           3 : }
      37             : 
      38             : // Write a command
      39           2 : bool AP_RangeFinder_LightWare_GRF_I2C::write_command(MessageID msgid,
      40             :                                                     const uint8_t *payload,
      41             :                                                     uint16_t payload_len)
      42             : {
      43             :     uint8_t tx[1 + GRF_I2C_MAX_WRITE_PAYLOAD_LEN];
      44           2 :     if (1U + payload_len > sizeof(tx)) {
      45           0 :         return false;
      46             :     }
      47           2 :     tx[0] = (uint8_t)msgid;
      48           2 :     if (payload_len > 0 && payload != nullptr) {
      49           2 :         memcpy(&tx[1], payload, payload_len);
      50             :     }
      51           2 :     return dev.transfer(tx, 1U + payload_len, nullptr, 0);
      52             : }
      53             : 
      54             : // Read a register and hand back the raw bytes.
      55        3457 : bool AP_RangeFinder_LightWare_GRF_I2C::read_payload(MessageID msgid,
      56             :                                                    uint8_t *payload_buf,
      57             :                                                    uint16_t payload_len)
      58             : {
      59        3457 :     const uint8_t reg = (uint8_t)msgid;
      60        3457 :     return dev.transfer(&reg, 1, payload_buf, payload_len);
      61             : }
      62             : 
      63           3 : bool AP_RangeFinder_LightWare_GRF_I2C::init()
      64             : {
      65           3 :     WITH_SEMAPHORE(dev.get_semaphore());
      66           3 :     dev.set_retries(2);
      67             : 
      68             :     // Ask the sensor for its product name. The reply starts with "GRF" if
      69             :     // we've found a GRF-family rangefinder. Retry a few times in case the
      70             :     // sensor is still waking up.
      71             :     uint8_t name[GRF_I2C_PRODUCT_NAME_PAYLOAD_LEN];
      72           3 :     bool detected = false;
      73          12 :     for (uint8_t attempt = 0; attempt < GRF_I2C_HANDSHAKE_ATTEMPTS && !detected; attempt++) {
      74          10 :         if (!read_payload(MessageID::PRODUCT_NAME, name, sizeof(name)) ||
      75           1 :             !matches_product_name(name, sizeof(name))) {
      76           8 :             hal.scheduler->delay(GRF_I2C_HANDSHAKE_RETRY_MS);
      77           8 :             continue;
      78             :         }
      79           1 :         detected = true;
      80             :     }
      81           3 :     if (!detected) {
      82           2 :         return false;
      83             :     }
      84           1 :     name[sizeof(name) - 1] = '\0';
      85           1 :     GCS_SEND_TEXT(MAV_SEVERITY_INFO, "LightWare %s detected (I2C)", (const char*)name);
      86             : 
      87             :     // Configuration happens inside the timer so we don't hold up boot.
      88           1 :     _config_step = ConfigStep::WRITE_UPDATE_RATE;
      89           1 :     _config_retry = 0;
      90             : 
      91           1 :     dev.register_periodic_callback(1000000UL / GRF_I2C_POLL_HZ,
      92             :         FUNCTOR_BIND_MEMBER(&AP_RangeFinder_LightWare_GRF_I2C::timer, void));
      93             : 
      94           1 :     return true;
      95           3 : }
      96             : 
      97             : // Run one tick of the config FSM: each call does exactly one I2C op.
      98             : // Each WRITE_* step is followed by a VERIFY_* step that reads the register
      99             : // back; mismatches retry the write a few times then give up and move on.
     100           4 : void AP_RangeFinder_LightWare_GRF_I2C::run_config_step()
     101             : {
     102             :     uint8_t payload[GRF_I2C_U32_PAYLOAD_LEN];
     103             :     uint8_t rx[GRF_I2C_U32_PAYLOAD_LEN];
     104             : 
     105             :     // Cap the user-set rate so a misconfigured GRF_RATE doesn't escape onto
     106             :     // the bus. The ceiling is well above the datasheet max — sane values
     107             :     // pass through untouched.
     108           4 :     const uint32_t rate_value = constrain_int16(update_rate, 1, GRF_MAX_RATE_HZ);
     109             : 
     110           4 :     switch (_config_step) {
     111           1 :     case ConfigStep::WRITE_UPDATE_RATE:
     112           1 :         put_le32_ptr(payload, rate_value);
     113           1 :         (void)write_command(MessageID::UPDATE_RATE, payload, sizeof(payload));
     114           1 :         _config_step = ConfigStep::VERIFY_UPDATE_RATE;
     115           4 :         return;
     116             : 
     117           1 :     case ConfigStep::VERIFY_UPDATE_RATE:
     118           1 :         put_le32_ptr(payload, rate_value);
     119           2 :         if (!read_payload(MessageID::UPDATE_RATE, rx, sizeof(rx)) ||
     120           1 :             memcmp(rx, payload, sizeof(rx)) != 0) {
     121           0 :             if (++_config_retry < 3) {
     122           0 :                 _config_step = ConfigStep::WRITE_UPDATE_RATE;
     123           0 :                 return;
     124             :             }
     125             :         }
     126           1 :         _config_retry = 0;
     127           1 :         _config_step = ConfigStep::WRITE_DISTANCE_OUTPUT;
     128           1 :         return;
     129             : 
     130           1 :     case ConfigStep::WRITE_DISTANCE_OUTPUT:
     131           1 :         put_le32_ptr(payload, build_distance_output_bitmask());
     132           1 :         (void)write_command(MessageID::DISTANCE_OUTPUT, payload, sizeof(payload));
     133           1 :         _config_step = ConfigStep::VERIFY_DISTANCE_OUTPUT;
     134           1 :         return;
     135             : 
     136           1 :     case ConfigStep::VERIFY_DISTANCE_OUTPUT:
     137           1 :         put_le32_ptr(payload, build_distance_output_bitmask());
     138           2 :         if (!read_payload(MessageID::DISTANCE_OUTPUT, rx, sizeof(rx)) ||
     139           1 :             memcmp(rx, payload, sizeof(rx)) != 0) {
     140           0 :             if (++_config_retry < 3) {
     141           0 :                 _config_step = ConfigStep::WRITE_DISTANCE_OUTPUT;
     142           0 :                 return;
     143             :             }
     144             :         }
     145           1 :         _config_retry = 0;
     146           1 :         _config_step = ConfigStep::DONE;
     147           1 :         return;
     148             : 
     149           0 :     case ConfigStep::DONE:
     150           0 :         return;
     151             :     }
     152             : }
     153             : 
     154        3450 : void AP_RangeFinder_LightWare_GRF_I2C::timer()
     155             : {
     156             :     // Finish boot-time config before we start polling for distance readings.
     157             :     // The gap between timer calls gives the sensor time to apply each write.
     158        3450 :     if (_config_step != ConfigStep::DONE) {
     159           4 :         run_config_step();
     160           4 :         return;
     161             :     }
     162             : 
     163             :     uint8_t rx[GRF_I2C_DISTANCE_PAYLOAD_LEN];
     164        3446 :     if (!read_payload(MessageID::DISTANCE_DATA_CM, rx, sizeof(rx))) {
     165           0 :         return;
     166             :     }
     167             : 
     168             :     float dist_m;
     169        3446 :     if (!parse_distance_cm_payload(rx, sizeof(rx), dist_m)) {
     170           0 :         return;
     171             :     }
     172             : 
     173        3446 :     WITH_SEMAPHORE(_sem);
     174        3446 :     _accum.sum_m += dist_m;
     175        3446 :     _accum.count++;
     176        3446 : }
     177             : 
     178        1348 : void AP_RangeFinder_LightWare_GRF_I2C::update()
     179             : {
     180        1348 :     WITH_SEMAPHORE(_sem);
     181             : 
     182        1348 :     if (_accum.count > 0) {
     183        1348 :         state.distance_m = _accum.sum_m / _accum.count;
     184        1348 :         state.last_reading_ms = AP_HAL::millis();
     185        1348 :         _accum.sum_m = 0;
     186        1348 :         _accum.count = 0;
     187        1348 :         update_status();
     188           0 :     } else if (AP_HAL::millis() - state.last_reading_ms > 500) {
     189           0 :         set_status(RangeFinder::Status::NoData);
     190             :     }
     191        1348 : }
     192             : 
     193             : #endif  // AP_RANGEFINDER_LIGHTWARE_GRF_I2C_ENABLED

Generated by: LCOV version 1.14