aboutsummaryrefslogtreecommitdiff
path: root/examples
diff options
context:
space:
mode:
authorpennae <[email protected]>2023-05-02 10:44:00 +0200
committerpennae <[email protected]>2023-05-02 13:44:24 +0200
commit8e22d574478d7480bc0473ed0ad9dac7d02602a8 (patch)
treecdae88f5f44402c9859170520b877d0ad186712b /examples
parent54e695b1b2105c1b2484584d100564afc0679596 (diff)
rp/pio: add hd44780 example
add an hd44780 example for pio. hd44780 with busy polling is a pretty complicated protocol if the busy polling is to be done by the peripheral, and this example exercises many pio features that we don't have good examples for yet.
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}