diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 429104e..50a2b11 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -42,18 +42,26 @@ jobs: working-directory: tests run: CFLAGS="${{ matrix.extra_cflags }}" make BOARD=${{ matrix.board }} - peripheral-drivers: + peripheral-tests: runs-on: ubuntu-latest strategy: matrix: - source: - - src/block/sdhc_spi.c - - src/flash/spi_nor.c + include: + - board: stm32wb55xx_nucleo + peripherals: bmi270 + tests: bmi270 + - board: stm32wb55xx_nucleo + peripherals: spi_nor_w25q64 + tests: flash + - board: stm32wb55xx_nucleo + peripherals: sdhc_spi_sdcard32gb + tests: block steps: - uses: actions/checkout@v4 - name: Install ARM toolchain run: sudo apt-get update && sudo apt-get install -y gcc-arm-none-eabi - - name: Compile ${{ matrix.source }} - run: arm-none-eabi-gcc -c -I. -Wall -Werror ${{ matrix.source }} -o /dev/null + - name: Build tests + working-directory: tests + run: make BOARD=${{ matrix.board }} PERIPHERALS="${{ matrix.peripherals }}" TESTS="${{ matrix.tests }}" diff --git a/boards/peripheral/Makefile.inc b/boards/peripheral/Makefile.inc index d6889b4..e92c3c3 100644 --- a/boards/peripheral/Makefile.inc +++ b/boards/peripheral/Makefile.inc @@ -2,14 +2,21 @@ _PERIPHERAL_DIR := $(patsubst %/,%,$(dir $(lastword $(MAKEFILE_LIST)))) BOARD_SOURCE += $(_PERIPHERAL_DIR)/peripheral.c -ifdef PERIPHERAL_SDHC_SPI_SDCARD32GB +ifneq ($(filter sdhc_spi_sdcard32gb,$(PERIPHERALS)),) CFLAGS += -DPERIPHERAL_SDHC_SPI_SDCARD32GB BOARD_SOURCE += $(_PERIPHERAL_DIR)/block/sdhc_spi_sdcard32gb.c BOARD_SOURCE += $(WHAL_DIR)/src/block/sdhc_spi.c endif -ifdef PERIPHERAL_SPI_NOR_W25Q64 +ifneq ($(filter spi_nor_w25q64,$(PERIPHERALS)),) CFLAGS += -DPERIPHERAL_SPI_NOR_W25Q64 BOARD_SOURCE += $(_PERIPHERAL_DIR)/flash/spi_nor_w25q64.c BOARD_SOURCE += $(WHAL_DIR)/src/flash/spi_nor.c endif + +ifneq ($(filter bmi270,$(PERIPHERALS)),) +CFLAGS += -DPERIPHERAL_BMI270 +BOARD_SOURCE += $(_PERIPHERAL_DIR)/sensor/imu/bmi270.c +BOARD_SOURCE += $(WHAL_DIR)/src/sensor/imu/bmi270.c +BOARD_SOURCE += $(WHAL_DIR)/src/sensor/imu/bmi270_config_data.c +endif diff --git a/boards/peripheral/peripheral.c b/boards/peripheral/peripheral.c index 2ef6d5f..979951d 100644 --- a/boards/peripheral/peripheral.c +++ b/boards/peripheral/peripheral.c @@ -8,6 +8,10 @@ #include "flash/spi_nor_w25q64.h" #endif +#ifdef PERIPHERAL_BMI270 +#include "sensor/imu/bmi270.h" +#endif + whal_PeripheralBlock_Cfg g_peripheralBlock[] = { #ifdef PERIPHERAL_SDHC_SPI_SDCARD32GB { @@ -33,6 +37,16 @@ whal_PeripheralFlash_Cfg g_peripheralFlash[] = { {0}, /* sentinel */ }; +whal_PeripheralSensor_Cfg g_peripheralSensor[] = { +#ifdef PERIPHERAL_BMI270 + { + .name = "bmi270", + .dev = &g_whalBmi270, + }, +#endif + {0}, /* sentinel */ +}; + whal_Error Peripheral_Init(void) { whal_Error err; @@ -49,6 +63,14 @@ whal_Error Peripheral_Init(void) return err; } +#if PERIPHERAL_SENSOR_COUNT > 0 + for (size_t i = 0; g_peripheralSensor[i].dev; i++) { + err = whal_Sensor_Init(g_peripheralSensor[i].dev); + if (err) + return err; + } +#endif + return WHAL_SUCCESS; } @@ -56,6 +78,14 @@ whal_Error Peripheral_Deinit(void) { whal_Error err; +#if PERIPHERAL_SENSOR_COUNT > 0 + for (size_t i = 0; g_peripheralSensor[i].dev; i++) { + err = whal_Sensor_Deinit(g_peripheralSensor[i].dev); + if (err) + return err; + } +#endif + for (size_t i = 0; g_peripheralFlash[i].dev; i++) { err = whal_Flash_Deinit(g_peripheralFlash[i].dev); if (err) diff --git a/boards/peripheral/peripheral.h b/boards/peripheral/peripheral.h index 48b7d1b..308614e 100644 --- a/boards/peripheral/peripheral.h +++ b/boards/peripheral/peripheral.h @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -24,9 +25,24 @@ typedef struct { size_t sectorSz; /* Sector (erase) size in bytes */ } whal_PeripheralFlash_Cfg; +/* Peripheral sensor device test configuration */ +typedef struct { + const char *name; + whal_Sensor *dev; +} whal_PeripheralSensor_Cfg; + extern whal_PeripheralBlock_Cfg g_peripheralBlock[]; extern whal_PeripheralFlash_Cfg g_peripheralFlash[]; +enum { +#ifdef PERIPHERAL_BMI270 + PERIPHERAL_SENSOR_BMI270, +#endif + PERIPHERAL_SENSOR_COUNT, +}; + +extern whal_PeripheralSensor_Cfg g_peripheralSensor[]; + whal_Error Peripheral_Init(void); whal_Error Peripheral_Deinit(void); diff --git a/boards/peripheral/sensor/imu/bmi270.c b/boards/peripheral/sensor/imu/bmi270.c new file mode 100644 index 0000000..b39dad8 --- /dev/null +++ b/boards/peripheral/sensor/imu/bmi270.c @@ -0,0 +1,29 @@ +#include "bmi270.h" +#include +#include +#include "board.h" + +/* + * Bosch BMI270 — 6-axis IMU (accelerometer + gyroscope) + * + * - I2C address: 0x68 (SDO low) + * - Standard mode (100 kHz) or Fast mode (400 kHz) + * - Requires 8192-byte config blob upload during init + */ + +whal_I2c_ComCfg g_bmi270ComCfg = { + .freq = 400000, /* 400 kHz fast mode */ + .addr = WHAL_BMI270_ADDR_LOW, + .addrSz = 7, +}; + +whal_Sensor g_whalBmi270 = { + .driver = &whal_Bmi270_Driver, + .cfg = &(whal_Bmi270_Cfg) { + .i2c = &g_whalI2c, + .comCfg = &g_bmi270ComCfg, + .configData = whal_bmi270_config_data, + .configDataSz = WHAL_BMI270_CONFIG_DATA_SZ, + .DelayMs = Board_WaitMs, + }, +}; diff --git a/boards/peripheral/sensor/imu/bmi270.h b/boards/peripheral/sensor/imu/bmi270.h new file mode 100644 index 0000000..1474a58 --- /dev/null +++ b/boards/peripheral/sensor/imu/bmi270.h @@ -0,0 +1,10 @@ +#ifndef BOARD_SENSOR_IMU_BMI270_H +#define BOARD_SENSOR_IMU_BMI270_H + +#include +#include +#include + +extern whal_Sensor g_whalBmi270; + +#endif /* BOARD_SENSOR_IMU_BMI270_H */ diff --git a/boards/stm32wb55xx_nucleo/Makefile.inc b/boards/stm32wb55xx_nucleo/Makefile.inc index 0aeceb0..2b20420 100644 --- a/boards/stm32wb55xx_nucleo/Makefile.inc +++ b/boards/stm32wb55xx_nucleo/Makefile.inc @@ -27,6 +27,8 @@ BOARD_SOURCE += $(wildcard $(WHAL_DIR)/src/*/timer.c) BOARD_SOURCE += $(wildcard $(WHAL_DIR)/src/*/supply.c) BOARD_SOURCE += $(wildcard $(WHAL_DIR)/src/*/flash.c) BOARD_SOURCE += $(wildcard $(WHAL_DIR)/src/*/spi.c) +BOARD_SOURCE += $(wildcard $(WHAL_DIR)/src/*/i2c.c) +BOARD_SOURCE += $(wildcard $(WHAL_DIR)/src/*/sensor.c) BOARD_SOURCE += $(wildcard $(WHAL_DIR)/src/*/rng.c) BOARD_SOURCE += $(wildcard $(WHAL_DIR)/src/*/crypto.c) BOARD_SOURCE += $(wildcard $(WHAL_DIR)/src/*/block.c) diff --git a/boards/stm32wb55xx_nucleo/board.c b/boards/stm32wb55xx_nucleo/board.c index 7062bce..b2146bf 100644 --- a/boards/stm32wb55xx_nucleo/board.c +++ b/boards/stm32wb55xx_nucleo/board.c @@ -9,16 +9,10 @@ /* SysTick timing */ volatile uint32_t g_tick = 0; -volatile uint8_t g_waiting = 0; -volatile uint8_t g_tickOverflow = 0; -void SysTick_Handler() +void SysTick_Handler(void) { - uint32_t tickBefore = g_tick++; - if (g_waiting) { - if (tickBefore > g_tick) - g_tickOverflow = 1; - } + g_tick++; } uint32_t Board_GetTick(void) @@ -64,6 +58,7 @@ static const whal_Stm32wbRcc_Clk g_clocks[] = { {WHAL_STM32WB55_SPI1_CLOCK}, {WHAL_STM32WB55_RNG_CLOCK}, {WHAL_STM32WB55_AES1_CLOCK}, + {WHAL_STM32WB55_I2C1_CLOCK}, }; #define CLOCK_COUNT (sizeof(g_clocks) / sizeof(g_clocks[0])) @@ -135,11 +130,39 @@ whal_Gpio g_whalGpio = { .speed = WHAL_STM32WB_GPIO_SPEED_FAST, .pull = WHAL_STM32WB_GPIO_PULL_UP, }, + [I2C_SCL_PIN] = { /* I2C1 SCL */ + .port = WHAL_STM32WB_GPIO_PORT_B, + .pin = 8, + .mode = WHAL_STM32WB_GPIO_MODE_ALTFN, + .outType = WHAL_STM32WB_GPIO_OUTTYPE_OPENDRAIN, + .speed = WHAL_STM32WB_GPIO_SPEED_FAST, + .pull = WHAL_STM32WB_GPIO_PULL_UP, + .altFn = 4, + }, + [I2C_SDA_PIN] = { /* I2C1 SDA */ + .port = WHAL_STM32WB_GPIO_PORT_B, + .pin = 9, + .mode = WHAL_STM32WB_GPIO_MODE_ALTFN, + .outType = WHAL_STM32WB_GPIO_OUTTYPE_OPENDRAIN, + .speed = WHAL_STM32WB_GPIO_SPEED_FAST, + .pull = WHAL_STM32WB_GPIO_PULL_UP, + .altFn = 4, + }, }, .pinCount = PIN_COUNT, }, }; +/* I2C */ +whal_I2c g_whalI2c = { + WHAL_STM32WB55_I2C1_DEVICE, + + .cfg = &(whal_Stm32wbI2c_Cfg) { + .pclk = 64000000, + .timeout = &g_whalTimeout, + }, +}; + /* SPI */ whal_Spi g_whalSpi = { WHAL_STM32WB55_SPI1_DEVICE, @@ -259,20 +282,7 @@ whal_Crypto g_whalCrypto = { void Board_WaitMs(size_t ms) { uint32_t startCount = g_tick; - g_waiting = 1; - while (1) { - uint32_t currentCount = g_tick; - if (g_tickOverflow) { - if ((UINT32_MAX - startCount) + currentCount > ms) { - break; - } - } else if (currentCount - startCount > ms) { - break; - } - } - - g_waiting = 0; - g_tickOverflow = 0; + while (g_tick - startCount < ms); } whal_Error Board_Init(void) @@ -347,6 +357,11 @@ whal_Error Board_Init(void) return err; } + err = whal_I2c_Init(&g_whalI2c); + if (err) { + return err; + } + err = whal_Flash_Init(&g_whalFlash); if (err) { return err; @@ -414,6 +429,11 @@ whal_Error Board_Deinit(void) return err; } + err = whal_I2c_Deinit(&g_whalI2c); + if (err) { + return err; + } + err = whal_Spi_Deinit(&g_whalSpi); if (err) { return err; diff --git a/boards/stm32wb55xx_nucleo/board.h b/boards/stm32wb55xx_nucleo/board.h index 1075024..0ed42b3 100644 --- a/boards/stm32wb55xx_nucleo/board.h +++ b/boards/stm32wb55xx_nucleo/board.h @@ -13,6 +13,7 @@ extern whal_Spi g_whalSpi; extern whal_Flash g_whalFlash; extern whal_Rng g_whalRng; extern whal_Crypto g_whalCrypto; +extern whal_I2c g_whalI2c; extern whal_Irq g_whalIrq; extern whal_Timeout g_whalTimeout; @@ -26,6 +27,8 @@ enum { SPI_MISO_PIN, SPI_MOSI_PIN, SPI_CS_PIN, + I2C_SCL_PIN, + I2C_SDA_PIN, PIN_COUNT, }; diff --git a/docs/adding_a_peripheral.md b/docs/adding_a_peripheral.md index 94e5336..a13e28f 100644 --- a/docs/adding_a_peripheral.md +++ b/docs/adding_a_peripheral.md @@ -22,7 +22,7 @@ Peripherals are organized by device type under `boards/peripheral/`: ``` boards/peripheral/ peripheral.h # Registry structs and extern arrays - peripheral.c # Registry arrays (g_peripheralBlock[], g_peripheralFlash[]) + peripheral.c # Registry arrays (g_peripheralBlock[], g_peripheralFlash[], g_peripheralSensor[]) Makefile.inc # Conditional build rules block/ sdhc_spi_sdcard32gb.h @@ -30,6 +30,9 @@ boards/peripheral/ flash/ spi_nor_w25q64.h spi_nor_w25q64.c + sensor/imu/ + bmi270.h + bmi270.c ``` ## Step 1: Create the Device Configuration @@ -120,6 +123,7 @@ The registry structs are defined in `peripheral.h`: - `whal_PeripheralBlock_Cfg` for block devices (`g_peripheralBlock[]`) - `whal_PeripheralFlash_Cfg` for flash devices (`g_peripheralFlash[]`) +- `whal_PeripheralSensor_Cfg` for sensor devices (`g_peripheralSensor[]`) Each array is terminated by a zero sentinel so that board init and test code can iterate without knowing the count. @@ -127,31 +131,32 @@ can iterate without knowing the count. ## Step 3: Add Build Rules In `boards/peripheral/Makefile.inc`, add a conditional block for your -peripheral: +peripheral. The block checks whether the peripheral name appears in the +`PERIPHERALS` variable and adds the define, config source, and driver source: ```makefile -ifdef PERIPHERAL_SPI_NOR_W25Q64 -CFLAGS += -DPERIPHERAL_SPI_NOR_W25Q64 -BOARD_SOURCE += $(_PERIPHERAL_DIR)/flash/spi_nor_w25q64.c -BOARD_SOURCE += $(WHAL_DIR)/src/flash/spi_nor.c +ifneq ($(filter mydevice,$(PERIPHERALS)),) +CFLAGS += -DPERIPHERAL_MYDEVICE +BOARD_SOURCE += $(_PERIPHERAL_DIR)/type/mydevice.c +BOARD_SOURCE += $(WHAL_DIR)/src/type/mydevice_driver.c endif ``` This compiles both the peripheral configuration and the underlying driver -source when the flag is set. +source when the peripheral is enabled. ## Building -Enable the peripheral by setting its flag when building: +Enable peripherals using the `PERIPHERALS` variable: ``` -make BOARD=stm32wb55xx_nucleo PERIPHERAL_SPI_NOR_W25Q64=1 +make BOARD=stm32wb55xx_nucleo PERIPHERALS="spi_nor_w25q64" ``` Multiple peripherals can be enabled simultaneously: ``` -make BOARD=stm32wb55xx_nucleo PERIPHERAL_SPI_NOR_W25Q64=1 PERIPHERAL_SDHC_SPI_SDCARD32GB=1 +make BOARD=stm32wb55xx_nucleo PERIPHERALS="spi_nor_w25q64 bmi270" ``` ## Testing @@ -163,7 +168,8 @@ framework. ## Naming Convention -- Flag: `PERIPHERAL__` (e.g., `PERIPHERAL_SPI_NOR_W25Q64`) -- Directory: `boards/peripheral//` (e.g., `flash/`, `block/`) -- Files: `_.h` and `_.c` -- Global instance: `g_whal` (e.g., `g_whalSpiNorW25q64`) +- Flag: `PERIPHERAL_` (e.g., `PERIPHERAL_BMI270`) +- PERIPHERALS variable: lowercase name (e.g., `bmi270`, `spi_nor_w25q64`) +- Directory: `boards/peripheral//` (e.g., `flash/`, `block/`, `sensor/imu/`) +- Files: `.h` and `.c` +- Global instance: `g_whal` (e.g., `g_whalBmi270`, `g_whalSpiNorW25q64`) diff --git a/docs/writing_a_driver.md b/docs/writing_a_driver.md index 9135d4c..f216beb 100644 --- a/docs/writing_a_driver.md +++ b/docs/writing_a_driver.md @@ -542,6 +542,97 @@ After the loop, wait for the bus to go idle before returning. --- +## I2C + +Header: `wolfHAL/i2c/i2c.h` + +The I2C driver provides inter-integrated circuit bus communication. A +communication session is bracketed by `StartCom` / `EndCom`, which configure +the peripheral for a specific target address and bus frequency. The message-based +`Transfer` API gives full control over bus conditions (START, STOP, direction). + +Convenience helpers `whal_I2c_WriteReg` and `whal_I2c_ReadReg` are provided +as inline functions for the common register read/write patterns. + +### Init + +Perform one-time I2C peripheral configuration (noise filters, enable). Do not +configure timing or address here — these are applied per-session via `StartCom`. + +The board must enable the peripheral clock before calling Init. + +### Deinit + +Disable the I2C peripheral. + +### StartCom + +Begin a communication session. Configures the peripheral from the +platform-independent `whal_I2c_ComCfg` struct: + +- `freq` — bus frequency in Hz (100000, 400000, 1000000) +- `addr` — target device address (7-bit or 10-bit) +- `addrSz` — address size in bits (7 or 10) + +The driver should compute timing parameters from `freq` and the peripheral +clock, then write the target address into the appropriate hardware register. + +### EndCom + +End the current communication session by clearing the target address. + +### Transfer + +Execute a sequence of I2C messages. Each message has a data buffer, size, and +flags that control bus conditions: + +- `WHAL_I2C_MSG_WRITE` (0) — master write +- `WHAL_I2C_MSG_READ` — master read +- `WHAL_I2C_MSG_START` — generate START (or repeated START) +- `WHAL_I2C_MSG_STOP` — generate STOP after this message + +The driver processes messages sequentially. When the current message has no +STOP and the next message has no START, use hardware RELOAD to continue the +same transfer without re-addressing (for multi-part writes). When the next +message has START, use TC (transfer complete) so the hardware can generate a +repeated START for direction changes. + +For messages larger than 255 bytes, the driver must split into chunks using +the hardware RELOAD mechanism internally. + +--- + +## Sensor + +Header: `wolfHAL/sensor/sensor.h` + +The sensor driver provides a bus-agnostic API for reading sensor data. Each +sensor driver implements the vtable and uses the appropriate bus (I2C, SPI, +etc.) internally. The `Read` function fills a driver-defined data struct +passed as a void pointer. + +### Init + +Initialize the sensor hardware. This may involve loading configuration data, +verifying device identity, and configuring operating parameters. The driver +should call `StartCom` / `EndCom` on its bus device to bracket I2C/SPI access. + +### Deinit + +Shut down the sensor (e.g., soft reset). Should bracket bus access with +`StartCom` / `EndCom`. + +### Read + +Read sensor data into a driver-defined struct. The driver fetches a new sample +from the hardware and fills the struct. The struct type is defined by each +sensor driver (e.g., `whal_Bmi270_Data` with `accelX/Y/Z`, `gyroX/Y/Z`). + +Each `Read` call should bracket its bus access with `StartCom` / `EndCom` so +the bus is released between reads and other devices can use it. + +--- + ## Flash Header: `wolfHAL/flash/flash.h` diff --git a/src/i2c/i2c.c b/src/i2c/i2c.c new file mode 100644 index 0000000..95e97cf --- /dev/null +++ b/src/i2c/i2c.c @@ -0,0 +1,48 @@ +#include +#include + +inline whal_Error whal_I2c_Init(whal_I2c *i2cDev) +{ + if (!i2cDev || !i2cDev->driver || !i2cDev->driver->Init) { + return WHAL_EINVAL; + } + + return i2cDev->driver->Init(i2cDev); +} + +inline whal_Error whal_I2c_Deinit(whal_I2c *i2cDev) +{ + if (!i2cDev || !i2cDev->driver || !i2cDev->driver->Deinit) { + return WHAL_EINVAL; + } + + return i2cDev->driver->Deinit(i2cDev); +} + +inline whal_Error whal_I2c_StartCom(whal_I2c *i2cDev, whal_I2c_ComCfg *comCfg) +{ + if (!i2cDev || !i2cDev->driver || !i2cDev->driver->StartCom || !comCfg) { + return WHAL_EINVAL; + } + + return i2cDev->driver->StartCom(i2cDev, comCfg); +} + +inline whal_Error whal_I2c_EndCom(whal_I2c *i2cDev) +{ + if (!i2cDev || !i2cDev->driver || !i2cDev->driver->EndCom) { + return WHAL_EINVAL; + } + + return i2cDev->driver->EndCom(i2cDev); +} + +inline whal_Error whal_I2c_Transfer(whal_I2c *i2cDev, whal_I2c_Msg *msgs, + size_t numMsgs) +{ + if (!i2cDev || !i2cDev->driver || !i2cDev->driver->Transfer || !msgs) { + return WHAL_EINVAL; + } + + return i2cDev->driver->Transfer(i2cDev, msgs, numMsgs); +} diff --git a/src/i2c/stm32wb_i2c.c b/src/i2c/stm32wb_i2c.c new file mode 100644 index 0000000..3ec2c91 --- /dev/null +++ b/src/i2c/stm32wb_i2c.c @@ -0,0 +1,509 @@ +#include +#include +#include +#include +#include +#include + +/* + * STM32WB I2C Register Definitions + * + * The I2C peripheral provides controller and target mode communication. + * This driver operates in controller mode with polling, using software + * end mode (AUTOEND=0) to support multi-message transfers with repeated + * START conditions. + */ + +/* Control Register 1 */ +#define I2C_CR1_REG 0x00 +#define I2C_CR1_PE_Pos 0 /* Peripheral enable */ +#define I2C_CR1_PE_Msk (1UL << I2C_CR1_PE_Pos) + +#define I2C_CR1_ANFOFF_Pos 12 /* Analog noise filter OFF */ +#define I2C_CR1_ANFOFF_Msk (1UL << I2C_CR1_ANFOFF_Pos) + +#define I2C_CR1_DNF_Pos 8 /* Digital noise filter */ +#define I2C_CR1_DNF_Msk (WHAL_BITMASK(4) << I2C_CR1_DNF_Pos) + +/* Control Register 2 */ +#define I2C_CR2_REG 0x04 +#define I2C_CR2_SADD_Pos 0 /* Target address */ +#define I2C_CR2_SADD_Msk (WHAL_BITMASK(10) << I2C_CR2_SADD_Pos) + +#define I2C_CR2_RD_WRN_Pos 10 /* Transfer direction */ +#define I2C_CR2_RD_WRN_Msk (1UL << I2C_CR2_RD_WRN_Pos) + +#define I2C_CR2_ADD10_Pos 11 /* 10-bit addressing mode */ +#define I2C_CR2_ADD10_Msk (1UL << I2C_CR2_ADD10_Pos) + +#define I2C_CR2_START_Pos 13 /* START generation */ +#define I2C_CR2_START_Msk (1UL << I2C_CR2_START_Pos) + +#define I2C_CR2_STOP_Pos 14 /* STOP generation */ +#define I2C_CR2_STOP_Msk (1UL << I2C_CR2_STOP_Pos) + +#define I2C_CR2_NBYTES_Pos 16 /* Number of bytes */ +#define I2C_CR2_NBYTES_Msk (WHAL_BITMASK(8) << I2C_CR2_NBYTES_Pos) + +#define I2C_CR2_RELOAD_Pos 24 /* NBYTES reload mode */ +#define I2C_CR2_RELOAD_Msk (1UL << I2C_CR2_RELOAD_Pos) + +#define I2C_CR2_AUTOEND_Pos 25 /* Automatic end mode */ +#define I2C_CR2_AUTOEND_Msk (1UL << I2C_CR2_AUTOEND_Pos) + +/* Timing Register */ +#define I2C_TIMINGR_REG 0x10 + +/* Interrupt and Status Register */ +#define I2C_ISR_REG 0x18 +#define I2C_ISR_TXE_Pos 0 /* Transmit data register empty */ +#define I2C_ISR_TXE_Msk (1UL << I2C_ISR_TXE_Pos) + +#define I2C_ISR_TXIS_Pos 1 /* Transmit interrupt status */ +#define I2C_ISR_TXIS_Msk (1UL << I2C_ISR_TXIS_Pos) + +#define I2C_ISR_RXNE_Pos 2 /* Receive data register not empty */ +#define I2C_ISR_RXNE_Msk (1UL << I2C_ISR_RXNE_Pos) + +#define I2C_ISR_NACKF_Pos 4 /* Not acknowledge received */ +#define I2C_ISR_NACKF_Msk (1UL << I2C_ISR_NACKF_Pos) + +#define I2C_ISR_STOPF_Pos 5 /* STOP detection */ +#define I2C_ISR_STOPF_Msk (1UL << I2C_ISR_STOPF_Pos) + +#define I2C_ISR_TC_Pos 6 /* Transfer complete */ +#define I2C_ISR_TC_Msk (1UL << I2C_ISR_TC_Pos) + +#define I2C_ISR_TCR_Pos 7 /* Transfer complete reload */ +#define I2C_ISR_TCR_Msk (1UL << I2C_ISR_TCR_Pos) + +#define I2C_ISR_BERR_Pos 8 /* Bus error */ +#define I2C_ISR_BERR_Msk (1UL << I2C_ISR_BERR_Pos) + +#define I2C_ISR_ARLO_Pos 9 /* Arbitration lost */ +#define I2C_ISR_ARLO_Msk (1UL << I2C_ISR_ARLO_Pos) + +#define I2C_ISR_BUSY_Pos 15 /* Bus busy */ +#define I2C_ISR_BUSY_Msk (1UL << I2C_ISR_BUSY_Pos) + +/* Interrupt Clear Register */ +#define I2C_ICR_REG 0x1C +#define I2C_ICR_NACKCF_Pos 4 /* NACK clear flag */ +#define I2C_ICR_NACKCF_Msk (1UL << I2C_ICR_NACKCF_Pos) + +#define I2C_ICR_STOPCF_Pos 5 /* STOP clear flag */ +#define I2C_ICR_STOPCF_Msk (1UL << I2C_ICR_STOPCF_Pos) + +#define I2C_ICR_BERRCF_Pos 8 /* Bus error clear flag */ +#define I2C_ICR_BERRCF_Msk (1UL << I2C_ICR_BERRCF_Pos) + +#define I2C_ICR_ARLOCF_Pos 9 /* Arbitration lost clear flag */ +#define I2C_ICR_ARLOCF_Msk (1UL << I2C_ICR_ARLOCF_Pos) + +/* Receive Data Register */ +#define I2C_RXDR_REG 0x24 + +/* Transmit Data Register */ +#define I2C_TXDR_REG 0x28 + +/* I2C_TIMINGR bit positions */ +#define I2C_TIMINGR_SCLL_Pos 0 +#define I2C_TIMINGR_SCLH_Pos 8 +#define I2C_TIMINGR_SDADEL_Pos 16 +#define I2C_TIMINGR_SCLDEL_Pos 20 +#define I2C_TIMINGR_PRESC_Pos 28 + +/* I2C spec minimum SCL timing values in nanoseconds */ +#define I2C_SM_TLOW_NS 4700 /* Standard mode tLOW min */ +#define I2C_SM_THIGH_NS 4000 /* Standard mode tHIGH min */ +#define I2C_FM_TLOW_NS 1300 /* Fast mode tLOW min */ +#define I2C_FM_THIGH_NS 600 /* Fast mode tHIGH min */ +#define I2C_FMP_TLOW_NS 500 /* Fast mode plus tLOW min */ +#define I2C_FMP_THIGH_NS 260 /* Fast mode plus tHIGH min */ + +static uint32_t Stm32wbI2c_CalcTimingr(uint32_t pclk, uint32_t freq) +{ + uint32_t tLowNs, tHighNs; + uint32_t presc, scll, sclh, sdadel, scldel; + uint32_t tPrescNs; + + if (freq <= 100000) { + tLowNs = I2C_SM_TLOW_NS; + tHighNs = I2C_SM_THIGH_NS; + sdadel = 2; + scldel = 4; + } else if (freq <= 400000) { + tLowNs = I2C_FM_TLOW_NS; + tHighNs = I2C_FM_THIGH_NS; + sdadel = 1; + scldel = 3; + } else { + tLowNs = I2C_FMP_TLOW_NS; + tHighNs = I2C_FMP_THIGH_NS; + sdadel = 0; + scldel = 1; + } + + for (presc = 0; presc < 16; presc++) { + tPrescNs = ((presc + 1) * 1000) / (pclk / 1000000); + if (tPrescNs == 0) + continue; + + scll = tLowNs / tPrescNs; + sclh = tHighNs / tPrescNs; + + if (scll > 0) scll--; + if (sclh > 0) sclh--; + + if (scll <= 255 && sclh <= 255) + break; + } + + if (presc >= 16) { + presc = 15; + scll = 255; + sclh = 255; + } + + return (presc << I2C_TIMINGR_PRESC_Pos) | + (scldel << I2C_TIMINGR_SCLDEL_Pos) | + (sdadel << I2C_TIMINGR_SDADEL_Pos) | + (sclh << I2C_TIMINGR_SCLH_Pos) | + (scll << I2C_TIMINGR_SCLL_Pos); +} + +static whal_Error Stm32wbI2c_CheckErrors(size_t base) +{ + size_t isr = whal_Reg_Read(base, I2C_ISR_REG); + + if (isr & I2C_ISR_NACKF_Msk) { + whal_Reg_Write(base, I2C_ICR_REG, I2C_ICR_NACKCF_Msk); + /* Generate STOP after NACK */ + whal_Reg_Update(base, I2C_CR2_REG, I2C_CR2_STOP_Msk, + whal_SetBits(I2C_CR2_STOP_Msk, I2C_CR2_STOP_Pos, 1)); + return WHAL_EHARDWARE; + } + + if (isr & I2C_ISR_BERR_Msk) { + whal_Reg_Write(base, I2C_ICR_REG, I2C_ICR_BERRCF_Msk); + return WHAL_EHARDWARE; + } + + if (isr & I2C_ISR_ARLO_Msk) { + whal_Reg_Write(base, I2C_ICR_REG, I2C_ICR_ARLOCF_Msk); + return WHAL_EHARDWARE; + } + + return WHAL_SUCCESS; +} + +static whal_Error Stm32wbI2c_WaitFlag(size_t base, size_t mask, size_t value, + whal_Timeout *timeout) +{ + whal_Error err; + + WHAL_TIMEOUT_START(timeout); + + while (1) { + size_t isr = whal_Reg_Read(base, I2C_ISR_REG); + + if ((isr & mask) == value) + return WHAL_SUCCESS; + + err = Stm32wbI2c_CheckErrors(base); + if (err) + return err; + + if (WHAL_TIMEOUT_EXPIRED(timeout)) + return WHAL_ETIMEOUT; + } +} + +/* + * Transfer up to 255 bytes in a single hardware operation. + * Caller must ensure nbytes <= 255. + */ +/* Transfer control bits mask — everything TransferChunk touches in CR2 */ +#define I2C_CR2_XFER_Msk (I2C_CR2_RD_WRN_Msk | I2C_CR2_NBYTES_Msk | \ + I2C_CR2_START_Msk | I2C_CR2_RELOAD_Msk) + +static whal_Error Stm32wbI2c_TransferChunk(size_t base, uint8_t *buf, + size_t nbytes, uint8_t isRead, + uint8_t start, uint8_t reload, + whal_Timeout *timeout) +{ + uint32_t cr2 = 0; + whal_Error err; + + cr2 |= whal_SetBits(I2C_CR2_RD_WRN_Msk, I2C_CR2_RD_WRN_Pos, isRead); + cr2 |= whal_SetBits(I2C_CR2_NBYTES_Msk, I2C_CR2_NBYTES_Pos, nbytes); + + if (start) + cr2 |= whal_SetBits(I2C_CR2_START_Msk, I2C_CR2_START_Pos, 1); + + if (reload) + cr2 |= whal_SetBits(I2C_CR2_RELOAD_Msk, I2C_CR2_RELOAD_Pos, 1); + + /* Update only transfer control bits, preserving SADD + ADD10 from StartCom */ + whal_Reg_Update(base, I2C_CR2_REG, I2C_CR2_XFER_Msk, cr2); + + for (size_t i = 0; i < nbytes; i++) { + if (isRead) { + err = Stm32wbI2c_WaitFlag(base, I2C_ISR_RXNE_Msk, + I2C_ISR_RXNE_Msk, timeout); + if (err) + return err; + + if (buf) + buf[i] = (uint8_t)whal_Reg_Read(base, I2C_RXDR_REG); + else + (void)whal_Reg_Read(base, I2C_RXDR_REG); + } else { + err = Stm32wbI2c_WaitFlag(base, I2C_ISR_TXIS_Msk, + I2C_ISR_TXIS_Msk, timeout); + if (err) + return err; + + whal_Reg_Write(base, I2C_TXDR_REG, buf ? buf[i] : 0xFF); + } + } + + if (reload) { + /* Wait for TCR before next chunk */ + err = Stm32wbI2c_WaitFlag(base, I2C_ISR_TCR_Msk, + I2C_ISR_TCR_Msk, timeout); + if (err) + return err; + } + + return WHAL_SUCCESS; +} + +/* + * Transfer a single I2C message on the bus. + * + * Handles messages of any size by splitting into <=255 byte chunks using + * the hardware RELOAD mechanism. The SADD and ADD10 bits in CR2 are + * configured by StartCom and preserved across writes. + * + * The caller controls bus conditions explicitly via message flags: + * WHAL_I2C_MSG_START — generate START (or repeated START) + * WHAL_I2C_MSG_STOP — generate STOP after this message + * WHAL_I2C_MSG_READ — master read + * WHAL_I2C_MSG_WRITE — master write + * + * When STOP is not set, the function waits for TC (transfer complete) which + * stretches SCL low, holding the bus for the next message's START. + */ +/* + * @param reloadLast If true, use RELOAD on the last chunk so the next + * message can continue the same transfer without + * re-addressing. If false, use TC so the next message + * can issue START for a direction change. + */ +static whal_Error Stm32wbI2c_TransferMsg(size_t base, whal_I2c_Msg *msg, + uint8_t reloadLast, + whal_Timeout *timeout) +{ + uint8_t *buf = (uint8_t *)msg->data; + uint8_t isRead = (msg->flags & WHAL_I2C_MSG_READ) ? 1 : 0; + uint8_t doStart = (msg->flags & WHAL_I2C_MSG_START) ? 1 : 0; + uint8_t doStop = (msg->flags & WHAL_I2C_MSG_STOP) ? 1 : 0; + size_t remaining = msg->dataSz; + whal_Error err; + + if (msg->dataSz == 0) + return WHAL_EINVAL; + + /* Split into <=255 byte RELOAD chunks */ + while (remaining > 255) { + err = Stm32wbI2c_TransferChunk(base, buf, 255, isRead, + doStart, 1, timeout); + if (err) + return err; + + doStart = 0; + if (buf) + buf += 255; + remaining -= 255; + } + + /* Last chunk: RELOAD if the next message continues the same transfer + * (no START), otherwise no RELOAD so hardware sets TC. + * Note: reloadLast and doStop are mutually exclusive — the caller + * (Transfer) never sets reloadLast when the message has STOP. */ + err = Stm32wbI2c_TransferChunk(base, buf, remaining, isRead, + doStart, reloadLast, timeout); + if (err) + return err; + + if (doStop) { + /* Wait for TC, then issue STOP */ + err = Stm32wbI2c_WaitFlag(base, I2C_ISR_TC_Msk, + I2C_ISR_TC_Msk, timeout); + if (err) + return err; + + whal_Reg_Update(base, I2C_CR2_REG, I2C_CR2_STOP_Msk, + whal_SetBits(I2C_CR2_STOP_Msk, I2C_CR2_STOP_Pos, 1)); + + err = Stm32wbI2c_WaitFlag(base, I2C_ISR_STOPF_Msk, + I2C_ISR_STOPF_Msk, timeout); + if (err) + return err; + + whal_Reg_Write(base, I2C_ICR_REG, I2C_ICR_STOPCF_Msk); + } + + return WHAL_SUCCESS; +} + +whal_Error whal_Stm32wbI2c_Init(whal_I2c *i2cDev) +{ + const whal_Regmap *reg; + + if (!i2cDev || !i2cDev->cfg) { + return WHAL_EINVAL; + } + + reg = &i2cDev->regmap; + + /* Disable PE before configuring */ + whal_Reg_Update(reg->base, I2C_CR1_REG, I2C_CR1_PE_Msk, + whal_SetBits(I2C_CR1_PE_Msk, I2C_CR1_PE_Pos, 0)); + + /* Enable analog noise filter (ANFOFF=0), disable digital filter (DNF=0) */ + whal_Reg_Update(reg->base, I2C_CR1_REG, + I2C_CR1_ANFOFF_Msk | I2C_CR1_DNF_Msk, + whal_SetBits(I2C_CR1_ANFOFF_Msk, I2C_CR1_ANFOFF_Pos, 0) | + whal_SetBits(I2C_CR1_DNF_Msk, I2C_CR1_DNF_Pos, 0)); + + /* Enable peripheral */ + whal_Reg_Update(reg->base, I2C_CR1_REG, I2C_CR1_PE_Msk, + whal_SetBits(I2C_CR1_PE_Msk, I2C_CR1_PE_Pos, 1)); + + return WHAL_SUCCESS; +} + +whal_Error whal_Stm32wbI2c_Deinit(whal_I2c *i2cDev) +{ + const whal_Regmap *reg; + + if (!i2cDev || !i2cDev->cfg) { + return WHAL_EINVAL; + } + + reg = &i2cDev->regmap; + + /* Disable peripheral */ + whal_Reg_Update(reg->base, I2C_CR1_REG, I2C_CR1_PE_Msk, + whal_SetBits(I2C_CR1_PE_Msk, I2C_CR1_PE_Pos, 0)); + + return WHAL_SUCCESS; +} + +whal_Error whal_Stm32wbI2c_StartCom(whal_I2c *i2cDev, whal_I2c_ComCfg *comCfg) +{ + const whal_Regmap *reg; + whal_Stm32wbI2c_Cfg *cfg; + uint32_t cr2 = 0; + + if (!i2cDev || !i2cDev->cfg || !comCfg) { + return WHAL_EINVAL; + } + + if ((comCfg->addrSz != 7 && comCfg->addrSz != 10) || comCfg->freq == 0) { + return WHAL_EINVAL; + } + + reg = &i2cDev->regmap; + cfg = (whal_Stm32wbI2c_Cfg *)i2cDev->cfg; + + /* Disable PE to configure timing */ + whal_Reg_Update(reg->base, I2C_CR1_REG, I2C_CR1_PE_Msk, + whal_SetBits(I2C_CR1_PE_Msk, I2C_CR1_PE_Pos, 0)); + + /* Compute and write timing register */ + whal_Reg_Write(reg->base, I2C_TIMINGR_REG, + Stm32wbI2c_CalcTimingr(cfg->pclk, comCfg->freq)); + + /* Re-enable PE */ + whal_Reg_Update(reg->base, I2C_CR1_REG, I2C_CR1_PE_Msk, + whal_SetBits(I2C_CR1_PE_Msk, I2C_CR1_PE_Pos, 1)); + + /* Configure target address and addressing mode in CR2 */ + if (comCfg->addrSz == 10) { + cr2 |= whal_SetBits(I2C_CR2_ADD10_Msk, I2C_CR2_ADD10_Pos, 1); + cr2 |= whal_SetBits(I2C_CR2_SADD_Msk, I2C_CR2_SADD_Pos, + comCfg->addr); + } else { + cr2 |= whal_SetBits(I2C_CR2_SADD_Msk, I2C_CR2_SADD_Pos, + (uint32_t)comCfg->addr << 1); + } + + whal_Reg_Update(reg->base, I2C_CR2_REG, + I2C_CR2_SADD_Msk | I2C_CR2_ADD10_Msk, cr2); + + return WHAL_SUCCESS; +} + +whal_Error whal_Stm32wbI2c_EndCom(whal_I2c *i2cDev) +{ + const whal_Regmap *reg; + + if (!i2cDev || !i2cDev->cfg) { + return WHAL_EINVAL; + } + + reg = &i2cDev->regmap; + + /* Clear target address and addressing mode */ + whal_Reg_Update(reg->base, I2C_CR2_REG, + I2C_CR2_SADD_Msk | I2C_CR2_ADD10_Msk, 0); + + return WHAL_SUCCESS; +} + +whal_Error whal_Stm32wbI2c_Transfer(whal_I2c *i2cDev, whal_I2c_Msg *msgs, + size_t numMsgs) +{ + const whal_Regmap *reg; + whal_Stm32wbI2c_Cfg *cfg; + whal_Error err; + + if (!i2cDev || !i2cDev->cfg || !msgs || numMsgs == 0) { + return WHAL_EINVAL; + } + + reg = &i2cDev->regmap; + cfg = (whal_Stm32wbI2c_Cfg *)i2cDev->cfg; + + for (size_t i = 0; i < numMsgs; i++) { + /* + * Use RELOAD on the last chunk if the current message has no STOP + * and the next message has no START (i.e. it continues the same + * transfer without re-addressing). Otherwise use TC so the next + * message's START can generate a repeated START. + */ + uint8_t reloadLast = 0; + if (!(msgs[i].flags & WHAL_I2C_MSG_STOP) && i + 1 < numMsgs && + !(msgs[i + 1].flags & WHAL_I2C_MSG_START)) { + reloadLast = 1; + } + + err = Stm32wbI2c_TransferMsg(reg->base, &msgs[i], reloadLast, + cfg->timeout); + if (err) + return err; + } + + return WHAL_SUCCESS; +} + +const whal_I2cDriver whal_Stm32wbI2c_Driver = { + .Init = whal_Stm32wbI2c_Init, + .Deinit = whal_Stm32wbI2c_Deinit, + .StartCom = whal_Stm32wbI2c_StartCom, + .EndCom = whal_Stm32wbI2c_EndCom, + .Transfer = whal_Stm32wbI2c_Transfer, +}; diff --git a/src/sensor/imu/bmi270.c b/src/sensor/imu/bmi270.c new file mode 100644 index 0000000..bf40dc5 --- /dev/null +++ b/src/sensor/imu/bmi270.c @@ -0,0 +1,217 @@ +#include +#include +#include +#include +#include + +/* BMI270 Register Addresses */ +#define BMI270_REG_CHIP_ID 0x00 +#define BMI270_REG_DATA_ACC_X_LSB 0x0C /* Accel X/Y/Z: 0x0C-0x11 */ +#define BMI270_REG_DATA_GYR_X_LSB 0x12 /* Gyro X/Y/Z: 0x12-0x17 */ +#define BMI270_REG_INTERNAL_STATUS 0x21 +#define BMI270_REG_ACC_CONF 0x40 +#define BMI270_REG_ACC_RANGE 0x41 +#define BMI270_REG_GYR_CONF 0x42 +#define BMI270_REG_GYR_RANGE 0x43 +#define BMI270_REG_INIT_CTRL 0x59 +#define BMI270_REG_INIT_DATA 0x5E +#define BMI270_REG_PWR_CONF 0x7C +#define BMI270_REG_PWR_CTRL 0x7D +#define BMI270_REG_CMD 0x7E + +/* BMI270 Commands and Values */ +#define BMI270_CMD_SOFT_RESET 0xB6 +#define BMI270_PWR_CTRL_EN 0x0E /* Enable accel + gyro + temp */ +#define BMI270_INTERNAL_STATUS_OK 0x01 +#define BMI270_INTERNAL_STATUS_Msk 0x0F +#define BMI270_PWR_CONF_NORMAL 0x02 /* adv_power_save off, fifo_self_wakeup on */ +#define BMI270_ACC_CONF_VAL 0xA8 /* Filter perf, OSR2, ODR 100 Hz */ +#define BMI270_ACC_RANGE_16G 0x03 /* +/- 16g */ +#define BMI270_GYR_CONF_VAL 0xA9 /* Filter perf, OSR2, ODR 200 Hz */ +#define BMI270_GYR_RANGE_2000DPS 0x08 /* +/- 2000 deg/s */ + +/* Number of raw data bytes: accel (6) + gyro (6) */ +#define BMI270_DATA_LEN 12 + +whal_Error whal_Bmi270_Init(whal_Sensor *dev) +{ + whal_Bmi270_Cfg *cfg; + whal_Error err; + uint8_t val; + + if (!dev || !dev->cfg) { + return WHAL_EINVAL; + } + + cfg = (whal_Bmi270_Cfg *)dev->cfg; + + if (!cfg->i2c || !cfg->comCfg || !cfg->configData || + cfg->configDataSz == 0 || !cfg->DelayMs) { + return WHAL_EINVAL; + } + + err = whal_I2c_StartCom(cfg->i2c, cfg->comCfg); + if (err) + return err; + + /* Soft reset */ + val = BMI270_CMD_SOFT_RESET; + err = whal_I2c_WriteReg(cfg->i2c, BMI270_REG_CMD, &val, 1); + if (err) + goto cleanup; + + cfg->DelayMs(2); + + /* Verify CHIP_ID */ + err = whal_I2c_ReadReg(cfg->i2c, BMI270_REG_CHIP_ID, &val, 1); + if (err) + goto cleanup; + + if (val != WHAL_BMI270_CHIP_ID) { + err = WHAL_EHARDWARE; + goto cleanup; + } + + /* Disable advanced power save for config upload */ + val = 0x00; + err = whal_I2c_WriteReg(cfg->i2c, BMI270_REG_PWR_CONF, &val, 1); + if (err) + goto cleanup; + + cfg->DelayMs(1); + + /* Prepare config load */ + val = 0x00; + err = whal_I2c_WriteReg(cfg->i2c, BMI270_REG_INIT_CTRL, &val, 1); + if (err) + goto cleanup; + + /* Burst write config blob */ + err = whal_I2c_WriteReg(cfg->i2c, BMI270_REG_INIT_DATA, + cfg->configData, cfg->configDataSz); + if (err) + goto cleanup; + + /* Trigger config load */ + val = 0x01; + err = whal_I2c_WriteReg(cfg->i2c, BMI270_REG_INIT_CTRL, &val, 1); + if (err) + goto cleanup; + + cfg->DelayMs(20); + err = whal_I2c_ReadReg(cfg->i2c, BMI270_REG_INTERNAL_STATUS, &val, 1); + if (err) + goto cleanup; + + if ((val & BMI270_INTERNAL_STATUS_Msk) != BMI270_INTERNAL_STATUS_OK) { + err = WHAL_EHARDWARE; + goto cleanup; + } + + /* Enable accelerometer, gyroscope, and temperature sensor */ + val = BMI270_PWR_CTRL_EN; + err = whal_I2c_WriteReg(cfg->i2c, BMI270_REG_PWR_CTRL, &val, 1); + if (err) + goto cleanup; + + /* Configure accelerometer: filter perf, normal BWP, 100 Hz ODR, 16g */ + val = BMI270_ACC_CONF_VAL; + err = whal_I2c_WriteReg(cfg->i2c, BMI270_REG_ACC_CONF, &val, 1); + if (err) + goto cleanup; + + val = BMI270_ACC_RANGE_16G; + err = whal_I2c_WriteReg(cfg->i2c, BMI270_REG_ACC_RANGE, &val, 1); + if (err) + goto cleanup; + + /* Configure gyroscope: filter perf, normal BWP, 200 Hz ODR, 2000 dps */ + val = BMI270_GYR_CONF_VAL; + err = whal_I2c_WriteReg(cfg->i2c, BMI270_REG_GYR_CONF, &val, 1); + if (err) + goto cleanup; + + val = BMI270_GYR_RANGE_2000DPS; + err = whal_I2c_WriteReg(cfg->i2c, BMI270_REG_GYR_RANGE, &val, 1); + if (err) + goto cleanup; + + /* Normal power mode */ + val = BMI270_PWR_CONF_NORMAL; + err = whal_I2c_WriteReg(cfg->i2c, BMI270_REG_PWR_CONF, &val, 1); + if (err) + goto cleanup; + + /* Wait for first sample to be ready */ + cfg->DelayMs(100); + +cleanup: + whal_I2c_EndCom(cfg->i2c); + return err; +} + +whal_Error whal_Bmi270_Deinit(whal_Sensor *dev) +{ + whal_Bmi270_Cfg *cfg; + whal_Error err; + uint8_t val = BMI270_CMD_SOFT_RESET; + + if (!dev || !dev->cfg) { + return WHAL_EINVAL; + } + + cfg = (whal_Bmi270_Cfg *)dev->cfg; + + err = whal_I2c_StartCom(cfg->i2c, cfg->comCfg); + if (err) + return err; + + err = whal_I2c_WriteReg(cfg->i2c, BMI270_REG_CMD, &val, 1); + whal_I2c_EndCom(cfg->i2c); + + return err; +} + +whal_Error whal_Bmi270_Read(whal_Sensor *dev, void *data) +{ + whal_Bmi270_Cfg *cfg; + whal_Bmi270_Data *out; + uint8_t raw[BMI270_DATA_LEN]; + whal_Error err; + + if (!dev || !dev->cfg || !data) { + return WHAL_EINVAL; + } + + cfg = (whal_Bmi270_Cfg *)dev->cfg; + out = (whal_Bmi270_Data *)data; + + err = whal_I2c_StartCom(cfg->i2c, cfg->comCfg); + if (err) + return err; + + /* Burst read accel + gyro data (0x0C through 0x17) */ + err = whal_I2c_ReadReg(cfg->i2c, BMI270_REG_DATA_ACC_X_LSB, + raw, BMI270_DATA_LEN); + + whal_I2c_EndCom(cfg->i2c); + + if (err) + return err; + + /* Parse little-endian 16-bit values */ + out->accelX = (int16_t)(raw[0] | (raw[1] << 8)); + out->accelY = (int16_t)(raw[2] | (raw[3] << 8)); + out->accelZ = (int16_t)(raw[4] | (raw[5] << 8)); + out->gyroX = (int16_t)(raw[6] | (raw[7] << 8)); + out->gyroY = (int16_t)(raw[8] | (raw[9] << 8)); + out->gyroZ = (int16_t)(raw[10] | (raw[11] << 8)); + + return WHAL_SUCCESS; +} + +const whal_SensorDriver whal_Bmi270_Driver = { + .Init = whal_Bmi270_Init, + .Deinit = whal_Bmi270_Deinit, + .Read = whal_Bmi270_Read, +}; diff --git a/src/sensor/imu/bmi270_config_data.c b/src/sensor/imu/bmi270_config_data.c new file mode 100644 index 0000000..ec55fe1 --- /dev/null +++ b/src/sensor/imu/bmi270_config_data.c @@ -0,0 +1,563 @@ +/* + * BMI270 configuration data blob. + * + * Extracted from the Bosch Sensortec BMI270_SensorAPI repository. + * Original license: + * + * ** + * * Copyright (c) 2023 Bosch Sensortec GmbH. All rights reserved. + * * + * * BSD-3-Clause + * * + * * Redistribution and use in source and binary forms, with or without + * * modification, are permitted provided that the following conditions are met: + * * + * * 1. Redistributions of source code must retain the above copyright + * * notice, this list of conditions and the following disclaimer. + * * + * * 2. Redistributions in binary form must reproduce the above copyright + * * notice, this list of conditions and the following disclaimer in the + * * documentation and/or other materials provided with the distribution. + * * + * * 3. Neither the name of the copyright holder nor the names of its + * * contributors may be used to endorse or promote products derived from + * * this software without specific prior written permission. + * * + * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * * POSSIBILITY OF SUCH DAMAGE. + * * + * * @file bmi270.c + * * @date 2023-05-03 + * * @version v2.86.1 + * * + */ + +#include + +#include + +const uint8_t whal_bmi270_config_data[WHAL_BMI270_CONFIG_DATA_SZ] = { + 0xc8, 0x2e, 0x00, 0x2e, 0x80, 0x2e, 0x3d, 0xb1, 0xc8, 0x2e, 0x00, 0x2e, 0x80, 0x2e, 0x91, 0x03, + 0x80, 0x2e, 0xbc, 0xb0, 0x80, 0x2e, 0xa3, 0x03, 0xc8, 0x2e, 0x00, 0x2e, 0x80, 0x2e, 0x00, 0xb0, + 0x50, 0x30, 0x21, 0x2e, 0x59, 0xf5, 0x10, 0x30, 0x21, 0x2e, 0x6a, 0xf5, 0x80, 0x2e, 0x3b, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x19, 0x01, 0x00, 0x22, 0x00, 0x75, 0x00, 0x00, 0x10, 0x00, 0x10, + 0xd1, 0x00, 0xb3, 0x43, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, + 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, + 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, + 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, + 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, + 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, + 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, + 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, + 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, + 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, + 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0xe0, 0x5f, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x92, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x19, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0xe0, 0xaa, 0x38, + 0x05, 0xe0, 0x90, 0x30, 0xfa, 0x00, 0x96, 0x00, 0x4b, 0x09, 0x11, 0x00, 0x11, 0x00, 0x02, 0x00, + 0x2d, 0x01, 0xd4, 0x7b, 0x3b, 0x01, 0xdb, 0x7a, 0x04, 0x00, 0x3f, 0x7b, 0xcd, 0x6c, 0xc3, 0x04, + 0x85, 0x09, 0xc3, 0x04, 0xec, 0xe6, 0x0c, 0x46, 0x01, 0x00, 0x27, 0x00, 0x19, 0x00, 0x96, 0x00, + 0xa0, 0x00, 0x01, 0x00, 0x0c, 0x00, 0xf0, 0x3c, 0x00, 0x01, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x32, 0x00, 0x05, 0x00, 0xee, 0x06, 0x04, 0x00, 0xc8, 0x00, 0x00, 0x00, + 0x04, 0x00, 0xa8, 0x05, 0xee, 0x06, 0x00, 0x04, 0xbc, 0x02, 0xb3, 0x00, 0x85, 0x07, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xb4, 0x00, 0x01, 0x00, 0xb9, 0x00, 0x01, 0x00, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x80, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x2e, 0x00, 0xc1, 0xfd, 0x2d, + 0xde, 0x00, 0xeb, 0x00, 0xda, 0x00, 0x00, 0x0c, 0xff, 0x0f, 0x00, 0x04, 0xc0, 0x00, 0x5b, 0xf5, + 0xc9, 0x01, 0x1e, 0xf2, 0x80, 0x00, 0x3f, 0xff, 0x19, 0xf4, 0x58, 0xf5, 0x66, 0xf5, 0x64, 0xf5, + 0xc0, 0xf1, 0xf0, 0x00, 0xe0, 0x00, 0xcd, 0x01, 0xd3, 0x01, 0xdb, 0x01, 0xff, 0x7f, 0xff, 0x01, + 0xe4, 0x00, 0x74, 0xf7, 0xf3, 0x00, 0xfa, 0x00, 0xff, 0x3f, 0xca, 0x03, 0x6c, 0x38, 0x56, 0xfe, + 0x44, 0xfd, 0xbc, 0x02, 0xf9, 0x06, 0x00, 0xfc, 0x12, 0x02, 0xae, 0x01, 0x58, 0xfa, 0x9a, 0xfd, + 0x77, 0x05, 0xbb, 0x02, 0x96, 0x01, 0x95, 0x01, 0x7f, 0x01, 0x82, 0x01, 0x89, 0x01, 0x87, 0x01, + 0x88, 0x01, 0x8a, 0x01, 0x8c, 0x01, 0x8f, 0x01, 0x8d, 0x01, 0x92, 0x01, 0x91, 0x01, 0xdd, 0x00, + 0x9f, 0x01, 0x7e, 0x01, 0xdb, 0x00, 0xb6, 0x01, 0x70, 0x69, 0x26, 0xd3, 0x9c, 0x07, 0x1f, 0x05, + 0x9d, 0x00, 0x00, 0x08, 0xbc, 0x05, 0x37, 0xfa, 0xa2, 0x01, 0xaa, 0x01, 0xa1, 0x01, 0xa8, 0x01, + 0xa0, 0x01, 0xa8, 0x05, 0xb4, 0x01, 0xb4, 0x01, 0xce, 0x00, 0xd0, 0x00, 0xfc, 0x00, 0xc5, 0x01, + 0xff, 0xfb, 0xb1, 0x00, 0x00, 0x38, 0x00, 0x30, 0xfd, 0xf5, 0xfc, 0xf5, 0xcd, 0x01, 0xa0, 0x00, + 0x5f, 0xff, 0x00, 0x40, 0xff, 0x00, 0x00, 0x80, 0x6d, 0x0f, 0xeb, 0x00, 0x7f, 0xff, 0xc2, 0xf5, + 0x68, 0xf7, 0xb3, 0xf1, 0x67, 0x0f, 0x5b, 0x0f, 0x61, 0x0f, 0x80, 0x0f, 0x58, 0xf7, 0x5b, 0xf7, + 0x83, 0x0f, 0x86, 0x00, 0x72, 0x0f, 0x85, 0x0f, 0xc6, 0xf1, 0x7f, 0x0f, 0x6c, 0xf7, 0x00, 0xe0, + 0x00, 0xff, 0xd1, 0xf5, 0x87, 0x0f, 0x8a, 0x0f, 0xff, 0x03, 0xf0, 0x3f, 0x8b, 0x00, 0x8e, 0x00, + 0x90, 0x00, 0xb9, 0x00, 0x2d, 0xf5, 0xca, 0xf5, 0xcb, 0x01, 0x20, 0xf2, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x50, 0x98, 0x2e, 0xd7, 0x0e, 0x50, 0x32, + 0x98, 0x2e, 0xfa, 0x03, 0x00, 0x30, 0xf0, 0x7f, 0x00, 0x2e, 0x00, 0x2e, 0xd0, 0x2e, 0x00, 0x2e, + 0x01, 0x80, 0x08, 0xa2, 0xfb, 0x2f, 0x98, 0x2e, 0xba, 0x03, 0x21, 0x2e, 0x19, 0x00, 0x01, 0x2e, + 0xee, 0x00, 0x00, 0xb2, 0x07, 0x2f, 0x01, 0x2e, 0x19, 0x00, 0x00, 0xb2, 0x03, 0x2f, 0x01, 0x50, + 0x03, 0x52, 0x98, 0x2e, 0x07, 0xcc, 0x01, 0x2e, 0xdd, 0x00, 0x00, 0xb2, 0x27, 0x2f, 0x05, 0x2e, + 0x8a, 0x00, 0x05, 0x52, 0x98, 0x2e, 0xc7, 0xc1, 0x03, 0x2e, 0xe9, 0x00, 0x40, 0xb2, 0xf0, 0x7f, + 0x08, 0x2f, 0x01, 0x2e, 0x19, 0x00, 0x00, 0xb2, 0x04, 0x2f, 0x00, 0x30, 0x21, 0x2e, 0xe9, 0x00, + 0x98, 0x2e, 0xb4, 0xb1, 0x01, 0x2e, 0x18, 0x00, 0x00, 0xb2, 0x10, 0x2f, 0x05, 0x50, 0x98, 0x2e, + 0x4d, 0xc3, 0x05, 0x50, 0x98, 0x2e, 0x5a, 0xc7, 0x98, 0x2e, 0xf9, 0xb4, 0x98, 0x2e, 0x54, 0xb2, + 0x98, 0x2e, 0x67, 0xb6, 0x98, 0x2e, 0x17, 0xb2, 0x10, 0x30, 0x21, 0x2e, 0x77, 0x00, 0x01, 0x2e, + 0xef, 0x00, 0x00, 0xb2, 0x04, 0x2f, 0x98, 0x2e, 0x7a, 0xb7, 0x00, 0x30, 0x21, 0x2e, 0xef, 0x00, + 0x01, 0x2e, 0xd4, 0x00, 0x04, 0xae, 0x0b, 0x2f, 0x01, 0x2e, 0xdd, 0x00, 0x00, 0xb2, 0x07, 0x2f, + 0x05, 0x52, 0x98, 0x2e, 0x8e, 0x0e, 0x00, 0xb2, 0x02, 0x2f, 0x10, 0x30, 0x21, 0x2e, 0x7d, 0x00, + 0x01, 0x2e, 0x7d, 0x00, 0x00, 0x90, 0x90, 0x2e, 0xf1, 0x02, 0x01, 0x2e, 0xd7, 0x00, 0x00, 0xb2, + 0x04, 0x2f, 0x98, 0x2e, 0x2f, 0x0e, 0x00, 0x30, 0x21, 0x2e, 0x7b, 0x00, 0x01, 0x2e, 0x7b, 0x00, + 0x00, 0xb2, 0x12, 0x2f, 0x01, 0x2e, 0xd4, 0x00, 0x00, 0x90, 0x02, 0x2f, 0x98, 0x2e, 0x1f, 0x0e, + 0x09, 0x2d, 0x98, 0x2e, 0x81, 0x0d, 0x01, 0x2e, 0xd4, 0x00, 0x04, 0x90, 0x02, 0x2f, 0x50, 0x32, + 0x98, 0x2e, 0xfa, 0x03, 0x00, 0x30, 0x21, 0x2e, 0x7b, 0x00, 0x01, 0x2e, 0x7c, 0x00, 0x00, 0xb2, + 0x90, 0x2e, 0x09, 0x03, 0x01, 0x2e, 0x7c, 0x00, 0x01, 0x31, 0x01, 0x08, 0x00, 0xb2, 0x04, 0x2f, + 0x98, 0x2e, 0x47, 0xcb, 0x10, 0x30, 0x21, 0x2e, 0x77, 0x00, 0x81, 0x30, 0x01, 0x2e, 0x7c, 0x00, + 0x01, 0x08, 0x00, 0xb2, 0x61, 0x2f, 0x03, 0x2e, 0x89, 0x00, 0x01, 0x2e, 0xd4, 0x00, 0x98, 0xbc, + 0x98, 0xb8, 0x05, 0xb2, 0x0f, 0x58, 0x23, 0x2f, 0x07, 0x90, 0x09, 0x54, 0x00, 0x30, 0x37, 0x2f, + 0x15, 0x41, 0x04, 0x41, 0xdc, 0xbe, 0x44, 0xbe, 0xdc, 0xba, 0x2c, 0x01, 0x61, 0x00, 0x0f, 0x56, + 0x4a, 0x0f, 0x0c, 0x2f, 0xd1, 0x42, 0x94, 0xb8, 0xc1, 0x42, 0x11, 0x30, 0x05, 0x2e, 0x6a, 0xf7, + 0x2c, 0xbd, 0x2f, 0xb9, 0x80, 0xb2, 0x08, 0x22, 0x98, 0x2e, 0xc3, 0xb7, 0x21, 0x2d, 0x61, 0x30, + 0x23, 0x2e, 0xd4, 0x00, 0x98, 0x2e, 0xc3, 0xb7, 0x00, 0x30, 0x21, 0x2e, 0x5a, 0xf5, 0x18, 0x2d, + 0xe1, 0x7f, 0x50, 0x30, 0x98, 0x2e, 0xfa, 0x03, 0x0f, 0x52, 0x07, 0x50, 0x50, 0x42, 0x70, 0x30, + 0x0d, 0x54, 0x42, 0x42, 0x7e, 0x82, 0xe2, 0x6f, 0x80, 0xb2, 0x42, 0x42, 0x05, 0x2f, 0x21, 0x2e, + 0xd4, 0x00, 0x10, 0x30, 0x98, 0x2e, 0xc3, 0xb7, 0x03, 0x2d, 0x60, 0x30, 0x21, 0x2e, 0xd4, 0x00, + 0x01, 0x2e, 0xd4, 0x00, 0x06, 0x90, 0x18, 0x2f, 0x01, 0x2e, 0x76, 0x00, 0x0b, 0x54, 0x07, 0x52, + 0xe0, 0x7f, 0x98, 0x2e, 0x7a, 0xc1, 0xe1, 0x6f, 0x08, 0x1a, 0x40, 0x30, 0x08, 0x2f, 0x21, 0x2e, + 0xd4, 0x00, 0x20, 0x30, 0x98, 0x2e, 0xaf, 0xb7, 0x50, 0x32, 0x98, 0x2e, 0xfa, 0x03, 0x05, 0x2d, + 0x98, 0x2e, 0x38, 0x0e, 0x00, 0x30, 0x21, 0x2e, 0xd4, 0x00, 0x00, 0x30, 0x21, 0x2e, 0x7c, 0x00, + 0x18, 0x2d, 0x01, 0x2e, 0xd4, 0x00, 0x03, 0xaa, 0x01, 0x2f, 0x98, 0x2e, 0x45, 0x0e, 0x01, 0x2e, + 0xd4, 0x00, 0x3f, 0x80, 0x03, 0xa2, 0x01, 0x2f, 0x00, 0x2e, 0x02, 0x2d, 0x98, 0x2e, 0x5b, 0x0e, + 0x30, 0x30, 0x98, 0x2e, 0xce, 0xb7, 0x00, 0x30, 0x21, 0x2e, 0x7d, 0x00, 0x50, 0x32, 0x98, 0x2e, + 0xfa, 0x03, 0x01, 0x2e, 0x77, 0x00, 0x00, 0xb2, 0x24, 0x2f, 0x98, 0x2e, 0xf5, 0xcb, 0x03, 0x2e, + 0xd5, 0x00, 0x11, 0x54, 0x01, 0x0a, 0xbc, 0x84, 0x83, 0x86, 0x21, 0x2e, 0xc9, 0x01, 0xe0, 0x40, + 0x13, 0x52, 0xc4, 0x40, 0x82, 0x40, 0xa8, 0xb9, 0x52, 0x42, 0x43, 0xbe, 0x53, 0x42, 0x04, 0x0a, + 0x50, 0x42, 0xe1, 0x7f, 0xf0, 0x31, 0x41, 0x40, 0xf2, 0x6f, 0x25, 0xbd, 0x08, 0x08, 0x02, 0x0a, + 0xd0, 0x7f, 0x98, 0x2e, 0xa8, 0xcf, 0x06, 0xbc, 0xd1, 0x6f, 0xe2, 0x6f, 0x08, 0x0a, 0x80, 0x42, + 0x98, 0x2e, 0x58, 0xb7, 0x00, 0x30, 0x21, 0x2e, 0xee, 0x00, 0x21, 0x2e, 0x77, 0x00, 0x21, 0x2e, + 0xdd, 0x00, 0x80, 0x2e, 0xf4, 0x01, 0x1a, 0x24, 0x22, 0x00, 0x80, 0x2e, 0xec, 0x01, 0x10, 0x50, + 0xfb, 0x7f, 0x98, 0x2e, 0xf3, 0x03, 0x57, 0x50, 0xfb, 0x6f, 0x01, 0x30, 0x71, 0x54, 0x11, 0x42, + 0x42, 0x0e, 0xfc, 0x2f, 0xc0, 0x2e, 0x01, 0x42, 0xf0, 0x5f, 0x80, 0x2e, 0x00, 0xc1, 0xfd, 0x2d, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9a, 0x01, 0x34, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x20, 0x50, 0xe7, 0x7f, 0xf6, 0x7f, 0x06, 0x32, 0x0f, 0x2e, 0x61, 0xf5, 0xfe, 0x09, + 0xc0, 0xb3, 0x04, 0x2f, 0x17, 0x30, 0x2f, 0x2e, 0xef, 0x00, 0x2d, 0x2e, 0x61, 0xf5, 0xf6, 0x6f, + 0xe7, 0x6f, 0xe0, 0x5f, 0xc8, 0x2e, 0x20, 0x50, 0xe7, 0x7f, 0xf6, 0x7f, 0x46, 0x30, 0x0f, 0x2e, + 0xa4, 0xf1, 0xbe, 0x09, 0x80, 0xb3, 0x06, 0x2f, 0x0d, 0x2e, 0xd4, 0x00, 0x84, 0xaf, 0x02, 0x2f, + 0x16, 0x30, 0x2d, 0x2e, 0x7b, 0x00, 0x86, 0x30, 0x2d, 0x2e, 0x60, 0xf5, 0xf6, 0x6f, 0xe7, 0x6f, + 0xe0, 0x5f, 0xc8, 0x2e, 0x01, 0x2e, 0x77, 0xf7, 0x09, 0xbc, 0x0f, 0xb8, 0x00, 0xb2, 0x10, 0x50, + 0xfb, 0x7f, 0x10, 0x30, 0x0b, 0x2f, 0x03, 0x2e, 0x8a, 0x00, 0x96, 0xbc, 0x9f, 0xb8, 0x40, 0xb2, + 0x05, 0x2f, 0x03, 0x2e, 0x68, 0xf7, 0x9e, 0xbc, 0x9f, 0xb8, 0x40, 0xb2, 0x07, 0x2f, 0x03, 0x2e, + 0x7e, 0x00, 0x41, 0x90, 0x01, 0x2f, 0x98, 0x2e, 0xdc, 0x03, 0x03, 0x2c, 0x00, 0x30, 0x21, 0x2e, + 0x7e, 0x00, 0xfb, 0x6f, 0xf0, 0x5f, 0xb8, 0x2e, 0x20, 0x50, 0xe0, 0x7f, 0xfb, 0x7f, 0x00, 0x2e, + 0x27, 0x50, 0x98, 0x2e, 0x3b, 0xc8, 0x29, 0x50, 0x98, 0x2e, 0xa7, 0xc8, 0x01, 0x50, 0x98, 0x2e, + 0x55, 0xcc, 0xe1, 0x6f, 0x2b, 0x50, 0x98, 0x2e, 0xe0, 0xc9, 0xfb, 0x6f, 0x00, 0x30, 0xe0, 0x5f, + 0x21, 0x2e, 0x7e, 0x00, 0xb8, 0x2e, 0x73, 0x50, 0x01, 0x30, 0x57, 0x54, 0x11, 0x42, 0x42, 0x0e, + 0xfc, 0x2f, 0xb8, 0x2e, 0x21, 0x2e, 0x59, 0xf5, 0x10, 0x30, 0xc0, 0x2e, 0x21, 0x2e, 0x4a, 0xf1, + 0x90, 0x50, 0xf7, 0x7f, 0xe6, 0x7f, 0xd5, 0x7f, 0xc4, 0x7f, 0xb3, 0x7f, 0xa1, 0x7f, 0x90, 0x7f, + 0x82, 0x7f, 0x7b, 0x7f, 0x98, 0x2e, 0x35, 0xb7, 0x00, 0xb2, 0x90, 0x2e, 0x97, 0xb0, 0x03, 0x2e, + 0x8f, 0x00, 0x07, 0x2e, 0x91, 0x00, 0x05, 0x2e, 0xb1, 0x00, 0x3f, 0xba, 0x9f, 0xb8, 0x01, 0x2e, + 0xb1, 0x00, 0xa3, 0xbd, 0x4c, 0x0a, 0x05, 0x2e, 0xb1, 0x00, 0x04, 0xbe, 0xbf, 0xb9, 0xcb, 0x0a, + 0x4f, 0xba, 0x22, 0xbd, 0x01, 0x2e, 0xb3, 0x00, 0xdc, 0x0a, 0x2f, 0xb9, 0x03, 0x2e, 0xb8, 0x00, + 0x0a, 0xbe, 0x9a, 0x0a, 0xcf, 0xb9, 0x9b, 0xbc, 0x01, 0x2e, 0x97, 0x00, 0x9f, 0xb8, 0x93, 0x0a, + 0x0f, 0xbc, 0x91, 0x0a, 0x0f, 0xb8, 0x90, 0x0a, 0x25, 0x2e, 0x18, 0x00, 0x05, 0x2e, 0xc1, 0xf5, + 0x2e, 0xbd, 0x2e, 0xb9, 0x01, 0x2e, 0x19, 0x00, 0x31, 0x30, 0x8a, 0x04, 0x00, 0x90, 0x07, 0x2f, + 0x01, 0x2e, 0xd4, 0x00, 0x04, 0xa2, 0x03, 0x2f, 0x01, 0x2e, 0x18, 0x00, 0x00, 0xb2, 0x0c, 0x2f, + 0x19, 0x50, 0x05, 0x52, 0x98, 0x2e, 0x4d, 0xb7, 0x05, 0x2e, 0x78, 0x00, 0x80, 0x90, 0x10, 0x30, + 0x01, 0x2f, 0x21, 0x2e, 0x78, 0x00, 0x25, 0x2e, 0xdd, 0x00, 0x98, 0x2e, 0x3e, 0xb7, 0x00, 0xb2, + 0x02, 0x30, 0x01, 0x30, 0x04, 0x2f, 0x01, 0x2e, 0x19, 0x00, 0x00, 0xb2, 0x00, 0x2f, 0x21, 0x30, + 0x01, 0x2e, 0xea, 0x00, 0x08, 0x1a, 0x0e, 0x2f, 0x23, 0x2e, 0xea, 0x00, 0x33, 0x30, 0x1b, 0x50, + 0x0b, 0x09, 0x01, 0x40, 0x17, 0x56, 0x46, 0xbe, 0x4b, 0x08, 0x4c, 0x0a, 0x01, 0x42, 0x0a, 0x80, + 0x15, 0x52, 0x01, 0x42, 0x00, 0x2e, 0x01, 0x2e, 0x18, 0x00, 0x00, 0xb2, 0x1f, 0x2f, 0x03, 0x2e, + 0xc0, 0xf5, 0xf0, 0x30, 0x48, 0x08, 0x47, 0xaa, 0x74, 0x30, 0x07, 0x2e, 0x7a, 0x00, 0x61, 0x22, + 0x4b, 0x1a, 0x05, 0x2f, 0x07, 0x2e, 0x66, 0xf5, 0xbf, 0xbd, 0xbf, 0xb9, 0xc0, 0x90, 0x0b, 0x2f, + 0x1d, 0x56, 0x2b, 0x30, 0xd2, 0x42, 0xdb, 0x42, 0x01, 0x04, 0xc2, 0x42, 0x04, 0xbd, 0xfe, 0x80, + 0x81, 0x84, 0x23, 0x2e, 0x7a, 0x00, 0x02, 0x42, 0x02, 0x32, 0x25, 0x2e, 0x62, 0xf5, 0x05, 0x2e, + 0xd6, 0x00, 0x81, 0x84, 0x25, 0x2e, 0xd6, 0x00, 0x02, 0x31, 0x25, 0x2e, 0x60, 0xf5, 0x05, 0x2e, + 0x8a, 0x00, 0x0b, 0x50, 0x90, 0x08, 0x80, 0xb2, 0x0b, 0x2f, 0x05, 0x2e, 0xca, 0xf5, 0xf0, 0x3e, + 0x90, 0x08, 0x25, 0x2e, 0xca, 0xf5, 0x05, 0x2e, 0x59, 0xf5, 0xe0, 0x3f, 0x90, 0x08, 0x25, 0x2e, + 0x59, 0xf5, 0x90, 0x6f, 0xa1, 0x6f, 0xb3, 0x6f, 0xc4, 0x6f, 0xd5, 0x6f, 0xe6, 0x6f, 0xf7, 0x6f, + 0x7b, 0x6f, 0x82, 0x6f, 0x70, 0x5f, 0xc8, 0x2e, 0xc0, 0x50, 0x90, 0x7f, 0xe5, 0x7f, 0xd4, 0x7f, + 0xc3, 0x7f, 0xb1, 0x7f, 0xa2, 0x7f, 0x87, 0x7f, 0xf6, 0x7f, 0x7b, 0x7f, 0x00, 0x2e, 0x01, 0x2e, + 0x60, 0xf5, 0x60, 0x7f, 0x98, 0x2e, 0x35, 0xb7, 0x02, 0x30, 0x63, 0x6f, 0x15, 0x52, 0x50, 0x7f, + 0x62, 0x7f, 0x5a, 0x2c, 0x02, 0x32, 0x1a, 0x09, 0x00, 0xb3, 0x14, 0x2f, 0x00, 0xb2, 0x03, 0x2f, + 0x09, 0x2e, 0x18, 0x00, 0x00, 0x91, 0x0c, 0x2f, 0x43, 0x7f, 0x98, 0x2e, 0x97, 0xb7, 0x1f, 0x50, + 0x02, 0x8a, 0x02, 0x32, 0x04, 0x30, 0x25, 0x2e, 0x64, 0xf5, 0x15, 0x52, 0x50, 0x6f, 0x43, 0x6f, + 0x44, 0x43, 0x25, 0x2e, 0x60, 0xf5, 0xd9, 0x08, 0xc0, 0xb2, 0x36, 0x2f, 0x98, 0x2e, 0x3e, 0xb7, + 0x00, 0xb2, 0x06, 0x2f, 0x01, 0x2e, 0x19, 0x00, 0x00, 0xb2, 0x02, 0x2f, 0x50, 0x6f, 0x00, 0x90, + 0x0a, 0x2f, 0x01, 0x2e, 0x79, 0x00, 0x00, 0x90, 0x19, 0x2f, 0x10, 0x30, 0x21, 0x2e, 0x79, 0x00, + 0x00, 0x30, 0x98, 0x2e, 0xdc, 0x03, 0x13, 0x2d, 0x01, 0x2e, 0xc3, 0xf5, 0x0c, 0xbc, 0x0f, 0xb8, + 0x12, 0x30, 0x10, 0x04, 0x03, 0xb0, 0x26, 0x25, 0x21, 0x50, 0x03, 0x52, 0x98, 0x2e, 0x4d, 0xb7, + 0x10, 0x30, 0x21, 0x2e, 0xee, 0x00, 0x02, 0x30, 0x60, 0x7f, 0x25, 0x2e, 0x79, 0x00, 0x60, 0x6f, + 0x00, 0x90, 0x05, 0x2f, 0x00, 0x30, 0x21, 0x2e, 0xea, 0x00, 0x15, 0x50, 0x21, 0x2e, 0x64, 0xf5, + 0x15, 0x52, 0x23, 0x2e, 0x60, 0xf5, 0x02, 0x32, 0x50, 0x6f, 0x00, 0x90, 0x02, 0x2f, 0x03, 0x30, + 0x27, 0x2e, 0x78, 0x00, 0x07, 0x2e, 0x60, 0xf5, 0x1a, 0x09, 0x00, 0x91, 0xa3, 0x2f, 0x19, 0x09, + 0x00, 0x91, 0xa0, 0x2f, 0x90, 0x6f, 0xa2, 0x6f, 0xb1, 0x6f, 0xc3, 0x6f, 0xd4, 0x6f, 0xe5, 0x6f, + 0x7b, 0x6f, 0xf6, 0x6f, 0x87, 0x6f, 0x40, 0x5f, 0xc8, 0x2e, 0xc0, 0x50, 0xe7, 0x7f, 0xf6, 0x7f, + 0x26, 0x30, 0x0f, 0x2e, 0x61, 0xf5, 0x2f, 0x2e, 0x7c, 0x00, 0x0f, 0x2e, 0x7c, 0x00, 0xbe, 0x09, + 0xa2, 0x7f, 0x80, 0x7f, 0x80, 0xb3, 0xd5, 0x7f, 0xc4, 0x7f, 0xb3, 0x7f, 0x91, 0x7f, 0x7b, 0x7f, + 0x0b, 0x2f, 0x23, 0x50, 0x1a, 0x25, 0x12, 0x40, 0x42, 0x7f, 0x74, 0x82, 0x12, 0x40, 0x52, 0x7f, + 0x00, 0x2e, 0x00, 0x40, 0x60, 0x7f, 0x98, 0x2e, 0x6a, 0xd6, 0x81, 0x30, 0x01, 0x2e, 0x7c, 0x00, + 0x01, 0x08, 0x00, 0xb2, 0x42, 0x2f, 0x03, 0x2e, 0x89, 0x00, 0x01, 0x2e, 0x89, 0x00, 0x97, 0xbc, + 0x06, 0xbc, 0x9f, 0xb8, 0x0f, 0xb8, 0x00, 0x90, 0x23, 0x2e, 0xd8, 0x00, 0x10, 0x30, 0x01, 0x30, + 0x2a, 0x2f, 0x03, 0x2e, 0xd4, 0x00, 0x44, 0xb2, 0x05, 0x2f, 0x47, 0xb2, 0x00, 0x30, 0x2d, 0x2f, + 0x21, 0x2e, 0x7c, 0x00, 0x2b, 0x2d, 0x03, 0x2e, 0xfd, 0xf5, 0x9e, 0xbc, 0x9f, 0xb8, 0x40, 0x90, + 0x14, 0x2f, 0x03, 0x2e, 0xfc, 0xf5, 0x99, 0xbc, 0x9f, 0xb8, 0x40, 0x90, 0x0e, 0x2f, 0x03, 0x2e, + 0x49, 0xf1, 0x25, 0x54, 0x4a, 0x08, 0x40, 0x90, 0x08, 0x2f, 0x98, 0x2e, 0x35, 0xb7, 0x00, 0xb2, + 0x10, 0x30, 0x03, 0x2f, 0x50, 0x30, 0x21, 0x2e, 0xd4, 0x00, 0x10, 0x2d, 0x98, 0x2e, 0xaf, 0xb7, + 0x00, 0x30, 0x21, 0x2e, 0x7c, 0x00, 0x0a, 0x2d, 0x05, 0x2e, 0x69, 0xf7, 0x2d, 0xbd, 0x2f, 0xb9, + 0x80, 0xb2, 0x01, 0x2f, 0x21, 0x2e, 0x7d, 0x00, 0x23, 0x2e, 0x7c, 0x00, 0xe0, 0x31, 0x21, 0x2e, + 0x61, 0xf5, 0xf6, 0x6f, 0xe7, 0x6f, 0x80, 0x6f, 0xa2, 0x6f, 0xb3, 0x6f, 0xc4, 0x6f, 0xd5, 0x6f, + 0x7b, 0x6f, 0x91, 0x6f, 0x40, 0x5f, 0xc8, 0x2e, 0x60, 0x51, 0x0a, 0x25, 0x36, 0x88, 0xf4, 0x7f, + 0xeb, 0x7f, 0x00, 0x32, 0x31, 0x52, 0x32, 0x30, 0x13, 0x30, 0x98, 0x2e, 0x15, 0xcb, 0x0a, 0x25, + 0x33, 0x84, 0xd2, 0x7f, 0x43, 0x30, 0x05, 0x50, 0x2d, 0x52, 0x98, 0x2e, 0x95, 0xc1, 0xd2, 0x6f, + 0x27, 0x52, 0x98, 0x2e, 0xd7, 0xc7, 0x2a, 0x25, 0xb0, 0x86, 0xc0, 0x7f, 0xd3, 0x7f, 0xaf, 0x84, + 0x29, 0x50, 0xf1, 0x6f, 0x98, 0x2e, 0x4d, 0xc8, 0x2a, 0x25, 0xae, 0x8a, 0xaa, 0x88, 0xf2, 0x6e, + 0x2b, 0x50, 0xc1, 0x6f, 0xd3, 0x6f, 0xf4, 0x7f, 0x98, 0x2e, 0xb6, 0xc8, 0xe0, 0x6e, 0x00, 0xb2, + 0x32, 0x2f, 0x33, 0x54, 0x83, 0x86, 0xf1, 0x6f, 0xc3, 0x7f, 0x04, 0x30, 0x30, 0x30, 0xf4, 0x7f, + 0xd0, 0x7f, 0xb2, 0x7f, 0xe3, 0x30, 0xc5, 0x6f, 0x56, 0x40, 0x45, 0x41, 0x28, 0x08, 0x03, 0x14, + 0x0e, 0xb4, 0x08, 0xbc, 0x82, 0x40, 0x10, 0x0a, 0x2f, 0x54, 0x26, 0x05, 0x91, 0x7f, 0x44, 0x28, + 0xa3, 0x7f, 0x98, 0x2e, 0xd9, 0xc0, 0x08, 0xb9, 0x33, 0x30, 0x53, 0x09, 0xc1, 0x6f, 0xd3, 0x6f, + 0xf4, 0x6f, 0x83, 0x17, 0x47, 0x40, 0x6c, 0x15, 0xb2, 0x6f, 0xbe, 0x09, 0x75, 0x0b, 0x90, 0x42, + 0x45, 0x42, 0x51, 0x0e, 0x32, 0xbc, 0x02, 0x89, 0xa1, 0x6f, 0x7e, 0x86, 0xf4, 0x7f, 0xd0, 0x7f, + 0xb2, 0x7f, 0x04, 0x30, 0x91, 0x6f, 0xd6, 0x2f, 0xeb, 0x6f, 0xa0, 0x5e, 0xb8, 0x2e, 0x03, 0x2e, + 0x97, 0x00, 0x1b, 0xbc, 0x60, 0x50, 0x9f, 0xbc, 0x0c, 0xb8, 0xf0, 0x7f, 0x40, 0xb2, 0xeb, 0x7f, + 0x2b, 0x2f, 0x03, 0x2e, 0x7f, 0x00, 0x41, 0x40, 0x01, 0x2e, 0xc8, 0x00, 0x01, 0x1a, 0x11, 0x2f, + 0x37, 0x58, 0x23, 0x2e, 0xc8, 0x00, 0x10, 0x41, 0xa0, 0x7f, 0x38, 0x81, 0x01, 0x41, 0xd0, 0x7f, + 0xb1, 0x7f, 0x98, 0x2e, 0x64, 0xcf, 0xd0, 0x6f, 0x07, 0x80, 0xa1, 0x6f, 0x11, 0x42, 0x00, 0x2e, + 0xb1, 0x6f, 0x01, 0x42, 0x11, 0x30, 0x01, 0x2e, 0xfc, 0x00, 0x00, 0xa8, 0x03, 0x30, 0xcb, 0x22, + 0x4a, 0x25, 0x01, 0x2e, 0x7f, 0x00, 0x3c, 0x89, 0x35, 0x52, 0x05, 0x54, 0x98, 0x2e, 0xc4, 0xce, + 0xc1, 0x6f, 0xf0, 0x6f, 0x98, 0x2e, 0x95, 0xcf, 0x04, 0x2d, 0x01, 0x30, 0xf0, 0x6f, 0x98, 0x2e, + 0x95, 0xcf, 0xeb, 0x6f, 0xa0, 0x5f, 0xb8, 0x2e, 0x03, 0x2e, 0xb3, 0x00, 0x02, 0x32, 0xf0, 0x30, + 0x03, 0x31, 0x30, 0x50, 0x8a, 0x08, 0x08, 0x08, 0xcb, 0x08, 0xe0, 0x7f, 0x80, 0xb2, 0xf3, 0x7f, + 0xdb, 0x7f, 0x25, 0x2f, 0x03, 0x2e, 0xca, 0x00, 0x41, 0x90, 0x04, 0x2f, 0x01, 0x30, 0x23, 0x2e, + 0xca, 0x00, 0x98, 0x2e, 0x3f, 0x03, 0xc0, 0xb2, 0x05, 0x2f, 0x03, 0x2e, 0xda, 0x00, 0x00, 0x30, + 0x41, 0x04, 0x23, 0x2e, 0xda, 0x00, 0x98, 0x2e, 0x92, 0xb2, 0x10, 0x25, 0xf0, 0x6f, 0x00, 0xb2, + 0x05, 0x2f, 0x01, 0x2e, 0xda, 0x00, 0x02, 0x30, 0x10, 0x04, 0x21, 0x2e, 0xda, 0x00, 0x40, 0xb2, + 0x01, 0x2f, 0x23, 0x2e, 0xc8, 0x01, 0xdb, 0x6f, 0xe0, 0x6f, 0xd0, 0x5f, 0x80, 0x2e, 0x95, 0xcf, + 0x01, 0x30, 0xe0, 0x6f, 0x98, 0x2e, 0x95, 0xcf, 0x11, 0x30, 0x23, 0x2e, 0xca, 0x00, 0xdb, 0x6f, + 0xd0, 0x5f, 0xb8, 0x2e, 0xd0, 0x50, 0x0a, 0x25, 0x33, 0x84, 0x55, 0x50, 0xd2, 0x7f, 0xe2, 0x7f, + 0x03, 0x8c, 0xc0, 0x7f, 0xbb, 0x7f, 0x00, 0x30, 0x05, 0x5a, 0x39, 0x54, 0x51, 0x41, 0xa5, 0x7f, + 0x96, 0x7f, 0x80, 0x7f, 0x98, 0x2e, 0xd9, 0xc0, 0x05, 0x30, 0xf5, 0x7f, 0x20, 0x25, 0x91, 0x6f, + 0x3b, 0x58, 0x3d, 0x5c, 0x3b, 0x56, 0x98, 0x2e, 0x67, 0xcc, 0xc1, 0x6f, 0xd5, 0x6f, 0x52, 0x40, + 0x50, 0x43, 0xc1, 0x7f, 0xd5, 0x7f, 0x10, 0x25, 0x98, 0x2e, 0xfe, 0xc9, 0x10, 0x25, 0x98, 0x2e, + 0x74, 0xc0, 0x86, 0x6f, 0x30, 0x28, 0x92, 0x6f, 0x82, 0x8c, 0xa5, 0x6f, 0x6f, 0x52, 0x69, 0x0e, + 0x39, 0x54, 0xdb, 0x2f, 0x19, 0xa0, 0x15, 0x30, 0x03, 0x2f, 0x00, 0x30, 0x21, 0x2e, 0x81, 0x01, + 0x0a, 0x2d, 0x01, 0x2e, 0x81, 0x01, 0x05, 0x28, 0x42, 0x36, 0x21, 0x2e, 0x81, 0x01, 0x02, 0x0e, + 0x01, 0x2f, 0x98, 0x2e, 0xf3, 0x03, 0x57, 0x50, 0x12, 0x30, 0x01, 0x40, 0x98, 0x2e, 0xfe, 0xc9, + 0x51, 0x6f, 0x0b, 0x5c, 0x8e, 0x0e, 0x3b, 0x6f, 0x57, 0x58, 0x02, 0x30, 0x21, 0x2e, 0x95, 0x01, + 0x45, 0x6f, 0x2a, 0x8d, 0xd2, 0x7f, 0xcb, 0x7f, 0x13, 0x2f, 0x02, 0x30, 0x3f, 0x50, 0xd2, 0x7f, + 0xa8, 0x0e, 0x0e, 0x2f, 0xc0, 0x6f, 0x53, 0x54, 0x02, 0x00, 0x51, 0x54, 0x42, 0x0e, 0x10, 0x30, + 0x59, 0x52, 0x02, 0x30, 0x01, 0x2f, 0x00, 0x2e, 0x03, 0x2d, 0x50, 0x42, 0x42, 0x42, 0x12, 0x30, + 0xd2, 0x7f, 0x80, 0xb2, 0x03, 0x2f, 0x00, 0x30, 0x21, 0x2e, 0x80, 0x01, 0x12, 0x2d, 0x01, 0x2e, + 0xc9, 0x00, 0x02, 0x80, 0x05, 0x2e, 0x80, 0x01, 0x11, 0x30, 0x91, 0x28, 0x00, 0x40, 0x25, 0x2e, + 0x80, 0x01, 0x10, 0x0e, 0x05, 0x2f, 0x01, 0x2e, 0x7f, 0x01, 0x01, 0x90, 0x01, 0x2f, 0x98, 0x2e, + 0xf3, 0x03, 0x00, 0x2e, 0xa0, 0x41, 0x01, 0x90, 0xa6, 0x7f, 0x90, 0x2e, 0xe3, 0xb4, 0x01, 0x2e, + 0x95, 0x01, 0x00, 0xa8, 0x90, 0x2e, 0xe3, 0xb4, 0x5b, 0x54, 0x95, 0x80, 0x82, 0x40, 0x80, 0xb2, + 0x02, 0x40, 0x2d, 0x8c, 0x3f, 0x52, 0x96, 0x7f, 0x90, 0x2e, 0xc2, 0xb3, 0x29, 0x0e, 0x76, 0x2f, + 0x01, 0x2e, 0xc9, 0x00, 0x00, 0x40, 0x81, 0x28, 0x45, 0x52, 0xb3, 0x30, 0x98, 0x2e, 0x0f, 0xca, + 0x5d, 0x54, 0x80, 0x7f, 0x00, 0x2e, 0xa1, 0x40, 0x72, 0x7f, 0x82, 0x80, 0x82, 0x40, 0x60, 0x7f, + 0x98, 0x2e, 0xfe, 0xc9, 0x10, 0x25, 0x98, 0x2e, 0x74, 0xc0, 0x62, 0x6f, 0x05, 0x30, 0x87, 0x40, + 0xc0, 0x91, 0x04, 0x30, 0x05, 0x2f, 0x05, 0x2e, 0x83, 0x01, 0x80, 0xb2, 0x14, 0x30, 0x00, 0x2f, + 0x04, 0x30, 0x05, 0x2e, 0xc9, 0x00, 0x73, 0x6f, 0x81, 0x40, 0xe2, 0x40, 0x69, 0x04, 0x11, 0x0f, + 0xe1, 0x40, 0x16, 0x30, 0xfe, 0x29, 0xcb, 0x40, 0x02, 0x2f, 0x83, 0x6f, 0x83, 0x0f, 0x22, 0x2f, + 0x47, 0x56, 0x13, 0x0f, 0x12, 0x30, 0x77, 0x2f, 0x49, 0x54, 0x42, 0x0e, 0x12, 0x30, 0x73, 0x2f, + 0x00, 0x91, 0x0a, 0x2f, 0x01, 0x2e, 0x8b, 0x01, 0x19, 0xa8, 0x02, 0x30, 0x6c, 0x2f, 0x63, 0x50, + 0x00, 0x2e, 0x17, 0x42, 0x05, 0x42, 0x68, 0x2c, 0x12, 0x30, 0x0b, 0x25, 0x08, 0x0f, 0x50, 0x30, + 0x02, 0x2f, 0x21, 0x2e, 0x83, 0x01, 0x03, 0x2d, 0x40, 0x30, 0x21, 0x2e, 0x83, 0x01, 0x2b, 0x2e, + 0x85, 0x01, 0x5a, 0x2c, 0x12, 0x30, 0x00, 0x91, 0x2b, 0x25, 0x04, 0x2f, 0x63, 0x50, 0x02, 0x30, + 0x17, 0x42, 0x17, 0x2c, 0x02, 0x42, 0x98, 0x2e, 0xfe, 0xc9, 0x10, 0x25, 0x98, 0x2e, 0x74, 0xc0, + 0x05, 0x2e, 0xc9, 0x00, 0x81, 0x84, 0x5b, 0x30, 0x82, 0x40, 0x37, 0x2e, 0x83, 0x01, 0x02, 0x0e, + 0x07, 0x2f, 0x5f, 0x52, 0x40, 0x30, 0x62, 0x40, 0x41, 0x40, 0x91, 0x0e, 0x01, 0x2f, 0x21, 0x2e, + 0x83, 0x01, 0x05, 0x30, 0x2b, 0x2e, 0x85, 0x01, 0x12, 0x30, 0x36, 0x2c, 0x16, 0x30, 0x15, 0x25, + 0x81, 0x7f, 0x98, 0x2e, 0xfe, 0xc9, 0x10, 0x25, 0x98, 0x2e, 0x74, 0xc0, 0x19, 0xa2, 0x16, 0x30, + 0x15, 0x2f, 0x05, 0x2e, 0x97, 0x01, 0x80, 0x6f, 0x82, 0x0e, 0x05, 0x2f, 0x01, 0x2e, 0x86, 0x01, + 0x06, 0x28, 0x21, 0x2e, 0x86, 0x01, 0x0b, 0x2d, 0x03, 0x2e, 0x87, 0x01, 0x5f, 0x54, 0x4e, 0x28, + 0x91, 0x42, 0x00, 0x2e, 0x82, 0x40, 0x90, 0x0e, 0x01, 0x2f, 0x21, 0x2e, 0x88, 0x01, 0x02, 0x30, + 0x13, 0x2c, 0x05, 0x30, 0xc0, 0x6f, 0x08, 0x1c, 0xa8, 0x0f, 0x16, 0x30, 0x05, 0x30, 0x5b, 0x50, + 0x09, 0x2f, 0x02, 0x80, 0x2d, 0x2e, 0x82, 0x01, 0x05, 0x42, 0x05, 0x80, 0x00, 0x2e, 0x02, 0x42, + 0x3e, 0x80, 0x00, 0x2e, 0x06, 0x42, 0x02, 0x30, 0x90, 0x6f, 0x3e, 0x88, 0x01, 0x40, 0x04, 0x41, + 0x4c, 0x28, 0x01, 0x42, 0x07, 0x80, 0x10, 0x25, 0x24, 0x40, 0x00, 0x40, 0x00, 0xa8, 0xf5, 0x22, + 0x23, 0x29, 0x44, 0x42, 0x7a, 0x82, 0x7e, 0x88, 0x43, 0x40, 0x04, 0x41, 0x00, 0xab, 0xf5, 0x23, + 0xdf, 0x28, 0x43, 0x42, 0xd9, 0xa0, 0x14, 0x2f, 0x00, 0x90, 0x02, 0x2f, 0xd2, 0x6f, 0x81, 0xb2, + 0x05, 0x2f, 0x63, 0x54, 0x06, 0x28, 0x90, 0x42, 0x85, 0x42, 0x09, 0x2c, 0x02, 0x30, 0x5b, 0x50, + 0x03, 0x80, 0x29, 0x2e, 0x7e, 0x01, 0x2b, 0x2e, 0x82, 0x01, 0x05, 0x42, 0x12, 0x30, 0x2b, 0x2e, + 0x83, 0x01, 0x45, 0x82, 0x00, 0x2e, 0x40, 0x40, 0x7a, 0x82, 0x02, 0xa0, 0x08, 0x2f, 0x63, 0x50, + 0x3b, 0x30, 0x15, 0x42, 0x05, 0x42, 0x37, 0x80, 0x37, 0x2e, 0x7e, 0x01, 0x05, 0x42, 0x12, 0x30, + 0x01, 0x2e, 0xc9, 0x00, 0x02, 0x8c, 0x40, 0x40, 0x84, 0x41, 0x7a, 0x8c, 0x04, 0x0f, 0x03, 0x2f, + 0x01, 0x2e, 0x8b, 0x01, 0x19, 0xa4, 0x04, 0x2f, 0x2b, 0x2e, 0x82, 0x01, 0x98, 0x2e, 0xf3, 0x03, + 0x12, 0x30, 0x81, 0x90, 0x61, 0x52, 0x08, 0x2f, 0x65, 0x42, 0x65, 0x42, 0x43, 0x80, 0x39, 0x84, + 0x82, 0x88, 0x05, 0x42, 0x45, 0x42, 0x85, 0x42, 0x05, 0x43, 0x00, 0x2e, 0x80, 0x41, 0x00, 0x90, + 0x90, 0x2e, 0xe1, 0xb4, 0x65, 0x54, 0xc1, 0x6f, 0x80, 0x40, 0x00, 0xb2, 0x43, 0x58, 0x69, 0x50, + 0x44, 0x2f, 0x55, 0x5c, 0xb7, 0x87, 0x8c, 0x0f, 0x0d, 0x2e, 0x96, 0x01, 0xc4, 0x40, 0x36, 0x2f, + 0x41, 0x56, 0x8b, 0x0e, 0x2a, 0x2f, 0x0b, 0x52, 0xa1, 0x0e, 0x0a, 0x2f, 0x05, 0x2e, 0x8f, 0x01, + 0x14, 0x25, 0x98, 0x2e, 0xfe, 0xc9, 0x4b, 0x54, 0x02, 0x0f, 0x69, 0x50, 0x05, 0x30, 0x65, 0x54, + 0x15, 0x2f, 0x03, 0x2e, 0x8e, 0x01, 0x4d, 0x5c, 0x8e, 0x0f, 0x3a, 0x2f, 0x05, 0x2e, 0x8f, 0x01, + 0x98, 0x2e, 0xfe, 0xc9, 0x4f, 0x54, 0x82, 0x0f, 0x05, 0x30, 0x69, 0x50, 0x65, 0x54, 0x30, 0x2f, + 0x6d, 0x52, 0x15, 0x30, 0x42, 0x8c, 0x45, 0x42, 0x04, 0x30, 0x2b, 0x2c, 0x84, 0x43, 0x6b, 0x52, + 0x42, 0x8c, 0x00, 0x2e, 0x85, 0x43, 0x15, 0x30, 0x24, 0x2c, 0x45, 0x42, 0x8e, 0x0f, 0x20, 0x2f, + 0x0d, 0x2e, 0x8e, 0x01, 0xb1, 0x0e, 0x1c, 0x2f, 0x23, 0x2e, 0x8e, 0x01, 0x1a, 0x2d, 0x0e, 0x0e, + 0x17, 0x2f, 0xa1, 0x0f, 0x15, 0x2f, 0x23, 0x2e, 0x8d, 0x01, 0x13, 0x2d, 0x98, 0x2e, 0x74, 0xc0, + 0x43, 0x54, 0xc2, 0x0e, 0x0a, 0x2f, 0x65, 0x50, 0x04, 0x80, 0x0b, 0x30, 0x06, 0x82, 0x0b, 0x42, + 0x79, 0x80, 0x41, 0x40, 0x12, 0x30, 0x25, 0x2e, 0x8c, 0x01, 0x01, 0x42, 0x05, 0x30, 0x69, 0x50, + 0x65, 0x54, 0x84, 0x82, 0x43, 0x84, 0xbe, 0x8c, 0x84, 0x40, 0x86, 0x41, 0x26, 0x29, 0x94, 0x42, + 0xbe, 0x8e, 0xd5, 0x7f, 0x19, 0xa1, 0x43, 0x40, 0x0b, 0x2e, 0x8c, 0x01, 0x84, 0x40, 0xc7, 0x41, + 0x5d, 0x29, 0x27, 0x29, 0x45, 0x42, 0x84, 0x42, 0xc2, 0x7f, 0x01, 0x2f, 0xc0, 0xb3, 0x1d, 0x2f, + 0x05, 0x2e, 0x94, 0x01, 0x99, 0xa0, 0x01, 0x2f, 0x80, 0xb3, 0x13, 0x2f, 0x80, 0xb3, 0x18, 0x2f, + 0xc0, 0xb3, 0x16, 0x2f, 0x12, 0x40, 0x01, 0x40, 0x92, 0x7f, 0x98, 0x2e, 0x74, 0xc0, 0x92, 0x6f, + 0x10, 0x0f, 0x20, 0x30, 0x03, 0x2f, 0x10, 0x30, 0x21, 0x2e, 0x7e, 0x01, 0x0a, 0x2d, 0x21, 0x2e, + 0x7e, 0x01, 0x07, 0x2d, 0x20, 0x30, 0x21, 0x2e, 0x7e, 0x01, 0x03, 0x2d, 0x10, 0x30, 0x21, 0x2e, + 0x7e, 0x01, 0xc2, 0x6f, 0x01, 0x2e, 0xc9, 0x00, 0xbc, 0x84, 0x02, 0x80, 0x82, 0x40, 0x00, 0x40, + 0x90, 0x0e, 0xd5, 0x6f, 0x02, 0x2f, 0x15, 0x30, 0x98, 0x2e, 0xf3, 0x03, 0x41, 0x91, 0x05, 0x30, + 0x07, 0x2f, 0x67, 0x50, 0x3d, 0x80, 0x2b, 0x2e, 0x8f, 0x01, 0x05, 0x42, 0x04, 0x80, 0x00, 0x2e, + 0x05, 0x42, 0x02, 0x2c, 0x00, 0x30, 0x00, 0x30, 0xa2, 0x6f, 0x98, 0x8a, 0x86, 0x40, 0x80, 0xa7, + 0x05, 0x2f, 0x98, 0x2e, 0xf3, 0x03, 0xc0, 0x30, 0x21, 0x2e, 0x95, 0x01, 0x06, 0x25, 0x1a, 0x25, + 0xe2, 0x6f, 0x76, 0x82, 0x96, 0x40, 0x56, 0x43, 0x51, 0x0e, 0xfb, 0x2f, 0xbb, 0x6f, 0x30, 0x5f, + 0xb8, 0x2e, 0x01, 0x2e, 0xb8, 0x00, 0x01, 0x31, 0x41, 0x08, 0x40, 0xb2, 0x20, 0x50, 0xf2, 0x30, + 0x02, 0x08, 0xfb, 0x7f, 0x01, 0x30, 0x10, 0x2f, 0x05, 0x2e, 0xcc, 0x00, 0x81, 0x90, 0xe0, 0x7f, + 0x03, 0x2f, 0x23, 0x2e, 0xcc, 0x00, 0x98, 0x2e, 0x55, 0xb6, 0x98, 0x2e, 0x1d, 0xb5, 0x10, 0x25, + 0xfb, 0x6f, 0xe0, 0x6f, 0xe0, 0x5f, 0x80, 0x2e, 0x95, 0xcf, 0x98, 0x2e, 0x95, 0xcf, 0x10, 0x30, + 0x21, 0x2e, 0xcc, 0x00, 0xfb, 0x6f, 0xe0, 0x5f, 0xb8, 0x2e, 0x00, 0x51, 0x05, 0x58, 0xeb, 0x7f, + 0x2a, 0x25, 0x89, 0x52, 0x6f, 0x5a, 0x89, 0x50, 0x13, 0x41, 0x06, 0x40, 0xb3, 0x01, 0x16, 0x42, + 0xcb, 0x16, 0x06, 0x40, 0xf3, 0x02, 0x13, 0x42, 0x65, 0x0e, 0xf5, 0x2f, 0x05, 0x40, 0x14, 0x30, + 0x2c, 0x29, 0x04, 0x42, 0x08, 0xa1, 0x00, 0x30, 0x90, 0x2e, 0x52, 0xb6, 0xb3, 0x88, 0xb0, 0x8a, + 0xb6, 0x84, 0xa4, 0x7f, 0xc4, 0x7f, 0xb5, 0x7f, 0xd5, 0x7f, 0x92, 0x7f, 0x73, 0x30, 0x04, 0x30, + 0x55, 0x40, 0x42, 0x40, 0x8a, 0x17, 0xf3, 0x08, 0x6b, 0x01, 0x90, 0x02, 0x53, 0xb8, 0x4b, 0x82, + 0xad, 0xbe, 0x71, 0x7f, 0x45, 0x0a, 0x09, 0x54, 0x84, 0x7f, 0x98, 0x2e, 0xd9, 0xc0, 0xa3, 0x6f, + 0x7b, 0x54, 0xd0, 0x42, 0xa3, 0x7f, 0xf2, 0x7f, 0x60, 0x7f, 0x20, 0x25, 0x71, 0x6f, 0x75, 0x5a, + 0x77, 0x58, 0x79, 0x5c, 0x75, 0x56, 0x98, 0x2e, 0x67, 0xcc, 0xb1, 0x6f, 0x62, 0x6f, 0x50, 0x42, + 0xb1, 0x7f, 0xb3, 0x30, 0x10, 0x25, 0x98, 0x2e, 0x0f, 0xca, 0x84, 0x6f, 0x20, 0x29, 0x71, 0x6f, + 0x92, 0x6f, 0xa5, 0x6f, 0x76, 0x82, 0x6a, 0x0e, 0x73, 0x30, 0x00, 0x30, 0xd0, 0x2f, 0xd2, 0x6f, + 0xd1, 0x7f, 0xb4, 0x7f, 0x98, 0x2e, 0x2b, 0xb7, 0x15, 0xbd, 0x0b, 0xb8, 0x02, 0x0a, 0xc2, 0x6f, + 0xc0, 0x7f, 0x98, 0x2e, 0x2b, 0xb7, 0x15, 0xbd, 0x0b, 0xb8, 0x42, 0x0a, 0xc0, 0x6f, 0x08, 0x17, + 0x41, 0x18, 0x89, 0x16, 0xe1, 0x18, 0xd0, 0x18, 0xa1, 0x7f, 0x27, 0x25, 0x16, 0x25, 0x98, 0x2e, + 0x79, 0xc0, 0x8b, 0x54, 0x90, 0x7f, 0xb3, 0x30, 0x82, 0x40, 0x80, 0x90, 0x0d, 0x2f, 0x7d, 0x52, + 0x92, 0x6f, 0x98, 0x2e, 0x0f, 0xca, 0xb2, 0x6f, 0x90, 0x0e, 0x06, 0x2f, 0x8b, 0x50, 0x14, 0x30, + 0x42, 0x6f, 0x51, 0x6f, 0x14, 0x42, 0x12, 0x42, 0x01, 0x42, 0x00, 0x2e, 0x31, 0x6f, 0x98, 0x2e, + 0x74, 0xc0, 0x41, 0x6f, 0x80, 0x7f, 0x98, 0x2e, 0x74, 0xc0, 0x82, 0x6f, 0x10, 0x04, 0x43, 0x52, + 0x01, 0x0f, 0x05, 0x2e, 0xcb, 0x00, 0x00, 0x30, 0x04, 0x30, 0x21, 0x2f, 0x51, 0x6f, 0x43, 0x58, + 0x8c, 0x0e, 0x04, 0x30, 0x1c, 0x2f, 0x85, 0x88, 0x41, 0x6f, 0x04, 0x41, 0x8c, 0x0f, 0x04, 0x30, + 0x16, 0x2f, 0x84, 0x88, 0x00, 0x2e, 0x04, 0x41, 0x04, 0x05, 0x8c, 0x0e, 0x04, 0x30, 0x0f, 0x2f, + 0x82, 0x88, 0x31, 0x6f, 0x04, 0x41, 0x04, 0x05, 0x8c, 0x0e, 0x04, 0x30, 0x08, 0x2f, 0x83, 0x88, + 0x00, 0x2e, 0x04, 0x41, 0x8c, 0x0f, 0x04, 0x30, 0x02, 0x2f, 0x21, 0x2e, 0xad, 0x01, 0x14, 0x30, + 0x00, 0x91, 0x14, 0x2f, 0x03, 0x2e, 0xa1, 0x01, 0x41, 0x90, 0x0e, 0x2f, 0x03, 0x2e, 0xad, 0x01, + 0x14, 0x30, 0x4c, 0x28, 0x23, 0x2e, 0xad, 0x01, 0x46, 0xa0, 0x06, 0x2f, 0x81, 0x84, 0x8d, 0x52, + 0x48, 0x82, 0x82, 0x40, 0x21, 0x2e, 0xa1, 0x01, 0x42, 0x42, 0x5c, 0x2c, 0x02, 0x30, 0x05, 0x2e, + 0xaa, 0x01, 0x80, 0xb2, 0x02, 0x30, 0x55, 0x2f, 0x03, 0x2e, 0xa9, 0x01, 0x92, 0x6f, 0xb3, 0x30, + 0x98, 0x2e, 0x0f, 0xca, 0xb2, 0x6f, 0x90, 0x0f, 0x00, 0x30, 0x02, 0x30, 0x4a, 0x2f, 0xa2, 0x6f, + 0x87, 0x52, 0x91, 0x00, 0x85, 0x52, 0x51, 0x0e, 0x02, 0x2f, 0x00, 0x2e, 0x43, 0x2c, 0x02, 0x30, + 0xc2, 0x6f, 0x7f, 0x52, 0x91, 0x0e, 0x02, 0x30, 0x3c, 0x2f, 0x51, 0x6f, 0x81, 0x54, 0x98, 0x2e, + 0xfe, 0xc9, 0x10, 0x25, 0xb3, 0x30, 0x21, 0x25, 0x98, 0x2e, 0x0f, 0xca, 0x32, 0x6f, 0xc0, 0x7f, + 0xb3, 0x30, 0x12, 0x25, 0x98, 0x2e, 0x0f, 0xca, 0x42, 0x6f, 0xb0, 0x7f, 0xb3, 0x30, 0x12, 0x25, + 0x98, 0x2e, 0x0f, 0xca, 0xb2, 0x6f, 0x90, 0x28, 0x83, 0x52, 0x98, 0x2e, 0xfe, 0xc9, 0xc2, 0x6f, + 0x90, 0x0f, 0x00, 0x30, 0x02, 0x30, 0x1d, 0x2f, 0x05, 0x2e, 0xa1, 0x01, 0x80, 0xb2, 0x12, 0x30, + 0x0f, 0x2f, 0x42, 0x6f, 0x03, 0x2e, 0xab, 0x01, 0x91, 0x0e, 0x02, 0x30, 0x12, 0x2f, 0x52, 0x6f, + 0x03, 0x2e, 0xac, 0x01, 0x91, 0x0f, 0x02, 0x30, 0x0c, 0x2f, 0x21, 0x2e, 0xaa, 0x01, 0x0a, 0x2c, + 0x12, 0x30, 0x03, 0x2e, 0xcb, 0x00, 0x8d, 0x58, 0x08, 0x89, 0x41, 0x40, 0x11, 0x43, 0x00, 0x43, + 0x25, 0x2e, 0xa1, 0x01, 0xd4, 0x6f, 0x8f, 0x52, 0x00, 0x43, 0x3a, 0x89, 0x00, 0x2e, 0x10, 0x43, + 0x10, 0x43, 0x61, 0x0e, 0xfb, 0x2f, 0x03, 0x2e, 0xa0, 0x01, 0x11, 0x1a, 0x02, 0x2f, 0x02, 0x25, + 0x21, 0x2e, 0xa0, 0x01, 0xeb, 0x6f, 0x00, 0x5f, 0xb8, 0x2e, 0x91, 0x52, 0x10, 0x30, 0x02, 0x30, + 0x95, 0x56, 0x52, 0x42, 0x4b, 0x0e, 0xfc, 0x2f, 0x8d, 0x54, 0x88, 0x82, 0x93, 0x56, 0x80, 0x42, + 0x53, 0x42, 0x40, 0x42, 0x42, 0x86, 0x83, 0x54, 0xc0, 0x2e, 0xc2, 0x42, 0x00, 0x2e, 0xa3, 0x52, + 0x00, 0x51, 0x52, 0x40, 0x47, 0x40, 0x1a, 0x25, 0x01, 0x2e, 0x97, 0x00, 0x8f, 0xbe, 0x72, 0x86, + 0xfb, 0x7f, 0x0b, 0x30, 0x7c, 0xbf, 0xa5, 0x50, 0x10, 0x08, 0xdf, 0xba, 0x70, 0x88, 0xf8, 0xbf, + 0xcb, 0x42, 0xd3, 0x7f, 0x6c, 0xbb, 0xfc, 0xbb, 0xc5, 0x0a, 0x90, 0x7f, 0x1b, 0x7f, 0x0b, 0x43, + 0xc0, 0xb2, 0xe5, 0x7f, 0xb7, 0x7f, 0xa6, 0x7f, 0xc4, 0x7f, 0x90, 0x2e, 0x1c, 0xb7, 0x07, 0x2e, + 0xd2, 0x00, 0xc0, 0xb2, 0x0b, 0x2f, 0x97, 0x52, 0x01, 0x2e, 0xcd, 0x00, 0x82, 0x7f, 0x98, 0x2e, + 0xbb, 0xcc, 0x0b, 0x30, 0x37, 0x2e, 0xd2, 0x00, 0x82, 0x6f, 0x90, 0x6f, 0x1a, 0x25, 0x00, 0xb2, + 0x8b, 0x7f, 0x14, 0x2f, 0xa6, 0xbd, 0x25, 0xbd, 0xb6, 0xb9, 0x2f, 0xb9, 0x80, 0xb2, 0xd4, 0xb0, + 0x0c, 0x2f, 0x99, 0x54, 0x9b, 0x56, 0x0b, 0x30, 0x0b, 0x2e, 0xb1, 0x00, 0xa1, 0x58, 0x9b, 0x42, + 0xdb, 0x42, 0x6c, 0x09, 0x2b, 0x2e, 0xb1, 0x00, 0x8b, 0x42, 0xcb, 0x42, 0x86, 0x7f, 0x73, 0x84, + 0xa7, 0x56, 0xc3, 0x08, 0x39, 0x52, 0x05, 0x50, 0x72, 0x7f, 0x63, 0x7f, 0x98, 0x2e, 0xc2, 0xc0, + 0xe1, 0x6f, 0x62, 0x6f, 0xd1, 0x0a, 0x01, 0x2e, 0xcd, 0x00, 0xd5, 0x6f, 0xc4, 0x6f, 0x72, 0x6f, + 0x97, 0x52, 0x9d, 0x5c, 0x98, 0x2e, 0x06, 0xcd, 0x23, 0x6f, 0x90, 0x6f, 0x99, 0x52, 0xc0, 0xb2, + 0x04, 0xbd, 0x54, 0x40, 0xaf, 0xb9, 0x45, 0x40, 0xe1, 0x7f, 0x02, 0x30, 0x06, 0x2f, 0xc0, 0xb2, + 0x02, 0x30, 0x03, 0x2f, 0x9b, 0x5c, 0x12, 0x30, 0x94, 0x43, 0x85, 0x43, 0x03, 0xbf, 0x6f, 0xbb, + 0x80, 0xb3, 0x20, 0x2f, 0x06, 0x6f, 0x26, 0x01, 0x16, 0x6f, 0x6e, 0x03, 0x45, 0x42, 0xc0, 0x90, + 0x29, 0x2e, 0xce, 0x00, 0x9b, 0x52, 0x14, 0x2f, 0x9b, 0x5c, 0x00, 0x2e, 0x93, 0x41, 0x86, 0x41, + 0xe3, 0x04, 0xae, 0x07, 0x80, 0xab, 0x04, 0x2f, 0x80, 0x91, 0x0a, 0x2f, 0x86, 0x6f, 0x73, 0x0f, + 0x07, 0x2f, 0x83, 0x6f, 0xc0, 0xb2, 0x04, 0x2f, 0x54, 0x42, 0x45, 0x42, 0x12, 0x30, 0x04, 0x2c, + 0x11, 0x30, 0x02, 0x2c, 0x11, 0x30, 0x11, 0x30, 0x02, 0xbc, 0x0f, 0xb8, 0xd2, 0x7f, 0x00, 0xb2, + 0x0a, 0x2f, 0x01, 0x2e, 0xfc, 0x00, 0x05, 0x2e, 0xc7, 0x01, 0x10, 0x1a, 0x02, 0x2f, 0x21, 0x2e, + 0xc7, 0x01, 0x03, 0x2d, 0x02, 0x2c, 0x01, 0x30, 0x01, 0x30, 0xb0, 0x6f, 0x98, 0x2e, 0x95, 0xcf, + 0xd1, 0x6f, 0xa0, 0x6f, 0x98, 0x2e, 0x95, 0xcf, 0xe2, 0x6f, 0x9f, 0x52, 0x01, 0x2e, 0xce, 0x00, + 0x82, 0x40, 0x50, 0x42, 0x0c, 0x2c, 0x42, 0x42, 0x11, 0x30, 0x23, 0x2e, 0xd2, 0x00, 0x01, 0x30, + 0xb0, 0x6f, 0x98, 0x2e, 0x95, 0xcf, 0xa0, 0x6f, 0x01, 0x30, 0x98, 0x2e, 0x95, 0xcf, 0x00, 0x2e, + 0xfb, 0x6f, 0x00, 0x5f, 0xb8, 0x2e, 0x83, 0x86, 0x01, 0x30, 0x00, 0x30, 0x94, 0x40, 0x24, 0x18, + 0x06, 0x00, 0x53, 0x0e, 0x4f, 0x02, 0xf9, 0x2f, 0xb8, 0x2e, 0xa9, 0x52, 0x00, 0x2e, 0x60, 0x40, + 0x41, 0x40, 0x0d, 0xbc, 0x98, 0xbc, 0xc0, 0x2e, 0x01, 0x0a, 0x0f, 0xb8, 0xab, 0x52, 0x53, 0x3c, + 0x52, 0x40, 0x40, 0x40, 0x4b, 0x00, 0x82, 0x16, 0x26, 0xb9, 0x01, 0xb8, 0x41, 0x40, 0x10, 0x08, + 0x97, 0xb8, 0x01, 0x08, 0xc0, 0x2e, 0x11, 0x30, 0x01, 0x08, 0x43, 0x86, 0x25, 0x40, 0x04, 0x40, + 0xd8, 0xbe, 0x2c, 0x0b, 0x22, 0x11, 0x54, 0x42, 0x03, 0x80, 0x4b, 0x0e, 0xf6, 0x2f, 0xb8, 0x2e, + 0x9f, 0x50, 0x10, 0x50, 0xad, 0x52, 0x05, 0x2e, 0xd3, 0x00, 0xfb, 0x7f, 0x00, 0x2e, 0x13, 0x40, + 0x93, 0x42, 0x41, 0x0e, 0xfb, 0x2f, 0x98, 0x2e, 0xa5, 0xb7, 0x98, 0x2e, 0x87, 0xcf, 0x01, 0x2e, + 0xd9, 0x00, 0x00, 0xb2, 0xfb, 0x6f, 0x0b, 0x2f, 0x01, 0x2e, 0x69, 0xf7, 0xb1, 0x3f, 0x01, 0x08, + 0x01, 0x30, 0xf0, 0x5f, 0x23, 0x2e, 0xd9, 0x00, 0x21, 0x2e, 0x69, 0xf7, 0x80, 0x2e, 0x7a, 0xb7, + 0xf0, 0x5f, 0xb8, 0x2e, 0x01, 0x2e, 0xc0, 0xf8, 0x03, 0x2e, 0xfc, 0xf5, 0x15, 0x54, 0xaf, 0x56, + 0x82, 0x08, 0x0b, 0x2e, 0x69, 0xf7, 0xcb, 0x0a, 0xb1, 0x58, 0x80, 0x90, 0xdd, 0xbe, 0x4c, 0x08, + 0x5f, 0xb9, 0x59, 0x22, 0x80, 0x90, 0x07, 0x2f, 0x03, 0x34, 0xc3, 0x08, 0xf2, 0x3a, 0x0a, 0x08, + 0x02, 0x35, 0xc0, 0x90, 0x4a, 0x0a, 0x48, 0x22, 0xc0, 0x2e, 0x23, 0x2e, 0xfc, 0xf5, 0x10, 0x50, + 0xfb, 0x7f, 0x98, 0x2e, 0x56, 0xc7, 0x98, 0x2e, 0x49, 0xc3, 0x10, 0x30, 0xfb, 0x6f, 0xf0, 0x5f, + 0x21, 0x2e, 0xcc, 0x00, 0x21, 0x2e, 0xca, 0x00, 0xb8, 0x2e, 0x03, 0x2e, 0xd3, 0x00, 0x16, 0xb8, + 0x02, 0x34, 0x4a, 0x0c, 0x21, 0x2e, 0x2d, 0xf5, 0xc0, 0x2e, 0x23, 0x2e, 0xd3, 0x00, 0x03, 0xbc, + 0x21, 0x2e, 0xd5, 0x00, 0x03, 0x2e, 0xd5, 0x00, 0x40, 0xb2, 0x10, 0x30, 0x21, 0x2e, 0x77, 0x00, + 0x01, 0x30, 0x05, 0x2f, 0x05, 0x2e, 0xd8, 0x00, 0x80, 0x90, 0x01, 0x2f, 0x23, 0x2e, 0x6f, 0xf5, + 0xc0, 0x2e, 0x21, 0x2e, 0xd9, 0x00, 0x11, 0x30, 0x81, 0x08, 0x01, 0x2e, 0x6a, 0xf7, 0x71, 0x3f, + 0x23, 0xbd, 0x01, 0x08, 0x02, 0x0a, 0xc0, 0x2e, 0x21, 0x2e, 0x6a, 0xf7, 0x30, 0x25, 0x00, 0x30, + 0x21, 0x2e, 0x5a, 0xf5, 0x10, 0x50, 0x21, 0x2e, 0x7b, 0x00, 0x21, 0x2e, 0x7c, 0x00, 0xfb, 0x7f, + 0x98, 0x2e, 0xc3, 0xb7, 0x40, 0x30, 0x21, 0x2e, 0xd4, 0x00, 0xfb, 0x6f, 0xf0, 0x5f, 0x03, 0x25, + 0x80, 0x2e, 0xaf, 0xb7, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, + 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, + 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, + 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, + 0x01, 0x2e, 0x5d, 0xf7, 0x08, 0xbc, 0x80, 0xac, 0x0e, 0xbb, 0x02, 0x2f, 0x00, 0x30, 0x41, 0x04, + 0x82, 0x06, 0xc0, 0xa4, 0x00, 0x30, 0x11, 0x2f, 0x40, 0xa9, 0x03, 0x2f, 0x40, 0x91, 0x0d, 0x2f, + 0x00, 0xa7, 0x0b, 0x2f, 0x80, 0xb3, 0xb3, 0x58, 0x02, 0x2f, 0x90, 0xa1, 0x26, 0x13, 0x20, 0x23, + 0x80, 0x90, 0x10, 0x30, 0x01, 0x2f, 0xcc, 0x0e, 0x00, 0x2f, 0x00, 0x30, 0xb8, 0x2e, 0xb5, 0x50, + 0x18, 0x08, 0x08, 0xbc, 0x88, 0xb6, 0x0d, 0x17, 0xc6, 0xbd, 0x56, 0xbc, 0xb7, 0x58, 0xda, 0xba, + 0x04, 0x01, 0x1d, 0x0a, 0x10, 0x50, 0x05, 0x30, 0x32, 0x25, 0x45, 0x03, 0xfb, 0x7f, 0xf6, 0x30, + 0x21, 0x25, 0x98, 0x2e, 0x37, 0xca, 0x16, 0xb5, 0x9a, 0xbc, 0x06, 0xb8, 0x80, 0xa8, 0x41, 0x0a, + 0x0e, 0x2f, 0x80, 0x90, 0x02, 0x2f, 0x2d, 0x50, 0x48, 0x0f, 0x09, 0x2f, 0xbf, 0xa0, 0x04, 0x2f, + 0xbf, 0x90, 0x06, 0x2f, 0xb7, 0x54, 0xca, 0x0f, 0x03, 0x2f, 0x00, 0x2e, 0x02, 0x2c, 0xb7, 0x52, + 0x2d, 0x52, 0xf2, 0x33, 0x98, 0x2e, 0xd9, 0xc0, 0xfb, 0x6f, 0xf1, 0x37, 0xc0, 0x2e, 0x01, 0x08, + 0xf0, 0x5f, 0xbf, 0x56, 0xb9, 0x54, 0xd0, 0x40, 0xc4, 0x40, 0x0b, 0x2e, 0xfd, 0xf3, 0xbf, 0x52, + 0x90, 0x42, 0x94, 0x42, 0x95, 0x42, 0x05, 0x30, 0xc1, 0x50, 0x0f, 0x88, 0x06, 0x40, 0x04, 0x41, + 0x96, 0x42, 0xc5, 0x42, 0x48, 0xbe, 0x73, 0x30, 0x0d, 0x2e, 0xd8, 0x00, 0x4f, 0xba, 0x84, 0x42, + 0x03, 0x42, 0x81, 0xb3, 0x02, 0x2f, 0x2b, 0x2e, 0x6f, 0xf5, 0x06, 0x2d, 0x05, 0x2e, 0x77, 0xf7, + 0xbd, 0x56, 0x93, 0x08, 0x25, 0x2e, 0x77, 0xf7, 0xbb, 0x54, 0x25, 0x2e, 0xc2, 0xf5, 0x07, 0x2e, + 0xfd, 0xf3, 0x42, 0x30, 0xb4, 0x33, 0xda, 0x0a, 0x4c, 0x00, 0x27, 0x2e, 0xfd, 0xf3, 0x43, 0x40, + 0xd4, 0x3f, 0xdc, 0x08, 0x43, 0x42, 0x00, 0x2e, 0x00, 0x2e, 0x43, 0x40, 0x24, 0x30, 0xdc, 0x0a, + 0x43, 0x42, 0x04, 0x80, 0x03, 0x2e, 0xfd, 0xf3, 0x4a, 0x0a, 0x23, 0x2e, 0xfd, 0xf3, 0x61, 0x34, + 0xc0, 0x2e, 0x01, 0x42, 0x00, 0x2e, 0x60, 0x50, 0x1a, 0x25, 0x7a, 0x86, 0xe0, 0x7f, 0xf3, 0x7f, + 0x03, 0x25, 0xc3, 0x52, 0x41, 0x84, 0xdb, 0x7f, 0x33, 0x30, 0x98, 0x2e, 0x16, 0xc2, 0x1a, 0x25, + 0x7d, 0x82, 0xf0, 0x6f, 0xe2, 0x6f, 0x32, 0x25, 0x16, 0x40, 0x94, 0x40, 0x26, 0x01, 0x85, 0x40, + 0x8e, 0x17, 0xc4, 0x42, 0x6e, 0x03, 0x95, 0x42, 0x41, 0x0e, 0xf4, 0x2f, 0xdb, 0x6f, 0xa0, 0x5f, + 0xb8, 0x2e, 0xb0, 0x51, 0xfb, 0x7f, 0x98, 0x2e, 0xe8, 0x0d, 0x5a, 0x25, 0x98, 0x2e, 0x0f, 0x0e, + 0xcb, 0x58, 0x32, 0x87, 0xc4, 0x7f, 0x65, 0x89, 0x6b, 0x8d, 0xc5, 0x5a, 0x65, 0x7f, 0xe1, 0x7f, + 0x83, 0x7f, 0xa6, 0x7f, 0x74, 0x7f, 0xd0, 0x7f, 0xb6, 0x7f, 0x94, 0x7f, 0x17, 0x30, 0xc7, 0x52, + 0xc9, 0x54, 0x51, 0x7f, 0x00, 0x2e, 0x85, 0x6f, 0x42, 0x7f, 0x00, 0x2e, 0x51, 0x41, 0x45, 0x81, + 0x42, 0x41, 0x13, 0x40, 0x3b, 0x8a, 0x00, 0x40, 0x4b, 0x04, 0xd0, 0x06, 0xc0, 0xac, 0x85, 0x7f, + 0x02, 0x2f, 0x02, 0x30, 0x51, 0x04, 0xd3, 0x06, 0x41, 0x84, 0x05, 0x30, 0x5d, 0x02, 0xc9, 0x16, + 0xdf, 0x08, 0xd3, 0x00, 0x8d, 0x02, 0xaf, 0xbc, 0xb1, 0xb9, 0x59, 0x0a, 0x65, 0x6f, 0x11, 0x43, + 0xa1, 0xb4, 0x52, 0x41, 0x53, 0x41, 0x01, 0x43, 0x34, 0x7f, 0x65, 0x7f, 0x26, 0x31, 0xe5, 0x6f, + 0xd4, 0x6f, 0x98, 0x2e, 0x37, 0xca, 0x32, 0x6f, 0x75, 0x6f, 0x83, 0x40, 0x42, 0x41, 0x23, 0x7f, + 0x12, 0x7f, 0xf6, 0x30, 0x40, 0x25, 0x51, 0x25, 0x98, 0x2e, 0x37, 0xca, 0x14, 0x6f, 0x20, 0x05, + 0x70, 0x6f, 0x25, 0x6f, 0x69, 0x07, 0xa2, 0x6f, 0x31, 0x6f, 0x0b, 0x30, 0x04, 0x42, 0x9b, 0x42, + 0x8b, 0x42, 0x55, 0x42, 0x32, 0x7f, 0x40, 0xa9, 0xc3, 0x6f, 0x71, 0x7f, 0x02, 0x30, 0xd0, 0x40, + 0xc3, 0x7f, 0x03, 0x2f, 0x40, 0x91, 0x15, 0x2f, 0x00, 0xa7, 0x13, 0x2f, 0x00, 0xa4, 0x11, 0x2f, + 0x84, 0xbd, 0x98, 0x2e, 0x79, 0xca, 0x55, 0x6f, 0xb7, 0x54, 0x54, 0x41, 0x82, 0x00, 0xf3, 0x3f, + 0x45, 0x41, 0xcb, 0x02, 0xf6, 0x30, 0x98, 0x2e, 0x37, 0xca, 0x35, 0x6f, 0xa4, 0x6f, 0x41, 0x43, + 0x03, 0x2c, 0x00, 0x43, 0xa4, 0x6f, 0x35, 0x6f, 0x17, 0x30, 0x42, 0x6f, 0x51, 0x6f, 0x93, 0x40, + 0x42, 0x82, 0x00, 0x41, 0xc3, 0x00, 0x03, 0x43, 0x51, 0x7f, 0x00, 0x2e, 0x94, 0x40, 0x41, 0x41, + 0x4c, 0x02, 0xc4, 0x6f, 0xd1, 0x56, 0x63, 0x0e, 0x74, 0x6f, 0x51, 0x43, 0xa5, 0x7f, 0x8a, 0x2f, + 0x09, 0x2e, 0xd8, 0x00, 0x01, 0xb3, 0x21, 0x2f, 0xcb, 0x58, 0x90, 0x6f, 0x13, 0x41, 0xb6, 0x6f, + 0xe4, 0x7f, 0x00, 0x2e, 0x91, 0x41, 0x14, 0x40, 0x92, 0x41, 0x15, 0x40, 0x17, 0x2e, 0x6f, 0xf5, + 0xb6, 0x7f, 0xd0, 0x7f, 0xcb, 0x7f, 0x98, 0x2e, 0x00, 0x0c, 0x07, 0x15, 0xc2, 0x6f, 0x14, 0x0b, + 0x29, 0x2e, 0x6f, 0xf5, 0xc3, 0xa3, 0xc1, 0x8f, 0xe4, 0x6f, 0xd0, 0x6f, 0xe6, 0x2f, 0x14, 0x30, + 0x05, 0x2e, 0x6f, 0xf5, 0x14, 0x0b, 0x29, 0x2e, 0x6f, 0xf5, 0x18, 0x2d, 0xcd, 0x56, 0x04, 0x32, + 0xb5, 0x6f, 0x1c, 0x01, 0x51, 0x41, 0x52, 0x41, 0xc3, 0x40, 0xb5, 0x7f, 0xe4, 0x7f, 0x98, 0x2e, + 0x1f, 0x0c, 0xe4, 0x6f, 0x21, 0x87, 0x00, 0x43, 0x04, 0x32, 0xcf, 0x54, 0x5a, 0x0e, 0xef, 0x2f, + 0x15, 0x54, 0x09, 0x2e, 0x77, 0xf7, 0x22, 0x0b, 0x29, 0x2e, 0x77, 0xf7, 0xfb, 0x6f, 0x50, 0x5e, + 0xb8, 0x2e, 0x10, 0x50, 0x01, 0x2e, 0xd4, 0x00, 0x00, 0xb2, 0xfb, 0x7f, 0x51, 0x2f, 0x01, 0xb2, + 0x48, 0x2f, 0x02, 0xb2, 0x42, 0x2f, 0x03, 0x90, 0x56, 0x2f, 0xd7, 0x52, 0x79, 0x80, 0x42, 0x40, + 0x81, 0x84, 0x00, 0x40, 0x42, 0x42, 0x98, 0x2e, 0x93, 0x0c, 0xd9, 0x54, 0xd7, 0x50, 0xa1, 0x40, + 0x98, 0xbd, 0x82, 0x40, 0x3e, 0x82, 0xda, 0x0a, 0x44, 0x40, 0x8b, 0x16, 0xe3, 0x00, 0x53, 0x42, + 0x00, 0x2e, 0x43, 0x40, 0x9a, 0x02, 0x52, 0x42, 0x00, 0x2e, 0x41, 0x40, 0x15, 0x54, 0x4a, 0x0e, + 0x3a, 0x2f, 0x3a, 0x82, 0x00, 0x30, 0x41, 0x40, 0x21, 0x2e, 0x85, 0x0f, 0x40, 0xb2, 0x0a, 0x2f, + 0x98, 0x2e, 0xb1, 0x0c, 0x98, 0x2e, 0x45, 0x0e, 0x98, 0x2e, 0x5b, 0x0e, 0xfb, 0x6f, 0xf0, 0x5f, + 0x00, 0x30, 0x80, 0x2e, 0xce, 0xb7, 0xdd, 0x52, 0xd3, 0x54, 0x42, 0x42, 0x4f, 0x84, 0x73, 0x30, + 0xdb, 0x52, 0x83, 0x42, 0x1b, 0x30, 0x6b, 0x42, 0x23, 0x30, 0x27, 0x2e, 0xd7, 0x00, 0x37, 0x2e, + 0xd4, 0x00, 0x21, 0x2e, 0xd6, 0x00, 0x7a, 0x84, 0x17, 0x2c, 0x42, 0x42, 0x30, 0x30, 0x21, 0x2e, + 0xd4, 0x00, 0x12, 0x2d, 0x21, 0x30, 0x00, 0x30, 0x23, 0x2e, 0xd4, 0x00, 0x21, 0x2e, 0x7b, 0xf7, + 0x0b, 0x2d, 0x17, 0x30, 0x98, 0x2e, 0x51, 0x0c, 0xd5, 0x50, 0x0c, 0x82, 0x72, 0x30, 0x2f, 0x2e, + 0xd4, 0x00, 0x25, 0x2e, 0x7b, 0xf7, 0x40, 0x42, 0x00, 0x2e, 0xfb, 0x6f, 0xf0, 0x5f, 0xb8, 0x2e, + 0x70, 0x50, 0x0a, 0x25, 0x39, 0x86, 0xfb, 0x7f, 0xe1, 0x32, 0x62, 0x30, 0x98, 0x2e, 0xc2, 0xc4, + 0xb5, 0x56, 0xa5, 0x6f, 0xab, 0x08, 0x91, 0x6f, 0x4b, 0x08, 0xdf, 0x56, 0xc4, 0x6f, 0x23, 0x09, + 0x4d, 0xba, 0x93, 0xbc, 0x8c, 0x0b, 0xd1, 0x6f, 0x0b, 0x09, 0xcb, 0x52, 0xe1, 0x5e, 0x56, 0x42, + 0xaf, 0x09, 0x4d, 0xba, 0x23, 0xbd, 0x94, 0x0a, 0xe5, 0x6f, 0x68, 0xbb, 0xeb, 0x08, 0xbd, 0xb9, + 0x63, 0xbe, 0xfb, 0x6f, 0x52, 0x42, 0xe3, 0x0a, 0xc0, 0x2e, 0x43, 0x42, 0x90, 0x5f, 0xd1, 0x50, + 0x03, 0x2e, 0x25, 0xf3, 0x13, 0x40, 0x00, 0x40, 0x9b, 0xbc, 0x9b, 0xb4, 0x08, 0xbd, 0xb8, 0xb9, + 0x98, 0xbc, 0xda, 0x0a, 0x08, 0xb6, 0x89, 0x16, 0xc0, 0x2e, 0x19, 0x00, 0x62, 0x02, 0x10, 0x50, + 0xfb, 0x7f, 0x98, 0x2e, 0x81, 0x0d, 0x01, 0x2e, 0xd4, 0x00, 0x31, 0x30, 0x08, 0x04, 0xfb, 0x6f, + 0x01, 0x30, 0xf0, 0x5f, 0x23, 0x2e, 0xd6, 0x00, 0x21, 0x2e, 0xd7, 0x00, 0xb8, 0x2e, 0x01, 0x2e, + 0xd7, 0x00, 0x03, 0x2e, 0xd6, 0x00, 0x48, 0x0e, 0x01, 0x2f, 0x80, 0x2e, 0x1f, 0x0e, 0xb8, 0x2e, + 0xe3, 0x50, 0x21, 0x34, 0x01, 0x42, 0x82, 0x30, 0xc1, 0x32, 0x25, 0x2e, 0x62, 0xf5, 0x01, 0x00, + 0x22, 0x30, 0x01, 0x40, 0x4a, 0x0a, 0x01, 0x42, 0xb8, 0x2e, 0xe3, 0x54, 0xf0, 0x3b, 0x83, 0x40, + 0xd8, 0x08, 0xe5, 0x52, 0x83, 0x42, 0x00, 0x30, 0x83, 0x30, 0x50, 0x42, 0xc4, 0x32, 0x27, 0x2e, + 0x64, 0xf5, 0x94, 0x00, 0x50, 0x42, 0x40, 0x42, 0xd3, 0x3f, 0x84, 0x40, 0x7d, 0x82, 0xe3, 0x08, + 0x40, 0x42, 0x83, 0x42, 0xb8, 0x2e, 0xdd, 0x52, 0x00, 0x30, 0x40, 0x42, 0x7c, 0x86, 0xb9, 0x52, + 0x09, 0x2e, 0x70, 0x0f, 0xbf, 0x54, 0xc4, 0x42, 0xd3, 0x86, 0x54, 0x40, 0x55, 0x40, 0x94, 0x42, + 0x85, 0x42, 0x21, 0x2e, 0xd7, 0x00, 0x42, 0x40, 0x25, 0x2e, 0xfd, 0xf3, 0xc0, 0x42, 0x7e, 0x82, + 0x05, 0x2e, 0x7d, 0x00, 0x80, 0xb2, 0x14, 0x2f, 0x05, 0x2e, 0x89, 0x00, 0x27, 0xbd, 0x2f, 0xb9, + 0x80, 0x90, 0x02, 0x2f, 0x21, 0x2e, 0x6f, 0xf5, 0x0c, 0x2d, 0x07, 0x2e, 0x71, 0x0f, 0x14, 0x30, + 0x1c, 0x09, 0x05, 0x2e, 0x77, 0xf7, 0xbd, 0x56, 0x47, 0xbe, 0x93, 0x08, 0x94, 0x0a, 0x25, 0x2e, + 0x77, 0xf7, 0xe7, 0x54, 0x50, 0x42, 0x4a, 0x0e, 0xfc, 0x2f, 0xb8, 0x2e, 0x50, 0x50, 0x02, 0x30, + 0x43, 0x86, 0xe5, 0x50, 0xfb, 0x7f, 0xe3, 0x7f, 0xd2, 0x7f, 0xc0, 0x7f, 0xb1, 0x7f, 0x00, 0x2e, + 0x41, 0x40, 0x00, 0x40, 0x48, 0x04, 0x98, 0x2e, 0x74, 0xc0, 0x1e, 0xaa, 0xd3, 0x6f, 0x14, 0x30, + 0xb1, 0x6f, 0xe3, 0x22, 0xc0, 0x6f, 0x52, 0x40, 0xe4, 0x6f, 0x4c, 0x0e, 0x12, 0x42, 0xd3, 0x7f, + 0xeb, 0x2f, 0x03, 0x2e, 0x86, 0x0f, 0x40, 0x90, 0x11, 0x30, 0x03, 0x2f, 0x23, 0x2e, 0x86, 0x0f, + 0x02, 0x2c, 0x00, 0x30, 0xd0, 0x6f, 0xfb, 0x6f, 0xb0, 0x5f, 0xb8, 0x2e, 0x40, 0x50, 0xf1, 0x7f, + 0x0a, 0x25, 0x3c, 0x86, 0xeb, 0x7f, 0x41, 0x33, 0x22, 0x30, 0x98, 0x2e, 0xc2, 0xc4, 0xd3, 0x6f, + 0xf4, 0x30, 0xdc, 0x09, 0x47, 0x58, 0xc2, 0x6f, 0x94, 0x09, 0xeb, 0x58, 0x6a, 0xbb, 0xdc, 0x08, + 0xb4, 0xb9, 0xb1, 0xbd, 0xe9, 0x5a, 0x95, 0x08, 0x21, 0xbd, 0xf6, 0xbf, 0x77, 0x0b, 0x51, 0xbe, + 0xf1, 0x6f, 0xeb, 0x6f, 0x52, 0x42, 0x54, 0x42, 0xc0, 0x2e, 0x43, 0x42, 0xc0, 0x5f, 0x50, 0x50, + 0xf5, 0x50, 0x31, 0x30, 0x11, 0x42, 0xfb, 0x7f, 0x7b, 0x30, 0x0b, 0x42, 0x11, 0x30, 0x02, 0x80, + 0x23, 0x33, 0x01, 0x42, 0x03, 0x00, 0x07, 0x2e, 0x80, 0x03, 0x05, 0x2e, 0xd3, 0x00, 0x23, 0x52, + 0xe2, 0x7f, 0xd3, 0x7f, 0xc0, 0x7f, 0x98, 0x2e, 0xb6, 0x0e, 0xd1, 0x6f, 0x08, 0x0a, 0x1a, 0x25, + 0x7b, 0x86, 0xd0, 0x7f, 0x01, 0x33, 0x12, 0x30, 0x98, 0x2e, 0xc2, 0xc4, 0xd1, 0x6f, 0x08, 0x0a, + 0x00, 0xb2, 0x0d, 0x2f, 0xe3, 0x6f, 0x01, 0x2e, 0x80, 0x03, 0x51, 0x30, 0xc7, 0x86, 0x23, 0x2e, + 0x21, 0xf2, 0x08, 0xbc, 0xc0, 0x42, 0x98, 0x2e, 0xa5, 0xb7, 0x00, 0x2e, 0x00, 0x2e, 0xd0, 0x2e, + 0xb0, 0x6f, 0x0b, 0xb8, 0x03, 0x2e, 0x1b, 0x00, 0x08, 0x1a, 0xb0, 0x7f, 0x70, 0x30, 0x04, 0x2f, + 0x21, 0x2e, 0x21, 0xf2, 0x00, 0x2e, 0x00, 0x2e, 0xd0, 0x2e, 0x98, 0x2e, 0x6d, 0xc0, 0x98, 0x2e, + 0x5d, 0xc0, 0xed, 0x50, 0x98, 0x2e, 0x44, 0xcb, 0xef, 0x50, 0x98, 0x2e, 0x46, 0xc3, 0xf1, 0x50, + 0x98, 0x2e, 0x53, 0xc7, 0x35, 0x50, 0x98, 0x2e, 0x64, 0xcf, 0x10, 0x30, 0x98, 0x2e, 0xdc, 0x03, + 0x20, 0x26, 0xc0, 0x6f, 0x02, 0x31, 0x12, 0x42, 0xab, 0x33, 0x0b, 0x42, 0x37, 0x80, 0x01, 0x30, + 0x01, 0x42, 0xf3, 0x37, 0xf7, 0x52, 0xfb, 0x50, 0x44, 0x40, 0xa2, 0x0a, 0x42, 0x42, 0x8b, 0x31, + 0x09, 0x2e, 0x5e, 0xf7, 0xf9, 0x54, 0xe3, 0x08, 0x83, 0x42, 0x1b, 0x42, 0x23, 0x33, 0x4b, 0x00, + 0xbc, 0x84, 0x0b, 0x40, 0x33, 0x30, 0x83, 0x42, 0x0b, 0x42, 0xe0, 0x7f, 0xd1, 0x7f, 0x98, 0x2e, + 0x58, 0xb7, 0xd1, 0x6f, 0x80, 0x30, 0x40, 0x42, 0x03, 0x30, 0xe0, 0x6f, 0xf3, 0x54, 0x04, 0x30, + 0x00, 0x2e, 0x00, 0x2e, 0x01, 0x89, 0x62, 0x0e, 0xfa, 0x2f, 0x43, 0x42, 0x11, 0x30, 0xfb, 0x6f, + 0xc0, 0x2e, 0x01, 0x42, 0xb0, 0x5f, 0xc1, 0x4a, 0x00, 0x00, 0x6d, 0x57, 0x00, 0x00, 0x77, 0x8e, + 0x00, 0x00, 0xe0, 0xff, 0xff, 0xff, 0xd3, 0xff, 0xff, 0xff, 0xe5, 0xff, 0xff, 0xff, 0xee, 0xe1, + 0xff, 0xff, 0x7c, 0x13, 0x00, 0x00, 0x46, 0xe6, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, + 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, + 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, + 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, + 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, + 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, + 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, + 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, + 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, + 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, + 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, + 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, + 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, + 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, + 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, +}; + diff --git a/src/sensor/sensor.c b/src/sensor/sensor.c new file mode 100644 index 0000000..ed62c64 --- /dev/null +++ b/src/sensor/sensor.c @@ -0,0 +1,29 @@ +#include +#include + +inline whal_Error whal_Sensor_Init(whal_Sensor *dev) +{ + if (!dev || !dev->driver || !dev->driver->Init) { + return WHAL_EINVAL; + } + + return dev->driver->Init(dev); +} + +inline whal_Error whal_Sensor_Deinit(whal_Sensor *dev) +{ + if (!dev || !dev->driver || !dev->driver->Deinit) { + return WHAL_EINVAL; + } + + return dev->driver->Deinit(dev); +} + +inline whal_Error whal_Sensor_Read(whal_Sensor *dev, void *data) +{ + if (!dev || !dev->driver || !dev->driver->Read || !data) { + return WHAL_EINVAL; + } + + return dev->driver->Read(dev, data); +} diff --git a/tests/Makefile b/tests/Makefile index c0bab79..d79c005 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -14,8 +14,8 @@ CFLAGS += $(foreach t,$(TESTS),$(if $(wildcard $(t)/test_$(PLATFORM)_$(t).c),-DW SOURCE = main.c SOURCE += $(BOARD_SOURCE) -SOURCE += $(foreach t,$(TESTS),$(t)/test_$(t).c) -SOURCE += $(foreach t,$(TESTS),$(wildcard $(t)/test_$(PLATFORM)_$(t).c)) +SOURCE += $(foreach t,$(TESTS),$(shell find . -name 'test_$(t).c')) +SOURCE += $(foreach t,$(TESTS),$(shell find . -name 'test_$(PLATFORM)_$(t).c')) OBJECTS = $(patsubst %.c,$(BUILD_DIR)/%.o,$(SOURCE)) DEPENDS = $(OBJECTS:.o=.d) diff --git a/tests/README.md b/tests/README.md index ef70790..5fe0b03 100644 --- a/tests/README.md +++ b/tests/README.md @@ -48,16 +48,18 @@ Board support (device instances, linker scripts, etc.) lives in the top-level ### Peripheral Devices -External peripheral drivers (SPI-NOR flash, SD cards, etc.) are opt-in. Enable -them by setting the corresponding variable when building: +External peripheral drivers (SPI-NOR flash, SD cards, IMUs, etc.) are opt-in. +Enable them using the `PERIPHERALS` variable when building: ``` -make BOARD=stm32wb55xx_nucleo PERIPHERAL_SPI_NOR_W25Q64=1 -make BOARD=stm32wb55xx_nucleo PERIPHERAL_SDHC_SPI_SDCARD32GB=1 +make BOARD=stm32wb55xx_nucleo PERIPHERALS="spi_nor_w25q64" +make BOARD=stm32wb55xx_nucleo PERIPHERALS="bmi270" TESTS="bmi270" +make BOARD=stm32wb55xx_nucleo PERIPHERALS="spi_nor_w25q64 bmi270" ``` Peripheral devices are automatically tested by their matching test suite (e.g., -`flash` tests iterate all entries in `g_peripheralFlash[]`). +`flash` tests iterate all entries in `g_peripheralFlash[]`, `bmi270` tests +use `g_peripheralSensor[]`). ## Core Tests diff --git a/tests/main.c b/tests/main.c index 1ec8d24..3a8e850 100644 --- a/tests/main.c +++ b/tests/main.c @@ -61,6 +61,10 @@ void whal_Test_Eth_Platform(void); #endif #endif +#ifdef WHAL_TEST_ENABLE_BMI270 +void whal_Test_Bmi270(void); +#endif + int g_whalTestPassed; int g_whalTestFailed; int g_whalTestSkipped; @@ -150,6 +154,10 @@ void main(void) #endif #endif +#ifdef WHAL_TEST_ENABLE_BMI270 + whal_Test_Bmi270(); +#endif + WHAL_TEST_SUMMARY(); if (g_whalTestFailed == 0) { diff --git a/tests/sensor/imu/test_bmi270.c b/tests/sensor/imu/test_bmi270.c new file mode 100644 index 0000000..2baebb3 --- /dev/null +++ b/tests/sensor/imu/test_bmi270.c @@ -0,0 +1,53 @@ +#include +#include +#include +#include "board.h" +#include "peripheral.h" +#include "test.h" + +/* + * BMI270 IMU test suite. + * + * Requires a BMI270 connected to the board's I2C bus. + * The board must provide g_whalBmi270 via the peripheral config. + */ + +static void Test_Bmi270_Read(void) +{ + whal_Bmi270_Data data = {0}; + + WHAL_ASSERT_EQ(whal_Sensor_Read( + g_peripheralSensor[PERIPHERAL_SENSOR_BMI270].dev, + &data), + WHAL_SUCCESS); + + /* At rest, at least one accel axis should be non-zero (gravity) */ + WHAL_ASSERT_NEQ(data.accelX | data.accelY | data.accelZ, 0); +} + +static void Test_Bmi270_ReadMultiple(void) +{ + whal_Bmi270_Data data1 = {0}; + whal_Bmi270_Data data2 = {0}; + + WHAL_ASSERT_EQ(whal_Sensor_Read( + g_peripheralSensor[PERIPHERAL_SENSOR_BMI270].dev, + &data1), + WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_Sensor_Read( + g_peripheralSensor[PERIPHERAL_SENSOR_BMI270].dev, + &data2), + WHAL_SUCCESS); + + /* Both reads should have non-zero accel (gravity) */ + WHAL_ASSERT_NEQ(data1.accelX | data1.accelY | data1.accelZ, 0); + WHAL_ASSERT_NEQ(data2.accelX | data2.accelY | data2.accelZ, 0); +} + +void whal_Test_Bmi270(void) +{ + WHAL_TEST_SUITE_START("bmi270"); + WHAL_TEST(Test_Bmi270_Read); + WHAL_TEST(Test_Bmi270_ReadMultiple); + WHAL_TEST_SUITE_END(); +} diff --git a/wolfHAL/i2c/i2c.h b/wolfHAL/i2c/i2c.h new file mode 100644 index 0000000..32f773b --- /dev/null +++ b/wolfHAL/i2c/i2c.h @@ -0,0 +1,193 @@ +#ifndef WHAL_I2C_H +#define WHAL_I2C_H + +#include +#include +#include +#include + +/* + * @file i2c.h + * @brief Generic I2C abstraction and driver interface. + */ + +typedef struct whal_I2c whal_I2c; + +/* I2C message flags */ +#define WHAL_I2C_MSG_WRITE 0 +#define WHAL_I2C_MSG_READ (1 << 0) +#define WHAL_I2C_MSG_STOP (1 << 1) +#define WHAL_I2C_MSG_START (1 << 2) + +/* + * @brief I2C transfer message descriptor. + * + * Each message represents a single segment of an I2C bus transaction. + * The caller must set the appropriate flags to control START, STOP, + * and data direction for each segment. + */ +typedef struct { + void *data; /* Data buffer */ + size_t dataSz; /* Number of bytes to transfer */ + uint8_t flags; /* Combination of WHAL_I2C_MSG_* flags */ +} whal_I2c_Msg; + +/* + * @brief I2C communication session parameters. + * + * Platform-independent configuration applied when starting a communication + * session via StartCom and kept in effect until EndCom. The platform driver + * translates these to hardware register values. + */ +typedef struct { + uint32_t freq; /* Bus frequency in Hz */ + uint16_t addr; /* Target device address */ + uint8_t addrSz; /* Address size in bits (7 or 10) */ +} whal_I2c_ComCfg; + +/* + * @brief Driver vtable for I2C devices. + */ +typedef struct { + /* Initialize the I2C hardware. */ + whal_Error (*Init)(whal_I2c *i2cDev); + /* Deinitialize the I2C hardware. */ + whal_Error (*Deinit)(whal_I2c *i2cDev); + /* Begin a communication session with the given parameters. */ + whal_Error (*StartCom)(whal_I2c *i2cDev, whal_I2c_ComCfg *comCfg); + /* End the current communication session. */ + whal_Error (*EndCom)(whal_I2c *i2cDev); + /* Execute a sequence of I2C messages on the bus. */ + whal_Error (*Transfer)(whal_I2c *i2cDev, whal_I2c_Msg *msgs, + size_t numMsgs); +} whal_I2cDriver; + +/* + * @brief I2C device instance tying a register map and driver. + */ +struct whal_I2c { + const whal_Regmap regmap; + const whal_I2cDriver *driver; + void *cfg; +}; + +#ifdef WHAL_CFG_DIRECT_CALLBACKS +#define whal_I2c_Init(i2cDev) ((i2cDev)->driver->Init((i2cDev))) +#define whal_I2c_Deinit(i2cDev) ((i2cDev)->driver->Deinit((i2cDev))) +#define whal_I2c_StartCom(i2cDev, i2cComCfg) \ + ((i2cDev)->driver->StartCom((i2cDev), (i2cComCfg))) +#define whal_I2c_EndCom(i2cDev) ((i2cDev)->driver->EndCom((i2cDev))) +#define whal_I2c_Transfer(i2cDev, msgs, numMsgs) \ + ((i2cDev)->driver->Transfer((i2cDev), (msgs), (numMsgs))) +#else +/* + * @brief Initializes an I2C device and its driver. + * + * @param i2cDev Pointer to the I2C instance to initialize. + * + * @retval WHAL_SUCCESS Driver-specific init completed. + * @retval WHAL_EINVAL Null pointer or driver rejected configuration. + */ +whal_Error whal_I2c_Init(whal_I2c *i2cDev); +/* + * @brief Deinitializes an I2C device and releases resources. + * + * @param i2cDev Pointer to the I2C instance to deinitialize. + * + * @retval WHAL_SUCCESS Driver-specific deinit completed. + * @retval WHAL_EINVAL Null pointer or driver refused to deinit. + */ +whal_Error whal_I2c_Deinit(whal_I2c *i2cDev); +/* + * @brief Begin a communication session with the given parameters. + * + * Configures the I2C peripheral for the specified frequency and target + * address. Must be called before any Transfer calls. + * + * @param i2cDev Pointer to the I2C instance. + * @param comCfg Per-session communication parameters. + * + * @retval WHAL_SUCCESS Communication session started. + * @retval WHAL_EINVAL Null pointer or invalid parameters. + */ +whal_Error whal_I2c_StartCom(whal_I2c *i2cDev, whal_I2c_ComCfg *comCfg); +/* + * @brief End the current communication session. + * + * @param i2cDev Pointer to the I2C instance. + * + * @retval WHAL_SUCCESS Communication session ended. + * @retval WHAL_EINVAL Null pointer or driver error. + */ +whal_Error whal_I2c_EndCom(whal_I2c *i2cDev); +/* + * @brief Execute a sequence of I2C messages on the bus. + * + * Each message in the array describes one segment of a bus transaction. + * The caller controls START, STOP, and data direction explicitly via + * the flags field of each message. + * + * @param i2cDev Pointer to the I2C instance. + * @param msgs Array of message descriptors. + * @param numMsgs Number of messages in the array. + * + * @retval WHAL_SUCCESS All messages transferred successfully. + * @retval WHAL_EINVAL Null pointer or invalid parameters. + */ +whal_Error whal_I2c_Transfer(whal_I2c *i2cDev, whal_I2c_Msg *msgs, + size_t numMsgs); +#endif + +/* + * @brief Write to a register on an I2C device. + * + * Sends START, addr+W, reg address byte, data bytes, STOP. + * + * @param i2cDev Pointer to the I2C instance. + * @param reg Register address. + * @param data Buffer of bytes to write after the register address. + * @param dataSz Number of bytes to write. + * + * @retval WHAL_SUCCESS Register written. + * @retval WHAL_EINVAL Null pointer. + */ +static inline whal_Error whal_I2c_WriteReg(whal_I2c *i2cDev, uint8_t reg, + const void *data, size_t dataSz) +{ + whal_I2c_Msg msgs[2] = { + { .data = ®, .dataSz = 1, + .flags = WHAL_I2C_MSG_START | WHAL_I2C_MSG_WRITE }, + { .data = (void *)data, .dataSz = dataSz, + .flags = WHAL_I2C_MSG_WRITE | WHAL_I2C_MSG_STOP }, + }; + + return whal_I2c_Transfer(i2cDev, msgs, 2); +} + +/* + * @brief Read one or more bytes from a register on an I2C device. + * + * Sends START, addr+W, reg address byte, RESTART, addr+R, data, STOP. + * + * @param i2cDev Pointer to the I2C instance. + * @param reg Register address. + * @param data Buffer to receive data into. + * @param dataSz Number of bytes to read. + * + * @retval WHAL_SUCCESS Register read. + * @retval WHAL_EINVAL Null pointer. + */ +static inline whal_Error whal_I2c_ReadReg(whal_I2c *i2cDev, uint8_t reg, + void *data, size_t dataSz) +{ + whal_I2c_Msg msgs[2] = { + { .data = ®, .dataSz = 1, + .flags = WHAL_I2C_MSG_START | WHAL_I2C_MSG_WRITE }, + { .data = data, .dataSz = dataSz, + .flags = WHAL_I2C_MSG_START | WHAL_I2C_MSG_READ | WHAL_I2C_MSG_STOP }, + }; + + return whal_I2c_Transfer(i2cDev, msgs, 2); +} + +#endif /* WHAL_I2C_H */ diff --git a/wolfHAL/i2c/stm32wb_i2c.h b/wolfHAL/i2c/stm32wb_i2c.h new file mode 100644 index 0000000..059144e --- /dev/null +++ b/wolfHAL/i2c/stm32wb_i2c.h @@ -0,0 +1,100 @@ +#ifndef WHAL_STM32WB_I2C_H +#define WHAL_STM32WB_I2C_H + +#include +#include +#include +#include + +/* + * @file stm32wb_i2c.h + * @brief STM32WB I2C driver configuration. + * + * The STM32WB I2C peripheral provides: + * - Controller and target modes (this driver supports controller only) + * - Standard-mode (up to 100 kHz), Fast-mode (up to 400 kHz), + * Fast-mode Plus (up to 1 MHz) + * - 7-bit and 10-bit addressing + * - Programmable setup and hold timings via I2C_TIMINGR + * - Hardware byte counter with automatic STOP/RESTART generation + */ + +/* + * @brief I2C device configuration. + */ +typedef struct whal_Stm32wbI2c_Cfg { + uint32_t pclk; /* I2C kernel clock frequency in Hz */ + whal_Timeout *timeout; +} whal_Stm32wbI2c_Cfg; + +/* + * @brief Driver instance for STM32WB I2C peripheral. + */ +extern const whal_I2cDriver whal_Stm32wbI2c_Driver; + +/* + * @brief Initialize the STM32WB I2C peripheral. + * + * Configures noise filters and enables the peripheral. + * + * @param i2cDev I2C device instance to initialize. + * + * @retval WHAL_SUCCESS Initialization completed. + * @retval WHAL_EINVAL Invalid arguments. + */ +whal_Error whal_Stm32wbI2c_Init(whal_I2c *i2cDev); + +/* + * @brief Deinitialize the STM32WB I2C peripheral. + * + * Disables the peripheral. + * + * @param i2cDev I2C device instance to deinitialize. + * + * @retval WHAL_SUCCESS Deinit completed. + * @retval WHAL_EINVAL Invalid arguments. + */ +whal_Error whal_Stm32wbI2c_Deinit(whal_I2c *i2cDev); + +/* + * @brief Begin a communication session on the STM32WB I2C peripheral. + * + * Computes I2C_TIMINGR from the configured pclk and the requested + * frequency, then writes the target address into CR2. + * + * @param i2cDev I2C device instance. + * @param comCfg Per-session communication parameters. + * + * @retval WHAL_SUCCESS Communication session started. + * @retval WHAL_EINVAL Invalid arguments. + */ +whal_Error whal_Stm32wbI2c_StartCom(whal_I2c *i2cDev, whal_I2c_ComCfg *comCfg); + +/* + * @brief End the current communication session on the STM32WB I2C peripheral. + * + * Clears the target address from CR2. + * + * @param i2cDev I2C device instance. + * + * @retval WHAL_SUCCESS Communication session ended. + * @retval WHAL_EINVAL Invalid arguments. + */ +whal_Error whal_Stm32wbI2c_EndCom(whal_I2c *i2cDev); + +/* + * @brief Execute a sequence of I2C messages on the bus. + * + * @param i2cDev I2C device instance. + * @param msgs Array of message descriptors. + * @param numMsgs Number of messages in the array. + * + * @retval WHAL_SUCCESS All messages transferred successfully. + * @retval WHAL_EINVAL Invalid arguments. + * @retval WHAL_ETIMEOUT Hardware did not respond in time. + * @retval WHAL_EHARDWARE NACK or bus error detected. + */ +whal_Error whal_Stm32wbI2c_Transfer(whal_I2c *i2cDev, whal_I2c_Msg *msgs, + size_t numMsgs); + +#endif /* WHAL_STM32WB_I2C_H */ diff --git a/wolfHAL/platform/st/stm32wb55xx.h b/wolfHAL/platform/st/stm32wb55xx.h index f783ea5..9a9f90d 100644 --- a/wolfHAL/platform/st/stm32wb55xx.h +++ b/wolfHAL/platform/st/stm32wb55xx.h @@ -12,6 +12,7 @@ #include #include #include +#include /* * @file stm32wb55xx.h @@ -74,6 +75,20 @@ }, \ .driver = &whal_Stm32wbAes_Driver +#define WHAL_STM32WB55_I2C1_DEVICE \ + .regmap = { \ + .base = 0x40005400, \ + .size = 0x400, \ + }, \ + .driver = &whal_Stm32wbI2c_Driver + +#define WHAL_STM32WB55_I2C3_DEVICE \ + .regmap = { \ + .base = 0x40005C00, \ + .size = 0x400, \ + }, \ + .driver = &whal_Stm32wbI2c_Driver + #define WHAL_STM32WB55_FLASH_DEVICE \ .regmap = { \ .base = 0x58004000, \ @@ -136,6 +151,16 @@ .enableMask = (1UL << 25), \ .enablePos = 25 +#define WHAL_STM32WB55_I2C1_CLOCK \ + .regOffset = 0x58, \ + .enableMask = (1UL << 21), \ + .enablePos = 21 + +#define WHAL_STM32WB55_I2C3_CLOCK \ + .regOffset = 0x58, \ + .enableMask = (1UL << 23), \ + .enablePos = 23 + #define WHAL_STM32WB55_LPUART1_CLOCK \ .regOffset = 0x5C, \ .enableMask = (1UL << 0), \ diff --git a/wolfHAL/sensor/imu/bmi270.h b/wolfHAL/sensor/imu/bmi270.h new file mode 100644 index 0000000..4d70966 --- /dev/null +++ b/wolfHAL/sensor/imu/bmi270.h @@ -0,0 +1,94 @@ +#ifndef WHAL_IMU_BMI270_H +#define WHAL_IMU_BMI270_H + +#include +#include +#include +#include + +/* + * @file bmi270.h + * @brief Bosch BMI270 6-axis IMU driver (accelerometer + gyroscope). + * + * The BMI270 communicates over I2C at address 0x68 (SDO low) or + * 0x69 (SDO high). It requires a binary config blob to be loaded + * during initialization before producing valid sensor data. + */ + +/* Default I2C address (SDO pin low) */ +#define WHAL_BMI270_ADDR_LOW 0x68 +/* Alternate I2C address (SDO pin high) */ +#define WHAL_BMI270_ADDR_HIGH 0x69 + +/* Expected CHIP_ID value */ +#define WHAL_BMI270_CHIP_ID 0x24 + +/* + * @brief BMI270 sensor data returned by Read. + */ +typedef struct { + int16_t accelX; /* Raw accelerometer X */ + int16_t accelY; /* Raw accelerometer Y */ + int16_t accelZ; /* Raw accelerometer Z */ + int16_t gyroX; /* Raw gyroscope X */ + int16_t gyroY; /* Raw gyroscope Y */ + int16_t gyroZ; /* Raw gyroscope Z */ +} whal_Bmi270_Data; + +/* + * @brief BMI270 driver configuration. + */ +typedef struct { + whal_I2c *i2c; /* I2C bus the sensor is connected to */ + whal_I2c_ComCfg *comCfg; /* I2C session parameters (addr, freq) */ + const uint8_t *configData; /* Bosch-provided config blob (8192 bytes) */ + size_t configDataSz; /* Size of config blob */ + void (*DelayMs)(size_t ms); /* Millisecond delay function */ +} whal_Bmi270_Cfg; + +/* + * @brief Driver instance for the BMI270. + */ +extern const whal_SensorDriver whal_Bmi270_Driver; + +/* + * @brief Initialize the BMI270 sensor. + * + * Performs soft reset, verifies CHIP_ID, loads the config blob, + * and enables the accelerometer and gyroscope. + * + * @param dev Sensor device instance. + * + * @retval WHAL_SUCCESS Initialization completed. + * @retval WHAL_EINVAL Invalid arguments. + * @retval WHAL_EHARDWARE CHIP_ID mismatch or config load failed. + */ +whal_Error whal_Bmi270_Init(whal_Sensor *dev); + +/* + * @brief Deinitialize the BMI270 sensor. + * + * Issues a soft reset to the device. + * + * @param dev Sensor device instance. + * + * @retval WHAL_SUCCESS Deinit completed. + * @retval WHAL_EINVAL Invalid arguments. + */ +whal_Error whal_Bmi270_Deinit(whal_Sensor *dev); + +/* + * @brief Read accelerometer and gyroscope data from the BMI270. + * + * Fetches a new sample from the hardware and fills a + * whal_Bmi270_Data struct with raw axis values. + * + * @param dev Sensor device instance. + * @param data Pointer to a whal_Bmi270_Data struct to fill. + * + * @retval WHAL_SUCCESS Data read successfully. + * @retval WHAL_EINVAL Invalid arguments. + */ +whal_Error whal_Bmi270_Read(whal_Sensor *dev, void *data); + +#endif /* WHAL_IMU_BMI270_H */ diff --git a/wolfHAL/sensor/imu/bmi270_config_data.h b/wolfHAL/sensor/imu/bmi270_config_data.h new file mode 100644 index 0000000..a1a582e --- /dev/null +++ b/wolfHAL/sensor/imu/bmi270_config_data.h @@ -0,0 +1,18 @@ +#ifndef WHAL_BMI270_CONFIG_DATA_H +#define WHAL_BMI270_CONFIG_DATA_H + +#include +#include + +/* + * @brief BMI270 configuration data blob (8192 bytes). + * + * Extracted from the Bosch Sensortec BMI270_SensorAPI repository + * under BSD-3-Clause license. See src/sensor/imu/bmi270_config_data.c + * for the full license text and data. + */ +#define WHAL_BMI270_CONFIG_DATA_SZ 8192 + +extern const uint8_t whal_bmi270_config_data[WHAL_BMI270_CONFIG_DATA_SZ]; + +#endif /* WHAL_BMI270_CONFIG_DATA_H */ diff --git a/wolfHAL/sensor/sensor.h b/wolfHAL/sensor/sensor.h new file mode 100644 index 0000000..63552f1 --- /dev/null +++ b/wolfHAL/sensor/sensor.h @@ -0,0 +1,78 @@ +#ifndef WHAL_SENSOR_H +#define WHAL_SENSOR_H + +#include +#include +#include + +/* + * @file sensor.h + * @brief Generic sensor abstraction and driver interface. + * + * Provides a bus-agnostic API for reading sensor data. Each sensor + * driver implements the vtable and uses the appropriate bus (I2C, SPI, + * etc.) internally. The Read function fills a driver-defined data + * struct passed as a void pointer. + */ + +typedef struct whal_Sensor whal_Sensor; + +/* + * @brief Driver vtable for sensor devices. + */ +typedef struct { + /* Initialize the sensor hardware. */ + whal_Error (*Init)(whal_Sensor *dev); + /* Deinitialize the sensor hardware. */ + whal_Error (*Deinit)(whal_Sensor *dev); + /* Read sensor data into a driver-defined data struct. */ + whal_Error (*Read)(whal_Sensor *dev, void *data); +} whal_SensorDriver; + +/* + * @brief Sensor device instance tying a driver and configuration. + */ +struct whal_Sensor { + const whal_SensorDriver *driver; + void *cfg; +}; + +#ifdef WHAL_CFG_DIRECT_CALLBACKS +#define whal_Sensor_Init(dev) ((dev)->driver->Init((dev))) +#define whal_Sensor_Deinit(dev) ((dev)->driver->Deinit((dev))) +#define whal_Sensor_Read(dev, data) ((dev)->driver->Read((dev), (data))) +#else +/* + * @brief Initialize a sensor device and its driver. + * + * @param dev Pointer to the sensor instance to initialize. + * + * @retval WHAL_SUCCESS Driver-specific init completed. + * @retval WHAL_EINVAL Null pointer or driver rejected configuration. + */ +whal_Error whal_Sensor_Init(whal_Sensor *dev); +/* + * @brief Deinitialize a sensor device and release resources. + * + * @param dev Pointer to the sensor instance to deinitialize. + * + * @retval WHAL_SUCCESS Driver-specific deinit completed. + * @retval WHAL_EINVAL Null pointer or driver refused to deinit. + */ +whal_Error whal_Sensor_Deinit(whal_Sensor *dev); +/* + * @brief Read sensor data into a driver-defined data struct. + * + * Fetches a new sample from the hardware and fills the provided + * data struct. The struct type is defined by each sensor driver. + * + * @param dev Pointer to the sensor instance. + * @param data Pointer to a driver-defined data struct to fill. + * + * @retval WHAL_SUCCESS Data read successfully. + * @retval WHAL_EINVAL Null pointer or driver error. + */ +whal_Error whal_Sensor_Read(whal_Sensor *dev, void *data); +#endif + +#endif /* WHAL_SENSOR_H */ diff --git a/wolfHAL/wolfHAL.h b/wolfHAL/wolfHAL.h index 2440672..3976170 100644 --- a/wolfHAL/wolfHAL.h +++ b/wolfHAL/wolfHAL.h @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -25,5 +26,6 @@ #include #include #include +#include #endif /* WOLFHAL_H */