Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions drivers/sensor/sensirion/common/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Copyright (c) 2025 Sensirion
#
# SPDX-License-Identifier: Apache-2.0
#
zephyr_library()

zephyr_library_sources(conversions.c)
zephyr_library_sources(i2c_packet.c)
zephyr_library_sources(i2c_general_call_reset.c)
45 changes: 45 additions & 0 deletions drivers/sensor/sensirion/common/conversions.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* Copyright (c) 2025 Sensirion
*
* SPDX-License-Identifier: Apache-2.0
*/

#include "conversions.h"

float sensirion_common_bytes_to_float(const uint8_t *bytes)
{
union {
uint32_t u32_value;
float float32;
} tmp;

tmp.u32_value = sys_get_be32(bytes);
return tmp.float32;
}

void sensirion_common_float_to_bytes(const float value, uint8_t *bytes)
{
union {
uint32_t u32_value;
float float32;
} tmp;
tmp.float32 = value;
sys_put_be32(tmp.u32_value, bytes);
}

void sensirion_common_to_integer(const uint8_t *source, uint8_t *destination, INT_TYPE int_type,
uint8_t data_length)
{
if (data_length > int_type) {
data_length = int_type;
}
// set all bytes in destination to 0 to make sure that the value is correct even if the data_length is smaller than the size of the integer type
memset(destination, 0, data_length);
uint8_t offset = int_type - data_length;

// copy the bytes from source to destination. The source is in big-endian/MSB-first format, so the first byte of the source is the most significant byte.
// The destination should be filled in the correct order for the system endianness.
for (uint8_t i = 1; i <= data_length; i++) {
destination[int_type - offset - i] = source[i - 1];
}
}
87 changes: 87 additions & 0 deletions drivers/sensor/sensirion/common/conversions.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/*
* Copyright (c) 2025 Sensirion
*
* SPDX-License-Identifier: Apache-2.0
*/

#ifndef CONVERSIONS_H
#define CONVERSIONS_H

#ifdef __cplusplus
extern "C" {
#endif

#include <stdint.h>
#include <zephyr/sys/byteorder.h>

/**
* NO_ERROR is used as alias for 0 return values of functions to improve
* readability of the code.
*/
#define NO_ERROR 0

/**
* @defgroup API to unmarshal basic data type from byte arrays
*
* This API provides functions for converting byte arrays received from the
* sensor.
* The function names in this API use the pattern bytes_to_<type> to indicate
* conversions from byte arrays to basic data types.
* Since Zephyr does not provide a function for converting big-endian
* (MSB-first) byte arrays to floating-point values, the function
* sensirion_common_bytes_to_float() is
* provided for this purpose.
* To maintain a consistent naming scheme for all functions that convert byte
* arrays to basic types, the functions
* sensirion_common_bytes_to_<integer>() are also provided,
* even in cases where Zephyr already provides equivalent functions for
* specific integer
* types.
* @{
*/

/**
* Convert an array of bytes to a float
*
* Convert an array of bytes received from the sensor in big-endian/MSB-first
* format to an float value in the correct system-endianness.
*
* @param bytes An array of at least four bytes (MSB first)
* @return The byte array represented as float
*/
float sensirion_common_bytes_to_float(const uint8_t *bytes);

#define sensirion_common_bytes_to_int16_t(bytes) ((int16_t)sys_get_be16(bytes))
#define sensirion_common_bytes_to_uint16_t(bytes) ((uint16_t)sys_get_be16(bytes))
#define sensirion_common_bytes_to_int32_t(bytes) ((int32_t)sys_get_be32(bytes))
#define sensirion_common_bytes_to_uint32_t(bytes) ((uint32_t)sys_get_be32(bytes))
#define sensirion_common_bytes_to_int64_t(bytes) ((int64_t)sys_get_be64(bytes))
#define sensirion_common_bytes_to_uint64_t(bytes) ((uint64_t)sys_get_be64(bytes))

/** @} */

/**
* Copy bytes from byte array to an integer of the specified type.
*
* The byte array is expected to be in big-endian/MSB-first format as it is
* received from the sensor.
* The data_length specifies how many bytes are available in the source byte
* array to copy.
* This length may not match the size of the destination integer.
* In this case the function will fill the missing bytes with zeros or
* set the destination to 0 if it is smaller than the source.
*
* @param source Array of bytes to be copied.
* @param destination Pointer to integer that will be filled with the copied
* data.
* @param destination_size Size of the destination integer in bytes.
* @param data_length Number of available bytes in source to copy.
*/
void sensirion_common_to_integer(const uint8_t *source, uint8_t *destination,
size_t destination_size, uint8_t data_length);

#ifdef __cplusplus
}
#endif

#endif /* CONVERSIONS_H */
7 changes: 7 additions & 0 deletions drivers/sensor/sensirion/common/i2c_general_call_reset.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
int sensirion_i2c_general_call_reset(const struct i2c_dt_spec *i2c_spec)
{
const uint8_t data = 0x06;
const uint8_t i2c_address = 0x00;

return i2c_write(i2c_spec->bus, &data, sizeof(data), i2c_address);
}
35 changes: 35 additions & 0 deletions drivers/sensor/sensirion/common/i2c_general_call_reset.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* Copyright (c) 2026 Sensirion
*
* SPDX-License-Identifier: Apache-2.0
*/

#ifndef I2C_GENERAL_CALL_RESET_H
#define I2C_GENERAL_CALL_RESET_H

#ifdef __cplusplus
extern "C" {
#endif

#include <stdint.h>
#include <zephyr/drivers/i2c.h>

/**
* Send a general call reset on the i2c bus.
*
* This function is used in drivers that implement a call reset.
*
* @warning This will reset all attached I2C devices on the bus which support
* general call reset.
*
* @param i2c_spec Sensor I2C specification
*
* @return 0 on success, an error code otherwise
*/
int sensirion_i2c_general_call_reset(const struct i2c_dt_spec *i2c_spec);

#ifdef __cplusplus
}
#endif

#endif /* I2C_GENERAL_CALL_RESET_H */
150 changes: 150 additions & 0 deletions drivers/sensor/sensirion/common/i2c_packet.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
/*
* Copyright (c) 2025 Sensirion
*
* SPDX-License-Identifier: Apache-2.0
*/

#include "i2c_packet.h"

#include <zephyr/device.h>
#include <zephyr/drivers/i2c.h>
#include <zephyr/sys/crc.h>


#define SENSIRION_WORD_SIZE 2
#define SENSIRION_CRC8_LEN 1


uint8_t sensirion_i2c_packet_get_crc(const i2c_packet *packet, uint16_t index)
{
return crc8(&packet->data[index], SENSIRION_WORD_SIZE,
packet->crc8_poly, packet->crc8_init, false);
}

bool sensirion_i2c_packet_check_crc(const i2c_packet *packet, uint16_t index)
{
if (sensirion_i2c_packet_get_crc(packet, index) != packet->data[index + SENSIRION_WORD_SIZE]) {
return false;
}
return true;
}



uint16_t sensirion_i2c_packet_add_command16(i2c_packet *packet, uint16_t offset, uint16_t command)
{
sys_put_be16(command, &packet->data[offset]);
offset += 2;
return offset;
}

uint16_t sensirion_i2c_packet_add_command8(i2c_packet *packet, uint16_t offset, uint8_t command)
{
packet->data[offset++] = command;
return offset;
}

uint16_t sensirion_i2c_packet_add_uint64_t(i2c_packet *packet, uint16_t offset, uint64_t data)
{
offset = sensirion_i2c_packet_add_uint32_t(packet, offset, data >> 32);
offset = sensirion_i2c_packet_add_uint32_t(packet, offset, data & 0xFFFFFFFF);
return offset;
}

uint16_t sensirion_i2c_packet_add_int64_t(i2c_packet *packet, uint16_t offset, int64_t data)
{
return sensirion_i2c_packet_add_uint64_t(packet, offset, (uint64_t)data);
}

uint16_t sensirion_i2c_packet_add_uint32_t(i2c_packet *packet, uint16_t offset, uint32_t data)
{
offset = sensirion_i2c_packet_add_uint16_t(packet, offset, data >> 16);
offset = sensirion_i2c_packet_add_uint16_t(packet, offset, data & 0xFFFF);

return offset;
}

uint16_t sensirion_i2c_packet_add_int32_t(i2c_packet *packet, uint16_t offset, int32_t data)
{
return sensirion_i2c_packet_add_uint32_t(packet, offset, (uint32_t)data);
}

uint16_t sensirion_i2c_packet_add_uint16_t(i2c_packet *packet, uint16_t offset, uint16_t data)
{
sys_put_be16(data, &packet->data[offset]);
offset += 2;
if (packet->crc8_poly != 0) {
packet->data[offset] = sensirion_i2c_packet_get_crc(packet, offset - SENSIRION_WORD_SIZE);
offset++;
}
return offset;
}

uint16_t sensirion_i2c_packet_add_int16_t(i2c_packet *packet, uint16_t offset, int16_t data)
{
return sensirion_i2c_packet_add_uint16_t(packet, offset, (uint16_t)data);
}

uint16_t sensirion_i2c_packet_add_float(i2c_packet *packet, uint16_t offset, float data)
{
union {
uint32_t uint32_data;
float float_data;
} convert;

convert.float_data = data;
return sensirion_i2c_packet_add_uint32_t(packet, offset, convert.uint32_data);
}

uint16_t sensirion_i2c_packet_add_bytes(i2c_packet *packet, uint16_t offset, const uint8_t *data,
uint16_t data_length)
{
uint16_t i;

if (data_length % SENSIRION_WORD_SIZE != 0) {
return -EINVAL;
}

for (i = 0; i < data_length; i += 2) {
packet->data[offset++] = data[i];
packet->data[offset++] = data[i + 1];
if (packet->crc8_poly != 0) {
packet->data[offset] = sensirion_i2c_packet_get_crc(packet, offset - SENSIRION_WORD_SIZE);
offset++;
}
}
return offset;
}


int sensirion_i2c_packet_read(const struct i2c_dt_spec *i2c_spec, i2c_packet *packet,
uint16_t expected_data_length)
{
int ret;
uint16_t i, j;
uint16_t crc_len = (packet->crc8_poly != 0) ? SENSIRION_CRC8_LEN : 0;
uint16_t size = (expected_data_length / SENSIRION_WORD_SIZE) *
(SENSIRION_WORD_SIZE + crc_len);

if (expected_data_length % SENSIRION_WORD_SIZE != 0) {
return -EINVAL;
}

ret = i2c_read_dt(i2c_spec, packet->data, size);
if (ret != 0) {
return ret;
}

for (i = 0, j = 0; i < size; i += SENSIRION_WORD_SIZE + crc_len) {

if (packet->crc8_poly != 0) {
if (!sensirion_i2c_packet_check_crc(packet, i)) {
return -EIO;
}
}
packet->data[j++] = packet->data[i];
packet->data[j++] = packet->data[i + 1];
}

return 0;
}
Loading