前回PWMで生成したクロックで十分かな~と思ってしまったので、もう一度似たようなことをやるのはカッタるいです。でも自分でPIO(プログラマブルIO)でもクロック作ってみると書いてしまったのでやらないと。大分ラズパイPicoのPIOにも慣れたんですが、PIOアセンブラでたった2行。けれどヘッダ部分書くのがメンドイのよね。
※「Pico三昧」投稿順 index はこちら
PIOでのFractional分周器の設定
前回PWMを使ってクロック生成したときは、周波数の計算のパラメータが多いので、いくつにしたらよいかチト迷いました。今回PIO(プログラマブルIO)アセンブラでの場合は、ひたすら1と0を繰り返すだけのパターン、2分周器としてSMを動かすので、Fractional分周器自体は
4.096×2=8.192MHz
が生成できればよいです。PIOのFractional分周器は小数点以下8ビットもあるゴージャスなもの(PWMは小数点以下4ビット)なので選択はたやすいです。ラズパイPicoの sysclk=125MHzに対して小数点以下は66/256=0.2578125で
125 / 15.2578125 ≒ 8.1925 MHz
と「かなり」近い値となります。周波数の計算はPIOの方が楽。
試作ソース
まずは、ビルドに使う CMakeLists.txt から。いつものように自動生成のCMakeLists.txtをベースとしますが、PIOを使用する場合、毎度書いているように
pico_generate_pio_header
のところは、手動で追加しないとPIOアセンブラのソースからヘッダを自動生成してくれません。メンドイ。
# 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(clkGenPIO 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(clkGenPIO clkGenPIO.c ) pico_set_program_name(clkGenPIO "clkGenPIO") pico_set_program_version(clkGenPIO "0.1") pico_enable_stdio_uart(clkGenPIO 1) pico_enable_stdio_usb(clkGenPIO 0) # Add the standard library to the build target_link_libraries(clkGenPIO pico_stdlib) pico_generate_pio_header(clkGenPIO ${CMAKE_CURRENT_LIST_DIR}/clkGenPIO.pio) # Add any user requested libraries target_link_libraries(clkGenPIO hardware_pio ) pico_add_extra_outputs(clkGenPIO)
続いてPIOアセンブラのソースです。以下のコードをご覧頂いたら分かるとおり、アセンブラ、たったの2行です。それも NOP。NOPループからクロックが紡ぎだされるのは side 出力のお陰です。ただ1と0を繰り返すだけ。
しかし、例によってPIOアセンブラを呼び出すヘッダファイル部分がメンドイです。特に今回気をつけないとならないのは、ここにFractional分周器の設定があることです。以下のところであります。
sm_config_set_clkdiv_int_frac
ここに整数部分15, 小数部分66と「ハードコード」してしまっています。こんな感じ。
; 4.096MHz clock generation by PIO ; spin CLK_OUT(16) .program clkGenPIO .side_set 1 .wrap_target nop side 1 nop side 0 .wrap % c-sdk { #include "hardware/gpio.h" static inline void clkGenPIO_program_init(PIO pio, uint sm, uint offset, uint spin) { pio_sm_config c = clkGenPIO_program_get_default_config(offset); sm_config_set_sideset_pins(&c, spin); pio_gpio_init(pio, spin); pio_sm_set_consecutive_pindirs(pio, sm, spin, 1, true); sm_config_set_clkdiv_int_frac(&c, 15, 66); pio_sm_init(pio, sm, offset, &c); pio_sm_set_enabled(pio, sm, true); } static inline void clkGenPIO_program_stop(PIO pio, uint sm) { pio_sm_set_enabled(pio, sm, false); } %}
最後にCのmain関数部分です。「伝統的なLチカ」Cコードの前でPIOアセンブラの上記コードを呼び出しています。上記はPIOステートマシンの初期化さえ済ませれば勝手に走りつづけます。initした後はCPUは何もすることがありません(それでLチカしてます、ハートビート信号的な?)
#include <stdio.h> #include "pico/stdlib.h" #include "hardware/pio.h" #include "clkGenPIO.pio.h" #define CLK_OUT (16) #define LED_PIN (25) int main() { stdio_init_all(); puts("Generate 4.096MHz clock by PIO. v001"); PIO pio = pio0; uint sm = pio_claim_unused_sm(pio, true); uint offset = pio_add_program(pio, &clkGenPIO_program); clkGenPIO_program_init(pio, sm, offset, CLK_OUT); gpio_init(LED_PIN); gpio_set_dir(LED_PIN, GPIO_OUT); 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; }
実機動作の確認
端子にオシロ(Digilent Analog Discovery2)をつけて観察した時間波形が以下に、周波数の測定機能を使って測定した周波数は4.0938MHzとな。
前回、クロックの品質が気になるぜ、ということでFFTかけてみました。こんな感じ。
これに対して、前回のPWMで生成したクロックにFFTかけるとこんな感じ。
たまたまなのかも知れないけれど、前回のPWMで生成したクロックの方が「横の邪魔者」が抑止されているんでないかい。ホントか。
大した用途でないから、どちらでも構わんですか。まあ実際に使ってみてOKなら良しといたしましょう。今度は実験の回路作るのがまたメンドイ。