IkawaMitsuhide’s blog

OSづくりにまつわることを発信したい

HiFiveでLチカ ~Real Time Clockを利用~

今回はHiFive 1 rev bでReal Time Clock(RTC)を利用して点滅周期が1秒のLチカを行います。

環境

タイマ割り込みに関するAPI

Freedom studioで用意されているAPIは以下のサイトが参考になります。
sifive.github.io
GPIO関連のAPIは以前の記事を参照してください。

struct metal_cpu *metal_cpu_get(unsigned int hartid)

CPUのhart(ハードウェアスレッド)への参照情報を取得します。

引数
  • hartid

利用したいCPU hartのID

戻り値

CPUデバイスハンドラのポインタ

int metal_cpu_get_current_hartid(void)

現在処理を行っているhartのIDを取得します。

引数

なし

戻り値

処理を行っているhartのID

struct metal_rtc *metal_rtc_get_device(int idex)

RTCのハンドラを取得します。

引数
  • index

RTCのインデックスです。HiFive 1 rev bでは0とします。

戻り値

RTCハンドラ(利用不可なインデックス指定時はNull)

__inline__ struct metal_interrupt *metal_cpu_interrupt_controller(struct metal_cpu *cpu)

割り込みコントローラを取得します。ここで取得した割り込みコントローラは割り込み処理を行う前に初期化する必要があります。

引数
  • cpu

CPUデバイスハンドラ

戻り値

割り込みコントローラのハンドル

__inline__ void metal_interrupt_init(struct metal_interrupt *controller)

割り込みコントローラの初期化を行います。

引数
  • controller

割り込みコントローラのハンドル

戻り値

なし

struct metal_interrupt *metal_rtc_get_interrupt(const struct metal_rtc *const rtc)

RTC比較による割り込みハンドラを取得します。

引数
  • rtc

RTCハンドラ

戻り値

割り込みハンドラ

int metal_rtc_get_interrupt_id(const struct metal_rtc *const rtc)

RTC比較による割り込みIDを取得します。RTCのIDは0x02となります。

引数
  • rtc

RTCデバイスハンドラ

戻り値

割り込みID

__inline_ int metal_interrupt_register_handler(struct metal_interrupt *controller, int id, metal_interrupt_handler_t handler, void *priv_data)

割り込みハンドラ(割り込みが発生したときの処理)を登録します。

引数
  • controller

割り込みコントローラのハンドル

  • id

登録先の割り込みID

  • handler

割り込み処理を行う関数。登録先の割り込みID(id)とvoidポインタ型の引数(priv_data)を引数に持つように作成する。

  • priv_data

割り込み処理を行う関数に与える引数

戻り値

設定が成功したときは0

uint64_t metal_rtc_get_rate(const struct metal_rtc *const rtc)

RTCの周波数を取得します。

引数
  • rtc

RTCデバイスハンドラ

戻り値

RTCの動作周波数

uint64_t metal_rtc_set_count(const struct metal_rtc *const rtc, const uint64_t count)

RTCのカウンタ値を任意の値に設定します。

引数
  • rtc

RTCデバイスハンドラ

  • count

設定するカウント値

戻り値

設定したカウント値
※必ずしも設定した値が帰ってくるわけではないそうです。

uint64_t metal_rtc_set_compare(const struct metal_rtc *const rtc, const uint64_t compare)

RTCの比較器に値を設定します。RTCのカウンタ値が比較器の値以上になると割り込みが発生します。

引数
  • rtc

RTCデバイスハンドラ

  • compare

比較器にセットする値

戻り値

比較器にセットした値
※必ずしも設定した値が帰ってくるわけではないそうです。

__inline__ int metal_interrupt_enable(struct metal_interrupt *controller, int id)

割り込みを使用可能にします。

引数
  • controller

割り込みコントローラのハンドラ

  • id

使用可能にしたい割り込みのID

戻り値

処理が成功したときは0

int metal_rtc_run(const struct metal_rtc *const rtc, const enum metal_rtc_run_option option)

RTCを起動・終了します。

引数
  • rtc

RTCデバイスハンドラ

  • option

RTCの起動・終了を設定する
METAL_RTC_STOP(数値では0)で終了、METAL_RTC_RUN(数値では1)で起動

戻り値

起動・終了が正常に行われれば0

サンプルプログラム

Freedom studioを立ち上げて適当なプロジェクトを作成します。プロジェクト内のメインプログラムに以下のプログラムを書き込みます。

/* Copyright 2019 SiFive, Inc */
/* SPDX-License-Identifier: Apache-2.0 */

#include <metal/cpu.h>
#include <metal/interrupt.h>
#include <metal/rtc.h>
#include <metal/gpio.h>

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

#define RTC_TIMEOUT_SECONDS 1

bool toggle = false;
void rtc_handler(int id, void *data) {
	uint64_t t = metal_rtc_get_count(data);
	uint64_t rate = metal_rtc_get_rate(data);
	metal_rtc_set_compare(data, t + RTC_TIMEOUT_SECONDS * rate);
	toggle = true;
	printf("Toggled\n\r");
}

int main() {
	int rc;
	struct metal_gpio *led;

	/*Set GPIO*/
	led = metal_gpio_get_device(0);
	metal_gpio_enable_output(led, 13);
	metal_gpio_set_pin(led, 13, 0);

	/*Set RTC*/
	struct metal_cpu *cpu = metal_cpu_get(metal_cpu_get_current_hartid());
	struct metal_rtc *rtc = metal_rtc_get_device(0);

	struct metal_interrupt *cpu_intr = metal_cpu_interrupt_controller(cpu);
	metal_interrupt_init(cpu_intr);

	struct metal_interrupt *rtc_intr = metal_rtc_get_interrupt(rtc);
	metal_interrupt_init(rtc_intr);

	int rtc_id = metal_rtc_get_interrupt_id(rtc);
	rc = metal_interrupt_register_handler(rtc_intr, rtc_id, rtc_handler, rtc);

	const uint64_t rate = metal_rtc_get_rate(rtc);
	printf("Rate: %d\n\r", rate);

	metal_rtc_set_count(rtc, 0);
	const uint64_t cmp = metal_rtc_set_compare(rtc, RTC_TIMEOUT_SECONDS * rate);
	printf("CMP: %d\n\r", cmp);

	rc = metal_interrupt_enable(rtc_intr, rtc_id);
	rc = metal_interrupt_enable(cpu_intr, 0);

	puts("Starting RTC\n");

	metal_rtc_run(rtc, METAL_RTC_RUN);

	while(1){
		if(toggle){
			metal_gpio_toggle_pin(led, 13);
			toggle = false;
		}
	}
	return 0;
}

HiFive 1 rev bとLEDの配線は以下の写真のようにしています。
f:id:IkawaMitsuhide:20210221134416j:plain

プログラムが正常に動作すれば、LEDが指定した時間間隔で点滅します。RTC_TIMEOUT_SECONDSの値を書き換えることで任意の点滅感覚に設定できます。

終わりに

RTCを利用することでLチカもより正確に行うことができます。Lチカに関連したプログラムはおおむね試せたと思います。次はボタン操作など外部入力を受け取る処理を行ってみたいと思います。