性无码一区二区三区在线观看,少妇被爽到高潮在线观看,午夜精品一区二区三区,无码中文字幕人妻在线一区二区三区,无码精品国产一区二区三区免费

Rust嵌入式
認(rèn)證:普通會(huì)員
作者動(dòng)態(tài)
嵌入式 Rust 開發(fā)推薦安裝的插件
4天前
嵌入式 Rust 如何使用 Timer 外設(shè)
6天前
Py32 如何使用 Rust 開發(fā)CRC 外設(shè)
2星期前
Py32F030 使用Rust驅(qū)動(dòng)I2C
2星期前
Py32 使用Rust測(cè)試獨(dú)立看門狗
2星期前

如何使用Rust控制單片機(jī)播放MIDI音樂:超簡單

引言:當(dāng)單片機(jī)遇上音樂

你是否想過,用一顆只有64KB Flash的廉價(jià)單片機(jī)(如PY32F030)就能播放復(fù)雜的MIDI音樂?傳統(tǒng)嵌入式音頻開發(fā)往往需要高性能芯片,但通過Rust語言的高效和現(xiàn)代嵌入式生態(tài),我們可以在資源受限的設(shè)備上實(shí)現(xiàn)比較滿意的的音樂效果。

本文將帶你探索:

  • MIDI文件的解析工具
  • 如何用Rust為ARM Cortex-M0單片機(jī)的定時(shí)器外設(shè)開發(fā)音頻蜂鳴器程序

硬件準(zhǔn)備:簡約而不簡單

核心器件:

  • PY32_Rust_Dev Board:Py32f030主控,ARM Cortex-M0內(nèi)核,主頻48MHz,64KB Flash/8KB RAM
  • 有源蜂鳴器(或無源蜂鳴器+驅(qū)動(dòng)電路或其他喇叭)

硬件連接:

  • 使用蜂鳴器的兩端連接GND和PA0或PA3。
  • 使用USB或SWD口供電

軟件設(shè)計(jì)

  1. Midi轉(zhuǎn)換工具由于單片機(jī)資源有限,因此需要先將Midi文件解析為直接用于控制頻率和延時(shí),因此開發(fā)了一個(gè)小工具用于轉(zhuǎn)換。部分代碼如下:
for track in &tracks {
    match track {
        Track::Midi(midi) => {
            for m in midi {
                let event = &m.event;
                let tick_ms: u16 = (m.delta_time asf32 * one_tick_ms) asu16;
                println!("track event: {:?}", m);
                match event {
                    MidiMsg::Meta { msg } => match *msg {
                        Meta::SetTempo(tempo) => {
                            let bpm_ms = tempo asf32 / 1000.0;
                            one_tick_ms = bpm_ms / tpqn asf32;
                            println!("tempo: {} bpm, one tick ms: {}", bpm_ms, one_tick_ms);
                        }
                        _ => {}
                    },
                    MidiMsg::ChannelVoice { channel, msg } => match *msg {
                        ChannelVoiceMsg::NoteOn { note, velocity } => {
                            note_list.push(if velocity == 0 {
                                Note::new(*channel asu8, 0, tick_ms)
                            } else {
                                Note::new(*channel asu8, note, tick_ms)
                            });
                        }
                        ChannelVoiceMsg::NoteOff {
                            note: _,
                            velocity: _,
                        } => {
                            // 關(guān)閉聲音時(shí),需要將note設(shè)置為0,否則會(huì)一直播放
                            note_list.push(Note::new(*channel asu8, 1, tick_ms));
                        }

                        _ => {
                            continue;
                        }
                    },
                    _ => {}
                }
            }
        }
        Track::AlienChunk(alien_chunk) => {
            for a in alien_chunk {
                println!("alien chunk: {}", a);
            }
        }
    }
}

可使用命令直接安裝在Cargo中。

cargo install --git https://github.com/hysonglet/midi2rust.git

使用方式如下:

midi2rust ~/Downloads/PLACE.MID place

執(zhí)行后將會(huì)生成rust數(shù)組如下:

struct Note {
    channel: u8,
    note: u8,
    delay: u16,
}

pubconst MIDI_CONTENT: [Note; 5898] = [
    Note {
        channel: 0,
        note: 60,
        delay: 24705,
    },
    Note {
        channel: 0,
        note: 0,
        delay: 192,
    },
    ...
    Note {
        channel: 9,
        note: 0,
        delay: 3,
    },
];

2.單片機(jī)播放MIDI音樂

單片機(jī)只需要遍歷音頻數(shù)組,執(zhí)行播放指定的頻率和延時(shí)即可,代碼如下:

#![no_std]
#![no_main]

use core::u16;

use hal::gpio::{Af, PinIoType, Speed};
// use hal::timer::advanced_timer::TimerChannel1Pin;
use hal::timer::advanced_timer::{AnyTimer, ChannelConfig, ChannelOutputConfig};
use py32f030_hal::gpio::gpioa::PA0;
use py32f030_hal::gpio::PinAF;
use py32f030_hal::{selfas hal, mode::Blocking, timer::advanced_timer::Channel};

use embassy_executor::Spawner;
use embassy_time::Timer;
// use hal::mcu::peripherals::TIM1;
use embedded_hal_027::Pwm;

use defmt::info;
use {defmt_rtt as _, panic_probe as _};

#[embassy_executor::main]
asyncfn main(_spawner: Spawner) {
    info!("time1 start...");
    let p = hal::init(Default::default());
    let gpioa = p.GPIOA.split();

    let timer: AnyTimer<_, Blocking> = AnyTimer::new(p.TIM1).unwrap();
    letmut pwm = timer.as_pwm();

    pwm.set_channel_1_pin::<_, _>(Some(gpioa.PA3), Some(gpioa.PA0));

    // 配置定時(shí)器
    pwm.config(
        /* 配置通道1 */
        Some(ChannelConfig::default().ch(ChannelOutputConfig::default())),
        None,
        None,
        None,
    );

    // 計(jì)數(shù)頻率為1M
    pwm.set_frequency(1_000_000);
    pwm.set_duty(Channel::CH1, 50);
    // 設(shè)置計(jì)數(shù)周期為1000,則波形的頻率為 1000_000/1000 = 1K
    // pwm.set_period(1000u16 - 1);
    // let max_duty = pwm.get_max_duty();
    // // 33%的占空比
    // pwm.set_duty(Channel::CH1, max_duty / 3);
    // 使能通道
    pwm.enable(Channel::CH1);
    // 開始計(jì)數(shù)器
    pwm.start();

    loop {
        for note in &MIDI_CONTENT {
            let delay = note.delay;
            let channel = note.channel;
            let note = note.note asu32;
            // 只播放指定的 通道
            if channel == 0 {
                let period = (1000_000.0 / NOTE_FREQ[note asusize] - 1.0) asu16;

                info!("freq: {}, note: {}, delay: {}", period, note, delay);

                Timer::after_millis((delay) asu64).await;
                pwm.set_period(period);
            }
        }
    }
}

const NOTE_FREQ: [f32; 128] = [
    // 8.18, /* 0 */
    0.05, /* 0 */
    8.66, 9.18, 9.72, 10.3, 10.91, 11.56, 12.25, 12.98, 13.75, 14.57, /* 1~10 */
    15.43, 16.35, 17.32, 18.35, 19.45, 20.6, 21.83, 23.12, 24.5, 25.96, 27.5, /* 11~21 */
    29.14, 30.87, 32.7, 34.65, 36.71, 38.89, 41.2, 43.65, 46.25, 49.0, 51.91, /* 22~32 */
    55.0, 58.27, 61.74, 65.41, 69.3, 73.42, 77.78, 82.41, 87.31, 92.5, 48.99, /* 33~43 */
    51.91, 55.00, 58.27, 61.74, 65.41, 69.30, 73.42, 77.78, 82.41, 87.31, 92.5, /* 44~54 */
    98.0, 103.8, 110.0, 116.5, 123.5, 130.8, 138.6, 146.8, 155.6, 164.8, 174.6, /* 55~65 */
    185.0, 196.0, 207.7, 220.0, 233.1, 246.9, 261.6, 277.2, 293.7, 311.1, 329.6, /* 66~76 */
    349.2, 370.0, 392.0, 415.3, 440.0, 466.2, 493.9, 523.3, 554.4, /* 77~85 */
    1174.66, 1244.51, 1318.51, 1396.91, 1479.98, 1567.98, 1661.22, 1760.0, 1864.66,
    1975.53, /* 86~95 */
    2093.0, 2217.46, 2349.32, 2489.02, 2637.02, 2793.83, 2959.96, 3135.96, 3322.44,
    3520.0, /* 96~105 */
    3729.31, 3951.07, 4186.01, 4434.92, 4698.64, 4978.03, 5274.04, 5587.65, 5919.91,
    6271.93, /* 106~115 */
    6644.88, 7040.0, 7458.62, 7902.13, 8372.02, 8869.84, 9397.27, 9956.06, 10548.08,
    11175.3, /* 116~125 */
    11839.82, 12543.85, /* 126~127 */
];

struct Note {
    channel: u8,
    note: u8,
    delay: u16,
}

const MIDI_CONTENT: [Note; 896] = [
    Note {
        channel: 0,
        note: 71,
        delay: 26,
    },
    Note {
        channel: 0,
        note: 0,
        delay: 465,
    },
    ...
    Note {
        channel: 0,
        note: 0,
        delay: 0,
    },
];

實(shí)驗(yàn)

如果下載了庫py32f030-hal,執(zhí)行以下命令即可用usb串口或stlink或jlink下載并運(yùn)行:

# 使用jlink或stlink下載
cargo r --example embassy_pwm_midi

# 使用USB串口下載
# 生成bin文件
cargo objcopy --example  embassy_pwm_midi -- -O binary embassy_pwm_midi.bin
# 串口下載
pyisp -s tty.usbserial-1140 -g -f embassy_pwm_midi.bin

固件大小為:

# Debug 編譯
text    data     bss     dec     hex filename
22336      72    5752   28160    6e00 embassy_pwm_midi

# Release 編譯
text    data     bss     dec     hex filename
19504      72    5752   25328    62f0 embassy_pwm_midi

結(jié)語

使用這個(gè)小demo,音質(zhì)和效果雖然有很多待改善的地方,但是仍然非常有趣,相比使用Arduno或C/C++去實(shí)現(xiàn)相同的功能,Rust的更加簡潔,在這個(gè)小嘗試中,我們可以感受到:

  • Rust的零成本抽象在資源受限設(shè)備上的優(yōu)勢(shì)
  • 現(xiàn)代嵌入式開發(fā)可以兼顧性能和開發(fā)效率
  • 即使0.5美元的MCU也能實(shí)現(xiàn)復(fù)雜音頻功能

附錄

完整代碼已開源([GitHub鏈接]),歡迎繼續(xù)優(yōu)化!

  • midi2rust:https://github.com/hysonglet/midi2rust
  • py32f030-hal: https://github.com/hysonglet/py32f030-hal
  • midi音樂庫:https://www.aigei.com/music/midi/
  • midi頻率表:https://newt.phys.unsw.edu.au/jw/notes.html
聲明:本內(nèi)容為作者獨(dú)立觀點(diǎn),不代表電子星球立場(chǎng)。未經(jīng)允許不得轉(zhuǎn)載。授權(quán)事宜與稿件投訴,請(qǐng)聯(lián)系:editor@netbroad.com
覺得內(nèi)容不錯(cuò)的朋友,別忘了一鍵三連哦!
贊 5
收藏 6
關(guān)注 13
成為作者 賺取收益
全部留言
0/200
成為第一個(gè)和作者交流的人吧