Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
c004969
Create index.md
Vulisu Apr 26, 2026
067428b
Add files via upload
Vulisu Apr 26, 2026
45b1bb0
Update index.md
Vulisu Apr 26, 2026
1199c6c
Update index.md
Vulisu Apr 26, 2026
afd855e
Delete website/versioned_docs/version-fils_en/project/2026/valentin.c…
Vulisu Apr 26, 2026
352484c
Add files via upload
Vulisu Apr 26, 2026
a4ec26c
Update index.md
Vulisu Apr 26, 2026
f8ad41a
Update index.md
Vulisu Apr 26, 2026
559e176
Delete website/versioned_docs/version-fils_en/project/2026/valentin.c…
Vulisu Apr 26, 2026
a358762
Delete website/versioned_docs/version-fils_en/project/2026/valentin.c…
Vulisu Apr 26, 2026
6db1c8a
Delete website/versioned_docs/version-fils_en/project/2026/valentin.c…
Vulisu Apr 26, 2026
22741c1
Add files via upload
Vulisu Apr 26, 2026
afb641a
Rename kicad (1).webp to kicad.webp
Vulisu Apr 26, 2026
e8592c7
Rename proiect (1).svg to proiect.svg
Vulisu Apr 26, 2026
c1a0483
Rename schema (1).webp to schema.webp
Vulisu Apr 26, 2026
75077d0
Update index.md
Vulisu Apr 26, 2026
ac3cfac
Update index.md
Vulisu Apr 26, 2026
c0fffba
Merge branch 'UPB-PMRust:main' into main
Vulisu Apr 26, 2026
4c15ec9
Delete website/versioned_docs/version-fils_en/project/2026/valentin.c…
Vulisu Apr 26, 2026
0c78d27
Add files via upload
Vulisu Apr 26, 2026
5917bd5
Update index.md
Vulisu Apr 26, 2026
317f2fb
Merge branch 'main' into main
Vulisu Apr 26, 2026
9d61c79
Merge branch 'main' into main
Vulisu Apr 29, 2026
510dce4
Merge branch 'main' into main
Vulisu Apr 29, 2026
400fdeb
Merge branch 'main' into main
Vulisu Apr 29, 2026
1601022
cod
Vulisu May 28, 2026
019d86c
Merge branch 'UPB-PMRust:main' into main
Vulisu Aug 23, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions wav-player-rp235x/.cargo/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[target.thumbv8m.main-none-eabihf]
runner = "probe-rs run --chip RP235x"

rustflags = [
"-C", "link-arg=-Tlink.x",
"-C", "link-arg=-Tdefmt.x",
]

[build]
target = "thumbv8m.main-none-eabihf"
1 change: 1 addition & 0 deletions wav-player-rp235x/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/target
24 changes: 24 additions & 0 deletions wav-player-rp235x/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
[package]
name = "wav-player-pico2w"
version = "0.1.0"
edition = "2021"

[dependencies]
embassy-rp = { version = "0.10", features = [
"rp235xa", # Correct for Pico 2W
"time-driver",
"critical-section-impl",
] }

embassy-executor = { version = "0.7", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread"] }
embassy-time = { version = "0.4", features = ["defmt"] }
embassy-sync = "0.6"
cortex-m-rt = "0.7"

defmt = "1.0"
defmt-rtt = "1.0"
panic-probe = { version = "0.3", features = ["print-defmt"] }

embedded-hal-bus = "0.2"
embedded-sdmmc = "0.8"
heapless = "0.8"
75 changes: 75 additions & 0 deletions wav-player-rp235x/memory.x
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
MEMORY {
/*
* The RP2350 has either external or internal flash.
*
* 2 MiB is a safe default here, although a Pico 2 has 4 MiB.
*/
FLASH : ORIGIN = 0x10000000, LENGTH = 2048K
/*
* RAM consists of 8 banks, SRAM0-SRAM7, with a striped mapping.
* This is usually good for performance, as it distributes load on
* those banks evenly.
*/
RAM : ORIGIN = 0x20000000, LENGTH = 512K
/*
* RAM banks 8 and 9 use a direct mapping. They can be used to have
* memory areas dedicated for some specific job, improving predictability
* of access times.
* Example: Separate stacks for core0 and core1.
*/
SRAM8 : ORIGIN = 0x20080000, LENGTH = 4K
SRAM9 : ORIGIN = 0x20081000, LENGTH = 4K
}

SECTIONS {
/* ### Boot ROM info
*
* Goes after .vector_table, to keep it in the first 4K of flash
* where the Boot ROM (and picotool) can find it
*/
.start_block : ALIGN(4)
{
__start_block_addr = .;
KEEP(*(.start_block));
KEEP(*(.boot_info));
} > FLASH

} INSERT AFTER .vector_table;

/* move .text to start /after/ the boot info */
_stext = ADDR(.start_block) + SIZEOF(.start_block);

SECTIONS {
/* ### Picotool 'Binary Info' Entries
*
* Picotool looks through this block (as we have pointers to it in our
* header) to find interesting information.
*/
.bi_entries : ALIGN(4)
{
/* We put this in the header */
__bi_entries_start = .;
/* Here are the entries */
KEEP(*(.bi_entries));
/* Keep this block a nice round size */
. = ALIGN(4);
/* We put this in the header */
__bi_entries_end = .;
} > FLASH
} INSERT AFTER .text;

SECTIONS {
/* ### Boot ROM extra info
*
* Goes after everything in our program, so it can contain a signature.
*/
.end_block : ALIGN(4)
{
__end_block_addr = .;
KEEP(*(.end_block));
} > FLASH

} INSERT AFTER .uninit;

PROVIDE(start_to_end = __end_block_addr - __start_block_addr);
PROVIDE(end_to_start = __start_block_addr - __end_block_addr);
268 changes: 268 additions & 0 deletions wav-player-rp235x/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,268 @@
#![no_std]
#![no_main]

use defmt::*;
use embassy_executor::Spawner;
use embassy_rp::adc::{self, Adc, Channel as AdcChannel, Config as AdcConfig};
use embassy_rp::bind_interrupts;
use embassy_rp::dma::InterruptHandler as DmaInterruptHandler;
use embassy_rp::gpio::{Input, Level, Output, Pull};
use embassy_rp::peripherals::{DMA_CH0, PIO0};
use embassy_rp::pio::{InterruptHandler as PioInterruptHandler, Pio};
use embassy_rp::pio_programs::i2s::{PioI2sOut, PioI2sOutProgram};
use embassy_rp::spi::{self, Spi};
use embassy_time::{Delay, Instant};
use embedded_hal_bus::spi::ExclusiveDevice;
use embedded_sdmmc::{SdCard, VolumeManager, VolumeIdx, Mode, ShortFileName};
use heapless::Vec;
use {defmt_rtt as _, panic_probe as _};

bind_interrupts!(struct Irqs {
PIO0_IRQ_0 => PioInterruptHandler<PIO0>;
DMA_IRQ_0 => DmaInterruptHandler<DMA_CH0>;
ADC_IRQ_FIFO => adc::InterruptHandler;
});

const SAMPLE_RATE: u32 = 48_000;
const BUFFER_SAMPLES: usize = 480;
const MAX_FILES: usize = 32;

struct DummyTimesource;
impl embedded_sdmmc::TimeSource for DummyTimesource {
fn get_timestamp(&self) -> embedded_sdmmc::Timestamp {
embedded_sdmmc::Timestamp {
year_since_1970: 54,
zero_indexed_month: 0,
zero_indexed_day: 0,
hours: 0,
minutes: 0,
seconds: 0,
}
}
}

fn parse_wav_header(header: &[u8; 44]) -> Option<(u32, u16, u16, u32)> {
if &header[0..4] != b"RIFF" || &header[8..12] != b"WAVE" || &header[12..16] != b"fmt " {
return None;
}
let channels = u16::from_le_bytes([header[22], header[23]]);
let sample_rate = u32::from_le_bytes([header[24], header[25], header[26], header[27]]);
let bit_depth = u16::from_le_bytes([header[34], header[35]]);
let data_size = u32::from_le_bytes([header[40], header[41], header[42], header[43]]);

Some((sample_rate, channels, bit_depth, data_size))
}

#[embassy_executor::main]
async fn main(_spawner: Spawner) {
info!("=== WAV Player RP235x ===");

let p = embassy_rp::init(Default::default());

let btn_play = Input::new(p.PIN_14, Pull::Up);
let btn_next = Input::new(p.PIN_15, Pull::Up);

let mut adc = Adc::new(p.ADC, Irqs, AdcConfig::default());
let mut pot = AdcChannel::new_pin(p.PIN_26, Pull::None);

// I2S
let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs);
let i2s_prog = PioI2sOutProgram::new(&mut common);
let mut i2s = PioI2sOut::new(
&mut common, sm0, p.DMA_CH0, Irqs,
p.PIN_9, p.PIN_10, p.PIN_11,
SAMPLE_RATE, 16, &i2s_prog,
);
i2s.start();

// SD Card
let mut spi_cfg = spi::Config::default();
spi_cfg.frequency = 400_000;
let spi = Spi::new_blocking(p.SPI0, p.PIN_2, p.PIN_3, p.PIN_4, spi_cfg);
let cs = Output::new(p.PIN_5, Level::High);
let spi_dev = ExclusiveDevice::new(spi, cs, Delay).unwrap();
let sdcard = SdCard::new(spi_dev, Delay);

info!("Initializare SD...");
match sdcard.num_bytes() {
Ok(size) => info!("SD card: {} MB", size / 1024 / 1024),
Err(e) => {
error!("SD eroare: {:?}", defmt::Debug2Format(&e));
loop {}
}
}
// Crește SPI după init
sdcard.spi(|dev| dev.bus_mut().set_config(&{
let mut c = spi::Config::default();
c.frequency = 16_000_000;
c
}));

let mut vol_mgr = VolumeManager::new(sdcard, DummyTimesource);
let mut volume = vol_mgr.open_volume(VolumeIdx(0)).unwrap();
let mut root = volume.open_root_dir().unwrap();

// === Scanare fișiere .wav ===
let mut files: Vec<ShortFileName, MAX_FILES> = Vec::new();
info!("Scanare fisiere WAV...");
root.iterate_dir(|entry| {
let name = entry.name.base_name();
let ext = entry.name.extension();
if ext.eq_ignore_ascii_case(b"WAV") && !entry.attributes.is_directory() {
let _ = files.push(entry.name.clone());
info!(" Gasit: {}.{} ({} bytes)",
core::str::from_utf8(name).unwrap_or("?"),
core::str::from_utf8(ext).unwrap_or("?"),
entry.size
);
}
}).unwrap();

if files.is_empty() {
error!("Niciun fisier WAV pe card!");
loop {}
}
info!("Total: {} fisiere", files.len());

// === State playerul ===
let mut current_file: usize = 0;
let mut playing: bool = true;
let mut volume_scale: f32 = 0.5; // 0.0 - 1.0

// State butoane (pentru edge detection)
let mut btn_play_prev: bool = true; // pull-up = HIGH normal
let mut btn_next_prev: bool = true;
let mut last_btn_check = Instant::now();
let mut last_pot_log = Instant::now();

// Buffer pentru I2S
let mut i2s_buf = [0u32; BUFFER_SAMPLES];

// === Loop principal ===
'outer: loop {
info!("Redare: {}", core::str::from_utf8(files[current_file].base_name()).unwrap_or("?"));

// Deschide fișierul curent
let mut file = match root.open_file_in_dir(files[current_file].clone(), Mode::ReadOnly) {
Ok(f) => f,
Err(e) => {
error!("Nu pot deschide: {:?}", defmt::Debug2Format(&e));
current_file = (current_file + 1) % files.len();
continue;
}
};

// Citește header WAV
let mut header = [0u8; 44];
if file.read(&mut header).is_err() {
error!("Eroare citire header");
current_file = (current_file + 1) % files.len();
continue;
}

let (sr, ch, bd, data_size) = match parse_wav_header(&header) {
Some(h) => h,
None => {
error!("Header WAV invalid");
current_file = (current_file + 1) % files.len();
continue;
}
};

info!("WAV: {} Hz, {} ch, {} bit, {} bytes", sr, ch, bd, data_size);

if sr != SAMPLE_RATE || ch != 2 || bd != 16 {
warn!("Format diferit ({}Hz {}ch {}bit), reproducere posibil incorecta", sr, ch, bd);
}

// === Inner loop: redare fișier ===
let mut bytes_read: u32 = 0;
let mut raw_buf = [0u8; BUFFER_SAMPLES * 4]; // 4 bytes per stereo frame

loop {
// === Verifică butoane (la fiecare ~5ms) ===
if last_btn_check.elapsed().as_millis() >= 5 {
last_btn_check = Instant::now();

let play_now = btn_play.is_high();
let next_now = btn_next.is_high();

// Edge falling = apăsare (pull-up: HIGH→LOW când apeși)
if !play_now && btn_play_prev {
playing = !playing;
info!(">> {}", if playing { "PLAY" } else { "PAUSE" });
}
if !next_now && btn_next_prev {
info!(">> NEXT");
current_file = (current_file + 1) % files.len();
file.close().unwrap_or(());
continue 'outer;
}
btn_play_prev = play_now;
btn_next_prev = next_now;
}

// === Citește pot la fiecare ~50ms ===
if last_pot_log.elapsed().as_millis() >= 50 {
last_pot_log = Instant::now();
if let Ok(raw) = adc.read(&mut pot).await {
let new_vol = raw as f32 / 4095.0;
// Smoothing simplu
volume_scale = volume_scale * 0.7 + new_vol * 0.3;
}
}

// === Dacă playing, redă audio ===
if playing {
// Citește chunk din WAV
let bytes_to_read = core::cmp::min(
raw_buf.len(),
(data_size - bytes_read) as usize,
);

if bytes_to_read == 0 {
info!("Sfarsit fisier - trec la urmatorul");
current_file = (current_file + 1) % files.len();
file.close().unwrap_or(());
continue 'outer;
}

let n = match file.read(&mut raw_buf[..bytes_to_read]) {
Ok(n) => n,
Err(_) => {
error!("Eroare citire");
current_file = (current_file + 1) % files.len();
file.close().unwrap_or(());
continue 'outer;
}
};
bytes_read += n as u32;

// Converteste raw bytes la samples I2S, aplica volum
let frames = n / 4;
for i in 0..frames {
let l_raw = i16::from_le_bytes([raw_buf[i*4], raw_buf[i*4 + 1]]);
let r_raw = i16::from_le_bytes([raw_buf[i*4 + 2], raw_buf[i*4 + 3]]);

let l = ((l_raw as f32) * volume_scale) as i16;
let r = ((r_raw as f32) * volume_scale) as i16;

let l_u = l as u16 as u32;
let r_u = r as u16 as u32;
i2s_buf[i] = (l_u << 16) | r_u;
}

// Zero rest dacă buffer parțial
for i in frames..BUFFER_SAMPLES {
i2s_buf[i] = 0;
}

i2s.write(&i2s_buf).await;
} else {
// PAUSE - scrie liniște la I2S (0) ca să nu se audă pop-uri
i2s_buf.iter_mut().for_each(|x| *x = 0);
i2s.write(&i2s_buf).await;
}
}
}
}
Loading