概要
PIC16F18326を使いました.クロックは32MHzです.
SDカードにあるPCMデータをMCCのFatFsで読み出して,PWM出力しました.
SDカードにファイルシステムを使ってアクセスしているので,音声データの書き込みがPCでちゃちゃっとできます(超便利).
音質は8bit, 24kHz, モノラルです.PICの性能的には分解能はもっと上げられますが,10bitとか9bitとかキリが悪いので8bitにしました.
僕は今までレジスタこちょこちょ派で,MCCなんか使ったら面白くないじゃん!と思っていたのですが,短時間でこんなに複雑なことができたので大満足です.
MCCの操作
MCCはClassicとMelodyがありますが,現時点(2024/08/21)ではMelodyにはSDカードのライブラリがない(?)のでClassicにします.
System Module
まずはじめに,System Moduleのタブで,クロックを32MHzに,Clock Dividerを1にします.
Timer 0
デューティ比を更新する割り込みを作るためにTimer 0を使います.
PWMのDuty値は,サンプリング周波数が24kHzなので41.66666666666[us]ごとに更新します.
説明がめんどくさいのでスクショはこんな感じです.
Timer 2
PWM
PWMです.キャリア周波数が125kHzなので,人間には聴こえないはずです.
SDCard
FatFS
いらない機能を削除すれば,プログラムメモリが節約できます(f_writeなどは今回必要ない).
これ以外はDefaultのままです.
PPS
設定は終わったのでGenerateします.
ユーザーコード
SDカード内のmusic.raw
を読み込んで再生します.music.raw
の作り方は後で説明します.
このプログラムは,常にBUF_SIZE
分だけデータを先読みしながら,データ読み出しと音声再生を並列処理しています.
#include "mcc_generated_files/mcc.h"
#define BUF_SIZE 256
/*
Main application
*/
uint8_t buf[2][BUF_SIZE];
uint8_t buf_id=0;
uint16_t ptr=0;
bool request=false;
void tmr0_isr() {
if(ptr == 0) {
request = true; // メインルーチンにバッファへの読み込みをリクエスト
}
PWM5_LoadDutyValue(buf[buf_id][ptr++]); // 読み込んだデータでデューティー比を更新
if(ptr == BUF_SIZE) { // バッファの末尾に達したら,バッファを切り替え
ptr = 0;
buf_id = buf_id+1 >= 2 ? 0 : buf_id+1;
}
}
static FATFS drive;
static FIL file;
void main(void)
{
// initialize the device
SYSTEM_Initialize();
// When using interrupts, you need to set the Global and Peripheral Interrupt Enable bits
// Use the following macros to:
// Enable the Global Interrupts
INTERRUPT_GlobalInterruptEnable();
// Enable the Peripheral Interrupts
INTERRUPT_PeripheralInterruptEnable();
// Disable the Global Interrupts
//INTERRUPT_GlobalInterruptDisable();
// Disable the Peripheral Interrupts
//INTERRUPT_PeripheralInterruptDisable();
f_mount(&drive, "0:", 1);
if(f_open(&file, "music.raw", FA_READ) != FR_OK) {
f_mount(NULL, "0:", 0);
while(1);
}
f_read(&file, buf[0], BUF_SIZE, NULL);
TMR0_StartTimer();
TMR0_SetInterruptHandler(tmr0_isr);
TMR2_StartTimer();
PWM5_LoadDutyValue(0);
uint16_t actual_len, len;
FRESULT res;
while (1)
{
// Add your application code
if(request) { // 割り込みルーチンからのリクエストがあったら,つぎのバッファにデータを準備
request = false;
len = 0;
do {
res = f_read(&file, &buf[buf_id+1 >= 2 ? 0 : buf_id+1][len], BUF_SIZE-len, &actual_len);
len += actual_len;
if(actual_len == 0 || res != FR_OK) {
TMR0_StopTimer();
PWM5_LoadDutyValue(0);
f_close(&file);
f_mount(NULL, "0:", 0);
while(1);
}
} while(len != BUF_SIZE);
}
}
}
/**
End of File
*/
音声データの作り方
ffmpegで一発
ffmpeg -i "任意の音楽ファイル" -ac 1 -ar 24000 -acodec pcm_u8 -f u8 music.raw
再生
YouTube人生初投稿!
SDカードの電源は3.3Vです
トランジスタ1個でアンプのようなものも作りました.
おわりに
$ \mathrm{I^2C} $のEEPROM(24LC512)で音楽再生をやったこともありますが,容量的に再生できるのが数秒程度でした.今回は,16GBのSDカードを使ったので,約715828秒,11930分,199時間,8.29日の音楽を流せます!🎉🎉🎉