simple picorom interface

This commit is contained in:
2025-11-16 20:57:31 +01:00
parent a2bc2232c0
commit f7d3c4c633
4 changed files with 511 additions and 16 deletions

View File

@@ -8,9 +8,14 @@
//! GP26 => CE
//! GP27 => OE
use core::cell::Cell;
mod serial;
use cortex_m::{delay::Delay, interrupt::Mutex};
use core::{
cell::Cell,
sync::atomic::{AtomicU8, Ordering},
};
use cortex_m::{delay::Delay, interrupt::Mutex, singleton};
use defmt_rtt as _;
use embedded_hal::digital::OutputPin;
use panic_probe as _;
@@ -21,11 +26,16 @@ use rp_pico::{
Sio, Watchdog,
clocks::init_clocks_and_plls,
gpio::{FunctionPio0, PullNone},
pio::{PIO0SM0, PIO0SM3, PIOBuilder, PioIRQ, Rx, ShiftDirection, Tx},
pio::{PIO0SM0, PIO0SM3, PIOBuilder, PioIRQ, Rx, Tx},
prelude::*,
usb::UsbBus,
},
pac::{self, CorePeripherals, Peripherals, interrupt},
};
use usb_device::{class_prelude::*, prelude::*};
use usbd_serial::SerialPort;
use crate::serial::{Link, PacketBuilder};
const ADDRESS_BASE: u8 = 0;
const ADDRESS_PINS: u8 = 15;
@@ -35,13 +45,19 @@ const CE: u8 = 26;
#[allow(unused)]
const OE: u8 = 27;
const ROM_SIZE: usize = 1024 * 32;
static GLOBAL_ADDRESS_RX: Mutex<Cell<Option<Rx<PIO0SM3>>>> = Mutex::new(Cell::new(None));
static GLOBAL_DATA_TX: Mutex<Cell<Option<Tx<PIO0SM0>>>> = Mutex::new(Cell::new(None));
static ROM_DATA: [AtomicU8; ROM_SIZE] = [const { AtomicU8::new(0) }; ROM_SIZE];
static GLOBAL_USB_DEVICE: Mutex<Cell<Option<UsbDevice<UsbBus>>>> = Mutex::new(Cell::new(None));
static GLOBAL_USB_SERIAL: Mutex<Cell<Option<SerialPort<UsbBus>>>> = Mutex::new(Cell::new(None));
#[entry]
fn main() -> ! {
let mut pac = Peripherals::take().unwrap();
let core = CorePeripherals::take().unwrap();
let mut core = CorePeripherals::take().unwrap();
let mut watchdog = Watchdog::new(pac.WATCHDOG);
@@ -195,8 +211,37 @@ fn main() -> ! {
address_rx.enable_rx_not_empty_interrupt(PioIRQ::Irq0);
cortex_m::interrupt::free(|cs| GLOBAL_ADDRESS_RX.borrow(cs).set(Some(address_rx)));
let usbctrl_regs = pac.USBCTRL_REGS;
let usbctrl_dpram = pac.USBCTRL_DPRAM;
let resets = &mut pac.RESETS;
let usb_bus = singleton!(: UsbBusAllocator<UsbBus> = UsbBusAllocator::new(UsbBus::new(
usbctrl_regs,
usbctrl_dpram,
clocks.usb_clock,
true,
resets,
)))
.unwrap();
let serial = SerialPort::new(usb_bus);
cortex_m::interrupt::free(|cs| GLOBAL_USB_SERIAL.borrow(cs).set(Some(serial)));
let usb_dev = UsbDeviceBuilder::new(usb_bus, UsbVidPid(0x2e8a, 0x000a))
.strings(&[StringDescriptors::default()
.manufacturer("Max Känner")
.product("PicoROM.rs")
.serial_number("1")])
.unwrap()
.device_class(2)
.build();
cortex_m::interrupt::free(|cs| GLOBAL_USB_DEVICE.borrow(cs).set(Some(usb_dev)));
unsafe {
// Highest priority for pio
core.NVIC.set_priority(pac::interrupt::PIO0_IRQ_0, 0x00);
// lowest priority for usb
core.NVIC.set_priority(pac::interrupt::USBCTRL_IRQ, 0xFF);
pac::NVIC::unmask(pac::interrupt::PIO0_IRQ_0);
pac::NVIC::unmask(pac::interrupt::USBCTRL_IRQ);
}
loop {
@@ -220,12 +265,86 @@ fn PIO0_IRQ_0() {
}
if let Some(address) = ADDRESS {
while let Some(address) = address.read() {
let address = (address & 0x7fff) as u16;
if let Some(data) = DATA {
defmt::trace!("replying with {:#04x} @ {:#06x}", address as u8, address);
data.write(u32::from(address));
while let Some(rx) = address.read() {
let address = (rx & 0x7fff) as u16;
if let Some(tx) = DATA {
let data = ROM_DATA[usize::from(address)].load(Ordering::Relaxed);
defmt::trace!("replying with {:#04x} @ {:#06x}", data, address);
tx.write(u32::from(data));
}
}
}
}
#[interrupt]
fn USBCTRL_IRQ() {
static mut USB_DEVICE: Option<UsbDevice<UsbBus>> = None;
static mut USB_SERIAL: Option<SerialPort<UsbBus>> = None;
static mut DO_PREAMBLE: bool = true;
static mut PACKET_BUILDER: PacketBuilder = PacketBuilder::new();
static mut LINK: Link = Link::new();
const PREAMBLE: &[u8] = b"PicoROM Hello";
if USB_DEVICE.is_none() {
*USB_DEVICE = cortex_m::interrupt::free(|cs| GLOBAL_USB_DEVICE.borrow(cs).take());
}
if USB_SERIAL.is_none() {
*USB_SERIAL = cortex_m::interrupt::free(|cs| GLOBAL_USB_SERIAL.borrow(cs).take());
}
let Some(usb_dev) = USB_DEVICE else { return };
let Some(serial) = USB_SERIAL else { return };
if !serial.rts() {
*DO_PREAMBLE = true;
}
if *DO_PREAMBLE && serial.rts() {
match serial.write(PREAMBLE) {
Ok(count) if count == PREAMBLE.len() => {
*DO_PREAMBLE = false;
defmt::info!("Send Preamble")
}
Ok(count) => {
defmt::warn!(
"Unable to write the whole preamble. Only wrote {} Bytes",
count
);
}
Err(UsbError::WouldBlock) => (),
Err(e) => {
defmt::error!("Unable to write preamble: {}", e)
}
}
}
if usb_dev.poll(&mut [serial]) {
let mut buf = [0u8; 64];
match serial.read(&mut buf) {
Ok(0) => {}
Ok(count) => {
for byte in buf.into_iter().take(count) {
if let Some(packet) = PACKET_BUILDER.push(byte) {
defmt::debug!("Got packet: {}", packet);
if let Some(response) = LINK.handle_packet(&packet) {
defmt::info!("Sending Response: {}", response);
if let Err(e) = response.send(serial)
&& e != UsbError::WouldBlock
{
defmt::error!("Unable to send response: {}", e);
}
}
}
}
}
Err(UsbError::WouldBlock) => (),
Err(e) => {
defmt::error!("USB Error: {}", e);
}
}
}
if usb_dev.state() == UsbDeviceState::Default {
*DO_PREAMBLE = true;
}
}