Initial commit
This commit is contained in:
47
.cargo/config.toml
Normal file
47
.cargo/config.toml
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
#
|
||||||
|
# Cargo Configuration for the https://github.com/rp-rs/rp-hal.git repository.
|
||||||
|
#
|
||||||
|
# Copyright (c) The RP-RS Developers, 2021
|
||||||
|
#
|
||||||
|
# You might want to make a similar file in your own repository if you are
|
||||||
|
# writing programs for Raspberry Silicon microcontrollers.
|
||||||
|
#
|
||||||
|
# This file is MIT or Apache-2.0 as per the repository README.md file
|
||||||
|
#
|
||||||
|
|
||||||
|
[build]
|
||||||
|
# Set the default target to match the Cortex-M0+ in the RP2040
|
||||||
|
target = "thumbv6m-none-eabi"
|
||||||
|
|
||||||
|
# Target specific options
|
||||||
|
[target.thumbv6m-none-eabi]
|
||||||
|
# Pass some extra options to rustc, some of which get passed on to the linker.
|
||||||
|
#
|
||||||
|
# * linker argument --nmagic turns off page alignment of sections (which saves
|
||||||
|
# flash space)
|
||||||
|
# * linker argument -Tlink.x tells the linker to use link.x as the linker
|
||||||
|
# script. This is usually provided by the cortex-m-rt crate, and by default
|
||||||
|
# the version in that crate will include a file called `memory.x` which
|
||||||
|
# describes the particular memory layout for your specific chip.
|
||||||
|
# * no-vectorize-loops turns off the loop vectorizer (seeing as the M0+ doesn't
|
||||||
|
# have SIMD)
|
||||||
|
rustflags = [
|
||||||
|
"-C",
|
||||||
|
"linker=flip-link",
|
||||||
|
"-C",
|
||||||
|
"link-arg=--nmagic",
|
||||||
|
"-C",
|
||||||
|
"link-arg=-Tlink.x",
|
||||||
|
"-C",
|
||||||
|
"link-arg=-Tdefmt.x",
|
||||||
|
"-C",
|
||||||
|
"no-vectorize-loops",
|
||||||
|
]
|
||||||
|
|
||||||
|
# This runner will make a UF2 file and then copy it to a mounted RP2040 in USB
|
||||||
|
# Bootloader mode:
|
||||||
|
# runner = "elf2uf2-rs -d"
|
||||||
|
|
||||||
|
# This runner will find a supported SWD debug probe and flash your RP2040 over
|
||||||
|
# SWD:
|
||||||
|
runner = "probe-rs run --chip RP2040"
|
||||||
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target
|
||||||
1126
Cargo.lock
generated
Normal file
1126
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
21
Cargo.toml
Normal file
21
Cargo.toml
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
[package]
|
||||||
|
name = "picorom-rs"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "picorom-rs"
|
||||||
|
test = false
|
||||||
|
bench = false
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
cortex-m = "0.7"
|
||||||
|
cortex-m-rt = "0.7"
|
||||||
|
panic-probe = { version = "1.0", features = ["print-defmt", "defmt-error"] }
|
||||||
|
rp-pico = "0.9"
|
||||||
|
embedded-hal = "1.0"
|
||||||
|
pio = "0.2"
|
||||||
|
pio-proc = "0.2"
|
||||||
|
|
||||||
|
defmt = "1.0"
|
||||||
|
defmt-rtt = "1.1"
|
||||||
14
build.rs
Normal file
14
build.rs
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
use std::fs::File;
|
||||||
|
use std::io::Write;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// Put the linker script somewhere the linker can find it
|
||||||
|
let out = PathBuf::from(std::env::var_os("OUT_DIR").unwrap());
|
||||||
|
let memory_x = include_bytes!("memory.x");
|
||||||
|
let mut f = File::create(out.join("memory.x")).unwrap();
|
||||||
|
f.write_all(memory_x).unwrap();
|
||||||
|
println!("cargo:rustc-link-search={}", out.display());
|
||||||
|
println!("cargo:rerun-if-changed=build.rs");
|
||||||
|
println!("cargo:rerun-if-changed=memory.x");
|
||||||
|
}
|
||||||
24
memory.x
Normal file
24
memory.x
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
MEMORY {
|
||||||
|
BOOT2 : ORIGIN = 0x10000000, LENGTH = 0x100
|
||||||
|
FLASH : ORIGIN = ORIGIN(BOOT2) + LENGTH(BOOT2), LENGTH = 2M - LENGTH(BOOT2) - 32K
|
||||||
|
ROM : ORIGIN = ORIGIN(FLASH) + LENGTH(FLASH), LENGTH = 32K
|
||||||
|
RAM : ORIGIN = 0x20000000, LENGTH = 256K
|
||||||
|
}
|
||||||
|
|
||||||
|
EXTERN(BOOT2_FIRMWARE)
|
||||||
|
|
||||||
|
SECTIONS {
|
||||||
|
/* ### Boot loader */
|
||||||
|
.boot2 ORIGIN(BOOT2) :
|
||||||
|
{
|
||||||
|
KEEP(*(.boot2));
|
||||||
|
} > BOOT2
|
||||||
|
} INSERT BEFORE .text;
|
||||||
|
|
||||||
|
SECTIONS {
|
||||||
|
/* ### Boot loader */
|
||||||
|
.romdata ORIGIN(ROM) :
|
||||||
|
{
|
||||||
|
KEEP(*(.romdata));
|
||||||
|
} > ROM
|
||||||
|
} INSERT AFTER .bss;
|
||||||
3
rust-toolchain.toml
Normal file
3
rust-toolchain.toml
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
[toolchain]
|
||||||
|
targets = ["thumbv6m-none-eabi"]
|
||||||
|
channel = "stable"
|
||||||
231
src/main.rs
Normal file
231
src/main.rs
Normal file
@@ -0,0 +1,231 @@
|
|||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
|
||||||
|
//! # Pinout
|
||||||
|
//!
|
||||||
|
//! GP0..GP14 => A0..A14
|
||||||
|
//! GP15..GP22 => D0..D7
|
||||||
|
//! GP26 => CE
|
||||||
|
//! GP27 => OE
|
||||||
|
|
||||||
|
use core::cell::Cell;
|
||||||
|
|
||||||
|
use cortex_m::{delay::Delay, interrupt::Mutex};
|
||||||
|
use defmt_rtt as _;
|
||||||
|
use embedded_hal::digital::OutputPin;
|
||||||
|
use panic_probe as _;
|
||||||
|
use pio_proc::pio_asm;
|
||||||
|
use rp_pico::{
|
||||||
|
Pins, XOSC_CRYSTAL_FREQ, entry,
|
||||||
|
hal::{
|
||||||
|
Sio, Watchdog,
|
||||||
|
clocks::init_clocks_and_plls,
|
||||||
|
gpio::{FunctionPio0, PullNone},
|
||||||
|
pio::{PIO0SM0, PIO0SM3, PIOBuilder, PioIRQ, Rx, ShiftDirection, Tx},
|
||||||
|
prelude::*,
|
||||||
|
},
|
||||||
|
pac::{self, CorePeripherals, Peripherals, interrupt},
|
||||||
|
};
|
||||||
|
|
||||||
|
const ADDRESS_BASE: u8 = 0;
|
||||||
|
const ADDRESS_PINS: u8 = 15;
|
||||||
|
const DATA_BASE: u8 = 15;
|
||||||
|
const DATA_PINS: u8 = 8;
|
||||||
|
const CE: u8 = 26;
|
||||||
|
#[allow(unused)]
|
||||||
|
const OE: u8 = 27;
|
||||||
|
|
||||||
|
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));
|
||||||
|
|
||||||
|
#[entry]
|
||||||
|
fn main() -> ! {
|
||||||
|
let mut pac = Peripherals::take().unwrap();
|
||||||
|
let core = CorePeripherals::take().unwrap();
|
||||||
|
|
||||||
|
let mut watchdog = Watchdog::new(pac.WATCHDOG);
|
||||||
|
|
||||||
|
let clocks = init_clocks_and_plls(
|
||||||
|
XOSC_CRYSTAL_FREQ,
|
||||||
|
pac.XOSC,
|
||||||
|
pac.CLOCKS,
|
||||||
|
pac.PLL_SYS,
|
||||||
|
pac.PLL_USB,
|
||||||
|
&mut pac.RESETS,
|
||||||
|
&mut watchdog,
|
||||||
|
)
|
||||||
|
.ok()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let mut delay = Delay::new(core.SYST, clocks.system_clock.freq().to_Hz());
|
||||||
|
|
||||||
|
let sio = Sio::new(pac.SIO);
|
||||||
|
|
||||||
|
let pins = Pins::new(
|
||||||
|
pac.IO_BANK0,
|
||||||
|
pac.PADS_BANK0,
|
||||||
|
sio.gpio_bank0,
|
||||||
|
&mut pac.RESETS,
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut led_pin = pins.led.into_push_pull_output();
|
||||||
|
|
||||||
|
// set address pins as schmitt-trigger inputs
|
||||||
|
pins.gpio0
|
||||||
|
.reconfigure::<FunctionPio0, PullNone>()
|
||||||
|
.set_schmitt_enabled(true);
|
||||||
|
pins.gpio1
|
||||||
|
.reconfigure::<FunctionPio0, PullNone>()
|
||||||
|
.set_schmitt_enabled(true);
|
||||||
|
pins.gpio2
|
||||||
|
.reconfigure::<FunctionPio0, PullNone>()
|
||||||
|
.set_schmitt_enabled(true);
|
||||||
|
pins.gpio3
|
||||||
|
.reconfigure::<FunctionPio0, PullNone>()
|
||||||
|
.set_schmitt_enabled(true);
|
||||||
|
pins.gpio4
|
||||||
|
.reconfigure::<FunctionPio0, PullNone>()
|
||||||
|
.set_schmitt_enabled(true);
|
||||||
|
pins.gpio5
|
||||||
|
.reconfigure::<FunctionPio0, PullNone>()
|
||||||
|
.set_schmitt_enabled(true);
|
||||||
|
pins.gpio6
|
||||||
|
.reconfigure::<FunctionPio0, PullNone>()
|
||||||
|
.set_schmitt_enabled(true);
|
||||||
|
pins.gpio7
|
||||||
|
.reconfigure::<FunctionPio0, PullNone>()
|
||||||
|
.set_schmitt_enabled(true);
|
||||||
|
pins.gpio8
|
||||||
|
.reconfigure::<FunctionPio0, PullNone>()
|
||||||
|
.set_schmitt_enabled(true);
|
||||||
|
pins.gpio9
|
||||||
|
.reconfigure::<FunctionPio0, PullNone>()
|
||||||
|
.set_schmitt_enabled(true);
|
||||||
|
pins.gpio10
|
||||||
|
.reconfigure::<FunctionPio0, PullNone>()
|
||||||
|
.set_schmitt_enabled(true);
|
||||||
|
pins.gpio11
|
||||||
|
.reconfigure::<FunctionPio0, PullNone>()
|
||||||
|
.set_schmitt_enabled(true);
|
||||||
|
pins.gpio12
|
||||||
|
.reconfigure::<FunctionPio0, PullNone>()
|
||||||
|
.set_schmitt_enabled(true);
|
||||||
|
pins.gpio13
|
||||||
|
.reconfigure::<FunctionPio0, PullNone>()
|
||||||
|
.set_schmitt_enabled(true);
|
||||||
|
pins.gpio14
|
||||||
|
.reconfigure::<FunctionPio0, PullNone>()
|
||||||
|
.set_schmitt_enabled(true);
|
||||||
|
// set data pins as inputs as we don't know if the output should be enabled yet
|
||||||
|
pins.gpio15.reconfigure::<FunctionPio0, PullNone>();
|
||||||
|
pins.gpio16.reconfigure::<FunctionPio0, PullNone>();
|
||||||
|
pins.gpio17.reconfigure::<FunctionPio0, PullNone>();
|
||||||
|
pins.gpio18.reconfigure::<FunctionPio0, PullNone>();
|
||||||
|
pins.gpio19.reconfigure::<FunctionPio0, PullNone>();
|
||||||
|
pins.gpio20.reconfigure::<FunctionPio0, PullNone>();
|
||||||
|
pins.gpio21.reconfigure::<FunctionPio0, PullNone>();
|
||||||
|
pins.gpio22.reconfigure::<FunctionPio0, PullNone>();
|
||||||
|
// set CE and OE as schmitt-triger inputs
|
||||||
|
pins.gpio26
|
||||||
|
.reconfigure::<FunctionPio0, PullNone>()
|
||||||
|
.set_schmitt_enabled(true);
|
||||||
|
pins.gpio27
|
||||||
|
.reconfigure::<FunctionPio0, PullNone>()
|
||||||
|
.set_schmitt_enabled(true);
|
||||||
|
|
||||||
|
// Pull from fifo and write to all data pins
|
||||||
|
let data = pio_asm!(".wrap_target", "out pins, 8", ".wrap");
|
||||||
|
// OR CE and OE and set 4 pindirs
|
||||||
|
let output_enable = pio_asm!(
|
||||||
|
".origin 0",
|
||||||
|
".side_set 4 pindirs",
|
||||||
|
"mov pc, pins side 0xf",
|
||||||
|
"mov pc, pins side 0x0",
|
||||||
|
"mov pc, pins side 0x0",
|
||||||
|
"mov pc, pins side 0x0"
|
||||||
|
);
|
||||||
|
// report pin activity to the cpu
|
||||||
|
let report_data = pio_asm!(
|
||||||
|
"wait_oe:",
|
||||||
|
"mov y, pins", // read all pins
|
||||||
|
"mov osr, y", // move into the osr so we can shift them out
|
||||||
|
"out null, 26", // skipp all pins until CE
|
||||||
|
"out x, 2", // put CE and OE into x
|
||||||
|
"jmp x--, wait_oe", // if any of CE and OE are set go back to the start
|
||||||
|
"mov isr, y", // put the address into the isr
|
||||||
|
"push",
|
||||||
|
".wrap_target",
|
||||||
|
"mov x, pins", // read all pins
|
||||||
|
"jmp x!=y wait_oe", // if any pins have changed, restart and check oe again
|
||||||
|
".wrap"
|
||||||
|
);
|
||||||
|
|
||||||
|
let (mut pio, sm0, sm1, sm2, sm3) = pac.PIO0.split(&mut pac.RESETS);
|
||||||
|
|
||||||
|
let data = pio.install(&data.program).unwrap();
|
||||||
|
let output_enable = pio.install(&output_enable.program).unwrap();
|
||||||
|
let address = pio.install(&report_data.program).unwrap();
|
||||||
|
|
||||||
|
let (data_sm, _, data_tx) = PIOBuilder::from_installed_program(data)
|
||||||
|
.out_pins(DATA_BASE, DATA_PINS)
|
||||||
|
.pull_threshold(DATA_PINS)
|
||||||
|
.autopull(true)
|
||||||
|
.build(sm0);
|
||||||
|
let _data_sm = data_sm.start();
|
||||||
|
cortex_m::interrupt::free(|cs| GLOBAL_DATA_TX.borrow(cs).set(Some(data_tx)));
|
||||||
|
|
||||||
|
let (output_enable_low_sm, _, _) =
|
||||||
|
PIOBuilder::from_installed_program(unsafe { output_enable.share() })
|
||||||
|
.side_set_pin_base(DATA_BASE)
|
||||||
|
.in_pin_base(CE)
|
||||||
|
.build(sm1);
|
||||||
|
let _output_enable_low_sm = output_enable_low_sm.start();
|
||||||
|
let (output_enable_high_sm, _, _) = PIOBuilder::from_installed_program(output_enable)
|
||||||
|
.side_set_pin_base(DATA_BASE + 4)
|
||||||
|
.in_pin_base(CE)
|
||||||
|
.build(sm2);
|
||||||
|
let _output_enable_high_sm = output_enable_high_sm.start();
|
||||||
|
|
||||||
|
let (address_sm, address_rx, _) = PIOBuilder::from_installed_program(address)
|
||||||
|
.in_pin_base(ADDRESS_BASE)
|
||||||
|
.push_threshold(ADDRESS_PINS)
|
||||||
|
.autopush(true)
|
||||||
|
.build(sm3);
|
||||||
|
let _address_sm = address_sm.start();
|
||||||
|
address_rx.enable_rx_not_empty_interrupt(PioIRQ::Irq0);
|
||||||
|
cortex_m::interrupt::free(|cs| GLOBAL_ADDRESS_RX.borrow(cs).set(Some(address_rx)));
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
pac::NVIC::unmask(pac::interrupt::PIO0_IRQ_0);
|
||||||
|
}
|
||||||
|
|
||||||
|
loop {
|
||||||
|
led_pin.set_high().unwrap();
|
||||||
|
delay.delay_ms(500);
|
||||||
|
led_pin.set_low().unwrap();
|
||||||
|
delay.delay_ms(500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[interrupt]
|
||||||
|
fn PIO0_IRQ_0() {
|
||||||
|
static mut ADDRESS: Option<Rx<PIO0SM3>> = None;
|
||||||
|
static mut DATA: Option<Tx<PIO0SM0>> = None;
|
||||||
|
|
||||||
|
if ADDRESS.is_none() {
|
||||||
|
*ADDRESS = cortex_m::interrupt::free(|cs| GLOBAL_ADDRESS_RX.borrow(cs).take());
|
||||||
|
}
|
||||||
|
if DATA.is_none() {
|
||||||
|
*DATA = cortex_m::interrupt::free(|cs| GLOBAL_DATA_TX.borrow(cs).take());
|
||||||
|
}
|
||||||
|
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user