如何控制 4 个链接在一起并由 Max7219 供电的 8x8 LED 矩阵?
How to control 4 8x8 led matrices that are chained together and are powered with the Max7219?
我想使用 Raspberrypi 控制矩阵的每个像素,但我不知道我必须发送什么数据以及如何发送。
我已经查看了 SPI 接口,但是在阅读了这个 article 之后他没有使用它并因此手动完成它,我不确定该怎么做。
它也很难调试,因为我无法检查何时发送什么数据
我还检查了各种 python 库,但我无法获取它们 运行,即使在安装了每个依赖项之后
但这是不必要的,因为我不想使用 python.
我从来没有做过这样的事情,所以我很没有经验。
我像这样连接起来 guide 并用 Rust 编写了一些代码以通过 SPI 接口发送数据:
use std::io;
use std::io::prelude::*;
use spidev::{Spidev, SpidevOptions, SpiModeFlags};
fn create_spi() -> io::Result<Spidev> {
let mut spi = Spidev::open("/dev/spidev0.0")?;
let options = SpidevOptions::new()
.bits_per_word(8)
.max_speed_hz(20_000)
.mode(SpiModeFlags::SPI_MODE_0)
.build();
spi.configure(&options)?;
Ok(spi)
}
fn main() {
let mut spi = create_spi().unwrap();
spi.write(&[0x01, 0x02, 0x03]).unwrap();
}
在 Pi 上 运行 之后,它似乎成功地发送了数据,没有出现恐慌,但我无法检查它是否真的有效。
矩阵上没有按预期显示任何内容。
有人可以向我解释如何配置 SPI 连接以及我必须发送哪些数据来初始化矩阵然后在上面显示一些东西吗?
更新
在查看 this driver 之后,我现在能够在第一行的第一个和第四个矩阵上显示 0 到 255 之间的值。
这是代码:
use std::io;
use std::io::prelude::*;
use spidev::{Spidev, SpidevOptions, SpiModeFlags};
use std::thread::sleep;
use std::time::Duration;
// Possible command register values on the display chip.
#[derive(Clone, Copy)]
pub enum Command {
Noop = 0x00,
Digit0 = 0x01,
Digit1 = 0x02,
Digit2 = 0x03,
Digit3 = 0x04,
Digit4 = 0x05,
Digit5 = 0x06,
Digit6 = 0x07,
Digit7 = 0x08,
DecodeMode = 0x09,
Intensity = 0x0A,
ScanLimit = 0x0B,
Power = 0x0C,
DisplayTest = 0x0F,
}
// Decode modes for BCD encoded input.
#[derive(Copy, Clone)]
pub enum DecodeMode {
NoDecode = 0x00,
CodeBDigit0 = 0x01,
CodeBDigits3_0 = 0x0F,
CodeBDigits7_0 = 0xFF,
}
fn create_spi() -> io::Result<Spidev> {
let mut spi = Spidev::open("/dev/spidev0.0")?;
let options = SpidevOptions::new()
.bits_per_word(8)
.max_speed_hz(20_000)
.mode(SpiModeFlags::SPI_MODE_0)
.build();
spi.configure(&options)?;
Ok(spi)
}
fn main() {
let mut spi = create_spi().unwrap();
// turns on 4 displays (address, command, on)
for display in 0..4_u8 {
spi.write(&[display, 0x0c, 0x01]).unwrap();
}
// sets decode mode (adress, mode) to 0x00
spi.write(&[0x00, 0x09, 0x00]).unwrap();
// writes data (address, digit, data)
for data in 0..255 {
spi.write(&[0x00, 0x01, data]).unwrap();
sleep(Duration::from_millis(50));
}
}
但我不知道如何访问其他行或单个矩阵。
更新 2
我现在清理了代码,发现将扫描限制设置为 7 可以让您将数据绘制到所有 8 行
这是代码:
#![allow(dead_code)]
use spidev::Spidev;
use std::io::prelude::*;
// Maximum number of addrs connected in series supported
const MAX_DISPLAYS: usize = 8;
// Digits per addr
const MAX_DIGITS: usize = 8;
// Possible command register values on the addr chip.
#[derive(Clone, Copy)]
pub enum Command {
Noop = 0x00,
Digit0 = 0x01,
Digit1 = 0x02,
Digit2 = 0x03,
Digit3 = 0x04,
Digit4 = 0x05,
Digit5 = 0x06,
Digit6 = 0x07,
Digit7 = 0x08,
DecodeMode = 0x09,
Intensity = 0x0A,
ScanLimit = 0x0B,
Power = 0x0C,
DisplayTest = 0x0F,
}
// Decode modes for BCD encoded input.
#[derive(Copy, Clone)]
pub enum DecodeMode {
NoDecode = 0x00,
CodeBDigit0 = 0x01,
CodeBDigits3_0 = 0x0F,
CodeBDigits7_0 = 0xFF,
}
pub struct Matrix {
pub spi: Spidev,
pub devices: u8,
pub decode_mode: DecodeMode
}
impl Matrix {
pub fn power_on(&mut self) {
for addr in 0..self.devices {
self.spi.write(&[addr, Command::Power as u8, 0x01]).unwrap();
}
}
pub fn power_off(&mut self) {
for addr in 0..self.devices {
self.spi.write(&[addr, Command::Power as u8, 0x00]).unwrap();
}
}
pub fn set_decode_mode(&mut self, addr: u8, mode: DecodeMode) {
// sets decode mode (adress, command, mode)
self.spi.write(&[addr, Command::DecodeMode as u8, mode as u8]).unwrap();
}
pub fn send_command(&mut self, addr: u8, command: Command, value: u8) {
self.spi.write(&[addr, command as u8, value]).unwrap();
}
pub fn draw_raw(&mut self, addr: u8, data: &[u8; MAX_DIGITS]) {
let mut digit: u8 = 1;
for b in data {
// addr ..
self.spi.write(&[addr, digit, *b]).unwrap();
digit += 1;
}
}
pub fn clear_addr(&mut self, addr: u8) {
for i in 1..9 {
self.spi.write(&[addr, i, 0x00]).unwrap();
}
}
pub fn set_intensity(&mut self, addr: u8, intesity: u8) {
self.spi.write(&[addr, Command::Intensity as u8, intesity]).unwrap();
}
}
let spi = create_spi().unwrap();
let mut matrix = Matrix{
spi,
devices: 4, // 4 displays chained together, not working rn
decode_mode: DecodeMode::NoDecode
};
matrix.power_on();
matrix.send_command(0x00, Command::ScanLimit, 0x07);
matrix.draw_raw(0x00, &[
0b1001001,
0b0110110,
0b1001001,
0b0110110,
0b1001001,
0b0110110,
0b1001001,
0b0110110
]);
sleep(Duration::from_secs(1));
matrix.power_off();
但是显示在第一个和第四个显示器上,我仍然无法单独控制它们。
因为我部分解决了我的问题,所以我将其标记为已解决。
我打开了一个关于如何控制链接在一起的多个矩阵的 updated question。
我想使用 Raspberrypi 控制矩阵的每个像素,但我不知道我必须发送什么数据以及如何发送。
我已经查看了 SPI 接口,但是在阅读了这个 article 之后他没有使用它并因此手动完成它,我不确定该怎么做。
它也很难调试,因为我无法检查何时发送什么数据
我还检查了各种 python 库,但我无法获取它们 运行,即使在安装了每个依赖项之后 但这是不必要的,因为我不想使用 python.
我从来没有做过这样的事情,所以我很没有经验。
我像这样连接起来 guide 并用 Rust 编写了一些代码以通过 SPI 接口发送数据:
use std::io;
use std::io::prelude::*;
use spidev::{Spidev, SpidevOptions, SpiModeFlags};
fn create_spi() -> io::Result<Spidev> {
let mut spi = Spidev::open("/dev/spidev0.0")?;
let options = SpidevOptions::new()
.bits_per_word(8)
.max_speed_hz(20_000)
.mode(SpiModeFlags::SPI_MODE_0)
.build();
spi.configure(&options)?;
Ok(spi)
}
fn main() {
let mut spi = create_spi().unwrap();
spi.write(&[0x01, 0x02, 0x03]).unwrap();
}
在 Pi 上 运行 之后,它似乎成功地发送了数据,没有出现恐慌,但我无法检查它是否真的有效。
矩阵上没有按预期显示任何内容。
有人可以向我解释如何配置 SPI 连接以及我必须发送哪些数据来初始化矩阵然后在上面显示一些东西吗?
更新
在查看 this driver 之后,我现在能够在第一行的第一个和第四个矩阵上显示 0 到 255 之间的值。 这是代码:
use std::io;
use std::io::prelude::*;
use spidev::{Spidev, SpidevOptions, SpiModeFlags};
use std::thread::sleep;
use std::time::Duration;
// Possible command register values on the display chip.
#[derive(Clone, Copy)]
pub enum Command {
Noop = 0x00,
Digit0 = 0x01,
Digit1 = 0x02,
Digit2 = 0x03,
Digit3 = 0x04,
Digit4 = 0x05,
Digit5 = 0x06,
Digit6 = 0x07,
Digit7 = 0x08,
DecodeMode = 0x09,
Intensity = 0x0A,
ScanLimit = 0x0B,
Power = 0x0C,
DisplayTest = 0x0F,
}
// Decode modes for BCD encoded input.
#[derive(Copy, Clone)]
pub enum DecodeMode {
NoDecode = 0x00,
CodeBDigit0 = 0x01,
CodeBDigits3_0 = 0x0F,
CodeBDigits7_0 = 0xFF,
}
fn create_spi() -> io::Result<Spidev> {
let mut spi = Spidev::open("/dev/spidev0.0")?;
let options = SpidevOptions::new()
.bits_per_word(8)
.max_speed_hz(20_000)
.mode(SpiModeFlags::SPI_MODE_0)
.build();
spi.configure(&options)?;
Ok(spi)
}
fn main() {
let mut spi = create_spi().unwrap();
// turns on 4 displays (address, command, on)
for display in 0..4_u8 {
spi.write(&[display, 0x0c, 0x01]).unwrap();
}
// sets decode mode (adress, mode) to 0x00
spi.write(&[0x00, 0x09, 0x00]).unwrap();
// writes data (address, digit, data)
for data in 0..255 {
spi.write(&[0x00, 0x01, data]).unwrap();
sleep(Duration::from_millis(50));
}
}
但我不知道如何访问其他行或单个矩阵。
更新 2
我现在清理了代码,发现将扫描限制设置为 7 可以让您将数据绘制到所有 8 行 这是代码:
#![allow(dead_code)]
use spidev::Spidev;
use std::io::prelude::*;
// Maximum number of addrs connected in series supported
const MAX_DISPLAYS: usize = 8;
// Digits per addr
const MAX_DIGITS: usize = 8;
// Possible command register values on the addr chip.
#[derive(Clone, Copy)]
pub enum Command {
Noop = 0x00,
Digit0 = 0x01,
Digit1 = 0x02,
Digit2 = 0x03,
Digit3 = 0x04,
Digit4 = 0x05,
Digit5 = 0x06,
Digit6 = 0x07,
Digit7 = 0x08,
DecodeMode = 0x09,
Intensity = 0x0A,
ScanLimit = 0x0B,
Power = 0x0C,
DisplayTest = 0x0F,
}
// Decode modes for BCD encoded input.
#[derive(Copy, Clone)]
pub enum DecodeMode {
NoDecode = 0x00,
CodeBDigit0 = 0x01,
CodeBDigits3_0 = 0x0F,
CodeBDigits7_0 = 0xFF,
}
pub struct Matrix {
pub spi: Spidev,
pub devices: u8,
pub decode_mode: DecodeMode
}
impl Matrix {
pub fn power_on(&mut self) {
for addr in 0..self.devices {
self.spi.write(&[addr, Command::Power as u8, 0x01]).unwrap();
}
}
pub fn power_off(&mut self) {
for addr in 0..self.devices {
self.spi.write(&[addr, Command::Power as u8, 0x00]).unwrap();
}
}
pub fn set_decode_mode(&mut self, addr: u8, mode: DecodeMode) {
// sets decode mode (adress, command, mode)
self.spi.write(&[addr, Command::DecodeMode as u8, mode as u8]).unwrap();
}
pub fn send_command(&mut self, addr: u8, command: Command, value: u8) {
self.spi.write(&[addr, command as u8, value]).unwrap();
}
pub fn draw_raw(&mut self, addr: u8, data: &[u8; MAX_DIGITS]) {
let mut digit: u8 = 1;
for b in data {
// addr ..
self.spi.write(&[addr, digit, *b]).unwrap();
digit += 1;
}
}
pub fn clear_addr(&mut self, addr: u8) {
for i in 1..9 {
self.spi.write(&[addr, i, 0x00]).unwrap();
}
}
pub fn set_intensity(&mut self, addr: u8, intesity: u8) {
self.spi.write(&[addr, Command::Intensity as u8, intesity]).unwrap();
}
}
let spi = create_spi().unwrap();
let mut matrix = Matrix{
spi,
devices: 4, // 4 displays chained together, not working rn
decode_mode: DecodeMode::NoDecode
};
matrix.power_on();
matrix.send_command(0x00, Command::ScanLimit, 0x07);
matrix.draw_raw(0x00, &[
0b1001001,
0b0110110,
0b1001001,
0b0110110,
0b1001001,
0b0110110,
0b1001001,
0b0110110
]);
sleep(Duration::from_secs(1));
matrix.power_off();
但是显示在第一个和第四个显示器上,我仍然无法单独控制它们。
因为我部分解决了我的问题,所以我将其标记为已解决。
我打开了一个关于如何控制链接在一起的多个矩阵的 updated question。