Threads I2C and SPI (#3471)
Issues due date / Add labels to issues (push) Has been cancelled
Doxygen / build (push) Has been cancelled

* [threads] Add binary semaphore timeout wait.

* [i2c] Update blocking functions to be RTOS aware.

* [SHT25] Refactor SHT25 driver as threaded I2C example.

* [spi] Update blocking function to be RTOS aware.

* [AMT22] add AMT22 driver using SPI blocking mode.

---------

Co-authored-by: Fabien-B <Fabien-B@github.com>
This commit is contained in:
Fabien-B
2025-06-23 20:08:33 +02:00
committed by GitHub
parent 486ae432e1
commit 982c6947a3
25 changed files with 565 additions and 281 deletions
+33
View File
@@ -0,0 +1,33 @@
<!DOCTYPE module SYSTEM "module.dtd">
<module name="encoder_amt22" dir="sensors" task="sensors">
<doc>
<description>Driver for AMT22 encoder from CUI devices.</description>
</doc>
<dep>
<depends>spi_master</depends>
</dep>
<header>
<file name="encoder_amt22.h"/>
</header>
<init fun="encoder_amt22_init()"/>
<periodic fun="encoder_amt22_periodic()" freq="50.0" autorun="TRUE"/>
<makefile target="ap">
<configure name="AMT22_SPI_DEV" default="SPI2" case="upper|lower"/>
<configure name="AMT22_SPI_SLAVE_IDX" default="SPI_SLAVE0"/>
<define name="USE_$(AMT22_SPI_DEV_UPPER)"/>
<define name="USE_$(AMT22_SPI_SLAVE_IDX)"/>
<define name="AMT22_SPI_DEV" value="$(AMT22_SPI_DEV_LOWER)"/>
<define name="AMT22_SPI_SLAVE_IDX" value="$(AMT22_SPI_SLAVE_IDX)"/>
<file name="encoder_amt22.c"/>
<file name="amt22.c" dir="peripherals"/>
<test>
<define name="AMT22_SPI_DEV" value="spi1" />
<define name="USE_SPI1" />
<define name="AMT22_SPI_SLAVE_IDX" value="0" />
<define name="SPI_MASTER"/>
<define name="DOWNLINK_TRANSPORT" value="pprz_tp"/>
<define name="DOWNLINK_DEVICE" value="uart0"/>
<define name="USE_UART0" />
</test>
</makefile>
</module>
+1 -5
View File
@@ -3,18 +3,14 @@
<module name="humid_sht_i2c" dir="meteo">
<doc>
<description>Sensirion SHT25 humidity sensor (I2C)</description>
<define name="SCP_I2C_DEV" value="i2cX" description="select i2c peripheral to use (default i2c0)"/>
<define name="SHT_I2C_DEV" value="i2cX" description="select i2c peripheral to use (default i2c0)"/>
</doc>
<header>
<file name="humid_sht_i2c.h"/>
</header>
<init fun="humid_sht_init_i2c()"/>
<periodic fun="humid_sht_periodic_i2c()" freq="4" delay="0."/>
<periodic fun="humid_sht_p_temp()" freq="4" delay="0.4"/>
<periodic fun="humid_sht_p_humid()" freq="4" delay="0.6"/>
<event fun="humid_sht_event_i2c()"/>
<makefile target="ap">
<file name="humid_sht_i2c.c"/>
</makefile>
</module>
@@ -235,6 +235,7 @@ static void handle_i2c_thd(struct i2c_periph *p)
}
i2cReleaseBus((I2CDriver *)p->reg_addr);
pprz_bsem_signal(&t->bsem);
}
/**
@@ -440,6 +441,7 @@ static bool i2c_chibios_submit(struct i2c_periph *p, struct i2c_transaction *t)
p->trans_insert_idx = temp;
chSysUnlock();
pprz_bsem_init(&t->bsem, true);
chSemSignal(&((struct i2c_init *)p->init_struct)->sem);
// transaction submitted
return TRUE;
@@ -389,6 +389,7 @@ static void handle_spi_thd(struct spi_periph *p)
t->after_cb(t);
}
pprz_bsem_signal(&t->bsem);
}
/**
@@ -552,6 +553,7 @@ bool spi_submit(struct spi_periph *p, struct spi_transaction *t)
p->trans_insert_idx = idx;
chSysUnlock();
pprz_bsem_init(&t->bsem, true);
chSemSignal(&((struct spi_init *)p->init_struct)->sem);
// transaction submitted
return TRUE;
@@ -44,6 +44,16 @@ void pprz_bsem_wait(pprz_bsem_t* bsem) {
chBSemWait(&bsem->bsem);
}
int pprz_bsem_wait_timeout(pprz_bsem_t* bsem, float timeout) {
sysinterval_t chtimeout;
if(timeout < 0.002) {
chtimeout = chTimeUS2I(timeout*1e6);
} else {
chtimeout = chTimeMS2I(timeout*1e3);
}
return chBSemWaitTimeout(&bsem->bsem, chtimeout);
}
void pprz_bsem_signal(pprz_bsem_t* bsem) {
chBSemSignal(&bsem->bsem);
}
@@ -104,6 +104,7 @@ static bool i2c_linux_submit(struct i2c_periph *p, struct i2c_transaction *t)
p->trans[p->trans_insert_idx] = t;
p->trans_insert_idx = next_idx;
pprz_bsem_init(&t->bsem, true);
/* wake handler thread */
pthread_cond_signal(condition);
pthread_mutex_unlock(mutex);
@@ -199,6 +200,7 @@ static void *i2c_thread(void *data)
pthread_mutex_lock(mutex);
p->trans_extract_idx = (p->trans_extract_idx + 1) % I2C_TRANSACTION_QUEUE_LEN;
pthread_mutex_unlock(mutex);
pprz_bsem_signal(&t->bsem);
}
return NULL;
}
@@ -15,6 +15,7 @@
#include "stdbool.h"
#include <semaphore.h>
#include <stdlib.h>
#include <time.h>
int pprz_mtx_init(pprz_mutex_t* mtx) {
@@ -44,6 +45,16 @@ void pprz_bsem_wait(pprz_bsem_t* bsem) {
sem_wait(&bsem->sem);
}
int pprz_bsem_wait_timeout(pprz_bsem_t* bsem, float timeout) {
struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
time_t timeout_secs = (time_t)timeout;
long timeout_nsecs = (timeout - timeout_secs) * 1e9;
ts.tv_sec += timeout_secs;
ts.tv_nsec += timeout_nsecs;
return sem_timedwait(&bsem->sem, &ts);
}
void pprz_bsem_signal(pprz_bsem_t* bsem) {
int val;
pthread_mutex_lock(&bsem->mtx);
@@ -51,6 +51,18 @@ void pprz_bsem_wait(pprz_bsem_t* bsem) {
bsem->value = 0;
}
int pprz_bsem_wait_timeout(pprz_bsem_t* bsem, float timeout) {
float time_end = get_sys_time_float() + timeout;
while(get_sys_time_float() - time_end > 0) {
// active wait
if(bsem->value) {
bsem->value = 0;
return 0;
}
}
return -1;
}
void pprz_bsem_signal(pprz_bsem_t* bsem) {
bsem->value = 1;
}
+6 -6
View File
@@ -74,7 +74,7 @@ void actuators_bebop_commit(void)
{
// Receive the status
actuators_bebop.i2c_trans.buf[0] = ACTUATORS_BEBOP_GET_OBS_DATA;
i2c_blocking_transceive(&i2c1, &actuators_bebop.i2c_trans, actuators_bebop.i2c_trans.slave_addr, 1, 13);
i2c_blocking_transceive(&i2c1, &actuators_bebop.i2c_trans, actuators_bebop.i2c_trans.slave_addr, 1, 13, 0.5);
// Update status
electrical.vsupply = (float)(actuators_bebop.i2c_trans.buf[9] + (actuators_bebop.i2c_trans.buf[8] << 8)) / 1000.f;
@@ -94,7 +94,7 @@ void actuators_bebop_commit(void)
if (actuators_bebop.i2c_trans.buf[10] != 4 && actuators_bebop.i2c_trans.buf[10] != 2 && autopilot_get_motors_on()) {
// Reset the error
actuators_bebop.i2c_trans.buf[0] = ACTUATORS_BEBOP_CLEAR_ERROR;
i2c_blocking_transmit(&i2c1, &actuators_bebop.i2c_trans, actuators_bebop.i2c_trans.slave_addr, 1);
i2c_blocking_transmit(&i2c1, &actuators_bebop.i2c_trans, actuators_bebop.i2c_trans.slave_addr, 1, 0.5);
// Start the motors
actuators_bebop.i2c_trans.buf[0] = ACTUATORS_BEBOP_START_PROP;
@@ -104,12 +104,12 @@ void actuators_bebop_commit(void)
#else
actuators_bebop.i2c_trans.buf[1] = 0b00000101;
#endif
i2c_blocking_transmit(&i2c1, &actuators_bebop.i2c_trans, actuators_bebop.i2c_trans.slave_addr, 2);
i2c_blocking_transmit(&i2c1, &actuators_bebop.i2c_trans, actuators_bebop.i2c_trans.slave_addr, 2, 0.5);
}
// Stop the motors
else if (actuators_bebop.i2c_trans.buf[10] == 4 && !autopilot_get_motors_on()) {
actuators_bebop.i2c_trans.buf[0] = ACTUATORS_BEBOP_STOP_PROP;
i2c_blocking_transmit(&i2c1, &actuators_bebop.i2c_trans, actuators_bebop.i2c_trans.slave_addr, 1);
i2c_blocking_transmit(&i2c1, &actuators_bebop.i2c_trans, actuators_bebop.i2c_trans.slave_addr, 1, 0.5);
} else if (actuators_bebop.i2c_trans.buf[10] == 4 && autopilot_get_motors_on()) {
// Send the commands
actuators_bebop.i2c_trans.buf[0] = ACTUATORS_BEBOP_SET_REF_SPEED;
@@ -126,14 +126,14 @@ void actuators_bebop_commit(void)
#pragma GCC diagnostic ignored "-Wcast-qual"
actuators_bebop.i2c_trans.buf[10] = actuators_bebop_checksum((uint8_t *)actuators_bebop.i2c_trans.buf, 9);
#pragma GCC diagnostic pop
i2c_blocking_transmit(&i2c1, &actuators_bebop.i2c_trans, actuators_bebop.i2c_trans.slave_addr, 11);
i2c_blocking_transmit(&i2c1, &actuators_bebop.i2c_trans, actuators_bebop.i2c_trans.slave_addr, 11, 0.5);
}
// Update the LEDs
if (actuators_bebop.led != (led_hw_values & 0x3)) {
actuators_bebop.i2c_trans.buf[0] = ACTUATORS_BEBOP_TOGGLE_GPIO;
actuators_bebop.i2c_trans.buf[1] = (led_hw_values & 0x3);
i2c_blocking_transmit(&i2c1, &actuators_bebop.i2c_trans, actuators_bebop.i2c_trans.slave_addr, 2);
i2c_blocking_transmit(&i2c1, &actuators_bebop.i2c_trans, actuators_bebop.i2c_trans.slave_addr, 2, 0.5);
actuators_bebop.led = led_hw_values & 0x3;
}
+2 -2
View File
@@ -194,7 +194,7 @@ static void write_reg(struct mt9f002_t *mt, uint16_t addr, uint32_t val, uint8_t
}
// Transmit the buffer
i2c_blocking_transmit(mt->i2c_periph, &mt->i2c_trans, MT9F002_ADDRESS, len + 2);
i2c_blocking_transmit(mt->i2c_periph, &mt->i2c_trans, MT9F002_ADDRESS, len + 2, 0.5);
}
/**
@@ -207,7 +207,7 @@ static uint32_t read_reg(struct mt9f002_t *mt, uint16_t addr, uint8_t len)
mt->i2c_trans.buf[1] = addr & 0xFF;
// Transmit the buffer and receive back
i2c_blocking_transceive(mt->i2c_periph, &mt->i2c_trans, MT9F002_ADDRESS, 2, len);
i2c_blocking_transceive(mt->i2c_periph, &mt->i2c_trans, MT9F002_ADDRESS, 2, len, 0.5);
/* Fix signdness */
for (uint8_t i = 0; i < len; i++) {
+3 -3
View File
@@ -231,7 +231,7 @@ static void write_reg(struct mt9v117_t *mt, uint16_t addr, uint32_t val, uint16_
}
// Transmit the buffer
i2c_blocking_transmit(mt->i2c_periph, &mt->i2c_trans, MT9V117_ADDRESS, len + 2);
i2c_blocking_transmit(mt->i2c_periph, &mt->i2c_trans, MT9V117_ADDRESS, len + 2, 0.5);
}
/**
@@ -244,7 +244,7 @@ static uint32_t read_reg(struct mt9v117_t *mt, uint16_t addr, uint16_t len)
mt->i2c_trans.buf[1] = addr & 0xFF;
// Transmit the buffer and receive back
i2c_blocking_transceive(mt->i2c_periph, &mt->i2c_trans, MT9V117_ADDRESS, 2, len);
i2c_blocking_transceive(mt->i2c_periph, &mt->i2c_trans, MT9V117_ADDRESS, 2, len, 0.5);
/* Fix sigdness */
for (uint8_t i = 0; i < len; i++) {
@@ -302,7 +302,7 @@ static inline void mt9v117_write_patch(struct mt9v117_t *mt)
}
// Transmit the buffer
i2c_blocking_transmit(mt->i2c_periph, &mt->i2c_trans, mt->i2c_trans.slave_addr, mt9v117_patch_lines[i].len);
i2c_blocking_transmit(mt->i2c_periph, &mt->i2c_trans, mt->i2c_trans.slave_addr, mt9v117_patch_lines[i].len, 0.5);
}
write_reg(mt, MT9V117_LOGICAL_ADDRESS_ACCESS, 0x0000, 2);
+5 -5
View File
@@ -119,7 +119,7 @@ void actuators_disco_commit(void)
// Receive the status
actuators_disco.i2c_trans.buf[0] = ACTUATORS_DISCO_GET_OBS_DATA;
i2c_blocking_transceive(&i2c1, &actuators_disco.i2c_trans, actuators_disco.i2c_trans.slave_addr, 1, sizeof(obs_data));
i2c_blocking_transceive(&i2c1, &actuators_disco.i2c_trans, actuators_disco.i2c_trans.slave_addr, 1, sizeof(obs_data), 0.5);
// copy data from buffer
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wcast-qual"
@@ -149,17 +149,17 @@ void actuators_disco_commit(void)
actuators_disco.motor_rpm > DISCO_BLDC_START_MOTOR_THRESHOLD) {
// Reset the error
actuators_disco.i2c_trans.buf[0] = ACTUATORS_DISCO_CLEAR_ERROR;
i2c_blocking_transmit(&i2c1, &actuators_disco.i2c_trans, actuators_disco.i2c_trans.slave_addr, 1);
i2c_blocking_transmit(&i2c1, &actuators_disco.i2c_trans, actuators_disco.i2c_trans.slave_addr, 1, 0.5);
// Start the motors
actuators_disco.i2c_trans.buf[0] = ACTUATORS_DISCO_START_PROP;
i2c_blocking_transmit(&i2c1, &actuators_disco.i2c_trans, actuators_disco.i2c_trans.slave_addr, 1);
i2c_blocking_transmit(&i2c1, &actuators_disco.i2c_trans, actuators_disco.i2c_trans.slave_addr, 1, 0.5);
}
// Stop the motors
else if ((bldc_status == DISCO_BLDC_STATUS_RUNNING || bldc_status == DISCO_BLDC_STATUS_RAMPUP) &&
actuators_disco.motor_rpm < DISCO_BLDC_START_MOTOR_THRESHOLD) {
actuators_disco.i2c_trans.buf[0] = ACTUATORS_DISCO_STOP_PROP;
i2c_blocking_transmit(&i2c1, &actuators_disco.i2c_trans, actuators_disco.i2c_trans.slave_addr, 1);
i2c_blocking_transmit(&i2c1, &actuators_disco.i2c_trans, actuators_disco.i2c_trans.slave_addr, 1, 0.5);
} else if (bldc_status == DISCO_BLDC_STATUS_RUNNING) {
// Send the commands
actuators_disco.i2c_trans.buf[0] = ACTUATORS_DISCO_SET_REF_SPEED;
@@ -170,7 +170,7 @@ void actuators_disco_commit(void)
#pragma GCC diagnostic ignored "-Wcast-qual"
actuators_disco.i2c_trans.buf[4] = actuators_disco_checksum((uint8_t *)actuators_disco.i2c_trans.buf, 3);
#pragma GCC diagnostic pop
i2c_blocking_transmit(&i2c1, &actuators_disco.i2c_trans, actuators_disco.i2c_trans.slave_addr, 11);
i2c_blocking_transmit(&i2c1, &actuators_disco.i2c_trans, actuators_disco.i2c_trans.slave_addr, 11, 0.5);
}
// Send ABI message
+21 -49
View File
@@ -229,73 +229,45 @@ bool i2c_transceive(struct i2c_periph *p, struct i2c_transaction *t,
return i2c_submit(p, t);
}
/** Default timeout for blocking I2C transactions */
#ifndef I2C_BLOCKING_TIMEOUT
#define I2C_BLOCKING_TIMEOUT 1.f
#endif
static enum I2CTransactionStatus i2c_blocking_submit(struct i2c_periph *p, struct i2c_transaction *t, float timeout) {
if (!i2c_submit(p, t)) {
return I2CTransFailed;
}
bool i2c_blocking_transmit(struct i2c_periph *p, struct i2c_transaction *t,
uint8_t s_addr, uint8_t len)
// Wait for transaction to complete
if(pprz_bsem_wait_timeout(&t->bsem, timeout) == 0) {
return t->status;
} else {
return I2CTransFailed;
}
}
enum I2CTransactionStatus i2c_blocking_transmit(struct i2c_periph *p, struct i2c_transaction *t,
uint8_t s_addr, uint8_t len, float timeout)
{
t->type = I2CTransTx;
t->slave_addr = s_addr;
t->len_w = len;
t->len_r = 0;
if (!i2c_submit(p, t)) {
return false;
}
// Wait for transaction to complete
float start_t = get_sys_time_float();
while (t->status == I2CTransPending || t->status == I2CTransRunning) {
if (p->spin) p->spin(p);
if (get_sys_time_float() - start_t > I2C_BLOCKING_TIMEOUT) {
break; // timeout after 1 second
}
}
return true;
return i2c_blocking_submit(p, t, timeout);
}
bool i2c_blocking_receive(struct i2c_periph *p, struct i2c_transaction *t,
uint8_t s_addr, uint16_t len)
enum I2CTransactionStatus i2c_blocking_receive(struct i2c_periph *p, struct i2c_transaction *t,
uint8_t s_addr, uint16_t len, float timeout)
{
t->type = I2CTransRx;
t->slave_addr = s_addr;
t->len_w = 0;
t->len_r = len;
if (!i2c_submit(p, t)) {
return false;
}
// Wait for transaction to complete
float start_t = get_sys_time_float();
while (t->status == I2CTransPending || t->status == I2CTransRunning) {
if (p->spin) p->spin(p);
if (get_sys_time_float() - start_t > I2C_BLOCKING_TIMEOUT) {
break; // timeout after 1 second
}
}
return true;
return i2c_blocking_submit(p, t, timeout);
}
bool i2c_blocking_transceive(struct i2c_periph *p, struct i2c_transaction *t,
uint8_t s_addr, uint8_t len_w, uint16_t len_r)
enum I2CTransactionStatus i2c_blocking_transceive(struct i2c_periph *p, struct i2c_transaction *t,
uint8_t s_addr, uint8_t len_w, uint16_t len_r, float timeout)
{
t->type = I2CTransTxRx;
t->slave_addr = s_addr;
t->len_w = len_w;
t->len_r = len_r;
if (!i2c_submit(p, t)) {
return false;
}
// Wait for transaction to complete
float start_t = get_sys_time_float();
while (t->status == I2CTransPending || t->status == I2CTransRunning) {
if (p->spin) p->spin(p);
if (get_sys_time_float() - start_t > I2C_BLOCKING_TIMEOUT) {
break; // timeout after 1 second
}
}
return true;
return i2c_blocking_submit(p, t, timeout);
}
+17 -9
View File
@@ -33,6 +33,7 @@
#include "std.h"
#include "mcu_periph/i2c_arch.h"
#include "modules/core/threads.h"
/**
* @addtogroup mcu_periph
@@ -124,6 +125,10 @@ struct i2c_transaction {
/** Transaction status.
*/
volatile enum I2CTransactionStatus status;
/** binary semaphore for blocking functions
*/
pprz_bsem_t bsem;
};
/** I2C transaction queue length.
@@ -328,10 +333,11 @@ extern bool i2c_transceive(struct i2c_periph *p, struct i2c_transaction *t,
* @param t i2c transaction
* @param s_addr slave address
* @param len number of bytes to transmit
* @return TRUE if insertion to the transaction queue succeeded
* @param timeout timeout in seconds after which the transaction is considered failed
* @return the status of the transaction, or I2CTransFailed if insertion to the transaction queue failed
*/
bool i2c_blocking_transmit(struct i2c_periph *p, struct i2c_transaction *t,
uint8_t s_addr, uint8_t len);
enum I2CTransactionStatus i2c_blocking_transmit(struct i2c_periph *p, struct i2c_transaction *t,
uint8_t s_addr, uint8_t len, float timeout);
/** Submit a read only transaction and wait for it to complete.
* Convenience function which is usually preferred over i2c_submit,
@@ -340,10 +346,11 @@ bool i2c_blocking_transmit(struct i2c_periph *p, struct i2c_transaction *t,
* @param t i2c transaction
* @param s_addr slave address
* @param len number of bytes to receive
* @return TRUE if insertion to the transaction queue succeeded
* @param timeout timeout in seconds after which the transaction is considered failed
* @return the status of the transaction, or I2CTransFailed if insertion to the transaction queue failed
*/
bool i2c_blocking_receive(struct i2c_periph *p, struct i2c_transaction *t,
uint8_t s_addr, uint16_t len);
enum I2CTransactionStatus i2c_blocking_receive(struct i2c_periph *p, struct i2c_transaction *t,
uint8_t s_addr, uint16_t len, float timeout);
/** Submit a write/read transaction and wait for it to complete.
* Convenience function which is usually preferred over i2c_submit,
@@ -353,10 +360,11 @@ bool i2c_blocking_receive(struct i2c_periph *p, struct i2c_transaction *t,
* @param s_addr slave address
* @param len_w number of bytes to transmit
* @param len_r number of bytes to receive
* @return TRUE if insertion to the transaction queue succeeded
* @param timeout timeout in seconds after which the transaction is considered failed
* @return the status of the transaction, or I2CTransFailed if insertion to the transaction queue failed
*/
bool i2c_blocking_transceive(struct i2c_periph *p, struct i2c_transaction *t,
uint8_t s_addr, uint8_t len_w, uint16_t len_r);
enum I2CTransactionStatus i2c_blocking_transceive(struct i2c_periph *p, struct i2c_transaction *t,
uint8_t s_addr, uint8_t len_w, uint16_t len_r, float timeout);
/** @}*/
/** @}*/
+14
View File
@@ -164,3 +164,17 @@ extern void spi_slave_init(struct spi_periph *p)
#endif /* SPI_SLAVE */
enum SPITransactionStatus spi_blocking_transceive(struct spi_periph *p, struct spi_transaction *t, float timeout) {
if (!spi_submit(p, t)) {
return SPITransFailed;
}
// Wait for transaction to complete
if(pprz_bsem_wait_timeout(&t->bsem, timeout) == 0) {
return t->status;
} else {
return SPITransFailed;
}
}
+5 -19
View File
@@ -35,11 +35,7 @@
#include "mcu_periph/spi_arch.h"
#include "mcu_periph/sys_time.h"
#ifndef SPI_BLOCKING_TIMEOUT
#define SPI_BLOCKING_TIMEOUT 1.f
#endif
#include "modules/core/threads.h"
/**
* @addtogroup mcu_periph
@@ -160,6 +156,7 @@ struct spi_transaction {
SPICallback before_cb; ///< NULL or function called before the transaction
SPICallback after_cb; ///< NULL or function called after the transaction
volatile enum SPITransactionStatus status;
pprz_bsem_t bsem;
};
/** SPI transaction queue length.
@@ -294,21 +291,10 @@ extern bool spi_submit(struct spi_periph *p, struct spi_transaction *t);
/** Perform a spi transaction (blocking).
* @param p spi peripheral to be used
* @param t spi transaction
* @return TRUE if transaction completed (success or failure)
* @param timeout timeout in seconds after which the transaction is considered failed
* @return the status of the transaction, or SPITransFailed if insertion to the transaction queue failed
*/
static inline bool spi_blocking_transceive(struct spi_periph *p, struct spi_transaction *t) {
if (!spi_submit(p, t)) {
return false;
}
// Wait for transaction to complete
float start_t = get_sys_time_float();
while (t->status == SPITransPending || t->status == SPITransRunning) {
if (get_sys_time_float() - start_t > SPI_BLOCKING_TIMEOUT) {
break;
}
}
return true;
}
enum SPITransactionStatus spi_blocking_transceive(struct spi_periph *p, struct spi_transaction *t, float timeout);
/** Select a slave.
* @param slave slave id
+8
View File
@@ -34,6 +34,14 @@ int pprz_mtx_unlock(pprz_mutex_t* mtx);
void pprz_bsem_init(pprz_bsem_t* bsem, bool taken);
void pprz_bsem_wait(pprz_bsem_t* bsem);
/**
* @brief Wait on semaphore no more than timeout.
* @param timeout in seconds
* @returns 0 on success
*/
int pprz_bsem_wait_timeout(pprz_bsem_t* bsem, float timeout);
void pprz_bsem_signal(pprz_bsem_t* bsem);
/**
+139 -154
View File
@@ -27,9 +27,6 @@
#include "modules/meteo/humid_sht_i2c.h"
#include "mcu_periph/i2c.h"
#include "mcu_periph/uart.h"
#include "pprzlink/messages.h"
#include "modules/datalink/downlink.h"
@@ -40,14 +37,146 @@
#define SHT_SLAVE_ADDR 0x80
struct i2c_transaction sht_trans;
uint8_t sht_status;
uint8_t sht_serial[8] = {0};
uint32_t sht_serial1 = 0, sht_serial2 = 0;
uint16_t humidsht_i2c, tempsht_i2c;
float fhumidsht_i2c, ftempsht_i2c;
#define SHT2_WRITE_USER 0xE6
#define SHT2_READ_USER 0xE7
#define SHT2_TRIGGER_TEMP 0xF3
#define SHT2_TRIGGER_HUMID 0xF5
#define SHT2_SOFT_RESET 0xFE
int8_t humid_sht_crc(volatile uint8_t *data)
static void humid_sht_thd(void* arg);
static void sht_read_serial(struct sht_humid_t* sht);
static int sht_read_temp(struct sht_humid_t* sht);
static int sht_read_humid(struct sht_humid_t* sht);
static int8_t humid_sht_crc(volatile uint8_t *data);
static struct sht_humid_t sht;
void humid_sht_init_i2c(void)
{
sht.sht_status = SHT2_UNINIT;
pprz_bsem_init(&sht.bsem_sht_status, true);
pprz_thread_create(&sht.thd_handle, 512, "humid_sht25", PPRZ_NORMAL_PRIO+1, humid_sht_thd, &sht);
}
void humid_sht_periodic_i2c(void)
{
if(sht.sht_status == SHT2_READING) {
/* send serial number every 30 seconds */
RunOnceEvery((4 * 30), DOWNLINK_SEND_SHT_I2C_SERIAL(DefaultChannel, DefaultDevice, &sht.sht_serial1, &sht.sht_serial2));
}
if(pprz_bsem_wait_timeout(&sht.bsem_sht_status, 0) == 0) {
DOWNLINK_SEND_SHT_I2C_STATUS(DefaultChannel, DefaultDevice, &sht.humidsht_i2c, &sht.tempsht_i2c, &sht.fhumidsht_i2c, &sht.ftempsht_i2c);
}
}
static void humid_sht_thd(void* arg) {
sys_time_usleep(100000);
struct sht_humid_t* sht = (struct sht_humid_t*)arg;
/* soft reset sensor */
sht->sht_trans.buf[0] = SHT2_SOFT_RESET;
while(i2c_blocking_transmit(&SHT_I2C_DEV, &sht->sht_trans, SHT_SLAVE_ADDR, 1, 0.5) != I2CTransSuccess) {
sys_time_usleep(100000);
}
/* read serial number */
sht_read_serial(sht);
sht->sht_status = SHT2_READING;
while(true) {
/* read temperature */
if(sht_read_temp(sht)) {continue;}
/* read humidity */
if(sht_read_humid(sht)) {continue;}
/* signal semaphore to handle data */
pprz_bsem_signal(&sht->bsem_sht_status);
sys_time_usleep(500000);
}
}
static void sht_read_serial(struct sht_humid_t* sht) {
uint8_t sht_serial[8] = {0};
/* request serial number part 1 */
sht->sht_trans.buf[0] = 0xFA;
sht->sht_trans.buf[1] = 0x0F;
while(i2c_blocking_transceive(&SHT_I2C_DEV, &sht->sht_trans, SHT_SLAVE_ADDR, 2, 8, 0.5) != I2CTransSuccess) {
sys_time_usleep(10000);
}
/* read serial number part 1 */
sht_serial[5] = sht->sht_trans.buf[0];
sht_serial[4] = sht->sht_trans.buf[2];
sht_serial[3] = sht->sht_trans.buf[4];
sht_serial[2] = sht->sht_trans.buf[6];
/* request serial number part 2 */
sht->sht_trans.buf[0] = 0xFC;
sht->sht_trans.buf[1] = 0xC9;
while(i2c_blocking_transceive(&SHT_I2C_DEV, &sht->sht_trans, SHT_SLAVE_ADDR, 2, 6, 0.5) != I2CTransSuccess) {
sys_time_usleep(10000);
}
/* read serial number part 2 */
sht_serial[1] = sht->sht_trans.buf[0];
sht_serial[0] = sht->sht_trans.buf[1];
sht_serial[7] = sht->sht_trans.buf[3];
sht_serial[6] = sht->sht_trans.buf[4];
sht->sht_serial1 = sht_serial[7] << 24 | sht_serial[6] << 16 | sht_serial[5] << 8 | sht_serial[4];
sht->sht_serial2 = sht_serial[3] << 24 | sht_serial[2] << 16 | sht_serial[1] << 8 | sht_serial[0];
}
static int sht_read_temp(struct sht_humid_t* sht) {
/* trigger temp measurement, no master hold */
sht->sht_trans.buf[0] = SHT2_TRIGGER_TEMP;
if(i2c_blocking_transmit(&SHT_I2C_DEV, &sht->sht_trans, SHT_SLAVE_ADDR, 1, 0.5) != I2CTransSuccess ) {
return -1;
}
/* needs 85ms delay from temp trigger measurement */
sys_time_usleep(85000);
/* read temperature */
if(i2c_blocking_receive(&SHT_I2C_DEV, &sht->sht_trans, SHT_SLAVE_ADDR, 3, 0.5) != I2CTransSuccess) {
return -1;
}
if (humid_sht_crc(sht->sht_trans.buf) != 0) {
/* checksum error*/
return -1;
}
sht->tempsht_i2c = ((sht->sht_trans.buf[0] << 8) | sht->sht_trans.buf[1]) & 0xFFFC;
sht->ftempsht_i2c = -46.85 + 175.72 / 65536. * sht->tempsht_i2c;
return 0;
}
static int sht_read_humid(struct sht_humid_t* sht) {
/* trigger humid measurement, no master hold */
sht->sht_trans.buf[0] = SHT2_TRIGGER_HUMID;
if(i2c_blocking_transmit(&SHT_I2C_DEV, &sht->sht_trans, SHT_SLAVE_ADDR, 1, 0.5) != I2CTransSuccess) {
return -1;
}
/* needs 29ms delay from humid trigger measurement */
sys_time_usleep(29000);
/* read humidity */
if(i2c_blocking_receive(&SHT_I2C_DEV, &sht->sht_trans, SHT_SLAVE_ADDR, 3, 0.5) != I2CTransSuccess) {
return -1;
}
if (humid_sht_crc(sht->sht_trans.buf) != 0) {
/* checksum error */
return -1;
}
sht->humidsht_i2c = ((sht->sht_trans.buf[0] << 8) | sht->sht_trans.buf[1]) & 0xFFFC;
sht->fhumidsht_i2c = -6. + 125. / 65536. * sht->humidsht_i2c;
return 0;
}
static int8_t humid_sht_crc(volatile uint8_t *data)
{
uint8_t i, bit, crc = 0;
@@ -67,147 +196,3 @@ int8_t humid_sht_crc(volatile uint8_t *data)
return 0;
}
}
void humid_sht_init_i2c(void)
{
sht_status = SHT2_UNINIT;
}
void humid_sht_periodic_i2c(void)
{
switch (sht_status) {
case SHT2_UNINIT:
/* do soft reset, then wait at least 15ms */
sht_status = SHT2_RESET;
sht_trans.buf[0] = SHT2_SOFT_RESET;
i2c_transmit(&SHT_I2C_DEV, &sht_trans, SHT_SLAVE_ADDR, 1);
break;
case SHT2_SERIAL:
/* get serial number part 1 */
sht_status = SHT2_SERIAL1;
sht_trans.buf[0] = 0xFA;
sht_trans.buf[1] = 0x0F;
i2c_transceive(&SHT_I2C_DEV, &sht_trans, SHT_SLAVE_ADDR, 2, 8);
break;
case SHT2_SERIAL1:
case SHT2_SERIAL2:
break;
default:
/* trigger temp measurement, no master hold */
sht_trans.buf[0] = SHT2_TRIGGER_TEMP;
sht_status = SHT2_TRIG_TEMP;
i2c_transmit(&SHT_I2C_DEV, &sht_trans, SHT_SLAVE_ADDR, 1);
/* send serial number every 30 seconds */
RunOnceEvery((4 * 30), DOWNLINK_SEND_SHT_I2C_SERIAL(DefaultChannel, DefaultDevice, &sht_serial1, &sht_serial2));
break;
}
}
/* needs 85ms delay from temp trigger measurement */
void humid_sht_p_temp(void)
{
if (sht_status == SHT2_GET_TEMP) {
/* get temp */
sht_status = SHT2_READ_TEMP;
i2c_receive(&SHT_I2C_DEV, &sht_trans, SHT_SLAVE_ADDR, 3);
}
}
/* needs 29ms delay from humid trigger measurement */
void humid_sht_p_humid(void)
{
if (sht_status == SHT2_GET_HUMID) {
/* read humid */
sht_status = SHT2_READ_HUMID;
i2c_receive(&SHT_I2C_DEV, &sht_trans, SHT_SLAVE_ADDR, 3);
}
}
void humid_sht_event_i2c(void)
{
if (sht_trans.status == I2CTransSuccess) {
switch (sht_status) {
case SHT2_TRIG_TEMP:
sht_status = SHT2_GET_TEMP;
sht_trans.status = I2CTransDone;
break;
case SHT2_READ_TEMP:
/* read temperature */
tempsht_i2c = (sht_trans.buf[0] << 8) | sht_trans.buf[1];
tempsht_i2c &= 0xFFFC;
if (humid_sht_crc(sht_trans.buf) == 0) {
/* trigger humid measurement, no master hold */
sht_trans.buf[0] = SHT2_TRIGGER_HUMID;
sht_status = SHT2_TRIG_HUMID;
i2c_transmit(&SHT_I2C_DEV, &sht_trans, SHT_SLAVE_ADDR, 1);
} else {
/* checksum error, restart */
sht_status = SHT2_IDLE;
sht_trans.status = I2CTransDone;
}
break;
case SHT2_TRIG_HUMID:
sht_status = SHT2_GET_HUMID;
sht_trans.status = I2CTransDone;
break;
case SHT2_READ_HUMID:
/* read humidity */
humidsht_i2c = (sht_trans.buf[0] << 8) | sht_trans.buf[1];
humidsht_i2c &= 0xFFFC;
fhumidsht_i2c = -6. + 125. / 65536. * humidsht_i2c;
ftempsht_i2c = -46.85 + 175.72 / 65536. * tempsht_i2c;
sht_status = SHT2_IDLE;
sht_trans.status = I2CTransDone;
if (humid_sht_crc(sht_trans.buf) == 0) {
DOWNLINK_SEND_SHT_I2C_STATUS(DefaultChannel, DefaultDevice, &humidsht_i2c, &tempsht_i2c, &fhumidsht_i2c, &ftempsht_i2c);
}
break;
case SHT2_RESET:
sht_status = SHT2_SERIAL;
sht_trans.status = I2CTransDone;
break;
case SHT2_SERIAL1:
/* read serial number part 1 */
sht_serial[5] = sht_trans.buf[0];
sht_serial[4] = sht_trans.buf[2];
sht_serial[3] = sht_trans.buf[4];
sht_serial[2] = sht_trans.buf[6];
/* get serial number part 2 */
sht_status = SHT2_SERIAL2;
sht_trans.buf[0] = 0xFC;
sht_trans.buf[1] = 0xC9;
i2c_transceive(&SHT_I2C_DEV, &sht_trans, SHT_SLAVE_ADDR, 2, 6);
break;
case SHT2_SERIAL2:
/* read serial number part 2 */
sht_serial[1] = sht_trans.buf[0];
sht_serial[0] = sht_trans.buf[1];
sht_serial[7] = sht_trans.buf[3];
sht_serial[6] = sht_trans.buf[4];
sht_serial1 = sht_serial[7] << 24 | sht_serial[6] << 16 | sht_serial[5] << 8 | sht_serial[4];
sht_serial2 = sht_serial[3] << 24 | sht_serial[2] << 16 | sht_serial[1] << 8 | sht_serial[0];
DOWNLINK_SEND_SHT_I2C_SERIAL(DefaultChannel, DefaultDevice, &sht_serial1, &sht_serial2);
sht_status = SHT2_IDLE;
sht_trans.status = I2CTransDone;
break;
default:
sht_trans.status = I2CTransDone;
break;
}
}
}
+19 -26
View File
@@ -1,39 +1,32 @@
#ifndef HUMID_SHT_I2C_H
#define HUMID_SHT_I2C_H
#include "mcu_periph/i2c.h"
#include "modules/core/threads.h"
#include "std.h"
#define SHT2_WRITE_USER 0xE6
#define SHT2_READ_USER 0xE7
#define SHT2_TRIGGER_TEMP 0xF3
#define SHT2_TRIGGER_HUMID 0xF5
#define SHT2_SOFT_RESET 0xFE
enum sht_stat_i2c {
SHT2_UNINIT,
SHT2_IDLE,
SHT2_RESET,
SHT2_SERIAL,
SHT2_SERIAL1,
SHT2_SERIAL2,
SHT2_SET_CONFIG,
SHT2_READ_SERIAL,
SHT2_TRIG_TEMP,
SHT2_GET_TEMP,
SHT2_READ_TEMP,
SHT2_TRIG_HUMID,
SHT2_GET_HUMID,
SHT2_READ_HUMID
SHT2_READING,
};
int8_t humid_sht_crc(volatile uint8_t *data);
struct sht_humid_t {
struct i2c_transaction sht_trans;
enum sht_stat_i2c sht_status;
uint32_t sht_serial1;
uint32_t sht_serial2;
uint16_t humidsht_i2c;
uint16_t tempsht_i2c;
float fhumidsht_i2c;
float ftempsht_i2c;
pprz_thread_t thd_handle;
pprz_bsem_t bsem_sht_status;
};
void humid_sht_init_i2c(void);
void humid_sht_periodic_i2c(void);
void humid_sht_p_temp(void);
void humid_sht_p_humid(void);
void humid_sht_event_i2c(void);
extern uint16_t humidsht_i2c, tempsht_i2c;
extern float fhumidsht_i2c, ftempsht_i2c;
#endif
@@ -0,0 +1,47 @@
/*
* Copyright (C) 2025 Flo&Fab <name.surname@enac.fr>
*
* This file is part of paparazzi
*
* paparazzi 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, or (at your option)
* any later version.
*
* paparazzi 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 paparazzi; see the file COPYING. If not, see
* <http://www.gnu.org/licenses/>.
*/
/** @file "modules/sensors/encoder_amt22.c"
* @author Flo&Fab <name.surname@enac.fr>
* Driver for AMT22 encoder from CUI devices.
*/
#include "modules/sensors/encoder_amt22.h"
#include "peripherals/amt22.h"
#include "modules/datalink/downlink.h"
static amt22_t amt22;
static amt22_config_t amt22_conf = {
.p = &AMT22_SPI_DEV,
.slave_idx = AMT22_SPI_SLAVE_IDX,
.type = AMT22_12_SINGLE,
};
void encoder_amt22_init(void)
{
amt22_init(&amt22, &amt22_conf);
}
void encoder_amt22_periodic(void)
{
amt22_periodic(&amt22);
float f[2] = {amt22_get_position(&amt22), amt22_get_turns(&amt22)};
DOWNLINK_SEND_PAYLOAD_FLOAT(DefaultChannel, DefaultDevice, 2, f);
}
@@ -0,0 +1,32 @@
/*
* Copyright (C) 2025 Flo&Fab <name.surname@enac.fr>
*
* This file is part of paparazzi
*
* paparazzi 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, or (at your option)
* any later version.
*
* paparazzi 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 paparazzi; see the file COPYING. If not, see
* <http://www.gnu.org/licenses/>.
*/
/** @file "modules/sensors/encoder_amt22.h"
* @author Flo&Fab <name.surname@enac.fr>
* Driver for AMT22 encoder from CUI devices.
*/
#ifndef ENCODER_AMT22_H
#define ENCODER_AMT22_H
extern void encoder_amt22_init(void);
extern void encoder_amt22_periodic(void);
#endif // ENCODER_AMT22_H
+133
View File
@@ -0,0 +1,133 @@
#include "amt22.h"
#ifndef AMT22_SPI_CDIV
#define AMT22_SPI_CDIV SPIDiv256
#endif
static bool amt22_checkbit(uint8_t p0, uint8_t p1);
static void amt22_thd(void* arg);
void amt22_init(amt22_t* amt, amt22_config_t* conf) {
amt->config = conf;
// Set up SPI peripheral and transaction
amt->trans.slave_idx = amt->config->slave_idx;
amt->trans.input_buf = amt->spi_input_buf;
amt->trans.output_buf = amt->spi_output_buf;
amt->trans.select = SPISelectUnselect;
amt->trans.cpol = SPICpolIdleLow;
amt->trans.cpha = SPICphaEdge1;
amt->trans.dss = SPIDss8bit;
amt->trans.bitorder = SPIMSBFirst;
amt->trans.cdiv = AMT22_SPI_CDIV;
amt->trans.before_cb = NULL;
amt->trans.after_cb = NULL;
amt->trans.status = SPITransDone;
amt->position = 0;
amt->turns = 0;
if(amt->config->type == AMT22_12_SINGLE || amt->config->type == AMT22_14_SINGLE) {
//single turn
amt->trans.output_buf[0] = 0x00;
amt->trans.output_buf[1] = 0x00;
amt->trans.output_length = 2;
amt->trans.input_length = 2;
} else {
//multi_turn
amt->trans.output_buf[0] = 0x00;
amt->trans.output_buf[1] = 0xA0;
amt->trans.output_buf[2] = 0x00;
amt->trans.output_buf[3] = 0x00;
amt->trans.output_length = 4;
amt->trans.input_length = 4;
}
pprz_bsem_init(&amt->bsem_amt22_read, true);
pprz_mtx_init(&amt->mtx);
pprz_thread_create(&amt->thd_handle, 512, "amt22", PPRZ_NORMAL_PRIO+1, amt22_thd, amt);
}
void amt22_periodic(amt22_t* amt) {
// trigger read
pprz_bsem_signal(&amt->bsem_amt22_read);
}
bool amt22_read(amt22_t* amt) {
if(spi_blocking_transceive(amt->config->p, &amt->trans, 0.5) != SPITransSuccess) {
return false;
}
uint8_t p0 = amt->trans.input_buf[0];
uint8_t p1 = amt->trans.input_buf[1];
if(!amt22_checkbit(p0,p1)) {
return false;
}
uint16_t position = (p0 << 8 | p1) & 0x3fff;
int16_t turns = 0;
// 12 bits so shift 2
if(amt->config->type == AMT22_12_SINGLE || amt->config->type == AMT22_12_MULTI) {
position >>= 2;
}
if(amt->config->type == AMT22_12_MULTI || amt->config->type == AMT22_14_MULTI) {
uint8_t t0 = amt->trans.input_buf[2];
uint8_t t1 = amt->trans.input_buf[3];
turns = (t0 << 8 | t1);
}
pprz_mtx_lock(&amt->mtx);
amt->position = position;
amt->turns = turns;
pprz_mtx_unlock(&amt->mtx);
return true;
}
static bool amt22_checkbit(uint8_t p0, uint8_t p1) {
uint16_t data = ((p0 << 8 | p1) & 0x3fff) >> 2;
uint8_t odd = 0;
uint8_t even = 0;
while (data)
{
even ^= data & 1;
data >>= 1;
odd ^= data & 1;
data >>= 1;
}
even = !even;
odd = !odd;
if((even == ((p0 & 0x40) >> 6)) && (odd == ((p0 & 0x80) >> 7))) {
return 1;
} else {
return 0;
}
}
uint16_t amt22_get_position(amt22_t* amt) {
pprz_mtx_lock(&amt->mtx);
uint16_t pos = amt->position;
pprz_mtx_unlock(&amt->mtx);
return pos;
}
int16_t amt22_get_turns(amt22_t* amt) {
pprz_mtx_lock(&amt->mtx);
int16_t turns = amt->turns;
pprz_mtx_unlock(&amt->mtx);
return turns;
}
static void amt22_thd(void* arg) {
amt22_t* amt = (amt22_t*)arg;
while(true) {
pprz_bsem_wait(&amt->bsem_amt22_read);
amt22_read(amt);
}
}
+38
View File
@@ -0,0 +1,38 @@
#pragma once
#include "mcu_periph/spi.h"
#include "modules/core/threads.h"
enum amt22_type {
AMT22_12_SINGLE, ///< 12-bits, single-turn
AMT22_14_SINGLE, ///< 14-bits, single-turn
AMT22_12_MULTI, ///< 12-bits, multi-turn
AMT22_14_MULTI, ///< 14-bits, multi-turn
};
typedef struct {
struct spi_periph *p;
uint8_t slave_idx;
enum amt22_type type;
} amt22_config_t;
typedef struct {
amt22_config_t *config;
// private
struct spi_transaction trans;
volatile uint8_t spi_input_buf[4];
volatile uint8_t spi_output_buf[4];
uint16_t position;
int16_t turns;
pprz_thread_t thd_handle;
pprz_bsem_t bsem_amt22_read;
pprz_mutex_t mtx;
} amt22_t;
void amt22_init(amt22_t* amt22, amt22_config_t* conf);
void amt22_periodic(amt22_t* amt22);
uint16_t amt22_get_position(amt22_t* amt22);
int16_t amt22_get_turns(amt22_t* amt22);
+2 -2
View File
@@ -50,7 +50,7 @@ bool pca95xx_configure(struct pca95xx *dev, uint8_t val, bool blocking)
dev->i2c_trans.buf[0] = PCA95XX_CONFIG_REG;
dev->i2c_trans.buf[1] = val;
if (blocking) {
return i2c_blocking_transmit(dev->i2c_p, &dev->i2c_trans, dev->i2c_trans.slave_addr, 2);
return i2c_blocking_transmit(dev->i2c_p, &dev->i2c_trans, dev->i2c_trans.slave_addr, 2, 0.5) == I2CTransSuccess;
} else {
return i2c_transmit(dev->i2c_p, &dev->i2c_trans, dev->i2c_trans.slave_addr, 2);
}
@@ -68,7 +68,7 @@ bool pca95xx_set_output(struct pca95xx *dev, uint8_t mask, bool blocking)
dev->i2c_trans.buf[0] = PCA95XX_OUTPUT_REG;
dev->i2c_trans.buf[1] = mask;
if (blocking) {
return i2c_blocking_transmit(dev->i2c_p, &dev->i2c_trans, dev->i2c_trans.slave_addr, 2);
return i2c_blocking_transmit(dev->i2c_p, &dev->i2c_trans, dev->i2c_trans.slave_addr, 2, 0.5) == I2CTransSuccess;
} else {
return i2c_transmit(dev->i2c_p, &dev->i2c_trans, dev->i2c_trans.slave_addr, 2);
}
+1 -1
View File
@@ -47,7 +47,7 @@ int8_t VL53L1_WriteMulti(VL53L1_DEV dev, uint16_t index, uint8_t *pdata, uint32_
dev->i2c_trans.buf[1] = (index & 0x00FF);
memcpy((uint8_t *) dev->i2c_trans.buf + 2, pdata, count);
return !i2c_blocking_transmit(dev->i2c_p, &dev->i2c_trans,
dev->i2c_trans.slave_addr, 2 + count);
dev->i2c_trans.slave_addr, 2 + count, 1.f);
}
int8_t VL53L1_ReadMulti(VL53L1_DEV dev, uint16_t index, uint8_t *pdata, uint32_t count)