Skip to main content
RKTK API Docs RKTK Home Repo

rktk_drivers_rp/split/
pio_half_duplex.rs

1use embassy_futures::yield_now;
2use embassy_rp::{
3    Peri,
4    pio::{Common, Config, Instance, Pin, Pio, PioPin, ShiftDirection, StateMachine},
5};
6use embassy_sync::semaphore::{FairSemaphore, Semaphore};
7use embassy_time::Timer;
8use fixed::traits::ToFixed;
9use rktk::{drivers::interface::split::SplitDriver, utils::RawMutex};
10
11pub const SPLIT_BITRATE: f64 = 1000000.0;
12pub const SPLIT_CLK_DIVIDER: f64 = 62_000_000.0 / (SPLIT_BITRATE * 8.0);
13
14// Data structure
15//
16// 0 bit: start
17// 1-8 bit: data
18// 9 bit: start check
19// 10bit: end check
20
21fn rx_init<'a, I: Instance>(
22    common: &mut Common<'a, I>,
23    sm: &mut StateMachine<'a, I, 0>,
24    data_pin: &Pin<'a, I>,
25) {
26    let prg = pio::pio_asm!(
27        "set pindirs 0",
28        ".wrap_target",
29        "wait 0 pin 0",
30        "set x 9 [8]",
31        "bitloop:",
32        "in pins 1 [6]",
33        "jmp x-- bitloop",
34        "push",
35        ".wrap"
36    );
37    let mut cfg = Config::default();
38    cfg.use_program(&common.load_program(&prg.program), &[]);
39
40    cfg.set_in_pins(&[data_pin]);
41
42    cfg.shift_in.direction = ShiftDirection::Left;
43
44    cfg.fifo_join = embassy_rp::pio::FifoJoin::RxOnly;
45
46    cfg.clock_divider = SPLIT_CLK_DIVIDER.to_fixed();
47    sm.set_config(&cfg);
48    sm.set_enable(true);
49}
50
51fn tx_init<'a, I: Instance>(
52    common: &mut Common<'a, I>,
53    sm: &mut StateMachine<'a, I, 1>,
54    data_pin: &Pin<'a, I>,
55) {
56    let prg = pio::pio_asm!(
57        "set pindirs 0",
58        ".wrap_target",
59        "pull",
60        "set x 9 [2]",
61        "set pins 0",
62        "set pindirs 1 [7]",
63        "bitloop:",
64        "out pins 1 [6]",
65        "jmp x-- bitloop",
66        "set pins 1",
67        "set pindirs 0 [6]",
68        ".wrap"
69    );
70    let mut cfg = Config::default();
71    cfg.use_program(&common.load_program(&prg.program), &[]);
72
73    cfg.set_out_pins(&[data_pin]);
74    cfg.set_set_pins(&[data_pin]);
75
76    cfg.shift_out.direction = ShiftDirection::Left;
77
78    cfg.fifo_join = embassy_rp::pio::FifoJoin::TxOnly;
79
80    cfg.clock_divider = SPLIT_CLK_DIVIDER.to_fixed();
81    sm.set_config(&cfg);
82    sm.set_enable(false);
83}
84
85pub struct PioHalfDuplexSplitDriver<'a, I: Instance> {
86    rx_sm: StateMachine<'a, I, 0>,
87    tx_sm: StateMachine<'a, I, 1>,
88    pin: Pin<'a, I>,
89}
90
91static COMM_SEMAPHORE: FairSemaphore<RawMutex, 3> = FairSemaphore::new(1);
92
93impl<'a, I: Instance> PioHalfDuplexSplitDriver<'a, I> {
94    pub fn new<'b: 'a>(
95        pio: Pio<'static, I>,
96        data_pin: Peri<'a, impl PioPin>,
97    ) -> PioHalfDuplexSplitDriver<'a, I> {
98        let mut common = pio.common;
99        let mut sm0 = pio.sm0;
100        let mut sm1 = pio.sm1;
101
102        let mut out_pin = common.make_pio_pin(data_pin);
103        out_pin.set_pull(embassy_rp::gpio::Pull::Up);
104
105        rx_init(&mut common, &mut sm0, &out_pin);
106        tx_init(&mut common, &mut sm1, &out_pin);
107
108        Self { rx_sm: sm0, tx_sm: sm1, pin: out_pin }
109    }
110
111    async fn enter_rx(&mut self) {
112        while !self.tx_sm.tx().empty() {
113            yield_now().await;
114        }
115
116        Timer::after_micros(300).await;
117
118        self.tx_sm.set_enable(false);
119        self.pin.set_drive_strength(embassy_rp::gpio::Drive::_2mA);
120        self.rx_sm.restart();
121        self.rx_sm.set_enable(true);
122    }
123
124    async fn enter_tx(&mut self) {
125        self.rx_sm.set_enable(false);
126        self.pin.set_drive_strength(embassy_rp::gpio::Drive::_12mA);
127        self.tx_sm.restart();
128        self.tx_sm.set_enable(true);
129    }
130
131    pub async fn recv_byte(&mut self) -> (bool, bool, u8) {
132        let mut data = self.rx_sm.rx().wait_pull().await;
133        let end_bit = data & 1;
134        data >>= 1;
135        let start_bit = data & 1;
136        data >>= 1;
137        (start_bit == 1, end_bit == 1, data as u8)
138    }
139}
140
141#[derive(Debug)]
142#[cfg_attr(feature = "defmt", derive(defmt::Format))]
143pub enum Error {
144    GeneralError(&'static str),
145}
146
147impl rktk::drivers::interface::Error for Error {}
148
149impl<I: Instance + 'static> SplitDriver for PioHalfDuplexSplitDriver<'static, I> {
150    type Error = Error;
151
152    async fn init(&mut self) -> Result<(), Self::Error> {
153        self.enter_rx().await;
154        Ok(())
155    }
156
157    async fn recv(&mut self, buf: &mut [u8], _is_master: bool) -> Result<usize, Self::Error> {
158        let _permit = loop {
159            let (start, end, data) = self.recv_byte().await;
160
161            let permit = COMM_SEMAPHORE.try_acquire(1);
162
163            if !start {
164                continue;
165            }
166
167            buf[0] = data;
168
169            if end {
170                return Ok(0);
171            }
172
173            break permit;
174        };
175
176        for (i, b) in buf.iter_mut().skip(1).enumerate() {
177            let (_start, end, data) = self.recv_byte().await;
178
179            *b = data;
180
181            if end {
182                return Ok(i + 2);
183            }
184        }
185
186        Err(Self::Error::GeneralError("Buffer overflow"))
187    }
188
189    async fn send_all(&mut self, buf: &[u8], _is_master: bool) -> Result<(), Self::Error> {
190        let _permit = COMM_SEMAPHORE.acquire(1).await;
191
192        self.enter_tx().await;
193
194        for (i, data) in buf.iter().enumerate() {
195            let mut data = (*data as u32) << 24;
196
197            if i == 0 {
198                data |= 1 << 23;
199            }
200            if i == buf.len() - 1 {
201                data |= 1 << 22;
202            }
203
204            self.tx_sm.tx().wait_push(data).await;
205            embassy_time::Timer::after_ticks(300).await;
206        }
207
208        self.enter_rx().await;
209
210        Ok(())
211    }
212}