Skip to content

Commit e4c37bb

Browse files
authored
drivers: TLV493d: Fixup a bit, disable frame checking and reset (#660)
1 parent e2a07cf commit e4c37bb

5 files changed

Lines changed: 171 additions & 67 deletions

File tree

drivers/sensor/TLV493D.zig

Lines changed: 95 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -11,19 +11,20 @@ const I2C_Device = mdf.base.I2C_Device;
1111
const Clock_Device = mdf.base.Clock_Device;
1212

1313
/// TLV493D I2C addresses
14-
pub const TLV493D_ADDRESS0: I2C_Device.Address = @enumFromInt(0x1F);
15-
pub const TLV493D_ADDRESS1: I2C_Device.Address = @enumFromInt(0x5E); // Default
14+
pub const ADDRESS0: I2C_Device.Address = @enumFromInt(0x1F);
15+
pub const ADDRESS1: I2C_Device.Address = @enumFromInt(0x5E); // Default
1616

1717
/// Startup delay in milliseconds
18-
pub const TLV493D_STARTUPDELAY_MS: u32 = 40;
18+
pub const STARTUPDELAY_MS: u32 = 40;
19+
pub const RESETDELAY_MS: u32 = 200;
1920

2021
/// Conversion factors
21-
pub const TLV493D_B_MULT: f32 = 0.098; // mT per LSB
22-
pub const TLV493D_TEMP_MULT: f32 = 1.1; // °C per LSB
23-
pub const TLV493D_TEMP_OFFSET = 340; // Temperature offset (in LSB)
22+
const B_MULT: f32 = 0.098; // mT per LSB
23+
const TEMP_MULT: f32 = 1.1; // °C per LSB
24+
const TEMP_OFFSET = 340; // Temperature offset (in LSB)
2425

2526
/// Default mode for sensor operation
26-
pub const TLV493D_DEFAULTMODE = AccessMode.master_controlled;
27+
pub const DEFAULTMODE = AccessMode.master_controlled;
2728

2829
/// TLV493D Measurement values
2930
pub const Values = struct {
@@ -35,19 +36,19 @@ pub const Values = struct {
3536

3637
/// TLV493D configuration
3738
pub const Config = struct {
38-
reset: bool = true,
39+
reset: bool = false,
3940
enable_temp: bool = false,
41+
access_mode: AccessMode = DEFAULTMODE,
4042
};
4143

4244
/// TLV493D related errors
4345
pub const Error = error{
44-
// TODO: We can only detect NoDevice if we get a NACK, which we can only tell with a proper i2c
45-
// interface
4646
NoDevice,
4747
BusError,
4848
FrameError,
4949
DatagramError,
5050
InvalidData,
51+
Unsupported,
5152
};
5253

5354
const ReadRegister = packed struct(u80) {
@@ -153,7 +154,7 @@ pub const TLV493D = struct {
153154
z_data: i12 = 0,
154155
temp_data: i12 = 0,
155156
mode: AccessMode,
156-
expected_frame_count: u8,
157+
expected_frame_count: u2 = 0,
157158

158159
/// Create a new TLV493D instance
159160
pub fn init(dev: I2C_Device, address: I2C_Device.Address, clock: Clock_Device, config: Config) Error!Self {
@@ -163,23 +164,34 @@ pub const TLV493D = struct {
163164
.clock = clock,
164165
.read_data = @bitCast([_]u8{0} ** 10),
165166
.write_data = @bitCast([_]u8{0} ** 4),
166-
// TODO: Add to config
167-
// .mode = .fast,
168-
.mode = TLV493D_DEFAULTMODE,
169-
.expected_frame_count = 0,
167+
.mode = config.access_mode,
170168
};
171169

170+
// We don't support setting the I2C bits for the extra 6 addresses
171+
// In fact, we don't support anything other than the default, since reseting seems to cause
172+
// the device to hang.
173+
if (address != ADDRESS1)
174+
return Error.Unsupported;
175+
176+
// TODO: Support other modes
177+
if (self.mode != DEFAULTMODE)
178+
return Error.Unsupported;
179+
172180
// The first thing we have to do is read out the factory calibration and pack it into the
173181
// write register so that we don't clear it on the first write.
174-
self.setup_write_buffer() catch return Error.BusError;
182+
try self.setup_write_buffer();
175183

176184
// Sleep for startup delay
177-
// TODO: Needed?
178-
self.clock.sleep_ms(TLV493D_STARTUPDELAY_MS);
185+
self.clock.sleep_ms(STARTUPDELAY_MS);
179186

180187
// Reset sensor if requested
181-
if (config.reset)
182-
try self.reset_sensor();
188+
// TODO: Figure out why when using the broadcast address, the subsequent reads seem to hang
189+
if (config.reset) {
190+
return Error.Unsupported;
191+
// try self.reset_sensor();
192+
}
193+
194+
// try self.synchronize_frame_count();
183195

184196
// Get all register data from sensor
185197
try self.read_out();
@@ -193,8 +205,13 @@ pub const TLV493D = struct {
193205
return self;
194206
}
195207

196-
fn setup_write_buffer(self: *Self) !void {
197-
try self.read_out();
208+
fn setup_write_buffer(self: *Self) Error!void {
209+
self.read_out() catch |e| switch (e) {
210+
// The FRAMECOUNTER doesn't seem to correctly reset to 0, so since this is the first
211+
// read we do, we ignore it
212+
Error.FrameError => {},
213+
else => return e,
214+
};
198215

199216
// Section 5.2 of the user manual:
200217
// > After that byte 7, 8 & 9 have to be read out at least one time and stored for later use
@@ -211,36 +228,57 @@ pub const TLV493D = struct {
211228
/// Deinitialize the device
212229
pub fn deinit(self: *Self) void {
213230
self.disable_interrupt() catch {};
214-
self.set_access_mode(AccessMode.powerdown) catch {};
231+
self.set_access_mode(.powerdown) catch {};
215232
}
216233

217234
/// Reset the sensor using recovery sequence
235+
// TODO: Figure out why this causes the device to stop responding
218236
fn reset_sensor(self: *Self) Error!void {
219237
// On startup the SDA line is sampled. If the line is high, it will listen on address 0x5E.
220238
// Otherwise, it will listen on 0x1F. The line must be kept stable for 200us.
221239
// Because we shouldn't be touching the line that early, and SDA is pulled high, it should
222240
// be assigned 0x5E, but it is better if we can explicitly set it.
223241
// We can explicitly set the address by writing either 0xFF ox 0x00 to address 0
224-
var reset_data: u8 = 0x00;
225-
if (self.address == TLV493D_ADDRESS1)
226-
reset_data = 0xFF; // Set SDA high for address 0x5E
242+
// Set SDA high for address 0x5E, low for 0x1F
243+
const reset_data: u8 = if (self.address == ADDRESS1) 0xFF else 0x00;
227244

228245
// Send recovery frame, clearing bad state
229-
self.dev.writev(.general_call, &.{&.{reset_data}}) catch return Error.DatagramError;
246+
self.dev.writev(.general_call, &.{&.{reset_data}}) catch |e|
247+
return map_error(e);
248+
self.clock.sleep_ms(RESETDELAY_MS);
249+
250+
// It seems that this resets the count to 1
251+
self.expected_frame_count = 1;
230252
}
231253

232254
/// Read all registers from sensor
233255
fn read_out(self: *Self) Error!void {
234256
// Due to padding on the structure, the slice is 16 bytes long, so we have to slice it to 10
235-
const bytes_read = self.dev.readv(self.address, &.{std.mem.asBytes(&self.read_data)[0..10]}) catch return Error.DatagramError;
257+
const bytes_read = self.dev.readv(self.address, &.{std.mem.asBytes(&self.read_data)[0..10]}) catch |e|
258+
return map_error(e);
236259
if (bytes_read != 10) return Error.InvalidData;
260+
261+
if (self.expected_frame_count != self.read_data.FRAMECOUNTER) {
262+
// Section 5.6: Corrective action: Reset sensor
263+
// try self.reset_sensor();
264+
}
265+
266+
self.expected_frame_count = @truncate(@as(u8, self.expected_frame_count) + 1);
237267
}
238268

239269
/// Write configuration to sensor
240270
fn write_out(self: *Self) Error!void {
241271
self.calc_parity();
242-
self.dev.writev(self.address, &.{std.mem.asBytes(&self.write_data)}) catch
243-
return Error.DatagramError;
272+
self.dev.writev(self.address, &.{std.mem.asBytes(&self.write_data)}) catch |e|
273+
return map_error(e);
274+
}
275+
276+
/// Synchronize the expected_frame_count with whatever the device thinks we're on.
277+
fn synchronize_frame_count(self: *Self) Error!void {
278+
const bytes_read = self.dev.readv(self.address, &.{std.mem.asBytes(&self.read_data)[0..10]}) catch |e|
279+
return map_error(e);
280+
if (bytes_read != 10) return Error.InvalidData;
281+
self.expected_frame_count = @truncate(@as(u8, self.read_data.FRAMECOUNTER) + 1);
244282
}
245283

246284
/// Set access mode
@@ -292,7 +330,7 @@ pub const TLV493D = struct {
292330
var powerdown = false;
293331

294332
// In POWERDOWNMODE, sensor has to be switched on for one measurement
295-
if (self.mode == AccessMode.powerdown) {
333+
if (self.mode == .powerdown) {
296334
self.set_access_mode(AccessMode.master_controlled) catch return Error.BusError;
297335

298336
// Wait for measurement delay
@@ -302,7 +340,7 @@ pub const TLV493D = struct {
302340
}
303341

304342
// Read measurement data
305-
self.read_out() catch return Error.BusError;
343+
try self.read_out();
306344

307345
// Construct results from registers
308346
self.x_data = @as(i12, self.read_data.BxH) << 4 | self.read_data.BxL;
@@ -311,54 +349,52 @@ pub const TLV493D = struct {
311349
self.temp_data = @as(i12, self.read_data.TEMPH) << 8 | self.read_data.TEMPL;
312350

313351
// Switch sensor back to POWERDOWNMODE if it was in POWERDOWNMODE before
314-
if (powerdown)
315-
self.set_access_mode(AccessMode.powerdown) catch return Error.BusError;
352+
if (self.mode == .powerdown)
353+
self.set_access_mode(.powerdown) catch return Error.BusError;
316354

317355
// Check for frame errors
318356
if (self.read_data.CHANNEL != 0)
319357
return Error.FrameError;
320-
321-
self.expected_frame_count = self.read_data.FRAMECOUNTER;
322358
}
323359

324360
/// Read magnetic field and temperature values
325361
pub fn read(self: *Self) Error!Values {
326362
try self.update_data();
327363

328364
return Values{
329-
.x = @as(f32, @floatFromInt(self.x_data)) * TLV493D_B_MULT,
330-
.y = @as(f32, @floatFromInt(self.y_data)) * TLV493D_B_MULT,
331-
.z = @as(f32, @floatFromInt(self.z_data)) * TLV493D_B_MULT,
332-
.temp = (@as(f32, @floatFromInt(self.temp_data - TLV493D_TEMP_OFFSET))) * TLV493D_TEMP_MULT,
365+
.x = self.get_x(),
366+
.y = self.get_y(),
367+
.z = self.get_z(),
368+
.temp = self.get_temp(),
333369
};
334370
}
335371

336372
/// Get X-axis magnetic field
337-
pub fn get_x(self: *Self) f32 {
338-
return @as(f32, @floatFromInt(self.x_data)) * TLV493D_B_MULT;
373+
pub inline fn get_x(self: *Self) f32 {
374+
return @as(f32, @floatFromInt(self.x_data)) * B_MULT;
339375
}
340376

341377
/// Get Y-axis magnetic field
342-
pub fn get_y(self: *Self) f32 {
343-
return @as(f32, @floatFromInt(self.y_data)) * TLV493D_B_MULT;
378+
pub inline fn get_y(self: *Self) f32 {
379+
return @as(f32, @floatFromInt(self.y_data)) * B_MULT;
344380
}
345381

346382
/// Get Z-axis magnetic field
347-
pub fn get_z(self: *Self) f32 {
348-
return @as(f32, @floatFromInt(self.z_data)) * TLV493D_B_MULT;
383+
pub inline fn get_z(self: *Self) f32 {
384+
return @as(f32, @floatFromInt(self.z_data)) * B_MULT;
349385
}
350386

351387
/// Get temperature in °C
352-
pub fn get_temp(self: *Self) f32 {
353-
return (@as(f32, @floatFromInt(self.temp_data)) - TLV493D_TEMP_OFFSET) * TLV493D_TEMP_MULT;
388+
pub inline fn get_temp(self: *Self) f32 {
389+
return (@as(f32, @floatFromInt(self.temp_data)) - TEMP_OFFSET) * TEMP_MULT;
354390
}
355391

356392
/// Get magnetic field magnitude
357393
pub fn get_magnitude(self: *Self) f32 {
358394
const x: f32 = @floatFromInt(self.x_data);
359395
const y: f32 = @floatFromInt(self.y_data);
360396
const z: f32 = @floatFromInt(self.z_data);
361-
return TLV493D_B_MULT * @sqrt(x * x + y * y + z * z);
397+
return B_MULT * @sqrt(x * x + y * y + z * z);
362398
}
363399

364400
/// Get azimuth angle (atan2(y, x))
@@ -394,3 +430,13 @@ pub const TLV493D = struct {
394430
self.write_data.PARITY = @truncate(y & 0x01);
395431
}
396432
};
433+
434+
/// Map I2C_Device errors to device errors
435+
fn map_error(err: I2C_Device.InterfaceError) Error {
436+
return switch (err) {
437+
I2C_Device.Error.NoAcknowledge,
438+
I2C_Device.Error.Timeout,
439+
=> Error.NoDevice,
440+
else => Error.BusError,
441+
};
442+
}

examples/nordic/nrf5x/src/i2c_hall_effect.zig

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,11 @@ const nrf = microzig.hal;
66

77
const gpio = nrf.gpio;
88
const i2c = nrf.i2c;
9-
// TODO: Try the DMA one?
10-
const i2cdma = nrf.i2cdma;
119

1210
const I2C_Device = nrf.drivers.I2C_Device;
13-
const uart = nrf.uart.num(0);
1411
const i2c0 = i2c.num(0);
15-
const i2c0d = i2cdma.num(0);
12+
13+
const uart = nrf.uart.num(0);
1614

1715
const TLV493D = microzig.drivers.sensor.TLV493D;
1816

@@ -34,24 +32,21 @@ pub fn main() !void {
3432

3533
nrf.uart.init_logger(uart);
3634

37-
defer i2c0.reset();
38-
35+
// Configure i2c peripheral
3936
try i2c0.apply(.{
4037
.scl_pin = gpio.num(0, 9),
4138
.sda_pin = gpio.num(0, 10),
4239
});
40+
defer i2c0.reset();
4341

44-
// Create i2c datagram device
45-
// var i2c_device = I2C_Device.init(i2c0, @enumFromInt(0x1F), null);
42+
// Create I2C_Device
4643
var i2c_device = I2C_Device.init(i2c0, null);
47-
// Pass i2c device to driver to create sensor instance
44+
// Pass i2c and clock device to driver to create sensor instance
4845
var dev = try TLV493D.init(
4946
i2c_device.i2c_device(),
5047
@enumFromInt(0x5E),
5148
nrf.drivers.clock_device(),
52-
.{
53-
.reset = false,
54-
},
49+
.{},
5550
);
5651

5752
while (true) {
@@ -60,10 +55,7 @@ pub fn main() !void {
6055
"accel: x {d: >6.2} y {d: >6.2} z {d: >6.2} (mT)",
6156
.{ data.x, data.y, data.z },
6257
);
63-
std.log.info("temp: {d: >6.2}°C", .{data.temp});
6458

65-
sleep_ms(500);
59+
sleep_ms(250);
6660
}
67-
68-
std.log.info("Done!", .{});
6961
}

examples/raspberrypi/rp2xxx/build.zig

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,9 @@ pub fn build(b: *std.Build) void {
4646

4747
const chip_agnostic_examples: []const ChipAgnosticExample = &.{
4848
.{ .name = "adc", .file = "src/adc.zig" },
49-
.{ .name = "i2c-bus-scan", .file = "src/i2c_bus_scan.zig" },
5049
.{ .name = "i2c-accel", .file = "src/i2c_accel.zig" },
50+
.{ .name = "i2c-bus-scan", .file = "src/i2c_bus_scan.zig" },
51+
.{ .name = "i2c-hall-effect", .file = "src/i2c_hall_effect.zig" },
5152
.{ .name = "pwm", .file = "src/pwm.zig" },
5253
.{ .name = "uart-echo", .file = "src/uart_echo.zig" },
5354
.{ .name = "uart-log", .file = "src/uart_log.zig" },

0 commit comments

Comments
 (0)