On this page
article
peary/hardware/boards/Carboard.hpp
peary/hardware/boards/Carboard.hpp
Carboard Class. More…
Namespaces
| Name |
|---|
| peary |
| peary::board |
| peary::board::carboard |
Classes
| Name | |
|---|---|
| class | peary::board::carboard::EEPROM EEPROM resource for CaR board. |
| class | peary::board::carboard::ClockGenerator Clock generator resource for CaR board. |
| class | peary::board::carboard::ZynqClockGenerator Clock generator resource on Zynq board. |
| class | peary::board::carboard::TemperatureSensor Temperature sensor resource for CaR board. |
| class | peary::board::carboard::ADCInput ADC input resource for CaR board. |
| class | peary::board::carboard::BiasRegulator Bias voltage regulator resource for CaR board. |
| class | peary::board::carboard::CMOSLevels CMOS level shifter resource for CaR board. |
| class | peary::board::carboard::CurrentSource Current source resource for CaR board. |
| class | peary::board::carboard::VoltageRegulator Voltage regulator resource for CaR board. |
| class | peary::board::carboard::Carboard Carboard class representing the CaR board. |
Detailed Description
Carboard Class.
Copyright: Copyright (c) 2016-2025 CERN and the Peary Caribou authors. This software is distributed under the terms of the LGPL-3.0-only License, copied verbatim in the file “LICENSE.md”. SPDX-License-Identifier: LGPL-3.0-only
Source code
#pragma once
#include <any>
#include <chrono>
#include <thread>
#include "peary/hal/Board.hpp"
#include "peary/hal/Resources.hpp"
#include "peary/hardware/boards/CarboardConstants.hpp"
#include "peary/hardware/components/ADS7828.hpp"
#include "peary/hardware/components/DAC7678.hpp"
#include "peary/hardware/components/EEPROM24LC32A.hpp"
#include "peary/hardware/components/INA226.hpp"
#include "peary/hardware/components/PCA9539.hpp"
#include "peary/hardware/components/Si5345.hpp"
#include "peary/hardware/components/Si570.hpp"
#include "peary/hardware/components/TMP101.hpp"
namespace peary::board::carboard {
class EEPROM : public hal::E2Writer, public hal::E2Reader {
public:
EEPROM(component::EEPROM24LC32A& eeprom) noexcept : _eeprom(eeprom) {}
void write(uint16_t address, int value) override { _eeprom.write(address, static_cast<uint8_t>(value)); }
uint8_t read(uint16_t address) override { return _eeprom.read(address); }
int getBoardID() {
LOG(DEBUG) << "Reading board ID from CaR EEPROM";
return _eeprom.read(ADDR_EEPROM_BOARD_ID);
}
private:
// Reference to the EEPROM24LC32A component
component::EEPROM24LC32A& _eeprom;
};
class ClockGenerator : public hal::ClockSynthesizer {
public:
ClockGenerator(component::Si5345& si5345) noexcept : _si5345(si5345) {}
void configure(const std::any& config) override { _si5345.configure(config); }
void disable() override { _si5345.disable(); }
bool isLocked() override { return _si5345.isLocked(); }
interface::iface_i2c::dataVector_type read(int page) { return _si5345.read(page); }
private:
// Reference to the Si5345 component
component::Si5345& _si5345;
};
class ZynqClockGenerator : public hal::Oscillator {
public:
ZynqClockGenerator(component::Si570& si570) noexcept : _si570(si570) {}
void setFrequency(uint64_t frequency) override { _si570.setFrequency(frequency); }
private:
// Reference to the Si570 component
component::Si570& _si570;
};
class TemperatureSensor : public hal::TemperatureProvider {
public:
TemperatureSensor(component::TMP101& sensor) noexcept : _sensor(sensor) {}
double getTemperature() override { return _sensor.readTemperature(); }
private:
// Reference to the TMP101 component
component::TMP101& _sensor;
};
class ADCInput : public hal::VoltageProvider {
public:
ADCInput(component::ADS7828& adc, component::ADS7828::Channel channel) noexcept : _adc(adc), _channel(channel) {}
double getVoltage() override { return (_adc.readSingleEnded(_channel)); }
private:
// Reference to the ADS7828 component
component::ADS7828& _adc;
// ADS7828 channel to read from
component::ADS7828::Channel _channel;
};
class BiasRegulator : public hal::VoltageControl, public hal::SwitchControl {
public:
BiasRegulator(component::DAC7678& dac, component::DAC7678::Channel channel) noexcept
: _dac(dac), _channel(channel) {}
void setVoltage(double voltage) override { _dac.setVoltage(_channel, voltage); }
void setSwitch(bool enable) override { _dac.setOutput(_channel, enable); }
private:
// Reference to the DAC7678 component
component::DAC7678& _dac;
// DAC7678 channel to control
component::DAC7678::Channel _channel;
};
class CMOSLevels : public hal::VoltageControl, public hal::SwitchControl {
public:
CMOSLevels(component::DAC7678& dac, std::vector<component::DAC7678::Channel> channels) noexcept
: _dac(dac), _channels(std::move(channels)) {}
void setVoltage(double voltage) override {
for(auto channel : _channels) {
_dac.setVoltage(channel, voltage);
}
}
void setSwitch(bool enable) override {
for(auto channel : _channels) {
_dac.setOutput(channel, enable);
}
}
private:
// Reference to the DAC7678 component
component::DAC7678& _dac;
// DAC7678 channels to control
std::vector<component::DAC7678::Channel> _channels;
};
class CurrentSource : public hal::CurrentControl, public hal::SwitchControl {
public:
CurrentSource(component::DAC7678& dac,
component::DAC7678::Channel dac_channel,
component::PCA9539& pca,
component::PCA9539::Port pca_port,
component::PCA9539::Channel pca_channel) noexcept
: _dac(dac), _dac_channel(dac_channel), _pca(pca), _pca_port(pca_port), _pca_channel(pca_channel) {
// Set the PCA9539 channel as output
_pca.setDirection(_pca_port, _pca_channel, utils::Direction::OUT);
};
void setCurrent(double current) override {
// FIXME this is in uA right now, should go to A
LOG(DEBUG) << "Setting " << current << "A " << "on " << name();
if(current > 1.024e-3) {
throw utils::InvalidArgumentError(
__func__, "Trying to set current source to " + std::to_string(current) + " A (max is 1.024e-3 A)");
}
// The output current is set by Vdac connected to a 1/40 voltage divider and sensed by a 100 Ohm resistor
// So the output current is Vdac / 40 / 100 Ohm
_dac.setVoltage(_dac_channel, current * 40 * 1000);
}
void setSwitch(bool enable) override { _dac.setOutput(_dac_channel, enable); }
void setPolarity(utils::Polarity polarity) override { _pca.setOutputLevel(_pca_port, _pca_channel, polarity); }
private:
// Reference to the DAC7678 component
component::DAC7678& _dac;
// DAC7678 channel to control the current source
component::DAC7678::Channel _dac_channel;
// Reference to the PCA9539 component
component::PCA9539& _pca;
// PCA9539 port to control the polarity
component::PCA9539::Port _pca_port;
// PCA9539 channel to control the polarity
component::PCA9539::Channel _pca_channel;
};
class VoltageRegulator : public hal::VoltageControl,
public hal::VoltageProvider,
public hal::CurrentLimiter,
public hal::CurrentProvider,
public hal::PowerProvider,
public hal::SwitchControl,
public hal::AlertMonitor {
public:
VoltageRegulator(component::DAC7678& dac,
component::DAC7678::Channel dac_channel,
component::INA226& ina,
component::PCA9539& pca_pwr,
component::PCA9539::Port pca_pwr_port,
component::PCA9539::Channel pca_pwr_channel,
component::PCA9539& pca_alert,
component::PCA9539::Port pca_alert_port,
component::PCA9539::Channel pca_alert_channel) noexcept
: _dac(dac), _dac_channel(dac_channel), _ina(ina), _pca_pwr(pca_pwr), _pca_pwr_port(pca_pwr_port),
_pca_pwr_channel(pca_pwr_channel), _pca_alert(pca_alert), _pca_alert_port(pca_alert_port),
_pca_alert_channel(pca_alert_channel) {
// Set the PCA9539 channel as output
_pca_pwr.setDirection(_pca_pwr_port, _pca_pwr_channel, utils::Direction::OUT);
};
void setVoltage(double voltage) override {
// Check if the voltage is within the valid range
if(voltage > 3.5 || voltage < 0) {
throw utils::InvalidArgumentError(
__func__, "Trying to set Voltage regulator to " + std::to_string(voltage) + " V (range is 0-3.5 V)");
}
// Set voltage on the DAC7678
LOG(DEBUG) << "Setting voltage " << voltage << "V " << "on " << name();
_dac.setVoltage(_dac_channel, 3.6 - voltage);
}
void setCurrentLimit(double current_limit) override {
// Set the current limit on the INA226
_ina.setCurrentLimit(current_limit);
}
void setSwitch(bool enable) override {
using namespace std::chrono_literals;
if(enable) {
_dac.setOutput(_dac_channel, enable);
std::this_thread::sleep_for(100ms);
_pca_pwr.setOutputLevel(_pca_pwr_port, _pca_pwr_channel, utils::Polarity::HIGH);
} else {
_pca_pwr.setOutputLevel(_pca_pwr_port, _pca_pwr_channel, utils::Polarity::LOW);
_dac.setOutput(_dac_channel, enable);
}
}
double getVoltage() override { return _ina.getVoltage(); }
double getCurrent() override { return _ina.getCurrent(); }
double getPower() override { return _ina.getPower(); }
bool getAlertStatus() override {
LOG(DEBUG) << "Reading alert status of " << name();
return _pca_alert.getInputLevel(_pca_alert_port, _pca_alert_channel);
}
private:
// Reference to the DAC7678 component
component::DAC7678& _dac;
// DAC7678 channel to control the voltage regulator
component::DAC7678::Channel _dac_channel;
// Reference to the INA226 component for voltage, current, and power monitoring
component::INA226& _ina;
// Reference to the PCA9539 component for power control
component::PCA9539& _pca_pwr;
// PCA9539 port for power control
component::PCA9539::Port _pca_pwr_port;
// PCA9539 channel for power control
component::PCA9539::Channel _pca_pwr_channel;
// Reference to the PCA9539 component for alert monitoring
component::PCA9539& _pca_alert;
// PCA9539 port for alert monitoring
component::PCA9539::Port _pca_alert_port;
// PCA9539 channel for alert monitoring
component::PCA9539::Channel _pca_alert_channel;
};
class Carboard : public hal::Board {
public:
Carboard()
: USRCLK(BUS_I2C4, ADDR_SI570), //
SI(BUS_I2C0, ADDR_CLKGEN), //
U30(BUS_I2C0, ADDR_TEMP), //
U15(BUS_I2C0, ADDR_IOEXP), U31(BUS_I2C0, ADDR_IOEXP2), //
U44(BUS_I2C3, ADDR_DAC_U44, VREF_4P0), U45(BUS_I2C3, ADDR_DAC_U45, VREF_4P0),
U46(BUS_I2C3, ADDR_DAC_U46, VREF_4P0), U47(BUS_I2C3, ADDR_DAC_U47, VREF_4P0),
U48(BUS_I2C3, ADDR_DAC_U48, VREF_4P0), U49(BUS_I2C3, ADDR_DAC_U49, VREF_4P0),
U50(BUS_I2C3, ADDR_DAC_U50, VREF_4P0), U87(BUS_I2C0, ADDR_CMOSDAC, VREF_4P0), //
U52(BUS_I2C1, ADDR_MONITOR_U52, INA226_R_SHUNT), U53(BUS_I2C1, ADDR_MONITOR_U53, INA226_R_SHUNT),
U54(BUS_I2C1, ADDR_MONITOR_U54, INA226_R_SHUNT), U55(BUS_I2C1, ADDR_MONITOR_U55, INA226_R_SHUNT),
U56(BUS_I2C1, ADDR_MONITOR_U56, INA226_R_SHUNT), U57(BUS_I2C1, ADDR_MONITOR_U57, INA226_R_SHUNT),
U58(BUS_I2C1, ADDR_MONITOR_U58, INA226_R_SHUNT), U59(BUS_I2C1, ADDR_MONITOR_U59, INA226_R_SHUNT), //
U77(BUS_I2C3, ADDR_ADC, VREF_4P0), //
U78(BUS_I2C0, ADDR_EEPROM) {
// Create resources from board components
// FIXME: putting this Zynq USR clock here for now
register_resource<ZynqClockGenerator>("USRCLK", USRCLK);
// EEPROM access
register_resource<EEPROM>("EEPROM", U78);
// Clock Generator
register_resource<ClockGenerator>("CLKGEN", SI);
// Temperature sensor
register_resource<TemperatureSensor>("TEMP", U30);
// Voltage regulators
register_resource<VoltageRegulator>("PWR_OUT_1",
U50,
component::DAC7678::VOUTA,
U53,
U15,
component::PCA9539::P1,
component::PCA9539::CH7,
U31,
component::PCA9539::P0,
component::PCA9539::CH7);
register_resource<VoltageRegulator>("PWR_OUT_2",
U50,
component::DAC7678::VOUTC,
U52,
U15,
component::PCA9539::P1,
component::PCA9539::CH6,
U31,
component::PCA9539::P1,
component::PCA9539::CH0);
register_resource<VoltageRegulator>("PWR_OUT_3",
U50,
component::DAC7678::VOUTE,
U55,
U15,
component::PCA9539::P1,
component::PCA9539::CH5,
U31,
component::PCA9539::P0,
component::PCA9539::CH6);
register_resource<VoltageRegulator>("PWR_OUT_4",
U50,
component::DAC7678::VOUTG,
U54,
U15,
component::PCA9539::P1,
component::PCA9539::CH4,
U31,
component::PCA9539::P1,
component::PCA9539::CH1);
register_resource<VoltageRegulator>("PWR_OUT_5",
U50,
component::DAC7678::VOUTB,
U57,
U15,
component::PCA9539::P1,
component::PCA9539::CH0,
U31,
component::PCA9539::P0,
component::PCA9539::CH0);
register_resource<VoltageRegulator>("PWR_OUT_6",
U50,
component::DAC7678::VOUTD,
U56,
U15,
component::PCA9539::P1,
component::PCA9539::CH1,
U31,
component::PCA9539::P1,
component::PCA9539::CH6);
register_resource<VoltageRegulator>("PWR_OUT_7",
U50,
component::DAC7678::VOUTF,
U59,
U15,
component::PCA9539::P1,
component::PCA9539::CH2,
U31,
component::PCA9539::P0,
component::PCA9539::CH1);
register_resource<VoltageRegulator>("PWR_OUT_8",
U50,
component::DAC7678::VOUTH,
U58,
U15,
component::PCA9539::P1,
component::PCA9539::CH3,
U31,
component::PCA9539::P1,
component::PCA9539::CH7);
// Current sources
register_resource<CurrentSource>(
"CUR_1", U47, component::DAC7678::VOUTB, U15, component::PCA9539::P0, component::PCA9539::CH6);
register_resource<CurrentSource>(
"CUR_2", U47, component::DAC7678::VOUTD, U15, component::PCA9539::P0, component::PCA9539::CH7);
register_resource<CurrentSource>(
"CUR_3", U47, component::DAC7678::VOUTF, U15, component::PCA9539::P0, component::PCA9539::CH5);
register_resource<CurrentSource>(
"CUR_4", U47, component::DAC7678::VOUTH, U15, component::PCA9539::P0, component::PCA9539::CH4);
register_resource<CurrentSource>(
"CUR_5", U47, component::DAC7678::VOUTG, U15, component::PCA9539::P0, component::PCA9539::CH2);
register_resource<CurrentSource>(
"CUR_6", U47, component::DAC7678::VOUTE, U15, component::PCA9539::P0, component::PCA9539::CH3);
register_resource<CurrentSource>(
"CUR_7", U47, component::DAC7678::VOUTC, U15, component::PCA9539::P0, component::PCA9539::CH1);
register_resource<CurrentSource>(
"CUR_8", U47, component::DAC7678::VOUTA, U15, component::PCA9539::P0, component::PCA9539::CH0);
// Bias supplies
register_resource<BiasRegulator>("BIAS_1", U44, component::DAC7678::VOUTA);
register_resource<BiasRegulator>("BIAS_2", U44, component::DAC7678::VOUTC);
register_resource<BiasRegulator>("BIAS_3", U44, component::DAC7678::VOUTE);
register_resource<BiasRegulator>("BIAS_4", U44, component::DAC7678::VOUTG);
register_resource<BiasRegulator>("BIAS_5", U44, component::DAC7678::VOUTB);
register_resource<BiasRegulator>("BIAS_6", U44, component::DAC7678::VOUTD);
register_resource<BiasRegulator>("BIAS_7", U44, component::DAC7678::VOUTF);
register_resource<BiasRegulator>("BIAS_8", U44, component::DAC7678::VOUTH);
register_resource<BiasRegulator>("BIAS_9", U46, component::DAC7678::VOUTA);
register_resource<BiasRegulator>("BIAS_10", U46, component::DAC7678::VOUTC);
register_resource<BiasRegulator>("BIAS_11", U46, component::DAC7678::VOUTE);
register_resource<BiasRegulator>("BIAS_12", U46, component::DAC7678::VOUTG);
register_resource<BiasRegulator>("BIAS_13", U46, component::DAC7678::VOUTB);
register_resource<BiasRegulator>("BIAS_14", U46, component::DAC7678::VOUTD);
register_resource<BiasRegulator>("BIAS_15", U46, component::DAC7678::VOUTF);
register_resource<BiasRegulator>("BIAS_16", U46, component::DAC7678::VOUTH);
register_resource<BiasRegulator>("BIAS_17", U45, component::DAC7678::VOUTA);
register_resource<BiasRegulator>("BIAS_18", U45, component::DAC7678::VOUTC);
register_resource<BiasRegulator>("BIAS_19", U45, component::DAC7678::VOUTE);
register_resource<BiasRegulator>("BIAS_20", U45, component::DAC7678::VOUTG);
register_resource<BiasRegulator>("BIAS_21", U45, component::DAC7678::VOUTB);
register_resource<BiasRegulator>("BIAS_22", U45, component::DAC7678::VOUTD);
register_resource<BiasRegulator>("BIAS_23", U45, component::DAC7678::VOUTF);
register_resource<BiasRegulator>("BIAS_24", U45, component::DAC7678::VOUTH);
register_resource<BiasRegulator>("BIAS_25", U48, component::DAC7678::VOUTA);
register_resource<BiasRegulator>("BIAS_26", U48, component::DAC7678::VOUTC);
register_resource<BiasRegulator>("BIAS_27", U48, component::DAC7678::VOUTE);
register_resource<BiasRegulator>("BIAS_28", U48, component::DAC7678::VOUTG);
register_resource<BiasRegulator>("BIAS_29", U48, component::DAC7678::VOUTB);
register_resource<BiasRegulator>("BIAS_30", U48, component::DAC7678::VOUTD);
register_resource<BiasRegulator>("BIAS_31", U48, component::DAC7678::VOUTF);
register_resource<BiasRegulator>("BIAS_32", U48, component::DAC7678::VOUTH);
// Slow ADC input channels:
register_resource<ADCInput>("VOL_IN_1", U77, component::ADS7828::CHO);
register_resource<ADCInput>("VOL_IN_2", U77, component::ADS7828::CH1);
register_resource<ADCInput>("VOL_IN_3", U77, component::ADS7828::CH2);
register_resource<ADCInput>("VOL_IN_4", U77, component::ADS7828::CH3);
register_resource<ADCInput>("VOL_IN_5", U77, component::ADS7828::CH4);
register_resource<ADCInput>("VOL_IN_6", U77, component::ADS7828::CH5);
register_resource<ADCInput>("VOL_IN_7", U77, component::ADS7828::CH6);
register_resource<ADCInput>("VOL_IN_8", U77, component::ADS7828::CH7);
// CMOS I/O voltage levels
register_resource<CMOSLevels>("CMOS_IN",
U87,
std::vector<component::DAC7678::Channel> {component::DAC7678::VOUTA,
component::DAC7678::VOUTB,
component::DAC7678::VOUTC,
component::DAC7678::VOUTD});
register_resource<CMOSLevels>(
"CMOS_OUT",
U87,
std::vector<component::DAC7678::Channel> {component::DAC7678::VOUTE, component::DAC7678::VOUTG});
register_resource<BiasRegulator>("CMOS_IN_1_TO_4", U87, component::DAC7678::VOUTC);
register_resource<BiasRegulator>("CMOS_IN_5_TO_8", U87, component::DAC7678::VOUTA);
register_resource<BiasRegulator>("CMOS_IN_9_TO_12", U87, component::DAC7678::VOUTB);
register_resource<BiasRegulator>("CMOS_IN_13_TO_14", U87, component::DAC7678::VOUTD);
register_resource<BiasRegulator>("CMOS_OUT_1_TO_4", U87, component::DAC7678::VOUTE);
register_resource<BiasRegulator>("CMOS_OUT_5_TO_8", U87, component::DAC7678::VOUTG);
// Injection voltage bias
register_resource<BiasRegulator>("INJ_1", U49, component::DAC7678::VOUTF);
register_resource<BiasRegulator>("INJ_2", U49, component::DAC7678::VOUTE);
register_resource<BiasRegulator>("INJ_3", U49, component::DAC7678::VOUTC);
register_resource<BiasRegulator>("INJ_4", U49, component::DAC7678::VOUTA);
}
virtual ~Carboard() = default;
private:
// Board components
component::Si570 USRCLK;
component::Si5345 SI;
component::TMP101 U30;
component::PCA9539 U15, U31;
component::DAC7678 U44, U45, U46, U47, U48, U49, U50, U87;
component::INA226 U52, U53, U54, U55, U56, U57, U58, U59;
component::ADS7828 U77;
component::EEPROM24LC32A U78;
};
} // namespace peary::board::carboard
Updated on 2025-11-14 at 11:31:23 +0100