前のページでPWMライブラリを使ってPWM出力をしてみました。そこではD0ピン、A5ピンの出力波形を示しました。2つのパルスは周期は同じに設定しましたが、立ち上がりタイミングはずれています。
GPTタイマーでのPWM出力はカウンタ値が0になったら出力をHIGHにする、と以前書きました。つまりPWMの立ち上がりのずれの元は、GPTをスタートさせる時間差ですね。その時間差は、begin()でPWMを設定するたびにGPTのカウントが個別にスタートするために生じます。
用途によってはPWMの立ち上がりエッジをそろえたいこともあります。GPTにはイベントが発生したらカウンタをクリアする機能がありますので、それによって別々にスタートしたGPTを強制的にそろえてみましょう。そのためには、イベントリンク機能とそれを設定するためのFspLinkIrq.hライブラリを使います。
ーーーーー 目次 ーーーーー
PWM出力(2) - PWMライブラリ + イベントリンク (このページ)
イベントリンク機能とは
イベントリンク機能とは、周辺機能などで発生したイベントを別の周辺機能に送って何らかの動作のトリガーにする機能です。

上の図の左側の周辺機能で発生したイベントは、いったんイベントリンクコントローラELCで記憶します。図の右側、送られるほうの周辺機能はELCに記憶された情報を受け取って特定の動作をします。送られ側になる周辺機能と設定可能な動作は以下になります。

この機能を設定するためには、ELCの設定と送られ側の周辺機能の両方の設定が必要です。ELCのほうは、FspLinkIrq.hライブラリを使って記憶すべきイベント番号を設定します。送られる側の周辺機能のほうは、通常はそれをハンドリングしているライブラリ、GPTタイマーではFspTimer.hライブラリを使って、イベントが発行されたことをELCの特定の場所から受け取るよう設定します。
イベントリンクを設定する方法
UNO R4 MINIMAを使って別々のタイマーGPT0とGPT1でPWMを出力し、その後特定の条件でイベントリンクを作動させ、タイマーカウンタをクリアして同期させるプログラムを作ってみました。GitHubに置いてあり、プログラム名は pwm_by_pwmlib_with_sync.ino です。ここでは主要部分を説明します。
https://github.com/inteGN/Arduino_UNO_R4_GPT_PWM_Examples
pwmD3.begin(pwm_reriod, pwm_count0, true, TIMER_SOURCE_DIV_1); //RA4M1 P104 GPT1_B
pwmD6.begin(pwm_reriod, pwm_count0, true, TIMER_SOURCE_DIV_1); //RA4M1 P106 GPT0_B
timer6.begin(TIMER_MODE_PERIODIC, GPT_TIMER, 6, pwm_reriod, pwm_count0, TIMER_SOURCE_DIV_1);
timer6.open();
timer6.start(); //GPT6 set and start
こちらはタイマーの起動部分です。pwm.hライブラリを使いD3、D6ピンからPWM出力します。この時、GPT0、GPT1のタイマーが使われます。またそれらとは別にGPT6を同じ周期で起動しておきます。GPT6はFspTimer.hライブラリで起動しています。
delay(20000);
//GPT0 clear by GPT1 overflow thru ELC_GPT_A
elcLink.linkGPTimerIrq(1, LINK_GPT_OVERFLOW, ELC_PERIPHERAL_GPT_A);
R_GPT0->GTCSR_b.CSELCA = 1; //GPT0 clear by ELC_GPT_A event
delay(20000);
//GPT0 and GPT1 clear by GPT6 overflow thru ELC_GPT_A
elcLink.linkGPTimerIrq(6, LINK_GPT_OVERFLOW, ELC_PERIPHERAL_GPT_A);
R_GPT1->GTCSR_b.CSELCA = 1; //GPT1 clear by ELC_GPT_A event, GPT0 clear is already set
起動から20sec後に、第一のイベントリンクを設定します。linkGPTimerIrq()によって、GPT1のオーバーフローをELC_PERIPHERAL_GPT_Aという場所に記憶できるようにします。その次にELC_PERIPHERAL_GPT_AによってGPT0がクリアされるよう、GPT側の設定をします。ここで、pwm.hはGPT のクリア条件を設定する関数を提供しておらず、また内部のFspTimerインスタンスも隠蔽しています。そのため、この部分だけレジスタ直接アクセスで設定しています。
さらに20sec経過したら、第二のイベントリンクを設定します。同じくlinkGPTimerIrq()によってGPT6のオーバーフローをELC_PERIPHERAL_GPT_Aに記憶するよう設定します。さらに同様に、ELC_PERIPHERAL_GPT_AによってGPT1がクリアされるよう、レジスタ直接アクセスで設定します。
PWMの波形を見てみると
このプログラムでの波形を示します。下の1つめは起動直後のもので、2つのPWMの立ち上がりのタイミングが明らかにずれています。

次は20sec後、GPT1のオーバーフローでGPT0をクリアした波形です。微妙なので時間を拡大しています。GPT1がそのオーバーフローで0になり、その時発生したイベントでGPT0をクリアするとPWMの立ち上がりのタイミングが揃います。が、わずかにズレが残っています。イベントリンク機能はハードウェアで構成されていて高速なのですが、それでもGPT1のオーバーフロー → ELC記憶 → GPT0クリアの伝搬に数クロック時間が掛かっているためと推定されます。

さらに20sec後、別に走らせておいたGPT6のオーバーフローでGPT0、GPT1の両方をクリアしています。これであればGPT0とGPT1にずれはなく、PWMの立ち上がりのタイミングは完全に揃っています。

pwm.hライブラリではタイマーを個別にbegin()するしかないし、begin()でカウントが始まってしまいます。PWMの立ち上がりを揃えたいときは、このように別のタイマーからイベントを発生させてカウンタをクリアして合わせる、という手法がひとつ使えます。
補足
イベントリンクで使うイベント番号は、対応する割り込み要因のイベント番号と同一です。またそれを扱うArduinoコアライブラリの名称はFspLinkIrq.hで、割り込みに関係ありそうなIrqという記号が入っています。ですが機能的にも設定方法も割り込み関係のレジスタとは異なります。ELCは割り込みを使わずに周辺機能同士を直接つなぐ仕組み、と考えると理解しやすいです。
ArduinoコアライブラリではFspLinkIrq.hライブラリは高位の抽象化がされておらず、そのためRA4M1ハードウェアマニュアルのELCの内容をある程度把握しておく必要があります。この点でも、コアライブラリにあるIRQManager.hの作法にしたがって割り込みを設定していくのとは異なる使い方になります。