PIC12F683 / PIC16F1937を使ってみる実験的な

 <HEADER>

あ、いつものgdgd無駄文章はカットです。

今回やりたいことまとめ

  • PIC12F683で簡単なプログラム動かしてみる : 所謂Lチカ的な。
  • 8ビットシフトレジスタを使ってみる(なおLEDの個数が足りない模様) : 複数のLEDを少ないピンで制御する。
  • 赤外線で通信してみる : PIC16F1937とRaspberry Pi 3と簡単な通信をしてみる。

この3点やってみようと思います。

使ったものと環境

使ったもの

環境

  • MacBook Pro (Retina, 13-inch, Early 2015) : macOS Sierra 10.12.5
  • MPLAB X IDE v3.6
  • MPLAB XC8 Compiler v1.42 (Free)

 <BODY>

PART 1 : PIC12F683でLチカ

これはそんなに難しくないですね。

そもそもこれ他のサイトに書いてありますし。

まぁLOGとして…。

普通にLチカしてみる

“Lチカ”ってプログラミング入門でやる所の”Hello, World”プログラムみたいなものですね。
たぶん世界一簡単かつ有名なLEDの使い道…

Lチカの回路とプログラム組む前にPICのピンの出力がどんなものか調べてみましょうか。

今回組む回路の電源は一般的な単三電池3個で構成しています。

つまり、電源電圧は1.5[v] * 3 = 4.5[v]です。

試しにひたすらあるピンから電気を流し続けるプログラムを書いて実行して見ました。

LEDが光ってますね。(そらな

このLEDに流れるVをテスタで測って見ましょう。

IMG_20170614_003628

図3 : なんか留め具がアレですが…

まぁだいたい電源電圧と同じ値(一般的な単三電池3本 = 1.5V * 3 = 4.5V)をとりますね。

これでオームの法則から使うLEDから抵抗が求まります。

ではLチカしてみましょう。

回路は図1, 図2と同じですね。(点滅するのはプログラムで制御するから当たり前…

で、コードは↓

/*
 * File:   main.c
 * Author: BEAN
 */

#include 

#pragma config FOSC = INTOSCIO  // Oscillator Selection bits (INTOSCIO oscillator: I/O function on RA4/OSC2/CLKOUT pin, I/O function on RA5/OSC1/CLKIN)
#pragma config WDTE = OFF       // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = ON       // Power-up Timer Enable bit (PWRT enabled)
#pragma config MCLRE = OFF      // MCLR Pin Function Select bit (MCLR pin function is digital input, MCLR internally tied to VDD)
#pragma config CP = OFF         // Code Protection bit (Program memory code protection is disabled)
#pragma config CPD = OFF        // Data Code Protection bit (Data memory code protection is disabled)
#pragma config BOREN = ON       // Brown Out Detect (BOR enabled)
#pragma config IESO = OFF       // Internal External Switchover bit (Internal External Switchover mode is disabled)
#pragma config FCMEN = OFF      // Fail-Safe Clock Monitor Enabled bit (Fail-Safe Clock Monitor is disabled)

#define _XTAL_FREQ  1000000 //__delay_ms関数のためのクロック周波数指定

int main(int argc, char** argv) {

    // PICマイコン初期化
    OSCCON = 0x40;  //クロック周波数は1MHz
    ANSEL  = 0x00;  //全てのピンをデジタルピンで使用
    TRISIO = 0x00;  

    GP5 = 0;

    while(1){
        GP5 = 1;
        __delay_ms(50);
        GP5 = 0;
        __delay_ms(950);
    }

    return;
}

GP5という変数が5番目のピンに出力させるかどうかを指示するピンです。

GP5に1を代入すると5番目のピンが図3に示したように4.33[V]を出力します。逆に0を代入すると0[V]を出力します。つまり1を代入した時5番ピンにLEDが接続されていると光ります。0を代入すると消えます。

__delay_ms関数は引数のミリ秒だけ待機する関数です。つまり__delay_ms(50);は50ミリ秒間待機しますし、__delay_ms(1000);は1000ミリ秒間–––つまり1秒間–––待機します。

ただこの関数を使う際にPICマイコンが何Hzで動いているか指定しておかなければなりません。それが#define _XTAL_FREQ 1000000 って部分です。
今回は1MHzで動かしている(”OSCCON = 0x40;”って部分)ので1M = 10⁶ = 1000000を指定しています。

で、実際に動かしてみると50ミリ秒光って950ミリ秒消灯を繰り返している気がします。いや計測できないんでコードを信じるしかありませんからね。

光るインターバルを超高速にしてみる

今まで動かしてきたプログラムはチカッ、チカッって感じでいかにも”Lチカ”て感じでした。

この”チカッ”が超高速ならどうなるでしょうか。あぁわかる?飛ばしてどうぞ。

__delay_msをもっと短くしてみましょう。

/*
 * File:   main.c
 * Author: BEAN
 */

#include 

#pragma config FOSC = INTOSCIO  // Oscillator Selection bits (INTOSCIO oscillator: I/O function on RA4/OSC2/CLKOUT pin, I/O function on RA5/OSC1/CLKIN)
#pragma config WDTE = OFF       // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = ON       // Power-up Timer Enable bit (PWRT enabled)
#pragma config MCLRE = OFF      // MCLR Pin Function Select bit (MCLR pin function is digital input, MCLR internally tied to VDD)
#pragma config CP = OFF         // Code Protection bit (Program memory code protection is disabled)
#pragma config CPD = OFF        // Data Code Protection bit (Data memory code protection is disabled)
#pragma config BOREN = ON       // Brown Out Detect (BOR enabled)
#pragma config IESO = OFF       // Internal External Switchover bit (Internal External Switchover mode is disabled)
#pragma config FCMEN = OFF      // Fail-Safe Clock Monitor Enabled bit (Fail-Safe Clock Monitor is disabled)

#define _XTAL_FREQ  1000000 //__delay_us関数のためのクロック周波数指定

int main(int argc, char** argv) {

    // PICマイコン初期化
    OSCCON = 0x40;  //クロック周波数は1MHz
    ANSEL  = 0x00;  //全てのピンをデジタルピンで使用
    TRISIO = 0x00;  

    GP5 = 0;

    while(1){
        GP5 = 1;
        __delay_us(100);  //100マイクロ秒点灯
        GP5 = 0;
        __delay_us(400);  //400マイクロ秒消灯
    }

    return;
}

今度は光る時間は100マイクロ秒消灯している時間はどちらも400マイクロ秒にしてみました。

するとどうでしょう。点灯し続けているように見えませんか?もっというとちょっと暗く光ってる気がしますよね。

ちょっとした差の絵を連続的に見せると動画になりますよね(アニメとか)。それと同じでこのプログラムは連続的にシュバババババッとLEDが着いたり消えたりを繰り返すので光ってるように見えるわけです。しかし光っていない時もあるのでその分暗く見えるわけです。

これで暗い時間を増やしていくとどんどんLEDが暗く光っているように見えていきます。

PART 2 : PIC12F683と8ビットシフトレジスタで複数LED制御

PICにLEDつけると若干ゃ楽しい気がしますね。

こういうのって数増やすといろんなことができる気がするのです。10個あればunsignedな2進数的表記をすれば0〜1023まで表示できますし、signed(2の補数表現の場合)だと-512〜511まで表現できます。

13個あれば10進数の数字一桁は表せそうですよね。

LEDが13個あれば...

図1 : 青い点はLEDですよ?

これらを実現しようとしましょう。しかしPICのピンが不足っ・・・・圧倒的不足っ・・・・!

ピンの個数の不足だけではありません。あまりLEDを繋げるとLED側に電力を取られてPICの動作に必要な電力も不足するんですよね。

この問題を解消するのが8ビットシフトレジスタです。

今回使う8ビットシフトレジスタ(SN74HC595N)はPIC側は合計4本のピンで最大8個のLEDを制御することができます。

8ビットシフトレジスタをどのように使用し、どう制御できるか調べてみましょう。

まずはピン配置を…!

スクリーンショット 2017-06-13 14.17.19

図2 : ピン配置(データシート *1より)

各ピンの役割

今回使うピンだけを説明していきます。

VCCが電池のプラス極, GNDが電池のマイナス極を繋げる所で、QAからQD(QEからQHまでも同様の動きをします)が一般的な出力ピンです。
SER, OE’(上付き棒が表現できないので” ‘ “で表します), RCLK, SRCLK, SRCLR’がそれぞれ役割があります。全て図3の表を見れば書かれているのですが自己満足で説明していきます。

スクリーンショット 2017-06-13 14.17.31

図3 : 各ピンの使いかた(データシート *1より)

データを受信/保存する

SERはデータを受信する入力ピンです。ここにLED制御する信号を送り込みます。

SECLKはSERに送られてきたデータを読み取るクロックを入力するピンです。
SERに入力がされている間にSECLKでクロックを送るとそのデータがQAからシフトレジスタに格納されます。
ちょっとややこしいので図4のタイミングチャートを使って説明。

ss

図4 : SRCLKとSERとシフトレジスタRAのタイミングチャート

SERに電圧がかかって(1が入力されて)いる時、SRCLKにも電圧がかかる(1が入力される)とシフトレジスタRAに1が保存されます。

そして”シフトレジスタ”なのでデータを保存していくと、どんどんずれて(シフト)していきます。

tc.gif

図5 : シフトの図

SRCLKに1が入力されるたびにSERに入力されている値がRAに保存され、それまでRAにあったデータはRBに、RBにあったデータはRCに…RHに保存されていたデータは…さようなら。となっていきます。

データの送信

次にRCLKを説明していきます。

RCLKはシフトレジスタに保存された値をストレージレジスタに移すクロックです。

もっと簡単に言うと、RCLKにクロックが与えられれば、シフトレジスタに保存した値を出力することができます。

これは簡単ですよね。ただの送信スイッチだと思えばいいので…。

同じデータの送信のくくりとしてOE’も説明しておきましょう。

OE’はQA〜QHまでの出力ピンを有効にするか無効にするかのピンです。

OE’に1が入力されていれば無効、OE’に0が入力されていれば有効になると言うことです。

今回の実験においてOE’に関してはGNDに繋ぎっぱなしでも良さそうですね。

データの初期化

ここで言うデータとはシフトレジスタに保存された値のことです。

SRCLR’に0を入力するとシフトレジスタRA〜RHに保存されていた値が全部0に書き換わります。

反対に言うと普通に使うときはSRCLR’に1を入力し続けなければならないわけです。

実験

回路

IMG_20170628_223550

図6 : 回路

うーん。なんだか汚い。綺麗な配線をしたい!

プログラム

今回動かすプログラムはこいつです。

/*
 * File:   main.c
 * Author: BEAN
 *
 * Created on June 10, 2017, 6:51 PM
 */

// CONFIG
#pragma config FOSC = INTOSCIO  // Oscillator Selection bits (INTOSCIO oscillator: I/O function on RA4/OSC2/CLKOUT pin, I/O function on RA5/OSC1/CLKIN)
#pragma config WDTE = OFF       // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = ON       // Power-up Timer Enable bit (PWRT enabled)
#pragma config MCLRE = OFF      // MCLR Pin Function Select bit (MCLR pin function is digital input, MCLR internally tied to VDD)
#pragma config CP = OFF         // Code Protection bit (Program memory code protection is disabled)
#pragma config CPD = OFF        // Data Code Protection bit (Data memory code protection is disabled)
#pragma config BOREN = ON       // Brown Out Detect (BOR enabled)
#pragma config IESO = OFF       // Internal External Switchover bit (Internal External Switchover mode is disabled)
#pragma config FCMEN = OFF      // Fail-Safe Clock Monitor Enabled bit (Fail-Safe Clock Monitor is disabled)

#define SRCLK GP0   //SERから読み取るクロックピン
#define RCLK GP1    //ストアするクロックピン
#define SER GP2     //データ送るピン
#define SRCLR GP4   //シフトレジスタのクリアピン Lowでクリア

#define _XTAL_FREQ 1000000  //__delay_ms関数のためのクロック周波数指定

#include 

void sendLedData(const unsigned char *, signed char);

void main(void) {

    //マイコン初期化
    OSCCON = 0x40;
    ANSEL = 0x00;
    TRISIO = 0x00;

    //シフトレジスタ初期状態
    SRCLK = 0;
    RCLK = 0;
    SER = 0;
    SRCLR = 0;

    SRCLR = 1;  //シフトレジスタのクリアをやめる
    unsigned char data[] = {0, 1, 0, 1};

    sendLedData(data, (signed char)(sizeof(data)/sizeof(data[0])));

    while(1);

    return;
}

void sendLedData(const unsigned char *data, signed char len){

    len = len - 1;

    while(len >= 0){
        SER = data[len];
        SRCLK = 1;
        SRCLK = 0;
        len--;
    }

    RCLK = 1;
    RCLK = 0;
}

今回LED4つに送るデータは”unsigned char data[] = {0, 1, 0, 1};”の部分で指定します。
この場合は1番目のLEDが光らなくて2番目のLEDは光って3番目のLEDが光らなくて4番目のLEDは光ってという具合です。

で、sendLedData関数はそのデータの配列とそのデータの数を引数に持ちます。

その受けた配列を要素ずつシフトレジスタにぶち込んでいきます。またシフトレジスタに値を保存するときはSERに1か0を出力しつつSRCLKにクロックを送ります。

それで全ての要素がシフトレジスタに格納されたらRCLKに1クロック送ってシフトレジスタの値をストレージレジスタに送ります。
これで配列で指定した通りにLEDが点灯します。

動作結果…

で配列が{0, 1, 0, 1}の時は…

IMG_20170628_224140

図7 : {0, 1, 0, 1}の時

うまく動いてますね。

じゃちょっと配列変えてみましょう。

“unsigned char data[] = {1, 0, 0, 1};”に変更して実行!

IMG_20170628_224326

図8 : {1, 0, 0, 1}の時

RES 中立 中立 ENL なポータルが並んでるみたい…

PART 3 : PIC12F683と赤外線LEDで簡単な通信

では今回の最後はちょっと高度っぽい(実際そうではない)ことやってみましょう。

PIC12F683とRaspberry Piで赤外線通信してみましょう。

…と思ってプログラム書いていたのですが、ピンの数が足りない!

うーん。

ここで電子工作グッズを放り込んでいる秋月のダンボールから..

PIC16F1937

図9 : PIC16F1937

PIC16F1937が発掘されました。

PIC16F1937のピンの数は40本あります。

これなら足りるでしょう(足りてなかったの1本だけですが…)

プランニング

で、実際どんな動きをさせるのかと言うと

図

図10 : 簡単に表すと…

LEDが4つ並んで2進数的表現で数値を表します。値を上げて行くボタンを押すと値が1個ずつ上がっていきます(15まできたらその次の値は0とします)。

送信するボタンを押すと赤外線LEDから普通のLED達で表現された値を送信し、RaspberryPiに接続された赤外線センサがそれを受信し、Raspberry Piの画面に受信した値を10進数に変換したものを表示します。

赤外線関係の話

赤外線LEDでどのように値を送るか

赤外線LEDで値を送信する時にどのようなフォーマットで送るのか…

簡単に考えれば1の時は赤外線LEDを点灯させて、 0の時は赤外線LEDを消灯させる。この点灯, 消灯の時間が同じであれば受信できそうです。

しかしこのままではある問題が発生します。最初に送る値が”0″である場合センサがそれを感知できない点です。

信号が送信されない時、つまり何もしていないときの入力値は”0″になり、0の値を送信するときも値の入力値は”0″になります。

この問題を解決するために最初に”1″を送り、その後から送信したい値を送信させます。

もし{0, 1, 1, 0}を送る時、実際には{1, 0, 1, 1, 0}を送信し、{1, 1, 1, 0}を送る時は{1, 1, 1, 1, 0}を送信すると言った具合です。

赤外線の変調云々の話

ここまで点灯させる, 消灯させるで話をしてきましたが、実際に赤外線LEDを点灯, 消灯しただけでは赤外線センサはちゃんと感知してくれません。なぜなら赤外線センサの多くは38kHz変調でフィルタリングされているためです。これは自然光など赤外線LED以外の光の波のノイズを受けないようにするためです。(詳しくは検索して見てください)

赤外線LEDの光を何Hzに変調させるかということは赤外線センサのデータシートにかかれてあります。(基本的に38kHzでしょうけど…)

赤外線センサ_OSRB38C9AA

図11 : 赤外線センサのデータシートより

赤枠で囲んであるところに書いている中心周波数≈38kHzで変調するのでいいでしょう。

ではその38kHzで変調された赤外線において”1″を表現–––つまり点灯–––する時どれぐらいの時間点灯させておけばいいかというと青枠の時間点灯させればいいっぽいです。(これに関してはよくわからないぞ)
Raspberry PiのGPIOの3.3Vに合わせて今回は600us点灯 or 消灯でやっていきます。

PIC16F1937関係の話

使うピンのA or Dとか入出力設定とかPICのクロックとか

PIC16F1937で使うピンはRD0~RD6です。

これら(RDnピン(0 < n < 7))の指定はANSELDレジスタとTRISDレジスタで行います。またクロック周波数はPIC12F683と変わらずOSCCONレジスタで行います。

configについて

PIC12F683よりもconfigの量が増えていますが

PIC16F1938使い方:16F1938覚書 : PIC16F1938とはピン数が違うぐらいっぽいので参考になります。

↑をみればなんとなくわかります。

クロック周波数の部分はPLLENをOFFにしてOSCCONレジスタのMSB(最上位ビット)を0にするとPIC12F683と同様にクロック周波数を設定できるみたいなのでこれでやっていきます。

プログラム

で書いたプログラムは以下の2つになります(送信側のPIC用と受信側のRaspberry Pi用です。)

PIC16F1937(送信)用プログラム

/*
 * File:   main.c
 * Author: BEAN
 *
 * Created on August 2, 2017, 3:57 AM
 */

/*
 * このプログラムについて
 * ^^^^^^^^^^^^^^^^^
 * PIC16F1937に接続された普通のLEDに対して格納されている値を2進数的表現で出力し、またそれは一つのタクトスイッチで値をインクリメントすることができる。
 * もう一つのタクトスイッチで赤外線LEDに対して格納されている値を送信することができる。
 */

// CONFIG1
#pragma config FOSC = INTOSC    // Oscillator Selection (INTOSC oscillator: I/O function on CLKIN pin)
#pragma config WDTE = OFF       // Watchdog Timer Enable (WDT disabled)
#pragma config PWRTE = ON       // Power-up Timer Enable (PWRT enabled)
#pragma config MCLRE = OFF      // MCLR Pin Function Select (MCLR/VPP pin function is digital input)
#pragma config CP = OFF         // Flash Program Memory Code Protection (Program memory code protection is disabled)
#pragma config CPD = OFF        // Data Memory Code Protection (Data memory code protection is disabled)
#pragma config BOREN = ON       // Brown-out Reset Enable (Brown-out Reset enabled)
#pragma config CLKOUTEN = OFF   // Clock Out Enable (CLKOUT function is disabled. I/O or oscillator function on the CLKOUT pin)
#pragma config IESO = OFF       // Internal/External Switchover (Internal/External Switchover mode is disabled)
#pragma config FCMEN = OFF      // Fail-Safe Clock Monitor Enable (Fail-Safe Clock Monitor is disabled)

// CONFIG2
#pragma config WRT = OFF        // Flash Memory Self-Write Protection (Write protection off)
#pragma config VCAPEN = OFF     // Voltage Regulator Capacitor Enable (All VCAP pin functionality is disabled)
#pragma config PLLEN = OFF      // PLL Enable (4x PLL disabled)
#pragma config STVREN = ON      // Stack Overflow/Underflow Reset Enable (Stack Overflow or Underflow will cause a Reset)
#pragma config BORV = LO        // Brown-out Reset Voltage Selection (Brown-out Reset Voltage (Vbor), low trip point selected.)
#pragma config LVP = OFF        // Low-Voltage Programming Enable (High-voltage on MCLR/VPP must be used for programming)

#include 

//括弧内回繰り返す関数形式マクロ, 上限はunsigned intの上限
#define repeat(CYCLE) for(unsigned int REPEATCNT = 0; REPEATCNT < CYCLE; REPEATCNT++)

//__delay_ms()関数のためにクロック指定
#define _XTAL_FREQ 16000000

/* 各ピン名称設定 *1 */
#define SRCLK RD0       //SERから読み取るクロックピン
#define RCLK RD1        //ストアするクロックピン
#define SER RD2         //データ送るピン
#define SRCLR RD4       //シフトレジスタのクリアピン Lowでクリア
#define SEND_PIN RD6    //赤外線LEDにデータを送るピン, 実際はトランジスタのベースにつながる
#define SEND_SWITCH RD3 //赤外線LEDでデータを送るのを開始するスイッチ
#define BINARY_PLUS RD5 //値を上げるスイッチの入力ピン

//送信する数字の初期値
#define INIT_NUM 2
//送信する数字のビット数
#define NUM_DATA_SIZE 4
//赤外線LEDで送信する総データの長さ(bit)
#define ALL_DATA_SIZE 8

//10進数を2進数に変換して配列にする関数
void dectobin(unsigned char, char *, signed char);
//8ビットシフトレジスタを用いてLEDに現在の数値を表示させる関数, *1が必須
void sendLedData(const unsigned char *, signed char);
//PICの一つのピンから現在の数値を送信する関数 *1が必須
void sendInfraredData(const unsigned char *);
//powが使えない為の代わりの関数
char msbis1(char);
//第一引数の値(0 or 1)により赤外線LEDを変調して光らせる関数
void outInfrared(const unsigned char);

void main(void) {

    /* PIC初期化 */
    OSCCON = 0b01111010;    //16Mhz駆動
    ANSELD = 0b00000000;    //全てデジタルピン
    TRISD = 0b00101000;     //RD3, RD5が入力ピン

    //シフトレジスタ初期化
    SRCLK = 0;
    RCLK = 0;
    SER = 0;
    SRCLR = 0;
    SEND_PIN = 0;

    unsigned char binaryNum[NUM_DATA_SIZE] = {0};    //2進数表現のための配列
    char num = INIT_NUM;

    while(1){
        dectobin(num, binaryNum, NUM_DATA_SIZE);
        sendLedData(binaryNum, NUM_DATA_SIZE);
        //送信ボタンが押された時の処理
        if(SEND_SWITCH){
            sendInfraredData(binaryNum);
            __delay_ms(400);                         //連続で押されないようにするためのdelay
        }
        //数値をインクリメントするボタンが押された時の処理
        if(BINARY_PLUS){
            if(num == (msbis1(NUM_DATA_SIZE) - 1)){
                num = 0;
            }else{
                num++;
            }
            __delay_ms(400);                         //連続で押されないようにするためのdelay
        }
    }

    return;
}

void dectobin(unsigned char num, char *array, signed char nbit){
    char digit = 0;
    while(nbit--){
        digit = msbis1(nbit);
        array[nbit] = (char)(num / digit);
        if(array[nbit]){
            num -= digit;
        }
    }
}

void sendLedData(const unsigned char *data, signed char len){

    //シフトレジスタ初期化
    SRCLR = 0;
    SRCLR = 1;

    //上位4ビットに0を格納
    repeat(ALL_DATA_SIZE - NUM_DATA_SIZE){
        SER = 0;
        SRCLK = 1;
        SRCLK = 0;
    }

    //数値データ
    for(int i = 0; i < len; i++){
        SER = data[i];
        SRCLK = 1;
        SRCLK = 0;
    }

    RCLK = 1;
    RCLK = 0;
}

void sendInfraredData(const unsigned char *data){

    char nbit = 0;
    SEND_PIN = 0;

    outInfrared(1);

    for(nbit = 0; nbit < 4; nbit++) outInfrared(data[nbit]);
}

char msbis1(char num){
    char result = 1;
    while(num--){
        result *= 2;
    }
    return result;
}

void outInfrared(const unsigned char data){
    if(data){
        // 600us点灯
        repeat(23){
            RD6 = 1;
            __delay_us(13);
            RD6 = 0;
            __delay_us(13);
        }
    }else{
        // 600us消灯
        __delay_us(600);
    }
}

 

Rapberry Pi 3(受信)用プログラム

/*
* File: main.c
* Author: BEAN
*
* Created on August 2, 2017, 3:57 AM
*/

/*
* このプログラムについて
* ^^^^^^^^^^^^^^^^^^^
* 赤外線センサより赤外線信号を読み、解析してそのデータを表示するRaspberry Pi 3用のプログラム
*/

#include
#include
// GPIO指定をわかりやすくする自作ヘッダファイル
#include "rpi-pin.h"

// 赤外線センサのOutputピンに接続するGPIOピンの指定
#define READPIN GPIO_18
// 数値データのビット長
#define BIT 4

// 赤外線センサから得られたデータを解析し、データの値を返す関数
int receve();
// 配列で表現された2進数を10進数に変換する関数, 4ビット長(=2^0 – 2^3まで)しか対応していない
int todec(int *);

int main(){
if(wiringPiSetup() == -1) return 1;

pinMode(READPIN, INPUT);

printf("%dを受信しました\n", receve());

return 0;
}

int receve(){
int bit = 0;
int data[BIT] = {0};

while(1){
/* 先頭のリーダビットを検知した時の処理 */
if(!digitalRead(READPIN)){
for(bit = 0; bit < BIT; bit++){
delayMicroseconds(600);
data[bit] = !digitalRead(READPIN);
}
return todec(data);
}
}
}

int todec(int *data){
return data[3] * 8 + data[2] * 4 + data[1] * 2 + data[0];
}

Raspberry Pi 3(受信)用のコードの中でincludeされている"rpi-pin.h"はここに置いてあります。

動作結果

OLYMPUS DIGITAL CAMERA

図12 : 2を送信した時

2017-08-09-073224_735x461_scrot

図13 : 2を受信

OLYMPUS DIGITAL CAMERA

図14 : 6を送信した時

2017-08-09-073511_735x461_scrot

図15 : 6を受信

実験の感じとか考察とか

まぁまぁうまくいっています。

ハード(?)な面

が、違う値を受信してしまうこともしばしばあります。
データの冗長化などのデータの確実性のための施策をしていないためノイズなどの影響を受けるからでしょうか。
実際に家電リモコンなどはいくつかのフォーマットの差はあれど基本的には送信するデータのエラーチェック–––正論理なデータの後に負論理なデータを送るなど–––をしているため精度の確保が可能になっています。

今回は精度を求める必要がなかったことと、赤外線を初めて扱ったので簡単なコードにしようとエラーチェックや他の赤外線信号の誤検知排除などはしませんでした。

ソフトな面

今回の実験のコードに関してはできるだけ分かりやすいように書こうと思ったのですが難しいなと。人にわかりやすく書けと最近買ったCの本に書かれていて、一応気をつけて書いたのですが少なくとも数日後にはよくわからんことをしてるなぁと思って書き直したり…。技巧を凝らさないわかりやすいコードをこれから目指そうかと思った所存。

PICの方のソースコードの方で一つ。「これのpow関数でいいじゃん」って関数があります。

char msbis1(char num){
    char result = 1;
    while(num--){
        result *= 2;
    }
    return result;
}

このmsbis1関数(MSB = 最上位ビットが1の時の値を返す…って意味で命名したんですがどうでしょう)は動作的にpow関数とほぼ同じ動きをします。違いはmsbis1関数はpow関数の第1引数が2固定ってとこぐらいでしょう。
なぜ別に関数を作ったのかと言うとメモリリソースの確保のためです。下の画像をみてください。

powuse

図16 : pow関数を使った場合

 

msbis1use

図17 : msbis1関数を使った場合

なんですか。このメモリの消費量の違いは…!。

pow関数(というよりdouble型などを扱う関数や変数)を使うと命令数, 変数などの格納領域を一気に食い漁ってしまうので多量に使えるものではありません。PICプログラミング的に使用するメモリ量をとにかく減らそうということで今回はmsbis1関数を作って最低限のリソースでプログラムを組んでみた次第です。

<Footer>

長かった…。PART.2, 3が説明で長かったのもありますががっつり実験できる時間もなかったので実験する日数も伸びてしまいました。

そういえば最近本をどっさり購入しまして!

LPIC レベル1を勉強しつつGitを読みながらCをもうちょっとできるようになろうと買い込みました。

が、LPICとCで処理がパンクしてGitの方はなかなか勉強できない状況に…。Cを早々終わらせてGitの時間を作りたい。

夏は時間がある…好きなことができる…などとほざいていた7月終盤。BEANさんは夏, 冬, 春休みだけしかバイトしない→長期休みに暇はない。のであまり時間がないよ!
とはいえ家にいる時間は多いのでソフトなことよりハードなことは多少できるか…という読みをしてます。

というわけで夏はもう一回ぐらい何かマシな電子工作ができればなと思ってます(何を作るかの構想だけはあります)。

それではそのうちにまた。

 

 

 

 

広告

PIC12F683 / PIC16F1937を使ってみる実験的な” への1件のフィードバック

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中