初めてのRaspberry Pi Pico ⑯ C言語 PWM その1


 第6回のように、pico/examplesではなく、pico/worksで作業をしています。
 pico/examples/pwm/からhello_pwmフォルダをpico/works/フォルダにコピーしてきます。pico/works/フォルダにあるCMakeLists.txtを次のように、最後に1行追加します。

cmake_minimum_required(VERSION 3.12)

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

project(pico_examples C CXX ASM)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)

set(PICO_EXAMPLES_PATH ${PROJECT_SOURCE_DIR})

# Initialize the SDK
pico_sdk_init()

# Add blink example
add_subdirectory(blink)
add_subdirectory(cmake)
add_subdirectory(LED7seg_74HC595)
add_subdirectory(bus_scan)
add_subdirectory(lcd_1602_i2c)
add_subdirectory(CRC8)
add_subdirectory(adt7410)
add_subdirectory(hello_pwm)

 /home/pi/pico/works/hello_pwmには、CMakeLists.txtとhello_pwm.cの二つのファイルが入っています。CMakeLists.txtは次のように、最後の行をコメントアウトします。

add_executable(hello_pwm
        hello_pwm.c
        )

# Pull in our pico_stdlib which pulls in commonly used features
target_link_libraries(hello_pwm pico_stdlib hardware_pwm)

# create map/bin/hex file etc.
pico_add_extra_outputs(hello_pwm)

# add url via pico_set_program_url
#example_auto_set_url(hello_pwm)

 第6回とは異なり、二つのCMakeLists.txtでは、urlのセットを使っていません(消しています。コメントアウトしています)。残したまま(生かしたまま)でも、支障はありません。

 hello_pwm.cを次のように修正します。元はGPIO0とGPIO1にPWM出力を出していますが、この二つは、printfの出力に使っているので避けます。

/**
 * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

// Output PWM signals on pins 0 and 1

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

int main() {
    stdio_init_all();  // need when UART 

    printf("\nstart GPIO 2,3\n");

    /// \tag::setup_pwm[]

    // Tell GPIO 0 and 1 they are allocated to the PWM
    gpio_set_function(2, GPIO_FUNC_PWM);
    gpio_set_function(3, GPIO_FUNC_PWM);

    // Find out which PWM slice is connected to GPIO 2 (it's slice 0)
    uint slice_num = pwm_gpio_to_slice_num(2);

    // Set period of 4 cycles (0 to 3 inclusive)
    pwm_set_wrap(slice_num, 3);
    // Set channel A output high for one cycle before dropping
    pwm_set_chan_level(slice_num, PWM_CHAN_A, 1);
    // Set initial B output high for three cycles before dropping
    pwm_set_chan_level(slice_num, PWM_CHAN_B, 3);
    // Set the PWM running
    pwm_set_enabled(slice_num, true);
    /// \end::setup_pwm[]

    // Note we could also use pwm_set_gpio_level(gpio, x) which looks up the
    // correct slice and channel for a given GPIO.
}

make

/home/pi/pico/works/buildにおります。

cmake ..

 これは一度だけ実行します。最初になかったhello_pwmができているので、このディレクトリにおります。

make -j4

make -j4を実行すると、このフォルダ内にhello_pwm.uf2ができています。
 第6回で作ったResetボタンを使います。Resetボタンを押したまま、BOOTSELボタンを押し、Resetボタンを放してから、BOOTSELボタンを放します。これでRPI-RP2ディスクがマウントができます。これにhello_pwm.uf2をドロップします。
 cmakeは1回だけ実行、makeはソースを修正するたびに実行します。ビルドされるとき、リンクされるライブラリ類は最初の1回だけコンパイルされるので、時間がかかるのは最初の1回だけです。

 その前に、第3回で解説した「標準出力を用意」でターミナルを動かしておきます。このターミナルに「start GPIO 2,3」のメッセージが出力されます。
 GPIO2,3をオシロスコープで見ます。黄色(上)がGPIO2、下の緑色がGPIO3です。

パラメータを変えて何が変化するのかを確認する

 2か所変更します。wrapを3から5へ、Bのchan_levelを3から5へ変更します。

    // Set period of 4 cycles (0 to 3 inclusive)
    pwm_set_wrap(slice_num, 5);  // 3->5
    // Set channel A output high for one cycle before dropping
    pwm_set_chan_level(slice_num, PWM_CHAN_A, 1);
    // Set initial B output high for three cycles before dropping
    pwm_set_chan_level(slice_num, PWM_CHAN_B, 3);  //3->5

 波形です。周波数が下がりました。GPIO3のHigh期間が長くなりました。

 slice_numは0~7の範囲を取り、物理的なピン番号GPIOxを論理的な数値として扱います。slice_numはAとBがそれぞれあるので、扱えるチャネル数は16になります。GPIOは30個ありますが、

    gpio_set_function(2, GPIO_FUNC_PWM);
    gpio_set_function(3, GPIO_FUNC_PWM);

 で、どのピンをPWMように使うかをセットし(物理ピン番号ではなくGPIOxのxの数値で指定)、

uint slice_num = pwm_gpio_to_slice_num(2);

 GPIO2を論理番号slice 0に割り当てています。

 wrapは、カウンタを0からffffまでカウントアップして'1'となります。すぐに0にもどり、カウントアップします。カウンタの周波数は、ここでは不明です。
 最初のプログラムではwrapは4でした。変更してwarpを6にしました。
 31.4MHzの周期は31.8471nsです。20.9MHzの周期は47.8469nsです。1warp=7.961775もしくは7.97448です。7.97nsとして逆数は125470kHzです。

クロックの情報をもっと調べる

 clkで検索して、それらしいソースをマニュアルから見つけ、追加しました。

/**
 * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

// Output PWM signals on pins 0 and 1

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

int main() {
    stdio_init_all();  // need when UART 

    printf("\nstart GPIO 2,3\n");

    /// \tag::setup_pwm[]

    // Tell GPIO 0 and 1 they are allocated to the PWM
    gpio_set_function(2, GPIO_FUNC_PWM);
    gpio_set_function(3, GPIO_FUNC_PWM);

    // Find out which PWM slice is connected to GPIO 2 (it's slice 0)
    uint slice_num = pwm_gpio_to_slice_num(2);

    // Set period of 4 cycles (0 to 3 inclusive)
    pwm_set_wrap(slice_num, 5);
    // Set channel A output high for one cycle before dropping
    pwm_set_chan_level(slice_num, PWM_CHAN_A, 1);
    // Set initial B output high for three cycles before dropping
    pwm_set_chan_level(slice_num, PWM_CHAN_B, 5);
    // Set the PWM running
    pwm_set_enabled(slice_num, true);
    /// \end::setup_pwm[]

    // Note we could also use pwm_set_gpio_level(gpio, x) which looks up the
    // correct slice and channel for a given GPIO.
uint f_pll_sys = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_PLL_SYS_CLKSRC_PRIMARY);
uint f_pll_usb = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_PLL_USB_CLKSRC_PRIMARY);
uint f_rosc = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_ROSC_CLKSRC);
uint f_clk_sys = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_CLK_SYS);
uint f_clk_peri = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_CLK_PERI);
uint f_clk_usb = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_CLK_USB);
uint f_clk_adc = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_CLK_ADC);
uint f_clk_rtc = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_CLK_RTC);

printf("pll_sys = %dkHz\n", f_pll_sys);
printf("pll_usb = %dkHz\n", f_pll_usb);
printf("rosc = %dkHz\n", f_rosc);
printf("clk_sys = %dkHz\n", f_clk_sys);
printf("clk_peri = %dkHz\n", f_clk_peri);
printf("clk_usb = %dkHz\n", f_clk_usb);
printf("clk_adc = %dkHz\n", f_clk_adc);
printf("clk_rtc = %dkHz\n", f_clk_rtc);
printf("-----\n");

}

ターミナルには、次のように表示が出ました。

pll_sys = 125000kHz                             
pll_usb = 48000kHz                              
rosc = 5093kHz                                  
clk_sys = 125000kHz                             
clk_peri = 125000kHz                            
clk_usb = 48000kHz                              
clk_adc = 48000kHz                              
clk_rtc = 47kHz   

 1warpは125470kHzでした。システム・クロックにほぼ等しいと推定できます。
周波数カウンタHP 5334Bで測ると20.8333794MHzでした。1warp=7.99998231nsは125000.2764kHzです。システム・クロックclk_sysそのものですね。