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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
|
//! [ws2812](https://www.sparkfun.com/datasheets/LCD/HD44780.pdf)
use embassy_time::Timer;
use fixed::types::U24F8;
use smart_leds::{RGB8, RGBW};
use crate::clocks::clk_sys_freq;
use crate::dma::{AnyChannel, Channel};
use crate::pio::{
Common, Config, FifoJoin, Instance, LoadedProgram, PioPin, ShiftConfig, ShiftDirection, StateMachine,
};
use crate::Peri;
const T1: u8 = 2; // start bit
const T2: u8 = 5; // data bit
const T3: u8 = 3; // stop bit
const CYCLES_PER_BIT: u32 = (T1 + T2 + T3) as u32;
/// This struct represents a ws2812 program loaded into pio instruction memory.
pub struct PioWs2812Program<'a, PIO: Instance> {
prg: LoadedProgram<'a, PIO>,
}
impl<'a, PIO: Instance> PioWs2812Program<'a, PIO> {
/// Load the ws2812 program into the given pio
pub fn new(common: &mut Common<'a, PIO>) -> Self {
let side_set = pio::SideSet::new(false, 1, false);
let mut a: pio::Assembler<32> = pio::Assembler::new_with_side_set(side_set);
let mut wrap_target = a.label();
let mut wrap_source = a.label();
let mut do_zero = a.label();
a.set_with_side_set(pio::SetDestination::PINDIRS, 1, 0);
a.bind(&mut wrap_target);
// Do stop bit
a.out_with_delay_and_side_set(pio::OutDestination::X, 1, T3 - 1, 0);
// Do start bit
a.jmp_with_delay_and_side_set(pio::JmpCondition::XIsZero, &mut do_zero, T1 - 1, 1);
// Do data bit = 1
a.jmp_with_delay_and_side_set(pio::JmpCondition::Always, &mut wrap_target, T2 - 1, 1);
a.bind(&mut do_zero);
// Do data bit = 0
a.nop_with_delay_and_side_set(T2 - 1, 0);
a.bind(&mut wrap_source);
let prg = a.assemble_with_wrap(wrap_source, wrap_target);
let prg = common.load_program(&prg);
Self { prg }
}
}
/// Pio backed RGB ws2812 driver
/// Const N is the number of ws2812 leds attached to this pin
pub struct PioWs2812<'d, P: Instance, const S: usize, const N: usize> {
dma: Peri<'d, AnyChannel>,
sm: StateMachine<'d, P, S>,
}
impl<'d, P: Instance, const S: usize, const N: usize> PioWs2812<'d, P, S, N> {
/// Configure a pio state machine to use the loaded ws2812 program.
pub fn new(
pio: &mut Common<'d, P>,
mut sm: StateMachine<'d, P, S>,
dma: Peri<'d, impl Channel>,
pin: Peri<'d, impl PioPin>,
program: &PioWs2812Program<'d, P>,
) -> Self {
// Setup sm0
let mut cfg = Config::default();
// Pin config
let out_pin = pio.make_pio_pin(pin);
cfg.set_out_pins(&[&out_pin]);
cfg.set_set_pins(&[&out_pin]);
cfg.use_program(&program.prg, &[&out_pin]);
// Clock config, measured in kHz to avoid overflows
let clock_freq = U24F8::from_num(clk_sys_freq() / 1000);
let ws2812_freq = U24F8::from_num(800);
let bit_freq = ws2812_freq * CYCLES_PER_BIT;
cfg.clock_divider = clock_freq / bit_freq;
// FIFO config
cfg.fifo_join = FifoJoin::TxOnly;
cfg.shift_out = ShiftConfig {
auto_fill: true,
threshold: 24,
direction: ShiftDirection::Left,
};
sm.set_config(&cfg);
sm.set_enable(true);
Self { dma: dma.into(), sm }
}
/// Write a buffer of [smart_leds::RGB8] to the ws2812 string
pub async fn write(&mut self, colors: &[RGB8; N]) {
// Precompute the word bytes from the colors
let mut words = [0u32; N];
for i in 0..N {
let word = (u32::from(colors[i].g) << 24) | (u32::from(colors[i].r) << 16) | (u32::from(colors[i].b) << 8);
words[i] = word;
}
// DMA transfer
self.sm.tx().dma_push(self.dma.reborrow(), &words, false).await;
Timer::after_micros(55).await;
}
}
/// Pio backed RGBW ws2812 driver
/// This version is intended for ws2812 leds with 4 addressable lights
/// Const N is the number of ws2812 leds attached to this pin
pub struct RgbwPioWs2812<'d, P: Instance, const S: usize, const N: usize> {
dma: Peri<'d, AnyChannel>,
sm: StateMachine<'d, P, S>,
}
impl<'d, P: Instance, const S: usize, const N: usize> RgbwPioWs2812<'d, P, S, N> {
/// Configure a pio state machine to use the loaded ws2812 program.
pub fn new(
pio: &mut Common<'d, P>,
mut sm: StateMachine<'d, P, S>,
dma: Peri<'d, impl Channel>,
pin: Peri<'d, impl PioPin>,
program: &PioWs2812Program<'d, P>,
) -> Self {
// Setup sm0
let mut cfg = Config::default();
// Pin config
let out_pin = pio.make_pio_pin(pin);
cfg.set_out_pins(&[&out_pin]);
cfg.set_set_pins(&[&out_pin]);
cfg.use_program(&program.prg, &[&out_pin]);
// Clock config, measured in kHz to avoid overflows
let clock_freq = U24F8::from_num(clk_sys_freq() / 1000);
let ws2812_freq = U24F8::from_num(800);
let bit_freq = ws2812_freq * CYCLES_PER_BIT;
cfg.clock_divider = clock_freq / bit_freq;
// FIFO config
cfg.fifo_join = FifoJoin::TxOnly;
cfg.shift_out = ShiftConfig {
auto_fill: true,
threshold: 32,
direction: ShiftDirection::Left,
};
sm.set_config(&cfg);
sm.set_enable(true);
Self { dma: dma.into(), sm }
}
/// Write a buffer of [smart_leds::RGBW] to the ws2812 string
pub async fn write(&mut self, colors: &[RGBW<u8>; N]) {
// Precompute the word bytes from the colors
let mut words = [0u32; N];
for i in 0..N {
let word = (u32::from(colors[i].g) << 24)
| (u32::from(colors[i].r) << 16)
| (u32::from(colors[i].b) << 8)
| u32::from(colors[i].a.0);
words[i] = word;
}
// DMA transfer
self.sm.tx().dma_push(self.dma.reborrow(), &words, false).await;
Timer::after_micros(55).await;
}
}
|