# Timer

16bit カウンタを用いるハードウェアタイマー(TIMER0 .. 4) を利用可能です。

このタイマーは、PWM 出力などのペリフェラルとして利用することもできますし、ソフトウェア処理を行うハードウェアタイマーとして利用することも可能です。

## ポートの割り当て

| タイマー番号 | 主        | 副     | 副2    | 解説           |
| ------ | -------- | ----- | ----- | ------------ |
| TIMER0 | 8, 9, 10 | 2,3,4 |       | 汎用タイマー機能利用可能 |
| TIMER1 | 11       | 5     |       | PWM 出力専用     |
| TIMER2 | 12       | 6     | DO0\* | PWM 出力専用     |
| TIMER3 | 13       | 7     | DO1\* | PWM 出力専用     |
| TIMER4 | 17       | 8     |       | PWM 出力専用     |

{% hint style="info" %}
ペリフェラル API vAHI\_TimerSetLocation() により、副割り当てに変更できます。

\* TIMER2と3 は DO0,1 に割り当てを変更できます。ただし、これらのポートについては、電源投入時に Vcc レベルになっていないと TWELITE 無線マイコンが正常起動しないことがあります。
{% endhint %}

{% hint style="warning" %}
TWELITE RED ではさらにPWM出力系統が増えていますが、TWELITE NET での標準的な対応は TIMER0..4 とします。
{% endhint %}

## 初期化と開始、終了

事前にペリフェラルマニュアルを参照の上、Timer で利用しないポートを宣言します。この宣言をしないと、Timer の初期化後、該当ポートは汎用のIOポートとして利用できなくなります。

以下の例では bit0,1,2 をセットします。これは TIMER0 で使用する PWM 用のピンを全て汎用IO向けに解放します。

```c
vAHI_TimerFineGrainDIOControl(0x7);
```

> **tsTimerContext() のパラメータ**

| <p><br>Bit</p> | Timer Input/Output |
| -------------- | ------------------ |
| 0              | TIMER 0 ゲート/イベント入力 |
| 1              | TIMER 0 キャプチャー入力   |
| 2              | TIMER 0 PWM 出力     |
| 3              | TIMER 1 PWM 出力     |
| 4              | TIMER 2 PWM 出力     |
| 5              | TIMER 3 PWM 出力     |
| 6              | TIMER 4 PWM 出力     |
| 7              | 予約                 |

次に Timer ライブラリ用の初期化構造体 [tsTimerContext](/hw-api-ref/perifuraru/timer/timerraiburari/tstimercontext.md) を静的に確保し、設定を行います。

必要なタイマーの周波数に対して適切な PreScale を選択するようにします。

{% hint style="info" %}
プリスケール(u8PreScale) は、要求する周期（周波数）に対して、多くの場合はより小さい値を選択します。

Timer は 16Mhz のカウンター値をプリスケールしており、１周期のカウント値(Ct)は以下の式で与えられます。

```
Ct = 16,000,000 / 2^p / hz
    p  : プリスケール値
    hz : Timer 周期
```

この Ct 値が 16bit の最大数(65535)を超えないように設定します。例えば64Hzのタイマーが欲しい場合は p=2 で Ct = 62500, p=4 で Ct = 15625 となります。 より正確なDuty比のPWM出力が必要な場合は、少しでも小さな p=2 を選択しますが、単純にソフトウェア用のタイマーが必要な場合は p=4 でも違いはありません。
{% endhint %}

```c
tsTimerContext sTimerApp; // global or static allocation

// set 64ticks/sec
memset(&sTimerApp, 0, sizeof(tsTimerContext));
sTimerApp.u8Device = E_AHI_DEVICE_TIMER0;
sTimerApp.u16Hz = 64;
sTimerApp.u8PreScale = 4; // 15625ct@2^4
```

最後に、Timer を開始します。

```c
vTimerConfig(&sTimerApp); // initialize
vTimerStart(&sTimerApp); // start timer
```

Timer を一時的に停止させるには、vTimerStop() を呼び出します。完全に無効化するには vTimerDisable() を呼び出します。

```c
// just stop the timer
vTimerStop(&sTimerApp);
...
// restart
vTimerStart(&sTimerApp);
...
// now, disable timer completely
vTimerStop(&sTimerApp);
vTimerDisable(&sTimerApp);
```

## タイマー割り込み

ToCoNet\_u8HwInt() ToCoNet\_vHwEvent() の u8DeviceID パラメータが E\_AHI\_DEVICE\_TIMER0 から E\_AHI\_DEVICE\_TIMER4 をとります。

{% hint style="warning" %}
割り込み周波数が高くなるとハード割り込みやイベントの処理が無視できなくなります。

* 不要なタイマーの割り込みは無効にしておく
  * tsTimerContext の bDisableInt を TRUE に設定する
* 割り込み処理のみで対応し、ハードイベントは発生させない
  * 割り込みハンドラを TRUE で return する
    {% endhint %}

```c
// hardware interrupt (return quickly!)
uint8 cbToCoNet_u8HwInt(
		uint32 u32DeviceId, 
		uint32 u32ItemBitmap) {
	uint8 u8handled = FALSE;

	switch (u32DeviceId) {
	case E_AHI_DEVICE_TIMER0:
		_C {
			static bool_t b;
			b = !b;
			vPortSet_TrueAsLo(PORT_DO1, b);
			
			u8handled = FALSE; // if TRUE, no HwEvent follows.
		}
		break;
	}
}

// hardware event (in the application loop)
//   where we can make a bigger job, like RF transmit.
void cbToCoNet_vHwEvent(
		uint32 u32DeviceId,
		uint32 u32ItemBitmap) {
		
	switch (u32DeviceId) {
	case E_AHI_DEVICE_TIMER0:
		vPutChar(&sSerStream, 'x'); // put x every tick
		if (...) {
			...
			ToCoNet_bMacTxReq(...); // send a packt
		}
		break;
	}	
	
}
```

## PWM 出力

Timerの出力として PWM 出力を設定することが可能です。

{% hint style="info" %}
Duty比の精度を向上させるため、上述のプリスケール値を十分小さい値にしておくことを推奨します。
{% endhint %}

PWM出力を得るためには以下の設定が必要です。

* [tsTimerContext](/hw-api-ref/perifuraru/timer/timerraiburari/tstimercontext.md) の bPWMOut を TRUE に設定する
* [tsTimerContext](/hw-api-ref/perifuraru/timer/timerraiburari/tstimercontext.md) の u16duty を 0..1024 の値に設定する
* vAHI\_TimerFineGrainDIOControl() で出力を無効にしていない

```c
tsTimerContext sTimerPWM; // global or static allocation

// initialize
memset(&sTimerPWM, 0, sizeof(tsTimerContext));
sTimerPWM.u8Device = E_AHI_DEVICE_TIMER1;
sTimerPWM.u16Hz = 1000; // 1Khz
sTimerPWM.u8PreScale = 0;
sTimerPWM.u16duty = 512; // 50%
sTimerPWM.bPWMout = TRUE; // PWM out
sTimerPWM.bDisableInt = TRUE; // no interrupt (for CPU save)
// start
vTimerConfig(&sTimerPWM);
vTimerStart(&sTimerPWM);

...
// change duty
sTimerPWM.u16Duty = 256; // set new duty ratio
vTimerStart(&sTimerPWM); // just start again to change duty
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://sdk.twelite.info/hw-api-ref/perifuraru/timer.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
