IkawaMitsuhide’s blog

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

HiFiveでLチカ ~タイマ割り込みを利用~

今回はHiFive 1 rev bでタイマ割り込みを利用したLチカを行います。

環境

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

Freedom studioで用意されているAPIは以下のサイトが参考になります。
sifive.github.io

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

__inline__ struct metal_interrupt *metal_cpu_interrupt_controller(struct metal_cpu *cpu)

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

引数
  • cpu

CPUデバイスハンドラ

戻り値

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

__inline__ void metal_interrupt_init(struct metal_interrupt *controller)

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

引数
  • controller

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

戻り値

なし

__inline__ struct metal_interrupt *metal_cpu_timer_interrupt_controller(struct metal_cpu *cpu)

RTC(Real Time Clock)タイマ割り込みコントローラへの参照情報を取得します。ここで取得した割り込みコントローラは割り込み処理を行う前に初期化する必要があります。

引数
  • cpu

CPUデバイスハンドラ

戻り値

タイマ割り込みハンドラのポインタ

__inline__ int metal_cpu_timer_get_interrupt_id(struct metal_cpu *cpu)

RTCタイマ割り込みのIDを取得します。HiFive 1 rev bではタイマ割り込みのID
IDは0x07になります。

引数
  • cpu

CPUデバイスハンドラ

戻り値

タイマ割り込みの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

__inline__ int metal_cpu_set_mtimecmp(struct metal_cpu *cpu, unsigned long long time)

RTCで使用する比較レジスタの値をセットします。

引数
  • cpu

CPUデバイスハンドラ

  • time

比較レジスタにセットする値

戻り値

処理が成功したときは設定した値、失敗したときは-1

__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 <stdio.h>
#include <metal/gpio.h>
#include <metal/cpu.h>

int intr_count;

void timer_handler (int id, void *data) {
    intr_count++;
    if(intr_count > 50000){
    	metal_gpio_toggle_pin(data, 13);
    	intr_count = 0;
    }
}

int main (void)
{
    struct metal_gpio *led;
    struct metal_cpu *cpu;
    struct metal_interrupt *cpu_intr;
    struct metal_interrupt *tmr_intr;
    int tmr_id;

    led = metal_gpio_get_device(0);

    if (led == NULL) {
    	printf("LED is null.\\n");
        return 1;
    }

    metal_gpio_enable_output(led, 13);
    metal_gpio_set_pin(led, 13, 0);


    cpu = metal_cpu_get(metal_cpu_get_current_hartid());

    cpu_intr = metal_cpu_interrupt_controller(cpu);

    metal_interrupt_init(cpu_intr);

    tmr_intr = metal_cpu_timer_interrupt_controller(cpu);

    metal_interrupt_init(tmr_intr);
    tmr_id = metal_cpu_timer_get_interrupt_id(cpu);
    metal_interrupt_register_handler(tmr_intr, tmr_id, timer_handler, led);

    intr_count = 0;
    metal_cpu_set_mtimecmp(cpu, 0);

    metal_interrupt_enable(tmr_intr, tmr_id);
    metal_interrupt_enable(cpu_intr, 0);

    return 0;
}

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

プログラムが正常に動作すれば、LEDが一定の周期で点滅します。time_handler関数内の値を書き換えることで点滅速度を調整できます。

終わりに

今回は割り込みハンドラ内で待ち時間をカウントしました。本来であればリアルタイムクロックなどを利用して、任意の時間間隔で割り込みを発生させるのが理想的です。次回はRTC(リアルタイムクロック)を設定して1秒おきのLチカを実現しようと思います。