
前回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なら良しといたしましょう。今度は実験の回路作るのがまたメンドイ。


