Skip to content

Commit 6b8fcae

Browse files
fabocodejhedberg
authored andcommitted
drivers: sensor: add support for ALS31300 3D Hall Effect Sensor
Add driver for Allegro Microsystems ALS31300 3-axis linear Hall Effect sensor. The driver supports: - I2C communication interface - X, Y, Z magnetic field measurements - Device temperature readings Signed-off-by: Fabian Barraez <[email protected]>
1 parent 5bad29f commit 6b8fcae

File tree

10 files changed

+914
-0
lines changed

10 files changed

+914
-0
lines changed

drivers/sensor/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ add_subdirectory(wsen)
3939
# zephyr-keep-sorted-stop
4040

4141
add_subdirectory_ifdef(CONFIG_A01NYUB a01nyub)
42+
add_subdirectory_ifdef(CONFIG_ALS31300 als31300)
4243
add_subdirectory_ifdef(CONFIG_AMD_SB_TSI amd_sb_tsi)
4344
add_subdirectory_ifdef(CONFIG_AMG88XX amg88xx)
4445
add_subdirectory_ifdef(CONFIG_APDS9253 apds9253)

drivers/sensor/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ comment "Device Drivers"
8888

8989
# zephyr-keep-sorted-start
9090
source "drivers/sensor/adi/Kconfig"
91+
source "drivers/sensor/als31300/Kconfig"
9192
source "drivers/sensor/ams/Kconfig"
9293
source "drivers/sensor/aosong/Kconfig"
9394
source "drivers/sensor/asahi_kasei/Kconfig"
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Copyright (c) 2025 Croxel
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
zephyr_library()
5+
6+
zephyr_library_sources(als31300.c)
7+
zephyr_library_sources_ifdef(CONFIG_SENSOR_ASYNC_API als31300_async.c als31300_decoder.c)

drivers/sensor/als31300/Kconfig

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# Copyright (c) 2025 Croxel
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
# ALS31300 3D Linear Hall-Effect Sensor configuration options
5+
6+
config ALS31300
7+
bool "ALS31300 3D Linear Hall-Effect Sensor"
8+
default y
9+
depends on DT_HAS_ALLEGRO_ALS31300_ENABLED
10+
select I2C
11+
help
12+
Enable driver for ALS31300 3D Linear Hall-Effect Sensor.
13+
This sensor provides 12-bit magnetic field measurements
14+
on X, Y, and Z axes via I2C interface.

drivers/sensor/als31300/als31300.c

Lines changed: 279 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,279 @@
1+
/*
2+
* Copyright (c) 2025 Croxel
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
#define DT_DRV_COMPAT allegro_als31300
7+
8+
#include "als31300.h"
9+
10+
#include <zephyr/device.h>
11+
#include <zephyr/drivers/sensor.h>
12+
#include <zephyr/logging/log.h>
13+
#include <zephyr/kernel.h>
14+
#include <zephyr/sys/util.h>
15+
#include <zephyr/drivers/i2c.h>
16+
17+
LOG_MODULE_REGISTER(als31300, CONFIG_SENSOR_LOG_LEVEL);
18+
19+
/**
20+
* @brief Convert 12-bit two's complement value to signed 16-bit
21+
* @param value 12-bit value to convert (bits 11:0)
22+
* @return Signed 16-bit value
23+
*/
24+
int16_t als31300_convert_12bit_to_signed(uint16_t value)
25+
{
26+
return (int16_t)sign_extend(value, ALS31300_12BIT_SIGN_BIT_INDEX);
27+
}
28+
29+
/**
30+
* @brief Parse raw register data from 8-byte buffer
31+
* @param buf 8-byte buffer containing register 0x28 and 0x29 data
32+
* @param readings Pointer to readings structure to store parsed data
33+
*/
34+
void als31300_parse_registers(const uint8_t *buf, struct als31300_readings *readings)
35+
{
36+
uint32_t reg28_data, reg29_data;
37+
uint16_t x_msb, y_msb, z_msb;
38+
uint8_t x_lsb, y_lsb, z_lsb;
39+
uint8_t temp_msb, temp_lsb;
40+
41+
/* Convert 8 bytes to two 32-bit values (MSB first) */
42+
reg28_data = ((uint32_t)buf[0] << 24) | ((uint32_t)buf[1] << 16) | ((uint32_t)buf[2] << 8) |
43+
((uint32_t)buf[3]);
44+
reg29_data = ((uint32_t)buf[4] << 24) | ((uint32_t)buf[5] << 16) | ((uint32_t)buf[6] << 8) |
45+
((uint32_t)buf[7]);
46+
47+
/* Extract fields from register 0x28 */
48+
temp_msb = (reg28_data & ALS31300_REG28_TEMP_MSB_MASK) >> ALS31300_REG28_TEMP_MSB_SHIFT;
49+
z_msb = (reg28_data & ALS31300_REG28_Z_AXIS_MSB_MASK) >> ALS31300_REG28_Z_AXIS_MSB_SHIFT;
50+
y_msb = (reg28_data & ALS31300_REG28_Y_AXIS_MSB_MASK) >> ALS31300_REG28_Y_AXIS_MSB_SHIFT;
51+
x_msb = (reg28_data & ALS31300_REG28_X_AXIS_MSB_MASK) >> ALS31300_REG28_X_AXIS_MSB_SHIFT;
52+
53+
/* Extract fields from register 0x29 */
54+
temp_lsb = (reg29_data & ALS31300_REG29_TEMP_LSB_MASK) >> ALS31300_REG29_TEMP_LSB_SHIFT;
55+
z_lsb = (reg29_data & ALS31300_REG29_Z_AXIS_LSB_MASK) >> ALS31300_REG29_Z_AXIS_LSB_SHIFT;
56+
y_lsb = (reg29_data & ALS31300_REG29_Y_AXIS_LSB_MASK) >> ALS31300_REG29_Y_AXIS_LSB_SHIFT;
57+
x_lsb = (reg29_data & ALS31300_REG29_X_AXIS_LSB_MASK) >> ALS31300_REG29_X_AXIS_LSB_SHIFT;
58+
59+
/* Combine MSB and LSB to form 12-bit values */
60+
const uint16_t x_raw = (x_msb << 4) | x_lsb;
61+
const uint16_t y_raw = (y_msb << 4) | y_lsb;
62+
const uint16_t z_raw = (z_msb << 4) | z_lsb;
63+
const uint16_t temp_raw = (temp_msb << 6) | temp_lsb;
64+
65+
/* Convert to signed values (proper 12-bit two's complement) */
66+
readings->x = als31300_convert_12bit_to_signed(x_raw);
67+
readings->y = als31300_convert_12bit_to_signed(y_raw);
68+
readings->z = als31300_convert_12bit_to_signed(z_raw);
69+
readings->temp = temp_raw;
70+
}
71+
72+
/**
73+
* @brief Convert raw magnetic field value to microgauss
74+
* This function converts the 12-bit signed raw magnetic field value to
75+
* microgauss units
76+
* Formula: microgauss = (raw_value * 500 * 1000000) / 4096
77+
* @param raw_value Signed 12-bit magnetic field value
78+
* @return Magnetic field in microgauss
79+
*/
80+
int32_t als31300_convert_to_gauss(int16_t raw_value)
81+
{
82+
/* Convert to microgauss
83+
* For 500G full scale: (raw_value * 500 * 1000000) / 4096
84+
* This gives us the fractional part in microgauss
85+
*/
86+
return ((int64_t)raw_value * ALS31300_FULL_SCALE_RANGE_GAUSS * 1000000) /
87+
ALS31300_12BIT_RESOLUTION;
88+
}
89+
90+
/**
91+
* @brief Convert raw temperature value to celsius
92+
* Based on datasheet formula: T(°C) = 302 * (raw_temp - 1708) / 4096
93+
* @param raw_temp 12-bit raw temperature value
94+
* @return Temperature in microcelsius
95+
*/
96+
int32_t als31300_convert_temperature(uint16_t raw_temp)
97+
{
98+
/* Convert to microcelsius
99+
* Formula: microcelsius = (302 * (raw_temp - 1708) * 1000000) / 4096
100+
*/
101+
return ((int64_t)ALS31300_TEMP_SCALE_FACTOR * ((int32_t)raw_temp - ALS31300_TEMP_OFFSET) *
102+
1000000) /
103+
ALS31300_TEMP_DIVISOR;
104+
}
105+
106+
/**
107+
* @brief Read and parse sensor data from ALS31300
108+
* This function performs an 8-byte I2C burst read from registers 0x28 and 0x29
109+
* to get magnetic field and temperature data. The data is parsed according to
110+
* the datasheet bit field layout and stored in the provided readings structure.
111+
* @param dev Pointer to the device structure
112+
* @param readings Pointer to readings structure to store data
113+
* @return 0 on success, negative error code on failure
114+
*/
115+
static int als31300_read_sensor_data(const struct device *dev, enum sensor_channel chan,
116+
struct als31300_readings *readings)
117+
{
118+
const struct als31300_config *cfg = dev->config;
119+
struct als31300_data *data = dev->data;
120+
uint8_t buf[8];
121+
int ret;
122+
123+
/* Read both data registers in a single 8-byte transaction for consistency */
124+
ret = i2c_burst_read_dt(&cfg->i2c, ALS31300_REG_DATA_28, buf, sizeof(buf));
125+
if (ret < 0) {
126+
LOG_ERR("Failed to read sensor data: %d", ret);
127+
return ret;
128+
}
129+
130+
/* Parse the register data using common helper */
131+
als31300_parse_registers(buf, readings);
132+
133+
/* Also update local data structure for compatibility */
134+
data->x_raw = readings->x;
135+
data->y_raw = readings->y;
136+
data->z_raw = readings->z;
137+
data->temp_raw = readings->temp;
138+
139+
return 0;
140+
}
141+
142+
static int als31300_sample_fetch(const struct device *dev, enum sensor_channel chan)
143+
{
144+
struct als31300_readings readings;
145+
146+
return als31300_read_sensor_data(dev, chan, &readings);
147+
}
148+
149+
static int als31300_channel_get(const struct device *dev, enum sensor_channel chan,
150+
struct sensor_value *val)
151+
{
152+
struct als31300_data *data = dev->data;
153+
int32_t raw_val;
154+
int32_t converted_val;
155+
156+
switch (chan) {
157+
case SENSOR_CHAN_MAGN_X:
158+
raw_val = data->x_raw;
159+
break;
160+
case SENSOR_CHAN_MAGN_Y:
161+
raw_val = data->y_raw;
162+
break;
163+
case SENSOR_CHAN_MAGN_Z:
164+
raw_val = data->z_raw;
165+
break;
166+
case SENSOR_CHAN_AMBIENT_TEMP:
167+
/* Temperature conversion */
168+
converted_val = als31300_convert_temperature(data->temp_raw);
169+
sensor_value_from_micro(val, converted_val);
170+
return 0;
171+
default:
172+
return -ENOTSUP;
173+
}
174+
175+
/* Convert raw magnetic data to gauss */
176+
converted_val = als31300_convert_to_gauss(raw_val);
177+
sensor_value_from_micro(val, converted_val);
178+
179+
return 0;
180+
}
181+
182+
static DEVICE_API(sensor, als31300_api) = {
183+
.sample_fetch = als31300_sample_fetch,
184+
.channel_get = als31300_channel_get,
185+
#ifdef CONFIG_SENSOR_ASYNC_API
186+
.submit = als31300_submit,
187+
.get_decoder = als31300_get_decoder,
188+
#endif
189+
};
190+
191+
/**
192+
* @brief Configure ALS31300 to Active Mode
193+
* This function sets the device to Active Mode by writing to the volatile
194+
* register 0x27. This register can be written without customer access mode.
195+
* @param dev Pointer to the device structure
196+
* @return 0 on success, negative error code on failure
197+
*/
198+
static int als31300_configure_device(const struct device *dev)
199+
{
200+
const struct als31300_config *cfg = dev->config;
201+
uint32_t reg27_value = 0x00000000; /* All bits to 0 = Active Mode */
202+
int ret;
203+
204+
LOG_INF("Configuring ALS31300 to Active Mode...");
205+
206+
/* Write 0x00000000 to register 0x27 to set Active Mode
207+
* Bits [1:0] = 0 → Active Mode
208+
* Bits [3:2] = 0 → Single read mode (default I2C mode)
209+
* Bits [6:4] = 0 → Low-power count = 0.5ms (doesn't matter in Active Mode)
210+
* Bits [31:7] = 0 → Reserved (should be 0)
211+
*/
212+
ret = i2c_burst_write_dt(&cfg->i2c, ALS31300_REG_VOLATILE_27, (uint8_t *)&reg27_value,
213+
sizeof(reg27_value));
214+
if (ret < 0) {
215+
LOG_ERR("Failed to write to register 0x27: %d", ret);
216+
return ret;
217+
}
218+
219+
return 0;
220+
}
221+
222+
/**
223+
* @brief Initialize ALS31300 device
224+
*/
225+
static int als31300_init(const struct device *dev)
226+
{
227+
const struct als31300_config *cfg = dev->config;
228+
int ret;
229+
230+
if (!i2c_is_ready_dt(&cfg->i2c)) {
231+
LOG_ERR("I2C device not ready");
232+
return -ENODEV;
233+
}
234+
235+
/* Wait for power-on delay as specified in datasheet */
236+
k_usleep(ALS31300_POWER_ON_DELAY_US);
237+
238+
/* Test communication by reading a register (can be done without customer access) */
239+
uint8_t test_val;
240+
241+
ret = i2c_reg_read_byte_dt(&cfg->i2c, ALS31300_REG_VOLATILE_27, &test_val);
242+
if (ret < 0) {
243+
LOG_ERR("Failed to communicate with sensor: %d", ret);
244+
return ret;
245+
}
246+
247+
/* Configure device to Active Mode */
248+
ret = als31300_configure_device(dev);
249+
if (ret < 0) {
250+
LOG_ERR("Failed to configure device: %d", ret);
251+
return ret;
252+
}
253+
254+
/* Wait a bit more for the sensor to be fully ready in Active Mode */
255+
k_msleep(ALS31300_REG_WRITE_DELAY_MS);
256+
257+
return 0;
258+
}
259+
260+
#define ALS31300_INIT(inst) \
261+
RTIO_DEFINE(als31300_rtio_ctx_##inst, 16, 16); \
262+
I2C_DT_IODEV_DEFINE(als31300_bus_##inst, DT_DRV_INST(inst)); \
263+
\
264+
static struct als31300_data als31300_data_##inst; \
265+
\
266+
static const struct als31300_config als31300_config_##inst = { \
267+
.i2c = I2C_DT_SPEC_INST_GET(inst), \
268+
.bus = \
269+
{ \
270+
.ctx = &als31300_rtio_ctx_##inst, \
271+
.iodev = &als31300_bus_##inst, \
272+
}, \
273+
}; \
274+
\
275+
SENSOR_DEVICE_DT_INST_DEFINE(inst, als31300_init, NULL, &als31300_data_##inst, \
276+
&als31300_config_##inst, POST_KERNEL, \
277+
CONFIG_SENSOR_INIT_PRIORITY, &als31300_api)
278+
279+
DT_INST_FOREACH_STATUS_OKAY(ALS31300_INIT);

0 commit comments

Comments
 (0)