第二幕:数据推送与播放控制
2026/5/9大约 2 分钟嵌入式开发STM32SPIVS1053
VS1053 初始化完成后,SCI 接口用于控制命令,SDI 接口用于推送音频数据。这一节实现从打开 WAV 文件到流式播放的完整逻辑。
Vs1053 驱动结构
pub struct Vs1053<'d, BD, TS>
where
BD: BlockDevice,
TS: TimeSource,
{
spi: Spi<'d, Async, Master>,
xcs: Output<'d>,
xdcs: Output<'d>,
rst: Output<'d>,
dreq: ExtiInput<'d, Async>,
volume: u16,
vol_mgr: Controller<BD, TS>,
sd_volume: Volume,
root_dir: Directory,
current_file: Option<embedded_sdmmc::File>,
state: PlaybackState,
byte_rate: u32, // 用于快进快退
}泛型参数 BD: BlockDevice 和 TS: TimeSource 让它兼容任何实现了对应 trait 的 SD 卡驱动。
音量控制
音量值映射:0x0000 = 最大声,0xFEFE = 几乎静音。
pub async fn set_volume(&mut self, percent: u8) -> Result<(), spi::Error> {
let percent = percent.min(100);
let val = (100 - percent as u16) * 254 / 100;
let vol = (val << 8) | val; // 左右声道相同
self.sci_write(0x0B, vol).await
}播放文件
pub async fn play(&mut self, filename: &str) -> Result<(), spi::Error> {
self.stop()?;
let file = self.vol_mgr.open_file_in_dir(
&mut self.sd_volume, &self.root_dir,
filename, embedded_sdmmc::Mode::ReadOnly,
).await?;
// 解析 WAV 头,提取字节率(第 28-31 字节)
let mut header = [0u8; 64];
if let Ok(bytes_read) = self.vol_mgr
.read(&self.sd_volume, &mut file, &mut header).await
&& bytes_read >= 44
{
self.byte_rate = u32::from_le_bytes(header[28..32].try_into().unwrap());
}
let _ = file.seek_from_start(0);
self.current_file = Some(file);
self.state = PlaybackState::Playing;
Ok(())
}byte_rate = sample_rate × channels × bits_per_sample / 8,记录下来用于后续 seek 计算偏移量。
数据推送
send_data 通过 XDCS 选中 SDI 接口,分块发送(每次最多 32 字节),块间等待 DREQ:
pub async fn send_data(&mut self, mut data: &[u8]) -> Result<(), spi::Error> {
while !data.is_empty() {
self.dreq.wait_for_high().await;
let chunk = &data[..data.len().min(32)];
self.xdcs.set_low();
self.spi.write(chunk).await?;
self.xdcs.set_high();
data = &data[chunk.len()..];
}
Ok(())
}播放主循环 process()
每调用一次就喂一坨数据(512 字节),读到 EOF 就自动停止:
pub async fn process(&mut self) -> Result<(), spi::Error> {
if self.state != PlaybackState::Playing { return Ok(()); }
let mut buf = [0u8; 512];
match self.vol_mgr.read(&self.sd_volume, &mut self.current_file, &mut buf).await {
Ok(0) => { self.stop()?; } // EOF
Ok(bytes_read) => {
self.send_data(&buf[..bytes_read]).await?;
}
Err(_) => { self.stop()?; }
}
Ok(())
}播放状态机
Stopped ──play()──▶ Playing ──pause()──▶ Paused
▲ │ │
└──stop()────◀─────┘ continue_play()───┘
└──EOF─────────┘#[derive(PartialEq)]
pub enum PlaybackState { Stopped, Playing, Paused }
pub fn is_playing(&self) -> bool { self.state == PlaybackState::Playing }
pub fn pause(&mut self) { self.state = PlaybackState::Paused; }
pub fn continue_play(&mut self) { self.state = PlaybackState::Playing; }stop() 会关闭当前文件并向 VS1053 发送 2048 字节的静音填充来清空 FIFO,确保下一首从头开始播放。
