Untested but possibly working LiDAR code

This commit is contained in:
Matthias Portzel 2024-04-20 23:12:49 -04:00
parent 675439c63a
commit a4a10ec3a1
No known key found for this signature in database
2 changed files with 215 additions and 62 deletions

View File

@ -1,19 +1,12 @@
const std = @import("std");
const algo = @import("algorithm.zig");
const robot = algo.robot;
const robot = @import("rp2040-bot.zig");
const map = @import("map.zig");
pub fn main() !noreturn {
// Setup
algo.setup();
robot.setup();
// Flood fill to the goal
algo.floodFill(map.goals[0..]);
// Flood fill back to the start
const starts = [_]map.Point{ map.start };
algo.floodFill(starts[0..]);
robot.stall();
while (true) {
robot.testUpdate();
}
}

View File

@ -2,6 +2,8 @@
// This file is only for defining a hardware abstraction layer
// The code in this file will be dirty, it will deal with setting up encoders and all sorts of BS
const std = @import("std");
const panic = @import("std").debug.panic;
const map = @import("map.zig");
const Point = map.Point;
const Cardinal = map.Cardinal;
@ -13,6 +15,8 @@ const time = rp2040.time;
const i2c = rp2040.i2c;
const gpio = rp2040.gpio;
// --- Setup ---
const pin_config = rp2040.pins.GlobalConfiguration {
// 6 colored LEDs
.GPIO0 = .{
@ -101,7 +105,7 @@ const pin_config = rp2040.pins.GlobalConfiguration {
},
// Motor Encoders
// Zach recommends triggering a double-edged interrupter on 1 and clocking on 2
// Zach recommends triggering a double-edged interrupt on 1 and clocking on 2
// You read the second to know if you're going forward or backwards
.GPIO14 = .{
// MAE2
@ -198,32 +202,136 @@ const pin_config = rp2040.pins.GlobalConfiguration {
.direction = .in,
},
};
// comptime {
// @compileLog(@typeInfo(@TypeOf(pin_config.apply)));
// }
// var pins: @typeInfo(@TypeOf(pin_config.apply)).Fn.return_type = undefined;
const LEFT_MOTOR = 1;
const RIGHT_MOTOR = 2;
// 254mm square size
const SQUARE_SIZE = 254;
var pins: rp2040.pins.Pins(pin_config) = undefined;
pub fn toggle_led_6 () void {
// TODO: this does bad things if pins hasn't been defined by the first core yet
pins.led_5.toggle();
// Our main I2C, used for communicating with the LiDARs, is I2C 0
const i2c0 = i2c.num(0);
const VL6180X_Status = enum (usize) {
// Codes and descriptions from the VL6180X datasheet
ValidMeasurement = 0, // Valid measurement
SystemErrorOne = 1, // System error detected (can only happen on
SystemErrorTwo = 2, // power on). No measurement possible
SystemErrorThree = 3,
SystemErrorFour = 4,
SystemErrorFive = 5,
EarlyConvergenceEstimateFailed = 6, // ECE check failed
MaxConvergenceExceeded = 7, // System did not converge before the specified max. convergence time limit
RangeIgnore = 8, // Ignore threshold check failed
// 9 and 10 are "Not used"
SignalToNoiseError = 11, // Ambient conditions too high. Measurement not valid
RangeUnderflowTwelve = 12, // Range value < 0
RangeOverflowThirteen = 13, // Range value out of range (A target is detected by the device, but at a distance that results in internal variable overflow)
RangeUnderflowFourteen = 14, // Range value < 0
RangeOverflowFifteen = 15, // Range value out of range
// 16 and 18 are emitted by the ST's API, not the hardware, if I understand the datasheet correctly
// .RangingFiltered = 16, // Distance filtered by Wrap Around Filter. Occurs when a high reflectance target is detected between 600mm to 1.2m
// 17 is "Not used"
// .DataNotReady = 18, // Returned from RangeGetMeasurementIfReady if not ready
};
const LIDAR_RANGE_MAX = 110;
const LiDAR = struct {
range: u8,
status: enum (usize) { Valid, OutOfRange, ReadError },
i2c_address: i2c.Address, // = @enumFromInt(0x29); // The default I2C address is 0x29 (datasheet)
pub fn updateReading (self: *LiDAR) void {
// Adapted from:
// => https://github.com/adafruit/Adafruit_VL6180X/blob/a42df76/Adafruit_VL6180X.cpp#L187
// wait for device to be ready for range measurement
const VL6180X_REG_RESULT_RANGE_STATUS = 0x04d;
while ((self.read8(VL6180X_REG_RESULT_RANGE_STATUS) & 0x01) == 0) {}
// Start a range measurement
const VL6180X_REG_SYSRANGE_START = 0x018;
self.write8(VL6180X_REG_SYSRANGE_START, 0x01);
// Poll until bit 2 is set
const VL6180X_REG_RESULT_INTERRUPT_STATUS_GPIO = 0x04f;
while ((self.read8(VL6180X_REG_RESULT_INTERRUPT_STATUS_GPIO) & 0x04) == 0) {}
// read range in mm
const VL6180X_REG_RESULT_RANGE_VAL = 0x062;
const range: u8 = self.read8(VL6180X_REG_RESULT_RANGE_VAL);
// clear interrupt
const VL6180X_REG_SYSTEM_INTERRUPT_CLEAR = 0x015;
self.write8(VL6180X_REG_SYSTEM_INTERRUPT_CLEAR, 0x07);
// Check the readRangeStatus
const status: VL6180X_Status = @enumFromInt(self.read8(VL6180X_REG_RESULT_RANGE_STATUS) >> 4);
if (status != .ValidMeasurement) {
// Okay this is bad but the first is the status from the chip and the second is the status in us
self.status = .ReadError;
}else if (range > LIDAR_RANGE_MAX) {
self.status = .OutOfRange;
}else {
self.status = .Valid;
}
}
// Underlying I2C communication functions
pub 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};
_ = i2c0.write_blocking(self.i2c_address, &buf) catch @panic("Failed to write");
}
// 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 {
// 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 = ;
// 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");
// From the microzig example
var rx_data: [1]u8 = undefined;
_ = i2c0.read_blocking(self.i2c_address, &rx_data) catch panic("I2C reply invalid/missing", .{});
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 {
// 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;
}
};
var lidar_front: LiDAR = undefined;
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};
fn updateLidars () void {
for (lidars) |lidar| {
lidar.updateReading();
}
}
// Zach confirms i2c 0
// const i2c0 = i2c.num(0);
// Pin stuff
pub fn setup () void {
pins = pin_config.apply();
// const lidar_ce_pins = [_]microzig.hal.gpio.Pin{
// @compileLog(@typeInfo(microzig.hal.pins.GlobalConfiguration).Struct);
// @compileLog(@typeInfo(@TypeOf(pins)));
// Zig's a bad language ...
// const lidar_ce_pins = [_]@TypeOf(pins.lidar_front_left_ce){
// pins.lidar_front_left_ce,
// pins.lidar_front_right_ce,
// pins.lidar_back_right_ce,
@ -231,17 +339,55 @@ pub fn setup () void {
// pins.lidar_front_ce,
// };
// _ = i2c0.apply(.{
// .clock_config = rp2040.clock_config, // Zach says the default here is fine
// .scl_pin = gpio.num(24),
// .sda_pin = gpio.num(25),
// });
_ = i2c0.apply(.{
.clock_config = rp2040.clock_config, // Zach says the default here is fine
.scl_pin = gpio.num(24),
.sda_pin = gpio.num(25),
});
// LiDAR setup
for (lidars) |lidar| {
lidar.* = LiDAR {
.range = undefined,
.status = undefined,
.i2c_address = @enumFromInt(0x29), // The default I2C address is 0x29 (datasheet)
};
}
var nextLiDARAddress: i2c.Address = @enumFromInt(0x2B); // The default is 0x2A so if we start at 2B and go up we should be fine
// Disable all of them
pins.lidar_front_ce.put(0);
pins.lidar_front_left_ce.put(0);
pins.lidar_front_right_ce.put(0);
pins.lidar_back_right_ce.put(0);
pins.lidar_back_left_ce.put(0);
// front
pins.lidar_front_ce.put(1);
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);
// 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);
// // We need to setup the LiDAR addresses
// // TODO: Create a Lidar struct with methods
// // The way we're doing this right now, we are enabling every LiDAR
// const LIDAR_ADDR_BASE: i2c.Address = @enumFromInt(0x50);
// // Disable all of them
// for (lidar_ce_pins) |pin| {
// pin.set_direction(.out);
// pin.put(0);
@ -257,31 +403,16 @@ pub fn setup () void {
// current_address = @enumFromInt(@intFromEnum(current_address) + 1);
// }
// pins.onboard_led.toggle();
// pins.onboard_led.toggle();
// TODO
// As elsewhere, blocked on upstream
// start_alt_core()
// TODO: Motor controller setup
}
pub fn moveForward() void {
// TODO
}
// --- IO access functions ---
pub fn turn90(direction: map.TurningDirection) void {
// TODO
_ = direction;
}
pub fn turn180() void {
// TODO
}
// pub fn readFront () !bool
// pub fn readLeft () !bool
// pub fn readRight () !bool
// pub fn readBack () ?? throw I guess
// TODO:
// These are used by the algorithm to check if we have walls in the respective directions
pub fn readRight() bool {
return true;
}
@ -302,6 +433,24 @@ pub fn start_alt_core() !void {
// multicore.launch_core1(alt_core_main_function);
}
// --- Map and movement functions ---
// 254mm square size
const SQUARE_SIZE = 254;
pub fn moveForward() void {
// TODO
}
pub fn turn90(direction: map.TurningDirection) void {
// TODO
_ = direction;
}
pub fn turn180() void {
// TODO
}
pub fn stall() noreturn {
pins.motor_right_1.put(1);
@ -357,3 +506,14 @@ pub fn recordWall(p: Point, direction: Cardinal) void {
_ = p;
_ = direction;
}
// --- Test functions ---
pub fn testUpdate () void {
updateLidars();
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);
}