I2C and GPIO improvements

This commit is contained in:
Ken Van Hoeylandt 2026-01-27 22:51:16 +01:00
parent d6e1c0d9cb
commit e389cb00db
7 changed files with 289 additions and 9 deletions

View File

@ -9,6 +9,7 @@
#include <tactility/drivers/esp32_i2c.h>
#define TAG LOG_TAG(esp32_i2c)
#define ACK_CHECK_EN 1
struct InternalData {
Mutex mutex { 0 };
@ -30,7 +31,7 @@ struct InternalData {
extern "C" {
static int read(Device* device, uint8_t address, uint8_t* data, size_t data_size, TickType_t timeout) {
static error_t read(Device* device, uint8_t address, uint8_t* data, size_t data_size, TickType_t timeout) {
vPortAssertIfInISR();
auto* driver_data = GET_DATA(device);
lock(driver_data);
@ -40,7 +41,7 @@ static int read(Device* device, uint8_t address, uint8_t* data, size_t data_size
return esp_err_to_error(esp_error);
}
static int write(Device* device, uint8_t address, const uint8_t* data, uint16_t dataSize, TickType_t timeout) {
static error_t write(Device* device, uint8_t address, const uint8_t* data, uint16_t dataSize, TickType_t timeout) {
vPortAssertIfInISR();
auto* driver_data = GET_DATA(device);
lock(driver_data);
@ -50,7 +51,7 @@ static int write(Device* device, uint8_t address, const uint8_t* data, uint16_t
return esp_err_to_error(esp_error);
}
static int write_read(Device* device, uint8_t address, const uint8_t* write_data, size_t write_data_size, uint8_t* read_data, size_t read_data_size, TickType_t timeout) {
static error_t write_read(Device* device, uint8_t address, const uint8_t* write_data, size_t write_data_size, uint8_t* read_data, size_t read_data_size, TickType_t timeout) {
vPortAssertIfInISR();
auto* driver_data = GET_DATA(device);
lock(driver_data);
@ -60,14 +61,59 @@ static int write_read(Device* device, uint8_t address, const uint8_t* write_data
return esp_err_to_error(esp_error);
}
static int start(Device* device) {
static error_t read_register(Device* device, uint8_t address, uint8_t reg, uint8_t* data, size_t dataSize, TickType_t timeout) {
auto* driver_data = GET_DATA(device);
lock(driver_data);
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
// Set address pointer
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (address << 1) | I2C_MASTER_WRITE, ACK_CHECK_EN);
i2c_master_write(cmd, &reg, 1, ACK_CHECK_EN);
// Read length of response from current pointer
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (address << 1) | I2C_MASTER_READ, ACK_CHECK_EN);
if (dataSize > 1) {
i2c_master_read(cmd, data, dataSize - 1, I2C_MASTER_ACK);
}
i2c_master_read_byte(cmd, data + dataSize - 1, I2C_MASTER_NACK);
i2c_master_stop(cmd);
// TODO: We're passing an inaccurate timeout value as we already lost time with locking
esp_err_t esp_error = i2c_master_cmd_begin(GET_CONFIG(device)->port, cmd, timeout);
i2c_cmd_link_delete(cmd);
unlock(driver_data);
ESP_ERROR_CHECK_WITHOUT_ABORT(esp_error);
return esp_err_to_error(esp_error);
}
static error_t write_register(Device* device, uint8_t address, uint8_t reg, const uint8_t* data, uint16_t dataSize, TickType_t timeout) {
auto* driver_data = GET_DATA(device);
lock(driver_data);
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (address << 1) | I2C_MASTER_WRITE, ACK_CHECK_EN);
i2c_master_write_byte(cmd, reg, ACK_CHECK_EN);
i2c_master_write(cmd, (uint8_t*) data, dataSize, ACK_CHECK_EN);
i2c_master_stop(cmd);
// TODO: We're passing an inaccurate timeout value as we already lost time with locking
esp_err_t esp_error = i2c_master_cmd_begin(GET_CONFIG(device)->port, cmd, timeout);
i2c_cmd_link_delete(cmd);
unlock(driver_data);
ESP_ERROR_CHECK_WITHOUT_ABORT(esp_error);
return esp_err_to_error(esp_error);
}
static error_t start(Device* device) {
ESP_LOGI(TAG, "start %s", device->name);
auto* data = new InternalData();
device_set_driver_data(device, data);
return ERROR_NONE;
}
static int stop(Device* device) {
static error_t stop(Device* device) {
ESP_LOGI(TAG, "stop %s", device->name);
auto* driver_data = static_cast<InternalData*>(device_get_driver_data(device));
device_set_driver_data(device, nullptr);
@ -75,10 +121,13 @@ static int stop(Device* device) {
return ERROR_NONE;
}
const static I2cControllerApi esp32_i2c_api = {
.read = read,
.write = write,
.write_read = write_read
.write_read = write_read,
.read_register = read_register,
.write_register = write_register
};
Driver esp32_i2c_driver = {

View File

@ -10,17 +10,85 @@ extern "C" {
#include <tactility/error.h>
struct GpioControllerApi {
/**
* @brief Sets the logical level of a GPIO pin.
* @param[in] device the GPIO controller device
* @param[in] pin the pin index
* @param[in] high true to set the pin high, false to set it low
* @return ERROR_NONE if successful
*/
error_t (*set_level)(struct Device* device, gpio_pin_t pin, bool high);
/**
* @brief Gets the logical level of a GPIO pin.
* @param[in] device the GPIO controller device
* @param[in] pin the pin index
* @param[out] high pointer to store the pin level
* @return ERROR_NONE if successful
*/
error_t (*get_level)(struct Device* device, gpio_pin_t pin, bool* high);
/**
* @brief Configures the options for a GPIO pin.
* @param[in] device the GPIO controller device
* @param[in] pin the pin index
* @param[in] options configuration flags (direction, pull-up/down, etc.)
* @return ERROR_NONE if successful
*/
error_t (*set_options)(struct Device* device, gpio_pin_t pin, gpio_flags_t options);
/**
* @brief Gets the configuration options for a GPIO pin.
* @param[in] device the GPIO controller device
* @param[in] pin the pin index
* @param[out] options pointer to store the configuration flags
* @return ERROR_NONE if successful
*/
error_t (*get_options)(struct Device* device, gpio_pin_t pin, gpio_flags_t* options);
};
/**
* @brief Sets the logical level of a GPIO pin.
* @param[in] device the GPIO controller device
* @param[in] pin the pin index
* @param[in] high true to set the pin high, false to set it low
* @return ERROR_NONE if successful
*/
error_t gpio_controller_set_level(struct Device* device, gpio_pin_t pin, bool high);
/**
* @brief Gets the logical level of a GPIO pin.
* @param[in] device the GPIO controller device
* @param[in] pin the pin index
* @param[out] high pointer to store the pin level
* @return ERROR_NONE if successful
*/
error_t gpio_controller_get_level(struct Device* device, gpio_pin_t pin, bool* high);
/**
* @brief Configures the options for a GPIO pin.
* @param[in] device the GPIO controller device
* @param[in] pin the pin index
* @param[in] options configuration flags (direction, pull-up/down, etc.)
* @return ERROR_NONE if successful
*/
error_t gpio_controller_set_options(struct Device* device, gpio_pin_t pin, gpio_flags_t options);
/**
* @brief Gets the configuration options for a GPIO pin.
* @param[in] device the GPIO controller device
* @param[in] pin the pin index
* @param[out] options pointer to store the configuration flags
* @return ERROR_NONE if successful
*/
error_t gpio_controller_get_options(struct Device* device, gpio_pin_t pin, gpio_flags_t* options);
/**
* @brief Configures the options for a GPIO pin using a pin configuration structure.
* @param[in] device the GPIO controller device
* @param[in] config the pin configuration structure
* @return ERROR_NONE if successful
*/
static inline error_t gpio_set_options_config(struct Device* device, const struct GpioPinConfig* config) {
return gpio_controller_set_options(device, config->pin, config->flags);
}

View File

@ -13,18 +13,154 @@ extern "C" {
#include <tactility/freertos/freertos.h>
#include <tactility/error.h>
/**
* @brief API for I2C controller drivers.
*/
struct I2cControllerApi {
/**
* @brief Reads data from an I2C device.
* @param[in] device the I2C controller device
* @param[in] address the 7-bit I2C address of the slave device
* @param[out] data the buffer to store the read data
* @param[in] dataSize the number of bytes to read
* @param[in] timeout the maximum time to wait for the operation to complete
* @retval ERROR_NONE when the read operation was successful
* @retval ERROR_TIMEOUT when the operation timed out
*/
error_t (*read)(struct Device* device, uint8_t address, uint8_t* data, size_t dataSize, TickType_t timeout);
/**
* @brief Writes data to an I2C device.
* @param[in] device the I2C controller device
* @param[in] address the 7-bit I2C address of the slave device
* @param[in] data the buffer containing the data to write
* @param[in] dataSize the number of bytes to write
* @param[in] timeout the maximum time to wait for the operation to complete
* @retval ERROR_NONE when the write operation was successful
* @retval ERROR_TIMEOUT when the operation timed out
*/
error_t (*write)(struct Device* device, uint8_t address, const uint8_t* data, uint16_t dataSize, TickType_t timeout);
/**
* @brief Writes data to then reads data from an I2C device.
* @param[in] device the I2C controller device
* @param[in] address the 7-bit I2C address of the slave device
* @param[in] writeData the buffer containing the data to write
* @param[in] writeDataSize the number of bytes to write
* @param[out] readData the buffer to store the read data
* @param[in] readDataSize the number of bytes to read
* @param[in] timeout the maximum time to wait for the operation to complete
* @retval ERROR_NONE when the operation was successful
* @retval ERROR_TIMEOUT when the operation timed out
*/
error_t (*write_read)(struct Device* device, uint8_t address, const uint8_t* writeData, size_t writeDataSize, uint8_t* readData, size_t readDataSize, TickType_t timeout);
/**
* @brief Reads data from a register of an I2C device.
* @param[in] device the I2C controller device
* @param[in] address the 7-bit I2C address of the slave device
* @param[in] reg the register address to read from
* @param[out] data the buffer to store the read data
* @param[in] dataSize the number of bytes to read
* @param[in] timeout the maximum time to wait for the operation to complete
* @retval ERROR_NONE when the read operation was successful
* @retval ERROR_TIMEOUT when the operation timed out
*/
error_t (*read_register)(struct Device* device, uint8_t address, uint8_t reg, uint8_t* data, size_t dataSize, TickType_t timeout);
/**
* @brief Writes data to a register of an I2C device.
* @param[in] device the I2C controller device
* @param[in] address the 7-bit I2C address of the slave device
* @param[in] reg the register address to write to
* @param[in] data the buffer containing the data to write
* @param[in] dataSize the number of bytes to write
* @param[in] timeout the maximum time to wait for the operation to complete
* @retval ERROR_NONE when the write operation was successful
* @retval ERROR_TIMEOUT when the operation timed out
*/
error_t (*write_register)(struct Device* device, uint8_t address, uint8_t reg, const uint8_t* data, uint16_t dataSize, TickType_t timeout);
};
/**
* @brief Reads data from an I2C device using the specified controller.
* @param[in] device the I2C controller device
* @param[in] address the 7-bit I2C address of the slave device
* @param[out] data the buffer to store the read data
* @param[in] dataSize the number of bytes to read
* @param[in] timeout the maximum time to wait for the operation to complete
* @retval ERROR_NONE when the read operation was successful
*/
error_t i2c_controller_read(struct Device* device, uint8_t address, uint8_t* data, size_t dataSize, TickType_t timeout);
/**
* @brief Writes data to an I2C device using the specified controller.
* @param[in] device the I2C controller device
* @param[in] address the 7-bit I2C address of the slave device
* @param[in] data the buffer containing the data to write
* @param[in] dataSize the number of bytes to write
* @param[in] timeout the maximum time to wait for the operation to complete
* @retval ERROR_NONE when the write operation was successful
*/
error_t i2c_controller_write(struct Device* device, uint8_t address, const uint8_t* data, uint16_t dataSize, TickType_t timeout);
/**
* @brief Writes data to then reads data from an I2C device using the specified controller.
* @param[in] device the I2C controller device
* @param[in] address the 7-bit I2C address of the slave device
* @param[in] writeData the buffer containing the data to write
* @param[in] writeDataSize the number of bytes to write
* @param[out] readData the buffer to store the read data
* @param[in] readDataSize the number of bytes to read
* @param[in] timeout the maximum time to wait for the operation to complete
* @retval ERROR_NONE when the operation was successful
*/
error_t i2c_controller_write_read(struct Device* device, uint8_t address, const uint8_t* writeData, size_t writeDataSize, uint8_t* readData, size_t readDataSize, TickType_t timeout);
/**
* @brief Reads data from a register of an I2C device using the specified controller.
* @param[in] device the I2C controller device
* @param[in] address the 7-bit I2C address of the slave device
* @param[in] reg the register address to read from
* @param[out] data the buffer to store the read data
* @param[in] dataSize the number of bytes to read
* @param[in] timeout the maximum time to wait for the operation to complete
* @retval ERROR_NONE when the read operation was successful
*/
error_t i2c_controller_read_register(struct Device* device, uint8_t address, uint8_t reg, uint8_t* data, size_t dataSize, TickType_t timeout);
/**
* @brief Writes data to a register of an I2C device using the specified controller.
* @param[in] device the I2C controller device
* @param[in] address the 7-bit I2C address of the slave device
* @param[in] reg the register address to write to
* @param[in] data the buffer containing the data to write
* @param[in] dataSize the number of bytes to write
* @param[in] timeout the maximum time to wait for the operation to complete
* @retval ERROR_NONE when the write operation was successful
*/
error_t i2c_controller_write_register(struct Device* device, uint8_t address, uint8_t reg, const uint8_t* data, uint16_t dataSize, TickType_t timeout);
/**
* @brief Writes an array of register-value pairs to an I2C device.
* @param[in] device the I2C controller device
* @param[in] address the 7-bit I2C address of the slave device
* @param[in] data an array of bytes where even indices are register addresses and odd indices are values
* @param[in] dataSize the number of bytes in the data array (must be even)
* @param[in] timeout the maximum time to wait for each operation to complete
* @retval ERROR_NONE when all write operations were successful
*/
error_t i2c_controller_write_register_array(struct Device* device, uint8_t address, const uint8_t* data, uint16_t dataSize, TickType_t timeout);
/**
* @brief Checks if an I2C device is present at the specified address.
* @param[in] device the I2C controller device
* @param[in] address the 7-bit I2C address to check
* @param[in] timeout the maximum time to wait for the check to complete
* @retval ERROR_NONE when a device responded at the address
*/
error_t i2c_controller_has_device_at_address(struct Device* device, uint8_t address, TickType_t timeout);
extern const struct DeviceType I2C_CONTROLLER_TYPE;
#ifdef __cplusplus

View File

@ -11,7 +11,7 @@
#include <tactility/log.h>
#include <atomic>
#define TAG LOG_TAG("Dispatcher")
#define TAG LOG_TAG(Dispatcher)
static constexpr EventBits_t BACKPRESSURE_WARNING_COUNT = 100U;
static constexpr EventBits_t WAIT_FLAG = 1U;

View File

@ -11,7 +11,7 @@
#include <string>
static const size_t LOCAL_STORAGE_SELF_POINTER_INDEX = 0;
static const char* TAG = LOG_TAG("Thread");
static const char* TAG = LOG_TAG(Thread);
struct Thread {
TaskHandle_t taskHandle = nullptr;

View File

@ -1,7 +1,7 @@
#include <tactility/freertos/task.h>
#include <tactility/log.h>
static const auto* TAG = LOG_TAG("Kernel");
static const auto* TAG = LOG_TAG(Kernel);
static void log_memory_info() {
#ifdef ESP_PLATFORM

View File

@ -23,6 +23,33 @@ error_t i2c_controller_write_read(Device* device, uint8_t address, const uint8_t
return I2C_DRIVER_API(driver)->write_read(device, address, writeData, writeDataSize, readData, readDataSize, timeout);
}
error_t i2c_controller_read_register(Device* device, uint8_t address, uint8_t reg, uint8_t* data, size_t dataSize, TickType_t timeout) {
const auto* driver = device_get_driver(device);
return I2C_DRIVER_API(driver)->read_register(device, address, reg, data, dataSize, timeout);
}
error_t i2c_controller_write_register(Device* device, uint8_t address, uint8_t reg, const uint8_t* data, uint16_t dataSize, TickType_t timeout) {
const auto* driver = device_get_driver(device);
return I2C_DRIVER_API(driver)->write_register(device, address, reg, data, dataSize, timeout);
}
error_t i2c_controller_write_register_array(Device* device, uint8_t address, const uint8_t* data, uint16_t dataSize, TickType_t timeout) {
const auto* driver = device_get_driver(device);
assert(dataSize % 2 == 0);
error_t error;
for (int i = 0; i < dataSize; i += 2) {
error = I2C_DRIVER_API(driver)->write_register(device, address, data[i], &data[i + 1], 1, timeout);
if (error != ERROR_NONE) break;
}
return error;
}
error_t i2c_controller_has_device_at_address(Device* device, uint8_t address, TickType_t timeout) {
const auto* driver = device_get_driver(device);
uint8_t message[2] = { 0, 0 };
return I2C_DRIVER_API(driver)->write(device, address, message, 2, timeout);
}
const struct DeviceType I2C_CONTROLLER_TYPE { 0 };
}