Skip to main content

rp_pio_serial/
lib.rs

1#![no_std]
2
3use core::fmt::{self, Write as FmtWrite};
4use core::hint::spin_loop;
5
6use cortex_m::asm;
7use heapless::spsc::Queue;
8#[cfg(feature = "rp2040")]
9use pio_proc::pio_asm;
10
11#[cfg(feature = "rp2040")]
12use rp2040_hal::pio::{
13    PIOBuilder, PIOExt, PinDir, Running, Rx, ShiftDirection, StateMachine, StateMachineIndex, Tx,
14    UninitStateMachine, PIO,
15};
16
17#[cfg(feature = "rp2350")]
18use rp235x_hal::pio::{
19    PIOBuilder, PIOExt, PinDir, Running, Rx, ShiftDirection, StateMachine, StateMachineIndex, Tx,
20    UninitStateMachine, PIO,
21};
22
23#[cfg(all(feature = "rp2350", target_arch = "arm", target_os = "none"))]
24use pio::pio_asm;
25
26#[cfg(all(feature = "rp2040", feature = "rp2350"))]
27compile_error!("features `rp2040` and `rp2350` cannot be enabled at the same time");
28
29#[cfg(not(any(feature = "rp2040", feature = "rp2350")))]
30compile_error!("either feature `rp2040` or `rp2350` must be enabled");
31
32#[cfg(all(feature = "rp2040", not(target_arch = "arm")))]
33compile_error!("`rp2040` only supports ARM targets");
34
35const DEFAULT_TX_BUF_SIZE: usize = 512;
36const DEFAULT_RX_BUF_SIZE: usize = 512;
37
38/// 经验值:TX FIFO(4) + 在途/移位路径(1)
39const DEFAULT_TX_PIPELINE_CHARS: u32 = 5;
40
41#[derive(Debug, Clone, Copy, PartialEq, Eq)]
42pub enum InitError {
43    InvalidBaud,
44    UnsupportedConfig,
45    InstallTxProgram,
46    InstallRxProgram,
47}
48
49#[derive(Debug, Clone, Copy, PartialEq, Eq)]
50pub enum DataBits {
51    Five = 5,
52    Six = 6,
53    Seven = 7,
54    Eight = 8,
55}
56
57#[derive(Debug, Clone, Copy, PartialEq, Eq)]
58pub enum StopBits {
59    One,
60    Two,
61}
62
63#[derive(Debug, Clone, Copy, PartialEq, Eq)]
64pub enum Parity {
65    None,
66    Even,
67    Odd,
68}
69
70#[derive(Debug, Clone, Copy, PartialEq, Eq)]
71pub enum ServiceMode {
72    Polling,
73    InterruptDriven,
74}
75
76#[derive(Debug, Clone, Copy, PartialEq, Eq)]
77pub struct UartConfig {
78    pub baud: u32,
79    pub data_bits: DataBits,
80    pub parity: Parity,
81    pub stop_bits: StopBits,
82    pub auto_echo: bool,
83    pub service_mode: ServiceMode,
84    pub tx_pipeline_chars: u32,
85}
86
87impl Default for UartConfig {
88    fn default() -> Self {
89        Self {
90            baud: 115_200,
91            data_bits: DataBits::Eight,
92            parity: Parity::None,
93            stop_bits: StopBits::One,
94            auto_echo: false,
95            service_mode: ServiceMode::Polling,
96            tx_pipeline_chars: DEFAULT_TX_PIPELINE_CHARS,
97        }
98    }
99}
100
101impl UartConfig {
102    #[inline]
103    pub fn frame_bits(&self) -> u32 {
104        let data_bits = self.data_bits as u32;
105        let parity_bits = match self.parity {
106            Parity::None => 0,
107            Parity::Even | Parity::Odd => 1,
108        };
109        let stop_bits = match self.stop_bits {
110            StopBits::One => 1,
111            StopBits::Two => 2,
112        };
113
114        1 + data_bits + parity_bits + stop_bits
115    }
116
117    #[inline]
118    pub fn is_supported_by_current_backend(&self) -> bool {
119        self.baud != 0
120            && self.data_bits == DataBits::Eight
121            && self.parity == Parity::None
122            && self.stop_bits == StopBits::One
123    }
124}
125
126pub struct RpPioSerial<
127    P: PIOExt,
128    SMTX: StateMachineIndex,
129    SMRX: StateMachineIndex,
130    const TX_BUF: usize = DEFAULT_TX_BUF_SIZE,
131    const RX_BUF: usize = DEFAULT_RX_BUF_SIZE,
132> {
133    _sm_tx: StateMachine<(P, SMTX), Running>,
134    _sm_rx: StateMachine<(P, SMRX), Running>,
135    tx: Tx<(P, SMTX)>,
136    rx: Rx<(P, SMRX)>,
137
138    tx_buf: Queue<u8, TX_BUF>,
139    rx_buf: Queue<u8, RX_BUF>,
140
141    config: UartConfig,
142    clock_hz: u32,
143    char_delay_cycles: u32,
144
145    dropped_tx: u32,
146    dropped_rx: u32,
147
148    /// 只要写入过硬件 TX FIFO,就置 true。
149    /// flush_blocking / write_all_blocking 在等待尾部清空后再清零。
150    hw_drain_pending: bool,
151}
152
153impl<
154        P: PIOExt,
155        SMTX: StateMachineIndex,
156        SMRX: StateMachineIndex,
157        const TX_BUF: usize,
158        const RX_BUF: usize,
159    > RpPioSerial<P, SMTX, SMRX, TX_BUF, RX_BUF>
160{
161    /// 创建一个基于任意 PIO/任意状态机组合的 UART。
162    ///
163    /// 注意:
164    /// - TX / RX 引脚应已切换到正确的 PIO function
165    /// - tx_pin / rx_pin 是 GPIO 号
166    /// - sm_tx / sm_rx 必须来自同一个 PIO block
167    /// - 当前稳定支持配置:8N1
168    pub fn new(
169        pio: &mut PIO<P>,
170        sm_tx: UninitStateMachine<(P, SMTX)>,
171        sm_rx: UninitStateMachine<(P, SMRX)>,
172        tx_pin: u8,
173        rx_pin: u8,
174        clock_hz: u32,
175        config: UartConfig,
176    ) -> Result<Self, InitError> {
177        if clock_hz == 0 || config.baud == 0 {
178            return Err(InitError::InvalidBaud);
179        }
180
181        if !config.is_supported_by_current_backend() {
182            return Err(InitError::UnsupportedConfig);
183        }
184
185        let (div_int, div_frac) = calc_clkdiv(clock_hz, config.baud)?;
186        let char_delay_cycles = calc_char_delay_cycles(clock_hz, config.frame_bits(), config.baud);
187
188        // =========================================================
189        // TX program (8N1)
190        // 8 cycles / bit
191        // =========================================================
192
193        //this version is ok for tx
194        let tx_program = pio_asm!(
195            ".side_set 1 opt"
196            "pull       side 1 [7]"
197            "set x, 7   side 0 [7]"
198            "bitloop:"
199            "out pins, 1"
200            "jmp x-- bitloop [6]"
201        );
202
203        let installed_tx = pio
204            .install(&tx_program.program)
205            .map_err(|_| InitError::InstallTxProgram)?;
206
207        let (mut sm_tx, _, tx) = PIOBuilder::from_installed_program(installed_tx)
208            .out_pins(tx_pin, 1)
209            .side_set_pin_base(tx_pin)
210            .clock_divisor_fixed_point(div_int, div_frac)
211            .out_shift_direction(ShiftDirection::Right)
212            .autopull(false)
213            //.pull_threshold(8u8)
214            .build(sm_tx);
215
216        sm_tx.set_pindirs([(tx_pin, PinDir::Output)]);
217        let sm_tx = sm_tx.start();
218
219        // =========================================================
220        // RX program (8N1)
221        // 8 cycles / bit
222        // =========================================================
223        let rx_program = pio_asm!(
224            "idle_wait:"
225            "wait 1 pin 0"      // 要求空闲为高,避免假 start
226
227            "start_wait:"
228            "wait 0 pin 0"      // start: 下降沿
229
230            "set x, 7 [10]"     // 对齐到第1个数据位中心附近(按你现有位周期调参点)
231
232            "bitloop:"
233            "in pins, 1"
234            "jmp x-- bitloop [6]"
235
236            "push"
237            "jmp idle_wait"
238        );
239        let installed_rx = pio
240            .install(&rx_program.program)
241            .map_err(|_| InitError::InstallRxProgram)?;
242
243        let (mut sm_rx, rx, _) = PIOBuilder::from_installed_program(installed_rx)
244            .in_pin_base(rx_pin)
245            .clock_divisor_fixed_point(div_int, div_frac)
246            .in_shift_direction(ShiftDirection::Left)
247            .autopush(false)
248            .build(sm_rx);
249
250        sm_rx.set_pindirs([(rx_pin, PinDir::Input)]);
251        let sm_rx = sm_rx.start();
252
253        Ok(Self {
254            _sm_tx: sm_tx,
255            _sm_rx: sm_rx,
256            tx,
257            rx,
258            tx_buf: Queue::new(),
259            rx_buf: Queue::new(),
260            config,
261            clock_hz,
262            char_delay_cycles,
263            dropped_tx: 0,
264            dropped_rx: 0,
265            hw_drain_pending: false,
266        })
267    }
268
269    // =========================================================
270    // 配置/状态查询
271    // =========================================================
272
273    pub fn config(&self) -> UartConfig {
274        self.config
275    }
276
277    pub fn baud(&self) -> u32 {
278        self.config.baud
279    }
280
281    pub fn clock_hz(&self) -> u32 {
282        self.clock_hz
283    }
284
285    pub fn data_bits(&self) -> DataBits {
286        self.config.data_bits
287    }
288
289    pub fn parity(&self) -> Parity {
290        self.config.parity
291    }
292
293    pub fn stop_bits(&self) -> StopBits {
294        self.config.stop_bits
295    }
296
297    pub fn auto_echo(&self) -> bool {
298        self.config.auto_echo
299    }
300
301    pub fn service_mode(&self) -> ServiceMode {
302        self.config.service_mode
303    }
304
305    pub fn set_auto_echo(&mut self, enable: bool) {
306        self.config.auto_echo = enable;
307    }
308
309    pub fn set_service_mode(&mut self, mode: ServiceMode) {
310        self.config.service_mode = mode;
311    }
312
313    pub fn dropped_tx(&self) -> u32 {
314        self.dropped_tx
315    }
316
317    pub fn dropped_rx(&self) -> u32 {
318        self.dropped_rx
319    }
320
321    pub fn available(&self) -> usize {
322        self.rx_buf.len()
323    }
324
325    pub fn tx_pending(&self) -> usize {
326        self.tx_buf.len()
327    }
328
329    pub fn clear_rx(&mut self) {
330        while self.rx_buf.dequeue().is_some() {}
331    }
332
333    pub fn clear_tx_buffer(&mut self) {
334        while self.tx_buf.dequeue().is_some() {}
335    }
336
337    // =========================================================
338    // Poll / IRQ service
339    // =========================================================
340
341    /// 轮询服务入口
342    pub fn poll(&mut self) {
343        self.pump_rx();
344        self.flush_tx_nonblocking();
345    }
346
347    /// 中断服务入口
348    ///
349    /// 在 PIO IRQ handler 中调用即可。
350    pub fn on_interrupt(&mut self) {
351        self.pump_rx();
352        self.flush_tx_nonblocking();
353    }
354
355    /// 当使用“中断驱动”时,可在写入数据后主动 kick 一次 TX
356    pub fn kick_tx(&mut self) {
357        self.flush_tx_nonblocking();
358    }
359
360    // =========================================================
361    // 非阻塞发送
362    // =========================================================
363
364    /// 非阻塞写入。
365    ///
366    /// 返回成功写入软件 TX 缓冲的字节数。
367    pub fn write(&mut self, data: &[u8]) -> usize {
368        let n = self.enqueue_tx(data);
369        self.flush_tx_nonblocking();
370        n
371    }
372
373    pub fn write_byte(&mut self, b: u8) -> bool {
374        self.write(&[b]) == 1
375    }
376
377    pub fn print(&mut self, s: &str) -> usize {
378        self.write(s.as_bytes())
379    }
380
381    pub fn println(&mut self, s: &str) -> usize {
382        let mut n = 0;
383        n += self.write(s.as_bytes());
384        n += self.write(b"\r\n");
385        n
386    }
387
388    // =========================================================
389    // 强阻塞发送
390    // =========================================================
391
392    /// 阻塞直到软件 TX 缓冲中的数据全部进入硬件,并等待尾部发完。
393    pub fn flush_blocking(&mut self) {
394        while let Some(b) = self.tx_buf.dequeue() {
395            while self.tx.is_full() {
396                spin_loop();
397            }
398            self.tx.write(u32::from(b));
399            self.hw_drain_pending = true;
400        }
401
402        if self.hw_drain_pending {
403            self.wait_chars(self.config.tx_pipeline_chars.max(1));
404            self.hw_drain_pending = false;
405        }
406    }
407
408    /// 更强的阻塞发送:
409    /// - 不依赖后续 `poll()`
410    /// - 不依赖 tx_buf 能否装下整段数据
411    /// - 会先清空之前的发送,再直接流式喂硬件 FIFO
412    pub fn write_all_blocking(&mut self, data: &[u8]) {
413        self.flush_blocking();
414
415        for &b in data {
416            while self.tx.is_full() {
417                spin_loop();
418            }
419            self.tx.write(u32::from(b));
420            self.hw_drain_pending = true;
421        }
422
423        if self.hw_drain_pending {
424            self.wait_chars(self.config.tx_pipeline_chars.max(1));
425            self.hw_drain_pending = false;
426        }
427    }
428
429    pub fn write_byte_blocking(&mut self, b: u8) {
430        self.write_all_blocking(&[b]);
431    }
432
433    pub fn print_blocking(&mut self, s: &str) {
434        self.write_all_blocking(s.as_bytes());
435    }
436
437    pub fn println_blocking(&mut self, s: &str) {
438        self.write_all_blocking(s.as_bytes());
439        self.write_all_blocking(b"\r\n");
440    }
441
442    /// 阻塞重复发送单字节
443    pub fn write_repeated_blocking(&mut self, byte: u8, count: usize) {
444        self.flush_blocking();
445
446        for _ in 0..count {
447            while self.tx.is_full() {
448                spin_loop();
449            }
450            self.tx.write(u32::from(byte));
451            self.hw_drain_pending = true;
452        }
453
454        if self.hw_drain_pending {
455            self.wait_chars(self.config.tx_pipeline_chars.max(1));
456            self.hw_drain_pending = false;
457        }
458    }
459
460    /// 阻塞格式化输出
461    pub fn fmt_write_blocking(&mut self, args: fmt::Arguments<'_>) -> fmt::Result {
462        struct BlockingFmt<
463            'a,
464            P: PIOExt,
465            SMTX: StateMachineIndex,
466            SMRX: StateMachineIndex,
467            const TX_BUF: usize,
468            const RX_BUF: usize,
469        > {
470            serial: &'a mut RpPioSerial<P, SMTX, SMRX, TX_BUF, RX_BUF>,
471        }
472
473        impl<
474                'a,
475                P: PIOExt,
476                SMTX: StateMachineIndex,
477                SMRX: StateMachineIndex,
478                const TX_BUF: usize,
479                const RX_BUF: usize,
480            > FmtWrite for BlockingFmt<'a, P, SMTX, SMRX, TX_BUF, RX_BUF>
481        {
482            fn write_str(&mut self, s: &str) -> fmt::Result {
483                self.serial.write_all_blocking(s.as_bytes());
484                Ok(())
485            }
486        }
487
488        let mut w = BlockingFmt { serial: self };
489        w.write_fmt(args)
490    }
491
492    // =========================================================
493    // 接收
494    // =========================================================
495
496    /// 非阻塞读取 RX 缓冲
497    pub fn read(&mut self, buf: &mut [u8]) -> usize {
498        self.pump_rx();
499
500        let mut n = 0;
501        while n < buf.len() {
502            match self.rx_buf.dequeue() {
503                Some(b) => {
504                    buf[n] = b;
505                    n += 1;
506                }
507                None => break,
508            }
509        }
510        n
511    }
512
513    pub fn read_byte(&mut self) -> Option<u8> {
514        self.pump_rx();
515        self.rx_buf.dequeue()
516    }
517
518    /// 阻塞读取 1 字节
519    pub fn read_byte_blocking(&mut self) -> u8 {
520        loop {
521            self.pump_rx();
522            if let Some(b) = self.rx_buf.dequeue() {
523                return b;
524            }
525            spin_loop();
526        }
527    }
528
529    /// 阻塞读取恰好 buf.len() 个字节
530    pub fn read_exact_blocking(&mut self, buf: &mut [u8]) {
531        for b in buf {
532            *b = self.read_byte_blocking();
533        }
534    }
535
536    // =========================================================
537    // 内部实现
538    // =========================================================
539
540    fn enqueue_tx(&mut self, data: &[u8]) -> usize {
541        let mut n = 0;
542        for &b in data {
543            if self.tx_buf.enqueue(b).is_ok() {
544                n += 1;
545            } else {
546                self.dropped_tx = self.dropped_tx.wrapping_add(1);
547                break;
548            }
549        }
550        n
551    }
552
553    fn pump_rx(&mut self) {
554        while let Some(word) = self.rx.read() {
555            let b = ((word & 0xFF) as u8).reverse_bits();
556            //let b = ((word >> 24) & 0xFF) as u8;
557
558            if self.rx_buf.enqueue(b).is_err() {
559                self.dropped_rx = self.dropped_rx.wrapping_add(1);
560            }
561
562            if self.config.auto_echo {
563                if self.tx_buf.enqueue(b).is_err() {
564                    self.dropped_tx = self.dropped_tx.wrapping_add(1);
565                }
566            }
567        }
568    }
569
570    fn flush_tx_nonblocking(&mut self) {
571        while !self.tx.is_full() {
572            let Some(b) = self.tx_buf.dequeue() else {
573                break;
574            };
575            self.tx.write(u32::from(b));
576            self.hw_drain_pending = true;
577        }
578    }
579
580    #[inline]
581    fn wait_chars(&self, chars: u32) {
582        let cycles = self.char_delay_cycles.saturating_mul(chars.max(1));
583        asm::delay(cycles);
584    }
585}
586
587// =========================================================
588// fmt::Write(默认走非阻塞发送)
589// =========================================================
590
591impl<
592        P: PIOExt,
593        SMTX: StateMachineIndex,
594        SMRX: StateMachineIndex,
595        const TX_BUF: usize,
596        const RX_BUF: usize,
597    > FmtWrite for RpPioSerial<P, SMTX, SMRX, TX_BUF, RX_BUF>
598{
599    fn write_str(&mut self, s: &str) -> fmt::Result {
600        let _ = self.write(s.as_bytes());
601        Ok(())
602    }
603}
604
605// =========================================================
606// 打印宏
607// =========================================================
608
609#[macro_export]
610macro_rules! pio_print {
611    ($serial:expr, $($tt:tt)*) => {{
612        let _ = core::fmt::Write::write_fmt(&mut $serial, format_args!($($tt)*));
613    }};
614}
615
616#[macro_export]
617macro_rules! pio_println {
618    ($serial:expr) => {{
619        let _ = $serial.write(b"\r\n");
620    }};
621    ($serial:expr, $($tt:tt)*) => {{
622        let _ = core::fmt::Write::write_fmt(&mut $serial, format_args!($($tt)*));
623        let _ = $serial.write(b"\r\n");
624    }};
625}
626
627/// 阻塞打印:整句输出不依赖后续 poll()
628#[macro_export]
629macro_rules! pio_bprint {
630    ($serial:expr, $($tt:tt)*) => {{
631        let _ = $serial.fmt_write_blocking(format_args!($($tt)*));
632    }};
633}
634
635#[macro_export]
636macro_rules! pio_bprintln {
637    ($serial:expr) => {{
638        $serial.write_all_blocking(b"\r\n");
639    }};
640    ($serial:expr, $($tt:tt)*) => {{
641        let _ = $serial.fmt_write_blocking(format_args!($($tt)*));
642        $serial.write_all_blocking(b"\r\n");
643    }};
644}
645
646// =========================================================
647// 工具函数
648// =========================================================
649
650fn calc_clkdiv(clock_hz: u32, baud: u32) -> Result<(u16, u8), InitError> {
651    if baud == 0 {
652        return Err(InitError::InvalidBaud);
653    }
654
655    // 当前 PIO UART 程序按 8 cycles / bit
656    let denom = (baud as u64) * 8;
657    if denom == 0 {
658        return Err(InitError::InvalidBaud);
659    }
660
661    let div = ((clock_hz as u64) << 8) / denom;
662    let int = (div >> 8) as u16;
663    let frac = (div & 0xFF) as u8;
664
665    if int == 0 && frac == 0 {
666        return Err(InitError::InvalidBaud);
667    }
668
669    Ok((int, frac))
670}
671
672fn calc_char_delay_cycles(clock_hz: u32, frame_bits: u32, baud: u32) -> u32 {
673    let cycles = (clock_hz as u64).saturating_mul(frame_bits.max(1) as u64) / (baud.max(1) as u64);
674
675    cycles.max(1).min(u32::MAX as u64) as u32
676}