Skip to content

Commit bc60f49

Browse files
committed
finish screen driver, add some test code for it
1 parent 60e3cd4 commit bc60f49

4 files changed

Lines changed: 192 additions & 24 deletions

File tree

firmware/Cargo.lock

Lines changed: 0 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

firmware/Cargo.toml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,7 @@ usbd-serial = "0.2"
4242
packed_struct = { version = "0.10.1", default-features = false }
4343

4444
ringbuffer = { version = "0.16", default-features = false }
45-
rgb = "0.8.52"
4645

47-
# pio = "0.2"
4846
# eeprom24x = { version = "0.7.2", features = ["defmt-03"] }
4947

5048
# mutually_exclusive_features = "0.1"

firmware/src/rgb.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ impl RgbMod {
4444
mut common, sm0, ..
4545
} = Pio::new(pio, Irqs);
4646
let program = PioWs2812Program::new(&mut common);
47+
4748
Self {
4849
ws2812: PioWs2812::new(&mut common, sm0, dma, pin, &program),
4950
poll_time: unwrap!(Instant::now().checked_add(POLL_TIME)),

firmware/src/screen.rs

Lines changed: 191 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,23 @@
33
//! See amazing things
44
55
use defmt::*;
6+
67
use embassy_futures::yield_now;
78
use embassy_rp::{
89
Peri, bind_interrupts,
910
gpio::Output,
1011
peripherals::{
1112
DMA_CH1, PIN_19, PIN_20, PIN_21, PIN_22, PIN_23, PIN_24, PIN_25, PIN_26, PIN_27, PIO1,
1213
},
13-
pio::InterruptHandler,
14-
pwm::Pwm,
14+
pio::{Config, InterruptHandler, Pio, StateMachine, program::pio_asm},
15+
pwm::{Pwm, SetDutyCycle},
1516
};
16-
use embassy_time::{Duration, Instant};
17+
use embassy_time::{Duration, Instant, Timer};
18+
19+
use crate::usb::usb_suspended;
1720

1821
type ScrPio = Peri<'static, PIO1>;
22+
type ScrPioSm = StateMachine<'static, PIO1, 0>;
1923
type ScrDma = Peri<'static, DMA_CH1>;
2024
type ScrDataPins = (
2125
Peri<'static, PIN_19>,
@@ -38,23 +42,181 @@ bind_interrupts!(struct Irqs {
3842
PIO1_IRQ_0 => InterruptHandler<PIO1>;
3943
});
4044

45+
struct St7789_8080 {
46+
sm: ScrPioSm,
47+
dma: ScrDma,
48+
cs: ScrCsPin,
49+
dc: ScrDcPin,
50+
bl: ScrBlPin,
51+
_rst: ScrRstPin,
52+
}
53+
impl St7789_8080 {
54+
pub fn new(
55+
pio: ScrPio,
56+
dma: ScrDma,
57+
data: ScrDataPins,
58+
clk: ScrClkPin,
59+
mut cs: ScrCsPin,
60+
mut dc: ScrDcPin,
61+
mut bl: ScrBlPin,
62+
mut rst: ScrRstPin,
63+
) -> Self {
64+
bl.set_duty_cycle_percent(0).unwrap();
65+
dc.set_low();
66+
cs.set_high();
67+
rst.set_high();
68+
69+
let Pio {
70+
mut common,
71+
mut sm0,
72+
..
73+
} = Pio::new(pio, Irqs);
74+
75+
let program = pio_asm!(
76+
// ".pio_version 1"
77+
// ".program st7789_8080"
78+
".side_set 1"
79+
".wrap_target"
80+
"out pins, 8 side 0"
81+
"nop side 1"
82+
".wrap"
83+
);
84+
85+
let mut cfg = Config::default();
86+
let clk = common.make_pio_pin(clk);
87+
let datas = [
88+
&common.make_pio_pin(data.0),
89+
&common.make_pio_pin(data.1),
90+
&common.make_pio_pin(data.2),
91+
&common.make_pio_pin(data.3),
92+
&common.make_pio_pin(data.4),
93+
&common.make_pio_pin(data.5),
94+
&common.make_pio_pin(data.6),
95+
&common.make_pio_pin(data.7),
96+
];
97+
cfg.set_out_pins(&datas);
98+
cfg.fifo_join = embassy_rp::pio::FifoJoin::TxOnly;
99+
cfg.shift_out.threshold = 16;
100+
cfg.shift_out.direction = embassy_rp::pio::ShiftDirection::Left;
101+
cfg.shift_out.auto_fill = true;
102+
cfg.clock_divider = 4u8.into();
103+
cfg.use_program(&common.load_program(&program.program), &[&clk]);
104+
105+
sm0.set_config(&cfg);
106+
sm0.set_pin_dirs(embassy_rp::pio::Direction::Out, &[&clk]);
107+
sm0.set_pin_dirs(embassy_rp::pio::Direction::Out, &datas);
108+
109+
sm0.set_enable(true);
110+
111+
Self {
112+
sm: sm0,
113+
dma,
114+
cs,
115+
dc,
116+
bl,
117+
_rst: rst,
118+
}
119+
}
120+
121+
fn set_backlight(&mut self, percent: u8) {
122+
self.bl.set_duty_cycle_percent(percent).unwrap();
123+
}
124+
125+
async fn set_dc_cs(&mut self, dc: bool, cs: bool) {
126+
Timer::after_micros(1).await;
127+
128+
if dc {
129+
self.dc.set_high();
130+
} else {
131+
self.dc.set_low();
132+
}
133+
if cs {
134+
self.cs.set_high();
135+
} else {
136+
self.cs.set_low();
137+
}
138+
139+
Timer::after_micros(1).await;
140+
}
141+
142+
async fn write(&mut self, word: u16) {
143+
self.sm.tx().wait_push((word as u32) << 16).await;
144+
}
145+
146+
async fn wait_idle(&mut self) {
147+
while !self.sm.tx().stalled() {
148+
yield_now().await;
149+
}
150+
}
151+
152+
async fn write_cmd(&mut self, cmd: &[u16]) {
153+
self.wait_idle().await;
154+
self.set_dc_cs(false, false).await;
155+
156+
self.write(cmd[0]).await;
157+
if cmd.len() >= 2 {
158+
self.wait_idle().await;
159+
self.set_dc_cs(true, false).await;
160+
for c in &cmd[1..] {
161+
self.write(*c).await;
162+
}
163+
}
164+
165+
self.wait_idle().await;
166+
self.set_dc_cs(true, true).await;
167+
}
168+
169+
pub async fn init(&mut self, w: u16, h: u16) {
170+
// init sequence
171+
// 16bit startup sequence
172+
self.write_cmd(&[0x0001]).await; // Software reset
173+
self.write_cmd(&[0x0011]).await; // Exit sleep mode
174+
self.write_cmd(&[0x003A, 0x5500]).await; // Set color mode to 16 bit
175+
self.write_cmd(&[0x0036, 0x0000]).await; // Set MADCTL: bottom to top, left to right, refresh is bottom to top // 0b111101_10
176+
self.write_cmd(&[0x002A, 0x0000, h]).await; // CASET: column addresses
177+
self.write_cmd(&[0x002B, 0x0000, w]).await; // RASET: row addresses
178+
self.write_cmd(&[0x0021]).await; // Inversion on
179+
self.write_cmd(&[0x0013]).await; // Normal display on
180+
self.write_cmd(&[0x0029]).await; // Main screen turn on
181+
}
182+
183+
async fn start_pixels(&mut self) {
184+
self.write_cmd(&[0x002C]).await;
185+
self.set_dc_cs(true, false).await;
186+
}
187+
188+
pub async fn push_framebuffer(&mut self, fb: &'static [u16; SCR_W * SCR_H]) {
189+
self.start_pixels().await;
190+
self.sm.tx().dma_push(self.dma.reborrow(), fb, false).await;
191+
}
192+
}
193+
41194
const POLL_TIME: Duration = Duration::from_millis(100);
195+
pub const SCR_W: usize = 320;
196+
pub const SCR_H: usize = 240;
197+
static mut FB: [u16; SCR_W * SCR_H] = [0xFFFF; SCR_W * SCR_H];
42198

43199
struct ScreenMod {
200+
scr: St7789_8080,
44201
poll_time: Instant,
45202
}
46203
impl ScreenMod {
47-
fn new(
204+
async fn new(
48205
pio: ScrPio,
49206
dma: ScrDma,
50-
data_pins: ScrDataPins,
51-
clk_pin: ScrClkPin,
52-
cs_pin: ScrCsPin,
53-
dc_pin: ScrDcPin,
54-
bl_pin: ScrBlPin,
55-
rst_pin: ScrRstPin,
207+
data: ScrDataPins,
208+
clk: ScrClkPin,
209+
cs: ScrCsPin,
210+
dc: ScrDcPin,
211+
bl: ScrBlPin,
212+
rst: ScrRstPin,
56213
) -> Self {
214+
let mut scr = St7789_8080::new(pio, dma, data, clk, cs, dc, bl, rst);
215+
216+
scr.init(SCR_W as u16, SCR_H as u16).await;
217+
57218
Self {
219+
scr,
58220
poll_time: unwrap!(Instant::now().checked_add(POLL_TIME)),
59221
}
60222
}
@@ -68,6 +230,15 @@ impl ScreenMod {
68230
continue;
69231
}
70232

233+
if !usb_suspended() {
234+
self.scr
235+
.push_framebuffer(unsafe { &mut *core::ptr::addr_of_mut!(FB) })
236+
.await;
237+
self.scr.set_backlight(100);
238+
} else {
239+
self.scr.set_backlight(0);
240+
}
241+
71242
self.poll_time = unwrap!(now.checked_add(POLL_TIME));
72243
}
73244
}
@@ -77,16 +248,15 @@ impl ScreenMod {
77248
pub async fn screen_task(
78249
pio: ScrPio,
79250
dma: ScrDma,
80-
data_pins: ScrDataPins,
81-
clk_pin: ScrClkPin,
82-
cs_pin: ScrCsPin,
83-
dc_pin: ScrDcPin,
84-
bl_pin: ScrBlPin,
85-
rst_pin: ScrRstPin,
251+
data: ScrDataPins,
252+
clk: ScrClkPin,
253+
cs: ScrCsPin,
254+
dc: ScrDcPin,
255+
bl: ScrBlPin,
256+
rst: ScrRstPin,
86257
) -> ! {
87-
ScreenMod::new(
88-
pio, dma, data_pins, clk_pin, cs_pin, dc_pin, bl_pin, rst_pin,
89-
)
90-
.task()
91-
.await;
258+
ScreenMod::new(pio, dma, data, clk, cs, dc, bl, rst)
259+
.await
260+
.task()
261+
.await;
92262
}

0 commit comments

Comments
 (0)