tmp: creating specific modules for 8 channels & 16 channels

This commit is contained in:
2025-08-17 08:57:05 +00:00
parent 57ace1383b
commit c9ca399e20
23 changed files with 253 additions and 88 deletions
+2 -1
View File
@@ -7,7 +7,8 @@ RUN apt-get update && \
gdb-multiarch \
gcc-arm-linux-gnueabihf \
g++-arm-linux-gnueabihf \
binutils-arm-linux-gnueabihf && \
binutils-arm-linux-gnueabihf \
libclang-dev && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
Generated
+1 -22
View File
@@ -144,26 +144,6 @@ dependencies = [
"windows-targets",
]
[[package]]
name = "bindgen"
version = "0.71.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f58bf3d7db68cfbac37cfc485a8d711e87e064c3d0fe0435b92f7a407f9d6b3"
dependencies = [
"bitflags 2.9.1",
"cexpr",
"clang-sys",
"itertools",
"log",
"prettyplease",
"proc-macro2",
"quote",
"regex",
"rustc-hash",
"shlex",
"syn",
]
[[package]]
name = "bindgen"
version = "0.72.0"
@@ -512,7 +492,7 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
name = "led_driver"
version = "0.1.0"
dependencies = [
"bindgen 0.72.0",
"bindgen",
]
[[package]]
@@ -543,7 +523,6 @@ version = "0.1.0"
dependencies = [
"alsa",
"artnet_protocol",
"bindgen 0.71.1",
"crossbeam",
"ctrlc",
"env_logger",
+11
View File
@@ -7,3 +7,14 @@ edition = "2024"
[build-dependencies]
bindgen = "0.72.0"
[features]
default = ["rpizero2"]
rpizero2 = ["rpi3"]
rpizero = ["rpi"]
rpi5 = []
rpi4 = []
rpi3 = []
rpi2 = []
rpi = []
16channel = []
+6 -1
View File
@@ -1,10 +1,15 @@
use std::{env, path::PathBuf};
fn main() {
println!("cargo::rustc-link-search=/workspaces/LightSabre/lightsabre_backend/led_driver/lib");
println!(
"cargo::rustc-link-search=/workspaces/LightSabre/lightsabre_backend/led_driver/lib/bin"
);
// Tell cargo to tell rustc to link the RPiLedBars_drivers and logc libraries.
#[cfg(not(feature = "16channel"))]
println!("cargo:rustc-link-lib=RpiLedBars_drivers_8ch");
#[cfg(feature = "16channel")]
println!("cargo:rustc-link-lib=RpiLedBars_drivers_16ch");
println!("cargo:rustc-link-lib=logc");
// The bindgen::Builder is the main entry point to bindgen, and lets you build up options for the resulting bindings.
@@ -47,6 +47,12 @@ MEM_MAP vc_mem;
TXDATA_T *txdata;
TXDATA_T tx_buffer[TX_BUFF_LEN(CHAN_MAXLEDS)];
#if LED_NCHANS <= 8
TXDATA_T tx_send_buffer[TX_BUFF_SIZE(CHAN_MAXLEDS)];
#else
TXDATA_T *tx_send_buffer = tx_buffer;
#endif /* LED_NCHANS <= 8 */
void swap_bytes();
void leddriver_setup() {
@@ -113,7 +119,7 @@ void leddriver_refresh() {
usleep(10);
}
memcpy(txdata, tx_buffer, TX_BUFF_SIZE(CHAN_MAXLEDS));
memcpy(txdata, tx_send_buffer, TX_BUFF_SIZE(CHAN_MAXLEDS));
enable_dma(DMA_CHAN_A);
start_smi(&vc_mem);
usleep(10);
@@ -122,11 +128,13 @@ void leddriver_refresh() {
// Swap adjacent bytes in transmit data
void swap_bytes() {
uint16_t *wp = (uint16_t *)tx_buffer;
uint16_t *wp_dest = (uint16_t *)tx_send_buffer;
int len = TX_BUFF_SIZE(CHAN_MAXLEDS);
len = (len + 1) / 2;
while (len-- > 0) {
*wp = __builtin_bswap16(*wp);
*wp_dest = __builtin_bswap16(*wp);
wp_dest++;
wp++;
}
}
+16 -6
View File
@@ -1,9 +1,13 @@
#include "include/log.h"
#include "src/leddriver/rpi_leddriver.h"
// #include "src/leddriver/rpi_leddriver.h"
#include "src/common.h"
#include "src/dma/rpi_dma.h"
#include "src/dma/rpi_videocore.h"
#include "src/smi/rpi_smi.h"
#include "src/gpio/rpi_gpio.h"
#define CHAN_MAXLEDS 60 // Maximum number of LEDs per channel
#define LED_NCHANS 8 // Number of LED channels (8 or 16)
#define TX_TEST 0 // If non-zero, use dummy Tx data
#define LED_NBITS 24 // Number of data bits per LED
@@ -11,8 +15,9 @@
#define LED_POSTBITS 4 // Number of zero bits after LED data
#define BIT_NPULSES 3 // Number of O/P pulses per LED bit
// Length of data for 1 row (1 LED on each channel)
// // Length of data for 1 row (1 LED on each channel)
#define LED_DLEN (LED_NBITS * BIT_NPULSES)
#define LED_NCHANS 8 // Number of channels, 8 or 16
// Transmit data type, 8 or 16 bits
#if LED_NCHANS > 8
@@ -21,14 +26,19 @@ typedef uint16_t TXDATA_T;
typedef uint8_t TXDATA_T;
#endif
// Ofset into Tx data buffer, given LED number in chan
// // Ofset into Tx data buffer, given LED number in chan
#define LED_TX_OSET(n) (LED_PREBITS + (LED_DLEN * (n)))
// Size of data buffers & NV memory, given number of LEDs per chan
// // Size of data buffers & NV memory, given number of LEDs per chan
#define TX_BUFF_LEN(n) (LED_TX_OSET(n) + LED_POSTBITS)
#define TX_BUFF_SIZE(n) (TX_BUFF_LEN(n) * sizeof(TXDATA_T))
#define PAGE_SIZE 0x1000
#define VC_MEM_SIZE (PAGE_SIZE + TX_BUFF_SIZE(CHAN_MAXLEDS))
extern TXDATA_T tx_buffer[TX_BUFF_LEN(CHAN_MAXLEDS)];
// extern TXDATA_T tx_buffer[TX_BUFF_LEN(CHAN_MAXLEDS)];
extern TXDATA_T tx_send_buffer[TX_BUFF_LEN(CHAN_MAXLEDS)];
extern TXDATA_T *txdata;
extern MEM_MAP vc_mem;
@@ -0,0 +1,4 @@
pub const LED_NCHANS: usize = 16_usize; // Number of channels (strips)
pub const CHAN_NUM_MASK: u8 = 0b1111_u8; // Mask to get channel number from port address
pub type TX_DATA_T = u16; // Type of Tx data buffer
@@ -0,0 +1,4 @@
pub const LED_NCHANS: usize = 8_usize; // Number of channels (strips)
pub const CHAN_NUM_MASK: u8 = 0b0111_u8; // Mask to get channel number from port address
pub type TX_DATA_T = u8; // Type of Tx data buffer
+187 -34
View File
@@ -5,34 +5,110 @@
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
pub struct LedDriver {
led_per_strip_count: usize,
strip_count: usize,
}
#[cfg_attr(not(feature = "16channel"), path = "channel_8.rs")]
#[cfg_attr(feature = "16channel", path = "channel_16.rs")]
pub mod channel;
pub use channel::*;
#[cfg(feature = "rpi")]
/// Location of peripheral registers in physical memory for RaspberryPi 1 and Zero
/// This is the base address for the BCM2835 SoC peripherals
const PHYS_REG_BASE: usize = 0x20000000;
#[cfg(any(feature = "rpi2", feature = "rpi3"))]
/// Location of peripheral registers in physical memory for RaspberryPi 2, 3 and Zero2
/// This is the base address for the BCM2836/BCM2837 SoC peripherals
const PHYS_REG_BASE: usize = 0x3F000000;
#[cfg(any(feature = "rpi4", feature = "rpi5"))]
// Location of peripheral registers in physical memory for RaspberryPi 4 and 5
/// This is the base address for the BCM2711/BCM2712 SoC peripherals
const PHYS_REG_BASE: usize = 0xFE000000;
#[cfg(feature = "rpi")]
/// Clock frequency for RaspberryPi 1 and Zero
const CLOCK_SPEED: usize = 400000000;
#[cfg(any(feature = "rpi2", feature = "rpi3", feature = "rpi4", feature = "rpi5"))]
/// Clock frequency for RaspberryPi 2, 3, Zero2 and 4 and 5
const CLOCK_SPEED: usize = 250000000;
#[cfg(not(any(feature = "rpi4", feature = "rpi5")))] // Timings for RPi v0-3 (1 GHz)
const SMI_TIMING: [std::os::raw::c_int; 4] = [10, 10, 20, 10]; // 400 ns cycle time
#[cfg(any(feature = "rpi4", feature = "rpi5"))] // Timings for RPi v4 (1.5 GHz)
const SMI_TIMING: [std::os::raw::c_int; 4] = [10, 15, 30, 15]; // 400 ns cycle time
// Offset into Tx data buffer, given LED number in chan
macro_rules! LED_TX_OSET {
macro_rules! LED_TX_OFFSET {
($n:expr) => {
LED_PREBITS + (LED_DLEN * $n)
LedDriver::LED_PREBITS + (LedDriver::LED_DLEN * $n)
};
}
macro_rules! TX_BUFF_LEN {
($n:expr) => {
LED_TX_OFFSET!($n) + LedDriver::LED_POSTBITS
};
}
macro_rules! TX_BUFF_SIZE {
($n:expr) => {
TX_BUFF_LEN!($n) * std::mem::size_of::<TX_DATA_T>()
};
}
pub struct LedDriver {
led_per_strip_count: usize,
strip_count: usize,
tx_buffer: [TX_DATA_T; TX_BUFF_LEN!(Self::CHAN_MAXLEDS)],
}
impl Drop for LedDriver {
fn drop(&mut self) {
self.close();
}
}
impl LedDriver {
const LED_NCHANS: usize = LED_NCHANS as usize; // Number of channels (strips)
const TX_TEST: usize = TX_TEST as usize; // If non-zero, use dummy Tx data
const LED_NBITS: usize = LED_NBITS as usize; // Number of data bits per LED
pub const LED_PREBITS: usize = LED_PREBITS as usize; // Number of zero bits before LED data
const LED_POSTBITS: usize = LED_POSTBITS as usize; // Number of zero bits after LED data
const BIT_NPULSES: usize = BIT_NPULSES as usize; // Number of O/P pulses per LED bit
pub const CHAN_MAXLEDS: usize = 60; // Maximum number of LEDs per channel
const TX_TEST: usize = 0; // If non-zero, use dummy Tx data
const LED_NBITS: usize = 24; // Number of data bits per LED
pub const LED_PREBITS: usize = 4; // Number of zero bits before LED data
pub const LED_POSTBITS: usize = 4; // Number of zero bits after LED data
const BIT_NPULSES: usize = 3; // Number of O/P pulses per LED bit
pub const PAGE_SIZE: usize = 0x1000;
pub const VC_MEM_SIZE: usize = Self::PAGE_SIZE + TX_BUFF_SIZE!(Self::CHAN_MAXLEDS);
// Length of data for 1 row (1 LED on each channel)
pub const LED_DLEN: usize = Self::LED_NBITS * Self::BIT_NPULSES;
pub fn new(led_per_strip_count: usize, strip_count: usize) -> LedDriver {
unsafe { leddriver_setup() };
unsafe {
videocore_setup(&raw mut vc_mem, Self::VC_MEM_SIZE as i32);
gpio_setup();
smi_setup(
channel::LED_NCHANS as i32,
SMI_TIMING[0],
SMI_TIMING[1],
SMI_TIMING[2],
SMI_TIMING[3],
&raw mut vc_mem,
TX_BUFF_LEN!(Self::CHAN_MAXLEDS) as i32,
&raw mut txdata,
);
}
LedDriver {
led_per_strip_count,
strip_count,
tx_buffer: [0; TX_BUFF_LEN!(Self::CHAN_MAXLEDS)],
}
}
pub fn close(&mut self) {
unsafe {
videocore_close(&raw mut vc_mem);
smi_close(channel::LED_NCHANS as i32);
gpio_close();
}
}
@@ -44,11 +120,7 @@ impl LedDriver {
self.strip_count
}
pub fn set_color(&self, rgb: u32, index: usize) {
unsafe { set_color(rgb, index as i32) };
}
pub fn set_color_till_led(&self, color: u32, led_num: usize) {
pub fn set_color_till_led(&mut self, color: u32, led_num: usize) {
let led_num = led_num.min(self.get_led_per_strip_count());
// Set color for all strips
for led_index in 0..=led_num {
@@ -59,13 +131,15 @@ impl LedDriver {
}
}
pub fn direct_set_color(&self, rgb: u32, index: usize) {
pub fn direct_set_color(&mut self, rgb: u32, index: usize) {
let mut mask = 0_u32;
let mut offset = LED_TX_OSET!(index as u32) as usize;
let mut offset = LED_TX_OFFSET!(index);
// For each bit of the 24-bit RGB values..
for n in 0..Self::LED_NBITS {
let tx_data = unsafe { &mut tx_buffer[offset..offset + Self::BIT_NPULSES] };
let tx_data = &mut self.tx_buffer[offset..offset + Self::BIT_NPULSES];
// let tx_data =
// unsafe { std::slice::from_raw_parts_mut(tx_buffer.add(offset), Self::BIT_NPULSES) };
// Mask to convert RGB to GRB, M.S bit first
Self::compute_mask(&mut mask, n);
@@ -79,14 +153,17 @@ impl LedDriver {
}
}
pub fn set_led_color(&self, rgb: u32, led_index: usize, strip_index: usize) {
pub fn set_led_color(&mut self, rgb: u32, led_index: usize, strip_index: usize) {
let mut mask = 0_u32;
let mut offset = LED_TX_OSET!(led_index as u32) as usize;
let strip_mask = !(0b1_u8 << strip_index); // Mask to clear the led bit of desired strip
let mut offset = LED_TX_OFFSET!(led_index);
let strip_mask = !(0b1 << strip_index); // Mask to clear the led bit of desired strip
// For each bit of the 24-bit RGB values..
for n in 0..Self::LED_NBITS {
let tx_data = unsafe { &mut tx_buffer[offset..offset + Self::BIT_NPULSES] };
// let tx_data = unsafe { &mut *tx_buffer[offset..offset + Self::BIT_NPULSES] };
// let tx_data =
// unsafe { std::slice::from_raw_parts_mut(tx_buffer.add(offset), Self::BIT_NPULSES) };
let tx_data = &mut self.tx_buffer[offset..offset + Self::BIT_NPULSES];
// Mask to convert RGB to GRB, M.S bit first
Self::compute_mask(&mut mask, n);
@@ -105,13 +182,17 @@ impl LedDriver {
}
}
pub fn set_strip_index_colors(&self, rgb: [u32; Self::LED_NCHANS], led_index: usize) {
pub fn set_strip_index_colors(&mut self, rgb: [u32; channel::LED_NCHANS], led_index: usize) {
let mut mask = 0_u32;
let mut offset = LED_TX_OSET!(led_index as u32) as usize;
let mut offset = LED_TX_OFFSET!(led_index);
// For each bit of the 24-bit RGB values..
for n in 0..Self::LED_NBITS {
let tx_data = unsafe { &mut tx_buffer[offset..offset + Self::BIT_NPULSES] };
// let tx_data = unsafe { &mut tx_buffer[offset..offset + Self::BIT_NPULSES] };
// let tx_data =
// unsafe { std::slice::from_raw_parts_mut(tx_buffer.add(offset), Self::BIT_NPULSES) };
let tx_data = &mut self.tx_buffer[offset..offset + Self::BIT_NPULSES];
// Mask to convert RGB to GRB, M.S bit first
Self::compute_mask(&mut mask, n);
@@ -131,8 +212,32 @@ impl LedDriver {
}
}
pub fn refresh(&self) {
unsafe { leddriver_refresh() };
// pub fn refresh(&self) {
// unsafe { leddriver_refresh() };
// }
pub fn direct_refresh(&mut self) {
// #[cfg(not(feature = "16channel"))]
// self.swap_bytes();
#[cfg(not(feature = "16channel"))]
let send_buffer = self.safe_swap_bytes();
#[cfg(feature = "16channel")]
let send_buffer = self.tx_buffer;
unsafe {
while dma_active(DMA_CHAN_A as i32) != 0 {
std::thread::sleep(std::time::Duration::from_micros(10));
}
#[allow(static_mut_refs)]
std::ptr::copy_nonoverlapping(
send_buffer.as_ptr(),
txdata,
TX_BUFF_LEN!(Self::CHAN_MAXLEDS),
);
enable_dma(DMA_CHAN_A as i32);
start_smi(&raw mut vc_mem);
std::thread::sleep(std::time::Duration::from_micros(10));
}
}
// pub fn color_hsv(&self, hue: u16, sat: u8, val: u8) -> u32 {
@@ -150,10 +255,58 @@ impl LedDriver {
*mask >> 1
}
}
}
impl Drop for LedDriver {
fn drop(&mut self) {
unsafe { leddriver_close() };
#[cfg(not(feature = "16channel"))]
fn safe_swap_bytes(&self) -> [TX_DATA_T; TX_BUFF_SIZE!(Self::CHAN_MAXLEDS)] {
self.tx_buffer
.chunks(2)
.map(|chunk| {
if chunk.len() == 2 {
[chunk[1], chunk[0]]
} else {
[0, chunk[0]]
}
})
.collect::<Vec<_>>()
.try_into()
.unwrap_or_else(|o: Vec<_>| {
panic!(
"Failed to convert Vec of length {} to array of size {}",
o.len(),
TX_BUFF_SIZE!(Self::CHAN_MAXLEDS)
);
})
}
#[cfg(not(feature = "16channel"))]
fn swap_bytes(&mut self) {
/*
uint16_t *wp = (uint16_t *)tx_buffer;
int len = TX_BUFF_SIZE(CHAN_MAXLEDS);
len = (len + 1) / 2;
while (len-- > 0) {
*wp = __builtin_bswap16(*wp);
wp++;
}
*/
unsafe {
for (i, chunk) in self.tx_buffer.chunks(2).enumerate() {
if chunk.len() == 2 {
tx_send_buffer[i * 2] = chunk[1];
// *tx_send_buffer.add(i * 2) = chunk[1];
tx_send_buffer[i * 2 + 1] = chunk[0];
// *tx_send_buffer.add(i * 2 + 1) = chunk[0];
}
}
}
// unsafe {
// let wp = self.tx_buffer as *mut u16;
// let len = (TX_BUFF_SIZE!(Self::CHAN_MAXLEDS) + 1) / 2;
// for i in 0..len {
// let p = wp.add(i);
// *p = (*p).swap_bytes();
// }
// }
}
}
-13
View File
@@ -16,16 +16,3 @@ rppal = "0.22.1"
spectrum-analyzer = "1.7.0"
textplots = "0.8.7"
led_driver = { path = "../led_driver" }
[build-dependencies]
bindgen = "0.71.0"
[features]
default = ["rpizero2", "16channel"]
rpizero2 = ["rpi3"]
rpizero = ["rpi"]
rpi4 = []
rpi3 = []
rpi2 = []
rpi = []
16channel = []
-1
View File
@@ -1 +0,0 @@
@@ -110,8 +110,10 @@ impl AppModeHandler for ArtNetMode {
is_first_data_frame = false;
}
let led_strip = (u16::from(output.port_address) & 0b0111_u16) as usize;
//output.port_address
let led_strip = (u16::from(output.port_address)
& led_driver::CHAN_NUM_MASK as u16)
as usize;
for i in 0..led_driver.get_led_per_strip_count() {
let data_index = i * 3;
let g = *output.data.as_ref().get(data_index).unwrap_or(&0);
@@ -120,9 +122,9 @@ impl AppModeHandler for ArtNetMode {
// let color = (r as u32) << 16 + (g as u32) << 8 + b;
let color: u32 =
((r as u32) << 16) | ((g as u32) << 8) | (b as u32);
led_driver.direct_set_color(color, i);
led_driver.set_led_color(color, i, led_strip);
}
led_driver.refresh();
led_driver.direct_refresh();
}
ArtCommand::PollReply(_) => {
log::trace!("[ArtNet] Received PollReply command, ignoring");
@@ -76,7 +76,7 @@ impl AppModeHandler for DiagnosticsMode {
}
};
log::trace!("Setting LED {} to color {:06x}", led, color);
led_driver.refresh();
led_driver.direct_refresh();
}
self.cycle_count += 1;
Ok(())
@@ -146,7 +146,7 @@ impl AppModeHandler for StandaloneMode {
// Set the remaining LEDs to black
led_driver.direct_set_color(0x000000, i);
}
led_driver.refresh();
led_driver.direct_refresh();
}
None => {
log::trace!("[Standalone] No audio data received");
+4 -2
View File
@@ -1,5 +1,4 @@
mod channels;
mod config;
mod cputasks;
mod devices;
mod iotasks;
@@ -107,7 +106,10 @@ impl From<LightSabre> for LightSabreIntitialized {
impl From<LightSabreIntitialized> for LightSabreRunning {
fn from(mut value: LightSabreIntitialized) -> Self {
LightSabreRunning {
join_handle: std::thread::spawn(move || run(&mut value.ctx)),
join_handle: std::thread::Builder::new()
.name("main_app".to_string())
.spawn(move || run(&mut value.ctx))
.expect("Failed to start main app thread"),
}
}
}