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)
割り込みコントローラを取得します。ここで取得した割り込みコントローラは割り込み処理を行う前に初期化する必要があります。
戻り値
割り込みコントローラのハンドル
__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となります。
戻り値
割り込み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の動作周波数
uint64_t metal_rtc_set_count(const struct metal_rtc *const rtc, const uint64_t count)
RTCのカウンタ値を任意の値に設定します。
戻り値
設定したカウント値
※必ずしも設定した値が帰ってくるわけではないそうです。
uint64_t metal_rtc_set_compare(const struct metal_rtc *const rtc, const uint64_t compare)
RTCの比較器に値を設定します。RTCのカウンタ値が比較器の値以上になると割り込みが発生します。
戻り値
比較器にセットした値
※必ずしも設定した値が帰ってくるわけではないそうです。
__inline__ int metal_interrupt_enable(struct metal_interrupt *controller, int id)
割り込みを使用可能にします。
引数
- controller
割り込みコントローラのハンドラ
- id
使用可能にしたい割り込みのID
戻り値
処理が成功したときは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の配線は以下の写真のようにしています。
プログラムが正常に動作すれば、LEDが指定した時間間隔で点滅します。RTC_TIMEOUT_SECONDSの値を書き換えることで任意の点滅感覚に設定できます。
終わりに
RTCを利用することでLチカもより正確に行うことができます。Lチカに関連したプログラムはおおむね試せたと思います。次はボタン操作など外部入力を受け取る処理を行ってみたいと思います。