Skip to content

Commit

Permalink
Merge pull request #62 from emfcamp/initial-pd
Browse files Browse the repository at this point in the history
Initial pd
  • Loading branch information
MatthewWilkes authored May 29, 2024
2 parents ff91f96 + b477999 commit f3fd760
Show file tree
Hide file tree
Showing 21 changed files with 2,433 additions and 51 deletions.
3 changes: 3 additions & 0 deletions drivers/micropython.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,8 @@ include(${CMAKE_CURRENT_LIST_DIR}/tildagon_usb/tildagon_usb.cmake)
# Add TCA9548A I2C MUX and micropython machine.I2C bindings
include(${CMAKE_CURRENT_LIST_DIR}/tildagon_i2c/tildagon_i2c.cmake)

# Add PMIC and usb PD and micropython power bindings
include(${CMAKE_CURRENT_LIST_DIR}/tildagon_power/tildagon_power.cmake)

# Add OTA helpers
include(${CMAKE_CURRENT_LIST_DIR}/ota/micropython.cmake)
39 changes: 25 additions & 14 deletions drivers/tildagon_i2c/tildagon_i2c.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@
#include "driver/i2c.h"
#include "hal/i2c_ll.h"

#include "tca9548a.h"
#include "tildagon_i2c.h"


Expand All @@ -48,16 +47,23 @@
#define MP_I2C_MUX_PORT_MAX (7)


typedef struct _tildagon_mux_i2c_obj_t {
mp_obj_base_t base;
const tca9548a_i2c_mux_t *mux;
tca9548a_i2c_port_t port;
} tildagon_mux_i2c_obj_t;

static tildagon_mux_i2c_obj_t tildagon_mux_i2c_obj[8];

static tca9548a_i2c_mux_t tildagon_i2c_mux;

tildagon_mux_i2c_obj_t* tildagon_get_mux_obj( uint8_t port )
{
if ( tildagon_mux_i2c_obj[port].base.type == NULL )
{
// Created for the first time
tildagon_mux_i2c_obj[port].base.type = &machine_i2c_type;
tildagon_mux_i2c_obj[port].mux = tildagon_get_i2c_mux();
tildagon_mux_i2c_obj[port].port = port;
}
return &tildagon_mux_i2c_obj[port];
}

const tca9548a_i2c_mux_t *tildagon_get_i2c_mux() {
return &tildagon_i2c_mux;
}
Expand All @@ -81,16 +87,11 @@ void tildagon_i2c_init() {
i2c_driver_install(TILDAGON_HOST_I2C_PORT, I2C_MODE_MASTER, 0, 0, 0);
}

int tildagon_mux_i2c_transfer(mp_obj_base_t *self_in, uint16_t addr, size_t n, mp_machine_i2c_buf_t *bufs, unsigned int flags) {
tildagon_mux_i2c_obj_t *self = MP_OBJ_TO_PTR(self_in);

if (addr == self->mux->addr) {
return -MP_ENODEV;
}


i2c_cmd_handle_t cmd = i2c_cmd_link_create();
int tildagon_mux_i2c_transaction(tildagon_mux_i2c_obj_t *self, uint16_t addr, size_t n, mp_machine_i2c_buf_t *bufs, unsigned int flags) {

int data_len = 0;
i2c_cmd_handle_t cmd = i2c_cmd_link_create();

if (flags & MP_MACHINE_I2C_FLAG_WRITE1) {
i2c_master_start(cmd);
Expand Down Expand Up @@ -134,6 +135,16 @@ int tildagon_mux_i2c_transfer(mp_obj_base_t *self_in, uint16_t addr, size_t n, m
return data_len;
}

int tildagon_mux_i2c_transfer(mp_obj_base_t *self_in, uint16_t addr, size_t n, mp_machine_i2c_buf_t *bufs, unsigned int flags) {
tildagon_mux_i2c_obj_t *self = MP_OBJ_TO_PTR(self_in);

if (addr == self->mux->addr) {
return -MP_ENODEV;
}

return tildagon_mux_i2c_transaction( self, addr, n, bufs, flags);
}

/******************************************************************************/
// MicroPython bindings for machine API

Expand Down
14 changes: 14 additions & 0 deletions drivers/tildagon_i2c/tildagon_i2c.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
#ifndef _MICROPY_PY_TILDAGON_I2C
#define _MICROPY_PY_TILDAGON_I2C


#include "tca9548a.h"
#include "driver/i2c.h"
#include "extmod/modmachine.h"

#if CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S3
#define I2C_SCLK_FREQ XTAL_CLK_FREQ
#elif CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2
Expand All @@ -15,9 +20,18 @@
#define TILDAGON_HOST_I2C_PORT (0)
#define TILDAGON_HOST_I2C_TIMEOUT (50000)

typedef struct _tildagon_mux_i2c_obj_t {
mp_obj_base_t base;
const tca9548a_i2c_mux_t *mux;
tca9548a_i2c_port_t port;
} tildagon_mux_i2c_obj_t;

const tca9548a_i2c_mux_t *tildagon_get_i2c_mux();

tildagon_mux_i2c_obj_t *tildagon_get_mux_obj( uint8_t port );

void tildagon_i2c_init();

int tildagon_mux_i2c_transaction(tildagon_mux_i2c_obj_t *self_in, uint16_t addr, size_t n, mp_machine_i2c_buf_t *bufs, unsigned int flags);

#endif // _MICROPY_PY_TILDAGON_I2C
194 changes: 194 additions & 0 deletions drivers/tildagon_power/bq25895/bq25895.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
/*
BQ25895 power management ic driver in c
The driver supports basic read write access with some helpers and setup
*/

#include "bq25895.h"

typedef struct
{
uint8_t regaddr;
uint8_t mask;
uint8_t position;
} bq_register_t;

typedef struct
{
uint8_t regaddr;
uint8_t mask;
uint8_t position;
float scaling;
float offset;
} scaled_register_t;

#define ADDRESS 0x6A
#define READ ( MP_MACHINE_I2C_FLAG_WRITE1 | MP_MACHINE_I2C_FLAG_READ | MP_MACHINE_I2C_FLAG_STOP )
#define WRITE MP_MACHINE_I2C_FLAG_STOP

static const scaled_register_t input_Ilim = { 0x00U, 0x3FU, 0U, 50.0F, 100.0F };
static const bq_register_t Ilim_pin = { 0x00U, 0x40U, 6U };
static const bq_register_t enable_HiZ = { 0x00U, 0x80U, 7U };
static const bq_register_t otg_boost = { 0x03U, 0x20U, 5U };
static const bq_register_t batfet_disable = { 0x09U, 0x20U, 5U };
static const bq_register_t register_reset = { 0x14U, 0x80U, 7U };

static void write_bits( bq_state_t* state, bq_register_t reg, uint8_t value );
static void write_scaled( bq_state_t* state, scaled_register_t scaledregister, float value );

/**
* @brief initialise the bq25895
* @details reset then setup 500mA Iin limit, boost disabled, charging enabled,
* ADC at 1Hz, disable unused features and disable watchdog to reduce interruptions
* @param state pmic object
*/
void bq_init( bq_state_t* state )
{
write_bits( state, register_reset, 1 );
uint8_t write_buffer[3] = { 0x02, 0x60, 0x1A };
mp_machine_i2c_buf_t buffer = { .len = 3, .buf = write_buffer };
tildagon_mux_i2c_transaction( state->mux_port, ADDRESS, 1, &buffer, WRITE );
write_buffer[0] = 0x07;
write_buffer[1] = 0x8C;
buffer.len = 2;
tildagon_mux_i2c_transaction( state->mux_port, ADDRESS, 1, &buffer, WRITE );
}

/**
* @brief Put the converter into High impedance mode on the input to prevent
* current draw or take it out of HiZ mode
* @param state pmic object
* @param enable uint8_t if > 0 enable the high impedance mode, if 0 disable
* the high impedance mode
*/
void bq_enable_HiZ_input( bq_state_t* state,uint8_t enable )
{
if (enable > 1)
{
enable = 1;
}
write_bits( state, enable_HiZ, enable );
}

/**
* @brief Control the boost output
* @param state pmic object
* @param enable uint8_t if > 0 enable boost converter
* if 0 disable the boost converter
*/
void bq_enable_boost( bq_state_t* state, uint8_t enable )
{
if (enable > 1)
{
enable = 1;
}
write_bits( state, otg_boost, enable );
}

/**
* @brief Disconnect the battery from the IC
* @param state pmic object
*/
void bq_disconnect_battery( bq_state_t* state )
{
write_bits( state, batfet_disable, 1 );
}

/**
* @brief Set the Input current limit
* @param state pmic object
* @param limit float Limit in mA, range 100-3250mA resolution 50mA
*/
void bq_set_input_current_limit( bq_state_t* state, float limit )
{
write_scaled( state, input_Ilim, limit );
if (limit > 1500.0F)
{
write_bits( state, Ilim_pin, 1 );
}
else
{
write_bits( state, Ilim_pin, 0 );
}
}

/**
* @brief update the status
* @param state pmic object
*/
void bq_update_state( bq_state_t* state )
{
uint8_t address = 0x0BU;
uint8_t read_buffer[8];
mp_machine_i2c_buf_t buffer[2] = { { .len = 1, .buf = &address },
{ .len = 8, .buf = read_buffer } };
tildagon_mux_i2c_transaction( state->mux_port, ADDRESS, 2, buffer, READ );
state->status = read_buffer[0];
state->fault = read_buffer[1];
if ( ( read_buffer[3] & 0x7F ) == 0U )
{
state->vbat = 0.0F;
}
else
{
state->vbat = ((float)( read_buffer[3] & 0x7F) * 0.02F ) + 2.304F;
}
if ( ( read_buffer[4] & 0x7F ) == 0U )
{
state->vsys = 0.0F;
}
else
{
state->vsys = ((float)( read_buffer[4] & 0x7F) * 0.02F ) + 2.304F;
}
if ( ( read_buffer[6] & 0x7F) == 0U )
{
state->vbus = 0.0F;
}
else
{
state->vbus = ((float)( read_buffer[6] & 0x7F) * 0.10F ) + 2.600F;
}
state->ichrg = ((float)( read_buffer[7] & 0x7F) * 0.05F );
}

/**
* @brief read modify write a single register entry
* @param state pmic object
* @param reg bq_register_t details of the register to read
* @param value uint to convert to a register entry
*/
inline void write_bits( bq_state_t* state, bq_register_t reg, uint8_t value )
{
uint8_t regVal = 0U;
mp_machine_i2c_buf_t buffer[2] = { { .len = 1, .buf = &reg.regaddr },
{ .len = 1, .buf = &regVal } };
tildagon_mux_i2c_transaction( state->mux_port, ADDRESS, 2, buffer, READ );
regVal = regVal & (~reg.mask);
regVal = regVal | ( value << reg.position );
uint8_t write_buffer[2] = { reg.regaddr, regVal };
buffer[0].len = 2;
buffer[0].buf = write_buffer;
tildagon_mux_i2c_transaction( state->mux_port, ADDRESS, 1, buffer, WRITE );
}

/**
* @brief read modify write a value from a register after applying scaling and offset.
* @param state pmic object
* @param scaledregister scaled_register_t details of the register to read
* @param value float to convert to a register entry
*/
inline void write_scaled( bq_state_t* state, scaled_register_t scaledregister, float value )
{
uint8_t regVal = 0;
mp_machine_i2c_buf_t buffer[2] = { { .len = 1, .buf = &scaledregister.regaddr },
{ .len = 1, .buf = &regVal } };
tildagon_mux_i2c_transaction( state->mux_port, ADDRESS, 2, buffer, READ );
regVal = regVal & (~scaledregister.mask);
regVal |= ( (int)(( value - scaledregister.offset ) / scaledregister.scaling ) << scaledregister.position ) & scaledregister.mask;
uint8_t write_buffer[2] = { scaledregister.regaddr, regVal };
((mp_machine_i2c_buf_t*)&buffer[0])->len = 2;
((mp_machine_i2c_buf_t*)&buffer[0])->buf = write_buffer;
tildagon_mux_i2c_transaction( state->mux_port, ADDRESS, 1, buffer, WRITE );
}
79 changes: 79 additions & 0 deletions drivers/tildagon_power/bq25895/bq25895.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@

#ifndef BQ25895_H
#define BQ25895_H

#include <stdint.h>
#include "tildagon_i2c.h"

typedef enum
{
BQ_NOTCHARGING = 0x00,
BQ_PRECHARGING = 0x08,
BQ_FASTCHARGING = 0x10,
BQ_TERMINATED = 0x18
} bq_charge_status_t;

typedef enum
{
BQ_FAULT_NONE = 0x00,
BQ_FAULT_INPUT = 0x04,
BQ_FAULT_THERMAL = 0x08,
BQ_FAULT_TIMER = 0x0C,
} bq_charge_fault_t;

#define BQ_CHARGE_STAT_MASK (uint8_t)0x18U
#define BQ_FAULT_BOOST_MASK (uint8_t)0x08U
#define BQ_FAULT_CHARGE_MASK (uint8_t)0x30U
#define BQ_FAULT_BATTERY_MASK (uint8_t)0x40U

typedef struct
{
float vbus;
float vsys;
float vbat;
float ichrg;
uint8_t fault;
uint8_t status;
tildagon_mux_i2c_obj_t* mux_port;
} bq_state_t;

/**
* @brief initialise the bq25895
* @details reset then setup 500mA Iin limit, boost disabled, charging enabled,
* ADC at 1Hz, disable unused features and disable watchdog to reduce interruptions
* @param state pmic object
*/
extern void bq_init( bq_state_t* state );
/**
* @brief Put the converter into High impedance mode on the input to prevent
* current draw or take it out of HiZ mode
* @param state pmic object
* @param enable uint8_t if > 0 enable the high impedance mode, if 0 disable
* the high impedance mode
*/
extern void bq_enable_HiZ_input( bq_state_t* state, uint8_t enable );
/**
* @brief Control the boost output
* @param state pmic object
* @param enable uint8_t if > 0 enable boost converter
* if 0 disable the boost converter
*/
extern void bq_enable_boost( bq_state_t* state, uint8_t enable );
/**
* @brief Disconnect the battery from the IC
* @param state pmic object
*/
extern void bq_disconnect_battery( bq_state_t* state );
/**
* @brief Set the Input current limit
* @param state pmic object
* @param limit float Limit in mA, range 100-3250mA resolution 50mA
*/
extern void bq_set_input_current_limit( bq_state_t* state, float limit );
/**
* @brief update the status
* @param state pmic object
*/
extern void bq_update_state( bq_state_t* state );

#endif /* BQ25895_H */
Loading

0 comments on commit f3fd760

Please sign in to comment.