lib/adc: SelectContinuousReadAdcs

- Adds Adc::SelectContinuousReadAdcs which allows setting which ADC
  channels are sampled in continuous read mode.
- Fix missing static_cast in Adc::VoltageMeasurement()
- Correct continuous read mode order to vshunt followed by vbus.

Change-Id: I0590af949399b6d46fd930df7575bab9f2fa2222
Reviewed-on: https://pigweed-review.googlesource.com/c/gonk/+/189640
Commit-Queue: Anthony DiGirolamo <tonymd@google.com>
Reviewed-by: Umang Shah <umangshah@google.com>
Reviewed-by: Eric Holland <hollande@google.com>
diff --git a/applications/fpga_config/main.cc b/applications/fpga_config/main.cc
index 5c48e8d..6492d01 100644
--- a/applications/fpga_config/main.cc
+++ b/applications/fpga_config/main.cc
@@ -127,6 +127,8 @@
     // Init ADCs
     fpga_adc.InitAdcs();
     fpga_adc.CheckAllAdcs();
+    // Select the first five ADC channels.
+    fpga_adc.SelectContinuousReadAdcs(0b11111);
 
     PW_LOG_INFO("Switching to ADC binary log");
     fpga_adc.SetContinuousReadMode();
diff --git a/lib/adc/adc.cc b/lib/adc/adc.cc
index 091513e..40ed56e 100644
--- a/lib/adc/adc.cc
+++ b/lib/adc/adc.cc
@@ -86,6 +86,7 @@
       fpga_cs_(fpga_cs_pin),
       spi_settings_(fpga_spi_baudrate, MSBFIRST, SPI_MODE1) {
   sample_read_index_ = 0;
+  sampled_adc_count_ = kMaxStreamingAdcCount;
   measurement_timestamp_ = 0;
   previous_timestamp_ = 0;
   sample_read_time_.fill(0);
@@ -199,6 +200,49 @@
       | mode);
 }
 
+uint32_t Adc::ADCAddressWriteSelection(uint16_t adc_selection,
+                                       uint8_t adc_command) {
+  return (
+      // Set adc selection bits
+      (adc_selection & 0x7FF) << 7
+      // ADC Register
+      | ((adc_command & 0x3f) << 1)
+      // 1=read, 0=write
+      | 0);
+}
+
+Status Adc::SelectContinuousReadAdcs(uint16_t adc_selection) {
+  // Count the number of selected ADCs.
+  uint16_t selected_bits = adc_selection;
+  uint16_t set_bits_count;
+  for (set_bits_count = 0; selected_bits; selected_bits >>= 1) {
+    set_bits_count += selected_bits & 1;
+  }
+  sampled_adc_count_ = set_bits_count;
+  if (sampled_adc_count_ > 11 || sampled_adc_count_ == 0) {
+    PW_LOG_ERROR("Invalid ADC channel selection: %x", adc_selection);
+    return pw::Status::OutOfRange();
+  }
+
+  PW_LOG_INFO("Selected ADC count: %d", sampled_adc_count_);
+
+  uint32_t adc_address =
+      ADCAddressWriteSelection(adc_selection, /*adc_command=*/0);
+
+  // Set bit 18 to signal to the FPGA to treat this as a continuous read mode
+  // update.
+  adc_address = 1 << 18 | adc_address;
+
+  StartSpiTransaction();
+  // Write the ADC address selection.
+  WriteAddress(adc_address);
+  // NOTE: The FPGA does not generate a valid signal for this write.
+  EndSpiTransaction();
+
+  // return wait_result;
+  return pw::OkStatus();
+}
+
 uint32_t Adc::ADCAddressWrite(uint8_t adc_number, uint8_t adc_command) {
   // Address with mode=0 for a write.
   return ADCAddress(adc_number, adc_command, 0);
@@ -311,20 +355,8 @@
   previous_timestamp_ = measurement_timestamp_;
   measurement_timestamp_ = micros();
   measurement_delta_micros_ = measurement_timestamp_ - previous_timestamp_;
-  for (int i = 0; i < kMaxStreamingAdcCount; i++) {
-    // Read VBUS
-    std::array<std::byte, 3> vbus_read_buffer;
-    const pw::Result<pw::ConstByteSpan> vbus_read_result =
-        ReadData(pw::ByteSpan(vbus_read_buffer));
-    if (!vbus_read_result.ok()) {
-      PW_LOG_ERROR("vbus_read failed i=%d", i);
-      return vbus_read_result.status();
-    }
-    int32_t vbus_value = VoltageMeasurement(vbus_read_result.value());
-    PW_LOG_DEBUG("Continuous Read ADC #%02d: VBUS   = %02x %02x %02x = %d", i,
-                 vbus_read_result.value()[0], vbus_read_result.value()[1],
-                 vbus_read_result.value()[2], vbus_value);
 
+  for (int i = 0; i < sampled_adc_count_; i++) {
     // Read VSHUNT
     std::array<std::byte, 3> vshunt_read_buffer;
     const pw::Result<pw::ConstByteSpan> vshunt_read_result =
@@ -338,6 +370,19 @@
                  vshunt_read_result.value()[0], vshunt_read_result.value()[1],
                  vshunt_read_result.value()[2], vshunt_value);
 
+    // Read VBUS
+    std::array<std::byte, 3> vbus_read_buffer;
+    const pw::Result<pw::ConstByteSpan> vbus_read_result =
+        ReadData(pw::ByteSpan(vbus_read_buffer));
+    if (!vbus_read_result.ok()) {
+      PW_LOG_ERROR("vbus_read failed i=%d", i);
+      return vbus_read_result.status();
+    }
+    int32_t vbus_value = VoltageMeasurement(vbus_read_result.value());
+    PW_LOG_DEBUG("Continuous Read ADC #%02d: VBUS   = %02x %02x %02x = %d", i,
+                 vbus_read_result.value()[0], vbus_read_result.value()[1],
+                 vbus_read_result.value()[2], vbus_value);
+
     // Save this measurement.
     measurements_[i].vbus_value_ = vbus_value;
     measurements_[i].vshunt_value_ = vshunt_value;
@@ -382,7 +427,7 @@
       PW_LOG_ERROR("WriteTimestamp: %d", status);
       return status;
     }
-    for (size_t i = 0; i < kMaxStreamingAdcCount; i++) {
+    for (size_t i = 0; i < sampled_adc_count_; i++) {
       auto adc_encoder = payload.GetAdcMeasurementsEncoder();
       status = adc_encoder.WriteVbusValue(measurements_[i].vbus_value_);
       if (!status.ok()) {
@@ -487,9 +532,9 @@
   // read_buffer[2] >> 4                   76543210--->
   // result                98765432109876543210|
   //
-  return pw::bytes::SignExtend<20>((uint32_t)read_buffer[0] << 12 |
-                                   (uint32_t)read_buffer[1] << 4 |
-                                   (uint32_t)read_buffer[2] >> 4);
+  return pw::bytes::SignExtend<20>(static_cast<uint32_t>(read_buffer[0]) << 12 |
+                                   static_cast<uint32_t>(read_buffer[1]) << 4 |
+                                   static_cast<uint32_t>(read_buffer[2]) >> 4);
 }
 
 Status Adc::InitAdcs() {
diff --git a/lib/adc/public/gonk/adc.h b/lib/adc/public/gonk/adc.h
index ee0393f..9607fd6 100644
--- a/lib/adc/public/gonk/adc.h
+++ b/lib/adc/public/gonk/adc.h
@@ -45,6 +45,7 @@
   Status WriteRegister(uint8_t adc_number, uint8_t adc_register,
                        pw::ByteSpan write_buffer);
   Status WriteRegisterAll(uint8_t adc_register, pw::ByteSpan write_buffer);
+  Status SelectContinuousReadAdcs(uint16_t adc_selection);
 
   // ADC Specific functions.
   pw::Result<pw::ConstByteSpan> GetManufacturerID(uint8_t adc_number);
@@ -75,6 +76,7 @@
   SPIClass &fpga_spi_;
   SPISettings spi_settings_;
 
+  uint8_t sampled_adc_count_;
   uint8_t sample_read_index_;
   std::array<uint32_t, 16> sample_read_time_;
 
@@ -85,6 +87,8 @@
   uint32_t ADCAddress(uint8_t adc_number, uint8_t adc_command, uint8_t mode);
   uint32_t ADCAddressWrite(uint8_t adc_number, uint8_t adc_command);
   uint32_t ADCAddressWriteAll(uint8_t adc_command);
+  uint32_t ADCAddressWriteSelection(uint16_t adc_selection,
+                                    uint8_t adc_command);
 
   pw::Result<pw::ConstByteSpan> GetThreeBytes();