Pico三昧(12) Pico C/C++SDKで4.096MHzクロック生成その1PWM

Joseph Halfmoon

あるデバイスを動かそうとしたら4.096MHzのクロックが必要と書いてありました。手元には4MHzピタリの水晶振動子の在庫はあったと思ったですが、4.096MHzありません。ラズパイPicoで制御するつもりなのだし、ラズパイPicoでクロックから作ってしまえ、という目論見。「普通のマイコン」ならタイマをいじるところですが、ラズパイPicoでは違う、と。

ラズパイPicoの場合、タイマは本当に時間だけに引っ付いている感じで、入出力を制御したい場合は、以下の2つのどちらかを選択することになります。

  1. PWM
  2. PIO(プログラマブルIO)

この2択を考えた場合、多分PIOの方が精度が良いじゃないかと思ったのです。根拠は分周器の「ゴージャス」さ。ラズパイPico内蔵の分周器はどれも「小数点以下あり」の高機能なものです。結構調整きくように思います。しかし周辺回路により差異があり、PWMの分周器は小数点以下が4ビットなのに対してPIOのは8ビットもあります。PIOの方が細かいです。

ただ、資源の限られたPIOを単純なクロック生成に使ってしまうのは勿体ないと思いました。ラズパイPicoの場合、PWM「スライス」が普通のマイコンのカウンタ・タイマの入出力機能を果たしているのだし、8スライス、16チャンネルとリソース豊富です。PWMを活用できるなら、そちらでクロック生成するのが良いのではないかと思いました。実際には両方やってみて比べるつもりですが、今回はPWMで。

PWMで4.096MHz作るに試作したソース

お馬鹿なことに、つい最近までラズパイPicoのsysclk、133MHzだと思い込んでいました。自分で書いたビットバンギングのコードの周波数を計測して数%遅いのは何故、などと疑問に思っていました。しかしある人から質問されて、調べた結果、

手元のラズパイPicoのデフォルトsysclkは125MHz

だということにようやく気付きました。おお間抜け。ましかし、今回は125MHzを基準にして、PWMの設定値をちゃんと計算いたしましたぞ。セッティングは以下ソースに。なおPWMの周波数の計算方法は、RP2040DatasheetのPWMの項に掲載されています(分周器が高機能なだけメンドイ式です。)

目論見ではこれにて 4.098MHz、デューティ50%のクロックが端子16番から出力される筈。まあ4.096MHzピタリではないけれど誤差は0.1%以下だし。

#include <stdio.h>
#include "pico/stdlib.h"
#include "hardware/gpio.h"
#include "hardware/pwm.h"

#define LED_PIN (25)
#define CLK_OUT (16)
#define TOP_CNT (7)
#define CHG_CNT (4)
#define DIV_INT  (3)
#define DIV_FRAC (13)

uint pwm_slice0;

void startCLK() {
    gpio_set_function(CLK_OUT, GPIO_FUNC_PWM);
    pwm_slice0 = pwm_gpio_to_slice_num(CLK_OUT);
    pwm_set_clkdiv_int_frac(pwm_slice0, DIV_INT, DIV_FRAC);
    pwm_set_wrap(pwm_slice0, TOP_CNT);
    pwm_set_chan_level(pwm_slice0, PWM_CHAN_A, CHG_CNT);
    pwm_set_enabled(pwm_slice0, true);
}

int main()
{
    stdio_init_all();
    puts("PWM 4.096MHz Trial 000.");
    gpio_init(LED_PIN);
    gpio_set_dir(LED_PIN, GPIO_OUT);
    startCLK();
    while (true) {
        gpio_put(LED_PIN, 1);
        sleep_ms(1000);
        gpio_put(LED_PIN, 0);
        sleep_ms(1000);
    }
    puts("End of Execution. <NEVER!>");
    return 0;
}

上記ソースのビルドに使ったCMakeLists.txtが以下に。

# Generated Cmake Pico project file

cmake_minimum_required(VERSION 3.13)

set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)

# initalize pico_sdk from installed location
# (note this can come from environment, CMake cache etc)
set(PICO_SDK_PATH "/home/pi/pico/pico-sdk")

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

project(clkGenPWM C CXX ASM)

# Initialise the Raspberry Pi Pico SDK
pico_sdk_init()

# Add executable. Default name is the project name, version 0.1

add_executable(clkGenPWM clkGenPWM.c )

pico_set_program_name(clkGenPWM "clkGenPWM")
pico_set_program_version(clkGenPWM "0.1")

pico_enable_stdio_uart(clkGenPWM 1)
pico_enable_stdio_usb(clkGenPWM 0)

# Add the standard library to the build
target_link_libraries(clkGenPWM pico_stdlib hardware_pwm)

pico_add_extra_outputs(clkGenPWM)

以前もやりましたが、target_link_librariesに hardware_pwm を入れておかないとなりませぬ。

実行結果

測定している様子をアイキャッチ画像に掲げました。こんな感じ。

pwmResultA

4.0964MHz、デューティは約49%とな、目論見よりも良い感じでないかい。しかし、上記は「たまたま良い方に転んだ時」の測定。

駄目なときはこんな感じ。目論見の倍くらいの誤差があるようです。

pwmResult

ジッタというか、揺らぎがあるので、これを評価する方法を考えないとならない気がします。ま、ホンワカした線で良いのだけれど。出来るのかい?

ま、次回はPIO使ったクロック生成を試みてみます。

Pico三昧(11)Pico C/C++SDKでinterp#5、補間ついでに配列舐める へ戻る

Pico三昧(13)Pico C/C++SDKで4.096MHzクロック生成その2PIO へ進む