読者です 読者をやめる 読者になる 読者になる

アルペンスキータイミングシステム開発中

アルペンスキー用のタイム測定器を開発しようとしています。

wiringPiで割り込み比較

gpioで入力を受けるにはポーリングでは遅すぎるので割り込みを使いたい。

WiringPiを使うのが一般的なようで割り込みも使える。 ライブラリもC,python,nodeがあるのでそれぞれ試してみる。

gpsのppsの立ち上がりを検出してみる。

C

ソース

#include <wiringPi.h>
#include <stdio.h>
#include <time.h>
#include <unistd.h>
#include <string.h>

static volatile int int_count = 0;
static struct timespec  now, prev;

static void int_test(void)
{
    int_count++;
    timespec_get(&now, TIME_UTC);

    struct tm tm = *localtime(&now.tv_sec);

    long nsec = now.tv_nsec - prev.tv_nsec + 1000000000 * (now.tv_sec - prev.tv_sec);

    printf("%d-%d-%d %02d:%02d:%02d.%09ld (%ld)\n",
        tm.tm_year + 1900,
        tm.tm_mon + 1,
        tm.tm_mday,
        tm.tm_hour,
        tm.tm_min,
        tm.tm_sec,
        now.tv_nsec,
        nsec);

    prev = now;

    //fflush(stdout);
}

int main(void)
{
    printf("start...\n");

    if (wiringPiSetupGpio())
        return 1;

    pinMode(6, INPUT);
    wiringPiISR(6, INT_EDGE_RISING, int_test);

    while (1)
    {
        sleep(1);
    }


    printf("exit\n");

    return 0;
}

結果

start...
2016-5-26 09:55:43.000089185 (-1424870815)
2016-5-26 09:55:43.000530170 (440985)
2016-5-26 09:55:44.000102998 (999572828)
2016-5-26 09:55:45.000085196 (999982198)
2016-5-26 09:55:46.000109009 (1000023813)
2016-5-26 09:55:47.000088967 (999979958)
2016-5-26 09:55:48.000084343 (999995376)
2016-5-26 09:55:49.000106124 (1000021781)
2016-5-26 09:55:50.000088388 (999982264)
2016-5-26 09:55:51.000086608 (999998220)
2016-5-26 09:55:52.000078772 (999992164)
2016-5-26 09:55:53.000079524 (1000000752)
2016-5-26 09:55:54.000100432 (1000020908)
2016-5-26 09:55:55.000085508 (999985076)
2016-5-26 09:55:56.000083448 (999997940)
2016-5-26 09:55:57.000100346 (1000016898)
2016-5-26 09:55:58.000082192 (999981846)
2016-5-26 09:55:59.000082945 (1000000753)
2016-5-26 09:56:00.000106041 (1000023096)

誤差40usぐらいには入ってそう。延滞は100usぐらい。 思ったよりかなりいい。

Python

ソースs

#!/usr/bin/python

import RPi.GPIO as GPIO
import time
import datetime

cnt = 0
led = 26
#pin_in = 17
pin_in = 6
prev = datetime.datetime.now();

def btn_rised(v):
        global cnt, prev
        GPIO.output(led, 1);
        time.sleep(0.01);
        GPIO.output(led, 0);
        now = datetime.datetime.now();
        diff = now - prev;

        print(str(now) + ' (' + str(diff) + ')')
        cnt += 1
        prev = now


GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)

GPIO.setup(led, GPIO.OUT)
GPIO.output(led, 0)

GPIO.setup(pin_in, GPIO.IN, GPIO.PUD_UP)
GPIO.add_event_detect(pin_in, GPIO.RISING, btn_rised)

while True:
        time.sleep(1)


GPIO.output(led, 0)
GPIO.remove_event_detect(pin_in)

GPIO.cleanup()

結果

2016-05-26 10:03:21.010254 (0:00:00.053590)
2016-05-26 10:03:22.010158 (0:00:00.999904)
2016-05-26 10:03:23.010156 (0:00:00.999998)
2016-05-26 10:03:24.010252 (0:00:01.000096)
2016-05-26 10:03:25.010223 (0:00:00.999971)
2016-05-26 10:03:26.010223 (0:00:01)
2016-05-26 10:03:27.010220 (0:00:00.999997)
2016-05-26 10:03:28.010220 (0:00:01)
2016-05-26 10:03:29.010223 (0:00:01.000003)
2016-05-26 10:03:30.010230 (0:00:01.000007)
2016-05-26 10:03:31.010224 (0:00:00.999994)
2016-05-26 10:03:32.010226 (0:00:01.000002)
2016-05-26 10:03:33.010231 (0:00:01.000005)
2016-05-26 10:03:34.010222 (0:00:00.999991)
2016-05-26 10:03:35.010234 (0:00:01.000012)

誤差こちらも誤差は40usくらいだが延滞は100msもある。

Node

ソース

var wpi = require('wiring-pi');
var moment = require('moment');

wpi.setup('gpio');
wpi.pinMode(6, wpi.INPUT);
wpi.pullUpDnControl(6, wpi.PUD_UP);
wpi.wiringPiISR(6, wpi.INT_EDGE_RISING, function(delta) {
  console.log(moment().format("YYYY-MM-DD HH:mm:ss.SSSS") + ' (' + delta + ')');
});

結果

2016-05-26 10:06:05.0470 (44)
2016-05-26 10:06:06.0000 (999925)
2016-05-26 10:06:07.0000 (999997)
2016-05-26 10:06:08.0000 (1000003)
2016-05-26 10:06:09.0000 (1000033)
2016-05-26 10:06:10.0000 (1000003)
2016-05-26 10:06:11.0000 (999991)
2016-05-26 10:06:12.0000 (999997)
2016-05-26 10:06:13.0000 (999998)
2016-05-26 10:06:14.0000 (1000015)
2016-05-26 10:06:15.0000 (1000002)
2016-05-26 10:06:16.0000 (1000005)
2016-05-26 10:06:17.0000 (999996)
2016-05-26 10:06:18.0000 (999997)
2016-05-26 10:06:19.0000 (999995)

これだけ見ると誤差20us位に収まってる。延滞はこれだけ見ると0だけどたぶんCと同じ。 スクリプトも簡単だしすごいな。

次はstressで負荷をかけてみる。

$ stress --cpu 4 --vm 4 --io 4 --timeout 10s

やはりスクリプトは簡単に止まるけどCはなかなか粘る。 けど止まるときは止まる。

いろいろ調べると優先度をあげたりするといいようなのでやってみる。 似たようなことをされている人がいるので次のサイトを参考にさせていただいた。

www.homu.net

#include <wiringPi.h>
#include <stdio.h>
#include <time.h>
#include <unistd.h>
#include <sched.h>
#include <string.h>
#include <sys/mman.h>

static volatile int int_count = 0;
static struct timespec  now, prev;
static int state = 0;

static void int_test(void)
{
        int_count++;
        timespec_get(&now, TIME_UTC);
        
        struct tm tm = *localtime(&now.tv_sec);

        state = !state;
        digitalWrite(26, 1);
        delay(100);
        digitalWrite(26, 0);

        long nsec = now.tv_nsec - prev.tv_nsec + 1000000000 * (now.tv_sec - prev.tv_sec);

        printf("%d-%d-%d %02d:%02d:%02d.%09ld (%ld)\n", 
                tm.tm_year + 1900,
                tm.tm_mon + 1,
                tm.tm_mday,
                tm.tm_hour,
                tm.tm_min,
                tm.tm_sec,
                now.tv_nsec,
                nsec);

        prev = now;

        //fflush(stdout);
}

int main(void)
{
        printf("start...\n");

        struct sched_param param;
        param.sched_priority = 99;
        if(sched_setscheduler(0, SCHED_FIFO, &param) == -1) {
                return 1;
        }

        if(mlockall(MCL_CURRENT | MCL_FUTURE) == -1) {
                return 2;
        }


        if (wiringPiSetupGpio())
                return 1;

        pinMode(6, INPUT);
        wiringPiISR(6, INT_EDGE_RISING, int_test);

        while (1)
        {
                sleep(1);
        }
        
        printf("exit\n");

        return 0;
}

優先度を変えてもあまり変わらない。それよりmlockallでスワップアウトしなくすると割り込みを取りこぼすことがほとんどない。