1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
#![no_std]
pub mod devices;
use embedded_hal::blocking::delay::DelayMs;
use embedded_hal::blocking::i2c::Write;
pub struct IS31FL3731<'a, I2C, DEL> {
pub i2c: I2C,
pub delay: &'a mut DEL,
pub address: u8,
pub frame: u8,
pub width: u8,
pub height: u8,
pub calc_pixel: fn(x: u8, y: u8) -> u8,
}
impl<I2C, DEL, I2cError> IS31FL3731<'_, I2C, DEL>
where
I2C: Write<Error = I2cError>,
DEL: DelayMs<u8>,
{
pub fn fill(&mut self, brightness: u8, blink: Option<bool>, frame: u8) -> Result<(), I2cError> {
self.bank(frame)?;
let mut payload = [brightness; 25];
for row in 0..6 {
payload[0] = addresses::COLOR_OFFSET + row * 24;
self.i2c.write(self.address, &payload)?;
}
if blink.is_some() {
let data = if blink.unwrap() { 1 } else { 0 } * 0xFF;
for col in 0..18 {
self.write_register(frame, addresses::BLINK_OFFSET + col, data)?;
}
}
Ok(())
}
pub fn setup(&mut self) -> Result<(), I2cError> {
self.sleep(true)?;
self.delay.delay_ms(10);
self.mode(addresses::PICTURE_MODE)?;
self.frame(0)?;
for frame in 0..8 {
self.fill(0, Some(false), frame)?;
for col in 0..18 {
self.write_register(frame, addresses::ENABLE_OFFSET + col, 0xFF)?;
}
}
self.audio_sync(false)?;
self.sleep(false)?;
Ok(())
}
pub fn pixel(&mut self, x: u8, y: u8, brightness: u8) -> Result<(), Error<I2cError>> {
if x > self.width {
return Err(Error::InvalidLocation(x));
}
if y > self.height {
return Err(Error::InvalidLocation(y));
}
let pixel = (self.calc_pixel)(x, y);
self.write_register(self.frame, addresses::COLOR_OFFSET + pixel, brightness)?;
Ok(())
}
pub fn set_address(&mut self, address: u8) {
self.address = address;
}
pub fn frame(&mut self, frame: u8) -> Result<(), I2cError> {
self.frame = frame;
self.write_register(addresses::CONFIG_BANK, addresses::FRAME, frame)?;
Ok(())
}
pub fn reset(&mut self) -> Result<(), I2cError> {
self.sleep(true)?;
self.delay.delay_ms(10);
self.sleep(false)?;
Ok(())
}
fn write_register(&mut self, bank: u8, register: u8, value: u8) -> Result<(), I2cError> {
self.bank(bank)?;
self.i2c.write(self.address, &[register, value])?;
Ok(())
}
fn bank(&mut self, bank: u8) -> Result<(), I2cError> {
self.i2c
.write(self.address, &[addresses::BANK_ADDRESS, bank])?;
Ok(())
}
fn mode(&mut self, mode: u8) -> Result<(), I2cError> {
self.write_register(addresses::CONFIG_BANK, addresses::MODE_REGISTER, mode)?;
Ok(())
}
fn audio_sync(&mut self, yes: bool) -> Result<(), I2cError> {
self.write_register(
addresses::CONFIG_BANK,
addresses::AUDIOSYNC,
if yes { 1 } else { 0 },
)?;
Ok(())
}
fn sleep(&mut self, yes: bool) -> Result<(), I2cError> {
self.write_register(
addresses::CONFIG_BANK,
addresses::SHUTDOWN,
if yes { 0 } else { 1 },
)?;
Ok(())
}
}
mod addresses {
#![allow(dead_code)]
pub const MODE_REGISTER: u8 = 0x00;
pub const FRAME: u8 = 0x01;
pub const AUTOPLAY1: u8 = 0x02;
pub const AUTOPLAY2: u8 = 0x03;
pub const BLINK: u8 = 0x05;
pub const AUDIOSYNC: u8 = 0x06;
pub const BREATH1: u8 = 0x08;
pub const BREATH2: u8 = 0x09;
pub const SHUTDOWN: u8 = 0x0A;
pub const GAIN: u8 = 0x0B;
pub const ADC: u8 = 0x0C;
pub const CONFIG_BANK: u8 = 0x0B;
pub const BANK_ADDRESS: u8 = 0xFD;
pub const PICTURE_MODE: u8 = 0x00;
pub const AUTOPLAY_MODE: u8 = 0x08;
pub const AUDIOPLAY_MODE: u8 = 0x18;
pub const ENABLE_OFFSET: u8 = 0x00;
pub const BLINK_OFFSET: u8 = 0x12;
pub const COLOR_OFFSET: u8 = 0x24;
}
#[derive(Clone, Copy, Debug)]
pub enum Error<I2cError> {
I2cError(I2cError),
InvalidLocation(u8),
}
impl<E> From<E> for Error<E> {
fn from(error: E) -> Self {
Error::I2cError(error)
}
}