LiDAR test program working

This commit is contained in:
Matthias Portzel 2024-04-23 22:06:16 -04:00
parent a4a10ec3a1
commit d12da1a678
No known key found for this signature in database
2 changed files with 211 additions and 33 deletions

View File

@ -37,3 +37,9 @@ git format-patch HEAD~2 -o outbox
This creates patchfiles for the last 2 commits before HEAD. The files that Git places in the "outbox" folder are a standardized plaintext representation of a commit. You can inspect them to ensure they're correct. You can then attach these files to an email in your GUI email application of choice, or other messenger application. (Note: Maintainers of professional projects that claim to accept email patches will not be happy if you do this, but I think it's much easier.)
Once I receive your changes, I'll review them and merge them into the repository. The resulting commits will still list you as the author.
TODO: Did I ever fix the issue where we weren't passing the correct optimization option?
The first one is using the correct
TODO: document error codes?

View File

@ -19,6 +19,7 @@ const gpio = rp2040.gpio;
const pin_config = rp2040.pins.GlobalConfiguration {
// 6 colored LEDs
// These are not in the order that they are on the board!
.GPIO0 = .{
.name = "led_4",
.direction = .out,
@ -205,6 +206,55 @@ const pin_config = rp2040.pins.GlobalConfiguration {
var pins: rp2040.pins.Pins(pin_config) = undefined;
// const Led = struct {
// pin: hal.gpio.Pin,
// fn on (self: Led) void {
// self.pin.put(0);
// }
// fn off () void {
// self.pin.put(1);
// }
// }
pub fn pin_error(error_code: u6) noreturn {
// In order on the board, 4, 3, 5, 2, 6, 1
var mut_error_code = error_code;
// TODO: invert
pins.led_1.put(@truncate(mut_error_code & 0x01));
mut_error_code >>= 1;
pins.led_6.put(@truncate(mut_error_code & 0x01));
mut_error_code >>= 1;
pins.led_2.put(@truncate(mut_error_code & 0x01));
mut_error_code >>= 1;
pins.led_5.put(@truncate(mut_error_code & 0x01));
mut_error_code >>= 1;
pins.led_3.put(@truncate(mut_error_code & 0x01));
mut_error_code >>= 1;
pins.led_4.put(@truncate(mut_error_code & 0x01));
while (true) {
time.sleep_ms(500);
}
}
test {
var mut_error_code: u6 = 0b010101;
std.log.warn("\n{}", .{ @as(u1, @truncate(mut_error_code & 0x1)) });
mut_error_code >>= 1;
std.log.warn("{}", .{ @as(u1, @truncate(mut_error_code & 0x1)) });
mut_error_code >>= 1;
std.log.warn("{}", .{ @as(u1, @truncate(mut_error_code & 0x1)) });
mut_error_code >>= 1;
std.log.warn("{}", .{ @as(u1, @truncate(mut_error_code & 0x1)) });
mut_error_code >>= 1;
std.log.warn("{}", .{ @as(u1, @truncate(mut_error_code & 0x1)) });
mut_error_code >>= 1;
std.log.warn("{}", .{ @as(u1, @truncate(mut_error_code & 0x1)) });
}
// Our main I2C, used for communicating with the LiDARs, is I2C 0
const i2c0 = i2c.num(0);
@ -239,6 +289,77 @@ const LiDAR = struct {
i2c_address: i2c.Address, // = @enumFromInt(0x29); // The default I2C address is 0x29 (datasheet)
pub fn setup(self: LiDAR) void {
// Loop until we're alive
var res: u8 = 0x00;
const VL6180X_REG_SYSTEM_FRESH_OUT_OF_RESET: u16 = 0x0016;
while (res != 0x01) {
res = self.read8(VL6180X_REG_SYSTEM_FRESH_OUT_OF_RESET);
time.sleep_ms(1);
// The datasheet says I should really toggle the ce pin but I don't feel like it and it wasn't needed in the test program
}
// Copied from https://github.com/adafruit/Adafruit_VL6180X/blob/a42df76ee9d469111de0875b11f26ddb37608ea2/Adafruit_VL6180X.cpp#L121
self.write8(0x0207, 0x01);
self.write8(0x0208, 0x01);
self.write8(0x0096, 0x00);
self.write8(0x0097, 0xfd);
self.write8(0x00e3, 0x00);
self.write8(0x00e4, 0x04);
self.write8(0x00e5, 0x02);
self.write8(0x00e6, 0x01);
self.write8(0x00e7, 0x03);
self.write8(0x00f5, 0x02);
self.write8(0x00d9, 0x05);
self.write8(0x00db, 0xce);
self.write8(0x00dc, 0x03);
self.write8(0x00dd, 0xf8);
self.write8(0x009f, 0x00);
self.write8(0x00a3, 0x3c);
self.write8(0x00b7, 0x00);
self.write8(0x00bb, 0x3c);
self.write8(0x00b2, 0x09);
self.write8(0x00ca, 0x09);
self.write8(0x0198, 0x01);
self.write8(0x01b0, 0x17);
self.write8(0x01ad, 0x00);
self.write8(0x00ff, 0x05);
self.write8(0x0100, 0x05);
self.write8(0x0199, 0x05);
self.write8(0x01a6, 0x1b);
self.write8(0x01ac, 0x3e);
self.write8(0x01a7, 0x1f);
self.write8(0x0030, 0x00);
self.write8(0x0011, 0x10); // Enables polling for 'New Sample ready'
// when measurement completes
self.write8(0x010a, 0x30); // Set the averaging sample period
// (compromise between lower noise and
// increased execution time)
self.write8(0x003f, 0x46); // Sets the light and dark gain (upper
// nibble). Dark gain should not be
// changed.
self.write8(0x0031, 0xFF); // sets the # of range measurements after
// which auto calibration of system is
// performed
self.write8(0x0041, 0x63); // Set ALS integration time to 100ms
self.write8(0x002e, 0x01); // perform a single temperature calibration
// of the ranging sensor
// Optional: Public registers - See data sheet for more detail
// self.write8(SYSRANGE__INTERMEASUREMENT_PERIOD, 0x09); // Set default ranging inter-measurement
// period to 100ms
self.write8(0x003e, 0x31); // Set default ALS inter-measurement period
// to 500ms
self.write8(0x0014, 0x24); // Configures interrupt on 'New Sample
// Ready threshold event'
const VL6180X_REG_IDENTIFICATION_MODEL_ID = 0x000;
if (self.read8(VL6180X_REG_IDENTIFICATION_MODEL_ID) != 0xB4) {
pin_error(0b111111);
}
}
pub fn updateReading (self: *LiDAR) void {
// Adapted from:
// => https://github.com/adafruit/Adafruit_VL6180X/blob/a42df76/Adafruit_VL6180X.cpp#L187
@ -247,6 +368,8 @@ const LiDAR = struct {
const VL6180X_REG_RESULT_RANGE_STATUS = 0x04d;
while ((self.read8(VL6180X_REG_RESULT_RANGE_STATUS) & 0x01) == 0) {}
pins.led_3.put(0);
// Start a range measurement
const VL6180X_REG_SYSRANGE_START = 0x018;
self.write8(VL6180X_REG_SYSRANGE_START, 0x01);
@ -255,6 +378,8 @@ const LiDAR = struct {
const VL6180X_REG_RESULT_INTERRUPT_STATUS_GPIO = 0x04f;
while ((self.read8(VL6180X_REG_RESULT_INTERRUPT_STATUS_GPIO) & 0x04) == 0) {}
pins.led_4.put(0);
// read range in mm
const VL6180X_REG_RESULT_RANGE_VAL = 0x062;
const range: u8 = self.read8(VL6180X_REG_RESULT_RANGE_VAL);
@ -263,6 +388,8 @@ const LiDAR = struct {
const VL6180X_REG_SYSTEM_INTERRUPT_CLEAR = 0x015;
self.write8(VL6180X_REG_SYSTEM_INTERRUPT_CLEAR, 0x07);
pins.led_5.put(0);
// Check the readRangeStatus
const status: VL6180X_Status = @enumFromInt(self.read8(VL6180X_REG_RESULT_RANGE_STATUS) >> 4);
@ -273,42 +400,51 @@ const LiDAR = struct {
self.status = .OutOfRange;
}else {
self.status = .Valid;
self.range = range;
}
}
// Underlying I2C communication functions
pub fn write8 (self: *LiDAR, write_index: u16, value: u8) void {
fn write8 (self: LiDAR, write_index: u16, value: u8) void {
// Doesn't work, I'm not smart
// var buf: [3]u8 = undefined;
// @memset(buf[0..2], std.mem.asBytes(&std.mem.nativeToBig(u16, write_index)));
// buf[2] = value;
const buf: [3]u8 = [_]u8{@truncate(write_index >> 8), @truncate(write_index & 0xFF), value};
const buf: [3]u8 = [_]u8{@truncate(write_index >> 8), @truncate(write_index & 0xFF), value};
_ = i2c0.write_blocking(self.i2c_address, &buf) catch @panic("Failed to write");
_ = i2c0.write_blocking(self.i2c_address, &buf) catch |err| switch (err) {
error.DeviceNotPresent => pin_error(0b000101),
error.Unexpected => pin_error(0b000110),
error.NoAcknowledge => pin_error(0b000111),
// error.Timeout => pin_error(0b001000),
};
}
// read_index is the register address internal to the sensor
// We write what address/index we want to read, and then it writes back the value at that location
pub fn read8 (self: *LiDAR, read_index: u16) u8 {
pub fn read8 (self: LiDAR, read_index: u16) u8 {
// Big endian. We just shifted/masked so it's fine to truncate.
// const buf: [2]u8 = [_]u8{@truncate(read_index >> 8), @truncate(read_index & 0xFF)};
const buf: [2]u8 = [_]u8{@truncate(read_index >> 8), @truncate(read_index & 0xFF)};
// const buf: [2]u8 = ;
// byteSwap to convert the native (ARM) little-endian to the big-endian expected by the LiDAR
_ = i2c0.write_blocking(self.i2c_address, std.mem.asBytes(&std.mem.nativeToBig(u16, read_index))) catch @panic("Failed to write pre-read");
// _ = i2c0.write_blocking(self.i2c_address, std.mem.asBytes(&std.mem.nativeToBig(u16, read_index))) catch pin_error(0b000010);
_ = i2c0.write_blocking(self.i2c_address, &buf) catch pin_error(0b000010);
// From the microzig example
var rx_data: [1]u8 = undefined;
_ = i2c0.read_blocking(self.i2c_address, &rx_data) catch panic("I2C reply invalid/missing", .{});
_ = i2c0.read_blocking(self.i2c_address, &rx_data) catch pin_error(0b000011);
return rx_data[0];
}
// TODO: is Zig camel or snake case?
// Assumes the pins are set so that this is on
pub fn setAddress(self: *LiDAR, newAddress: i2c.Address) void {
pub fn setAddress(self: *LiDAR, new_address: i2c.Address) void {
// Write to the old address
const VL6180X_REG_SLAVE_DEVICE_ADDRESS = 0x212;
self.write8(VL6180X_REG_SLAVE_DEVICE_ADDRESS, @intFromEnum(newAddress) & 0x7F);
self.i2c_address = newAddress;
// TODO: the & 0x7F here is copy-pasted from Adafruit, I think to coerce into 7 bits, but in Zig we shouldn't need it
self.write8(VL6180X_REG_SLAVE_DEVICE_ADDRESS, @intFromEnum(new_address));
self.i2c_address = new_address;
}
};
@ -317,7 +453,8 @@ var lidar_front_right: LiDAR = undefined;
var lidar_back_right: LiDAR = undefined;
var lidar_front_left: LiDAR = undefined;
var lidar_back_left: LiDAR = undefined;
const lidars = [_]*LiDAR{&lidar_front, &lidar_front_right, &lidar_back_right, &lidar_front_left, &lidar_back_left};
// const lidars = [_]*LiDAR{&lidar_front, &lidar_front_right, &lidar_back_right, &lidar_front_left, &lidar_back_left};
const lidars = [_]*LiDAR{&lidar_front};
fn updateLidars () void {
for (lidars) |lidar| {
@ -339,12 +476,23 @@ pub fn setup () void {
// pins.lidar_front_ce,
// };
// Turn off all the LEDs, which are on by default since they're active low
pins.led_1.put(1);
pins.led_2.put(1);
pins.led_3.put(1);
pins.led_4.put(1);
pins.led_5.put(1);
pins.led_6.put(1);
_ = i2c0.apply(.{
.clock_config = rp2040.clock_config, // Zach says the default here is fine
.scl_pin = gpio.num(24),
.sda_pin = gpio.num(25),
});
// Turn on LED 2
pins.led_2.put(0);
// LiDAR setup
for (lidars) |lidar| {
lidar.* = LiDAR {
@ -354,7 +502,7 @@ pub fn setup () void {
};
}
var nextLiDARAddress: i2c.Address = @enumFromInt(0x2B); // The default is 0x2A so if we start at 2B and go up we should be fine
var nextLiDARAddress: i2c.Address = @enumFromInt(0x2A); // The default is 0x29 so if we start at 2A and go up we should be fine
// Disable all of them
pins.lidar_front_ce.put(0);
@ -363,30 +511,50 @@ pub fn setup () void {
pins.lidar_back_right_ce.put(0);
pins.lidar_back_left_ce.put(0);
// TODO: Instead of writing 1 to the CE pins, we should set them as inputs and change them to pull-high
time.sleep_ms(1000);
// Turn on LED 3
pins.led_3.put(0);
// front
pins.lidar_front_ce.put(1);
time.sleep_ms(1); // From the datasheet, 1ms to boot
lidar_front.setup();
// Zach has sanity-checked that these CE are all off except for the front CE one
// So this is failing.
lidar_front.setAddress(nextLiDARAddress);
nextLiDARAddress = @enumFromInt(@intFromEnum(nextLiDARAddress) + 1);
// front left
pins.lidar_front_left_ce.put(1);
lidar_front_left.setAddress(nextLiDARAddress);
nextLiDARAddress = @enumFromInt(@intFromEnum(nextLiDARAddress) + 1);
if (lidar_front.read8(0x00) == 0xB4) {
pins.led_4.put(0);
}else {
pin_error(0b011111);
}
// front right
pins.lidar_front_right_ce.put(1);
lidar_front_right.setAddress(nextLiDARAddress);
nextLiDARAddress = @enumFromInt(@intFromEnum(nextLiDARAddress) + 1);
// // Turn on LED 4
// back left
pins.lidar_back_left_ce.put(1);
lidar_back_left.setAddress(nextLiDARAddress);
nextLiDARAddress = @enumFromInt(@intFromEnum(nextLiDARAddress) + 1);
// These 4 are not yet connected
// // front left
// pins.lidar_front_left_ce.put(1);
// lidar_front_left.setAddress(nextLiDARAddress);
// nextLiDARAddress = @enumFromInt(@intFromEnum(nextLiDARAddress) + 1);
// back right
pins.lidar_back_right_ce.put(1);
lidar_back_right.setAddress(nextLiDARAddress);
nextLiDARAddress = @enumFromInt(@intFromEnum(nextLiDARAddress) + 1);
// // front right
// pins.lidar_front_right_ce.put(1);
// lidar_front_right.setAddress(nextLiDARAddress);
// nextLiDARAddress = @enumFromInt(@intFromEnum(nextLiDARAddress) + 1);
// // back left
// pins.lidar_back_left_ce.put(1);
// lidar_back_left.setAddress(nextLiDARAddress);
// nextLiDARAddress = @enumFromInt(@intFromEnum(nextLiDARAddress) + 1);
// // back right
// pins.lidar_back_right_ce.put(1);
// lidar_back_right.setAddress(nextLiDARAddress);
// nextLiDARAddress = @enumFromInt(@intFromEnum(nextLiDARAddress) + 1);
// for (lidar_ce_pins) |pin| {
// pin.set_direction(.out);
@ -452,7 +620,6 @@ pub fn turn180() void {
}
pub fn stall() noreturn {
pins.motor_right_1.put(1);
pins.motor_right_2.put(0);
pins.motor_left_1.put(1);
@ -511,9 +678,14 @@ pub fn recordWall(p: Point, direction: Cardinal) void {
pub fn testUpdate () void {
updateLidars();
// LEDs are on by default
pins.led_6.toggle();
pins.led_1.put(if (lidar_front.range > 50) 1 else 0);
pins.led_2.put(if (lidar_front_right.range > 50) 1 else 0);
pins.led_3.put(if (lidar_front_left.range > 50) 1 else 0);
pins.led_4.put(if (lidar_back_left.range > 50) 1 else 0);
pins.led_5.put(if (lidar_back_right.range > 50) 1 else 0);
// pins.led_2.put(if (lidar_front_right.range > 50) 1 else 0);
// pins.led_3.put(if (lidar_front_left.range > 50) 1 else 0);
// pins.led_4.put(if (lidar_back_left.range > 50) 1 else 0);
// pins.led_5.put(if (lidar_back_right.range > 50) 1 else 0);
time.sleep_ms(100);
}