ダイオードI-V特性測定器作成:4-2. マイコン側ソフト設計 – Raspberry Pi Pico / Pico 2

2025年9月19日

Google Ad

本記事の主旨

ダイオードI-V特性測定器(カーブトレーサ)作成のRaspberry Pi Pico / Pico 2用マイコン側ソフトについて記す。

本記事の目的

本記事の目的を以下に記す。

  • Raspberry Pi Pico / Pico 2用マイコン側ソフトのソースについての解説を記すこと

本記事の背景

ダイオードI-V特性測定器(カーブトレーサ)作成の中でマイコン側ソフトを作成したため、本記事でその部分的な解説をする。

本記事の位置づけ

以下の流れで作業説明と解説を行った。

記事に対応したリンクを添付した。

概要説明:ダイオードI-V特性測定器作成:0. 概要 | 工事中.com

  1. 取扱説明
  2. 回路設計
    1. ダイオードのI-V特性を手作業で確認
    2. 回路の実装
      1. 回路の実装(PWM平滑化)
      2. 回路の実装(PWM平滑化以外)
  3. PC側ソフト設計
    1. pySerialでUSBシリアル通信
    2. Matplotlibでのグラフ表示
    3. Pythonで最小二乗法の指数関数近似
    4. openpyxlでグラフ描画と最小二乗法の指数関数近似
  4. マイコン側ソフト設計
    1. Arduino Uno Rev3 / R4 Minima
    2. Raspberry Pi Pico / Pico 2★現在のページ
  5. 実験
    1. 1N4148の測定でマイコンとソースの組み合わせを比較
    2. 種々のダイオードを測定
    3. 種々の高精度ゲルマニウムダイオードを測定
  6. まとめと補足

コードの全体

今回解説するコードの全体は、以下にアップロードした。

pico-sdk用CMakeファイルとソースファイル。

CMakeの解説

Raspberry Pi Pico / Pico 2では、CMakeファイルを以下のように記述した。

CMake設定

pico_enable_stdio_usbで1を設定することにより、PCとRaspberry Pi PicoがUSBを介して通信可能になる。

target_link_librariesにPWMを使用するためにhardware_pwm、ADCを使用するためにhardware_adcを設定する。

コードの解説

Raspberry Pi Pico / Pico 2では、ソースファイルを以下のように実装した。

中央値の求め方以外は特に難しい処理がないため、マイコン特有の処理についてのみ解説する。

初期化処理

全体初期化

stdio_init_all()関数で、全体の初期化をする。

    /* 初期化 */
    // 全体
    stdio_init_all();

PWM初期化

gpio_set_function()関数で、PWM出力に使うピンを指定する。

pwm_gpio_to_slice_num()関数で、PWM出力するGPIOに対応するスライス番号を取得する。

スライスは、GPIOによって固定の値をとる。

pwm_gpio_to_channel()関数で、PWM出力するGPIOに対応するチャネル文字を取得する。

チャネルは、GPIOによって固定の値をとる。

pwm_set_wrap()関数で、PWM出力のサイクル数を設定する。

PWM出力を最大の65536段階にするとき65536 - 1はuint32_t扱いになるため、今回はuint16_tでキャストしている。

    // PWM
    gpio_set_function(PWM_OUTPUT_PIN, GPIO_FUNC_PWM);           // PWM出力に使う5番ピンを出力に指定
    slice_num = pwm_gpio_to_slice_num(PWM_OUTPUT_PIN);          // PWM出力するGPIOに対応するスライス番号を取得(スライスはGPIOによって固定。)
    channel_str = pwm_gpio_to_channel(PWM_OUTPUT_PIN);          // PWM出力するGPIOに対応するチャネル文字を取得(チャネルはGPIOによって固定。)
    pwm_set_wrap(slice_num, (uint16_t)(NUM_OF_PWM_STG - 1));    // PWM出力をNUM_OF_PWM_STG段階にセット(NUM_OF_PWM_STGサイクルのなかで何サイクルHにするかでデューティー比を決める。)、NUM_OF_PWM_STGが65536のときuint32_t扱いになるため、uint16_tでキャストする。

ADC初期化

adc_init()関数で、ADC初期化をする。

adc_gpio_init()関数で、ADCに使用するGPIOを指定して初期化する。

    // ADC
    adc_init();     // ADC初期化
    adc_gpio_init(ADC_INPUT_PIN_0);  // ADCのGPIO初期化
    adc_gpio_init(ADC_INPUT_PIN_1);  // ADCのGPIO初期化

ループ処理

シリアルポート監視

scanf()関数で、入力待ちをする。

        char r_str[128] = "";   //受け取った配列
        scanf("%s", &r_str);   //入力待ち

測定開始判定

strcmp()関数で受信した文字列がSerialStartであると判定した場合、測定を開始する。

        if (strcmp(r_str, "SerialStart\r\n"))  // 受信した文字列がSerialStartである場合
        {

測定開始時初期化

pwm_set_chan_level()関数で、設定したサイクル中任意のサイクルをHにする。

pwm_set_enabled()関数で、指定したスライスのPWMを有効化する。

            pwm_set_chan_level(slice_num, channel_str, 0);  // NUM_OF_PWM_STGサイクル中0サイクルをHにする。
            pwm_set_enabled(slice_num, true);       // スライス毎PWMを有効化する。

測定中処理

pwm_set_chan_level()関数で、設定したサイクル中任意のサイクルをHにする。

pwm_set_enabled()関数で、指定したスライスのPWMを有効化する。

この有効化は、PWMのデューティー比変更ごとに行わなければ変更が反映されない。

adc_select_input()関数で、ADCを使用するGPIOを設定する。

adc_read()関数で、現在使用する設定にしたADCの値を取得する。

            for (i = 0; i <= NUM_OF_PWM_STG - 1 + 5000; i++)   // iがuint16_tであると、for (i = 0; i <= NUM_OF_PWM_STG - 1; i++)ではi = NUM_OF_PWM_STG - 1 = 65535のときi++でi = 0になり無限ループになることに注意。PWMの65535段階 + PWM出力のデューティー比が100%になってから少なくとも5τ(時定数)分待つための5000回ループ
            {
                if (i <= NUM_OF_PWM_STG - 1)
                {
                    pwm_set_chan_level(slice_num, channel_str, (uint16_t)i);  // NUM_OF_PWM_STGサイクル中iサイクルをHにする。NUM_OF_PWM_STG - 1 = 65535までで、iはuint16_tの間に収まる。
                }
                pwm_set_enabled(slice_num, true);       // スライス毎PWMを有効化する。
                sleep_ms(1);    // 定常状態になるのを待つ
                for (j = 0; j < NUM_MEASUREMENT; j++)   // 測定
                {
                    adc_select_input(0);    // ADC0を使用する設定
                    v_d[j] = adc_read();    // ダイオードの電圧
                    adc_select_input(1);    // ADC1を使用する設定
                    v_dr[j] = adc_read();   // ダイオード + 抵抗の電圧
                    v_d_cp[j] = v_d[j];     // ダイオードの電圧のコピー
                }

中央値導出

qsort()関数を用いて並べ替えをしているが、このときに使用する並べ替えのための関数は自作する必要がある。

ダイオードの電圧の中央値を取り、そのときにあたる電流測定用抵抗にかかる電圧の中央値をとる。

観測値を加工したくはないため、偶数個ある観測値の中央値をとる場合は、真ん中にある2つの観測値のうち小さな要素番号のものを中央値として採用している。

int cmp_func(const void* x, const void* y)
{
	if (*(int16_t*)x > *(int16_t*)y)
	{
		return 1;
	}
	else if (*(int16_t*)x < *(int16_t*)y)
	{
		return -1;
	}
	else
	{
		return 0;
	}
}
                qsort(v_d_cp, NUM_MEASUREMENT, sizeof(int16_t), cmp_func);   // 並べ替え
                // 中央値探し
                k = 0;
                for (j = 0; j < NUM_MEASUREMENT; j++)
                {
                    if (v_d[j] == v_d_cp[NUM_MEASUREMENT / 2])
                    {
                        cnddt_v_r[k] = v_dr[j] - v_d[j];
                        k++;
                    }
                }
                qsort(cnddt_v_r, NUM_MEASUREMENT, sizeof(int16_t), cmp_func);   // 並べ替え
                if (k % 2 == 0)
                {
                    printf("%d\t%d\r\n", v_d_cp[NUM_MEASUREMENT / 2], cnddt_v_r[NUM_MEASUREMENT - k / 2]);       // 観測値を加工したくないため、本来の中央値の定義とは異なる。
                }
                else
                {
                    printf("%d\t%d\r\n", v_d_cp[NUM_MEASUREMENT / 2], cnddt_v_r[NUM_MEASUREMENT - k / 2 - 1]);
                }

測定終了処理

測定後は測定回路に電圧をかける必要がないため、PWM出力のデューティー比を0にする。

このとき、デューティー比を0にしてPWMを有効化してから、反映されるまで1[ms]待って無効化する。

            pwm_set_chan_level(slice_num, channel_str, 0);  // NUM_OF_PWM_STGサイクル中0サイクルをHにする。PWM出力を初期化
            pwm_set_enabled(slice_num, true);       // スライス毎PWMを有効化する。
            sleep_ms(1);    // PWM出力に反映されるために時間がかかるようだ。
            pwm_set_enabled(slice_num, false);      // スライス毎PWMを無効化する。
            sleep_ms(200); // 定常状態になるのを待つ、スライス毎PWMを無効化する前でないと、電圧が初期化されない。

補足

ここでは解説しないが、MicroPythonやArduino IDEでも実装を行った。

Arduino IDEの実装は、Arduino Uno Rev3 / R4 Minimaで行ったものとほぼ同じである。

以下にそれぞれアップロードした。

MicroPython

Arduino IDE

補足

ログインなしでOpenJDKダウンロード

Raspberry Pi Picoのソースコンパイルには、OpenJDKが必要である。

このとき、ドキュメントから飛べるものは、RedHatがビルドしたものである。

残念なことに、RedHatがビルドしたバージョンをダウンロードするには、RedHatアカウントが必要である。

しかし、代わりにMicrosoftのビルドしたバージョンを使う場合は、アカウントなしでダウンロードできる。

Microsoftがビルドしたバージョンは、以下のURLからダウンロードできる。

Microsoft Build of OpenJDK

対応OSは、macOS、Linux、Windowsである。

日本語パスによるninjaのエラー

コンパイル時はninjaを使うが、日本語の入ったパスで行うとエラーが出る。

したがって、日本語の入らないパスをワークスペースとする必要がある。

試しに日本語の入ったパスにサンプルのblink.cを置いてコンパイルすると、以下のようなエラーが生じる。

#include エラーが検出されました。compile_commands.json または includePath を更新してみてください。この翻訳単位では、波線が無効になっています。",
ソース ファイルを開けません pico_config_extra_headers.h

Zadigをインストールして設定

Raspberry Pi Pico上でRunするとき、以下のようなエラーが出ることがある。

「Getting started with Raspberry Pi Pico」に書いている通り、Zadigをインストールして設定する。

RP2040 device at bus 2, address 53 appears to be in BOOTSEL mode, but picotool was unable to connect. You may need to install a driver via Zadig. See "Getting started with Raspberry Pi Pico" 
    for more information

Zadigは、起動したときの設定で、[Install Driver]ボタンを押下するだけで設定できる。

pico-sdkソースの流用

Raspberry Pi Picoで作成したpico-sdkを用いたソースをRaspberry Pi Pico 2に流用するときは、CMakeList.txtの「set(PICO_BOARD pico2 CACHE STRING "Board type")」のみを変更する。

前回(4-1. マイコン側ソフト設計 - Arduino Uno Rev3 / R4 Minima)

次回(5-1. 実験 - 1N4148の測定でマイコンとソースの組み合わせを比較)

他の回

「ダイオードI-V特性測定器作成 連載記事」でタグ付けを行っている。

広告

Amazonで定番になりそうな、Raspberry Pi Pico 2 Wスターターキット。

広告