#include "HUB238.hpp" #include "I2CBB2.hpp" #include "Utils.h" #include "configuration.h" #if POW_PD_EXT == 1 bool hub238_probe() { return I2CBB2::probe(HUB238_ADDR); } extern int32_t powerSupplyWattageLimit; uint16_t hub238_debug_state() { uint8_t status0 = 0; uint8_t status1 = 0; if (!I2CBB2::Mem_Read(HUB238_ADDR, HUB238_REG_PD_STATUS0, &status0, 1)) { return 0xFFFF; } if (!I2CBB2::Mem_Read(HUB238_ADDR, HUB238_REG_PD_STATUS1, &status1, 1)) { return 0xFFFF; } return status1 | (((uint16_t)status0) << 8); } uint16_t pdo_slot_to_currentx100(uint8_t temp) { temp = temp & 0b1111; switch (temp) { case 0b0000: return 50; case 0b0001: return 70; case 0b0010: return 100; case 0b0011: return 125; case 0b0100: return 150; case 0b0101: return 175; case 0b0110: return 200; case 0b0111: return 225; case 0b1000: return 250; case 0b1001: return 275; case 0b1010: return 300; case 0b1011: return 325; case 0b1100: return 350; case 0b1101: return 400; case 0b1110: return 450; case 0b1111: return 500; } } uint16_t hub238_getVoltagePDOCurrent(uint8_t voltage) { uint8_t reg = HUB238_REG_SRC_PDO_5V; switch (voltage) { case 5: reg = HUB238_REG_SRC_PDO_5V; break; case 9: reg = HUB238_REG_SRC_PDO_9V; break; case 12: reg = HUB238_REG_SRC_PDO_12V; break; case 15: reg = HUB238_REG_SRC_PDO_15V; break; case 18: reg = HUB238_REG_SRC_PDO_18V; break; case 20: reg = HUB238_REG_SRC_PDO_20V; break; default: return 0; } uint8_t temp = 0; if (I2CBB2::Mem_Read(HUB238_ADDR, reg, &temp, 1) == true) { if (temp & HUB238_PDO_DETECTED) { return pdo_slot_to_currentx100(temp); } } return 0; } uint8_t findBestPDO() { uint8_t temp = 0; uint16_t ilim = 0; uint16_t minimumx10current = 0; #if USB_PD_VMAX >= 20 ilim = hub238_getVoltagePDOCurrent(20); minimumx10current = Utils::RequiredCurrentForTipAtVoltage(200); if (ilim != 0 && ilim / 10 >= minimumx10current) { powerSupplyWattageLimit = ((20 * ilim) / 100) - 2; // We take off 2W for safety of overhead return 0b1010; } #endif #if USB_PD_VMAX >= 18 ilim = hub238_getVoltagePDOCurrent(18); minimumx10current = Utils::RequiredCurrentForTipAtVoltage(180); if (ilim != 0 && ilim / 10 >= minimumx10current) { powerSupplyWattageLimit = ((18 * ilim) / 100) - 2; // We take off 2W for safety of overhead return 0b1001; } #endif #if USB_PD_VMAX >= 15 ilim = hub238_getVoltagePDOCurrent(15); minimumx10current = Utils::RequiredCurrentForTipAtVoltage(150); if (ilim != 0 && ilim / 10 >= minimumx10current) { powerSupplyWattageLimit = ((15 * ilim) / 100) - 2; // We take off 2W for safety of overhead return 0b1000; } #endif #if USB_PD_VMAX >= 12 ilim = hub238_getVoltagePDOCurrent(12); minimumx10current = Utils::RequiredCurrentForTipAtVoltage(120); if (ilim != 0 && (ilim / 10) >= minimumx10current) { powerSupplyWattageLimit = ((12 * ilim) / 100) - 2; // We take off 2W for safety of overhead return 0b0011; } #endif #if USB_PD_VMAX >= 9 ilim = hub238_getVoltagePDOCurrent(9); minimumx10current = Utils::RequiredCurrentForTipAtVoltage(90); if (ilim != 0 && ilim / 10 >= minimumx10current) { powerSupplyWattageLimit = ((9 * ilim) / 100) - 2; // We take off 2W for safety of overhead return 0b0010; } #endif powerSupplyWattageLimit = 10; return 0b0001; // 5V PDO } volatile uint8_t haveSelected = 0xFF; void hub238_check_negotiation() { // Dont do anything for first 2 seconds as its internal state machine corrupts if we ask it to change too fast if (xTaskGetTickCount() < 2000) { return; } // Want to check if there is a better PDO to be using // First, exit early if we already have changed _or_ no PD // Even if it negotiates the same voltage as we want, we still re-run it as that makes it ignore the resistor // and instead ask for max amps if (haveSelected != 0xFF || !hub238_has_negotiated() || hub238_source_voltage() == 0) { return; } uint8_t currentPDO = 0; vTaskDelay(5); uint8_t bestPDO = findBestPDO(); if (I2CBB2::Mem_Read(HUB238_ADDR, HUB238_REG_SRC_PDO, ¤tPDO, 1) == true) { currentPDO >>= 4; // grab upper bits if (currentPDO == bestPDO) { haveSelected = bestPDO; return; } currentPDO = bestPDO << 4; if (I2CBB2::Mem_Write(HUB238_ADDR, HUB238_REG_SRC_PDO, ¤tPDO, 1) == true) { currentPDO = 0x01; // request for new PDO if (I2CBB2::Mem_Write(HUB238_ADDR, HUB238_REG_GO_COMMAND, ¤tPDO, 1) == true) { haveSelected = bestPDO; vTaskDelay(50); return; } } } } bool hub238_has_run_selection() { return haveSelected != 0xFF; } bool hub238_has_negotiated() { uint8_t temp = 0; if (I2CBB2::Mem_Read(HUB238_ADDR, HUB238_REG_PD_STATUS1, &temp, 1) == true) { temp >>= 3; return (temp & 0b111) == 0b001; // success } return false; } // Return selected source voltage in V uint16_t hub238_source_voltage() { uint8_t temp = 0; if (I2CBB2::Mem_Read(HUB238_ADDR, HUB238_REG_PD_STATUS0, &temp, 1) == true) { temp >>= 4; switch (temp) { case 0b0001: return 5; case 0b0010: return 9; case 0b0011: return 12; case 0b0100: return 15; case 0b0101: return 18; case 0b0110: return 20; } } return 0; } // Return selected source current in Amps * 100 uint8_t hub238_source_currentX100() { uint8_t temp = 0; if (I2CBB2::Mem_Read(HUB238_ADDR, HUB238_REG_PD_STATUS0, &temp, 1) == true) { temp &= 0b1111; return pdo_slot_to_currentx100(temp); } return 10; // Failsafe to 0.1 amp } #endif