Help us understand the problem. What is going on with this article?

ベアメタルでRaspberryPiのPWMから音を鳴らす

More than 1 year has passed since last update.

この記事はadventar Raspberry Pi Advent Calendar 2017の21日目です。

初めに

どうも。gyaboと申します。

去年はRaspberryPiベアメタルでGPUをいじくってポリゴンをだして遊びました。記事は以下です。
https://qiita.com/gyabo/items/f3a411a63d608d00b384

今年またグラフィック系でやるのはなんかへんてこだったので、なんか音出せればうれしいなと思って
ベアメタルで音鳴らす方法を書きます。
で、これをマルチコアでやるサンプルをこしらえてたんですがいろいろな環境で動かなかったので体力つきました。すみません。

目的 : ベアメタルで音鳴らす

HDMIつながってるんだから、音鳴らせるだろうと思っていろいろ見てみたんですがどうもHDMI関連は全部VideoCore側に移譲していて、かつオーディオがちゃんとなってくれるソースを見てみたんですが、イマイチよくわからなかったんで、イヤホンジャックのPWMで素直に出すことにしました。フォーラム調べてみると同じ事を考えてる方がいるけど、何も資料がありませんでした。
なので、そこまで複雑なことはしません。

資料

以下がとても詳しいです。
https://github.com/PeterLemon/RaspberryPI

あと、開発環境は前回の私の記事を参考にしていただければと思います。
https://qiita.com/gyabo/items/f3a411a63d608d00b384

環境

RaspberryPi2です。3では動作確認していません。

音ネタを用意する

以下のサイトからお借りしました。感謝!

https://modarchive.org/index.php?request=view_by_moduleid&query=38211

一式

以下からDLしてください。
image.binをkernel.imgなりなんなり、config.txtに合わせて変えてください。

http://gyabo.sakura.ne.jp/prog/RPI2_PWM_AUDIO_DEMO.7z

で、起動すると音が鳴るはずです!
元の曲が良いのか、十分な良い感じになってくれます。

main.cのコアの部分

以下の通りです。

void main() {
    uart_init();

    IO_WRITE(GPIO_SEL4, GPIO_FSEL0_ALT0 | GPIO_FSEL5_ALT0 );
    usleep(10);
    int idiv = 11;
    IO_WRITE(CM_PWMCTL, PM_PASSWORD | CM_KILL);
    usleep(150);
    IO_WRITE(CM_PWMDIV, PM_PASSWORD | (idiv << 12) );
    IO_WRITE(CM_PWMCTL, PM_PASSWORD | CM_ENAB | CM_SRC_PLLDPER);
    usleep(150);

    int range      = 1024;
    int samplerate = 500000000.0 / idiv / range;
    usleep(10);
    IO_WRITE(PWM_RNG1, range);
    IO_WRITE(PWM_RNG2, range);
    usleep(10);
    IO_WRITE(PWM_CTL,  PWM_PWEN1 | PWM_PWEN2 | PWM_USEF1 | PWM_USEF2 | PWM_CLRF1);

    uint32_t count = 0;
    uint32_t phase = 0;
    while(1) {
        uint32_t offset = (phase >> 8) & 0xFFFFFFFE;
        uint8_t data1 = test_wave[offset + 0];
        uint8_t data2 = test_wave[offset + 1];
        phase += 512;
        IO_WRITE(PWM_FIF1, data1);
        while(IO_READ(PWM_STA) & PWM_FULL1) {}
        IO_WRITE(PWM_FIF1, data2);
        while(IO_READ(PWM_STA) & PWM_FULL1) {}
        count++;
        if((count % 0x1000) == 0) {
            uart_debug_puts("data1=\n", data1);
        }
    }
}

即値があっていやな感じですが、イヤホンジャックをPWMの出力として設定PLLDから分周比設定して、チャンネルレンジを考慮してtest.binからLEFT, PWMのRIGHTそれぞれPCMデータをFIFOにどんどん詰めていく、という単純なものです。
そこそこきれいな音が出ます。ただ鳴らすだけなら十分だと思います。

こいつを適当なcore1とかで実行して、core0側は別なことをできれば~と思ってたんですが、力尽きました。

終わりに

なんか今年はラズベリーパイを使ってもっと変なことをする、というところまで手が回らなかったような気がします。
来年はリベンジしたいです。

おまけ - 開発環境について

やっすいカーナビのアナログTV(1200円くらい)と、HDMI -> AV変換(500円)がとても便利です。
写真の通りなのですが、実にコンパクトな環境が構築できます。3000円程度で機材が手に入るので、ぜひ。

image.png

※地べたにおいてるのは床暖房を全身に受けながら作業したかったからです

終わりです!

gyabo
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした