第27回 ブザーを制御するコードを追加

プログラムにブザーを制御するコードを追加して、基礎編のプログラムを完成させます!

目次

今回の説明

PICマイコン電子工作入門の基礎編は、次のステップで説明しています。

この記事の説明は「❻ ベース回路にブザーを追加」のうち、「ブザーを制御するコードの追加」を説明します。

  1. LEDを電池と抵抗のみで光らせる回路を組み立てる ⬅︎ 今回
    PICマイコンの回路を組み立てる前に、ブレッドボードの取り扱いに慣れておくことにします。電池、抵抗、LEDのみを使って、ブレッドボード上に回路を組み立ててLEDを光らせてみます。ここでは電池、抵抗、発光ダイオードの回路記号と回路図の説明をして、回路図からブレッドボードに組む方法を説明します。まずはブレッドボードに慣れましょう!
  2. PICマイコンのベース回路を組む
    PICマイコンのはじめの一歩の回路は、LEDを1秒に1回光らせるだけの回路です。
    この回路をブレッドボードに組み立てます。
  3. プログラムを作る
    LEDを1秒に1回光らせるプログラムを作成します。
  4. PICマイコンに書き込んで動作させる
    作成したプログラムをPICマイコンに書き込んで動作させてみます。
  5. ベース回路にスイッチを追加
    LEDの点滅をスイッチで開始させるために、ベース回路にスイッチを追加します。
    これまではLEDを光らせる、という出力制御をしましたが、今度はPICマイコンで外部からスイッチの信号を入力する方法を習得します。
  6. ベース回路にブザーを追加
    スタートスイッチ付きの、1秒に1回光らせる回路を作りましたので、ブザーを追加してタイマーとして完成させます。

動作仕様の検討

今までに作成したプログラムは次のような制御をしています。

  1. 動作開始後の初期状態として、RA5ピンをONにして、LEDを点灯させる
    (電源が入ってPICマイコンが動いたことを示すため)
  2. スイッチが接続されているRA3ピンの監視を続けて、スイッチがONになるまで待つ
  3. スイッチが押されたことを検知後、LEDを1秒に1回、点滅させる。これを永遠繰り返す

今度は、一定時間経過したらブザーを鳴らすようにします。

これをどのように制御すればよいか、上の処理をベースに差分を考えてみます。

差分を赤文字にしてあります。

  1. 動作開始後の初期状態として、RA5ピンをONにして、LEDを点灯させる
    (電源が入ってPICマイコンが動いたことを示すため)
    またRA4ピンをOFFにしてブザーをOFFにしておく
  2. スイッチが接続されているRA3ピンの監視を続けて、スイッチがONになるまで待つ
  3. スイッチが押されたことを検知後、LEDを1秒に1回、点滅させる
    この点滅は一定時間繰り返す
  4. 一定時間経過したら、RA4ピンをONにして一定時間ブザーを鳴らす
    またRA5ピンをONにしてLEDを点灯状態にする

今まで作成したプログラムに、赤文字の部分を追加、あるいは変更すればプログラムが作れそうです。

それではひとつひとつ、どのようにすればよいか検討しましょう。

❶ 動作開始後はブザーをOFF

ブザーをOFFにする制御は、RA4ピンをOFFにすればよいので、次のコードを追加すれば対応できますね。

LATA4 = 0;

❸ 点滅を一定時間繰り返す

一定時間繰り返すのはどうしたらよいでしょうか?

LEDは1秒で1回点滅処理をしています。

例えばタイマー時間を3分にしたい場合、LEDの点滅を180回繰り返せばよいことになります。

ということは繰り返し処理をすればよいので、for文を使えばよさそうですよね。

とりあえず、最初は動作確認のためにタイマー時間を10秒にしたいと思います。

現在のwhile処理の部分を次のように書き換えてみます。

// LEDを一定回数点滅する
for(uint8_t timer=0; timer<10; timer++) {
    // LEDを950ms消灯する
    LATA5 = 0;
    __delay_ms(950);            
    // LEDを50ms点灯する
    LATA5 = 1;
    __delay_ms(50);
}

❹ ブザーを鳴らしてLEDを点灯

forの処理が終わると、一定時間経過したということになりますのでブザーを鳴らしてLEDを点灯状態にします。

ブザーを鳴らすのは、RA4ピンをONにすればよいので、次のコードを追加します。

// ブザーを鳴らす
LATA4 = 1;

同時に、LEDを点灯しますので次のコードも追加します。

// LEDを点灯する
LATA5 = 1;

ところで、ブザーが鳴ったままですとちょっとうるさいので、ブザーは3秒間だけ鳴らすことにしたいと思います。

__delay_ms関数で一定時間待ってブザーを消せばよいので次のコードを追加します。

// 一定時間待ってブザーを消す
__delay_ms(3000);
LATA4 = 0;

あとは この状態をずっと保持する、ということにしたいと思いますので、次のwhileで処理を止めるのはそのままにします。

while(1) {
}

それではプログラムを完成させましょう!

プログラム作成

上の変更点を反映して、次のようにプログラムを完成してみました。

/*
 * File:   main.c
 * 変更履歴
 *   2025.4.14: スイッチ制御追加
 *   2025.4.16: ブザー制御追加
 */

#include <xc.h>

// 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 ) {
    }
    
    // LEDを一定回数点滅する
    for(uint8_t timer=0; timer<10; timer++) {
        // LEDを950ms消灯する
        LATA5 = 0;
        __delay_ms(950);            
        // LEDを50ms点灯する
        LATA5 = 1;
        __delay_ms(50);
    }

    // ブザーとLEDをONにする
    LATA4 = 1; // ブザー
    LATA5 = 1; // LED
    
    // 一定時間待ってブザーをOFFにする
    __delay_ms(3000);
    LATA4 = 0;

    // 以下の命令は実行されない
    return;

}

動作確認

それではプログラムをビルドしてPICマイコンに書き込み、動作確認してみましょう!

10秒間点滅を繰り返したら、ブザーが鳴ってLEDが点灯した状態になるはずです。

うまく動いたら、基礎編の回路とプログラムは完成です!

ただ、ブザー音がちょっと、って感じがしますよね。

タイマーでしたら「ピピピッ」とかのアラーム音の方が良さそうです。

この変更についてはチャレンジ課題として取り組んでみたいと思います。

プログラムの注意点

さて、これでようやく完成したわけですが、上のプログラムではあとあと問題が出てしまうような、よくありがちな実装をしています。

もしかしたら、プログラムを読んでいる時に気づかれた方もいらっしゃるかもしれませんね。

上のプログラムでは動作確認のため、タイマー時間を10秒としました。

でも実用的なタイマーでしたらもうちょっと時間をのばしてみたいですよね。

上のプログラムは、このような時に問題が発生します。

発生する問題

プログラムを変更して5分タイマーを作ってみたいと思います。

今の10秒タイマーを5分タイマーにするには、for文の繰り返し処理を5分ぶん(=300秒)、つまり300回ループするように変更すれば、簡単ですよね。

実際にfor文の繰り返し回数を単に10から300に変更してみます。

for(uint8_t timer=0; timer<300; timer++) {

変更したプログラムをPICマイコンに書き込んで動作させてみると…

いくら待ってももブザーが鳴りません。

このプログラムではいつまで待ってもブザーは鳴りません。

解決方法

この問題は、変数timerの型uint8_tに原因があります。

uint8_tは符号なし8ビットですので、扱える数値範囲は0〜255です。

for文ではtimer++で変数をカウントアップしていますが、timerが255の状態でtimer++をすると0に戻ってしまいます。

つまり、いつまで待っても300までカウントできないわけです。


そこで、変数を宣言するときは変数型に注意する必要があります。

整数の変数型としては次のものが用意されていますので、適切なものを意識して使うようにした方がいいですね。(数値範囲はXC8コンパイラのケースです)

変数型(stdint)変数型数値範囲
int8_tchar−128 〜 127
uint8_tunsigned char0 〜 255
int16_tshort
int
−32768 〜 32767
uint16_tunsigned short
unsigned int
0 〜 65535
int32_tlong−2147483648 〜
2147483647
uint32_tunsigned long0 〜 4294967295

uint8_t型(8ビット)は255までのカウント、つまり4分強のタイマーしか作れません。

uint16_t型(16ビット)であれば65535までのカウント、時間に直すと18時間強まで計測できるタイマーとなります。

このシリーズ記事で製作している回路は、PICマイコンの内部発振回路を使用しています。外部の水晶発振子を使用した場合にくらべるとそれほど正確ではありません。

プログラムで時間計測する場合、変数型はuint16_tunsigned short)で十分ですね。


プログラムを作成する場合、変数型に注意したほうが良いですが、上のような問題があるのであれば、十分余裕があるint32_tlong)やuint32_tunsigned long)を使った方がいいような気がしますよね。

でも、PICマイコンやArduinoなど、一般的なマイコンでプログラムを作成する場合、安易にint32_tuint32_tを使うと問題が出るケースがあります。

そこで、最後に使用する変数型のポイントについて確認します。

使用する変数型のポイント

PICマイコンでプログラムを作成するとき、変数は必要十分なサイズの変数型を使用することがポイントになります。

このシリーズで製作するものはそれほど気にする必要はありませんが、多くのデータを扱う場合は注意が必要です。


例えば、測定した気温を10分おきに測定して保存しておく場合を考えてみます。

気温の値を整数で保存する場合、uint8_t型の数値範囲は−128〜127までですので、uint8_t型で十分です。

一方、PIC12F1822は変数を保存できるRAMの領域は128バイトしかありません。

この領域を全て気温テータの保存に利用できるとすると(現実には他の変数も必要ですので128バイト丸々使うことはできませんが…)、128個分の気温データを保存することができます。10分に1回測定する場合、約21時間分のデータを保存できます。

この気温データをuint32_t型で保存する場合を考えてみます。

uint32_t型は4バイトですので、128バイトのメモリでは32個分のデータしか保存することができません。10分に1回の測定では、約5時間分のデータになってしまいます。


このように、マイコンはメモリが少ないケースがありますので、安易にメモリを使わないように普段から意識するといいかな、と思います。


これでPICマイコン電子工作入門の基礎編の内容は一通り終わりました。

長い間いろいろと寄り道もしてしまいましたが、ようやく基礎編の回路とプログラムが完成しました!

いろいろとわかりづらいところもあったかもしれませんが、長い間お疲れさまでした!

次回から、基礎編の締めくくりとしてチャレンジ課題に挑戦しましょう!

更新履歴

日付内容
2016.12.5新規投稿
2018.11.24プログラムテンプレートをMPLABX IDE v5.10版に変更
2025.4.16ブザー音を3秒間に変更
通知の設定
通知タイミング
guest
9 コメント
新しい準
古い順 一番投票が多い
本文中にフィードバック
全てのコメントを見る
ahjime wada
ahjime wada
8 年 前

ありがとうございます。一応動いたのでこの先もうすこし悪あがきしてみようと思います。お忙しいと思いますのでお返事はあわてません。こちらはもう集中してできない歳なのでゆっくりのほうがありがたいです。
 それにしてもたぶん初歩的なことがわかっていないので起こすミスが多いのでしょうね。今回は丁寧に教えていただいてありがたかったです。また、わからないところがあればよろしくお願いします。

管理者
管理者
8 年 前

早速エラーメッセージどうもありがとうございました。

メッセージを見る限り、マイコンの型番が正しく認識されていないようですが、2回目の書き込みで動作したとのことで、プログラム書き込みは成功しているようです。

こちらでももう少し調べてみますので、何か分かりましたらこのコメント欄に書き込みいたします。

時間が取れないため、週末の調査になりますことをご容赦ください。

hajime wada
hajime wada
8 年 前

お世話になっております。お忙しいのに無理なことを言っているのではないかと心配です。先ほど思い立って最初から新規のプロジェクトにして更のプログラムをコピーし、接続も確認し、pickitも慎重に押さえて書き込みましたところ、やっぱりエラーが出ましたが、2度目になんと最後のverifyがでて正常に作動しました!(verifyは昨日もなんかの拍子に出ていたのですが作動はしませんでした)これで成功ということでいいのでしょうか?

 念のためエラーメッセージをお送ります。

Connecting to MPLAB PICkit 3…

Currently loaded firmware on PICkit 3
Firmware Suite Version…..01.46.14
Firmware type…………..Enhanced Midrange

Programmer to target power is enabled – VDD = 5.000000 volts.
Target Device ID (0x0) is an Invalid Device ID. Please check your connections to the Target Device.

Device Erased…

Programming…

The following memory area(s) will be programmed:
program memory: start address = 0x0, end address = 0x7ff
configuration memory
program memory
Address: 0 Expected Value: 2801 Received Value: 0
Failed to program device

*****************************************************

Connecting to MPLAB PICkit 3…

Currently loaded firmware on PICkit 3
Firmware Suite Version…..01.46.14
Firmware type…………..Enhanced Midrange

Programmer to target power is enabled – VDD = 5.000000 volts.
Target Device ID (0x0) is an Invalid Device ID. Please check your connections to the Target Device.

Device Erased…

Programming…

The following memory area(s) will be programmed:
program memory: start address = 0x0, end address = 0x7ff
configuration memory
Programming/Verify complete
p.s.1 返信のところではDISQUS が邪魔をして送れません。
p.s.2 スクリーンショットの意味が分かりません(^^;)

管理者
管理者
8 年 前

早速ご確認いただき、どうもありがとうございました。
DeviceIDが0x2700ということはPICマイコンを正しく認識しているようですが、なぜエラーになるのか、もう少し調べる必要がありそうです。

お手数ですが、MPLABXで書き込みエラーが発生した際の、エラーメッセージを丸ごとコピペして教えていただけますでしょうか。(スクリーンショットでしたら本サイトの問い合わせページから一度ご連絡いただければと思います。こちらのメールアドレスをご連絡いたします)

hajime wada
hajime wada
8 年 前

すみません 投稿するのに2時間くらいかかりました(;_;)まあ、いつもこんな調子なので気長にお付き合いください。

hajime wada
hajime wada
8 年 前

早速回答していただいてありがとうございます。午前中外出していていま拝見しました。まず電圧の変更ですがFailed

がでました。

 次に、ブザーを外して書き込みするとやはりエラーが出るのですがDEVICE ID(0x2700)と数字が変わってました。そこで、振出しに戻ってスイッチ回路第25回のスイッチ回路のプログラムをコピーして書き込んだところまた同じエラーが出ました!ということで以前うまくいっていたところが、ダメになってしまい落ち込んでおります。

 ちょっと頭を冷やしてひと休み後もう一度挑戦したいと思いますが、なんか基本的なところでミスをしているのかもしれません。書き込みソフトの使い方がおかしいのかもしれません。このへんが初心者の悲しさですね。

 ちなみにブザーそのものは直接つなぐとなります。

しかし投稿がうまくいきません・・・

hajime wada
hajime wada
8 年 前

古希を迎えてから電子工作に取り組み、だいたいが挫折で終わっておりますが、このブログは何とかここまでついてこれました(といっても全部わかっているわけではありません)。
 ところがここにきてブザーが鳴りません。書き込むときに Target Device ID(0x0) is an Invalid DEvice ID. という警告が出て、続けると Failed to Program Device になります。
 プログラムはコピーしたし、ブザーは DB Products のHDB06LFPNを秋月で買いました。先に進めないのでどうしたらいいのか教えていただけるとありがたいです。
 ・・・とここまで書いて投稿の仕方がわからない・・・

管理者
管理者
返信  hajime wada
8 年 前

wadaさま、はじめまして。
古希を迎えられてから電子工作に取り組まれたとのことで、心より尊敬いたします。自分も何歳になっても色々なことに興味をもつ心を持ちたいと思っていますので、そのような姿勢をお持ちでとても羨ましいです。

ところで、ブザーをつけるとInvalid Device IDになる、とのことですが、すみません、すぐに時間が取れませんので少々お時間をいただければと思います。

また、お手間でなければ、以下の動作がどのようになるかお教えいただけませんでしょうか。

1) ブレッドボードからブサーを取り外して(ブザーだけで構いません)、ブザーを鳴らすプログラムを書き込むことができるか

2) (1)がうまく行くようであれば、プログラム書き込み後、ブザーを取り付けて、ブザーを鳴らすことができるか

3) (1)がうまくいかない場合、ブザーとブザーを接続するためのワイヤを外して、ブザーを鳴らすプログラムを書き込むことができるか

試していたただく内容が不明確でしたらコメントいただければと思います。

お手数ですがよろしくお願いいたします。

管理者
管理者
返信  hajime wada
8 年 前

度々申し訳ございません。

取り急ぎ、このサイトで作成しているブレッドボードで確認したところ、現象は発生しませんでした。

ただ、確認していて思い出したのですが、以前、書き込みできていた回路で、急にInvalid Device IDのエラーが出ることがありました。

その時の対処方法は正確に思い出せないのですが、一度、以下の操作を試していただけませんでしょうか。書き込み時の電圧を5Vではなく3.375Vにしてみる、というものです。(特に根拠はないのでただ試していただくだけですが…)

MPLABX上で

1) プロジェクトフォルダを右クリック
2) メニューが表示されるので一番下のPropertiesメニューを選択
3) プロパティダイアログが開くので、左側のCategories領域のPicKit3を選択
4) 上のメニューのOption CategoriesのPowerを選択
5) 電圧メニューから3.375を選択

この設定で書き込みを行うとどうなるかご確認いただければと思います。

目次