Skip to main content

rp_usb_serial/
lib.rs

1#![no_std]
2
3use core::cell::RefCell;
4use core::fmt::Write;
5use core::sync::atomic::{AtomicBool, Ordering};
6
7use critical_section::Mutex;
8use heapless::spsc::Queue;
9use static_cell::StaticCell;
10use usb_device::prelude::*;
11use usb_device::{class_prelude::UsbBusAllocator, device::UsbDeviceState};
12use usbd_serial::SerialPort;
13/*
14//mini example for rp2040
15#![no_std]
16#![no_main]
17// For string formatting.
18// The macro for our start-up function
19// A shorter alias for the Peripheral Access Crate, which provides low-level
20// register access
21use hal::{entry, pac, Clock};
22use rp2040_hal as hal;
23
24use rp_usb_serial::RpUsbConsole;
25use rp_usb_serial::usb_println;
26
27// Ensure we halt the program on panic (if we don't mention this crate it won't
28// be linked)
29use panic_halt as _;
30
31/// External high-speed crystal on the Raspberry Pi Pico board is 12 MHz. Adjust
32/// if your board has a different frequency
33const XTAL_FREQ_HZ: u32 = 12_000_000u32;
34
35/// The linker will place this boot block at the start of our program image. We
36/// need this to help the ROM bootloader get our code up and running.
37/// Note: This boot block is not necessary when using a rp-hal based BSP
38/// as the BSPs already perform this step.
39#[link_section = ".boot2"]
40#[used]
41pub static BOOT2: [u8; 256] = rp2040_boot2::BOOT_LOADER_GENERIC_03H;
42
43/// Entry point to our bare-metal application.
44///
45/// The `#[entry]` macro ensures the Cortex-M start-up code calls this function
46/// as soon as all global variables are initialised.
47///
48/// The function configures the RP2040 peripherals,
49/// gets a handle on the I2C peripheral,
50/// initializes the SSD1306 driver, initializes the text builder
51/// and then draws some text on the display.
52///
53///
54fn test () {
55    //this is the test for usb_println
56    usb_println!("这是在外部函数测试");
57}
58#[entry]
59fn main() -> ! {
60    // Grab our singleton objects
61    let mut pac = pac::Peripherals::take().unwrap();
62    let core = pac::CorePeripherals::take().unwrap();
63    // Set up the watchdog driver - needed by the clock setup code
64    let mut watchdog = hal::Watchdog::new(pac.WATCHDOG);
65
66    // Configure the clocks
67    //
68    // The default is to generate a 125 MHz system clock
69    let clocks = hal::clocks::init_clocks_and_plls(
70        XTAL_FREQ_HZ,
71        pac.XOSC,
72        pac.CLOCKS,
73        pac.PLL_SYS,
74        pac.PLL_USB,
75        &mut pac.RESETS,
76        &mut watchdog,
77    )
78    .ok()
79    .unwrap();
80
81    let mut delay = cortex_m::delay::Delay::new(core.SYST, clocks.system_clock.freq().to_Hz());
82    // The single-cycle I/O block controls our GPIO pins
83    // let sio = hal::Sio::new(pac.SIO);
84
85    // 初始化 USB
86    RpUsbConsole::init(
87        pac.USBCTRL_REGS,
88        pac.USBCTRL_DPRAM,
89        &mut pac.RESETS,
90        clocks.usb_clock,
91    );
92
93    // 打开 USB 中断
94    unsafe {
95        pac::NVIC::unpend(pac::Interrupt::USBCTRL_IRQ);
96        pac::NVIC::unmask(pac::Interrupt::USBCTRL_IRQ);
97    };
98    // 直接打印日志!
99    usb_println!("=== RP2040 USB 串口启动成功 ===");
100    usb_println!("系统时钟: {} Hz", clocks.system_clock.freq().to_Hz());
101
102    test();
103
104    loop {
105        usb_println!("主循环运行中...");
106        delay.delay_ms(1000);
107    }
108}
109*/
110
111#[cfg(feature = "rp2040")]
112use rp2040_hal as hal;
113#[cfg(feature = "rp2350")]
114use rp235x_hal as hal;
115
116use hal::pac;
117use hal::usb::UsbBus;
118
119// 由 PAC 提供中断属性宏;在 ARM / RP2350-RISCV 下都统一使用它
120#[cfg(any(
121    target_arch = "arm",
122    all(feature = "rp2350", target_arch = "riscv32", target_os = "none")
123))]
124use hal::pac::interrupt;
125
126// ==============================
127// 配置合法性检查
128// ==============================
129
130#[cfg(all(feature = "rp2040", feature = "rp2350"))]
131compile_error!("features `rp2040` and `rp2350` cannot be enabled at the same time");
132
133#[cfg(not(any(feature = "rp2040", feature = "rp2350")))]
134compile_error!("either feature `rp2040` or `rp2350` must be enabled");
135
136#[cfg(all(feature = "rp2040", not(target_arch = "arm")))]
137compile_error!("`rp2040` only supports ARM targets");
138
139#[cfg(all(
140    feature = "rp2350",
141    not(any(target_arch = "arm", all(target_arch = "riscv32", target_os = "none")))
142))]
143compile_error!("`rp2350` supports ARM or bare-metal riscv32 targets only");
144
145// ==============================
146
147const TX_BUF_SIZE: usize = 1024;
148const RX_BUF_SIZE: usize = 1024;
149
150/// 是否自动回显主机发来的数据
151/// 为后续 Modbus 调试保留:现在打开,后面可直接改成 false
152const AUTO_ECHO: bool = true;
153
154static USB_ALLOCATOR: StaticCell<UsbBusAllocator<UsbBus>> = StaticCell::new();
155static USB_CONSOLE: Mutex<RefCell<Option<RpUsbConsole>>> = Mutex::new(RefCell::new(None));
156static USB_INIT: AtomicBool = AtomicBool::new(false);
157
158#[cfg(feature = "rp2040")]
159const USB_CHIP_NAME: &str = "RP2040";
160#[cfg(feature = "rp2040")]
161const SERIAL_NUM: &str = "B8ED93AE";
162#[cfg(feature = "rp2040")]
163const USB_VID_PID: (u16, u16) = (0x2E8A, 0x0003);
164
165#[cfg(feature = "rp2350")]
166const USB_CHIP_NAME: &str = "RP2350";
167#[cfg(feature = "rp2350")]
168const SERIAL_NUM: &str = "A74D15D8";
169#[cfg(feature = "rp2350")]
170const USB_VID_PID: (u16, u16) = (0x2E8A, 0x10B0);
171
172pub struct RpUsbConsole {
173    device: UsbDevice<'static, UsbBus>,
174    serial: SerialPort<'static, UsbBus>,
175    tx_buf: Queue<u8, TX_BUF_SIZE>,
176    rx_buf: Queue<u8, RX_BUF_SIZE>,
177}
178
179impl RpUsbConsole {
180    pub fn init(
181        #[cfg(feature = "rp2040")] usbctrl_reg: pac::USBCTRL_REGS,
182        #[cfg(feature = "rp2350")] usbctrl_reg: pac::USB,
183        #[cfg(feature = "rp2040")] usbctrl_dpram: pac::USBCTRL_DPRAM,
184        #[cfg(feature = "rp2350")] usbctrl_dpram: pac::USB_DPRAM,
185        usb_reset: &mut pac::RESETS,
186        clocks: hal::clocks::UsbClock,
187    ) {
188        if USB_INIT.load(Ordering::SeqCst) {
189            return;
190        }
191
192        let usb_bus = UsbBus::new(usbctrl_reg, usbctrl_dpram, clocks, true, usb_reset);
193        let alloc = USB_ALLOCATOR.init(UsbBusAllocator::new(usb_bus));
194
195        let serial = SerialPort::new(alloc);
196        let device = UsbDeviceBuilder::new(alloc, UsbVidPid(USB_VID_PID.0, USB_VID_PID.1))
197            .strings(&[StringDescriptors::default()
198                .manufacturer("Raspberry Pi")
199                .product(USB_CHIP_NAME)
200                .serial_number(SERIAL_NUM)])
201            .unwrap()
202            .device_class(2)
203            .build();
204
205        critical_section::with(|cs| {
206            USB_CONSOLE.borrow(cs).replace(Some(Self {
207                device,
208                serial,
209                tx_buf: Queue::new(),
210                rx_buf: Queue::new(),
211            }));
212        });
213
214        USB_INIT.store(true, Ordering::SeqCst);
215    }
216
217    /// 轮询 USB:
218    /// 1. 推进 USB 协议
219    /// 2. 读取主机输入
220    /// 3. 放入 RX 缓冲
221    /// 4. 可选回显
222    /// 5. 刷新 TX 发送
223    pub fn poll() {
224        Self::force_poll();
225        Self::pump_rx_echo();
226        Self::flush_tx();
227    }
228
229    /// 强制轮询 USB 协议
230    #[inline(always)]
231    fn force_poll() {
232        if !USB_INIT.load(Ordering::SeqCst) {
233            return;
234        }
235
236        critical_section::with(|cs| {
237            let mut opt = USB_CONSOLE.borrow(cs).borrow_mut();
238            if let Some(usb) = opt.as_mut() {
239                usb.device.poll(&mut [&mut usb.serial]);
240            }
241        });
242    }
243
244    /// 从 USB 端点读取数据,写入 RX 队列,并按需回显到 TX 队列
245    fn pump_rx_echo() {
246        if !USB_INIT.load(Ordering::SeqCst) {
247            return;
248        }
249
250        critical_section::with(|cs| {
251            let mut opt = USB_CONSOLE.borrow(cs).borrow_mut();
252            let Some(usb) = opt.as_mut() else { return };
253
254            if usb.device.state() != UsbDeviceState::Configured {
255                return;
256            }
257
258            let mut temp = [0u8; 64];
259
260            loop {
261                match usb.serial.read(&mut temp) {
262                    Ok(0) => break,
263                    Ok(n) => {
264                        for &b in &temp[..n] {
265                            // 保存到 RX 缓冲,供上层协议读取
266                            let _ = usb.rx_buf.enqueue(b);
267
268                            // 自动回显:收到什么就回什么
269                            if AUTO_ECHO {
270                                let _ = usb.tx_buf.enqueue(b);
271                            }
272                        }
273                    }
274                    Err(usb_device::UsbError::WouldBlock) => break,
275                    Err(_) => break,
276                }
277            }
278        });
279    }
280
281    /// 自动发送缓冲数据
282    fn flush_tx() {
283        if !USB_INIT.load(Ordering::SeqCst) {
284            return;
285        }
286
287        critical_section::with(|cs| {
288            let mut opt = USB_CONSOLE.borrow(cs).borrow_mut();
289            let Some(usb) = opt.as_mut() else { return };
290
291            if usb.device.state() != UsbDeviceState::Configured {
292                return;
293            }
294
295            while let Some(b) = usb.tx_buf.dequeue() {
296                if usb.serial.write(&[b]).is_err() {
297                    let _ = usb.tx_buf.enqueue(b);
298                    break;
299                }
300            }
301        });
302    }
303
304    /// 写数据到发送队列,并立即尝试发送
305    pub fn write(data: &[u8]) {
306        if !USB_INIT.load(Ordering::SeqCst) {
307            return;
308        }
309
310        critical_section::with(|cs| {
311            let mut binding = USB_CONSOLE.borrow(cs).borrow_mut();
312            if let Some(usb) = binding.as_mut() {
313                for &b in data {
314                    let _ = usb.tx_buf.enqueue(b);
315                }
316            }
317        });
318
319        // 发送后顺便做一次完整轮询
320        Self::poll();
321    }
322
323    pub fn print(s: &str) {
324        Self::write(s.as_bytes());
325    }
326
327    pub fn println(s: &str) {
328        Self::print(s);
329        Self::write(b"\r\n");
330    }
331
332    pub fn fmt_write(args: core::fmt::Arguments<'_>) {
333        struct Wrapper;
334
335        impl Write for Wrapper {
336            fn write_str(&mut self, s: &str) -> core::fmt::Result {
337                RpUsbConsole::print(s);
338                Ok(())
339            }
340        }
341
342        let _ = Wrapper.write_fmt(args);
343    }
344
345    /// 从内部 RX 缓冲读取数据
346    /// 注意:现在 read() 不再直接访问 USB 端点,而是读取 poll() 已缓存的数据
347    pub fn read(buf: &mut [u8]) -> usize {
348        if !USB_INIT.load(Ordering::SeqCst) {
349            return 0;
350        }
351
352        critical_section::with(|cs| {
353            let mut opt = USB_CONSOLE.borrow(cs).borrow_mut();
354            let Some(usb) = opt.as_mut() else { return 0 };
355
356            let mut n = 0;
357            while n < buf.len() {
358                match usb.rx_buf.dequeue() {
359                    Some(b) => {
360                        buf[n] = b;
361                        n += 1;
362                    }
363                    None => break,
364                }
365            }
366            n
367        })
368    }
369}
370
371// ==============================
372// 中断公共处理逻辑
373// ==============================
374
375#[inline(always)]
376fn usbctrl_irq_impl() {
377    RpUsbConsole::poll();
378}
379
380// ARM: RP2040 / RP2350-ARM
381#[cfg(target_arch = "arm")]
382#[interrupt]
383fn USBCTRL_IRQ() {
384    usbctrl_irq_impl();
385}
386
387// RISC-V: RP2350-Hazard3
388#[cfg(all(feature = "rp2350", target_arch = "riscv32", target_os = "none"))]
389#[no_mangle]
390pub extern "C" fn USBCTRL_IRQ() {
391    usbctrl_irq_impl();
392}
393
394// ==============================
395// 打印宏
396// ==============================
397
398#[macro_export]
399macro_rules! usb_print {
400    ($($tt:tt)*) => {
401        $crate::RpUsbConsole::fmt_write(format_args!($($tt)*));
402    };
403}
404
405#[macro_export]
406macro_rules! usb_println {
407    ($($tt:tt)*) => {
408        $crate::RpUsbConsole::fmt_write(format_args!($($tt)*));
409        $crate::RpUsbConsole::write(b"\r\n");
410    };
411}