Skip to content

Commit b8f1df3

Browse files
committed
sensor_ldc1612: Initial support for bulk reading ldc1612 sensor
Signed-off-by: Alan.Ma from BigTreeTech <[email protected]> Signed-off-by: Kevin O'Connor <[email protected]>
1 parent acdf8bb commit b8f1df3

File tree

3 files changed

+216
-1
lines changed

3 files changed

+216
-1
lines changed

src/Kconfig

+8-1
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,10 @@ config WANT_LIS2DW
104104
bool
105105
depends on HAVE_GPIO_SPI
106106
default y
107+
config WANT_LDC1612
108+
bool
109+
depends on HAVE_GPIO_I2C
110+
default y
107111
config WANT_SOFTWARE_I2C
108112
bool
109113
depends on HAVE_GPIO && HAVE_GPIO_I2C
@@ -114,7 +118,7 @@ config WANT_SOFTWARE_SPI
114118
default y
115119
config NEED_SENSOR_BULK
116120
bool
117-
depends on WANT_SENSORS || WANT_LIS2DW
121+
depends on WANT_SENSORS || WANT_LIS2DW || WANT_LDC1612
118122
default y
119123
menu "Optional features (to reduce code size)"
120124
depends on HAVE_LIMITED_CODE_SIZE
@@ -130,6 +134,9 @@ config WANT_SENSORS
130134
config WANT_LIS2DW
131135
bool "Support lis2dw 3-axis accelerometer"
132136
depends on HAVE_GPIO_SPI
137+
config WANT_LDC1612
138+
bool "Support ldc1612 eddy current sensor"
139+
depends on HAVE_GPIO_I2C
133140
config WANT_SOFTWARE_I2C
134141
bool "Support software based I2C \"bit-banging\""
135142
depends on HAVE_GPIO && HAVE_GPIO_I2C

src/Makefile

+1
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,5 @@ sensors-src-$(CONFIG_HAVE_GPIO_SPI) := thermocouple.c sensor_adxl345.c \
1919
sensors-src-$(CONFIG_HAVE_GPIO_I2C) += sensor_mpu9250.c
2020
src-$(CONFIG_WANT_SENSORS) += $(sensors-src-y)
2121
src-$(CONFIG_WANT_LIS2DW) += sensor_lis2dw.c
22+
src-$(CONFIG_WANT_LDC1612) += sensor_ldc1612.c
2223
src-$(CONFIG_NEED_SENSOR_BULK) += sensor_bulk.c

src/sensor_ldc1612.c

+207
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
// Support for eddy current sensor data from ldc1612 chip
2+
//
3+
// Copyright (C) 2023 Alan.Ma <[email protected]>
4+
// Copyright (C) 2024 Kevin O'Connor <[email protected]>
5+
//
6+
// This file may be distributed under the terms of the GNU GPLv3 license.
7+
8+
#include <string.h> // memcpy
9+
#include "basecmd.h" // oid_alloc
10+
#include "board/gpio.h" // i2c_read
11+
#include "board/irq.h" // irq_disable
12+
#include "board/misc.h" // timer_read_time
13+
#include "command.h" // DECL_COMMAND
14+
#include "i2ccmds.h" // i2cdev_oid_lookup
15+
#include "sched.h" // DECL_TASK
16+
#include "sensor_bulk.h" // sensor_bulk_report
17+
#include "trsync.h" // trsync_do_trigger
18+
19+
enum {
20+
LDC_PENDING = 1<<0,
21+
LH_AWAIT_HOMING = 1<<1, LH_CAN_TRIGGER = 1<<2
22+
};
23+
24+
struct ldc1612 {
25+
struct timer timer;
26+
uint32_t rest_ticks;
27+
struct i2cdev_s *i2c;
28+
uint8_t flags;
29+
struct sensor_bulk sb;
30+
// homing
31+
struct trsync *ts;
32+
uint8_t homing_flags;
33+
uint8_t trigger_reason;
34+
uint32_t trigger_threshold;
35+
uint32_t homing_clock;
36+
};
37+
38+
static struct task_wake ldc1612_wake;
39+
40+
// Query ldc1612 data
41+
static uint_fast8_t
42+
ldc1612_event(struct timer *timer)
43+
{
44+
struct ldc1612 *ld = container_of(timer, struct ldc1612, timer);
45+
if (ld->flags & LDC_PENDING)
46+
ld->sb.possible_overflows++;
47+
ld->flags |= LDC_PENDING;
48+
sched_wake_task(&ldc1612_wake);
49+
ld->timer.waketime += ld->rest_ticks;
50+
return SF_RESCHEDULE;
51+
}
52+
53+
void
54+
command_config_ldc1612(uint32_t *args)
55+
{
56+
struct ldc1612 *ld = oid_alloc(args[0], command_config_ldc1612
57+
, sizeof(*ld));
58+
ld->timer.func = ldc1612_event;
59+
ld->i2c = i2cdev_oid_lookup(args[1]);
60+
}
61+
DECL_COMMAND(command_config_ldc1612, "config_ldc1612 oid=%c i2c_oid=%c");
62+
63+
void
64+
command_ldc1612_setup_home(uint32_t *args)
65+
{
66+
struct ldc1612 *ld = oid_lookup(args[0], command_config_ldc1612);
67+
68+
ld->trigger_threshold = args[2];
69+
if (!ld->trigger_threshold) {
70+
ld->ts = NULL;
71+
ld->homing_flags = 0;
72+
return;
73+
}
74+
ld->homing_clock = args[1];
75+
ld->ts = trsync_oid_lookup(args[3]);
76+
ld->trigger_reason = args[4];
77+
ld->homing_flags = LH_AWAIT_HOMING | LH_CAN_TRIGGER;
78+
}
79+
DECL_COMMAND(command_ldc1612_setup_home,
80+
"ldc1612_setup_home oid=%c clock=%u threshold=%u"
81+
" trsync_oid=%c trigger_reason=%c");
82+
83+
void
84+
command_query_ldc1612_home_state(uint32_t *args)
85+
{
86+
struct ldc1612 *ld = oid_lookup(args[0], command_config_ldc1612);
87+
sendf("ldc1612_home_state oid=%c homing=%c trigger_clock=%u"
88+
, args[0], !!(ld->homing_flags & LH_CAN_TRIGGER), ld->homing_clock);
89+
}
90+
DECL_COMMAND(command_query_ldc1612_home_state,
91+
"query_ldc1612_home_state oid=%c");
92+
93+
// Chip registers
94+
#define REG_DATA0_MSB 0x00
95+
#define REG_DATA0_LSB 0x01
96+
#define REG_STATUS 0x18
97+
98+
// Read a register on the ldc1612
99+
static void
100+
read_reg(struct ldc1612 *ld, uint8_t reg, uint8_t *res)
101+
{
102+
i2c_read(ld->i2c->i2c_config, sizeof(reg), &reg, 2, res);
103+
}
104+
105+
// Read the status register on the ldc1612
106+
static uint16_t
107+
read_reg_status(struct ldc1612 *ld)
108+
{
109+
uint8_t data_status[2];
110+
read_reg(ld, REG_STATUS, data_status);
111+
return (data_status[0] << 8) | data_status[1];
112+
}
113+
114+
#define BYTES_PER_SAMPLE 4
115+
116+
// Query ldc1612 data
117+
static void
118+
ldc1612_query(struct ldc1612 *ld, uint8_t oid)
119+
{
120+
// Clear pending flag
121+
irq_disable();
122+
ld->flags &= ~LDC_PENDING;
123+
irq_enable();
124+
125+
// Check if data available
126+
uint16_t status = read_reg_status(ld);
127+
if (status != 0x48)
128+
return;
129+
130+
// Read coil0 frequency
131+
uint8_t *d = &ld->sb.data[ld->sb.data_count];
132+
read_reg(ld, REG_DATA0_MSB, &d[0]);
133+
read_reg(ld, REG_DATA0_LSB, &d[2]);
134+
ld->sb.data_count += BYTES_PER_SAMPLE;
135+
136+
// Check for endstop trigger
137+
uint8_t homing_flags = ld->homing_flags;
138+
if (homing_flags & LH_CAN_TRIGGER) {
139+
uint32_t time = timer_read_time();
140+
if (!(homing_flags & LH_AWAIT_HOMING)
141+
|| !timer_is_before(time, ld->homing_clock)) {
142+
homing_flags &= ~LH_AWAIT_HOMING;
143+
uint32_t data = (d[0] << 24L) | (d[1] << 16L) | (d[2] << 8) | d[3];
144+
if (data > ld->trigger_threshold) {
145+
homing_flags = 0;
146+
ld->homing_clock = time;
147+
trsync_do_trigger(ld->ts, ld->trigger_reason);
148+
}
149+
ld->homing_flags = homing_flags;
150+
}
151+
}
152+
153+
// Flush local buffer if needed
154+
if (ld->sb.data_count + BYTES_PER_SAMPLE > ARRAY_SIZE(ld->sb.data))
155+
sensor_bulk_report(&ld->sb, oid);
156+
}
157+
158+
void
159+
command_query_ldc1612(uint32_t *args)
160+
{
161+
struct ldc1612 *ld = oid_lookup(args[0], command_config_ldc1612);
162+
163+
sched_del_timer(&ld->timer);
164+
ld->flags = 0;
165+
if (!args[1])
166+
// End measurements
167+
return;
168+
169+
// Start new measurements query
170+
ld->rest_ticks = args[1];
171+
sensor_bulk_reset(&ld->sb);
172+
irq_disable();
173+
ld->timer.waketime = timer_read_time() + ld->rest_ticks;
174+
sched_add_timer(&ld->timer);
175+
irq_enable();
176+
}
177+
DECL_COMMAND(command_query_ldc1612, "query_ldc1612 oid=%c rest_ticks=%u");
178+
179+
void
180+
command_query_ldc1612_status(uint32_t *args)
181+
{
182+
struct ldc1612 *ld = oid_lookup(args[0], command_config_ldc1612);
183+
184+
uint32_t time1 = timer_read_time();
185+
uint16_t status = read_reg_status(ld);
186+
uint32_t time2 = timer_read_time();
187+
188+
uint32_t fifo = status == 0x48 ? BYTES_PER_SAMPLE : 0;
189+
sensor_bulk_status(&ld->sb, args[0], time1, time2-time1, fifo);
190+
}
191+
DECL_COMMAND(command_query_ldc1612_status, "query_ldc1612_status oid=%c");
192+
193+
void
194+
ldc1612_task(void)
195+
{
196+
if (!sched_check_wake(&ldc1612_wake))
197+
return;
198+
uint8_t oid;
199+
struct ldc1612 *ld;
200+
foreach_oid(oid, ld, command_config_ldc1612) {
201+
uint_fast8_t flags = ld->flags;
202+
if (!(flags & LDC_PENDING))
203+
continue;
204+
ldc1612_query(ld, oid);
205+
}
206+
}
207+
DECL_TASK(ldc1612_task);

0 commit comments

Comments
 (0)