From d43713574189be4b34c626d2f658487269aaaf09 Mon Sep 17 00:00:00 2001 From: wispl Date: Sat, 1 Aug 2026 18:52:30 -0400 Subject: [PATCH] refactor: sensors abstraction --- CMakeLists.txt | 1 + include/defs.h | 224 +++++++++++++++++++++++++++++++++---- include/flash.h | 2 + include/flash/gd5f1gq5xe.h | 2 +- include/sensors/bmi088.h | 7 +- include/sensors/bmp581.h | 6 +- src/defs.c | 145 ++++++++++++++++++++++++ src/flash/gd5f1gq5xe.c | 180 ++++++++--------------------- src/sensors/bmi088.c | 75 ++++--------- src/sensors/bmp581.c | 75 ++++--------- test/sensors/test_bmp581.c | 1 + 11 files changed, 451 insertions(+), 267 deletions(-) create mode 100644 src/defs.c diff --git a/CMakeLists.txt b/CMakeLists.txt index aceb612..4ff1008 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -48,6 +48,7 @@ target_include_directories(littlefs PUBLIC ${littlefs_SOURCE_DIR}) message(STATUS "Dependencies resolved, now creating library...") add_library(common_drivers STATIC "${CMAKE_CURRENT_SOURCE_DIR}/src/flash.c" + "${CMAKE_CURRENT_SOURCE_DIR}/src/defs.c" "${CMAKE_CURRENT_SOURCE_DIR}/src/flash/gd5f1gq5xe.c" "${CMAKE_CURRENT_SOURCE_DIR}/src/sensors/bmp581.c" "${CMAKE_CURRENT_SOURCE_DIR}/src/sensors/bmi088.c" diff --git a/include/defs.h b/include/defs.h index 648dc8d..232d1e8 100644 --- a/include/defs.h +++ b/include/defs.h @@ -1,6 +1,8 @@ #ifndef DEFS_H #define DEFS_H +#include +#include #ifdef TEST // Mark functions you want to unit test with STATIC. We expose everything // through `struct sensor` and `struct flash` to avoid leaking implmentation. @@ -24,33 +26,211 @@ #endif #endif // end TEST -/// Common abstraction over SPI, UART, I2C -/// Use this handle struct when a sensor could be configured to use more than of -/// the protocols or if the sensor uses a protocol that might be disabled, like -/// UART or I2C. This helps isolate ifdefs to only implementation files. -enum protocol { SPI, UART, I2C }; -struct handle { - enum protocol protocol; - union { +//// | Handle Abstractions | +//// We provide a thin abstraction over the built-in STM32 handles for two +//// main reasons +//// 1. compile time errors, not all projects will enable all modules +//// 2. group required data together +//// For example, the spi handle requires CS ports and pins to work, so it is a +//// good idea to group them together. The compile time errors are due to +//// missing definitions, when HAL_I2C_MODULE_ENABLED is not defined, no I2C +//// functions are imported, which results in errors accross this code base. +//// +//// To resolve that, we gate code using ifdefs in this centralized location +//// and replace missing definitions with an empty one, which will throw an +//// error at compile and run-time when you try to use them. + +/// SPI Handle Abstraction +/// Aside the handle typedef, a port and pin representing the CS is required +#ifdef HAL_SPI_MODULE_ENABLED +struct handle_spi { + SPI_HandleTypeDef *handle; + GPIO_TypeDef *port; + uint8_t pin; +}; + +#define HANDLE_SPI(in_handle, in_port, in_pin) \ + (struct handle) { \ + .protocol = SPI, \ + .serial = { \ + .spi = { \ + .pin = (in_pin), \ + .port = (in_port), \ + .handle = (in_handle), \ + } \ + } \ + }; +#else +struct handle_spi { int placeholder; }; +#define HANDLE_SPI(handle, port, pin) assert(0 & "SPI is not enabled!"); +#endif + +/// I2C Handle Abstraction +/// Aside the handle typedef, the address of the device on the line is required #ifdef HAL_I2C_MODULE_ENABLED - struct handle_i2c { - I2C_HandleTypeDef *handle; - uint32_t address; - } i2c; +struct handle_i2c { + I2C_HandleTypeDef *handle; + uint32_t address; +}; + +#define HANDLE_I2C(in_handle, in_address) \ + (struct handle) { \ + .protocol = I2C, \ + .serial = { \ + .i2c = { \ + .address = (in_address), \ + .handle = (in_handle), \ + } \ + } \ + }; +#else +struct handle_i2c { int placeholder; }; +#define HANDLE_I2C(handle, address) assert(0 & "I2C is not enabled!"); #endif - // We always have spi present, so we don't have to gate it - struct handle_spi { - SPI_HandleTypeDef *handle; - GPIO_TypeDef *port; - uint8_t pin; - } spi; + +/// UART Handle Abstraction +/// Only the handle typedef is required #ifdef HAL_UART_MODULE_ENABLED - struct handle_uart { - UART_HandleTypeDef *handle; - } uart; +struct handle_uart { UART_HandleTypeDef *handle; }; + +#define HANDLE_QSPI(handle) \ + (struct handle) { \ + .protocol = UART, \ + .serial = { \ + .uart = { .handle = handle } \ + }; +#else +struct handle_uart { int placeholder; }; +#define HANDLE_UART(handle) assert(0 & "UART is not enabled!"); +#endif + +/// QSPI Handle Abstraction +/// Only the handle typedef is required +#ifdef HAL_QSPI_MODULE_ENABLED +struct handle_qspi { QSPI_HandleTypeDef *handle; }; + +#define HANDLE_QSPI(handle) \ + (struct handle) { \ + .protocol = QSPI, \ + .serial = { \ + .qspi = { .handle = handle } \ + }; +#else +struct handle_qspi { int placeholder; }; +#define HANDLE_QSPI(handle) assert(0 & "QSPI is not enabled!"); #endif - } def; + +//// | Unified Handle Abstractions | +//// This handle uses a tagged union to allow a sensor to switch between +//// different serial protocols. You may pass in a handle with the I2C protocol +//// or SPI protocol and it will use the correct read and write functions. +//// +//// To create a handle, you MUST use the following methods +//// struct handle spi = HANDLE_SPI(&hspi1, port, pin); +//// struct handle i2c = HANDLE_I2C(&hi2c1, address); +//// struct handle uart = HANDLE_UART(&huart1); +//// struct handle qspi = HANDLE_QSPI(&hqspi); +//// Then you may pass the handle to the initialization function of a device. + +enum protocol { SPI, UART, I2C, QSPI }; +struct handle { + enum protocol protocol; + union { + struct handle_spi spi; + struct handle_i2c i2c; + struct handle_uart uart; + struct handle_qspi qspi; + } serial; +}; + +static inline struct handle_spi* handle_as_spi(struct handle *handle) +{ + return handle->protocol == SPI ? &(handle->serial.spi) : NULL; +} + +static inline struct handle_i2c* handle_as_i2c(struct handle *handle) +{ + return handle->protocol == I2C ? &(handle->serial.i2c) : NULL; +} + +static inline struct handle_uart* handle_as_uart(struct handle *handle) +{ + return handle->protocol == UART ? &(handle->serial.uart) : NULL; +} + +static inline struct handle_qspi* handle_as_qspi(struct handle *handle) +{ + return handle->protocol == QSPI ? &(handle->serial.qspi) : NULL; +} + +//// | Serial API Abstractions | +//// All serial protocols implement an API with the same write and read +//// functions, with differing implmentations of course. The serial api contains +//// the handle and is requested by a device in its initialization function. +//// +//// See the currently provided drivers for an example. But the gist is +//// int8_t sensor_init(struct sensor_ctx *ctx, struct sensor *sensor, struct handle *handle) +//// { +//// assert(handle->protocol == SPI); +//// sensor->ctx = ctx; +//// sensor->read = sensor_read; +//// serial_api_spi(ctx->api, handle); +//// } +//// Of course you may decide to support multiple protocols if you wish, which +//// this abstraction makes really simple. You would pass `api` around and call +//// `api->write(...)` and `api->read(...)` for writing and reading respectively. + +struct op_params { + // For sensors, cmd is where you pass either the address of the register + // to access or an actual opcode to send to the device + const void *cmd; + const size_t cmd_size; + + // For reads, this is the buffer to read into + // For writes, this is the buffer to transmit + // Set buffer_size to zero to transmit or read nothing + void *buffer; + const size_t buffer_size; + + // Used only for qspi, for spi and other methods you may embed dummy + // cycles directly in the cmd buffer like so + // char *cmd = [MY_OPCODE, 0x000000, 0x000000]; + // This value is ignored for non-qspi methods + const size_t dummy_cycles; +}; +/// Each protocol implements an instance of the serial api, which is then passed +/// to a sensor to allow it to use the given protocol. +struct serial_api { + struct handle *handle; + bool (*read)(struct handle*, struct op_params*); + bool (*write)(struct handle*, struct op_params*); }; +void serial_api_spi(struct serial_api *api, struct handle *handle); +void serial_api_i2c(struct serial_api *api, struct handle *handle); +void serial_api_qspi(struct serial_api *api, struct handle *handle); + +//// | Helper methods | +//// These are used for when both `cmd` and `buffer` are static arrays. +//// In that case you may use STATIC_CMD as a shorthand. STATIC_EXEC is +//// is a shorthand for `buffer_size = 0`, useful for when you want to +//// to send a command which doesn't return anything back. + +#define STATIC_CMD(cmd_arr, buf_arr) \ + (struct op_params){ \ + .cmd = (cmd_arr), \ + .cmd_size = sizeof(cmd_arr), \ + .buffer = (buf_arr), \ + .buffer_size = sizeof(buf_arr), \ + .dummy_cycles = 0 \ + } +#define STATIC_EXEC(cmd_arr) \ + (struct op_params){ \ + .cmd = (cmd_arr), \ + .cmd_size = sizeof(cmd_arr), \ + .buffer = NULL, \ + .buffer_size = 0, \ + .dummy_cycles = 0 \ + } #endif // end DEFS_H diff --git a/include/flash.h b/include/flash.h index f7cec3c..d9a4cfe 100644 --- a/include/flash.h +++ b/include/flash.h @@ -2,11 +2,13 @@ #define FLASH_H #include "lfs.h" +#include "defs.h" #include struct flash { struct lfs_config config; + struct serial_api api; lfs_t lfs; }; diff --git a/include/flash/gd5f1gq5xe.h b/include/flash/gd5f1gq5xe.h index 0e7feb9..878a439 100644 --- a/include/flash/gd5f1gq5xe.h +++ b/include/flash/gd5f1gq5xe.h @@ -7,6 +7,6 @@ #include #include -bool gd5f1gq5xe_init(struct flash *flash, struct handle_spi *spi); +bool gd5f1gq5xe_init(struct flash *flash, struct handle *handle); #endif diff --git a/include/sensors/bmi088.h b/include/sensors/bmi088.h index 51f45db..d19fd22 100644 --- a/include/sensors/bmi088.h +++ b/include/sensors/bmi088.h @@ -16,9 +16,10 @@ struct bmi088_ctx { struct bmi08_dev dev; - struct handle_spi accel_spi; - struct handle_spi gyro_spi; + struct serial_api accel; + struct serial_api gyro; }; -int8_t bmi088_init(struct bmi088_ctx *ctx, struct sensor *sensor); +int8_t bmi088_init(struct bmi088_ctx *ctx, struct sensor *sensor, + struct handle* accel, struct handle* gyro); #endif /* INC_BMI088_H_ */ diff --git a/include/sensors/bmp581.h b/include/sensors/bmp581.h index 1acd4eb..6756e66 100644 --- a/include/sensors/bmp581.h +++ b/include/sensors/bmp581.h @@ -11,10 +11,8 @@ struct bmp581_ctx { struct bmp5_dev dev; struct bmp5_osr_odr_press_config odr_config; struct bmp5_int_source_select int_config; - struct handle handle; + struct serial_api api; }; - -int8_t bmp581_init(struct bmp581_ctx *ctx, struct sensor *sensor); -int8_t bmp581_get_power_mode(struct bmp581_ctx *ctx, enum bmp5_powermode *powermode); +int8_t bmp581_init(struct bmp581_ctx *ctx, struct sensor *sensor, struct handle *handle); #endif diff --git a/src/defs.c b/src/defs.c new file mode 100644 index 0000000..5451f8d --- /dev/null +++ b/src/defs.c @@ -0,0 +1,145 @@ +#include "defs.h" +#include + +#ifdef HAL_SPI_MODULE_ENABLED +static bool spi_read(struct handle* handle, struct op_params* params) +{ + HAL_StatusTypeDef res; + struct handle_spi *spi = handle_as_spi(handle); + + HAL_GPIO_WritePin(spi->port, spi->pin, GPIO_PIN_RESET); + HAL_SPI_Transmit(spi->handle, params->cmd, params->cmd_size, HAL_MAX_DELAY); + res = HAL_SPI_Receive(spi->handle, params->buffer, params->buffer_size, HAL_MAX_DELAY); + HAL_GPIO_WritePin(spi->port, spi->pin, GPIO_PIN_SET); + return res; +} + +static bool spi_write(struct handle* handle, struct op_params* params) +{ + HAL_StatusTypeDef res; + struct handle_spi *spi = handle_as_spi(handle); + + HAL_GPIO_WritePin(spi->port, spi->pin, GPIO_PIN_RESET); + HAL_SPI_Transmit(spi->handle, params->cmd, params->cmd_size, HAL_MAX_DELAY); + if (params->buffer_size > 0) { + res = HAL_SPI_Transmit(spi->handle, params->buffer, params->buffer_size, HAL_MAX_DELAY); + } + HAL_GPIO_WritePin(spi->port, spi->pin, GPIO_PIN_SET); + return res; +} + +void serial_api_spi(struct serial_api *api, struct handle* handle) +{ + assert(handle->protocol == SPI); + api->handle = handle; + api->write = spi_write; + api->read = spi_read; +} +#else +void serial_api_spi(struct serial_api *api, struct handle* handle) +{ + assert(0 && "SPI module is disabled, SPI is inaccessible!"); +} +#endif + +#ifdef HAL_I2C_MODULE_ENABLED +static bool i2c_read(struct handle* handle, struct op_params* params) +{ + HAL_StatusTypeDef res; + struct handle_i2c *i2c = handle_as_i2c(handle); + HAL_I2C_Mem_Read(i2c->handle, i2c->address << 1, + params->cmd[0], I2C_MEMADD_SIZE_8BIT, + params->buffer, params->buffer_size, HAL_MAX_DELAY); + return res; +} + +static bool i2c_write(struct handle* handle, struct op_params* params) +{ + HAL_StatusTypeDef res; + struct handle_i2c *i2c = handle_as_i2c(handle); + HAL_I2C_Mem_Write(i2c->handle, i2c->address << 1, + params->cmd[0], I2C_MEMADD_SIZE_8BIT, + params->buffer, params->buffer_size, HAL_MAX_DELAY); + return res; +} + +void serial_api_i2c(struct serial_api *api, struct handle* handle) +{ + assert(handle->protocol == I2C); + api->handle = handle; + api->write = i2c_write; + api->read = i2c_read; +} +#else +void serial_api_i2c(struct serial_api *api, struct handle* handle) +{ + assert(0 && "I2C module is disabled, I2C api is inaccessible!"); +} +#endif + +#ifdef HAL_QSPI_MODULE_ENABLED +static bool qspi_read(struct handle* handle, struct op_params* params) +{ + QSPI_CommandTypeDef cmd; + HAL_StatusTypeDef res; + struct handle_qspi *qspi = handle_as_qspi(handle); + + // Common configuration + cmd.Instruction = params->cmd[0]; + cmd.DummyCycles = params->dummy_cycles; + cmd.InstructionMode = QSPI_INSTRUCTION_1_LINE; + cmd.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; + cmd.AddressMode = QSPI_ADDRESS_1_LINE; + cmd.AddressSize = QSPI_ADDRESS_24_BITS; + cmd.Address = 0; + cmd.DataMode = QSPI_DATA_4_LINES; + cmd.NbData = params->buffer_size; + HAL_QSPI_Command(&qspi->handle, &cmd, HAL_MAX_DELAY); + res = HAL_QSPI_Receive(&qspi->handle, params->buffer, HAL_MAX_DELAY); + return res; +} + +static bool qspi_write(struct handle* handle, struct op_params* params) +{ + QSPI_CommandTypeDef cmd; + HAL_StatusTypeDef res; + struct handle_qspi *qspi = handle_as_qspi(handle); + + // Common configuration + cmd.Instruction = params->cmd[0]; + cmd.DummyCycles = params->dummy_cycles; + cmd.InstructionMode = QSPI_INSTRUCTION_1_LINE; + cmd.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; + if (params->buffer_size > 0) { + // Write mode + cmd.Address = 0; + cmd.AddressMode = QSPI_ADDRESS_1_LINE; + cmd.AddressSize = QSPI_ADDRESS_24_BITS; + cmd.DataMode = QSPI_DATA_4_LINES; + cmd.NbData = params->buffer_size; + } else { + // Command mode + sCommand.AddressMode = QSPI_ADDRESS_NONE; + cmd.DataMode = QSPI_DATA_NONE; + } + + HAL_QSPI_Command(&qspi->handle, &cmd, HAL_MAX_DELAY); + if (params->buffer_size > 0) { + res = HAL_QSPI_Transmit(&qspi->handle, params->buffer, HAL_MAX_DELAY); + } + return res; +} + +void serial_api_qspi(struct serial_api *api, struct handle* handle) +{ + assert(handle->protocol == QSPI); + api->handle = handle; + api->write = qspi_write; + api->read = qspi_read; +} +#else +void serial_api_qspi(struct serial_api *api, struct handle* handle) +{ + assert(0 && "QSPI module is disabled, QSPI api is inaccessible!"); +} +#endif diff --git a/src/flash/gd5f1gq5xe.c b/src/flash/gd5f1gq5xe.c index 288586f..83c7331 100644 --- a/src/flash/gd5f1gq5xe.c +++ b/src/flash/gd5f1gq5xe.c @@ -35,53 +35,23 @@ // Note that this flash has a page size of 2048, so we actually only need 11 bytes for // the column address. -static void chip_select(struct handle_spi *spi) +static int write_enable(struct serial_api *api) { - HAL_GPIO_WritePin(spi->port, spi->pin, GPIO_PIN_RESET); -} - -static void chip_deselect(struct handle_spi *spi) -{ - HAL_GPIO_WritePin(spi->port, spi->pin, GPIO_PIN_SET); -} - -static bool spi_transmit(struct handle_spi *spi, void *buffer, const size_t size) -{ - return HAL_SPI_Transmit(spi->handle, (uint8_t*) buffer, size, HAL_MAX_DELAY); -} - -static bool spi_receive(struct handle_spi *spi, void *buffer, const size_t size) -{ - return HAL_SPI_Receive(spi->handle, (uint8_t*) buffer, size, HAL_MAX_DELAY); -} - -static int write_enable(struct handle_spi *spi) -{ - uint8_t tx = GD5F_WRITE_ENABLE; - chip_select(spi); - if (spi_transmit(spi, &tx, sizeof(tx)) != 0) { - chip_deselect(spi); - return 1; + uint8_t cmd[] = { GD5F_WRITE_ENABLE }; + bool res = api->write(api->handle, &STATIC_EXEC(cmd)); + if (res != 0 && api->handle->protocol == SPI) { + struct handle_spi *spi = handle_as_spi(api->handle); + HAL_GPIO_WritePin(spi->port, spi->pin, GPIO_PIN_SET); } - chip_deselect(spi); - return 0; + return res; } -static bool check_id(struct handle_spi *spi) +static bool check_id(struct serial_api *api) { - chip_select(spi); uint8_t cmd[] = { GD5F_READ_ID, 0x00 }; uint8_t data[] = { 0, 0 }; - if (spi_transmit(spi, cmd, sizeof(cmd)) != 0) { - chip_deselect(spi); - } - spi_receive(spi, data, sizeof(data)); - chip_deselect(spi); - // Sometimes the check is not consistent - // Investigate for now + api->read(api->handle, &STATIC_CMD(cmd, data)); return data[0] == 0xC8 && data[1] == 0x31; - /* assert(data[0] == 0xC8); */ - /* assert(data[1] == 0x31); */ } /// Read a page from the flash into the `buffer`. @@ -92,7 +62,7 @@ static bool check_id(struct handle_spi *spi) /// read up to the page-size boundary /// /// Returns the number of bytes read. -static uint32_t read_page(struct handle_spi *spi, const uint32_t block, +static uint32_t read_page(struct serial_api *api, const uint32_t block, const uint32_t offset, void *buffer, uint32_t size) { assert(block < GD5F_BLOCK_COUNT); @@ -105,37 +75,26 @@ static uint32_t read_page(struct handle_spi *spi, const uint32_t block, // We can then use the column addreess to read offsets into the page uint16_t col = offset % GD5F_PAGE_SIZE; - uint8_t tx1[] = { + uint8_t cmd1[] = { GD5F_READ_TO_CACHE, (addr & 0xFF0000) >> 16, (addr & 0x00FF00) >> 8, (addr & 0x0000FF) }; - chip_select(spi); - if (spi_transmit(spi, tx1, sizeof(tx1)) != 0) { - chip_deselect(spi); - return 0; - } - chip_deselect(spi); + api->write(api->handle, &STATIC_EXEC(cmd1)); HAL_Delay(2); // Required delay for cache read - uint8_t tx2[] = { + uint8_t cmd2[] = { GD5F_READ_FROM_CACHE, (col & 0x0F00) >> 8, // first 4 bytes are not needed, (col & 0x00FF), // remember that we only need 12 bytes 0x00 // we need a dummy byte (from datasheet) }; - chip_select(spi); - if (spi_transmit(spi, tx2, sizeof(tx2)) != 0) { - chip_deselect(spi); - return 0; - }; uint32_t read_size = size <= GD5F_PAGE_SIZE - col ? size : GD5F_PAGE_SIZE - col; - if (spi_receive(spi, buffer, read_size) != 0) { - chip_deselect(spi); - return 0; - } - chip_deselect(spi); + api->read(api->handle, &(struct op_params) { + .cmd = cmd2, .cmd_size = sizeof(cmd2), + .buffer = buffer, .buffer_size = read_size + }); return read_size; } @@ -147,7 +106,7 @@ static uint32_t read_page(struct handle_spi *spi, const uint32_t block, /// write up to the page-size boundary /// /// Returns the number of bytes written. -static uint32_t write_page(struct handle_spi *spi, const uint32_t block, +static uint32_t write_page(struct serial_api *api, const uint32_t block, const uint32_t offset, const void *buffer, const uint32_t size) { assert(block < GD5F_BLOCK_COUNT); @@ -156,50 +115,34 @@ static uint32_t write_page(struct handle_spi *spi, const uint32_t block, uint32_t addr = block * GD5F_PAGES_PER_BLOCK + (offset / GD5F_PAGE_SIZE); uint16_t col = offset % GD5F_PAGE_SIZE; - uint8_t tx1[] = { + uint8_t cmd1[] = { GD5F_PROGRAM_LOAD, (col & 0x0F00) >> 8, // similar to before, the first 4 bytes (col & 0x00FF) // are not needed, hence the 0x0F00 }; - chip_select(spi); - if (spi_transmit(spi, tx1, sizeof(tx1)) != 0) { - chip_deselect(spi); - return 0; - } uint32_t write_size = size <= GD5F_PAGE_SIZE - col ? size : GD5F_PAGE_SIZE - col; - if (spi_transmit(spi, buffer, write_size) != 0) { - chip_deselect(spi); - return 0; - }; - chip_deselect(spi); - - if (write_enable(spi) != 0) { - chip_deselect(spi); - return 0; - } + api->write(api->handle, &(struct op_params) { + .cmd = cmd1, .cmd_size = sizeof(cmd1), + .buffer = buffer, .buffer_size = write_size + }); - uint8_t tx2[] = { + if (write_enable(api) != 0) return 0; + uint8_t cmd2[] = { GD5F_PROGRAM_EXECUTE, (addr & 0xFF0000) >> 16, (addr & 0x00FF00) >> 8, (addr & 0x0000FF) }; - chip_select(spi); - if (spi_transmit(spi, tx2, sizeof(tx2)) != 0) { - chip_deselect(spi); - return 0; - } - chip_deselect(spi); - + api->write(api->handle, &STATIC_EXEC(cmd2)); HAL_Delay(1); return write_size; } -static bool read(struct handle_spi *spi, uint32_t block, uint32_t offset, void *buffer, uint32_t size) +static bool read(struct serial_api *api, uint32_t block, uint32_t offset, void *buffer, uint32_t size) { uint8_t *buf = (uint8_t *) buffer; while (size > 0) { - uint32_t s = read_page(spi, block, offset, buf, size); + uint32_t s = read_page(api, block, offset, buf, size); if (s == 0) return false; size -= s; offset += s; @@ -208,11 +151,11 @@ static bool read(struct handle_spi *spi, uint32_t block, uint32_t offset, void * return true; } -static bool write(struct handle_spi *spi, uint32_t block, uint32_t offset, void *buffer, uint32_t size) +static bool write(struct serial_api *api, uint32_t block, uint32_t offset, void *buffer, uint32_t size) { uint8_t *buf = (uint8_t *) buffer; while (size > 0) { - uint32_t s = write_page(spi, block, offset, buf, size); + uint32_t s = write_page(api, block, offset, buf, size); if (s == 0) return false; size -= s; offset += s; @@ -221,55 +164,33 @@ static bool write(struct handle_spi *spi, uint32_t block, uint32_t offset, void return true; } -static bool erase(struct handle_spi *spi, uint32_t block) +static bool erase(struct serial_api *api, uint32_t block) { + if (write_enable(api) != 0) return false; // Erase acts on blocks and not pages, so we should only have the block // section of the address set and not the page section. uint32_t addr = block * GD5F_PAGES_PER_BLOCK; - - if (write_enable(spi) != 0) { - chip_deselect(spi); - return false; - } - - uint8_t tx[] = { + uint8_t cmd[] = { GD5F_ERASE, (addr & 0xFF0000) >> 16, (addr & 0x00FF00) >> 8, (addr & 0x0000FF) }; - chip_select(spi); - if (spi_transmit(spi, tx, sizeof(tx)) != 0) { - chip_deselect(spi); - return false; - } - chip_deselect(spi); + api->write(api->handle, &STATIC_EXEC(cmd)); HAL_Delay(12); return true; } -static bool unlock(struct handle_spi *spi) +static bool unlock(struct serial_api *api) { // Needed for some reason, I don't know why HAL_Delay(5000); - if (!check_id(spi)) return false; - if (write_enable(spi) != 0) { - chip_deselect(spi); - return false; - } + if (!check_id(api)) return false; + if (write_enable(api) != 0) return false; - uint8_t tx[] = { - GD5F_SET_FEATURE, - 0xA0, - 0x00, - }; - chip_select(spi); - if (spi_transmit(spi, tx, sizeof(tx)) != 0) { - chip_deselect(spi); - return false; - } - chip_deselect(spi); + uint8_t cmd[] = { GD5F_SET_FEATURE, 0xA0, 0x00 }; + api->write(api->handle, &STATIC_EXEC(cmd)); HAL_Delay(5000); return true; @@ -278,23 +199,23 @@ static bool unlock(struct handle_spi *spi) static int lfs_read(const struct lfs_config *c, lfs_block_t block, lfs_off_t offset, void *data, lfs_size_t size) { - struct handle_spi *spi = (struct handle_spi*) c->context; - if (!read(spi, block, offset, data, size)) return LFS_ERR_IO; + struct serial_api *api = (struct serial_api*) c->context; + if (!read(api, block, offset, data, size)) return LFS_ERR_IO; return LFS_ERR_OK; } static int lfs_prog(const struct lfs_config *c, lfs_block_t block, lfs_off_t offset, const void *data, lfs_size_t size) { - struct handle_spi *spi = (struct handle_spi*) c->context; - if (!write(spi, block, offset, data, size)) return LFS_ERR_IO; + struct serial_api *api = (struct serial_api*) c->context; + if (!write(api, block, offset, data, size)) return LFS_ERR_IO; return LFS_ERR_OK; } static int lfs_erase(const struct lfs_config *c, lfs_block_t block) { - struct handle_spi *spi = (struct handle_spi*) c->context; - if (!erase(spi, block)) return LFS_ERR_IO; + struct serial_api *api = (struct serial_api*) c->context; + if (!erase(api, block)) return LFS_ERR_IO; return LFS_ERR_OK; } @@ -303,11 +224,11 @@ static int lfs_sync(const struct lfs_config *c) return LFS_ERR_OK; } -bool gd5f1gq5xe_init(struct flash *flash, struct handle_spi *spi) +bool gd5f1gq5xe_init(struct flash *flash, struct handle *handle) { - assert(spi->handle != NULL); + serial_api_spi(&flash->api, handle); flash->config = (struct lfs_config ) { - .context = spi, + .context = &flash->api, .read = lfs_read, .prog = lfs_prog, .erase = lfs_erase, @@ -321,9 +242,6 @@ bool gd5f1gq5xe_init(struct flash *flash, struct handle_spi *spi) .lookahead_size = 128, .block_cycles = 512, }; - if (!unlock(spi)) { - return false; - } - + if (!unlock(&flash->api)) return false; return true; } diff --git a/src/sensors/bmi088.c b/src/sensors/bmi088.c index dc59e8c..3e50062 100644 --- a/src/sensors/bmi088.c +++ b/src/sensors/bmi088.c @@ -21,56 +21,18 @@ static BMI08_INTF_RET_TYPE bmi088_read_spi(uint8_t reg_addr, uint8_t *reg_data, uint32_t len, void *intf_ptr) // GCOVR_EXCL_FUNCTION { - struct handle_spi* spi = (struct handle_spi*) intf_ptr; - - HAL_GPIO_WritePin(spi->port, spi->pin, GPIO_PIN_RESET); - - HAL_StatusTypeDef ret = HAL_OK; - - ret = HAL_SPI_Transmit(spi->handle, ®_addr, 1, HAL_MAX_DELAY); - - if (ret != HAL_OK) { - HAL_GPIO_WritePin(spi->port, spi->pin, GPIO_PIN_SET); - return ret; - } - - ret = HAL_SPI_Receive(spi->handle, reg_data, len, HAL_MAX_DELAY); - - if (ret != HAL_OK) { - HAL_GPIO_WritePin(spi->port, spi->pin, GPIO_PIN_SET); - return ret; - } - - HAL_GPIO_WritePin(spi->port, spi->pin, GPIO_PIN_SET); - - return 0; + struct serial_api *api = (struct serial_api*) intf_ptr; + struct op_params params = { .cmd = ®_addr, .cmd_size = 1, + .buffer = reg_data, .buffer_size = len }; + return api->read(api->handle, ¶ms); } static BMI08_INTF_RET_TYPE bmi088_write_spi(uint8_t reg_addr, const uint8_t *reg_data, uint32_t len, void *intf_ptr) // GCOVR_EXCL_FUNCTION { - struct handle_spi* spi = (struct handle_spi*) intf_ptr; - - HAL_GPIO_WritePin(spi->port, spi->pin, GPIO_PIN_RESET); - - HAL_StatusTypeDef ret = HAL_OK; - - ret = HAL_SPI_Transmit(spi->handle, ®_addr, 1, HAL_MAX_DELAY); - - if (ret != HAL_OK) { - HAL_GPIO_WritePin(spi->port, spi->pin, GPIO_PIN_SET); - return ret; - } - - ret = HAL_SPI_Transmit(spi->handle, reg_data, len, HAL_MAX_DELAY); - - if (ret != HAL_OK) { - HAL_GPIO_WritePin(spi->port, spi->pin, GPIO_PIN_SET); - return ret; - } - - HAL_GPIO_WritePin(spi->port, spi->pin, GPIO_PIN_SET); - - return 0; + struct serial_api *api = (struct serial_api*) intf_ptr; + struct op_params params = { .cmd = ®_addr, .cmd_size = 1, + .buffer = reg_data, .buffer_size = len }; + return api->write(api->handle, ¶ms); } static void bmi088_delay_us(uint32_t period, void *intf_ptr) // GCOVR_EXCL_FUNCTION @@ -120,19 +82,22 @@ STATIC bool bmi088_read(void *context, struct packet *packet) return true; } -int8_t bmi088_init(struct bmi088_ctx *ctx, struct sensor *sensor) // GCOVR_EXCL_FUNCTION +int8_t bmi088_init(struct bmi088_ctx *ctx, struct sensor *sensor, + struct handle* accel, struct handle* gyro) // GCOVR_EXCL_FUNCTION { - assert(ctx->accel_spi.handle != NULL); - assert(ctx->gyro_spi.handle != NULL); - - HAL_GPIO_WritePin(ctx->accel_spi.port, ctx->accel_spi.pin, GPIO_PIN_SET); - HAL_GPIO_WritePin(ctx->gyro_spi.port, ctx->gyro_spi.pin, GPIO_PIN_SET); - + assert(accel->protocol == SPI && gyro->protocol == SPI); + struct handle_spi *spi_accel = handle_as_spi(accel); + struct handle_spi *spi_gyro = handle_as_spi(gyro); struct bmi08_accel_int_channel_cfg accel_new_data_int_cfg; struct bmi08_gyro_int_channel_cfg gyro_new_data_int_cfg; - ctx->dev.intf_ptr_accel = &ctx->accel_spi; - ctx->dev.intf_ptr_gyro = &ctx->gyro_spi; + HAL_GPIO_WritePin(spi_accel->port, spi_accel->pin, GPIO_PIN_SET); + HAL_GPIO_WritePin(spi_gyro->port, spi_gyro->pin, GPIO_PIN_SET); + serial_api_spi(&ctx->accel, accel); + serial_api_spi(&ctx->gyro, gyro); + + ctx->dev.intf_ptr_accel = &ctx->accel; + ctx->dev.intf_ptr_gyro = &ctx->gyro; ctx->dev.intf = BMI08_SPI_INTF; ctx->dev.variant = BMI088_VARIANT; ctx->dev.read_write_len = 8; diff --git a/src/sensors/bmp581.c b/src/sensors/bmp581.c index 4ae2890..d2a9723 100644 --- a/src/sensors/bmp581.c +++ b/src/sensors/bmp581.c @@ -64,42 +64,25 @@ static inline float bmp581_estimate_altitude_msl(struct bmp5_sensor_data *data) } } -#ifdef HAL_I2C_MODULE_ENABLED -static BMP5_INTF_RET_TYPE read_i2c(uint8_t reg_addr, uint8_t *reg_data, uint32_t length, void *intf_ptr) // GCOVR_EXCL_FUNCTION +static inline int8_t bmp581_get_power_mode(struct bmp581_ctx *ctx, enum bmp5_powermode *powermode) // GCOVR_EXCL_FUNCTION { - struct handle_i2c *i2c = (struct handle_i2c*) intf_ptr; - HAL_StatusTypeDef res = HAL_I2C_Mem_Read(i2c->handle, i2c->address << 1, reg_addr, I2C_MEMADD_SIZE_8BIT, reg_data, length, HAL_MAX_DELAY); - return res; + return bmp5_get_power_mode(powermode, &(ctx->dev)); } -static BMP5_INTF_RET_TYPE write_i2c(uint8_t reg_addr, const uint8_t *reg_data, uint32_t length, void *intf_ptr) // GCOVR_EXCL_FUNCTION +static BMP5_INTF_RET_TYPE read(uint8_t reg_addr, uint8_t *reg_data, uint32_t length, void *intf_ptr) // GCOVR_EXCL_FUNCTION { - struct handle_i2c *i2c = (struct handle_i2c*) intf_ptr; - HAL_StatusTypeDef res = HAL_I2C_Mem_Write(i2c->handle, i2c->address << 1, reg_addr, I2C_MEMADD_SIZE_8BIT, reg_data, length, HAL_MAX_DELAY); - return res; + struct serial_api *api = (struct serial_api*) intf_ptr; + struct op_params params = { .cmd = ®_addr, .cmd_size = 1, + .buffer = reg_data, .buffer_size = length }; + return api->read(api->handle, ¶ms); } -#endif -static BMP5_INTF_RET_TYPE read_spi(uint8_t reg_addr, uint8_t *reg_data, uint32_t length, void *intf_ptr) // GCOVR_EXCL_FUNCTION +static BMP5_INTF_RET_TYPE write(uint8_t reg_addr, const uint8_t *reg_data, uint32_t length, void *intf_ptr) // GCOVR_EXCL_FUNCTION { - struct handle_spi *spi = (struct handle_spi*) intf_ptr; - HAL_GPIO_WritePin(spi->port, spi->pin, GPIO_PIN_RESET); - HAL_SPI_Transmit(spi->handle, ®_addr, 1, HAL_MAX_DELAY); - - HAL_StatusTypeDef res = HAL_SPI_Receive(spi->handle, reg_data, length, HAL_MAX_DELAY); - HAL_GPIO_WritePin(spi->port, spi->pin, GPIO_PIN_SET); - return res; -} - -static BMP5_INTF_RET_TYPE write_spi(uint8_t reg_addr, const uint8_t *reg_data, uint32_t length, void *intf_ptr) // GCOVR_EXCL_FUNCTION -{ - struct handle_spi *spi = (struct handle_spi*) intf_ptr; - HAL_GPIO_WritePin(spi->port, spi->pin, GPIO_PIN_RESET); - HAL_SPI_Transmit(spi->handle, ®_addr, 1, HAL_MAX_DELAY); - - HAL_StatusTypeDef res = HAL_SPI_Transmit(spi->handle, reg_data, length, HAL_MAX_DELAY); - HAL_GPIO_WritePin(spi->port, spi->pin, GPIO_PIN_SET); - return res; + struct serial_api *api = (struct serial_api*) intf_ptr; + struct op_params params = { .cmd = ®_addr, .cmd_size = 1, + .buffer = reg_data, .buffer_size = length }; + return api->write(api->handle, ¶ms); } static void delay(uint32_t period, void *intf_ptr) // GCOVR_EXCL_FUNCTION @@ -120,31 +103,29 @@ STATIC bool bmp581_read(void *context, struct packet *packet) return true; } -int8_t bmp581_init(struct bmp581_ctx *ctx, struct sensor *sensor) // GCOVR_EXCL_FUNCTION +int8_t bmp581_init(struct bmp581_ctx *ctx, struct sensor *sensor, struct handle *handle) // GCOVR_EXCL_FUNCTION { int8_t result = BMP5_OK; - switch (ctx->handle.protocol) { + sensor->ctx = ctx; + sensor->read = bmp581_read; + + switch (handle->protocol) { case SPI: - ctx->dev.intf_ptr = &ctx->handle.def.spi; ctx->dev.intf = BMP5_SPI_INTF; - ctx->dev.read = read_spi; - ctx->dev.write = write_spi; + serial_api_spi(&ctx->api, handle); break; case I2C: -#ifdef HAL_I2C_MODULE_ENABLED - ctx->dev.intf_ptr = &ctx->handle.def.i2c; ctx->dev.intf = BMP5_I2C_INTF; - ctx->dev.read = read_i2c; - ctx->dev.write = write_i2c; + serial_api_i2c(&ctx->api, handle); break; -#else - assert("I2C module is not enabled! Please choose SPI for BMP581"); - break; -#endif - default: assert("Invalid interface for bmp581, must choose either SPI or I2C"); + default: + assert("Invalid interface for bmp581, must choose either SPI or I2C"); }; + ctx->dev.intf_ptr = &ctx->api; + ctx->dev.read = read; + ctx->dev.write = write; ctx->dev.delay_us = delay; ctx->odr_config.odr = BMP5_ODR_240_HZ; ctx->odr_config.press_en = BMP5_ENABLE; @@ -153,9 +134,6 @@ int8_t bmp581_init(struct bmp581_ctx *ctx, struct sensor *sensor) // GCOVR_EXCL_ ctx->int_config.fifo_thres_en = BMP5_DISABLE; ctx->int_config.oor_press_en = BMP5_DISABLE; - sensor->ctx = ctx; - sensor->read = bmp581_read; - bmp5_soft_reset(&(ctx->dev)); // Initialize the device @@ -183,8 +161,3 @@ int8_t bmp581_init(struct bmp581_ctx *ctx, struct sensor *sensor) // GCOVR_EXCL_ result = bmp5_set_power_mode(BMP5_POWERMODE_NORMAL, &(ctx->dev)); return result; } - -int8_t bmp581_get_power_mode(struct bmp581_ctx *ctx, enum bmp5_powermode *powermode) // GCOVR_EXCL_FUNCTION -{ - return bmp5_get_power_mode(powermode, &(ctx->dev)); -} diff --git a/test/sensors/test_bmp581.c b/test/sensors/test_bmp581.c index 2012156..0f674a1 100644 --- a/test/sensors/test_bmp581.c +++ b/test/sensors/test_bmp581.c @@ -2,6 +2,7 @@ #include "cmock.h" #include "mock_bmp5.h" +#include "mock_defs.h" #include "bmp581.h" #include "sensor.h"