LCOV - code coverage report
Current view: top level - SITL - SIM_INA3221.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 91 100 91.0 %
Date: 2026-09-09 11:02:08 Functions: 6 6 100.0 %

          Line data    Source code
       1             : #include "SIM_config.h"
       2             : 
       3             : #if AP_SIM_INA3221_ENABLED
       4             : 
       5             : #include "SIM_INA3221.h"
       6             : 
       7             : #include <SITL/SITL.h>
       8             : 
       9        2145 : SITL::INA3221::INA3221()
      10             : {
      11        2145 :     writable_registers.set(0);
      12        2145 :     writable_registers.set(7);
      13        2145 :     writable_registers.set(8);
      14        2145 :     writable_registers.set(9);
      15        2145 :     writable_registers.set(10);
      16        2145 :     writable_registers.set(11);
      17        2145 :     writable_registers.set(12);
      18        2145 :     writable_registers.set(14);
      19        2145 :     writable_registers.set(15);
      20        2145 :     writable_registers.set(16);
      21        2145 :     writable_registers.set(254);
      22        2145 :     writable_registers.set(255);
      23             : 
      24        2145 :     reset();
      25        2145 : }
      26             : 
      27        2147 : void SITL::INA3221::reset()
      28             : {
      29             :     // from page 24 of datasheet:
      30        2147 :     registers.byname.configuration.word = 0x7127;
      31        2147 :     registers.byname.Channel_1_Shunt_Voltage = 0x0;
      32        2147 :     registers.byname.Channel_1_Bus_Voltage = 0x0;
      33        2147 :     registers.byname.Channel_2_Shunt_Voltage = 0x0;
      34        2147 :     registers.byname.Channel_2_Bus_Voltage = 0x0;
      35        2147 :     registers.byname.Channel_3_Shunt_Voltage = 0x0;
      36        2147 :     registers.byname.Channel_3_Bus_Voltage = 0x0;
      37        2147 :     registers.byname.Channel_1_CriticalAlertLimit = 0x7FF8;
      38        2147 :     registers.byname.Channel_1_WarningAlertLimit = 0x7FF8;
      39        2147 :     registers.byname.Channel_2_CriticalAlertLimit = 0x7FF8;
      40        2147 :     registers.byname.Channel_2_WarningAlertLimit = 0x7FF8;
      41        2147 :     registers.byname.Channel_3_CriticalAlertLimit = 0x7FF8;
      42        2147 :     registers.byname.Channel_3_WarningAlertLimit = 0x7FF8;
      43        2147 :     registers.byname.Shunt_VoltageSum = 0x0;
      44        2147 :     registers.byname.Shunt_VoltageSumLimit = 0x7FFE;
      45        2147 :     registers.byname.Mask_Enable = 0x0002;
      46        2147 :     registers.byname.Power_ValidUpperLimit = 0x2710;
      47        2147 :     registers.byname.Power_ValidLowerLimit = 0x2328;
      48        2147 :     registers.byname.ManufacturerID = MANUFACTURER_ID;
      49        2147 :     registers.byname.Die_ID = DIE_ID;
      50        2147 : }
      51             : 
      52         562 : int SITL::INA3221::rdwr(I2C::i2c_rdwr_ioctl_data *&data)
      53             : {
      54         562 :     if (data->nmsgs == 2) {
      55         560 :         if (data->msgs[0].flags != 0) {
      56           0 :             AP_HAL::panic("Unexpected flags");
      57             :         }
      58         560 :         if (data->msgs[1].flags != I2C_M_RD) {
      59           0 :             AP_HAL::panic("Unexpected flags");
      60             :         }
      61         560 :         const uint8_t reg_addr = data->msgs[0].buf[0];
      62         560 :         const uint16_t register_value = registers.word[reg_addr];
      63         560 :         data->msgs[1].buf[0] = register_value >> 8;
      64         560 :         data->msgs[1].buf[1] = register_value & 0xFF;
      65         560 :         data->msgs[1].len = 2;
      66         560 :         return 0;
      67             :     }
      68             : 
      69           2 :     if (data->nmsgs == 1) {
      70           2 :         if (data->msgs[0].flags != 0) {
      71           0 :             AP_HAL::panic("Unexpected flags");
      72             :         }
      73           2 :         const uint8_t reg_addr = data->msgs[0].buf[0];
      74           2 :         if (!writable_registers.get(reg_addr)) {
      75           0 :             AP_HAL::panic("Register 0x%02x is not writable!", reg_addr);
      76             :         }
      77           2 :         const uint16_t register_value = data->msgs[0].buf[2] << 8 | data->msgs[0].buf[1];
      78           2 :         registers.word[reg_addr] = register_value;
      79           2 :         return 0;
      80             :     }
      81             : 
      82           0 :     return -1;
      83             : };
      84             : 
      85   536279826 : static uint16_t convert_voltage(float voltage_v) {
      86             :     // 8mV per count, register value is x8 (3 lowest bits not used)
      87   536279826 :     float volt_counts = (voltage_v/8e-3)*8;
      88   536279826 :     if (volt_counts < INT16_MIN) {
      89           0 :         volt_counts = INT16_MIN;
      90   536279826 :     } else if (volt_counts > INT16_MAX) {
      91      734723 :         volt_counts = INT16_MAX;
      92             :     }
      93             : 
      94             :     // register value is signed
      95   536279826 :     return (uint16_t)((int16_t)volt_counts);
      96             : }
      97             : 
      98   536279826 : static uint16_t convert_current(float current_a) {
      99             :     // V = IR
     100   536279826 :     const float shunt_resistance_ohms = 0.001; // default value in driver
     101   536279826 :     const float shunt_voltage = current_a*shunt_resistance_ohms;
     102             : 
     103             :     // 40uV per count, register value is x8 (3 lowest bits not used)
     104   536279826 :     float shunt_counts = (shunt_voltage/40e-6)*8;
     105   536279826 :     if (shunt_counts < INT16_MIN) {
     106           0 :         shunt_counts = INT16_MIN;
     107   536279826 :     } else if (shunt_counts > INT16_MAX) {
     108      208970 :         shunt_counts = INT16_MAX;
     109             :     }
     110             : 
     111             :     // register value is signed
     112   536279826 :     return (uint16_t)((int16_t)shunt_counts);
     113             : }
     114             : 
     115             : 
     116   178759942 : void SITL::INA3221::update(const class Aircraft &aircraft)
     117             : {
     118   178759942 :     if (registers.byname.configuration.bits.reset != 0) {
     119           2 :         reset();
     120             :     }
     121             : 
     122             :     // update readings
     123   178759942 :     if (registers.byname.configuration.bits.mode == 0b000 ||
     124   178759942 :         registers.byname.configuration.bits.mode == 0b100) {
     125             :         // power-off
     126           0 :         return;
     127             :     }
     128             : 
     129   178759942 :     const bool update_shunt = registers.byname.configuration.bits.mode & 0b001;
     130   178759942 :     const bool update_bus = registers.byname.configuration.bits.mode & 0b010;
     131   178759942 :     if ((registers.byname.configuration.bits.mode & 0b100) == 0) {
     132             :         // single-shot only
     133           0 :         registers.byname.configuration.bits.mode &= ~0b011;
     134             :     }
     135             : 
     136             :     // channel 2 gets the first simulated battery's voltage and current, others are test values:
     137   178759942 :     if (registers.byname.configuration.bits.ch1_enable) {
     138             :         // values close to chip limits (assuming 1mOhm current shunt)
     139   178759942 :         if (update_bus) {
     140   178759942 :             registers.byname.Channel_1_Bus_Voltage = convert_voltage(25); // max of 26V
     141             :         }
     142   178759942 :         if (update_shunt) {
     143   178759942 :             registers.byname.Channel_1_Shunt_Voltage = convert_current(160); // max of 163.8A
     144             :         }
     145             :     }
     146             : 
     147   178759942 :     if (registers.byname.configuration.bits.ch2_enable) {
     148   178759942 :         if (update_bus) {
     149   178759942 :             registers.byname.Channel_2_Bus_Voltage = convert_voltage(AP::sitl()->state.battery_voltage);
     150             :         }
     151   178759942 :         if (update_shunt) {
     152   178759942 :             registers.byname.Channel_2_Shunt_Voltage = convert_current(AP::sitl()->state.battery_current);
     153             :         }
     154             :     }
     155             : 
     156   178759942 :     if (registers.byname.configuration.bits.ch3_enable) {
     157             :         // values different from above to test channel switching
     158   178759942 :         if (update_bus) {
     159   178759942 :             registers.byname.Channel_3_Bus_Voltage = convert_voltage(3.14159);
     160             :         }
     161   178759942 :         if (update_shunt) {
     162   178759942 :             registers.byname.Channel_3_Shunt_Voltage = convert_current(2.71828);
     163             :         }
     164             :     }
     165             : }
     166             : 
     167             : #endif  // AP_SIM_INA3221_ENABLED

Generated by: LCOV version 1.14