Skip to content

Commit

Permalink
USB-PD Support for Sequre S60P (#1883)
Browse files Browse the repository at this point in the history
* Basic Init

* Rought implementation of fs2711 usb pd interface

* Rought implementation of fs2711 usb pd interface

* Still needs work overcurrent protection keeps getting tripped

* New pdo selection logic

* Update push.yml

* Update push.yml

* Update push.yml

* Update Makefile

* Adds PPS

* Removed unused define

* Adds PPS

* Apply suggestions from code review

Co-authored-by: Ben V. Brown <[email protected]>

* Code review changes

* Added osDelay include

* New line alignment for S60 softwarei2c

* Code review

* Fixes code review stuff

* code review changes

* Change voltage limit to 20 as that's what the device is rated for

* Shortened wait time for usb pd

* Fixed issues that cuase S60P to restart constantly

* fixing minimal OLED brightness

With the current settings, the OLED turns off if the first level is selected.

* Adds protocol to s60p debug menu

* loosened fs2711 protocol selection timing

* Adds PDO register reading to negotiation logic

* Fixes FS2711 timeout issue and cleans up driver

* Adds FS2711 protocol negotiation to power loop

* Removed uneeded define

* Reverts changes to Font.h and adds clang-format comments

---------

Co-authored-by: Ben V. Brown <[email protected]>
Co-authored-by: discip <[email protected]>
Co-authored-by: Ben V. Brown <[email protected]>
  • Loading branch information
4 people authored Feb 25, 2024
1 parent 9f6f2f8 commit 9ea71bc
Show file tree
Hide file tree
Showing 12 changed files with 507 additions and 14 deletions.
15 changes: 13 additions & 2 deletions source/Core/BSP/Sequre_S60/BSP.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include "BSP.h"
#include "BootLogo.h"
#include "FS2711.hpp"
#include "HUB238.hpp"
#include "I2C_Wrapper.hpp"
#include "Pins.h"
Expand Down Expand Up @@ -210,13 +211,23 @@ bool isTipDisconnected() {

void setStatusLED(const enum StatusLED state) {}
uint8_t preStartChecks() {
#if POW_PD_EXT == 1
if (!hub238_has_run_selection() && (xTaskGetTickCount() < TICKS_SECOND * 5)) {
return 0;
}
// We check if we are in a "Limited" mode; where we have to run the PWM really fast
// Where as if we are on 9V for example, the tip resistance is enough
uint16_t voltage = hub238_source_voltage();
uint16_t currentx100 = hub238_source_currentX100();
uint16_t voltage = hub238_source_voltage();
uint16_t currentx100 = hub238_source_currentX100();
#endif
#if POW_PD_EXT == 2
if (!FS2711::has_run_selection() && (xTaskGetTickCount() < TICKS_SECOND * 5)) {
return 0;
}
uint16_t voltage = FS2711::source_voltage();
uint16_t currentx100 = FS2711::source_currentx100();
#endif

uint16_t thresholdResistancex10 = ((voltage * 1000) / currentx100) + 5;

if (getTipResistanceX10() <= thresholdResistancex10) {
Expand Down
1 change: 0 additions & 1 deletion source/Core/BSP/Sequre_S60/Pins.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@

#endif


#ifdef MODEL_S60P

#define KEY_B_Pin GPIO_PIN_1
Expand Down
2 changes: 2 additions & 0 deletions source/Core/BSP/Sequre_S60/Software_I2C.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,14 @@
#define SOFT_SDA2_LOW() HAL_GPIO_WritePin(SDA2_GPIO_Port, SDA2_Pin, GPIO_PIN_RESET)
#define SOFT_SDA2_READ() (HAL_GPIO_ReadPin(SDA2_GPIO_Port, SDA2_Pin) == GPIO_PIN_SET ? 1 : 0)
#define SOFT_SCL2_READ() (HAL_GPIO_ReadPin(SCL2_GPIO_Port, SCL2_Pin) == GPIO_PIN_SET ? 1 : 0)
// clang-format off
#define SOFT_I2C_DELAY() \
{ \
for (int xx = 0; xx < 12; xx++) { \
asm("nop"); \
} \
}
// clang-format on

#endif
// 40 ~= 100kHz; 15 gives around 250kHz or so which is fast _and_ stable
Expand Down
10 changes: 5 additions & 5 deletions source/Core/BSP/Sequre_S60/configuration.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@
* OLED Brightness
*
*/
#define MIN_BRIGHTNESS 0 // Min OLED brightness selectable
#define MAX_BRIGHTNESS 100 // Max OLED brightness selectable
#define MIN_BRIGHTNESS 1 // Min OLED brightness selectable
#define MAX_BRIGHTNESS 101 // Max OLED brightness selectable
#define BRIGHTNESS_STEP 25 // OLED brightness increment
#define DEFAULT_BRIGHTNESS 25 // default OLED brightness

Expand Down Expand Up @@ -166,7 +166,6 @@
#define MODEL_HAS_DCDC // We dont have DC/DC but have reallly fast PWM that gets us roughly the same place
#endif /* S60 */


#ifdef MODEL_S60P
#define VOLTAGE_DIV 460 // Default divider scaler
#define CALIBRATION_OFFSET 200 // Default adc offset in uV
Expand All @@ -176,7 +175,7 @@
#define POWER_LIMIT_STEPS 5
#define OP_AMP_GAIN_STAGE 536
#define TEMP_uV_LOOKUP_S60
#define USB_PD_VMAX 12 // Maximum voltage for PD to negotiate
#define USB_PD_VMAX 20 // Maximum voltage for PD to negotiate

#define HARDWARE_MAX_WATTAGE_X10 600

Expand All @@ -187,7 +186,7 @@

#define OLED_128x32
#define GPIO_VIBRATION
#define POW_PD_EXT 1
#define POW_PD_EXT 2
#define USB_PD_EPR_WATTAGE 0 /*No EPR*/
#define DEBUG_POWER_MENU_BUTTON_B 1
#define HAS_POWER_DEBUG_MENU
Expand All @@ -197,6 +196,7 @@

#define MODEL_HAS_DCDC // We dont have DC/DC but have reallly fast PWM that gets us roughly the same place
#endif /* S60P */

#define FLASH_LOGOADDR (0x08000000 + (62 * 1024))
#define SETTINGS_START_PAGE (0x08000000 + (63 * 1024))

Expand Down
239 changes: 239 additions & 0 deletions source/Core/Drivers/FS2711.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,239 @@
#include "FS2711.hpp"
#include "FS2711_defines.h"
#include "I2CBB2.hpp"
#include "configuration.h"
#if POW_PD_EXT == 2
#include "BSP.h"
#include "cmsis_os.h"
#include <stdbool.h>
#include <stdint.h>
#include <string.h>

#ifndef USB_PD_VMAX
#error Max PD Voltage must be defined
#endif

#define PROTOCOL_TIMEOUT 100 // ms

extern int32_t powerSupplyWattageLimit;

fs2711_state_t FS2711::state;

inline void i2c_write(uint8_t addr, uint8_t data) { I2CBB2::Mem_Write(FS2711_ADDR, addr, &data, 1); }

inline uint8_t i2c_read(uint8_t addr) {
uint8_t data = 0;
I2CBB2::Mem_Read(FS2711_ADDR, addr, &data, 1);
return data;
}

inline bool i2c_probe(uint8_t addr) { return I2CBB2::probe(addr); }

void FS2711::start() {
memset(&state, 0, sizeof(fs2711_state_t));
state.req_pdo_num = 0xFF;

enable_protocol(false);
osDelay(PROTOCOL_TIMEOUT);
select_protocol(FS2711_PROTOCOL_PD);
enable_protocol(true);
osDelay(PROTOCOL_TIMEOUT);
}

uint8_t FS2711::selected_protocol() { return i2c_read(FS2711_REG_SELECT_PROTOCOL); }

void FS2711::enable_protocol(bool enable) { i2c_write(FS2711_REG_ENABLE_PROTOCOL, enable ? FS2711_ENABLE : FS2711_DISABLE); }

void FS2711::select_protocol(uint8_t protocol) { i2c_write(FS2711_REG_SELECT_PROTOCOL, protocol); }

void FS2711::enable_voltage() { i2c_write(FS2711_REG_ENABLE_VOLTAGE, FS2711_ENABLE); }

bool FS2711::probe() { return i2c_probe(FS2711_ADDR); }

void FS2711::pdo_update() {
uint8_t pdo_b0 = 0, pdo_b1 = 0, pdo_b2 = 0, pdo_b3 = 0;

state.pdo_num = 0;
memset(state.pdo_type, 0, 7);
memset(state.pdo_min_volt, 0, 7);
memset(state.pdo_max_volt, 0, 7);
memset(state.pdo_max_curr, 0, 7);

for (uint8_t i = 0; i < 7; i++) {
pdo_b0 = i2c_read(FS2711_REG_PDO_B0 + i * 4);
pdo_b1 = i2c_read(FS2711_REG_PDO_B1 + i * 4);
pdo_b2 = i2c_read(FS2711_REG_PDO_B2 + i * 4);
pdo_b3 = i2c_read(FS2711_REG_PDO_B3 + i * 4);

if (pdo_b0) {
if ((pdo_b3 & FS2711_REG_PDO_B0) == FS2711_REG_PDO_B0) {
state.pdo_type[i] = FS2711_PDO_PPS;
state.pdo_min_volt[i] = pdo_b1 * 100;
state.pdo_max_volt[i] = ((pdo_b2 >> 1) + ((pdo_b3 & 0x1) << 7)) * 100;
state.pdo_max_curr[i] = (pdo_b0 & 0x7F) * 50;
} else {
state.pdo_type[i] = FS2711_PDO_FIX;
state.pdo_min_volt[i] = ((pdo_b1 >> 2) + ((pdo_b2 & 0xF) << 6)) * 50;
state.pdo_max_volt[i] = state.pdo_min_volt[i];
state.pdo_max_curr[i] = (pdo_b0 + ((pdo_b1 & 0x3) << 8)) * 10;
}
state.pdo_num++;
}
}
}

bool FS2711::open_pps(uint8_t pdoid, uint16_t volt, uint16_t max_curr) {
uint16_t wr;

if (pdoid > state.pdo_num)
return false;
if ((volt > state.pdo_max_volt[pdoid]) || (volt < state.pdo_min_volt[pdoid]))
return false;
if ((volt > state.pdo_max_volt[pdoid]) || (volt < state.pdo_min_volt[pdoid]))
return false;
if (max_curr > state.pdo_max_curr[pdoid])
return false;
if (state.pdo_type[pdoid] != FS2711_PDO_PPS)
return false;

if (FS2711::selected_protocol() == FS2711_PROTOCOL_PD) {
select_protocol(FS2711_PROTOCOL_PPS);
enable_protocol(true);
}

if (FS2711::selected_protocol() != FS2711_PROTOCOL_PPS) {
return false;
}

i2c_write(FS2711_REG_PDO_IDX, pdoid + (pdoid << 4));
wr = (volt - state.pdo_min_volt[pdoid]) / 20;
i2c_write(FS2711_PROTOCOL_PPS_CURRENT, max_curr / 50);

i2c_write(FS2711_REG_VOLT_CFG_B0, wr & 0xFF);
i2c_write(FS2711_REG_VOLT_CFG_B1, (wr >> 8) & 0xFF);
i2c_write(FS2711_REG_VOLT_CFG_B2, wr & 0xFF);
i2c_write(FS2711_REG_VOLT_CFG_B3, (wr >> 8) & 0xFF);

enable_voltage();

state.source_voltage = volt;
state.source_current = max_curr;
state.req_pdo_num = pdoid;
powerSupplyWattageLimit = ((volt * max_curr) / 1000000) - 2;
return true;
}

bool FS2711::open_pd(uint8_t pdoid) {
if (pdoid >= state.pdo_num) {
return false;
}
if (state.pdo_type[pdoid] != FS2711_PDO_FIX) {
return false;
}

if (FS2711::selected_protocol() != FS2711_PROTOCOL_PD) {
return false;
}

i2c_write(FS2711_REG_PDO_IDX, pdoid + (pdoid << 4));

enable_voltage();

state.source_voltage = state.pdo_max_volt[pdoid];
state.source_current = state.pdo_max_curr[pdoid];
state.req_pdo_num = pdoid;

powerSupplyWattageLimit = ((state.source_voltage * state.source_current) / 1000000) - 2;
return true;
}

void FS2711::negotiate() {
uint16_t best_voltage = 0;
uint16_t best_current = 0;
uint8_t best_pdoid = 0xFF;
bool pps = false;

int min_resistance_omhsx10 = 0;

// FS2711 uses mV instead of V
const uint16_t vmax = USB_PD_VMAX * 1000;
const uint8_t tip_resistance = getTipResistanceX10() + 5;

uint16_t pdo_min_mv = 0, pdo_max_mv = 0, pdo_max_curr = 0, pdo_type = 0;

FS2711::pdo_update();

for (int i = 0; state.pdo_num > i; i++) {
pdo_min_mv = state.pdo_min_volt[i];
pdo_max_mv = state.pdo_max_volt[i];
pdo_max_curr = state.pdo_max_curr[i];
pdo_type = state.pdo_type[i];

min_resistance_omhsx10 = (pdo_max_mv / pdo_max_curr) * 10;

switch (pdo_type) {
case FS2711_PDO_FIX:
if (pdo_max_mv > 0 && vmax >= pdo_max_mv) {
if (min_resistance_omhsx10 <= tip_resistance) {
if (pdo_max_mv > best_voltage) {
pps = false;
best_pdoid = i;
best_voltage = pdo_max_mv;
best_current = pdo_max_curr;
}
}
}
break;

case FS2711_PDO_PPS: {
int ideal_mv = tip_resistance * (pdo_max_curr / 10);
if (ideal_mv > pdo_max_mv) {
ideal_mv = pdo_max_mv;
}

if (ideal_mv > vmax) {
ideal_mv = vmax;
}

if (ideal_mv > best_voltage) {
best_pdoid = i;
best_voltage = ideal_mv;
best_current = pdo_max_curr;
pps = true;
}
}

break;

default:
break;
}
}

if (best_pdoid != 0xFF && best_pdoid != state.req_pdo_num) {
if (pps) {
FS2711::open_pps(best_pdoid, best_voltage, best_current);
} else {
FS2711::open_pd(best_pdoid);
}
}
}

bool FS2711::has_run_selection() { return state.req_pdo_num != 0xFF; }

uint16_t FS2711::source_voltage() { return state.source_voltage / 1000; }

// FS2711 does current in mV so it needs to be converted to x100 intead of x1000
uint16_t FS2711::source_currentx100() { return state.source_current / 10; }

uint16_t FS2711::debug_pdo_max_voltage(uint8_t pdoid) { return state.pdo_max_volt[pdoid]; }

uint16_t FS2711::debug_pdo_min_voltage(uint8_t pdoid) { return state.pdo_min_volt[pdoid]; }

uint16_t FS2711::debug_pdo_source_current(uint8_t pdoid) { return state.pdo_max_curr[pdoid]; }

uint16_t FS2711::debug_pdo_type(uint8_t pdoid) { return state.pdo_type[pdoid]; }

fs2711_state_t FS2711::debug_get_state() { return state; }

#endif
Loading

0 comments on commit 9ea71bc

Please sign in to comment.