FIRMWARE WORKING

This commit is contained in:
Naxdy 2023-03-22 17:16:13 +01:00
commit a091567aa2
No known key found for this signature in database
GPG key ID: C0437AAE9755550F
13 changed files with 1689 additions and 0 deletions

28
.cargo/config.toml Normal file
View file

@ -0,0 +1,28 @@
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
# Choose a default "cargo run" tool:
# - probe-run provides flashing and defmt via a hardware debugger, and stack unwind on panic
# - elf2uf2-rs loads firmware over USB when the rp2040 is in boot mode
# - "probe-rs-cli run" is similar to probe-run but it uses the latest probe-rs lib crate
runner = "probe-run --chip RP2040"
# runner = "elf2uf2-rs -d"
# runner = "probe-rs-cli run --chip RP2040 --protocol swd"
rustflags = [
"-C", "linker=flip-link",
"-C", "link-arg=--nmagic",
"-C", "link-arg=-Tlink.x",
"-C", "link-arg=-Tdefmt.x",
# Code-size optimizations.
# trap unreachable can save a lot of space, but requires nightly compiler.
# uncomment the next line if you wish to enable it
# "-Z", "trap-unreachable=no",
"-C", "inline-threshold=5",
"-C", "no-vectorize-loops",
]
[target.thumbv6m-none-eabi]
runner = "elf2uf2-rs -d"
[env]
DEFMT_LOG = "debug"

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
target/

1001
Cargo.lock generated Normal file

File diff suppressed because it is too large Load diff

82
Cargo.toml Normal file
View file

@ -0,0 +1,82 @@
[package]
name = "rustgcc"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
cortex-m = "0.7.7"
cortex-m-rt = "0.7.3"
embedded-hal = { version = "0.2.7", features = ["unproven"] }
defmt = "0.3.2"
defmt-rtt = "0.4.0"
panic-probe = { version = "0.3.0", features = ["print-defmt"] }
# We're using a Pico by default on this template
rp-pico = "0.7.0"
rp2040-hal = { version="0.8", features=["rt", "critical-section-impl"] }
pio-proc = "0.2"
pio = "0.2"
# but you can use any BSP. Uncomment this to use the pro_micro_rp2040 BSP instead
# sparkfun-pro-micro-rp2040 = "0.3"
# If you're not going to use a Board Support Package you'll need these:
# rp2040-hal = { version="0.8", features=["rt", "critical-section-impl"] }
# rp2040-boot2 = "0.2"
# cargo build/run
[profile.dev]
codegen-units = 1
debug = 2
debug-assertions = true
incremental = false
opt-level = 3
overflow-checks = true
# cargo build/run --release
[profile.release]
codegen-units = 1
debug = 2
debug-assertions = false
incremental = false
lto = 'fat'
opt-level = 3
overflow-checks = false
strip = true
# do not optimize proc-macro crates = faster builds from scratch
[profile.dev.build-override]
codegen-units = 8
debug = false
debug-assertions = false
opt-level = 0
overflow-checks = false
[profile.release.build-override]
codegen-units = 8
debug = false
debug-assertions = false
opt-level = 0
overflow-checks = false
# cargo test
[profile.test]
codegen-units = 1
debug = 2
debug-assertions = true
incremental = false
opt-level = 3
overflow-checks = true
# cargo test --release
[profile.bench]
codegen-units = 1
debug = 2
debug-assertions = false
incremental = false
lto = 'fat'
opt-level = 3

39
Embed.toml Normal file
View file

@ -0,0 +1,39 @@
[default.probe]
protocol = "Swd"
speed = 20000
# If you only have one probe cargo embed will pick automatically
# Otherwise: add your probe's VID/PID/serial to filter
## rust-dap
# usb_vid = "6666"
# usb_pid = "4444"
# serial = "test"
[default.flashing]
enabled = true
[default.reset]
enabled = true
halt_afterwards = false
[default.general]
chip = "RP2040"
log_level = "WARN"
# RP2040 does not support connect_under_reset
connect_under_reset = false
[default.rtt]
enabled = true
up_mode = "NoBlockSkip"
channels = [
{ up = 0, down = 0, name = "name", up_mode = "NoBlockSkip", format = "Defmt" },
]
timeout = 3000
show_timestamps = true
log_enabled = false
log_path = "./logs"
[default.gdb]
enabled = false
gdb_connection_string = "127.0.0.1:2345"

31
build.rs Normal file
View file

@ -0,0 +1,31 @@
//! This build script copies the `memory.x` file from the crate root into
//! a directory where the linker can always find it at build time.
//! For many projects this is optional, as the linker always searches the
//! project root directory -- wherever `Cargo.toml` is. However, if you
//! are using a workspace or have a more complicated build setup, this
//! build script becomes required. Additionally, by requesting that
//! Cargo re-run the build script whenever `memory.x` is changed,
//! updating `memory.x` ensures a rebuild of the application with the
//! new memory settings.
use std::env;
use std::fs::File;
use std::io::Write;
use std::path::PathBuf;
fn main() {
// Put `memory.x` in our output directory and ensure it's
// on the linker search path.
let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
File::create(out.join("memory.x"))
.unwrap()
.write_all(include_bytes!("memory.x"))
.unwrap();
println!("cargo:rustc-link-search={}", out.display());
// By default, Cargo will re-run a build script whenever
// any file in the project changes. By specifying `memory.x`
// here, we ensure the build script is only re-run when
// `memory.x` is changed.
println!("cargo:rerun-if-changed=memory.x");
}

15
memory.x Normal file
View file

@ -0,0 +1,15 @@
MEMORY {
BOOT2 : ORIGIN = 0x10000000, LENGTH = 0x100
FLASH : ORIGIN = 0x10000100, LENGTH = 2048K - 0x100
RAM : ORIGIN = 0x20000000, LENGTH = 256K
}
EXTERN(BOOT2_FIRMWARE)
SECTIONS {
/* ### Boot loader */
.boot2 ORIGIN(BOOT2) :
{
KEEP(*(.boot2));
} > BOOT2
} INSERT BEFORE .text;

30
pio/joybus.pio Normal file
View file

@ -0,0 +1,30 @@
; THIS PIO PROGRAM EXPECTS THE SYSTEM CLOCK TO BE 125MHz
; Input is a succession of optional bools: (value/do we output something)
; This makes so we can output an arbitrary number of bits without worrying about sizes
; Useful since the end bit makes it so the output size is always = 1 [8]
; Clock is / 5 i.e 25MHz
; This will crash if fed an output of len == -1 [Z/8Z]
.program joybus ;
public inmode: ; Code must force a jump here once it's done using the out mode
set pindirs, 0 ;
inagain: ;
wait 0 pin 0 [31] ; After the instruction complete we wait 61 cycles and read on the 62th
nop [31] ;
in pins, 1 ;
wait 1 pin 0 ;
jmp inagain ;
;
public outmode: ; External code must force a jump here in reaction to the RX FIFO contents
set pins, 1 ;
set pindirs, 1 ;
outagain: ;
pull ifempty block ; On pull 1 et ensuite on l'utilise pour X ET Y ?
out x, 1 ; X = what should we output
out y, 1 ; Y = should we output something
jmp !y inmode ; if Y 0, empty the queue
set pins, 0 [ 24 ] ;
mov pins, x [ 24 ] ;
mov pins, x [ 24 ] ;
set pins, 1 [ 19 ] ;
jmp outagain

74
src/gccstate.rs Normal file
View file

@ -0,0 +1,74 @@
use core::ops::Deref;
#[derive(Debug, Clone, Copy)]
pub struct GccState {
pub a: bool,
pub b: bool,
pub x: bool,
pub y: bool,
pub start: bool,
pub dup: bool,
pub dleft: bool,
pub dright: bool,
pub ddown: bool,
pub l: bool,
pub r: bool,
pub z: bool,
pub lstickx: u8,
pub lsticky: u8,
pub rstickx: u8,
pub rsticky: u8,
}
impl GccState {
pub fn new() -> GccState {
GccState {
a: false,
b: false,
x: false,
y: false,
start: false,
dup: false,
dleft: true,
dright: false,
ddown: false,
l: false,
r: false,
z: true,
lstickx: 127,
lsticky: 127,
rstickx: 127,
rsticky: 127,
}
}
}
impl From<GccState> for [u8; 8] {
fn from(value: GccState) -> Self {
let mut response = [0; 8];
response[0] = (value.start as u8) << 4;
response[0] |= (value.y as u8) << 3;
response[0] |= (value.x as u8) << 2;
response[0] |= (value.b as u8) << 1;
response[0] |= (value.a as u8) << 0;
response[1] = 1 << 7;
response[1] |= (value.l as u8) << 6;
response[1] |= (value.r as u8) << 5;
response[1] |= (value.z as u8) << 4;
response[1] |= (value.dup as u8) << 3;
response[1] |= (value.ddown as u8) << 2;
response[1] |= (value.dright as u8) << 1;
response[1] |= (value.dleft as u8) << 0;
response[2] = value.lstickx;
response[3] = value.lsticky;
response[4] = value.rstickx;
response[5] = value.rsticky;
response[6] = value.l as u8 * 127;
response[7] = value.r as u8 * 127;
response
}
}

248
src/joybus.rs Normal file
View file

@ -0,0 +1,248 @@
use cortex_m::{delay::Delay, singleton};
use embedded_hal::digital::v2::{InputPin, OutputPin};
use rp_pico::{
hal::{
dma::{double_buffer, single_buffer, DMAExt},
gpio::{Pin, PinId, PinMode, Pins, ValidPinMode},
pio::{
InstalledProgram, Rx, StateMachine, StateMachineIndex, Stopped, Tx, UninitStateMachine,
ValidStateMachine, PIO, SM0,
},
prelude::_rphal_pio_PIOExt,
Sio,
},
pac::{self, Peripherals, PIO0, RESETS},
};
use crate::{
gccstate::GccState,
pio::{to_pio_response, PioResponse},
};
#[link_section = ".time_critical.init_state_machine"]
fn init_state_machine<PE, SM>(
pio: &mut PIO<PE>,
sm: UninitStateMachine<(PE, SM)>,
pin_id: u8,
) -> (
(StateMachine<(PE, SM), Stopped>, Rx<(PE, SM)>, Tx<(PE, SM)>),
u8,
i32,
i32,
)
where
PE: _rphal_pio_PIOExt,
SM: StateMachineIndex,
{
let program_with_defines =
pio_proc::pio_file!("./pio/joybus.pio", options(max_program_size = 32));
let program = program_with_defines.program;
let installed = pio.install(&program).unwrap();
let offset = installed.offset();
(
init_state_machine_with_installed_program(sm, pin_id, installed),
offset,
program_with_defines.public_defines.inmode,
program_with_defines.public_defines.outmode,
)
}
#[link_section = ".time_critical.init_state_machine_with_installed_program"]
fn init_state_machine_with_installed_program<PE, SM>(
sm: UninitStateMachine<(PE, SM)>,
pin_id: u8,
installed: InstalledProgram<PE>,
) -> (StateMachine<(PE, SM), Stopped>, Rx<(PE, SM)>, Tx<(PE, SM)>)
where
PE: _rphal_pio_PIOExt,
SM: StateMachineIndex,
{
let (sm, rx, tx) = rp_pico::hal::pio::PIOBuilder::from_program(installed)
.set_pins(pin_id, 1)
.in_shift_direction(rp_pico::hal::pio::ShiftDirection::Left)
.out_shift_direction(rp_pico::hal::pio::ShiftDirection::Right)
.pull_threshold(32)
.push_threshold(8)
.clock_divisor_fixed_point(5, 0)
.in_pin_base(pin_id)
.autopush(true)
.autopull(false)
.out_pins(pin_id, 1)
.build(sm);
(sm, rx, tx)
}
#[link_section = ".time_critical.datatransfer"]
pub fn datatransfer(
pins: rp_pico::Pins,
pio0: PIO0,
mut resets: &mut RESETS,
mut delay: Delay,
) -> ! {
let datapin = pins.gpio28.into_pull_up_input();
let datapin = datapin.into_mode::<rp2040_hal::gpio::pin::FunctionPio0>();
let (mut pio, sm0, _, _, _) = pio0.split(&mut resets);
delay.delay_us(100);
let ((mut sm, mut rx, mut tx), offset, offset_inmode, offset_outmode) =
init_state_machine(&mut pio, sm0, datapin.id().num);
sm.exec_instruction(pio::Instruction {
operands: pio::InstructionOperands::JMP {
condition: pio::JmpCondition::Always,
address: offset + offset_inmode as u8,
},
delay: 0,
side_set: None,
});
let mut s = sm.start();
let abutton = pins.gpio0.into_pull_up_input();
let mut ledpin = pins.led.into_push_pull_output();
loop {
let data = loop {
match rx.read() {
Some(x) => break x as u8,
None => {
continue;
}
}
};
if data == 0x40 {
let mut state = GccState::new();
state.a = !abutton.is_high().unwrap();
match state.a {
true => ledpin.set_high().unwrap(),
false => ledpin.set_low().unwrap(),
}
if state.a {
state.lstickx = 255;
}
let bitstate = state.into();
let pio_response: PioResponse<5> = to_pio_response(bitstate);
while rx.read().is_none() {}
let rumble = loop {
match rx.read() {
Some(x) => break x as u8,
None => continue,
}
};
delay.delay_us(8);
// let (st, installed) = s.uninit(rx, tx);
// (sm, rx, tx) =
// init_state_machine_with_installed_program(st, datapin.id().num, installed);
// sm.exec_instruction(pio::Instruction {
// operands: pio::InstructionOperands::JMP {
// condition: pio::JmpCondition::Always,
// address: offset + offset_outmode as u8,
// },
// delay: 0,
// side_set: None,
// });
// s = sm.start();
let mut st = s.stop();
st.exec_instruction(pio::Instruction {
operands: pio::InstructionOperands::JMP {
condition: pio::JmpCondition::Always,
address: offset + offset_outmode as u8,
},
delay: 0,
side_set: None,
});
s = st.start();
for i in 0..pio_response.len() {
while !tx.write((*pio_response)[i]) {}
}
// if rumble & 1 == 1 {
// ledpin.set_high().unwrap();
// } else {
// ledpin.set_low().unwrap();
// }
} else if data == 0x41 {
let origin_response: [u8; 10] = [0x00, 0x80, 127, 127, 127, 127, 0, 0, 0, 0];
let pio_response: PioResponse<6> = to_pio_response(origin_response);
delay.delay_us(6);
let mut st = s.stop();
st.exec_instruction(pio::Instruction {
operands: pio::InstructionOperands::JMP {
condition: pio::JmpCondition::Always,
address: offset + offset_outmode as u8,
},
delay: 0,
side_set: None,
});
s = st.start();
for i in 0..pio_response.len() {
while !tx.write((*pio_response)[i]) {}
}
// make ledpin blink twice with 500 ms delay
} else if data == 0 {
let probe_response: [u8; 3] = [0x09, 0x00, 0x03];
let pio_response: PioResponse<2> = to_pio_response(probe_response);
delay.delay_us(6);
let mut st = s.stop();
st.exec_instruction(pio::Instruction {
operands: pio::InstructionOperands::JMP {
condition: pio::JmpCondition::Always,
address: offset + offset_outmode as u8,
},
delay: 0,
side_set: None,
});
s = st.start();
for i in 0..pio_response.len() {
while !tx.write((*pio_response)[i]) {}
}
} else {
ledpin.set_high().unwrap();
let (st, installed) = s.uninit(rx, tx);
(sm, rx, tx) =
init_state_machine_with_installed_program(st, datapin.id().num, installed);
delay.delay_us(400);
sm.exec_instruction(pio::Instruction {
operands: pio::InstructionOperands::JMP {
condition: pio::JmpCondition::Always,
address: offset + offset_inmode as u8,
},
delay: 0,
side_set: None,
});
s = sm.start();
}
}
}

76
src/main.rs Normal file
View file

@ -0,0 +1,76 @@
//! Blinks the LED on a Pico board
//!
//! This will blink an LED attached to GP25, which is the pin the Pico uses for the on-board LED.
#![no_std]
#![no_main]
use bsp::entry;
use defmt::*;
use defmt_rtt as _;
use embedded_hal::digital::v2::{InputPin, OutputPin};
use panic_probe as _;
use rp2040_hal::xosc::CrystalOscillator;
// Provide an alias for our BSP so we can switch targets quickly.
// Uncomment the BSP you included in Cargo.toml, the rest of the code does not need to change.
use rp_pico as bsp;
// use sparkfun_pro_micro_rp2040 as bsp;
use bsp::hal::{
clocks::{init_clocks_and_plls, Clock},
pac,
sio::Sio,
watchdog::Watchdog,
};
use crate::joybus::datatransfer;
mod gccstate;
mod joybus;
mod pio;
mod types;
#[entry]
fn main() -> ! {
info!("Program start");
let mut pac = pac::Peripherals::take().unwrap();
let core = pac::CorePeripherals::take().unwrap();
let mut watchdog = Watchdog::new(pac.WATCHDOG);
let sio = Sio::new(pac.SIO);
// External high-speed crystal on the pico board is 12Mhz
let external_xtal_freq_hz = 12_000_000u32;
let clocks = init_clocks_and_plls(
external_xtal_freq_hz,
pac.XOSC,
pac.CLOCKS,
pac.PLL_SYS,
pac.PLL_USB,
&mut pac.RESETS,
&mut watchdog,
)
.ok()
.unwrap();
let delay = cortex_m::delay::Delay::new(core.SYST, clocks.system_clock.freq().to_Hz());
let pins = bsp::Pins::new(
pac.IO_BANK0,
pac.PADS_BANK0,
sio.gpio_bank0,
&mut pac.RESETS,
);
// This is the correct pin on the Raspberry Pico board. On other boards, even if they have an
// on-board LED, it might need to be changed.
// Notably, on the Pico W, the LED is not connected to any of the RP2040 GPIOs but to the cyw43 module instead. If you have
// a Pico W and want to toggle a LED with a simple GPIO output pin, you can connect an external
// LED to one of the GPIO pins, and reference that pin here.
// let mut led_pin = pins.led.into_push_pull_output();
// let datapin = pins.gpio0.into_pull_up_input();
datatransfer(pins, pac.PIO0, &mut pac.RESETS, delay)
}
// End of file

47
src/pio.rs Normal file
View file

@ -0,0 +1,47 @@
use core::ops::{Deref, DerefMut};
#[derive(Debug, Clone, Copy)]
pub struct PioResponse<const T: usize>([u32; T]);
impl<const T: usize> Deref for PioResponse<T> {
type Target = [u32; T];
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<const T: usize> DerefMut for PioResponse<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
#[link_section = ".time_critical.to_pio_response"]
pub fn to_pio_response<const T: usize, const Y: usize>(value: [u8; T]) -> PioResponse<Y> {
let mut response = PioResponse([0; Y]);
if T / 2 + 1 != Y {
panic!("T / 2 + 1 != Y");
}
if T == 0 {
return response;
}
for i in 0..(T / 2 + 1) {
response[i] = 0;
}
for i in 0..T {
for j in 0..8 {
response[i / 2] += 1 << (2 * (8 * (i % 2) + j) + 1);
response[i / 2] +=
(((value[i] & (0x80u8 >> j)) != 0) as u32) << (2 * (8 * (i % 2) + j));
}
}
response[T / 2] += 3 << (2 * (8 * (T % 2)));
response
}

17
src/types.rs Normal file
View file

@ -0,0 +1,17 @@
struct GccState {
a: u8,
b: u8,
x: u8,
y: u8,
start: u8,
dup: u8,
dleft: u8,
dright: u8,
ddown: u8,
l: u8,
r: u8,
lstickx: i8,
lsticky: i8,
rstickx: i8,
rsticky: i8,
}