2020年8月2日日曜日

Raspberry pi 2とFreeBSDで7セグLED時計を作る - プログラミング編


の続きです。

前回まででRaspberry pi 2のFreeBSDの設定は、ハード・ソフトともに終了しました。
最後は、時計のプログラムを書いて終了です。
はじめはSPIを使って制御しようと思ったのですが、1つしかないSPIを使うのは勿体無いので、GPIOを4つ使うことにしました。

GPIOの制御はgpioctlを使ってもできるのですが、シェルスクリプトで時計を書くのはちょっと面倒なので、C言語で書くことにします。

C言語でのGPIOの制御の仕方は、gpio(3)のmanを見ると良いと思います。

サンプルコードはこんな感じです。
コンパイルには、libgpio.hとgpio.cが必要です。
FreeBSDのソースツリーから持ってくるか、以下から入手してください。


clock.c

#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/time.h>

#include "libgpio.h"

#define CLOCK_PIN_MOSI  (17)
#define CLOCK_PIN_SCLK  (27)
#define CLOCK_PIN_LATCH (23)
#define CLOCK_PIN_BLINK (24)

#define CLOCK_INTERRUPTION (20)

unsigned char iTable[256];

void CLOCK_initTable(void)
{
    for(int tCount1=0;tCount1<256;tCount1++)
    {
        iTable[tCount1]=0x00;
    }

    iTable['0']=0x3f;
    iTable['1']=0x06;
    iTable['2']=0x5b;
    iTable['3']=0x4f;
    iTable['4']=0x66;
    iTable['5']=0x6d;
    iTable['6']=0x7d;
    iTable['7']=0x27;
    iTable['8']=0x7f;
    iTable['9']=0x6f;
    iTable['C']=0x39;
    iTable['E']=0x79;
    iTable['G']=0x3d;
    iTable['H']=0x76;
    iTable['I']=0x06;
    iTable['J']=0x1e;
    iTable['L']=0x38;
    iTable['O']=0x3f;
    iTable['P']=0x73;
    iTable['T']=0x31;
    iTable['U']=0x3e;
    iTable['Y']=0x66;
    iTable['a']=0x77;
    iTable['b']=0x7c;
    iTable['c']=0x58;
    iTable['d']=0x5e;
    iTable['e']=0x7b;
    iTable['f']=0x71;
    iTable['g']=0x6f;
    iTable['h']=0x74;
    iTable['i']=0x05;
    iTable['j']=0x0d;
    iTable['l']=0x18;
    iTable['n']=0x54;
    iTable['o']=0x5c;
    iTable['p']=0x73;
    iTable['q']=0x67;
    iTable['r']=0x50;
    iTable['s']=0x6d;
    iTable['t']=0x78;
    iTable['u']=0x1c;
    iTable['y']=0x6e;
    iTable['=']=0x09;
    iTable['-']=0x40;
}

void CLOCK_sendChar(gpio_handle_t aHandle,unsigned char aChar)
{
    for(int tCount1=0;tCount1<8;tCount1++)
    {
        gpio_pin_low(aHandle,CLOCK_PIN_SCLK);

        if(iTable[aChar]&(0x80>>tCount1))
        {
            gpio_pin_high(aHandle,CLOCK_PIN_MOSI);
        }
        else
        {
            gpio_pin_low(aHandle,CLOCK_PIN_MOSI);
        }

        gpio_pin_high(aHandle,CLOCK_PIN_SCLK);
    }
}

void CLOCK_reflect(gpio_handle_t aHandle)
{
    gpio_pin_high(aHandle,CLOCK_PIN_LATCH);
    gpio_pin_low(aHandle,CLOCK_PIN_LATCH);
}

volatile sig_atomic_t iEFlag=0;

void signal_handler(int aSig, siginfo_t *aInfo, void *aCtx)
{
    iEFlag=1;
}

int main(void)
{
    gpio_handle_t tHandle;
    char tDateString[64];
    char tDateSecond;
    time_t tEpocMinites;
    struct timeval tCurrentTime;
    int tToggleCount;
    long tInterruption;

    struct sigaction tSASigAbrt,tSASigTerm;

    memset(&tSASigAbrt,0,sizeof(tSASigAbrt));
    memset(&tSASigTerm,0,sizeof(tSASigTerm));

    tSASigAbrt.sa_sigaction=signal_handler;
    tSASigAbrt.sa_flags=SA_SIGINFO;

    tSASigTerm.sa_sigaction=signal_handler;
    tSASigTerm.sa_flags=SA_SIGINFO;

    if(sigaction(SIGINT,&tSASigAbrt,NULL)<0)
    {
        exit(1);
    }

    if(sigaction(SIGTERM,&tSASigTerm,NULL)<0)
    {
        exit(1);
    }

    CLOCK_initTable();

    tHandle=gpio_open(0);

    gpio_pin_output(tHandle,CLOCK_PIN_MOSI);
    gpio_pin_output(tHandle,CLOCK_PIN_SCLK);
    gpio_pin_output(tHandle,CLOCK_PIN_LATCH);
    gpio_pin_output(tHandle,CLOCK_PIN_BLINK);

    gpio_pin_low(tHandle,CLOCK_PIN_MOSI);
    gpio_pin_low(tHandle,CLOCK_PIN_SCLK);
    gpio_pin_low(tHandle,CLOCK_PIN_LATCH);
    gpio_pin_low(tHandle,CLOCK_PIN_BLINK);

    tInterruption=(1000000/CLOCK_INTERRUPTION);
    tToggleCount=0;

    tDateSecond=0x00;

    while(1)
    {
        gettimeofday(&tCurrentTime,NULL);

        tEpocMinites=(time_t)tCurrentTime.tv_sec;

        strftime(tDateString,sizeof(tDateString),"%H%M%S",localtime(&tEpocMinites));

        if(tDateSecond!=tDateString[5])
        {
            tDateSecond=tDateString[5];

            if(tDateString[0]=='0')
            {
                tDateString[0]=0x00;
            }

            for(int tCount1=0;tCount1<6;tCount1++)
            {
                CLOCK_sendChar(tHandle,tDateString[tCount1]);
            }

            CLOCK_reflect(tHandle);

            gpio_pin_high(tHandle,CLOCK_PIN_BLINK);
            tToggleCount=0;
        }

        if(tToggleCount==(CLOCK_INTERRUPTION/2))
        {
            gpio_pin_low(tHandle,CLOCK_PIN_BLINK);
        }

        tToggleCount++;

        usleep(tInterruption);

        if(iEFlag)
        {
            for(int tCount1=0;tCount1<6;tCount1++)
            {
                CLOCK_sendChar(tHandle,0x00);
            }

            CLOCK_reflect(tHandle);

            gpio_pin_low(tHandle,CLOCK_PIN_MOSI);
            gpio_pin_low(tHandle,CLOCK_PIN_SCLK);
            gpio_pin_low(tHandle,CLOCK_PIN_LATCH);
            gpio_pin_low(tHandle,CLOCK_PIN_BLINK);

            gpio_close(tHandle);

            exit(1);           
        }

    }

    gpio_close(tHandle);

    return 0;
}

冒頭のdefineで設定しているのは、GPIOのピン番号です。
物理ピン番号ではなく、論理ピン番号(=BCMピン番号)ですので注意してください。

冒頭に制作したLED時計との接続は、以下の通りです。

Vcc       ----  物理ピン 4番
SDI       ----  物理ピン11番(GPIO 17)
CLK      ----  物理ピン13番(GPIO 27)
BLK      ----  物理ピン16番(GPIO 23)
LATCH ----  物理ピン18番(GPIO 24)
GND     ----  物理ピン 6番


このプログラムでは、論理ピン番号17、27のGPIOで、SPIをエミュレートしています。

大したことをしていないプログラムですので、読めばわかると思いますが、比較的わかりにくい部分だけ解説すると、

iTable                           7セグLED用のフォントデータ
CLOCK_sendChar   CLKに8クロックを出力して1文字分のコードを送る関数
CLOCK_reflect          LATCHに立ち上がりクロックを出力して、シフトレジスタに蓄積された文字を7セグLEDに反映させる関数

です。

mainでは、1/CLOCK_INTERRUPTION秒毎に現在の時間を取得し、前回の取得時から1秒進んでいた場合に、時計の再描画を掛けています。

やる気のないビルド用スクリプトもつけておきますので、これを使ってビルドしてください。

build.sh

#!/bin/sh

rm *.o

cc -c gpio.c
cc -c main.c
cc -o clock gpio.o main.o

特に問題なければ、clockという実行ファイルができるので、これを実行すれば7セグLED時計は動作するはずです。

0 件のコメント:

コメントを投稿