あるデバイスを動かそうとしたら4.096MHzのクロックが必要と書いてありました。手元には4MHzピタリの水晶振動子の在庫はあったと思ったですが、4.096MHzありません。ラズパイPicoで制御するつもりなのだし、ラズパイPicoでクロックから作ってしまえ、という目論見。「普通のマイコン」ならタイマをいじるところですが、ラズパイPicoでは違う、と。
※「Pico三昧」投稿順 index はこちら
ラズパイPicoの場合、タイマは本当に時間だけに引っ付いている感じで、入出力を制御したい場合は、以下の2つのどちらかを選択することになります。
-
- PWM
- 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 を入れておかないとなりませぬ。
実行結果
測定している様子をアイキャッチ画像に掲げました。こんな感じ。
4.0964MHz、デューティは約49%とな、目論見よりも良い感じでないかい。しかし、上記は「たまたま良い方に転んだ時」の測定。
駄目なときはこんな感じ。目論見の倍くらいの誤差があるようです。
ジッタというか、揺らぎがあるので、これを評価する方法を考えないとならない気がします。ま、ホンワカした線で良いのだけれど。出来るのかい?
ま、次回はPIO使ったクロック生成を試みてみます。