aboutsummaryrefslogtreecommitdiff
path: root/examples
diff options
context:
space:
mode:
Diffstat (limited to 'examples')
-rw-r--r--examples/rp/src/bin/pio_hd44780.rs244
1 files changed, 244 insertions, 0 deletions
diff --git a/examples/rp/src/bin/pio_hd44780.rs b/examples/rp/src/bin/pio_hd44780.rs
new file mode 100644
index 000000000..6bcd0652b
--- /dev/null
+++ b/examples/rp/src/bin/pio_hd44780.rs
@@ -0,0 +1,244 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use core::fmt::Write;
6
7use embassy_executor::Spawner;
8use embassy_rp::dma::{AnyChannel, Channel};
9use embassy_rp::gpio::Pin;
10use embassy_rp::peripherals::PIO0;
11use embassy_rp::pio::{
12 FifoJoin, PioCommon, PioInstanceBase, PioPeripheral, PioStateMachine, PioStateMachineInstance, ShiftDirection,
13 SmInstanceBase,
14};
15use embassy_rp::pwm::{Config, Pwm};
16use embassy_rp::relocate::RelocatedProgram;
17use embassy_rp::{into_ref, Peripheral, PeripheralRef};
18use embassy_time::{Duration, Instant, Timer};
19use {defmt_rtt as _, panic_probe as _};
20
21#[embassy_executor::main]
22async fn main(_spawner: Spawner) {
23 // this test assumes a 2x16 HD44780 display attached as follow:
24 // rs = PIN0
25 // rw = PIN1
26 // e = PIN2
27 // db4 = PIN3
28 // db5 = PIN4
29 // db6 = PIN5
30 // db7 = PIN6
31 // additionally a pwm signal for a bias voltage charge pump is provided on pin 15,
32 // allowing direct connection of the display to the RP2040 without level shifters.
33 let p = embassy_rp::init(Default::default());
34
35 let _pwm = Pwm::new_output_b(p.PWM_CH7, p.PIN_15, {
36 let mut c = Config::default();
37 c.divider = 125.into();
38 c.top = 100;
39 c.compare_b = 50;
40 c
41 });
42
43 let mut hd = HD44780::new(
44 p.PIO0, p.DMA_CH3, p.PIN_0, p.PIN_1, p.PIN_2, p.PIN_3, p.PIN_4, p.PIN_5, p.PIN_6,
45 )
46 .await;
47
48 loop {
49 struct Buf<const N: usize>([u8; N], usize);
50 impl<const N: usize> Write for Buf<N> {
51 fn write_str(&mut self, s: &str) -> Result<(), core::fmt::Error> {
52 for b in s.as_bytes() {
53 if self.1 >= N {
54 return Err(core::fmt::Error);
55 }
56 self.0[self.1] = *b;
57 self.1 += 1;
58 }
59 Ok(())
60 }
61 }
62 let mut buf = Buf([0; 16], 0);
63 write!(buf, "up {}s", Instant::now().as_micros() as f32 / 1e6).unwrap();
64 hd.add_line(&buf.0[0..buf.1]).await;
65 Timer::after(Duration::from_secs(1)).await;
66 }
67}
68
69pub struct HD44780<'l> {
70 dma: PeripheralRef<'l, AnyChannel>,
71 sm: PioStateMachineInstance<PioInstanceBase<0>, SmInstanceBase<0>>,
72
73 buf: [u8; 40],
74}
75
76impl<'l> HD44780<'l> {
77 pub async fn new(
78 pio: PIO0,
79 dma: impl Peripheral<P = impl Channel> + 'l,
80 rs: impl Pin,
81 rw: impl Pin,
82 e: impl Pin,
83 db4: impl Pin,
84 db5: impl Pin,
85 db6: impl Pin,
86 db7: impl Pin,
87 ) -> HD44780<'l> {
88 into_ref!(dma);
89
90 let db7pin = db7.pin();
91 let (mut common, mut sm0, ..) = pio.split();
92
93 // takes command words (<wait:24> <command:4> <0:4>)
94 let prg = pio_proc::pio_asm!(
95 r#"
96 .side_set 1 opt
97
98 loop:
99 out x, 24
100 delay:
101 jmp x--, delay
102 out pins, 4 side 1
103 out null, 4 side 0
104 jmp !osre, loop
105 irq 0
106 "#,
107 );
108
109 let rs = common.make_pio_pin(rs);
110 let rw = common.make_pio_pin(rw);
111 let e = common.make_pio_pin(e);
112 let db4 = common.make_pio_pin(db4);
113 let db5 = common.make_pio_pin(db5);
114 let db6 = common.make_pio_pin(db6);
115 let db7 = common.make_pio_pin(db7);
116
117 sm0.set_set_pins(&[&rs, &rw]);
118 embassy_rp::pio_instr_util::set_pindir(&mut sm0, 0b11);
119 sm0.set_set_pins(&[&e]);
120 embassy_rp::pio_instr_util::set_pindir(&mut sm0, 0b1);
121 sm0.set_set_pins(&[&db4, &db5, &db6, &db7]);
122 embassy_rp::pio_instr_util::set_pindir(&mut sm0, 0b11111);
123
124 let relocated = RelocatedProgram::new(&prg.program);
125 common.write_instr(relocated.origin() as usize, relocated.code());
126 embassy_rp::pio_instr_util::exec_jmp(&mut sm0, relocated.origin());
127 sm0.set_clkdiv(125 * 256);
128 let pio::Wrap { source, target } = relocated.wrap();
129 sm0.set_wrap(source, target);
130 sm0.set_side_enable(true);
131 sm0.set_out_pins(&[&db4, &db5, &db6, &db7]);
132 sm0.set_sideset_base_pin(&e);
133 sm0.set_sideset_count(2);
134 sm0.set_out_shift_dir(ShiftDirection::Left);
135 sm0.set_fifo_join(FifoJoin::TxOnly);
136 sm0.set_autopull(true);
137 sm0.set_pull_threshold(32);
138
139 sm0.set_enable(true);
140 // init to 8 bit thrice
141 sm0.push_tx((50000 << 8) | 0x30);
142 sm0.push_tx((5000 << 8) | 0x30);
143 sm0.push_tx((200 << 8) | 0x30);
144 // init 4 bit
145 sm0.push_tx((200 << 8) | 0x20);
146 // set font and lines
147 sm0.push_tx((50 << 8) | 0x20);
148 sm0.push_tx(0b1100_0000);
149
150 sm0.wait_irq(0).await;
151 sm0.set_enable(false);
152
153 // takes command sequences (<rs:1> <count:7>, data...)
154 // many side sets are only there to free up a delay bit!
155 let prg = pio_proc::pio_asm!(
156 r#"
157 .origin 7
158 .side_set 1
159
160 .wrap_target
161 pull side 0
162 out x 1 side 0 ; !rs
163 out y 7 side 0 ; #data - 1
164
165 ; rs/rw to e: >= 60ns
166 ; e high time: >= 500ns
167 ; e low time: >= 500ns
168 ; read data valid after e falling: ~5ns
169 ; write data hold after e falling: ~10ns
170
171 loop:
172 pull side 0
173 jmp !x data side 0
174 command:
175 set pins 0b00 side 0
176 jmp shift side 0
177 data:
178 set pins 0b01 side 0
179 shift:
180 out pins 4 side 1 [9]
181 nop side 0 [9]
182 out pins 4 side 1 [9]
183 mov osr null side 0 [7]
184 out pindirs 4 side 0
185 set pins 0b10 side 0
186 busy:
187 nop side 1 [9]
188 jmp pin more side 0 [9]
189 mov osr ~osr side 1 [9]
190 nop side 0 [4]
191 out pindirs 4 side 0
192 jmp y-- loop side 0
193 .wrap
194 more:
195 nop side 1 [9]
196 jmp busy side 0 [9]
197 "#
198 );
199
200 let relocated = RelocatedProgram::new(&prg.program);
201 common.write_instr(relocated.origin() as usize, relocated.code());
202 embassy_rp::pio_instr_util::exec_jmp(&mut sm0, relocated.origin());
203 let pio::Wrap { source, target } = relocated.wrap();
204 sm0.set_clkdiv(8 * 256); // ~64ns/insn
205 sm0.set_side_enable(false);
206 sm0.set_jmp_pin(db7pin);
207 sm0.set_wrap(source, target);
208 sm0.set_set_pins(&[&rs, &rw]);
209 sm0.set_out_pins(&[&db4, &db5, &db6, &db7]);
210 sm0.set_sideset_base_pin(&e);
211 sm0.set_sideset_count(1);
212 sm0.set_out_shift_dir(ShiftDirection::Left);
213 sm0.set_fifo_join(FifoJoin::TxOnly);
214
215 sm0.set_enable(true);
216
217 // display on and cursor on and blinking, reset display
218 sm0.dma_push(dma.reborrow(), &[0x81u8, 0x0f, 1]).await;
219
220 Self {
221 dma: dma.map_into(),
222 sm: sm0,
223 buf: [0x20; 40],
224 }
225 }
226
227 pub async fn add_line(&mut self, s: &[u8]) {
228 // move cursor to 0:0, prepare 16 characters
229 self.buf[..3].copy_from_slice(&[0x80, 0x80, 15]);
230 // move line 2 up
231 self.buf.copy_within(22..38, 3);
232 // move cursor to 1:0, prepare 16 characters
233 self.buf[19..22].copy_from_slice(&[0x80, 0xc0, 15]);
234 // file line 2 with spaces
235 self.buf[22..38].fill(0x20);
236 // copy input line
237 let len = s.len().min(16);
238 self.buf[22..22 + len].copy_from_slice(&s[0..len]);
239 // set cursor to 1:15
240 self.buf[38..].copy_from_slice(&[0x80, 0xcf]);
241
242 self.sm.dma_push(self.dma.reborrow(), &self.buf).await;
243 }
244}