ラベル Raspberry pi の投稿を表示しています。 すべての投稿を表示
ラベル Raspberry pi の投稿を表示しています。 すべての投稿を表示

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時計は動作するはずです。

2020年7月31日金曜日

Raspberry pi 2とFreeBSDで7セグLED時計を作る - ソフトウェア設定編



の続きです。

Raspberry pi 2にインストールした後の設定方法は、一般的なi386/AMD64のFreeBSDとほぼ同じです。

rootのパスワードの変更は以下のコマンドで行います。

passwd

Raspberry pi 2版のFreeBSDでは、予めfreebsdというユーザが作られているので、必要に応じてrmuserで削除します。

rmuser freebsd

私の環境では、/etc/rc.confに以下の記述を追記しています。

Raspberry piには標準でRTCが搭載されていない為、再起動するたびに時間がクリアされてしまうため、起動毎にntpdateで時計合わせをするようにしています。
また、標準のkeypmapはUS配列ですので、これを106キー日本語配列に設定しています。

keymap="jp.kbd"
ntpdate_enable="YES"
ntpdate_flags="-b NTPサーバのIPアドレス"

タイムゾーンをJSTに変更するには、以下のようにします。

cp -p /usr/share/zoneinfo/Asia/Tokyo /etc/localtime

これぐらいしておけば、あとはほぼ一般的なi386/AMD64版のFreeBSDと同じように使うことができると思います。

2020年7月30日木曜日

Raspberry pi 2とFreeBSDで7セグLED時計を作る - ハードウェア設定編


の続きです。

FreeBSDをインストールしただけでは、Raspberry pi 2が持っている性能や機能をすべて使うことはできません。
いくつかの設定/チューニングが必要です。

まず、RPI2版のFreeBSDでは、デフォルトのCPUクロックが600MHzと低く設定されている為、これを標準の900MHzに設定します。
「/boot/msdos/config.txt」に以下の記述を追加します。

force_turbo=1
arm_freq=900
arm_freq_min=600

また、RPI2版のFreeBSDは、標準の状態ではGPIOは有効化されていますが、I2CやSPIは無効化されている為、必要に応じてこれを有効化します。
I2Cを有効にするには、config.txtに以下の記述を追加します。

dtparam=i2c=on

SPIを有効にするには、/boot/loader.confに以下の記述を追加します。

fdt_overlays="spigen-rpi2.dtbo"

I2CおよびSPIが有効されているかは、再起動後、以下のようにして確認してください。

dmesg | grep iic
dmesg | grep spigen

何らかの表示がされればOKです。

2020年7月27日月曜日

Raspberry pi 2とFreeBSDで7セグLED時計を作る - OSインストール編

Raspberry pi 2とFreeBSDで7セグLED時計を作る - はじめに
Raspberry pi 2とFreeBSDで7セグLED時計を作る - ハードウェア制作

の続きです。

前回までで、ハードウェアの制作が終わりましたので、次にRaspberry pi 2にFreeBSDをインストールしたいと思います。

先に言っておきますが、Raspberry piで手っ取り早く色々やろうとするならば、公式のOSであるRaspbianを使うのが圧倒的に便利です。
インターネット上に情報も多いですし、書籍もたくさん出ています。
どうしてもFreeBSDじゃなきゃ嫌、という人でない限り、Raspbianか他のLinuxディストリビューションを使うことをおすすめします。

Raspberry pi向けのFreeBSDですが、私の知る限りここに情報が集まっています。

https://wiki.freebsd.org/arm/Raspberry%20Pi

2020/07/25現在、FreeBSDでまともに使える/使えそうなRaspberry piは、

Raspberry pi B/B+
Raspberry pi 2
Raspberry pi 3
Raspberry pi Zero(W含む)

です、またRaspberry pi 3およびZero WのWi-Fi機能はFreeBSDでは使えません。
(Wi-Fi機能を提供しているICはSDIO接続で、FreeBSDがSDIOをまともにサポートしていないのが原因です。)

このページでは、私の手元にあるRaspberry pi 2の情報を中心にまとめていきたいと思います。

Raspberry pi 2をFreeBSDで使うには、本体にあったOSイメージをダウンロードしてSDカードに焼く必要があります。

OSイメージですが、FreeBSD 11ではRaspberry pi B/B+および2向けが、FreeBSD 12以降ではB/B+、2、3向けが提供されています。
Raspberry pi 2はarmv7でビルドされていて32bitをターゲットとしています。

Raspberry piに対応したFreeBSDは、以下からダウンロードできます。

https://www.freebsd.org/ja/where.html

RPI-B、RPI2、RPI3がそれぞれRaspberry pi B/B+、2、3に対応したSDイメージになります。

FreeBSDにおいてarm版はTier-2ですので、freebsd-updateが利用できません。
RELEASE版よりも、最新のSTABLE版をダウンロードして利用することをオススメします。
(常に最新の機能を追いかけるのならば、CURRENT版を使って下さい。)

FreeBSD-12.1-STABLE-arm-armv7-RPI2-202XXXXX-rXXXXXX.img.xz

xzアーカイブを解凍してimgファイルにします。
xzコマンドで展開できます。

xz -dv FreeBSD-12.1-STABLE-arm-armv7-RPI2-202XXXXX-rXXXXXX.img.xz

以下のようにしてSD/microSDカードに書き込みます。
(「da0」はご利用の環境によって変わります、dmesg等でSD/microSDカードに割り当てられるデバイス名を確認してから指定してください。)

dd if='PATH TO IMG FILE` of=/dev/da0 bs=16M

作成したSD/microSDカードをRaspberry pi本体にセットして電源を入れれば、FreeBSDがブートするはずです。

尚、rootのデフォルトのパスワードはrootになっています。

2020年6月12日金曜日

Raspberry pi 2とFreeBSDで7セグLED時計を作る - ハードウェア制作


まずはハードウェアの制作からですが、回路設計はそれほど難しくありません。

基本的に「青色7セグメントLEDシリアルドライバモジュール」のVcc、GND、SDI、SCLK、LATCHをRaspberry piの40ピン端子と繋げばよいです。
ドライバモジュール間は、Raspberry pi 2から見て手前のモジュールのSDOを次のモジュールのSDIに接続します。

また、7セグメントモジュール以外に、秒針として点滅するLEDを時分秒の間に2つずつ、計4つ挟みたいと思います。

回路図はこんな感じです。


方眼紙に書いてあるのは、秒針+7セグメントLED 2つ分の回路です。
これを左側に直列に繋げて、合計6つの7セグメントLEDをドライブします。

1つのGPIOで4つのLEDを点灯させるのはやや力不足な為、スイッチ使うトランジスタも追加しています。

最終的に、以下の部品を秋月電子で注文しました。
※ これ以外に、10KΩと100Ωの抵抗が2本ずつ必要です。

最終的に、こんな感じに実装しました。


Raspberry piと接続する端子は、
  • Vcc
  • SDI(シリアルデータ入力)
  • CLK(シリアルクロック入力)
  • BLK(秒針LED点滅用)
  • LATCH(7セグメントLED表示ラッチ用入力)
  • GND
の6端子になります。

2020年6月10日水曜日

Raspberry pi 2とFreeBSDで7セグLED時計を作る - はじめに

今まで自分の部屋では、青色7セグメントLEDの電波時計を使っていたのですが、最近近所に高い建物が建ったためか、電波を受信しなくなり、時間が狂うようになってしまいました。
幸いにも、我が家には以前購入したままになっていたRaspberry pi 2があったので、NTPで正確な時刻を取得するのは容易です。
これを使って、電波が届かなくても狂わない時計を作ることにしました。


■表示部分に何を使うか?

アナログ時計はハードルが高そうなので、電子部品を使ってなんとかしてみたいと思います。
  • 7セグメントLED
  • ドットマトリクスLED
あたりがぱっと思いつきますが、ドットマトリクスLEDは制御が難しいし、ほぼダイナミック点灯(高速で点滅を繰り返す表示方法、人間の目には、残像により常時点灯しているように見える)しかできないので、7セグメントLEDをスタティック点灯駆動させて作ってみたいと思います。

■利用できそうな汎用ロジックIC

Raspberry pi 2にはGPIOが豊富にありますが、後先考えずにたくさん使ってしまうのももったいないので、汎用ロジックICを使って、使用するGPIOポートをなるべく節約してみたいと思います。
7セグメントLEDを駆動させるのに使えそうな汎用ロジックICをざっと洗い出してみました。

74HC4511

4bitの数値入力を7セグLEDの8出力に変換してくれる優れものです。
しかも、ラッチ回路があるので、一度入力した値を記憶してくれ、保持している間はRaspberry pi 2本体は他の処理をすることができます。
ただ、フォントは製造元によりまちまちで、「6」および「9」が5セグメントで点灯するものがあるので、意図した表示にするにはデータシートの確認が必要です。
また、「.」や、ABCDEFなどのアルファベット表示に対応していないのがイマイチです。
加えて、値段が他の汎用ロジックICよりやや高めです。

74HC137/74HC138

3入力→8出力のデコーダで、3bitの数値出力を8bit出力に変換してくれるICです。
74HC137はラッチ回路あり、74HC138はラッチなしの違いです。
7セグLEDの出力を保持する為に使うのではなく、ドライバICのセレクタに使う感じです。
これ1つで、3bit入力で8個の7セグメントLEDをセレクトすることができます。

74HC4514

4入力→16出力デコーダーで、4bitの数値出力を16bit出力に変換してくれるICです。
あまり生産されておらず、入手が難しいです。
74HC137/74HC138 2つで代用可能です。
74HC137/74HC138同様に、7セグLEDのドライバICのセレクタに使うのが良いかもしれません。

74HC595

ストレージ機能付きのシフトレジスタで、シリアル入力をパラレル出力に変換してくれるICです。
シリアル出力端子もあるため、連結して複数つなげることが可能です。
Raspberry pi 2からはSPIなどで比較的容易に駆動できます。

SPIが使えるなら、汎用ロジックICの総数を少なく済ませられる74HC595を7セグメントLED分直接に接続するのが一番リーズナブルです。

■便利な既製品

実は、74HC595と7セグメントLEDを駆動する為の抵抗やキャパシタがセットになった商品があります。


さすが秋月電子です、心得ていらっしゃいます。
今回は手っ取り早くこれを使いたいと思います。