1
0
Fork 0

Unify i2c_master headers (#24846)

* Unify i2c_master headers

* More documentation improvements

* Reorganise PAL mode defaults
This commit is contained in:
Ryan 2025-01-26 23:48:46 +11:00 committed by GitHub
parent 291d154d7b
commit ef29a46c87
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 225 additions and 136 deletions

View file

@ -16,17 +16,22 @@ You can then call the I2C API by including `i2c_master.h` in your code.
## I2C Addressing {#note-on-i2c-addresses} ## I2C Addressing {#note-on-i2c-addresses}
All of the addresses expected by this driver should be pushed to the upper 7 bits of the address byte. Setting I2C addresses listed on datasheets and the internet are usually represented as a 7-bit value. The eighth bit (the least significant bit) controls whether the operation is a read or a write.
the lower bit (indicating read/write) will be done by the respective functions. Almost all I2C addresses listed
on datasheets and the internet will be represented as 7 bits occupying the lower 7 bits and will need to be
shifted to the left (more significant) by one bit. This is easy to do via the bitwise shift operator `<< 1`.
You can either do this on each call to the functions below, or once in your definition of the address. For example, if your device has an address of `0x18`: All of the address parameters expected by the driver API should therefore be pushed to the upper 7 bits of the address byte; the driver will take care of setting the read/write bit as appropriate.
This is easy to do via the bitwise left shift operator. For example, if your device has an address of `0x18` you might create a define for convenience:
```c ```c
#define MY_I2C_ADDRESS (0x18 << 1) #define MY_I2C_ADDRESS (0x18 << 1)
``` ```
Or, you can shift the address ahead of time:
```c
#define MY_I2C_ADDRESS 0x30
```
See https://www.robot-electronics.co.uk/i2c-tutorial for more information about I2C addressing and other technical details. See https://www.robot-electronics.co.uk/i2c-tutorial for more information about I2C addressing and other technical details.
## AVR Configuration {#avr-configuration} ## AVR Configuration {#avr-configuration}
@ -40,7 +45,7 @@ The following defines can be used to configure the I2C master driver:
No further setup is required - just connect the `SDA` and `SCL` pins of your I2C devices to the matching pins on the MCU: No further setup is required - just connect the `SDA` and `SCL` pins of your I2C devices to the matching pins on the MCU:
|MCU |`SCL`|`SDA`| |MCU |`SCL`|`SDA`|
|------------------|-----|-----| |-------------|-----|-----|
|ATmega16/32U4|`D0` |`D1` | |ATmega16/32U4|`D0` |`D1` |
|AT90USB64/128|`D0` |`D1` | |AT90USB64/128|`D0` |`D1` |
|ATmega32A |`C0` |`C1` | |ATmega32A |`C0` |`C1` |
@ -52,7 +57,7 @@ The ATmega16/32U2 does not possess I2C functionality, and so cannot use this dri
## ChibiOS/ARM Configuration {#arm-configuration} ## ChibiOS/ARM Configuration {#arm-configuration}
You'll need to determine which pins can be used for I2C -- a an example, STM32 parts generally have multiple I2C peripherals, labeled I2C1, I2C2, I2C3 etc. You'll need to determine which pins can be used for I2C -- as an example, STM32 parts generally have multiple I2C peripherals, labeled I2C1, I2C2, I2C3 etc.
To enable I2C, modify your board's `halconf.h` to enable I2C, then modify your board's `mcuconf.h` to enable the peripheral you've chosen: To enable I2C, modify your board's `halconf.h` to enable I2C, then modify your board's `mcuconf.h` to enable the peripheral you've chosen:
@ -83,15 +88,19 @@ To enable I2C, modify your board's `halconf.h` to enable I2C, then modify your b
Configuration-wise, you'll need to set up the peripheral as per your MCU's datasheet -- the defaults match the pins for a Proton-C, i.e. STM32F303. Configuration-wise, you'll need to set up the peripheral as per your MCU's datasheet -- the defaults match the pins for a Proton-C, i.e. STM32F303.
|`config.h` Overrride |Description |Default| |`config.h` Override|Description |Default|
|------------------------|--------------------------------------------------------------|-------| |-------------------|-------------------------------------------------------------|-------|
|`I2C_DRIVER` |I2C peripheral to use - I2C1 -> `I2CD1`, I2C2 -> `I2CD2` etc.|`I2CD1`| |`I2C_DRIVER` |I2C peripheral to use - I2C1 -> `I2CD1`, I2C2 -> `I2CD2` etc.|`I2CD1`|
|`I2C1_SCL_PIN` |The pin definition for SCL |`B6` | |`I2C1_SCL_PIN` |The pin to use for SCL |`B6` |
|`I2C1_SCL_PAL_MODE`|The alternate function mode for SCL |`4` | |`I2C1_SCL_PAL_MODE`|The alternate function mode for SCL |`4` |
|`I2C1_SDA_PIN` |The pin definition for SDA |`B7` | |`I2C1_SDA_PIN` |The pin to use for SDA |`B7` |
|`I2C1_SDA_PAL_MODE`|The alternate function mode for SDA |`4` | |`I2C1_SDA_PAL_MODE`|The alternate function mode for SDA |`4` |
The following configuration values depend on the specific MCU in use. ::: tip
Currently only a single I2C peripheral is supported, therefore the `I2C1_*` defines are used for configuration regardless of the selected peripheral.
:::
The following configuration values are dependent on the ChibiOS I2C LLD, which is dictated by the microcontroller.
### I2Cv1 {#arm-configuration-i2cv1} ### I2Cv1 {#arm-configuration-i2cv1}
@ -168,6 +177,29 @@ Send multiple bytes to the selected I2C device.
--- ---
### `i2c_status_t i2c_transmit_P(uint8_t address, const uint8_t* data, uint16_t length, uint16_t timeout)` {#api-i2c-transmit-p}
Send multiple bytes from PROGMEM to the selected I2C device.
On ARM devices, this function is simply an alias for `i2c_transmit(address, data, length, timeout)`.
#### Arguments {#api-i2c-transmit-p-arguments}
- `uint8_t address`
The 7-bit I2C address of the device.
- `const uint8_t* data`
A pointer to the data to transmit.
- `uint16_t length`
The number of bytes to write. Take care not to overrun the length of `data`.
- `uint16_t timeout`
The time in milliseconds to wait for a response from the target device.
#### Return Value {#api-i2c-transmit-p-return}
`I2C_STATUS_TIMEOUT` if the timeout period elapses, `I2C_STATUS_ERROR` if some other error occurs, otherwise `I2C_STATUS_SUCCESS`.
---
### `i2c_status_t i2c_receive(uint8_t address, uint8_t* data, uint16_t length, uint16_t timeout)` {#api-i2c-receive} ### `i2c_status_t i2c_receive(uint8_t address, uint8_t* data, uint16_t length, uint16_t timeout)` {#api-i2c-receive}
Receive multiple bytes from the selected I2C device. Receive multiple bytes from the selected I2C device.
@ -177,7 +209,7 @@ Receive multiple bytes from the selected I2C device.
- `uint8_t address` - `uint8_t address`
The 7-bit I2C address of the device. The 7-bit I2C address of the device.
- `uint8_t* data` - `uint8_t* data`
A pointer to the buffer to read into. A pointer to a buffer to read into.
- `uint16_t length` - `uint16_t length`
The number of bytes to read. Take care not to overrun the length of `data`. The number of bytes to read. Take care not to overrun the length of `data`.
- `uint16_t timeout` - `uint16_t timeout`
@ -191,7 +223,7 @@ Receive multiple bytes from the selected I2C device.
### `i2c_status_t i2c_write_register(uint8_t devaddr, uint8_t regaddr, const uint8_t* data, uint16_t length, uint16_t timeout)` {#api-i2c-write-register} ### `i2c_status_t i2c_write_register(uint8_t devaddr, uint8_t regaddr, const uint8_t* data, uint16_t length, uint16_t timeout)` {#api-i2c-write-register}
Writes to a register with an 8-bit address on the I2C device. Write to a register with an 8-bit address on the I2C device.
#### Arguments {#api-i2c-write-register-arguments} #### Arguments {#api-i2c-write-register-arguments}
@ -214,7 +246,7 @@ Writes to a register with an 8-bit address on the I2C device.
### `i2c_status_t i2c_write_register16(uint8_t devaddr, uint16_t regaddr, const uint8_t* data, uint16_t length, uint16_t timeout)` {#api-i2c-write-register16} ### `i2c_status_t i2c_write_register16(uint8_t devaddr, uint16_t regaddr, const uint8_t* data, uint16_t length, uint16_t timeout)` {#api-i2c-write-register16}
Writes to a register with a 16-bit address (big endian) on the I2C device. Write to a register with a 16-bit address (big endian) on the I2C device.
#### Arguments {#api-i2c-write-register16-arguments} #### Arguments {#api-i2c-write-register16-arguments}
@ -237,7 +269,7 @@ Writes to a register with a 16-bit address (big endian) on the I2C device.
### `i2c_status_t i2c_read_register(uint8_t devaddr, uint8_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout)` {#api-i2c-read-register} ### `i2c_status_t i2c_read_register(uint8_t devaddr, uint8_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout)` {#api-i2c-read-register}
Reads from a register with an 8-bit address on the I2C device. Read from a register with an 8-bit address on the I2C device.
#### Arguments {#api-i2c-read-register-arguments} #### Arguments {#api-i2c-read-register-arguments}
@ -260,7 +292,7 @@ Reads from a register with an 8-bit address on the I2C device.
### `i2c_status_t i2c_read_register16(uint8_t devaddr, uint16_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout)` {#api-i2c-read-register16} ### `i2c_status_t i2c_read_register16(uint8_t devaddr, uint16_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout)` {#api-i2c-read-register16}
Reads from a register with a 16-bit address (big endian) on the I2C device. Read from a register with a 16-bit address (big endian) on the I2C device.
#### Arguments {#api-i2c-read-register16-arguments} #### Arguments {#api-i2c-read-register16-arguments}
@ -283,16 +315,16 @@ Reads from a register with a 16-bit address (big endian) on the I2C device.
### `i2c_status_t i2c_ping_address(uint8_t address, uint16_t timeout)` {#api-i2c-ping-address} ### `i2c_status_t i2c_ping_address(uint8_t address, uint16_t timeout)` {#api-i2c-ping-address}
Pings the I2C bus for a specific address. Ping the I2C bus for a specific address.
On ChibiOS a "best effort" attempt is made by reading a single byte from register 0 at the requested address. This should generally work except for I2C devices that do not not respond to a register 0 read request, which will result in a false negative result (unsuccessful response to ping attempt). On ChibiOS a "best effort" attempt is made by reading a single byte from register 0 at the given address. This should generally work except for I2C devices that do not not respond to a register 0 read request, which will result in a false negative result (unsuccessful response to ping attempt).
This function is weakly defined, meaning it can be overridden if necessary for your particular use case. This function is weakly defined, meaning it can be overridden if necessary for your particular use case.
#### Arguments {#api-i2c-ping-address-arguments} #### Arguments {#api-i2c-ping-address-arguments}
- `uint8_t address` - `uint8_t address`
The 7-bit I2C address of the device (ie. without the read/write bit - this will be set automatically). The 7-bit I2C address of the device.
- `uint16_t timeout` - `uint16_t timeout`
The time in milliseconds to wait for a response from the target device. The time in milliseconds to wait for a response from the target device.

141
drivers/i2c_master.h Normal file
View file

@ -0,0 +1,141 @@
// Copyright 2025 QMK
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <stdint.h>
/**
* \file
*
* \defgroup i2c_master I2C Master API
*
* \brief API to communicate with I2C devices.
* \{
*/
typedef int16_t i2c_status_t;
#define I2C_STATUS_SUCCESS (0)
#define I2C_STATUS_ERROR (-1)
#define I2C_STATUS_TIMEOUT (-2)
#define I2C_TIMEOUT_IMMEDIATE (0)
#define I2C_TIMEOUT_INFINITE (0xFFFF)
/**
* \brief Initialize the I2C driver. This function must be called only once, before any of the below functions can be called.
*
* This function is weakly defined, meaning it can be overridden if necessary for your particular use case.
*/
void i2c_init(void);
/**
* \brief Send multiple bytes to the selected I2C device.
*
* \param address The 7-bit I2C address of the device.
* \param data A pointer to the data to transmit.
* \param length The number of bytes to write. Take care not to overrun the length of `data`.
* \param timeout The time in milliseconds to wait for a response from the target device.
*
* \return `I2C_STATUS_TIMEOUT` if the timeout period elapses, `I2C_STATUS_ERROR` if some other error occurs, otherwise `I2C_STATUS_SUCCESS`.
*/
i2c_status_t i2c_transmit(uint8_t address, const uint8_t* data, uint16_t length, uint16_t timeout);
#if defined(__AVR__) || defined(__DOXYGEN__)
/**
* \brief Send multiple bytes from PROGMEM to the selected I2C device.
*
* On ARM devices, this function is simply an alias for i2c_transmit(address, data, length, timeout).
*
* \param address The 7-bit I2C address of the device.
* \param data A pointer to the data to transmit.
* \param length The number of bytes to write. Take care not to overrun the length of `data`.
* \param timeout The time in milliseconds to wait for a response from the target device.
*
* \return `I2C_STATUS_TIMEOUT` if the timeout period elapses, `I2C_STATUS_ERROR` if some other error occurs, otherwise `I2C_STATUS_SUCCESS`.
*/
i2c_status_t i2c_transmit_P(uint8_t address, const uint8_t* data, uint16_t length, uint16_t timeout);
#else
# define i2c_transmit_P(address, data, length, timeout) i2c_transmit(address, data, length, timeout)
#endif
/**
* \brief Receive multiple bytes from the selected I2C device.
*
* \param address The 7-bit I2C address of the device.
* \param data A pointer to a buffer to read into.
* \param length The number of bytes to read. Take care not to overrun the length of `data`.
* \param timeout The time in milliseconds to wait for a response from the target device.
*
* \return `I2C_STATUS_TIMEOUT` if the timeout period elapses, `I2C_STATUS_ERROR` if some other error occurs, otherwise `I2C_STATUS_SUCCESS`.
*/
i2c_status_t i2c_receive(uint8_t address, uint8_t* data, uint16_t length, uint16_t timeout);
/**
* \brief Write to a register with an 8-bit address on the I2C device.
*
* \param devaddr The 7-bit I2C address of the device.
* \param regaddr The register address to write to.
* \param data A pointer to the data to transmit.
* \param length The number of bytes to write. Take care not to overrun the length of `data`.
* \param timeout The time in milliseconds to wait for a response from the target device.
*
* \return `I2C_STATUS_TIMEOUT` if the timeout period elapses, `I2C_STATUS_ERROR` if some other error occurs, otherwise `I2C_STATUS_SUCCESS`.
*/
i2c_status_t i2c_write_register(uint8_t devaddr, uint8_t regaddr, const uint8_t* data, uint16_t length, uint16_t timeout);
/**
* \brief Write to a register with a 16-bit address (big endian) on the I2C device.
*
* \param devaddr The 7-bit I2C address of the device.
* \param regaddr The register address to write to.
* \param data A pointer to the data to transmit.
* \param length The number of bytes to write. Take care not to overrun the length of `data`.
* \param timeout The time in milliseconds to wait for a response from the target device.
*
* \return `I2C_STATUS_TIMEOUT` if the timeout period elapses, `I2C_STATUS_ERROR` if some other error occurs, otherwise `I2C_STATUS_SUCCESS`.
*/
i2c_status_t i2c_write_register16(uint8_t devaddr, uint16_t regaddr, const uint8_t* data, uint16_t length, uint16_t timeout);
/**
* \brief Read from a register with an 8-bit address on the I2C device.
*
* \param devaddr The 7-bit I2C address of the device.
* \param regaddr The register address to read from.
* \param data A pointer to a buffer to read into.
* \param length The number of bytes to read. Take care not to overrun the length of `data`.
* \param timeout The time in milliseconds to wait for a response from the target device.
*
* \return `I2C_STATUS_TIMEOUT` if the timeout period elapses, `I2C_STATUS_ERROR` if some other error occurs, otherwise `I2C_STATUS_SUCCESS`.
*/
i2c_status_t i2c_read_register(uint8_t devaddr, uint8_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout);
/**
* \brief Read from a register with a 16-bit address (big endian) on the I2C device.
*
* \param devaddr The 7-bit I2C address of the device.
* \param regaddr The register address to read from.
* \param data A pointer to a buffer to read into.
* \param length The number of bytes to read. Take care not to overrun the length of `data`.
* \param timeout The time in milliseconds to wait for a response from the target device.
*
* \return `I2C_STATUS_TIMEOUT` if the timeout period elapses, `I2C_STATUS_ERROR` if some other error occurs, otherwise `I2C_STATUS_SUCCESS`.
*/
i2c_status_t i2c_read_register16(uint8_t devaddr, uint16_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout);
/**
* \brief Ping the I2C bus for a specific address.
*
* On ChibiOS a "best effort" attempt is made by reading a single byte from register 0 at the given address. This should generally work except for I2C devices that do not not respond to a register 0 read request, which will result in a false negative result (unsuccessful response to ping attempt).
*
* This function is weakly defined, meaning it can be overridden if necessary for your particular use case.
*
* \param address The 7-bit I2C address of the device.
* \param timeout The time in milliseconds to wait for a response from the target device.
*
* \return `I2C_STATUS_TIMEOUT` if the timeout period elapses, `I2C_STATUS_ERROR` if some other error occurs, otherwise `I2C_STATUS_SUCCESS`.
*/
i2c_status_t i2c_ping_address(uint8_t address, uint16_t timeout);
/** \} */

View file

@ -1,41 +0,0 @@
/* Copyright (C) 2019 Elia Ritterbusch
+
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
/* Library made by: g4lvanix
* GitHub repository: https://github.com/g4lvanix/I2C-master-lib
*/
#pragma once
#include <stdint.h>
typedef int16_t i2c_status_t;
#define I2C_STATUS_SUCCESS (0)
#define I2C_STATUS_ERROR (-1)
#define I2C_STATUS_TIMEOUT (-2)
#define I2C_TIMEOUT_IMMEDIATE (0)
#define I2C_TIMEOUT_INFINITE (0xFFFF)
void i2c_init(void);
i2c_status_t i2c_transmit(uint8_t address, const uint8_t* data, uint16_t length, uint16_t timeout);
i2c_status_t i2c_transmit_P(uint8_t address, const uint8_t* data, uint16_t length, uint16_t timeout);
i2c_status_t i2c_receive(uint8_t address, uint8_t* data, uint16_t length, uint16_t timeout);
i2c_status_t i2c_write_register(uint8_t devaddr, uint8_t regaddr, const uint8_t* data, uint16_t length, uint16_t timeout);
i2c_status_t i2c_write_register16(uint8_t devaddr, uint16_t regaddr, const uint8_t* data, uint16_t length, uint16_t timeout);
i2c_status_t i2c_read_register(uint8_t devaddr, uint8_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout);
i2c_status_t i2c_read_register16(uint8_t devaddr, uint16_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout);
i2c_status_t i2c_ping_address(uint8_t address, uint16_t timeout);

View file

@ -29,17 +29,37 @@
#include "i2c_master.h" #include "i2c_master.h"
#include "gpio.h" #include "gpio.h"
#include "chibios_config.h" #include "chibios_config.h"
#include <string.h>
#include <ch.h> #include <ch.h>
#include <hal.h> #include <hal.h>
#ifndef I2C_DRIVER
# define I2C_DRIVER I2CD1
#endif
#ifndef I2C1_SCL_PIN #ifndef I2C1_SCL_PIN
# define I2C1_SCL_PIN B6 # define I2C1_SCL_PIN B6
#endif #endif
#ifndef I2C1_SCL_PAL_MODE
# ifdef USE_GPIOV1
# define I2C1_SCL_PAL_MODE PAL_MODE_ALTERNATE_OPENDRAIN
# else
# define I2C1_SCL_PAL_MODE 4
# endif
#endif
#ifndef I2C1_SDA_PIN #ifndef I2C1_SDA_PIN
# define I2C1_SDA_PIN B7 # define I2C1_SDA_PIN B7
#endif #endif
#ifndef I2C1_SDA_PAL_MODE
# ifdef USE_GPIOV1
# define I2C1_SDA_PAL_MODE PAL_MODE_ALTERNATE_OPENDRAIN
# else
# define I2C1_SDA_PAL_MODE 4
# endif
#endif
#ifdef USE_I2CV1 #ifdef USE_I2CV1
# ifndef I2C1_OPMODE # ifndef I2C1_OPMODE
# define I2C1_OPMODE OPMODE_I2C # define I2C1_OPMODE OPMODE_I2C
@ -70,27 +90,6 @@
# endif # endif
#endif #endif
#ifndef I2C_DRIVER
# define I2C_DRIVER I2CD1
#endif
#ifdef USE_GPIOV1
# ifndef I2C1_SCL_PAL_MODE
# define I2C1_SCL_PAL_MODE PAL_MODE_ALTERNATE_OPENDRAIN
# endif
# ifndef I2C1_SDA_PAL_MODE
# define I2C1_SDA_PAL_MODE PAL_MODE_ALTERNATE_OPENDRAIN
# endif
#else
// The default PAL alternate modes are used to signal that the pins are used for I2C
# ifndef I2C1_SCL_PAL_MODE
# define I2C1_SCL_PAL_MODE 4
# endif
# ifndef I2C1_SDA_PAL_MODE
# define I2C1_SDA_PAL_MODE 4
# endif
#endif
static const I2CConfig i2cconfig = { static const I2CConfig i2cconfig = {
#if defined(USE_I2CV1_CONTRIB) #if defined(USE_I2CV1_CONTRIB)
I2C1_CLOCK_SPEED, I2C1_CLOCK_SPEED,

View file

@ -1,42 +0,0 @@
/* Copyright 2018 Jack Humbert
* Copyright 2018 Yiancar
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* This library follows the convention of the AVR i2c_master library.
* As a result addresses are expected to be already shifted (addr << 1).
* I2CD1 is the default driver which corresponds to pins B6 and B7. This
* can be changed.
* Please ensure that HAL_USE_I2C is TRUE in the halconf.h file and that
* STM32_I2C_USE_I2C1 is TRUE in the mcuconf.h file.
*/
#pragma once
#include <stdint.h>
typedef int16_t i2c_status_t;
#define I2C_STATUS_SUCCESS (0)
#define I2C_STATUS_ERROR (-1)
#define I2C_STATUS_TIMEOUT (-2)
void i2c_init(void);
i2c_status_t i2c_transmit(uint8_t address, const uint8_t* data, uint16_t length, uint16_t timeout);
i2c_status_t i2c_receive(uint8_t address, uint8_t* data, uint16_t length, uint16_t timeout);
i2c_status_t i2c_write_register(uint8_t devaddr, uint8_t regaddr, const uint8_t* data, uint16_t length, uint16_t timeout);
i2c_status_t i2c_write_register16(uint8_t devaddr, uint16_t regaddr, const uint8_t* data, uint16_t length, uint16_t timeout);
i2c_status_t i2c_read_register(uint8_t devaddr, uint8_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout);
i2c_status_t i2c_read_register16(uint8_t devaddr, uint16_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout);
i2c_status_t i2c_ping_address(uint8_t address, uint16_t timeout);