diff --git a/drivers/io/io.cpp b/drivers/io/io.cpp index 9c968b2e..d809e77b 100644 --- a/drivers/io/io.cpp +++ b/drivers/io/io.cpp @@ -3,14 +3,14 @@ namespace pimoroni { IO::IO(uint pin) : pin(pin) { - gpio_init(pin); - }; + gpio_init(pin); + }; IO::IO(uint pin, bool out, bool pull_up, bool pull_down) : pin(pin) { - gpio_init(pin); - direction(out, pull_up, pull_down); - }; + gpio_init(pin); + direction(out, pull_up, pull_down); + }; bool IO::direction() { return gpio_is_dir_out(pin); diff --git a/libraries/yukon/modules/audio_amp/audio_amp.cpp b/libraries/yukon/modules/audio_amp/audio_amp.cpp index 3a116f37..3422a248 100644 --- a/libraries/yukon/modules/audio_amp/audio_amp.cpp +++ b/libraries/yukon/modules/audio_amp/audio_amp.cpp @@ -1,6 +1,7 @@ #include "audio_amp.hpp" #include "../../logging.hpp" #include "../../errors.hpp" +#include "tas_regs.hpp" namespace pimoroni { @@ -15,7 +16,10 @@ namespace pimoroni { } AudioAmpModule::AudioAmpModule() : - YukonModule() { + YukonModule(), + slow_sda(nullptr), + slow_scl(nullptr), + amp_en(nullptr) { } AudioAmpModule::~AudioAmpModule() { @@ -26,6 +30,13 @@ namespace pimoroni { } void AudioAmpModule::initialise(const SLOT& slot, SlotAccessor& accessor) { + // Create the "I2C" pin objects + slow_sda = new TCA_IO(slot.SLOW1, accessor); + slow_scl = new TCA_IO(slot.SLOW2, accessor); + + // Create the enable pin object + amp_en = new TCA_IO(slot.SLOW3, accessor); + // Configure strip and power pins configure(); @@ -34,27 +45,99 @@ namespace pimoroni { } void AudioAmpModule::configure() { - + slow_sda->to_output(true); + slow_scl->to_output(true); + amp_en->to_output(false); } void AudioAmpModule::enable() { + CHECK_INITIALISED + amp_en->value(true); + // Pre-Reset Configuration + write_i2c_reg(PAGE, 0x01); // Page 1 + write_i2c_reg(0x37, 0x3a); // Bypass + + write_i2c_reg(PAGE, 0xFD); // Page FD + write_i2c_reg(0x0D, 0x0D); // Access page + write_i2c_reg(0x06, 0xC1); // Set Dmin + + write_i2c_reg(PAGE, 0x01); // Page 1 + write_i2c_reg(0x19, 0xC0); // Force modulation + write_i2c_reg(PAGE, 0xFD); // Page FD + write_i2c_reg(0x0D, 0x0D); // Access page + write_i2c_reg(0x06, 0xD5); // Set Dmin + + // Software Reset + write_i2c_reg(PAGE, 0x00); // Page 0 + write_i2c_reg(0x7F, 0x00); // Book 0 + write_i2c_reg(0x01, 0x01); // Software Reset + + // Post-Reset Configuration + write_i2c_reg(PAGE, 0x01); // Page 1 + write_i2c_reg(0x37, 0x3a); // Bypass + + write_i2c_reg(PAGE, 0xFD); // Page FD + write_i2c_reg(0x0D, 0x0D); // Access page + write_i2c_reg(0x06, 0xC1); // Set Dmin + write_i2c_reg(0x06, 0xD5); // Set Dmin + + // Initial Device Configuration - PWR_MODE0 + write_i2c_reg(PAGE, 0x00); // Page 0 + write_i2c_reg(0x0E, 0x44); // TDM tx vsns transmit enable with slot 4 + write_i2c_reg(0x0F, 0x40); // TDM tx isns transmit enable with slot 0 + + write_i2c_reg(PAGE, 0x01); // Page 1 + write_i2c_reg(0x21, 0x00); // Disable Comparator Hysterisis + write_i2c_reg(0x17, 0xC8); // SARBurstMask=0 + write_i2c_reg(0x19, 0x00); // LSR Mode + write_i2c_reg(0x35, 0x74); // Noise minimized + + write_i2c_reg(PAGE, 0xFD); // Page FD + write_i2c_reg(0x0D, 0x0D); // Access page + write_i2c_reg(0x3E, 0x4A); // Optimal Dmin + write_i2c_reg(0x0D, 0x00); // Remove access + + write_i2c_reg(PAGE, 0x00); // Page 0 + write_i2c_reg(CHNL_0, 0xA8); // PWR_MODE0 selected + write_i2c_reg(PVDD_UVLO, 0x00); // PVDD UVLO set to 2.76V + // My addition + write_i2c_reg(DC_BLK0, 0xA1); // VBAT1S_MODE set to internally generated + write_i2c_reg(DVC, 0x68); // Go to a low default + write_i2c_reg(INT_CLK_CFG, 0x99 + 0b0100000); // CLK_PWR_UD_EN abled, with long time. This causes output to stay active without mute. + + write_i2c_reg(INT_MASK0, 0xFF); + write_i2c_reg(INT_MASK1, 0xFF); + write_i2c_reg(INT_MASK2, 0xFF); + write_i2c_reg(INT_MASK3, 0xFF); + write_i2c_reg(INT_MASK4, 0xFF); + + write_i2c_reg(MODE_CTRL, 0x80); // Play audio, power up with playback, IV enabled + // A second play command is required for some reason, to take it out of software shutdown + // Temp commented out self.write_i2c_reg(MODE_CTRL, 0x80) # Play audio, power up with playback, IV enabled } void AudioAmpModule::disable() { - + CHECK_INITIALISED + amp_en->value(false); } bool AudioAmpModule::is_enabled() { - return 0; // TODO + CHECK_INITIALISED + return amp_en->value(); } void AudioAmpModule::exit_soft_shutdown() { - + CHECK_INITIALISED + write_i2c_reg(MODE_CTRL, 0x80); // Calling this after a play seems to wake the amp up, but adds around 16ms } void AudioAmpModule::set_volume(float volume) { + if(volume < 0.0f || volume > 1.0f) { + throw std::invalid_argument("Volume out of range. Expected 0.0 to 1.0\n"); + } + write_i2c_reg(DVC, (uint8_t)((1.0f - volume) * 0xC8)); } float AudioAmpModule::read_temperature() { diff --git a/libraries/yukon/modules/audio_amp/audio_amp.hpp b/libraries/yukon/modules/audio_amp/audio_amp.hpp index 7dc20f5f..13431ec7 100644 --- a/libraries/yukon/modules/audio_amp/audio_amp.hpp +++ b/libraries/yukon/modules/audio_amp/audio_amp.hpp @@ -44,10 +44,9 @@ namespace pimoroni { uint I2S_CLK; uint I2S_FS; private: - TCA slow_sda; - TCA slow_scl; - TCA amp_en; - + TCA_IO* slow_sda; + TCA_IO* slow_scl; + TCA_IO* amp_en; //-------------------------------------------------- diff --git a/libraries/yukon/modules/audio_amp/tas_regs.hpp b/libraries/yukon/modules/audio_amp/tas_regs.hpp new file mode 100644 index 00000000..2a0b25a5 --- /dev/null +++ b/libraries/yukon/modules/audio_amp/tas_regs.hpp @@ -0,0 +1,119 @@ +namespace pimoroni { + + enum TASRegs { + // PAGE 0 Regs + PAGE = 0x00, // Device Page Section 8.9.5 + SW_RESET = 0x01, // Software Reset Section 8.9.6 + MODE_CTRL = 0x02, // Device operational mode Section 8.9.7 + CHNL_0 = 0x03, // Y Bridge and Channel settings Section 8.9.8 + DC_BLK0 = 0x04, // SAR Filter and DC Path Blocker Section 8.9.9 + DC_BLK1 = 0x05, // Record DC Blocker Section 8.9.10 + MISC_CFG1 = 0x06, // Misc Configuration 1 Section 8.9.11 + MISC_CFG2 = 0x07, // Misc Configuration 2 Section 8.9.12 + TDM_CFG0 = 0x08, // TDM Configuration 0 Section 8.9.13 + TDM_CFG1 = 0x09, // TDM Configuration 1 Section 8.9.14 + TDM_CFG2 = 0x0A, // TDM Configuration 2 Section 8.9.15 + LIM_MAX_ATTN = 0x0B, // Limiter Section 8.9.16 + TDM_CFG3 = 0x0C, // TDM Configuration 3 Section 8.9.17 + TDM_CFG4 = 0x0D, // TDM Configuration 4 Section 8.9.18 + TDM_CFG5 = 0x0E, // TDM Configuration 5 Section 8.9.19 + TDM_CFG6 = 0x0F, // TDM Configuration 6 Section 8.9.20 + TDM_CFG7 = 0x10, // TDM Configuration 7 Section 8.9.21 + TDM_CFG8 = 0x11, // TDM Configuration 8 Section 8.9.22 + TDM_CFG9 = 0x12, // TDM Configuration 9 Section 8.9.23 + TDM_CFG10 = 0x13, // TDM Configuration 10 Section 8.9.24 + TDM_CFG11 = 0x14, // TDM Configuration 11 Section 8.9.25 + ICC_CNFG2 = 0x15, // ICC Mode Section 8.9.26 + TDM_CFG12 = 0x16, // TDM Configuration 12 Section 8.9.27 + ICLA_CFG0 = 0x17, // Inter Chip Limiter Alignment 0 Section 8.9.28 + ICLA_CFG1 = 0x18, // Inter Chip Gain Alignment 1 Section 8.9.29 + DG_0 = 0x19, // Diagnostic Signal Section 8.9.30 + DVC = 0x1A, // Digital Volume Control Section 8.9.31 + LIM_CFG0 = 0x1B, // Limiter Configuration 0 Section 8.9.32 + LIM_CFG1 = 0x1C, // Limiter Configuration 1 Section 8.9.33 + BOP_CFG0 = 0x1D, // Brown Out Prevention 0 Section 8.9.34 + BOP_CFG1 = 0x1E, // Brown Out Prevention 1 Section 8.9.35 + BOP_CFG2 = 0x1F, // Brown Out Prevention 2 Section 8.9.36 + BOP_CFG3 = 0x20, // Brown Out Prevention 3 Section 8.9.37 + BOP_CFG4 = 0x21, // Brown Out Prevention 4 Section 8.9.38 + BOP_CFG5 = 0x22, // BOP Configuration 5 Section 8.9.40 + BOP_CFG6 = 0x23, // Brown Out Prevention 6 Section 8.9.41 + BOP_CFG7 = 0x24, // Brown Out Prevention 7 Section 8.9.42 + BOP_CFG8 = 0x25, // Brown Out Prevention 8 Section 8.9.43 + BOP_CFG9 = 0x26, // Brown Out Prevention 9 Section 8.9.44 + BOP_CFG10 = 0x27, // BOP Configuration 10 Section 8.9.45 + BOP_CFG11 = 0x28, // Brown Out Prevention 11 Section 8.9.46 + BOP_CFG12 = 0x29, // Brown Out Prevention 12 Section 8.9.47 + BOP_CFG13 = 0x2A, // Brown Out Prevention 13 Section 8.9.48 + BOP_CFG14 = 0x2B, // Brown Out Prevention 14 Section 8.9.49 + BOP_CFG15 = 0x2C, // BOP Configuration 15 Section 8.9.50 + BOP_CFG17 = 0x2D, // Brown Out Prevention 16 Section 8.9.51 + BOP_CFG18 = 0x2E, // Brown Out Prevention 17 Section 8.9.52 + BOP_CFG19 = 0x2F, // Brown Out Prevention 18 Section 8.9.53 + BOP_CFG20 = 0x30, // Brown Out Prevention 19 Section 8.9.54 + BOP_CFG21 = 0x31, // BOP Configuration 21 Section 8.9.55 + BOP_CFG22 = 0x32, // Brown Out Prevention 22 Section 8.9.56 + BOP_CFG23 = 0x33, // Lowest PVDD Measured Section 8.9.57 + BOP_CFG24 = 0x34, // Lowest BOP Attack Rate Section 8.9.57 + NG_CFG0 = 0x35, // Noise Gate 0 Section 8.9.60 + NG_CFG1 = 0x36, // Noise Gate 1 Section 8.9.61 + LVS_CFG0 = 0x37, // Low Voltage Signaling Section 8.9.62 + DIN_PD = 0x38, // Digital Input Pin Pull Down Section 8.9.63 + IO_DRV0 = 0x39, // Output Driver Strength Section 8.9.64 + IO_DRV1 = 0x3A, // Output Driver Strength Section 8.9.65 + INT_MASK0 = 0x3B, // Interrupt Mask 0 Section 8.9.66 + INT_MASK1 = 0x3C, // Interrupt Mask 1 Section 8.9.67 + INT_MASK4 = 0x3D, // Interrupt Mask 4 Section 8.9.68 + INT_MASK2 = 0x40, // Interrupt Mask 2 Section 8.9.69 + INT_MASK3 = 0x41, // Interrupt Mask 3 Section 8.9.70 + INT_LIVE0 = 0x42, // Live Interrupt Read-back 0 Section 8.9.71 + INT_LIVE1 = 0x43, // Live Interrupt Read-back 1 Section 8.9.72 + INT_LIVE1_0 = 0x44, // Live Interrupt Read-back 1_0 Section 8.9.73 + INT_LIVE2 = 0x47, // Live Interrupt Read-back 2 Section 8.9.74 + INT_LIVE3 = 0x48, // Live Interrupt Read-back 3 Section 8.9.75 + INT_LTCH0 = 0x49, // Latched Interrupt Read-back 0 Section 8.9.76 + INT_LTCH1 = 0x4A, // Latched Interrupt Read-back 1 Section 8.9.77 + INT_LTCH1_0 = 0x4B, // Latched Interrupt Read-back 1_0 Section 8.9.78 + INT_LTCH2 = 0x4F, // Latched Interrupt Read-back 2 Section 8.9.79 + INT_LTCH3 = 0x50, // Latched Interrupt Read-back 3 Section 8.9.80 + INT_LTCH4 = 0x51, // Latched Interrupt Read-back 4 Section 8.9.81 + VBAT_MSB = 0x52, // SAR VBAT1S 0 Section 8.9.82 + VBAT_LSB = 0x53, // SAR VBAT1S 1 Section 8.9.83 + PVDD_MSB = 0x54, // SAR PVDD 0 Section 8.9.84 + PVDD_LSB = 0x55, // SAR PVDD 1 Section 8.9.85 + TEMP = 0x56, // SAR ADC Conversion 2 Section 8.9.86 + INT_CLK_CFG = 0x5C, // Clock Setting and IRQZ Section 8.9.87 + MISC_CFG3 = 0x5D, // Misc Configuration 3 Section 8.9.88 + CLOCK_CFG = 0x60, // Clock Configuration Section 8.9.89 + IDLE_IND = 0x63, // Idle channel current optimization Section 8.9.90 + MISC_CFG4 = 0x65, // Misc Configuration 4 Section 8.9.91 + TG_CFG0 = 0x67, // Tone Generator Section 8.9.92 + CLK_CFG = 0x68, // Detect Clock Ration and Sample Rate Section 8.9.93 + LV_EN_CFG = 0x6A, // Class-D and LVS Delays Section 8.9.94 + NG_CFG2 = 0x6B, // Noise Gate 2 Section 8.9.95 + NG_CFG3 = 0x6C, // Noise Gate 3 Section 8.9.96 + NG_CFG4 = 0x6D, // Noise Gate 4 Section 8.9.97 + NG_CFG5 = 0x6E, // Noise Gate 5 Section 8.9.98 + NG_CFG6 = 0x6F, // Noise Gate 6 Section 8.9.99 + NG_CFG7 = 0x70, // Noise Gate 7 Section 8.9.100 + PVDD_UVLO = 0x71, // UVLO Threshold Section 8.9.101 + DMD = 0x73, // DAC Modulator Dither Section 8.9.102 + I2C_CKSUM = 0x7E, // I2C Checksum Section 8.9.104 + BOOK = 0x7F, // Device Book Section 8.9.105 + + // PAGE 1 Regs + LSR = 0x19, // Modulation Section 8.9.106 + INT_LDO = 0x36, // Internal LDO Setting Section 8.9.107 + SDOUT_HIZ_1 = 0x3D, // Slots Control Section 8.9.108 + SDOUT_HIZ_2 = 0x3E, // Slots Control Section 8.9.109 + SDOUT_HIZ_3 = 0x3F, // Slots Control Section 8.9.110 + SDOUT_HIZ_4 = 0x40, // Slots Control Section 8.9.111 + SDOUT_HIZ_5 = 0x41, // Slots Control Section 8.9.112 + SDOUT_HIZ_6 = 0x42, // Slots Control Section 8.9.113 + SDOUT_HIZ_7 = 0x43, // Slots Control Section 8.9.114 + SDOUT_HIZ_8 = 0x44, // Slots Control Section 8.9.115 + SDOUT_HIZ_9 = 0x45, // Slots Control Section 8.9.116 + TG_EN = 0x47, // Thermal Detection Enable Section 8.9.117 + }; + +} \ No newline at end of file diff --git a/libraries/yukon/modules/bench_power/bench_power.cpp b/libraries/yukon/modules/bench_power/bench_power.cpp index 3e3e68c9..4d26f648 100644 --- a/libraries/yukon/modules/bench_power/bench_power.cpp +++ b/libraries/yukon/modules/bench_power/bench_power.cpp @@ -34,6 +34,13 @@ namespace pimoroni { } void BenchPowerModule::initialise(const SLOT& slot, SlotAccessor& accessor) { + // Create the voltage pwm object + voltage_pwm = new PWMCluster(pio0, 0, slot.FAST2, 1, true); + + // Create the power control pin objects + power_en = new IO(slot.FAST1); + power_good = new TCA_IO(slot.SLOW1, accessor); + // Configure strip and power pins configure(); @@ -42,47 +49,87 @@ namespace pimoroni { } void BenchPowerModule::configure() { + // Calculate a suitable pwm wrap period for this frequency + uint32_t period; uint32_t div256; + if(pimoroni::PWMCluster::calculate_pwm_factors(250000, period, div256)) { + pwm_period = period; + // Update the pwm before setting the new wrap + voltage_pwm->set_chan_level(0, 0, false); + voltage_pwm->set_chan_offset(0, 0, false); + + // Set the new wrap (should be 1 less than the period to get full 0 to 100%) + voltage_pwm->set_wrap(pwm_period, true); // NOTE Minus 1 not needed here. Maybe should change Wrap behaviour so it is needed, for consistency with hardware pwm? + + // Apply the new divider + // This is done after loading new PWM values to avoid a lockup condition + uint8_t div = div256 >> 8; + uint8_t mod = div256 % 256; + voltage_pwm->set_clkdiv_int_frac(div, mod); + } + + power_en->to_output(false); + power_good->to_input(); } void BenchPowerModule::enable() { - + CHECK_INITIALISED + power_en->value(true); } void BenchPowerModule::disable() { - + CHECK_INITIALISED + power_en->value(false); } bool BenchPowerModule::is_enabled() { - return 0; // TODO + CHECK_INITIALISED + return power_en->value(); } void BenchPowerModule::set_pwm(float percent) { - + voltage_pwm->set_chan_level(0, (uint32_t)(percent * (float)pwm_period)); } void BenchPowerModule::set_target_voltage(float voltage) { - + CHECK_INITIALISED + float percent; + if(voltage >= VOLTAGE_MID) { + percent = (voltage - VOLTAGE_MID) * 0.5f / (VOLTAGE_MAX - VOLTAGE_MID) + 0.5f; + percent = MIN(percent, 1.0f); + } + else { + percent = (voltage - VOLTAGE_MIN) * 0.5f / (VOLTAGE_MID - VOLTAGE_MIN); + percent = MAX(percent, 0.0); + } + set_target(percent); } void BenchPowerModule::set_target(float percent) { + CHECK_INITIALISED + if(percent < 0.0f || percent > 1.0f) { + throw std::invalid_argument("percent out of range. Expected 0.0 to 1.0\n"); + } + set_pwm((percent * (PWM_MAX - PWM_MIN)) + PWM_MIN); } float BenchPowerModule::read_voltage() { // return (self.__shared_adc_voltage() * (100 + 22)) / 22 float value = __read_adc1(); + float voltage; if(value >= VOLTAGE_MID_MEASURE) { - return ((value - VOLTAGE_MID_MEASURE) * (VOLTAGE_MAX - VOLTAGE_MID)) / (VOLTAGE_MAX_MEASURE - VOLTAGE_MID_MEASURE) + VOLTAGE_MID; + voltage = ((value - VOLTAGE_MID_MEASURE) * (VOLTAGE_MAX - VOLTAGE_MID)) / (VOLTAGE_MAX_MEASURE - VOLTAGE_MID_MEASURE) + VOLTAGE_MID; } else { - float voltage = ((value - VOLTAGE_MIN_MEASURE) * (VOLTAGE_MID - VOLTAGE_MIN)) / (VOLTAGE_MID_MEASURE - VOLTAGE_MIN_MEASURE) + VOLTAGE_MIN; - return MAX(voltage, 0.0); + voltage = ((value - VOLTAGE_MIN_MEASURE) * (VOLTAGE_MID - VOLTAGE_MIN)) / (VOLTAGE_MID_MEASURE - VOLTAGE_MIN_MEASURE) + VOLTAGE_MIN; + voltage = MAX(voltage, 0.0); } + return voltage; } bool BenchPowerModule::read_power_good() { - return 0; // TODO + return power_good->value(); } float BenchPowerModule::read_temperature() { diff --git a/libraries/yukon/modules/bench_power/bench_power.hpp b/libraries/yukon/modules/bench_power/bench_power.hpp index 74a534ec..e29a8769 100644 --- a/libraries/yukon/modules/bench_power/bench_power.hpp +++ b/libraries/yukon/modules/bench_power/bench_power.hpp @@ -47,12 +47,14 @@ namespace pimoroni { float min_temperature; float avg_temperature; float count_avg; + //-------------------------------------------------- private: PWMCluster* voltage_pwm; IO* power_en; - IO* power_good; + TCA_IO* power_good; + uint32_t pwm_period; //-------------------------------------------------- diff --git a/libraries/yukon/modules/big_motor/big_motor.cpp b/libraries/yukon/modules/big_motor/big_motor.cpp index 2aeaf85e..c4fcad8d 100644 --- a/libraries/yukon/modules/big_motor/big_motor.cpp +++ b/libraries/yukon/modules/big_motor/big_motor.cpp @@ -18,12 +18,16 @@ namespace pimoroni { YukonModule(), frequency(frequency), motor(nullptr), - encoder(nullptr) { + encoder(nullptr), + motor_en(nullptr), + motor_nfault(nullptr) { } BigMotorModule::~BigMotorModule() { delete(motor); delete(encoder); + delete(motor_en); + delete(motor_nfault); } std::string BigMotorModule::name() { @@ -31,6 +35,13 @@ namespace pimoroni { } void BigMotorModule::initialise(const SLOT& slot, SlotAccessor& accessor) { + // Create motor object + motor = new MotorCluster(pio0, 0, slot.FAST3, NUM_MOTORS); + + // Create motor control pin objects + motor_en = new TCA_IO(slot.SLOW3, accessor); + motor_nfault = new TCA_IO(slot.SLOW2, accessor); + // Configure strip and power pins configure(); @@ -39,23 +50,31 @@ namespace pimoroni { } void BigMotorModule::configure() { + motor->disable_all(); + motor->decay_mode(0, SLOW_DECAY); + motor_nfault ->to_input(); + motor_en->to_output(false); } void BigMotorModule::enable() { - + CHECK_INITIALISED + motor_en->value(true); } void BigMotorModule::disable() { - + CHECK_INITIALISED + motor_en->value(true); } bool BigMotorModule::is_enabled() { - return 0; // TODO + CHECK_INITIALISED + return motor_en->value(); } bool BigMotorModule::read_fault() { - return 0; // TODO + CHECK_INITIALISED + return !motor_nfault->value(); } bool BigMotorModule::read_current() { diff --git a/libraries/yukon/modules/big_motor/big_motor.hpp b/libraries/yukon/modules/big_motor/big_motor.hpp index 3603902b..e69e14dc 100644 --- a/libraries/yukon/modules/big_motor/big_motor.hpp +++ b/libraries/yukon/modules/big_motor/big_motor.hpp @@ -15,6 +15,7 @@ namespace pimoroni { //-------------------------------------------------- public: static const std::string NAME; + static const uint NUM_MOTORS = 2; static constexpr float DEFAULT_FREQUENCY = 25000.0f; static constexpr float TEMPERATURE_THRESHOLD = 50.0f; static constexpr float CURRENT_THRESHOLD = 25.0f; @@ -50,8 +51,8 @@ namespace pimoroni { MotorCluster* motor; Encoder* encoder; private: - TCA motor_en; - TCA motor_nfault; + TCA_IO* motor_en; + TCA_IO* motor_nfault; //-------------------------------------------------- diff --git a/libraries/yukon/modules/common.cpp b/libraries/yukon/modules/common.cpp new file mode 100644 index 00000000..15974b6a --- /dev/null +++ b/libraries/yukon/modules/common.cpp @@ -0,0 +1,50 @@ +#include "common.hpp" + +namespace pimoroni { + TCA_IO::TCA_IO(TCA pin, TCAAccessor& accessor) : + pin(pin), + accessor(accessor) { + to_input(); + }; + + TCA_IO::TCA_IO(TCA pin, TCAAccessor& accessor, bool out) : + pin(pin), + accessor(accessor) { + direction(out); + }; + + //TCA_IO::~TCA_IO() { + // to_input(); + //} + + bool TCA_IO::direction() { + return accessor.get_slow_config(pin); + } + + void TCA_IO::direction(bool out) { + accessor.set_slow_config(pin, out); + } + + void TCA_IO::to_output(bool val) { + value(val); + direction(GPIO_OUT); + } + + void TCA_IO::to_input() { + direction(GPIO_IN); + value(false); + } + + bool TCA_IO::value() { + if(direction()) { + return accessor.get_slow_output(pin); + } + else { + return accessor.get_slow_input(pin); + } + } + + void TCA_IO::value(bool val) { + accessor.set_slow_output(pin, val); + } +}; \ No newline at end of file diff --git a/libraries/yukon/modules/common.hpp b/libraries/yukon/modules/common.hpp index 52525c2c..81875212 100644 --- a/libraries/yukon/modules/common.hpp +++ b/libraries/yukon/modules/common.hpp @@ -8,6 +8,11 @@ #include namespace pimoroni { +#define CHECK_INITIALISED \ + if(!is_initialised()) { \ + throw std::runtime_error("Module not initialised\n"); \ + } + struct TCA { uint CHIP; uint GPIO; @@ -43,7 +48,7 @@ namespace pimoroni { HIGH = true }; - class SlotAccessor { + class TCAAccessor { public: virtual bool get_slow_input(TCA gpio) = 0; virtual bool get_slow_output(TCA gpio) = 0; @@ -53,11 +58,30 @@ namespace pimoroni { virtual void set_slow_output(TCA gpio, bool value) = 0; virtual void set_slow_config(TCA gpio, bool output) = 0; virtual void set_slow_polarity(TCA gpio, bool polarity) = 0; + }; + class SlotAccessor : public TCAAccessor { + public: virtual float read_slot_adc1(SLOT slot) = 0; virtual float read_slot_adc2(SLOT slot) = 0; }; + class TCA_IO { + public: + TCA_IO(TCA pin, TCAAccessor& accessor); + TCA_IO(TCA pin, TCAAccessor& accessor, bool out); + //~TCA_IO(); + bool direction(); + void direction(bool out); + void to_output(bool val); + void to_input(); + bool value(); + void value(bool val); + private: + TCA pin; + TCAAccessor& accessor; + }; + class YukonModule { public: static constexpr float ROOM_TEMP = 273.15f + 25.0f; @@ -102,23 +126,17 @@ namespace pimoroni { } float __read_adc1() { - if(!is_initialised()) { - throw std::runtime_error("Module not initialised\n"); - } + CHECK_INITIALISED return __accessor->read_slot_adc1(slot); } float __read_adc2() { - if(!is_initialised()) { - throw std::runtime_error("Module not initialised\n"); - } + CHECK_INITIALISED return __accessor->read_slot_adc2(slot); } float __read_adc2_as_temp() { - if(!is_initialised()) { - throw std::runtime_error("Module not initialised\n"); - } + CHECK_INITIALISED float sense = __accessor->read_slot_adc2(slot); float r_thermistor = sense / ((3.3f - sense) / 5100.0f); float t_kelvin = (BETA * ROOM_TEMP) / (BETA + (ROOM_TEMP * log(r_thermistor / RESISTOR_AT_ROOM_TEMP))); diff --git a/libraries/yukon/modules/dual_motor/dual_motor.cpp b/libraries/yukon/modules/dual_motor/dual_motor.cpp index 0151d27a..c10d871e 100644 --- a/libraries/yukon/modules/dual_motor/dual_motor.cpp +++ b/libraries/yukon/modules/dual_motor/dual_motor.cpp @@ -18,11 +18,17 @@ namespace pimoroni { YukonModule(), motor_type(DUAL), frequency(frequency), - motors(nullptr) { + motors(nullptr), + motors_decay(nullptr), + motors_toff(nullptr), + motors_en(nullptr) { } DualMotorModule::~DualMotorModule() { delete(motors); + delete(motors_decay); + delete(motors_toff); + delete(motors_en); } std::string DualMotorModule::name() { @@ -30,7 +36,15 @@ namespace pimoroni { } void DualMotorModule::initialise(const SLOT& slot, SlotAccessor& accessor) { - // Configure strip and power pins + // Create motor objects + motors = new MotorCluster(pio0, 0, slot.FAST1, NUM_MOTORS); + + // Create motor control pin objects + motors_decay = new TCA_IO(slot.SLOW1, accessor); + motors_toff = new TCA_IO(slot.SLOW2, accessor); + motors_en = new TCA_IO(slot.SLOW3, accessor); + + // Configure motors configure(); // Pass the slot and adc functions up to the parent now that module specific initialisation has finished @@ -38,35 +52,46 @@ namespace pimoroni { } void DualMotorModule::configure() { + motors->disable_all(); + motors_decay->to_output(false); + motors_toff->to_output(false); + motors_en->to_output(false); } void DualMotorModule::enable() { - + CHECK_INITIALISED + motors_en->value(true); } void DualMotorModule::disable() { - + CHECK_INITIALISED + motors_en->value(false); } bool DualMotorModule::is_enabled() { - return 0; // TODO + CHECK_INITIALISED + return motors_en->value(); } bool DualMotorModule::decay() { - return 0; // TODO + CHECK_INITIALISED + return motors_decay->value(); } void DualMotorModule::decay(bool val) { - + CHECK_INITIALISED + motors_decay->value(val); } bool DualMotorModule::toff() { - return 0; // TODO + CHECK_INITIALISED + return motors_toff->value(); } void DualMotorModule::toff(bool val) { - + CHECK_INITIALISED + return motors_toff->value(val); } bool DualMotorModule::read_fault() { diff --git a/libraries/yukon/modules/dual_motor/dual_motor.hpp b/libraries/yukon/modules/dual_motor/dual_motor.hpp index edf30200..4207495c 100644 --- a/libraries/yukon/modules/dual_motor/dual_motor.hpp +++ b/libraries/yukon/modules/dual_motor/dual_motor.hpp @@ -55,9 +55,9 @@ namespace pimoroni { //-------------------------------------------------- private: MotorCluster* motors; - TCA motors_decay; - TCA motors_toff; - TCA motors_en; + TCA_IO* motors_decay; + TCA_IO* motors_toff; + TCA_IO* motors_en; //-------------------------------------------------- diff --git a/libraries/yukon/modules/dual_switched/dual_switched.cpp b/libraries/yukon/modules/dual_switched/dual_switched.cpp index 451abcc7..703a312d 100644 --- a/libraries/yukon/modules/dual_switched/dual_switched.cpp +++ b/libraries/yukon/modules/dual_switched/dual_switched.cpp @@ -27,6 +27,8 @@ namespace pimoroni { delete(sw_output[1]); delete(sw_enable[0]); delete(sw_enable[1]); + delete(power_good[0]); + delete(power_good[1]); } std::string DualSwitchedModule::name() { @@ -39,8 +41,8 @@ namespace pimoroni { sw_output[1] = new IO(slot.FAST3); sw_enable[0] = new IO(slot.FAST1); sw_enable[1] = new IO(slot.FAST3); - power_good[0] = slot.SLOW1; - power_good[1] = slot.SLOW3; + power_good[0] = new TCA_IO(slot.SLOW1, accessor); + power_good[1] = new TCA_IO(slot.SLOW3, accessor); // Configure switch and power pins configure(); @@ -56,14 +58,12 @@ namespace pimoroni { sw_enable[0]->to_output(false); sw_enable[1]->to_output(false); - __accessor->set_slow_config(power_good[0], false); - __accessor->set_slow_config(power_good[1], false); + power_good[0]->to_input(); + power_good[1]->to_input(); } void DualSwitchedModule::enable(uint output) { - if(!is_initialised()) { - throw std::runtime_error("Module not initialised\n"); - } + CHECK_INITIALISED if(output < 1 || output > NUM_SWITCHES) { throw std::runtime_error("switch index out of range. Expected 1 to 2\n"); } @@ -72,9 +72,7 @@ namespace pimoroni { } void DualSwitchedModule::disable(uint output) { - if(!is_initialised()) { - throw std::runtime_error("Module not initialised\n"); - } + CHECK_INITIALISED if(output < 1 || output > NUM_SWITCHES) { throw std::runtime_error("switch index out of range. Expected 1 to 2\n"); } @@ -83,9 +81,7 @@ namespace pimoroni { } bool DualSwitchedModule::is_enabled(uint output) { - if(!is_initialised()) { - throw std::runtime_error("Module not initialised\n"); - } + CHECK_INITIALISED if(output < 1 || output > NUM_SWITCHES) { throw std::runtime_error("switch index out of range. Expected 1 to 2\n"); } @@ -94,9 +90,7 @@ namespace pimoroni { } void DualSwitchedModule::output(uint output, bool val) { - if(!is_initialised()) { - throw std::runtime_error("Module not initialised\n"); - } + CHECK_INITIALISED if(output < 1 || output > NUM_SWITCHES) { throw std::runtime_error("switch index out of range. Expected 1 to 2\n"); } @@ -105,9 +99,7 @@ namespace pimoroni { } bool DualSwitchedModule:: read_output(uint output) { - if(!is_initialised()) { - throw std::runtime_error("Module not initialised\n"); - } + CHECK_INITIALISED if(output < 1 || output > NUM_SWITCHES) { throw std::runtime_error("switch index out of range. Expected 1 to 2\n"); } @@ -116,14 +108,12 @@ namespace pimoroni { } bool DualSwitchedModule::read_power_good(uint output) { - if(!is_initialised()) { - throw std::runtime_error("Module not initialised\n"); - } + CHECK_INITIALISED if(output < 1 || output > NUM_SWITCHES) { throw std::runtime_error("switch index out of range. Expected 1 to 2\n"); } - return __accessor->get_slow_input(power_good[output - 1]); + return power_good[output - 1]->value(); } float DualSwitchedModule::read_temperature() { diff --git a/libraries/yukon/modules/dual_switched/dual_switched.hpp b/libraries/yukon/modules/dual_switched/dual_switched.hpp index 4ed25d4f..1fd413e9 100644 --- a/libraries/yukon/modules/dual_switched/dual_switched.hpp +++ b/libraries/yukon/modules/dual_switched/dual_switched.hpp @@ -43,7 +43,7 @@ namespace pimoroni { private: IO* sw_output[2]; IO* sw_enable[2]; - TCA power_good[2]; + TCA_IO* power_good[2]; //-------------------------------------------------- diff --git a/libraries/yukon/modules/led_strip/led_strip.cpp b/libraries/yukon/modules/led_strip/led_strip.cpp index 88ded89d..76c4e8d9 100644 --- a/libraries/yukon/modules/led_strip/led_strip.cpp +++ b/libraries/yukon/modules/led_strip/led_strip.cpp @@ -86,30 +86,22 @@ namespace pimoroni { } void LEDStripModule::enable() { - if(!is_initialised()) { - throw std::runtime_error("Module not initialised\n"); - } + CHECK_INITIALISED power_en->value(true); } void LEDStripModule::disable() { - if(!is_initialised()) { - throw std::runtime_error("Module not initialised\n"); - } + CHECK_INITIALISED power_en->value(false); } bool LEDStripModule::is_enabled() { - if(!is_initialised()) { - throw std::runtime_error("Module not initialised\n"); - } + CHECK_INITIALISED return power_en->value(); } bool LEDStripModule::read_power_good() { - if(!is_initialised()) { - throw std::runtime_error("Module not initialised\n"); - } + CHECK_INITIALISED return power_good->value(); } diff --git a/libraries/yukon/modules/quad_servo/quad_servo_direct.cpp b/libraries/yukon/modules/quad_servo/quad_servo_direct.cpp index 7dd29769..9db349d4 100644 --- a/libraries/yukon/modules/quad_servo/quad_servo_direct.cpp +++ b/libraries/yukon/modules/quad_servo/quad_servo_direct.cpp @@ -27,6 +27,9 @@ namespace pimoroni { } void QuadServoDirectModule::initialise(const SLOT& slot, SlotAccessor& accessor) { + // Create servo cluster object + servos = new ServoCluster(pio0, 0, slot.FAST1, NUM_SERVOS); + // Configure strip and power pins configure(); @@ -35,7 +38,7 @@ namespace pimoroni { } void QuadServoDirectModule::configure() { - + servos->disable_all(); } float QuadServoDirectModule::read_adc1() { diff --git a/libraries/yukon/modules/quad_servo/quad_servo_reg.cpp b/libraries/yukon/modules/quad_servo/quad_servo_reg.cpp index c9a93eaf..c56b4bd2 100644 --- a/libraries/yukon/modules/quad_servo/quad_servo_reg.cpp +++ b/libraries/yukon/modules/quad_servo/quad_servo_reg.cpp @@ -16,11 +16,15 @@ namespace pimoroni { QuadServoRegModule::QuadServoRegModule(bool halt_on_not_pgood) : YukonModule(), halt_on_not_pgood(halt_on_not_pgood), - servos(nullptr) { + servos(nullptr), + power_en(nullptr), + power_good(nullptr) { } QuadServoRegModule::~QuadServoRegModule() { delete(servos); + delete(power_en); + delete(power_good); } std::string QuadServoRegModule::name() { @@ -28,6 +32,13 @@ namespace pimoroni { } void QuadServoRegModule::initialise(const SLOT& slot, SlotAccessor& accessor) { + // Create servo cluster object + servos = new ServoCluster(pio0, 0, slot.FAST1, NUM_SERVOS); + + // Create the power control pin objects + power_en = new TCA_IO(slot.SLOW1, accessor); + power_good = new TCA_IO(slot.SLOW2, accessor); + // Configure strip and power pins configure(); @@ -36,23 +47,30 @@ namespace pimoroni { } void QuadServoRegModule::configure() { + servos->disable_all(); + power_en->to_output(false); + power_good->to_input(); } void QuadServoRegModule::enable() { - + CHECK_INITIALISED + power_en->value(true); } void QuadServoRegModule::disable() { - + CHECK_INITIALISED + power_en->value(false); } bool QuadServoRegModule::is_enabled() { - return 0; // TODO + CHECK_INITIALISED + return power_en->value(); } bool QuadServoRegModule::read_power_good() { - return 0; // TODO + CHECK_INITIALISED + return power_good->value(); } float QuadServoRegModule::read_temperature() { diff --git a/libraries/yukon/modules/quad_servo/quad_servo_reg.hpp b/libraries/yukon/modules/quad_servo/quad_servo_reg.hpp index 66258e5c..415c15f1 100644 --- a/libraries/yukon/modules/quad_servo/quad_servo_reg.hpp +++ b/libraries/yukon/modules/quad_servo/quad_servo_reg.hpp @@ -44,8 +44,8 @@ public: public: ServoCluster* servos; private: - IO* power_en; - IO* power_good; + TCA_IO* power_en; + TCA_IO* power_good; //-------------------------------------------------- diff --git a/libraries/yukon/yukon.cmake b/libraries/yukon/yukon.cmake index 071d8dcf..10a7444c 100644 --- a/libraries/yukon/yukon.cmake +++ b/libraries/yukon/yukon.cmake @@ -2,6 +2,7 @@ add_library(yukon INTERFACE) target_sources(yukon INTERFACE ${CMAKE_CURRENT_LIST_DIR}/yukon.cpp + ${CMAKE_CURRENT_LIST_DIR}/modules/common.cpp ${CMAKE_CURRENT_LIST_DIR}/logging.cpp ${CMAKE_CURRENT_LIST_DIR}/modules/led_strip/led_strip.cpp ${CMAKE_CURRENT_LIST_DIR}/modules/quad_servo/quad_servo_direct.cpp