最終回 チャレンジ課題(5)

最後に、ちょっだけ実用的なタイマーを2種類作ってみます。

目次

チャレンジ課題

❶ そら豆タイマー

初夏になると、そら豆が出回り始めますよね。

この記事をお読みの季節によりますが、旬でしたらぜひ「そら豆タイマー」を作ってください。

「そら豆タイマー」って勝手に作った言葉なんですが、何のタイマーかというと、その名前の通り、そら豆を茹でる時のタイマーです。

それって、茹でる時間を計るだけでいいんじゃないの?、って思うかもしれませんが、そら豆はもうちょっと奥が深いんです。


そら豆ってよく見ると、こんな感じで2種類あります。

Pic basic 30 bean types

ヘタの部分が黒っぽいものと、淡い緑色のものがあります。

この違いはそら豆の成熟度合いに起因しているそうです。

そら豆は鞘の中で、この部分と鞘が繋がって栄養をもらっていて、豆が十分な栄養を蓄えると、そら豆は自分からこのつながりを切るそうなんです。

ヘタの部分が黒っぽいものは、栄養もらう繋がりを切ってしばらく経過したもので、少し硬くなってきています。そのため、少しだけ茹で時間を長くするとホクホクになります。

淡い緑色のものは、まだ栄養をもらうために鞘と繋がっているものです。豆がまだ若いため、茹で時間はちょっと短めがいいようです。

茹で時間ですが、黒っぽいものは3分程度、淡い緑色のものは2分30秒程度が最適な茹で時間のようです。

ということで、そら豆を美味しく茹でる「そら豆タイマー」を作りましょう。

実装は以下の仕様とします。

  • タイマー時間は3分
  • タイマー開始から30秒経過したら、淡い緑色の豆を投入する合図をする

これで美味しいそら豆を茹でることができますね。

そら豆は食べないですか?

それでしたら、次の課題に取り組んでみてください。

❷ 集中用作業タイマー

「ポモドーロ・テクニック」って聞いたことはありますでしょうか。

試験勉強などをする時、何時間も机に向かっていると集中力が続かないですよね。

このような時は、短時間作業を集中して、ちょっと休憩する、ということを繰り返すと集中力がより継続するそうです。

ポモドーロ・テクニックとは、次のように作業を進めて集中力を維持するテクニックです。

  • 25分集中して作業をします
  • そのあと5分休憩します
  • ❶と❷を1セットとして、3〜4セット繰り返します
  • 1セット終わったら、長めの休憩(15分〜30分程度)をとります

この「ポモドーロ・テクニック」はフランチェスコ・シリロ氏により発明され登録商標にもなっています。

「ポモドーロ」という名前は、氏が使用していたトマト型のキッチンタイマーにちなんでつけられたそうです。

個人的にポモドーロ・テクニックを使用することもありますが、25分集中、5分休憩を2セット繰り返すと、そのあとの集中時間はもう少し短い方がいいと感じています。

そこで、基礎編の最後のチャレンジ課題は、自分にあった集中用作業タイマーを作ることにしたいと思います。

基本仕様は以下の内容とし、実際に使ってみて自分に合わせたタイマーにすると良いと思います。

  1. タイマースタート
  2. 25分経過したら休憩の合図のアラームを鳴らす
  3. 5分経過したら作業再開の合図のアラームを鳴らす
  4. ❷と❸を3セット行ったら最後に作業終了のアラームを鳴らす

なお、このポモドーロ・テクニックは、勉強に限らず、掃除など、何か一気に作業を進めたい場合に向いているので、いろいろな生活場面で使ってみてください。

解答例

❶ そら豆タイマー

プログラムは前回とほとんど同じになりますが、#defineで定義する時間をそら豆タイマーの動作に合わせて変更してみました。

また、LEDの点滅は今回はタイマー全体を通して1秒に1回点滅するようにしてみました。(余り深い意味はないのですが…)

/*
 * PICマイコン電子工作入門 基礎編 第30回
 * チャレンジ課題1 解答例
 */

#include <xc.h>

// タイマー時間設定
#define TIMER_TOTAL  180  // トータルの茹で時間 (秒)
#define TIMER_GREEN  30   // 茹で始めてからヘタの部分が緑色のそら豆を投入するまでの時間 (秒)
#define ALARM_NOTIFY 300  // 途中のアラーム音の長さ (単位はミリ秒で950ms未満にする)

// PIC12F1822 Configuration Bit Settings
// 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 = OFF      // Power-up Timer Enable (PWRT disabled)
#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 PLLEN = OFF      // PLL Enable (4x PLL disabled)
#pragma config STVREN = OFF     // Stack Overflow/Underflow Reset Enable (Stack Overflow or Underflow will not 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)

// クロック周波数指定
// __delay_ms()関数が使用する
#define _XTAL_FREQ 1000000

void main(void) {

    // PICマイコン設定
    OSCCON = 0b01011000;  // 内部クロック周波数を1MHzに設定
    ANSELA = 0b00000000;  // すべてのピンをデジタルモードに設定
    TRISA  = 0b00001000;  // すべてのピンを出力モードに設定(ただしRA3ピンは常に入力モード)

    // ブザーをOFF、LEDをONにする
    LATA4 = 0; // ブザー
    LATA5 = 1; // LED

    // スイッチが押されるまで待つ
    while( RA3 ) {
    }
    
    // 最初の茹で時間カウント
    for(uint16_t timer=0; timer<(TIMER_GREEN); timer++) {
        LATA5 = 0;
        __delay_ms(950);            
        LATA5 = 1;
        __delay_ms(50);
        LATA5 = 0;
    }

    // ヘタが青いそら豆の投入タイミングを知らせる
    LATA4 = 1;
    __delay_ms(ALARM_NOTIFY);
    LATA4 = 0;

    // 時間を調整してLEDを点滅
    __delay_ms(950 - ALARM_NOTIFY);
    LATA5 = 1;
    __delay_ms(50);

    // ブザーを鳴らすために1回分使ったので、残り時間はマイナス1秒にする
    for(uint16_t timer=0; timer<(TIMER_TOTAL - TIMER_GREEN - 1); timer++) {
        LATA5 = 0;
        __delay_ms(950);            
        LATA5 = 1;
        __delay_ms(50);
    }

    // アラーム音を3回鳴らす
    for(uint8_t i=0; i<3; i++) {
        // ピッ
        LATA4 = 1;
        __delay_ms(70);
        LATA4 = 0;
        __delay_ms(70);
        // ピッ
        LATA4 = 1;
        __delay_ms(70);
        LATA4 = 0;
        __delay_ms(70);
        // ピーッ
        LATA4 = 1;
        __delay_ms(70);
        LATA4 = 0;
        // 1回分の音パターンの間を少し空ける
        __delay_ms(800);
    }

    // 動作を止める
    while(1) {
    }
    
    // 以下の命令は実行されない
    return;
}

❷ 集中用作業タイマー

実装方法はいろいろ考えられますが、次のようにプログラムを作成してみました。

  • 作業時間、休憩時間、繰り返し回数はプログラム先頭で#define定義
  • 作業時間、休憩時間とも、LEDの点滅感覚は2秒に1回
  • 作業時間、休憩時間とも、残りの時間が短くなってきたらLEDを点灯状態にする
    (点滅間隔を早くすることも考えられますが、忙しなくなるので点灯状態にしてみました)
  • アラーム音は、休憩時間に入る時は「ピーッ」、作業再開のときは「ピッピッ」、全体作業終了は「ピッピッピッ」
  • 作業、休憩ともに、正味の時間を計測するために、アラーム音を鳴らす時間は作業時間、休憩時間にカウントしない

他にもいろいろ工夫するところはありそうなので、ご自身の実装したい仕様に合わせてプログラミングしていただければと思います。

自分で自由にカスタマイズできるというのは、電子工作の醍醐味ですね。

/*
 * PICマイコン電子工作入門 基礎編 第30回
 * チャレンジ課題2 解答例
 */

#include <xc.h>

// タイマー時間設定
#define WORK_TIME   (60*25)  // 作業時間 (秒)
#define REST_TIME   (60*5)   // 休憩時間 (秒)
#define NOTIFY_TIME 60       // 作業時間、休憩時間の残り時間が少なくなったらLEDを点灯状態にするので、その時間を指定 (秒)
#define REPEAT_TIME 3        // 繰り返し回数

// PIC12F1822 Configuration Bit Settings
// 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 = OFF      // Power-up Timer Enable (PWRT disabled)
#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 PLLEN = OFF      // PLL Enable (4x PLL disabled)
#pragma config STVREN = OFF     // Stack Overflow/Underflow Reset Enable (Stack Overflow or Underflow will not 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)

// クロック周波数指定
// __delay_ms()関数が使用する
#define _XTAL_FREQ 1000000

void main(void) {

    // PICマイコン設定
    OSCCON = 0b01011000;  // 内部クロック周波数を1MHzに設定
    ANSELA = 0b00000000;  // すべてのピンをデジタルモードに設定
    TRISA  = 0b00001000;  // すべてのピンを出力モードに設定(ただしRA3ピンは常に入力モード)

    // ブザーをOFF、LEDをONにする
    LATA4 = 0; // ブザー
    LATA5 = 1; // LED

    // スイッチが押されるまで待つ
    while( RA3 ) {
    }

    // 1セットを3回繰り返す
    for(uint8_t repeat=0; repeat<REPEAT_TIME; repeat++) {
    
        // 作業時間計測
        // 残り少なくなるまでの時間帯は、早い点滅は忙しないので2秒に1回点滅
        for(uint16_t timer=0; timer<(WORK_TIME-NOTIFY_TIME); timer++) {
            if(timer%2 == 0) { // timerが偶数のときのみLEDを点滅して1秒計測
                LATA5 = 0;
                __delay_ms(950);            
                LATA5 = 1;
                __delay_ms(50);
                LATA5 = 0;
            } else { // timerが奇数のときは何もしないで1秒計測
                __delay_ms(1000);
            }
        }
        
        // 残り時間が少なくなったらLEDを点灯状態のまま時間待ち
        LATA5 = 1;
        for(uint16_t timer=0; timer<NOTIFY_TIME; timer++) {
            __delay_ms(1000); //
        }
        LATA5 = 0;

        // 休憩に入るタイマー
        //   「ピーッ」という音
        LATA4 = 1;
        __delay_ms(500);
        LATA4 = 0;
        
        // 休憩時間も作業時間と同様に計測
        // 残り少なくなるまでの時間帯は、早い点滅は忙しないので2秒に1回点滅
        for(uint16_t timer=0; timer<(REST_TIME-NOTIFY_TIME); timer++) {
            if(timer%2 == 0) { // timerが偶数のときのみLEDを点滅して1秒計測
                LATA5 = 0;
                __delay_ms(950);            
                LATA5 = 1;
                __delay_ms(50);
                LATA5 = 0;
            } else { // timerが奇数のときは何もしないで1秒計測
                __delay_ms(1000);
            }
        }
        
        // 残り時間が少なくなったらLEDを点灯状態のまま時間待ち
        LATA5 = 1;
        for(uint16_t timer=0; timer<NOTIFY_TIME; timer++) {
            __delay_ms(1000); //
        }
        LATA4 = 0;

        // 作業開始合図としてピピッと鳴らす
        // ピッ
        LATA4 = 1;
        __delay_ms(70);
        LATA4 = 0;
        __delay_ms(70);
        // ピッ
        LATA4 = 1;
        __delay_ms(70);
        LATA4 = 0;
    }
    
    // 作業全体終了を知らせるためにピッピッピッのアラーム音を3回鳴らす
    for(uint8_t i=0; i<3; i++) {
        // ピッ
        LATA4 = 1;
        __delay_ms(70);
        LATA4 = 0;
        __delay_ms(70);
        // ピッ
        LATA4 = 1;
        __delay_ms(70);
        LATA4 = 0;
        __delay_ms(70);
        // ピーッ
        LATA4 = 1;
        __delay_ms(70);
        LATA4 = 0;
        // 1回分の音パターンの間を少し空ける
        __delay_ms(800);
    }

    // 動作を止める
    while(1) {
    }
    
    // 以下の命令は実行されない
    return;
}

更新履歴

日付内容
2016.12.5新規投稿
2025.4.21解答例追加
通知の設定
通知タイミング
guest
3 コメント
新しい準
古い順 一番投票が多い
本文中にフィードバック
全てのコメントを見る
村雨
村雨
6 年 前

初めまして。質問させてください。
以前の記事にunsigned char型の変数だとfor文で256以上の繰り返しをできないので、より繰り返しが必要になる場合はより数を保持できる型を使用したほうがいいとの指摘がありました。また同記事ではその場合メモリを食い処理時間もかかるので、いかに効率よくプログラムをするか考えたほうがいいということも付け加えられていました。
そこで本記事のプログラムを作る場合、unsigned char型を使ったfor文でfor(timer=0; timer<240; timer++)として、これを4回繰り返したのちに、for(timer=0; timer<60; timer++)とすれば25分を作れると思うのですが、メモリの観点からは効率化されているといえるのでしょうか? それともプログラム分が多くなる分効率が悪いとなってしまうのでしょうか。
上位の型を用いたほうがあとで書き直しがしやすいというのは理解できますが。

iwa
iwa
返信  村雨
2 年 前

横からすみません。
上位の型を用いるほうが簡単ですね。
MPLAB X IDEでビルドすると、下記のような表示が出ます。
(これは課題を行った結果ではなく別のPICマイコンの別プログラムでの表示です)
Memory Summary:
  Program space    used  C1Dh ( 3101) of 1000h words  ( 75.7%)
  Data space      used  4Fh (  79) of  170h bytes  ( 21.5%)
  EEPROM space     used  10h (  16) of  100h bytes  ( 6.2%)
  Configuration bits  used   2h (   2) of   2h words  (100.0%)
  ID Location space  used   4h (   4) of   4h bytes  (100.0%)
これを見てプログラムスペースに余裕があるのかデータスペースに余裕があるのかで判断されれば良いのではと思います。
組み込みマイコンプログラムなので使うPICマイコンに合わせてどちらが良いかは判断された方が良いと思います。

目次