TOPに戻る

Raspberry Pi PicoとMOSFET ③ PWMのAPI

 PicoからPWM出力をします。PWMはどのGPIOからでも出力できます。ここでは、GPIO2、GPIO3、GPIO4、GPIO5の4本を最初に利用します。

PWMの周波数

 Picoは125MHzのクロックで動いています。このクロックを2種類の分周機構で低い周波数のPWM信号を得られるように作られています。下記の二つは縦続接続されているので、どちらか一方を使ってもよいし、両方を使って細かな周波数を得ることもできます。

  • wrap(16ビット) pwm_set_wrap(slice_num, 99);  // 1/100
  • 固定小数点形式で指定する分周ユニット

   uint8_t div = 1;
   uint8_t fract = 4;
   pwm_set_clkdiv_int_frac(slice_num, div, fract);  // 1/1.25

  上記以外に、

   float div = 3;
   pwm_config_set_clkdiv(&config, div);

 構成オブジェクトをつかって分周できますが、使いかたがよくわかりません。

 このファンクションは、PWMの出力以外に、パルスのカウントもできます。

PWMディバイダ・モードの設定内容

 enum pwm_clkdiv_mode { PWM_DIV_FREE_RUNNING = 0, PWM_DIV_B_HIGH = 1, PWM_DIV_B_RISING = 2, PWM_DIV_B_FALLING = 3 }

pwmファンクション

スライス PWM出力の論理番号。

フェーズ 位相。位相補正で使われる。

チャネル すべてのGPIOピンは、PWM出力に使えるが、次のようにチャネルが割り付けられている。一つのチャネルにはAとBがある。8チャネルしかないので、GPIO16以降は、再度0から。なお、23、25、25、29はピンが存在しない。

GPIO 0 1 2 3 4 5 6 7
PWMチャネル 0A 0B 1A 1B 2A 2B 3A 3B

GPIO 8 9 10 11 12 13 14 15
PWMチャネル 4A 4B 5A 5B 6A 6B 7A 7B

GPIO 16 17 18 19 20 21 22 23
PWMチャネル 0A 0B 1A 1B 2A 2B 3A 3B

GPIO 24 25 26 27 28 29
PWMチャネル 4A 4B 5A 5B 6A 6B

構成オブジェクト 初期値=(システム・クロック速度でフリー・ランニング、位相補正なし、0xffffでのwrapする、標準極性、チャネルAおよびB)?

ファンクション 要約
static uint pwm_gpio_to_slice_num (uint gpio) 指定されたGPIOに接続されているPWMスライスが返る
static uint pwm_gpio_to_channel (uint gpio) 指定されたGPIOに接続されているPWMチャネルが返る
static void pwm_config_set_phase_correct (pwm_config *c, bool phase_correct) 位相制御をtrueに設定すると、wrapポイントに達したときにゼロにラップバックする代わりに、PWMがカウント・ダウンを開始する(下の図参照)。位相補正モードを有効にすると、出力周波数が半分になる
static void pwm_config_set_clkdiv (pwm_config *c, float div) PWM構成でクロック分周器を設定する
static void pwm_config_set_clkdiv_int (pwm_config *c, uint div) PWMクロック分周器をPWM構成に設定する
static void pwm_config_set_clkdiv_mode (pwm_config *c, enum pwm_clkdiv_mode mode) PWM構成でPWMカウント・モードを設定する
static void pwm_config_set_output_polarity (pwm_config *c, bool a, bool b) PWM構成で出力極性を設定する
static void pwm_config_set_wrap (pwm_config *c, uint16_t wrap) PWM構成でPWMカウンタwrap値を設定する。
0に戻る前にカウンタが到達する最大値を設定。TOPとも呼ばれる
static void pwm_init (uint slice_num, pwm_config *c, bool start) 構成オブジェクトからの設定を使用してPWMを初期化する
static pwm_config pwm_get_default_config (void) PWM構成(システム・クロック速度でフリー・ランニング、位相補正なし、0xffffでのラッピング、標準極性、チャネルAおよびB)のデフォルト値のセットを取得する
static void pwm_set_wrap (uint slice_num, uint16_t wrap) PWMカウンタwrap値を設定する。
0に戻る前にカウンタが到達する最大値を設定。TOPとも呼ばれる
static void pwm_set_chan_level (uint slice_num, uint chan, uint16_t level)
チャネルAまたはチャネルBのいずれかのPWMカウンタ比較値の値を設定する。
カウンタ比較レジスタは、ハードウェアでダブル・バッファリングされている。これは、PWMが実行されているときに、カウンタ比較値は、次にPWMスライスがラップするまで(または、位相補正モードでは、次回スライスが0に達したとき)。PWMが実行されていない場合、書き込みはすぐにラッチされる。
 chan 更新するチャネル。Aの場合は0、Bの場合は1
static void pwm_set_both_levels (uint slice_num, uint16_t level_a, uint16_t level_b)
PWMカウンタの比較値AとBの値を設定する。
カウンタ比較レジスタは、ハードウェアでダブル・バッファリングされている。これは、PWMが実行されているときに、カウンタ比較値は、次にPWMスライスがラップするまで(または、位相補正モードでは、次回スライスが0に達したとき)。PWMが実行されていない場合、書き込みはすぐにラッチされる。
カウンタが設定値に達すると、AもしくはB出力がディアサートされる
static void pwm_set_gpio_level (uint gpio, uint16_t level)
GPIOに関連付けられたスライスとチャネルのPWMレベルを設定するヘルパ関数。
特定のGPIOの正しいスライス(0から7)とチャネル(AまたはB)を検索し、対応するカウンタ比較フィールドを更新。
このPWMスライスは、すでに構成され、実行に設定されている必要がある。また、複数のGPIOがにマッピングされることに注意。
同じスライスとチャネル(GPIOの差が16の場合)。
カウンタ比較レジスタは、ハードウェアでダブル・バッファリングされている。これは、PWMが実行されているときに、カウンタ比較値は、次にPWMスライスがラップするまで(または、位相補正モードでは、次回スライスが0に達したとき)。PWMが実行されていない場合、書き込みはすぐにラッチされる
static uint16_t pwm_get_counter (uint slice_num) PWMカウンタの現在の値を取得する
static void pwm_set_counter (uint slice_num, uint16_t c) PWMカウンタの値を設定する
static void pwm_advance_count (uint slice_num) カウンタを実行するフェーズを1カウント進める
static void pwm_retard_count (uint slice_num) 実行中のカウンタのフェーズを1カウント遅らせる
static void pwm_set_clkdiv_int_frac (uint slice_num, uint8_t integer, uint8_t fract) 8(integer整数):4(fract小数値)を使用してPWMクロック分周器を設定する
static void pwm_set_clkdiv (uint slice_num, float divider)

クロック分周器を設定する。カウンタの増分は、ゲーティングを考慮して、sysclockをこの値で割った値になる。

divider  1.f ⇐ value < 256.f

static void pwm_set_output_polarity (uint slice_num, bool a, bool b) PWM出力の極性を設定する。trueで反転
static void pwm_set_clkdiv_mode (uint slice_num, enum pwm_clkdiv_mode mode) PWM分周器モードを設定する
static void pwm_set_phase_correct (uint slice_num, bool phase_correct)
位相制御をtrueに設定すると、wrapポイントに達したときにゼロにwrapバックせずに、PWMがカウントダウンを開始する。位相補正モードを有効にすると、出力周波数が半分になる
static void pwm_set_enabled (uint slice_num, bool enabled) PWMを有効/無効にする。trueは指定されたPWMを有効にし、falseは無効にする
static void pwm_set_mask_enabled (uint32_t mask) 複数のPWMスライスを同時に有効/無効にする
static void pwm_set_irq_enabled (uint slice_num, bool enabled) PWMインスタンス割り込みを有効にする
static void pwm_set_irq_mask_enabled (uint32_t slice_mask, bool enabled) 複数のPWMインスタンス割り込みを有効にする
static void pwm_clear_irq (uint slice_num) 単一のPWMチャネル割り込みをクリアする
static uint32_t pwm_get_irq_status_mask (void) 現在設定されているすべてのPWM割り込みのビット・マスクを取得する
static void pwm_force_irq (uint slice_num) PWM割り込みを強制する

 通常のwrapは0からカウントアップしてTOPになるとすぐに0に戻り、またカウントアップを始める

 位相補正モード(phase_correct)を有効にすると、TOPになった後カウントダウンする

wrapを使ってみる

 pico/worksフォルダにpwmフォルダを作ります。なかに、CMakeLists.txtとpwm.cを入れます。コンパイルの方法などは、下記の連載を参照してください。

  連載 Raspberry Pi Picoでプログラミング

 pico/worksフォルダのCMakeLists.txtの内容です。


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(cmake)
add_subdirectory(blink)
add_subdirectory(serial)
add_subdirectory(clock)
add_subdirectory(i2cscanner)
add_subdirectory(lps25hb)
add_subdirectory(tmp117)
add_subdirectory(aht20)
add_subdirectory(mcp3008)
add_subdirectory(mcp3208)
add_subdirectory(pwm)

 pico/works/pwmフォルダのCMakeLists.txtの内容です。


add_executable(pwm
        pwm.c
        )

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

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

pwm.cの内容

 GPIO機能を選択するファンクションでGP2/GP3端子をPWM用にします。

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

 スライスはチャネルの低いほうのGPxの番号xをあてるようです。

uint slice_num = pwm_gpio_to_slice_num(2);

 wrapを125にしました。0から数えるので、124を記入しています。上記のファンクションの説明では、0からTOPが1周期です。TOPは初期値では0xffffが設定されているようです。この関数の解説では、「0に戻る前にカウンタが到達する最大値を設定」となっているので、0xffffより小さな数値のように思えます。

 しかし、この関数では、wrapのカウント数ぶん分周するという動作をします。システム・クロックは125MHzですから、125で割ると1MHzです。オシロスコープの計測でも1MHzになっていました。

pwm_set_wrap(slice_num, 124);

pwm_set_chan_level(slice_num, PWM_CHAN_A, 12);

は、赤色の波形で、12/125=約10%のデューティになります。

pwm_set_chan_level(slice_num, PWM_CHAN_B, 63);

は、黄色の波形で、63/125=約50%のデューティになります。

 pwm.cのソースです。


#include "pico/stdlib.h"
#include <stdio.h>
#include "hardware/pwm.h" int main() { stdio_init_all(); printf("\nHello, PWM GP2/3\n"); gpio_set_function(2, GPIO_FUNC_PWM); gpio_set_function(3, GPIO_FUNC_PWM); // Find out which PWM slice is connected to GPIO 0 (it's slice 0) uint slice_num = pwm_gpio_to_slice_num(2); // Set period of 10 cycles (0 to 3 inclusive) pwm_set_wrap(slice_num, 124); // Set channel A output high for one cycle before dropping pwm_set_chan_level(slice_num, PWM_CHAN_A, 12); // Set initial B output high for three cycles before dropping pwm_set_chan_level(slice_num, PWM_CHAN_B, 63); // Set the PWM running pwm_set_enabled(slice_num, true); return 0; }

連載 Raspberry Pi PicoとMOSFET

(1) MOSFETの特徴

(2) MOSFETを発振器でドライブ

(3) PWMのAPI

(4) PWM 分周

(5) NチャネルMOSFETをPicoからドライブ

(6) PチャネルMOSFETをPicoからドライブ

(7) P/NチャネルMOSFETをPicoからドライブ<その1>

(8) P/NチャネルMOSFETをPicoからドライブ<その2>


連載 Raspberry Pi Picoでプログラミング

(1) ラズパイ4の準備(1) USBブートの設定

(2) ラズパイ4の準備(2) 標準入出力の用意

(3) ラズパイ4の準備(3) LチカとHello, world!の実行

(4) ラズパイ4の準備(4) リモート環境の設定

(5) プログラミングの環境整備とLチカ

(6) Hello, World!

(7) 使用するピンと機能

(8) クロックの値の表示

(9) i2cscanner

(10) i2c APIと気圧センサLPS25

(11) i2c 温度センサTMP117

(12) i2c 湿度センサAHT20

(13) spi APIとA-DコンバータMCP3008

(14) spi A-DコンバータMCP3208

(15) gpioファンクション

(16) gpio スイッチを押すとLEDが点灯する STEP2

(17) gpioファンクション MASK