ラズパイ7桁時計(自作中)の1/10秒表示とGPSの1ppsを比べてみました。

↓ 右端の7セグLEDが1/10秒で、GPSの赤LEDのフラッシュ時間も1/10秒です。

 

Stratum 1 のNTPサーバーと参照同期中のラズパイ7桁時計の1/10秒と、GPSの1PPSを比べる為に、高速度カメラ(iPhoneのスローモーション240fps)で撮影した動画です。

1/10秒単位の表示もバッチリ合っていますね。

このラズパイとGPSは繋げていません。別々に作動しています。

 

↓ ラズパイ7桁時計のソースコード

/*
 * clock2.cpp
 * 
 * 1/10秒表示テスト
 * 
 * 7桁ダイナミック表示対応
 * 時刻 08:34:27.6 の場合
 * 表示順序は 6 -> 7 -> 2 -> 4 -> 3 -> 8 -> 0
 *
 */
 
#include <iostream>
#include <pigpio.h>
#include <unistd.h>

#define H 0x1
#define L 0x0
#define COM_ON 0x1
#define COM_OFF 0x0

using namespace std;

void mainLoop(void);

int main(void)
{
    if (daemon(0, 0) == 0) { // デーモン関数
        mainLoop();
    } else {
        cout << "error" << endl;
    }
    return 0;
}

void mainLoop(void)
{    
    int i, j;
    int num[7];
    struct timespec ts;
    struct tm lotm;
		       // .1秒 1秒 10秒 1分 10分 1時 10時
	          // Common 1   2   3   4   5   6   7
    static int common[7] = {25, 24, 23, 18, 22, 27, 17};
                    // GPIO 25  24  23  18  22  27  17

	             // 7seg a   b   c   d   e   f  g   dp
    static int nanaseg[8] = {21, 20, 16, 26, 19, 6, 13, 12};
                     // GPIO 21  20  16  26  19  6  13  12

    static int segdata[12][8] = { // 7セグメントデータ
      // a  b  c  d  e  f  g  dp
	{H, H, H, H, H, H, L, L}, // 0
	{L, H, H, L, L, L, L, L}, // 1
	{H, H, L, H, H, L, H, L}, // 2
	{H, H, H, H, L, L, H, L}, // 3
	{L, H, H, L, L, H, H, L}, // 4
	{H, L, H, H, L, H, H, L}, // 5
	{H, L, H, H, H, H, H, L}, // 6
	{H, H, H, L, L, L, L, L}, // 7
	{H, H, H, H, H, H, H, L}, // 8
	{H, H, H, H, L, H, H, L}, // 9
	{L, L, L, L, L, L, L, L}, // Blank
	{L, L, L, L, L, L, L, H}, // dp
    };
    
    // pigpioライブラリ初期化
    if (gpioInitialise() < 0) exit(1);
    
    // コモン用GPIOピンをアウトプット設定
    for (i = 0; i < 7; i++) {
	gpioSetMode(common[i], PI_OUTPUT);
    }
    
    // セグメント用GPIOピンをアウトプット設定
    for (i = 0; i < 8; i++) {
	gpioSetMode(nanaseg[i], PI_OUTPUT);
    }
    
    /*
     *
     * 時刻取得及びダイナミック表示ルーチン(無限ループ)
     *
     * 時刻取得、1桁2ミリ秒表示×7、時刻取得、1桁2ミリ秒表示×7の繰り返し
     *
     */
    while (1) {
	
	clock_gettime(CLOCK_REALTIME, &ts); // 時刻の取得
	localtime_r(&ts.tv_sec, &lotm); // ローカル時間に変換
	num[0] = ts.tv_nsec / 100000000; // 0.1秒の桁取得
	num[1] = lotm.tm_sec % 10;       //   1秒の桁取得
	num[2] = lotm.tm_sec / 10;       //  10秒の桁取得
	num[3] = lotm.tm_min % 10;       //   1分の桁取得
	num[4] = lotm.tm_min / 10;       //  10分の桁取得
	num[5] = lotm.tm_hour % 10;      //   1時の桁取得
	num[6] = lotm.tm_hour / 10;      //  10時の桁取得
	
	for (j = 0; j < 7; j++) { // 7桁ダイナミック表示ルーチン

	    for (i = 0; i < 8; i++) { // 7セグメントデータセット
		gpioWrite(nanaseg[i], segdata[num[j]][i]);
	    }
	    gpioWrite(common[j], COM_ON);   // 7セグLED点灯
	    gpioDelay(2000);                // 2ミリ秒点灯維持
	    gpioWrite(common[j], COM_OFF);  // 7セグLED消灯
	}
    }
    
    // pigpioライブラリ終了処理
    gpioTerminate(); //  これは無限ループに含まれないので不要?
}
$ g++ -Wall -pthread -o clock2 clock2.cpp -lpigpio -lrt
$ sudo ./clock2

 

ラズパイをPi4からZeroに変えました。CPU使用率の変化は

Pi4:約8.0%
Zero:約10.5%

 

このプログラムの表示遅延は最大で約14.8ミリ秒(0.0148秒)、最小は限り無くゼロのはず。その理由は約14.8ミリ秒置きに時刻取得して、表示を更新しているからです。

↓ 0.1秒桁用7セグLEDのコモン端子に繋がるオンオフを、オシロスコープで観測した画像です。

ブレッドボード上ですが、7セグLED6桁ダイナミック表示成功です。

私は初体験なので嬉しいですね。自作プログラムも表示に関しては上手く出来たと自画自賛。

 

調子に乗って1/10秒表示の7桁にしてみました。

今、ラズパイでNTP時計を自作しようと奮闘中なんですが、プログラムの自作に目処が付いたので、その自作プログラムをデーモン化して自動起動するようにしてみました。

この情報は、無知の私がネットで知り得た事なので、全てが誤りかも知れません。また、OSの再起動コマンドsudo reboot時、自作プログラムの終了が遅い時があります。でもsudo shutdown -r nowには直ぐに反応しますが。

これらを理解した上で参考にして下さい。

 

自作プログラム(test11)やユニットファイルの置き場所は

自作プログラム
/usr/local/sbin/test11

ユニットファイル
/etc/systemd/system/test11.service

 

自作プログラムのmain()にデーモン関数を使用。
これにはヘッダファイル unistd.h が必要です。

#include <unistd.h>

int main(void)
{
    if(daemon(0, 0) == 0) { // デーモン関数
        mainLoop(); // ここに自作プログラム(無限ループ)を書く
    } else {
        cout << "error" << endl; // この1行は自信無し
    }
    return 0;
}

 

ユニットファイル作成。ntp.serviceの後に起動、GUIログイン環境。

$ sudo vi /etc/systemd/system/test11.service
[Unit]
Description = test11 daemon
After = ntp.service
ConditionPathExists = /usr/local/sbin

[Service]
ExecStart = /usr/local/sbin/test11
Restart = no
Type = forking

[Install]
WantedBy = graphical.target

 

パーミッションを644に変更。

$ sudo chmod 644 /etc/systemd/system/test11.service

 

systemctlコマンドでデーモンとする。

$ sudo systemctl daemon-reload

$ sudo systemctl start test11.service

$ sudo systemctl enable test11.service

ハードは出来ていないけどソフトはおおかた完成?

↓ この大型7セグLED6個とラズパイでNTP時計を作るのが目標なんです。丁度、今発売中のハム関係の雑誌で同じような記事があったから、そのソースコードが気になるんですが。

 

時刻取得及び7セグLED6桁ダイナミック表示ルーチン(無限ループ)は


このフローチャートはLucidchartのフリー版で作成。

一桁当たりの表示時間を3ミリ秒とすると、時刻の取得は1秒間に約55回になります。この時、Pi4でのCPU使用率は約8%でした。実際にはZeroにする予定なので、どの位のCPU使用率なるのか心配です。Zeroでも少ないCPU使用率なら時刻の取得サイクルは、このまま変えずに行きたいです。

 

/*
 * test12.cpp
 * 
 * daemon化
 * 
 * 6桁ダイナミック表示対応
 * 時刻 08:34:27 の場合
 * 表示順序は 7 -> 2 -> 4 -> 3 -> 8 -> 0
 *
 */
 
#include <iostream>
#include <pigpio.h>
#include <unistd.h>

#define H 0x1
#define L 0x0
#define SEG_ON 0x1
#define SEG_OFF 0x0

using namespace std;

void mainLoop(void);

int main(void)
{
    if(daemon(0, 0) == 0) { // デーモン関数
        mainLoop();
    } else {
        cout << "error" << endl;
    }
    return 0;
}

void mainLoop(void)
{    
    int i, j;
    int num[6];
    struct timespec ts;
    struct tm lotm;
		        // 1秒 10秒 1分 10分 1時 10時
	          // Common 1   2   3   4   5   6
    static int common[6] = {18, 23, 24, 17, 25, 5};
                    // GPIO 18  23  24  17  25  5

	             // 7seg a   b   c   d   e   f  g   dp
    static int nanaseg[8] = {21, 20, 16, 26, 19, 6, 13, 12};
                     // GPIO 21  20  16  26  19  6  13  12

    static int segdata[12][8] = { // 7セグメントデータ
      // a  b  c  d  e  f  g  dp
	{H, H, H, H, H, H, L, L}, // 0
	{L, H, H, L, L, L, L, L}, // 1
	{H, H, L, H, H, L, H, L}, // 2
	{H, H, H, H, L, L, H, L}, // 3
	{L, H, H, L, L, H, H, L}, // 4
	{H, L, H, H, L, H, H, L}, // 5
	{H, L, H, H, H, H, H, L}, // 6
	{H, H, H, L, L, L, L, L}, // 7
	{H, H, H, H, H, H, H, L}, // 8
	{H, H, H, H, L, H, H, L}, // 9
	{L, L, L, L, L, L, L, L}, // Blank
	{L, L, L, L, L, L, L, H}, // dp
    };
    
    // pigpioライブラリ初期化
    if (gpioInitialise() < 0) exit(1);
    
    // コモン用GPIOピンをアウトプット設定
    for (i = 0; i < 6; i++) {
	gpioSetMode(common[i], PI_OUTPUT);
    }
    
    // セグメント用GPIOピンをアウトプット設定
    for (i = 0; i < 8; i++) {
	gpioSetMode(nanaseg[i], PI_OUTPUT);
    }
    
    /*
     *
     * 時刻取得及びダイナミック表示ルーチン(無限ループ)
     *
     * 時刻取得、1桁3ミリ秒表示×6、時刻取得、1桁3ミリ秒表示×6の繰り返し
     *
     */
    while (1) {
	
	clock_gettime(CLOCK_REALTIME, &ts); // 時刻の取得
	localtime_r(&ts.tv_sec, &lotm); // ローカル時間に変換
	num[0] = lotm.tm_sec % 10;  //  1秒の桁取得
	num[1] = lotm.tm_sec / 10;  // 10秒の桁取得
	num[2] = lotm.tm_min % 10;  //  1分の桁取得
	num[3] = lotm.tm_min / 10;  // 10分の桁取得
	num[4] = lotm.tm_hour % 10; //  1時の桁取得
	num[5] = lotm.tm_hour / 10; // 10時の桁取得
	
	for (j = 0; j < 6; j++) { // 6桁ダイナミック表示ルーチン

	    for (i = 0; i < 8; i++) { // 7セグメントデータセット
		gpioWrite(nanaseg[i], segdata[num[j]][i]);
	    }
	    gpioWrite(common[j], SEG_ON);   // 7セグLED点灯
	    gpioDelay(3000);                // 3ミリ秒点灯維持
	    gpioWrite(common[j], SEG_OFF);  // 7セグLED消灯
	}
    }
    
    // pigpioライブラリ終了処理
    gpioTerminate(); //  これは無限ループに含まれないので不要?
}

先日、Raspberry Pi 4でLチカを経験してから、毎晩これに夢中です。

あれから物凄く努力して、7セグLEDを1つ繋げて現時刻の0秒から9秒を表示出来るようになりました。

↑ 1桁ですが、疑似ダイナミック表示させています。点灯は0.25ミリ秒、消灯は1.25ミリ秒を繰り返して、6桁のダイナミック表示1桁目のつもりなんですが。

 

私の開発環境は、このブログ内にある「ラズパイで初めてのLチカ」を見て下さい。

↓ 点灯消灯時間を変えてありますが、そのソースコードです。

/*
 * test7.cpp
 * clock_gettime()に変更
 */

#include <iostream>
#include <pigpio.h>
#include <time.h>

using namespace std;

int main(void)
{
    int i, j;
    
    struct timespec ts;
    struct tm lotm;
    
	       // 7seg a   b   c   d   e   f   g   dp
    int gpio_pin[8] = {21, 20, 16, 26, 19, 06, 13, 12};
	       // GPIO 21  20  16  26  19  06  13  12
	       
    int segdata[12][8] = { // 7segment 2次元配列
      // a  b  c  d  e  f  g  dp
	{0, 0, 0, 0, 0, 0, 1, 1}, // 0
	{1, 0, 0, 1, 1, 1, 1, 1}, // 1
	{0, 0, 1, 0, 0, 1, 0, 1}, // 2
	{0, 0, 0, 0, 1, 1, 0, 1}, // 3
	{1, 0, 0, 1, 1, 0, 0, 1}, // 4
	{0, 1, 0, 0, 1, 0, 0, 1}, // 5
	{0, 1, 0, 0, 0, 0, 0, 1}, // 6
	{0, 0, 0, 1, 1, 1, 1, 1}, // 7
	{0, 0, 0, 0, 0, 0, 0, 1}, // 8
	{0, 0, 0, 0, 1, 0, 0, 1}, // 9
	{1, 1, 1, 1, 1, 1, 1, 1}, // Blank
	{1, 1, 1, 1, 1, 1, 1, 0}  // dp
    };
    
    // pigpioライブラリ初期化
    if (gpioInitialise() < 0) exit(1);
    
    // GPIO使用ピンをアウトプット設定
    for (i = 0; i < 8; i++) {
	gpioSetMode(gpio_pin[i], PI_OUTPUT);
    }
    
    // 表示消灯を100回繰り返して終了
    for (i = 0; i < 100; i++) {
   
	clock_gettime(CLOCK_REALTIME, &ts); // 時刻の取得
	localtime_r(&ts.tv_sec, &lotm); // ローカル時間に変換
	// int num = lotm.tm_hour / 10; // 時、十の位取得
	// int num = lotm.tm_hour % 10; // 時、一の位取得
	// int num = lotm.tm_min / 10; // 分、十の位取得
	// int num = lotm.tm_min % 10; // 分、一の位取得
	// int num = lotm.tm_sec / 10; // 秒、十の位取得
        int num = lotm.tm_sec % 10; // 秒、一の桁取得
	cout << num << flush;
	
	for (j = 0; j < 8; j++) { // 7セグLED表示(秒一の位)
	    gpioWrite(gpio_pin[j], segdata[num][j]);
	}

	gpioDelay(4000); // 4ミリ秒
	
	for (j = 0; j < 8; j++) { // 消灯(10 -> Blank)
	    gpioWrite(gpio_pin[j], segdata[10][j]);
	}

	gpioDelay(20000); // 20ミリ秒

    }
    
    cout << endl;
    
    // pigpioライブラリ終了処理
    gpioTerminate();
    
    return 0;

}
$ sudo ./test7
6666666666677777777777777777777777777777777777777777888888888888888888888888888888888888888889999999

 

このソースコードの点灯4ミリ秒、消灯20ミリ秒、約41Hzの疑似ダイナミック表示なら、私の目に7セグLEDのちらつきは感じません。

でもVNCで遠隔操作している時は、そのサイクルを早くしても、ちらつきます。