Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion examples/esp32_everloop/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@ cty = "0.2"
esp-idf-sys = "0.1.2"

[build-dependencies]
esp_idf_build = "0.1"
esp_idf_build = ">= 0.1.0, < 0.1.1"
15 changes: 4 additions & 11 deletions src/bus/esp/bus.rs
Original file line number Diff line number Diff line change
Expand Up @@ -188,19 +188,12 @@ fn spi_address_bytes(address: u16, readnwrite: bool) -> HardwareAddress {
}

impl MatrixBus for Bus {
fn write(&self, write_buffer: &mut [u8]) {
// Unpack the write address from the first 32-bits
let buffer_u32 = unsafe { core::intrinsics::transmute::<&mut [u8], &mut [u32]>(write_buffer) };
let address = u16::try_from(buffer_u32[0]).unwrap();
// Write actual data to the address
self.write_address(address, &write_buffer[crate::MATRIXBUS_HEADER_BYTES..])
fn write(&self, address: u16, write_buffer: &[u8]) {
self.write_address(address, write_buffer)
}

fn read(&self, read_buffer: &mut [u8]) {
// Unpack the read address from the first 32-bits
let buffer_u32 = unsafe { core::intrinsics::transmute::<&mut [u8], &mut [u32]>(read_buffer) };
let address = u16::try_from(buffer_u32[0]).unwrap();
self.read_address(address, &mut read_buffer[crate::MATRIXBUS_HEADER_BYTES..])
fn read(&self, address: u16, read_buffer: &mut [u8]) {
self.read_address(address, read_buffer)
}

fn close(&self) {
Expand Down
4 changes: 2 additions & 2 deletions src/bus/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ pub trait MatrixBus {
/// // send buffer
/// bus.write(unsafe { std::mem::transmute::<&mut [u32], &mut [u8]>(&mut buffer) });
/// ```
fn write(&self, write_buffer: &mut [u8]);
fn write(&self, address: u16, write_buffer: &[u8]);

/// Send a read buffer to the MATRIX Bus. The buffer requires an `address` to request and
/// the `byte_length` of what's expected to be returned. Once sent, the buffer return with populated
Expand All @@ -57,7 +57,7 @@ pub trait MatrixBus {
/// // returned data will start at buffer[2]
/// println!("{:?}", buffer);
/// ```
fn read(&self, read_buffer: &mut [u8]);
fn read(&self, address: u16, read_buffer: &mut [u8]);

/// If possible, close the connection to the MATRIX Bus.
fn close(&self);
Expand Down
6 changes: 4 additions & 2 deletions src/bus/std_bus/direct/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,15 +68,17 @@ impl Bus {
}

impl MatrixBus for Bus {
fn write(&self, write_buffer: &mut [u8]) {
fn write(&self, address: u16, write_buffer: &[u8]) {
unsafe {
// TODO: ....
unimplemented!()
}
}

fn read(&self, read_buffer: &mut [u8]) {
fn read(&self, address: u16, read_buffer: &mut [u8]) {
unsafe {
// TODO: ....
unimplemented!()
}
}

Expand Down
58 changes: 52 additions & 6 deletions src/bus/std_bus/kernel.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use super::super::MatrixBus;
use crate::info;
use crate::{bus::memory_map::*, error::Error, Device};
use crate::{as_mut_u8_slice, as_u8_slice, bus::memory_map::*, error::Error, Device, info};
use nix::fcntl::{open, OFlag}; // https://linux.die.net/man/3/open
use nix::sys::stat::Mode;
use nix::unistd::close;
Expand Down Expand Up @@ -61,18 +60,65 @@ impl Bus {
}
}

// Calculate number of i32 needed to contain:
// - Address
// - Buffer size
// - buffer.len() bytes
fn ioctl_buffer_i32(buffer: &[u8]) -> usize {
const SIZE_OF_I32: usize = core::mem::size_of::<i32>();
// `(x + y - 1) / y` is the same as `ceiling(x as f32/y as f32) as usize`:
// |buffer.len() | return
// |-|-|
// | 0 | 2 + 0
// | 1 - 4 | 2 + 1
// | 5 - 8 | 2 + 2
2 + (buffer.len() + SIZE_OF_I32 - 1) / SIZE_OF_I32
}

impl MatrixBus for Bus {
fn write(&self, write_buffer: &mut [u8]) {
fn write(&self, address: u16, write_buffer: &[u8]) {
unsafe {
// Pack request into word-sized/aligned buffer
// Bytes:
// [0..4] = address
// [4..8] = byte size of write payload
// [8..8+sizeof(payload)] = payload
let write_buffer = {
let mut retval = vec![0i32; ioctl_buffer_i32(write_buffer)];
retval[0] = address as i32;
retval[1] = write_buffer.len() as i32;
as_mut_u8_slice(&mut retval[2..])[..write_buffer.len()].copy_from_slice(write_buffer);
retval
};
// TODO: error handling. Not sure if an error here would be worth recovering from.
ioctl_write(self.regmap_fd, write_buffer).expect("error in IOCTL WRITE");
ioctl_write(self.regmap_fd, as_u8_slice(&write_buffer[..])).expect("error in IOCTL WRITE");
}
}

fn read(&self, read_buffer: &mut [u8]) {
fn read(&self, address: u16, read_buffer: &mut [u8]) {
unsafe {
// Pack request into word-sized/aligned buffer
// Bytes:
// [0..4] = address
// [4..8] = byte size of read request
// [8..8+sizeof(payload)] = destination for read payload
let mut buffer = {
let mut retval = vec![0i32; ioctl_buffer_i32(read_buffer)];
retval[0] = address as i32;
retval[1] = read_buffer.len() as i32;
retval
};
// TODO: error handling. Not sure if an error here would be worth recovering from.
ioctl_read(self.regmap_fd, read_buffer).expect("error in IOCTL READ");
ioctl_read(self.regmap_fd, as_mut_u8_slice(&mut buffer[..])).expect("error in IOCTL READ");
// Copy read data back into original argument buffer
read_buffer.copy_from_slice(
&as_u8_slice(
// Skip address and size words
&buffer[2..]
)
// Limit to size of destination buffer (request was word-aligned)
[..read_buffer.len()]
);
}
}

Expand Down
4 changes: 3 additions & 1 deletion src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ pub enum Error {
/// ESP-IDF call failed.
#[cfg(not(feature = "std"))]
#[fail(display = "esp-idf error: {}", error)]
EspIdf { error: crate::bus::esp::error::EspError },
EspIdf {
error: crate::bus::esp::error::EspError,
},
}

with_std! {
Expand Down
20 changes: 11 additions & 9 deletions src/everloop/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,21 @@ mod led;
use crate::bus::memory_map::*;
use crate::bus::MatrixBus;
use heapless::Vec;
use typenum::Unsigned;
pub use led::Rgbw;
use typenum::Unsigned;

/// Bytes to set LED color as [R, G, B, W].
type LedBytes = [u8; 4];
/// Heapless capacity type large enough maximum number of possible LEDs across all devices.
type MaxLeds = heapless::consts::U35;
/// Heapless capacity type large enough for byte array of maximum possible LEDs and header bytes as passed to `MatrixBus` methods.
type MaxLedBytes = heapless::consts::U148;
type MaxLedBytes = heapless::consts::U140;

const LED_BYTES: usize = core::mem::size_of::<LedBytes>();

fn _compile_time_checks() {
// Need to enforce constraint that certain const/enum values correspond to heapless capacity type "values".
// One way to accomplish this is to use both as the length of an array and assign them to eachother. If the values
// One way to accomplish this is to use both as the length of an array and assign them to eachother. If the values
// differ, the arrays are different lengths and type, and Rust will trigger a compiler error.
{
// Require `MaxLeds` == `MATRIX_CREATOR_LEDS`
Expand All @@ -25,7 +25,7 @@ fn _compile_time_checks() {
}
{
// Require `MaxLedBytes` has enough space to maximum possible LEDs and header bytes
type ValueArray = [u8; device_info::MATRIX_CREATOR_LEDS as usize * LED_BYTES + crate::MATRIXBUS_HEADER_BYTES];
type ValueArray = [u8; device_info::MATRIX_CREATOR_LEDS as usize * LED_BYTES];
let _unused: ValueArray = [0u8; MaxLedBytes::USIZE];
}
}
Expand Down Expand Up @@ -59,25 +59,27 @@ impl<'a> Everloop<'a> {
// create write buffer
let led_bytes = device_leds * LED_BYTES;
let mut request: Vec<u8, MaxLedBytes> = Vec::new();
request.extend_from_slice(&(fpga_address::EVERLOOP as i32).to_ne_bytes()).unwrap();
request.extend_from_slice(&(led_bytes as i32).to_ne_bytes()).unwrap(); // each LED RGBW requires 4 bytes

// store all LED colors given
for led in leds {
request.extend_from_slice(&led.to_bytes()).unwrap();
}
// set remaining LEDs to black
for _ in 0..(device_leds - leds.len()) {
request.extend_from_slice(&Rgbw::black().to_bytes()).unwrap();
request
.extend_from_slice(&Rgbw::black().to_bytes())
.unwrap();
}
// render LEDs
self.bus
.write(&mut request);
.write(fpga_address::EVERLOOP, &mut (request[..led_bytes]));
}

/// Set all MATRIX LEDs to a single color
pub fn set_all(&self, color: Rgbw) {
let leds: Vec<Rgbw, MaxLeds> = core::iter::repeat(color).take(self.bus.device_leds() as usize).collect();
let leds: Vec<Rgbw, MaxLeds> = core::iter::repeat(color)
.take(self.bus.device_leds() as usize)
.collect();
self.set(&leds)
}
}
13 changes: 4 additions & 9 deletions src/gpio/bank.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use crate::bus::memory_map::*;
use crate::bus::MatrixBus;
use core::intrinsics::transmute;
use crate::as_u8_slice;
use crate::bus::{memory_map::*, MatrixBus};

/// Bank contains functions to configure a PWM.
/// A bank is a set of 4 pins, starting from pin 0 and going in order.
Expand Down Expand Up @@ -59,12 +58,8 @@ impl<'a> Bank<'a> {
/// Send a bank configuration to the MATRIX bus.
fn bus_write(&self, memory_offset: u16, timer_setup: u16) {
// create and populate write buffer
let mut buffer: [u32; 3] = [0; 3];
buffer[0] = (memory_offset) as u32; // address to write to
buffer[1] = 2; // byte length of timer_setup
buffer[2] = timer_setup as u32;
let buffer = [timer_setup; 1];

self.bus
.write(unsafe { transmute::<&mut [u32], &mut [u8]>(&mut buffer) });
self.bus.write(memory_offset, as_u8_slice(&buffer));
}
}
34 changes: 12 additions & 22 deletions src/gpio/mod.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
use crate::bus::MatrixBus;
use crate::Error;
use crate::bus::{memory_map::*, MatrixBus};
use crate::{as_mut_u8_slice, as_u8_slice, Error};
pub mod bank;
pub mod config;
use crate::bus::memory_map::*;
pub use bank::*;
pub use config::*;
use core::intrinsics::transmute;
use core::sync::atomic::{AtomicU16, Ordering};

/// Controls the GPIO pins on a MATRIX device.
Expand Down Expand Up @@ -56,14 +54,14 @@ impl<'a> Gpio<'a> {
Gpio::is_pin_valid(pin).unwrap();

// create read buffer
let mut data: [u32; 3] = [0; 3];
let mut data = [0u16; 1];

// update read buffer
self.bus_read(&mut data, 2, 1); // all pin states are encoded as a single u16. 2 bytes needed (8*2 = 16 pins)
self.bus_read(&mut data, 1); // all pin states are encoded as a single u16. 2 bytes needed (8*2 = 16 pins)

// bit operation to extract the current pin's state
let mask = 0x1 << pin;
let state = (data[2] & mask) >> pin;
let state = (data[0] & mask) >> pin;

match state {
0 => false,
Expand All @@ -78,16 +76,16 @@ impl<'a> Gpio<'a> {
/// Returns the current digital value of every MATRIX GPIO pin (0->15)
pub fn get_states(&self) -> [bool; 16] {
// create read buffer
let mut data: [u32; 3] = [0; 3];
let mut data = [0u16; 1];

// update read buffer
self.bus_read(&mut data, 2, 1); // all pin states are encoded as a single u16. 2 bytes needed (8*2 = 16 pins)
self.bus_read(&mut data, 1); // all pin states are encoded as a single u16. 2 bytes needed (8*2 = 16 pins)

// bit operation to extract each pin state (0-15)
let mut pins: [bool; 16] = [false; 16];
for i in 0..16 {
let mask = 0x1 << i;
let state = ((data[2] & mask) >> i) as u8;
let state = ((data[0] & mask) >> i) as u8;

pins[i] = match state {
0 => false,
Expand All @@ -102,16 +100,11 @@ impl<'a> Gpio<'a> {
}

/// Shortener to populate a read buffer, through `bus.read`, for GPIO pin information.
fn bus_read(&self, buffer: &mut [u32], buffer_length: u32, address_offset: u16) {
// address to query
buffer[0] = (fpga_address::GPIO + address_offset) as u32;
// size of expected data (bytes)
buffer[1] = buffer_length;

fn bus_read(&self, buffer: &mut [u16], address_offset: u16) {
// populate buffer
// the buffer will be passed a value that contains the state of each GPIO pin
self.bus
.read(unsafe { transmute::<&mut [u32], &mut [u8]>(buffer) });
.read(fpga_address::GPIO + address_offset, as_mut_u8_slice(buffer));
}
}

Expand Down Expand Up @@ -150,13 +143,10 @@ impl<'a> Gpio<'a> {

/// Shortener to send pin configurations through `bus.write`.
fn bus_write(&self, value: u16, address_offset: u16) {
let mut buffer: [u32; 3] = [0; 3];
buffer[0] = (fpga_address::GPIO + address_offset) as u32; // address to write to
buffer[1] = 2; // byte length of u16 value
buffer[2] = value as u32;
let buffer = [value; 1];

self.bus
.write(unsafe { transmute::<&mut [u32], &mut [u8]>(&mut buffer) });
.write(fpga_address::GPIO + address_offset, as_u8_slice(&buffer));
}

/// Set the prescaler value for a specific bank
Expand Down
27 changes: 10 additions & 17 deletions src/info.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,14 @@
use crate::bus::{memory_map::*, MatrixBus};
use crate::{Device, Error};
use core::intrinsics::transmute;
use crate::{as_mut_u8_slice, Device, Error};

/// Return the type of MATRIX device being used and the version of the board.
pub fn get_device_info(bus: &dyn MatrixBus) -> Result<(Device, u32), Error> {
// create read buffer
let mut data: [i32; 4] = [0; 4];
data[0] = fpga_address::CONF as i32;
data[1] = 8; // device_name(4 bytes) device_version(4 bytes)
let mut data = [0i32; 2]; // device_name(4 bytes) device_version(4 bytes)

bus.read(unsafe { transmute::<&mut [i32], &mut [u8]>(&mut data) });
let device_name = data[2];
let device_version = data[3];
bus.read(fpga_address::CONF, as_mut_u8_slice(&mut data));
let device_name = data[0];
let device_version = data[1];

Ok((
match device_name {
Expand All @@ -26,16 +23,12 @@ pub fn get_device_info(bus: &dyn MatrixBus) -> Result<(Device, u32), Error> {
/// Updates the Bus to have the last known FPGA frequency of the MATRIX device.
pub fn get_fpga_frequency(bus: &dyn MatrixBus) -> Result<u32, Error> {
// create read buffer
let mut data: [i32; 3] = [0; 3];
data[0] = (fpga_address::CONF + 4) as i32;
data[1] = 4; // value0(2 bytes) value1(2bytes) // TODO: ask what these values represent
let mut data = [0u16; 2]; // value0(2 bytes) value1(2bytes) // TODO: ask what these values represent
bus.read(fpga_address::CONF + 4, as_mut_u8_slice(&mut data));

bus.read(unsafe { transmute::<&mut [i32], &mut [u8]>(&mut data) });

// extract both u16 numbers from u32
let value0 = data[2] >> 16; // store 1st 16 bits
let value1 = !(value0 << 16) & data[2]; // store 2nd 16 bits
let frequency = (device_info::FPGA_CLOCK * value0 as u32) / value1 as u32;
let value0 = data[0] as u32; // store 1st 16 bits
let value1 = data[1] as u32; // store 2nd 16 bits
let frequency = (device_info::FPGA_CLOCK * value0) / value1;

Ok(frequency)
}
Loading