LoginSignup
4
2

More than 3 years have passed since last update.

初めてのRaspberry Pi Pico ㉙ C言語 I2S その1

Last updated at Posted at 2021-03-06

 I2Sのサンプルにsine_waveがあります。これを動かします。

環境を整える

 第8回で、pico/worksのディレクトリで開発をするように環境を整えました。その延長の設定です。I2Sのライブラリは、/pico/pic-extrasに入っているので、そのためのパスを通します。

 /home/pi/picoにworksフォルダを作っています。
 CmakeLists.txt、pico_sdk_import.cmakeファイルとcmakeフォルダが入っています。ここに、pico_extras_import.cmakeファイルを追加します。このファイルは、/home/pi/pico/pico-playgroundに置いてあったものをコピーしてきます。
 pico_sdk_import.cmakeとpico_extras_import.cmakeは中身はそのままです。CmakeLists.txtの内容です。

CmakeLists.txt
cmake_minimum_required(VERSION 3.12)

# Pull in SDK (must be before project)
include(pico_sdk_import.cmake)

# We also need PICO EXTRAS
include(pico_extras_import.cmake)

project(pico_examples C CXX ASM)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)

set(PICO_EXAMPLES_PATH ${PROJECT_SOURCE_DIR})

# Initialize the SDK
pico_sdk_init()

# Add blink example
add_subdirectory(blink)
add_subdirectory(cmake)
add_subdirectory(LED7seg_74HC595)
add_subdirectory(bus_scan)
add_subdirectory(lcd_1602_i2c)
add_subdirectory(CRC8)
add_subdirectory(adt7410)
add_subdirectory(hello_pwm)
add_subdirectory(mcp3201)
add_subdirectory(mcp3202)
add_subdirectory(mcp3204)
add_subdirectory(mcp3208)
add_subdirectory(mcp3208x2)
add_subdirectory(LTC2461)
add_subdirectory(ads1110)
add_subdirectory(ads1115)
add_subdirectory(ads122u04)
add_subdirectory(pwm2)
add_subdirectory(sine_wave)

 追加したのは、

# We also need PICO EXTRAS
include(pico_extras_import.cmake)

と、最後に、今回作成するsine_waveフォルダの記述です。

add_subdirectory(sine_wave)

 /home/pi/pico/pico-playground/audioから、sine_waveフォルダをコピーしてきます。
i2s101a.png

関連のソースの場所

 /home/pi/pico/pico-extras/src/rp2_common/pico_audio_i2sに、修正することがあるかもしれないプログラムが二つ置いてあります。

  • LRCLK(WS)とBCKの関係を記述したaudio_i2s.pioがあります。16ビット、32fsで波形を作っています。最近のハイレゾは、32(24)ビット、64fsです。

  • audio_i2s.cは、system_clock_frequency(システム・クロック。通常125MHz)とsample_freq(サンプリング周波数)の関係から、BCKのクロック周波数を求めています。32fsですから、ハイレゾでは使えません。

フォーマット

 I2Sには3種類のフォーマットがあります。Picoのドキュメントの中にどれが採用されているかは、記述が見つけられませんでした。
 Hardware design with RP2040のサンプル回路図「Figure 24. Schematic section showing the connections to Raspberry Pi Pico」で使われているPCM5101AではFMT端子がGNDに落ちているので、I2Sフォーマットと思われます。
 しかし、オシロスコープで見ると、LRCLKの切り替わりの直後の1BCKサイクルのウェイトがないように見えます。audio_i2s.pioのソースでもそのような記述がないように見えます。
i2s102.png

用意したハードウェア

 たぶんテレビ用だと思いますが、NXPのUDA1334Aを搭載したボードを使いました。スイッチサイエンスでもAdafruitの製品を取り扱っています。

ファイル名

接続

UDA1334Aボードの端子 Picoの端子(GPIO) 名称
Vin 3.3V 3.3V
Vo - -
GND GND GND
Wsel GP27 LRCK
Din GP28 Data
BCK GP26 BCK
SCLK - -
FS0 3.3V 3.3V
MUTE - -
FS1 GND GND
PLL - -
DEEM - -

 FS0、FS1の設定は、データシートから、I2SではなくLSB-justified 16 bits inputを選択しました。FS0、FS1の両方をGNDに落としたI2Sフォーマットでは、ノイズだけがでました。

 Hardware design with RP2040のサンプル回路図「Figure 24. Schematic section showing the connections to Raspberry Pi Pico」で使われているPCM5101Aでは、

  • GP28 LRCK
  • GP26 Data
  • GP27 BCK

となっています。しかし、/home/pi/pico/pico-extras/src/rp2_common/pico_audio_i2s/include/pico/audio_i2s.hでは、

  • #define PICO_AUDIO_I2S_DATA_PIN 28
  • #define PICO_AUDIO_I2S_CLOCK_PIN_BASE 26

 したがって、次の接続にしました。
- GP28 Data
- GP26 BCK
- GP27 LRCK

プログラム

 worksにコピーしてきたsine_waveフォルダには、CMakeLists.txtとsine_wave.cが入っています。どちらも修正します。

CMakeLists.txt
add_executable(sine_wave_i2s
        sine_wave.c
        )

target_link_libraries(sine_wave_i2s PRIVATE
        pico_stdlib
        pico_audio_i2s
        )

target_compile_definitions(sine_wave_i2s PRIVATE
        # compile time configuration of I2S
        PICO_AUDIO_I2S_MONO_INPUT=1
        #define for our example code
        USE_AUDIO_I2S=1
        )
# create map/bin/hex file etc.
pico_add_extra_outputs(sine_wave_i2s)

 サンプリング周波数は24kHzです。

sine_wave.c
/**
 * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

#include <stdio.h>
#include <math.h>

#include "hardware/clocks.h"
#include "hardware/structs/clocks.h"

#include "pico/stdlib.h"
#include "pico/audio_i2s.h"

#define SINE_WAVE_TABLE_LEN 2048
#define SAMPLES_PER_BUFFER 256

static int16_t sine_wave_table[SINE_WAVE_TABLE_LEN];

struct audio_buffer_pool *init_audio() {

    static audio_format_t audio_format = {
            .format = AUDIO_BUFFER_FORMAT_PCM_S16,

            .sample_freq = 24000 ,

            .channel_count = 1,
    };

    static struct audio_buffer_format producer_format = {
            .format = &audio_format,
            .sample_stride = 2
    };

    struct audio_buffer_pool *producer_pool = audio_new_producer_pool(&producer_format, 3,
                                                                      SAMPLES_PER_BUFFER); // todo correct size
    bool __unused ok;
    const struct audio_format *output_format;

    struct audio_i2s_config config = {
            .data_pin = PICO_AUDIO_I2S_DATA_PIN,
            .clock_pin_base = PICO_AUDIO_I2S_CLOCK_PIN_BASE,
            .dma_channel = 0,
            .pio_sm = 0,
    };

    output_format = audio_i2s_setup(&audio_format, &config);
    if (!output_format) {
        panic("PicoAudio: Unable to open audio device.\n");
    }

    ok = audio_i2s_connect(producer_pool);
    assert(ok);
    audio_i2s_set_enabled(true);

    return producer_pool;

}

int main() {

//    set_sys_clock_48mhz();

    stdio_init_all();
    printf("\nstart I2S wave\n");
    for (int i = 0; i < SINE_WAVE_TABLE_LEN; i++) {
        sine_wave_table[i] = 32767 * cosf(i * 20 * (float) (M_PI / SINE_WAVE_TABLE_LEN));
    }

    struct audio_buffer_pool *ap = init_audio();
    uint32_t step = 0x200000;
    uint32_t pos = 0;
    uint32_t pos_max = 0x10000 * SINE_WAVE_TABLE_LEN;
    uint vol = 128;
    while (true) {
//#if USE_AUDIO_PWM
//        enum audio_correction_mode m = audio_pwm_get_correction_mode();
//#endif
        int c = getchar_timeout_us(0);
        if (c >= 0) {
            if (c == '-' && vol) vol -= 4;
            if ((c == '=' || c == '+') && vol < 255) vol += 4;
            if (c == '[' && step > 0x10000) step -= 0x10000;
            if (c == ']' && step < (SINE_WAVE_TABLE_LEN / 16) * 0x20000) step += 0x10000;
            if (c == 'q') break;

            printf("vol = %d, step = %d      \r", vol, step >> 16);

        }
        struct audio_buffer *buffer = take_audio_buffer(ap, true);
        int16_t *samples = (int16_t *) buffer->buffer->bytes;
        for (uint i = 0; i < buffer->max_sample_count; i++) {
            samples[i] = (vol * sine_wave_table[pos >> 16u]) >> 8u;
            pos += step;
            if (pos >= pos_max) pos -= pos_max;
        }
        buffer->sample_count = buffer->max_sample_count;
        give_audio_buffer(ap, buffer);
    }
    puts("\n");
    return 0;
}

 sin波の周波数は、

sine_wave_table[i] = 32767 * cosf(i * 20 * (float) (M_PI / SINE_WAVE_TABLE_LEN));

 iに乗じている20を変更すると変わります。volの値は、修正時にプログラムのどこかが間違っていて機能しません。 ターミナルで、+ -などのキーに応じて変化します。

 ターミナルで、~/pico/works/buildに降りて、cmake ..します。cd sine_waveでさらに降りてmake -j4でビルドします。ソースを変更したときは再度make -j4で新しいコードができます。
 第6回で作ったResetボタンを使います。Resetボタンを押したまま、BOOTSELボタンを押し、Resetボタンを放してから、BOOTSELボタンを放します。これでRPI-RP2ディスクがマウントができます。
 /home/pi/pico/works/build/sine_waveフォルダにできているsine_wave_i2s.uf2をRPI-RP2ディスクにドロップすると、プログラムが実行されます。

4
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
2