RKTK API Docs RKTK Home Repo

rktk_drivers_rp/split/
pio_half_duplex.rs

1use embassy_futures::yield_now;
2use embassy_rp::{
3    Peripheral,
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: impl Peripheral<P = impl PioPin + 'a> + 'a,
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 {
109            rx_sm: sm0,
110            tx_sm: sm1,
111            pin: out_pin,
112        }
113    }
114
115    async fn enter_rx(&mut self) {
116        while !self.tx_sm.tx().empty() {
117            yield_now().await;
118        }
119
120        Timer::after_micros(300).await;
121
122        self.tx_sm.set_enable(false);
123        self.pin.set_drive_strength(embassy_rp::gpio::Drive::_2mA);
124        self.rx_sm.restart();
125        self.rx_sm.set_enable(true);
126    }
127
128    async fn enter_tx(&mut self) {
129        self.rx_sm.set_enable(false);
130        self.pin.set_drive_strength(embassy_rp::gpio::Drive::_12mA);
131        self.tx_sm.restart();
132        self.tx_sm.set_enable(true);
133    }
134
135    pub async fn recv_byte(&mut self) -> (bool, bool, u8) {
136        let mut data = self.rx_sm.rx().wait_pull().await;
137        let end_bit = data & 1;
138        data >>= 1;
139        let start_bit = data & 1;
140        data >>= 1;
141        (start_bit == 1, end_bit == 1, data as u8)
142    }
143}
144
145#[derive(Debug)]
146#[cfg_attr(feature = "defmt", derive(defmt::Format))]
147pub enum Error {
148    GeneralError(&'static str),
149}
150
151impl rktk::drivers::interface::Error for Error {}
152
153impl<I: Instance + 'static> SplitDriver for PioHalfDuplexSplitDriver<'static, I> {
154    type Error = Error;
155
156    async fn init(&mut self) -> Result<(), Self::Error> {
157        self.enter_rx().await;
158        Ok(())
159    }
160
161    async fn recv(&mut self, buf: &mut [u8], _is_master: bool) -> Result<usize, Self::Error> {
162        let _permit = loop {
163            let (start, end, data) = self.recv_byte().await;
164
165            let permit = COMM_SEMAPHORE.try_acquire(1);
166
167            if !start {
168                continue;
169            }
170
171            buf[0] = data;
172
173            if end {
174                return Ok(0);
175            }
176
177            break permit;
178        };
179
180        for (i, b) in buf.iter_mut().skip(1).enumerate() {
181            let (_start, end, data) = self.recv_byte().await;
182
183            *b = data;
184
185            if end {
186                return Ok(i);
187            }
188        }
189
190        Err(Self::Error::GeneralError("Buffer overflow"))
191    }
192
193    async fn send_all(&mut self, buf: &[u8], _is_master: bool) -> Result<(), Self::Error> {
194        let _permit = COMM_SEMAPHORE.acquire(1).await;
195
196        self.enter_tx().await;
197
198        for (i, data) in buf.iter().enumerate() {
199            let mut data = (*data as u32) << 24;
200
201            if i == 0 {
202                data |= 1 << 23;
203            }
204            if i == buf.len() - 1 {
205                data |= 1 << 22;
206            }
207
208            self.tx_sm.tx().wait_push(data).await;
209            embassy_time::Timer::after_ticks(300).await;
210        }
211
212        self.enter_rx().await;
213
214        Ok(())
215    }
216}