Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions radio/src/boards/generic_stm32/i2c_bus.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,27 @@
#include "hal/i2c_driver.h"
#include "hal.h"

#if defined(FREE_RTOS)
#include "os/task.h"
#endif

#define I2C_DEFAULT_TIMEOUT 10
#define I2C_DEFAULT_RETRIES 2

#if defined(FREE_RTOS)
static mutex_handle_t _i2c_mutex[MAX_I2C_BUSES];
static bool _i2c_mutex_initialized[MAX_I2C_BUSES] = {};

static void i2c_ensure_mutex(uint8_t bus)
{
if (bus < MAX_I2C_BUSES && !_i2c_mutex_initialized[bus] &&
scheduler_is_running()) {
mutex_create(&_i2c_mutex[bus]);
_i2c_mutex_initialized[bus] = true;
}
}
#endif

#if defined(I2C_B1)
static const stm32_i2c_hw_def_t _i2c1 = {
.I2Cx = I2C_B1,
Expand Down Expand Up @@ -92,6 +110,35 @@ int i2c_deinit(etx_i2c_bus_t bus)
return -1;
}

void i2c_lock(etx_i2c_bus_t bus)
{
#if defined(FREE_RTOS)
if (bus >= MAX_I2C_BUSES) return;
i2c_ensure_mutex(bus);
if (scheduler_is_running())
mutex_lock(&_i2c_mutex[bus]);
Comment thread
coderabbitai[bot] marked this conversation as resolved.
#endif
}

bool i2c_trylock(etx_i2c_bus_t bus)
{
#if defined(FREE_RTOS)
if (bus >= MAX_I2C_BUSES) return false;
i2c_ensure_mutex(bus);
if (scheduler_is_running())
return mutex_trylock(&_i2c_mutex[bus]);
#endif
return true;
}

void i2c_unlock(etx_i2c_bus_t bus)
{
#if defined(FREE_RTOS)
if (bus < MAX_I2C_BUSES && scheduler_is_running())
mutex_unlock(&_i2c_mutex[bus]);
#endif
}

int i2c_dev_ready(etx_i2c_bus_t bus, uint16_t addr)
{
return stm32_i2c_is_dev_ready(bus, addr, I2C_DEFAULT_RETRIES,
Expand Down
3 changes: 3 additions & 0 deletions radio/src/boards/jumper-h750/touch_driver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,8 @@ TouchState touchPanelRead()
{
if (!touchEventOccured) return internalTouchState;

i2c_lock(TOUCH_I2C_BUS);

touchEventOccured = false;

tmr10ms_t now = timersGetMsTick();
Expand Down Expand Up @@ -285,6 +287,7 @@ TouchState touchPanelRead()
if (internalTouchState.event == TE_UP ||
internalTouchState.event == TE_SLIDE_END)
internalTouchState.event = TE_NONE;
i2c_unlock(TOUCH_I2C_BUS);
return ret;
}

Expand Down
5 changes: 5 additions & 0 deletions radio/src/boards/rm-h750/bsp_io.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include "stm32_rgbleds.h"
#include "stm32_switch_driver.h"
#include "stm32_i2c_driver.h"
#include "hal/i2c_driver.h"
#include "hal/switch_driver.h"
#include "drivers/pca95xx.h"
#include "timers_driver.h"
Expand Down Expand Up @@ -112,8 +113,12 @@ static void _poll_switches(void *param1, uint32_t trigger_source)
// Suspend hardware reads when required
if (suspendI2CTasks) return;

if (!i2c_trylock(IO_EXPANDER_I2C_BUS)) return;

_read_io_expander(&_io_switches);
_read_io_expander(&_io_fs_switches);

i2c_unlock(IO_EXPANDER_I2C_BUS);
}

static void _io_int_handler()
Expand Down
5 changes: 5 additions & 0 deletions radio/src/boards/rm-h750/tp_gt911.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,8 @@ struct TouchState touchPanelRead()

if (!touchEventOccured) return internalTouchState;

i2c_lock(TOUCH_I2C_BUS);

touchEventOccured = false;

uint32_t startReadStatus = timersGetMsTick();
Expand All @@ -241,6 +243,7 @@ struct TouchState touchPanelRead()
touchGT911hiccups++;
TRACE("GT911 I2C read XY error");
if (!I2C_ReInit()) TRACE("I2C B1 ReInit failed");
i2c_unlock(TOUCH_I2C_BUS);
return internalTouchState;
}

Expand All @@ -266,6 +269,7 @@ struct TouchState touchPanelRead()
touchGT911hiccups++;
TRACE("GT911 I2C data read error");
if (!I2C_ReInit()) TRACE("I2C B1 ReInit failed");
i2c_unlock(TOUCH_I2C_BUS);
return internalTouchState;
}

Expand Down Expand Up @@ -316,6 +320,7 @@ struct TouchState touchPanelRead()
}

TRACE("touch event = %s", event2str(internalTouchState.event));
i2c_unlock(TOUCH_I2C_BUS);
return internalTouchState;
}

Expand Down
40 changes: 40 additions & 0 deletions radio/src/cli.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1845,6 +1845,45 @@ int cliResetGT911(const char** argv)
}
#endif

#include "hal/i2c_driver.h"

int cliI2C(const char** argv)
{
if (!argv[1]) {
cliSerialPrint("Usage: i2c scan <bus>");
return 0;
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}

if (!strcmp(argv[1], "scan")) {
int bus = 0;
if (toInt(argv, 2, &bus) < 0) {
cliSerialPrint("Usage: i2c scan <bus>");
return -1;
}
if (bus < 0 || bus >= MAX_I2C_BUSES) {
cliSerialPrint("Invalid I2C bus: %d", bus);
return -1;
}

auto i2cBus = (etx_i2c_bus_t)bus;
cliSerialPrint("Scanning I2C bus %d...", bus);
i2c_lock(i2cBus);
int found = 0;
for (int addr = 0x08; addr < 0x78; addr++) {
if (i2c_dev_ready(i2cBus, addr) >= 0) {
cliSerialPrint(" 0x%02X: ACK", addr);
found++;
}
}
i2c_unlock(i2cBus);
cliSerialPrint("Found %d device(s)", found);
return 0;
}

cliSerialPrint("Unknown subcommand: %s", argv[1]);
return -1;
}

const CliCommand cliCommands[] = {
{ "beep", cliBeep, "[<frequency>] [<duration>]" },
{ "ls", cliLs, "<directory>" },
Expand All @@ -1871,6 +1910,7 @@ const CliCommand cliCommands[] = {
{ "repeat", cliRepeat, "<interval in ms> <command>" },
{ "testfatfs", cliTestFatFsSD, "" },
#endif
{ "i2c", cliI2C, "scan <bus>" },
{ "help", cliHelp, "[<command>]" },
#if defined(JITTER_MEASURE)
{ "jitter", cliShowJitter, "" },
Expand Down
191 changes: 191 additions & 0 deletions radio/src/drivers/csd203.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
/*
* Copyright (C) EdgeTX
*
* Based on code named
* opentx - https://github.com/opentx/opentx
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/

#include "csd203.h"

#include "delays_driver.h"
#include "hal/i2c_driver.h"
#include "os/timer.h"

// CSD203 register map
#define CSD203_REG_CONFIG 0x00
#define CSD203_REG_SHUNT_V 0x01
#define CSD203_REG_BUS_V 0x02
#define CSD203_REG_POWER 0x03
#define CSD203_REG_CURRENT 0x04
#define CSD203_REG_CALIBRATION 0x05
#define CSD203_REG_MFR_ID 0xFE

#define CSD203_MFR_ID_EXPECTED 0x4153

// Calibration parameter: gain * 10000K
#define CSD203_CAL_PARAM 51200

// Config register fields (matching CSD203 datasheet)
#define CFG_RST (1 << 15)
#define CFG_AVG_16 (2 << 9)
#define CFG_VBUS_CT_1MS (4 << 5)
#define CFG_VSHT_CT_1MS (4 << 2)
#define CFG_MODE_CONT 7

extern bool suspendI2CTasks;

static int csd203_read_reg(csd203_t* dev, uint8_t reg, uint16_t* value)
{
uint8_t buf[2];
if (i2c_read(dev->bus, dev->addr, reg, 1, buf, 2) < 0)
return -1;
*value = (buf[0] << 8) | buf[1];
return 0;
}

static int csd203_write_reg(csd203_t* dev, uint8_t reg, uint16_t value)
{
uint8_t buf[2] = {(uint8_t)(value >> 8), (uint8_t)(value & 0xFF)};
return i2c_write(dev->bus, dev->addr, reg, 1, buf, 2);
}

int csd203_init(csd203_t* dev, etx_i2c_bus_t bus, uint16_t addr,
uint16_t rshunt, uint16_t current_lsb)
{
dev->bus = bus;
dev->addr = addr;
dev->initialized = false;

uint16_t config = CFG_RST | CFG_AVG_16 | CFG_VBUS_CT_1MS |
CFG_VSHT_CT_1MS | CFG_MODE_CONT;
if (csd203_write_reg(dev, CSD203_REG_CONFIG, config) < 0)
return -1;

uint16_t cal = CSD203_CAL_PARAM / (current_lsb * rshunt);
if (csd203_write_reg(dev, CSD203_REG_CALIBRATION, cal) < 0)
Comment thread
coderabbitai[bot] marked this conversation as resolved.
return -1;

delay_ms(1);

uint16_t mfr_id;
if (csd203_read_reg(dev, CSD203_REG_MFR_ID, &mfr_id) < 0)
return -1;

if (mfr_id != CSD203_MFR_ID_EXPECTED)
return -1;

// Dummy current read to clear conversion-ready flag
uint16_t dummy;
csd203_read_reg(dev, CSD203_REG_CURRENT, &dummy);

dev->initialized = true;
return 0;
}

uint16_t csd203_read_voltage(csd203_t* dev)
{
uint16_t val = 0;
csd203_read_reg(dev, CSD203_REG_BUS_V, &val);
return val;
}

uint16_t csd203_read_current(csd203_t* dev)
{
uint16_t val = 0;
csd203_read_reg(dev, CSD203_REG_CURRENT, &val);
return val;
}

// Board-level: 3 CSD203 sensors (main, internal, external)
// Addresses: A1=VS/A0=GND (0x44), A1=VS/A0=VS (0x45), A1=GND/A0=VS (0x41)
#define CSD203_ADDR_MAIN 0x44
#define CSD203_ADDR_INTERNAL 0x45
#define CSD203_ADDR_EXTERNAL 0x41

// Shunt resistor 10 mOhm, current LSB 1 mA
#define CSD203_RSHUNT 10
#define CSD203_CURRENT_LSB 10

static csd203_t csd203_main;
static csd203_t csd203_internal;
static csd203_t csd203_external;

static etx_i2c_bus_t csd203_bus;
static uint16_t csd203_ext_vbus;
static timer_handle_t csd203_timer = TIMER_INITIALIZER;

#define CSD203_POLL_PERIOD_MS 10

static void csd203TimerCb(timer_handle_t* timer)
{
(void)timer;
static uint16_t step = 0;

if (suspendI2CTasks) return;
if (!i2c_trylock(csd203_bus)) return;

if (step == 0 && csd203_main.initialized) {
csd203_read_current(&csd203_main);
csd203_read_voltage(&csd203_main);
} else if (step == 1 && csd203_internal.initialized) {
csd203_read_current(&csd203_internal);
csd203_read_voltage(&csd203_internal);
} else if (step == 2 && csd203_external.initialized) {
csd203_read_current(&csd203_external);
csd203_ext_vbus = (uint16_t)(csd203_read_voltage(&csd203_external) * 1.25f);
}

i2c_unlock(csd203_bus);
if (++step >= 3) step = 0;
}

void csd203_start(etx_i2c_bus_t bus)
{
csd203_bus = bus;

if (i2c_init(bus) < 0)
return;

csd203_init(&csd203_main, bus, CSD203_ADDR_MAIN,
CSD203_RSHUNT, CSD203_CURRENT_LSB);

delay_ms(5);
csd203_init(&csd203_internal, bus, CSD203_ADDR_INTERNAL,
CSD203_RSHUNT, CSD203_CURRENT_LSB);

delay_ms(5);
csd203_init(&csd203_external, bus, CSD203_ADDR_EXTERNAL,
CSD203_RSHUNT, CSD203_CURRENT_LSB);
Comment thread
coderabbitai[bot] marked this conversation as resolved.

// Prime the external Vbus cache so getBatteryVoltage() doesn't report a
// false 0V before the timer's first external-sensor poll. Runs pre-scheduler,
// so no bus lock is needed (matches the init reads above).
if (csd203_external.initialized)
csd203_ext_vbus = (uint16_t)(csd203_read_voltage(&csd203_external) * 1.25f);

if (csd203_main.initialized || csd203_internal.initialized ||
csd203_external.initialized) {
timer_create(&csd203_timer, csd203TimerCb, "csd203",
CSD203_POLL_PERIOD_MS, true);
timer_start(&csd203_timer);
}
}

uint16_t getBatteryVoltage()
{
return csd203_ext_vbus / 10;
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}
Loading