Arduino UNO R4のFspTimerライブラリの使い方

2023/7/23追記: APIについて調査した結果を追記
2023/7/26修正・追記: 今後の方針を追加
2023/8/10修正: 誤記およびタグを修正
2023/8/11追加: サンプルコードを追加
## 1. はじめに

Arduino UNO R4の入手性がよくなり、既に入手された方も多いかと思います。
このボードでタイマ割り込みを使う方法を調べていて、FspTimerというライブラリが標準で用意されていることがわかりましたので、使い方を調べてみました。

## 2. FspTimerの実装

FspTimerのヘッダファイルとソースファイルは、以下の場所にあります。

C:\Users\\%USERNAME%\AppData\Local\Arduino15\packages\arduino\hardware\renesas_uno\1.0.2\cores\arduino\FspTimer.h
C:\Users\\%USERNAME%\AppData\Local\Arduino15\packages\arduino\hardware\renesas_uno\1.0.2\cores\arduino\FspTimer.cpp

使い方は？と思ったのですが、APIの説明は見当たりませんでした。

## 3. APIの調査

まずは、以下のソースで使われているものから使い方を調べていきます。
[Arduino_LED_Matrix.h](https://github.com/arduino/ArduinoCore-renesas/blob/a086f96acef69189224f84eafce160acff6b01c4/libraries/Arduino_LED_Matrix/src/Arduino_LED_Matrix.h#L158-L163)
### 3.1 get_available_timer
#### 形式
```cpp
static int8_t get_available_timer(uint8_t &type, bool force = false);
```
#### 説明
未使用のタイマchannel番号とタイマ種別を取得します。タイマchannel番号は戻り値、タイマ種別は第一引数で取得します。タイマ種別はGPT_TIMERあるいはAGT_TIMERが返されます。
処理を見ると使っていないタイマはGPT_TIMERから検索され、どちらかを指定して取得するようなことはできないようです。

#### 使用例
```cpp
    uint8_t timer_type;
    int8_t timer_ch = FspTimer::get_available_timer(timer_type);
    if (timer_ch < 0) {
        // 割り当て失敗
    }
```

### 3.2 begin

#### 形式
```cpp
bool begin(timer_mode_t mode, uint8_t type, uint8_t channel,  float freq_hz, float duty_perc, GPTimerCbk_f cbk = nullptr , void *ctx = nullptr );
```
#### 説明
FspTimerのインスタンスにパラメータを設定します。
第1引数のmodeに指定できるのは、以下のものです。現状どのような動作になるかは未確認です。
| 値 | 説明 |
|:---|:-:|
| TIMER_MODE_PERIODIC                           | 周期タイマ |
| TIMER_MODE_ONE_SHOT                           | ワンショットタイマ |
| TIMER_MODE_PWM                                | PWMモード |
| TIMER_MODE_ONE_SHOT_PULSE                     | ワンショットパルスモード |
| TIMER_MODE_TRIANGLE_WAVE_SYMMETRIC_PWM        | 対称三角波PWMモード     |
| TIMER_MODE_TRIANGLE_WAVE_ASYMMETRIC_PWM       | 非対称三角波PWMモード   |
| TIMER_MODE_TRIANGLE_WAVE_ASYMMETRIC_PWM_MODE3 | 非対称三角波PWMモード3  |

typeとchannelは、前述のget_available_timer()で取得した値を指定します。
freq_hzには周波数をfloat形式で指定します。
duty_percにはデューティー比をfloat形式で指定します。
cbkには割り込み発生時に呼ばれるコールバック関数を指定します。
ctxには上記のコールバック関数に渡す引数を指定します。

#### 使用例
```cpp
void timer_callback(timer_callback_args_t *arg);
uint8_t timer_type;
int8_t timer_ch
FspTimer fsp_timer;

fsp_timer.begin(TIMER_MODE_PERIODIC, timer_type, static_cast<uint8_t>(timer_ch), 1000.0, 25.0, timer_callback, nullptr);
```

:::note warn
FspTimerクラスには、以下のbeginメソッドも定義されているため、freq_hzとduty_percはfloatで指定します。
```cpp
bool begin(timer_mode_t mode, uint8_t type, uint8_t channel,  uint32_t period, uint32_t pulse, timer_source_div_t sd, GPTimerCbk_f cbk = nullptr , void *ctx = nullptr);
```
:::

### 3.3 setup_overflow_irq
#### 形式
```cpp
bool setup_overflow_irq(uint8_t priority = 12,  Irq_f isr_fnc = nullptr );
```
#### 説明
タイマオーバーフロー割込みを有効にします。第2引数のisr_fncは未使用のようです。

#### 使用例

```cpp
FspTimer fsp_timer;

fsp_timer.setup_overflow_irq();
```

### 3.4 open
#### 形式
```cpp
bool open();
```

#### 説明
対応するタイマのチャネルを有効にします。
#### 使用例

```cpp
FspTimer fsp_timer;

fsp_timer.open();
```
### 3.5 start
#### 形式
```cpp
bool start();
```
タイマをスタートします。

#### 使用例

```cpp
FspTimer fsp_timer;

fsp_timer.start();
```
## 4. プログラムの例
タイマを使ってLEDを点滅させるプログラムです。

```cpp:fsptimer.ino
#include <FspTimer.h>

static constexpr uint32_t led_on_ms = 100;
static constexpr uint32_t led_off_ms = 400; 

static FspTimer fsp_timer;

static bool is_led_on;

void
timer_callback([[maybe_unused]]timer_callback_args_t *arg)
{
  static uint32_t cnt = 0;

  cnt++;
  if (is_led_on) {
    if (cnt > led_on_ms) {
      digitalWrite(LED_BUILTIN, LOW);   // turn the LED off
      is_led_on = false;
      cnt = 0;
    }
  } else {
    if (cnt > led_off_ms) {
      digitalWrite(LED_BUILTIN, HIGH);   // turn the LED on
      is_led_on = true;
      cnt = 0;
    }
  }
}

void
setup()
{
  while (!Serial) {
    ;
  }
  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, HIGH);  // turn the LED on
  is_led_on = true;

  uint8_t timer_type;
  int8_t timer_ch = FspTimer::get_available_timer(timer_type);
  if (timer_ch < 0) {
    Serial.println("get_available_timer() failed.");
    return;
  }
  fsp_timer.begin(TIMER_MODE_PERIODIC, timer_type, static_cast<uint8_t>(timer_ch), 1000.0, 25.0, timer_callback, nullptr);
  fsp_timer.setup_overflow_irq();
  fsp_timer.open();
  fsp_timer.start();
  Serial.println("started.");
}

void
loop()
{
  __WFI(); 
}
```

## 5. 今後の方針

次は、FspTimerのPWM機能を調べていきたいと思っています。