【PIC12F683】マイコンで電子ホタルを作成しよう
電子ホタルとは
日本で「ホタル」といえば、ゲンジボタルです。お尻が綺麗な発行をする昆虫です。それを、マイコンを使ってLEDで再現するという取り組みです。LEDが徐々に光っていき、徐々に暗くなっていく光る方法を再現します。マイコンの電源は電池のため、直流で動作しています。そのため、LEDを徐々に光らせるためには、プログラムか回路側で工夫が必要になります。今回は、PWMを使って再現していきたいと思います。
PWMとは
PWM(Pulse Width Modulation)とは、半導体を使った電力を制御する方式の1つです。オンとオフの繰り返しスイッチングを行い、出力される電力を制御します。直流電圧を可変(エネルギー)させる方法です。過去の記事に詳しく記載しているのでそちらを参考にしてください。
PWMの記事はこちら ーーー> ここをクリック
今回の作業で必要なもの
下記に今回使用した部品と金額の一覧を記載します。秋月電子さんで購入した場合の金額を記載しておきました。今回の部品を揃えると、金額は「1035円」かかります。使いまわせる部品も多くあるので、初めに揃えれば購入する必要はありません。
PIC12F683 | 1個 | 170円(I-00801) |
---|---|---|
ブレッドボード | 1個 | 200円(P-05294) |
LED | 2個 | 200円(I-03982)※チップLEDです。 |
コンデンサ0.1u | 1個 | 15円(P-10147) |
抵抗10k | 1個 | 100円(R-25103)※100個入り |
ジャンパワイヤ | 10本 | 350円(P-02932)※10本入り |
PIC12F683の詳細
下記に記事を書いておきましたので、そちらをご覧ください。接続に必要なページは切り抜いておきました。データシートが必要な方は、まとめに記載しておきます。
回路の作成
Eagleを使って今回の回路図を作成します。基本的な回路なので割と簡単に作成可能です。Eagleを使った図面の書き方については、後日記事にしたいと思います。
回路作成についてです。
ポイント1:PICマイコンは必ず「MCLR」があります。このピンは、リセット機能があるため、必ずプルアップでスイッチを入れるか信号
を常にHightにしなければなりません。今回は、常にHightにしました
ポイント2:電源とGNDには、0.1uのコンデンサを接続します。電圧変動やノイズ等の対策で保険的な要素で接続します。
ポイント3:「1」光る 「0」消去 LEDを光らせる方法をプログラムで制御したいと思うので、下図のような配線にしました。
MPLABを使ってプロジェクトを作成
プロジェクトの作成について記事を書いていますので、そちらを参考にしてください。
ホタルの光 ソースコード
PWMの解説通りのプログラムを再現します。ポイントは、「firefly」という自作関数です。マイコンは、電圧を「ON」「OFF」することしかできないため、下図のような動作をするようにプログラミングします。パルスの幅は、88で固定しintervalの値によってONの幅を設定し残りをOFFにするというものです。余り使いたくなかったのですが、「__delay_us(Hotaltime);」を今回採用しています。カッコ内の値×マイクロ秒だけマイコンを停止します。このプログラムは、パルス全てONの状態だけできませんので、工夫してみてください。
#define Hotal 88
#define Hotaltime 100
void firefly(char interval){
unsigned char i;
for(i=1; i<Hotal; i++){
if(i<interval){
GPIO = 0b00000110;
}else{
GPIO = 0b00000000;
}
__delay_us(Hotaltime);
}
}
プログラムの解説
プログラムで使用している光り方は、上記で説明しているように疑似的なPWM波形で表現しています。
(1)for文を使った横幅の決定(時間軸)
1マス _delay_us(Hotaltime)で設定しますが、およそ100μ秒となり、完全な点灯となる時間は、およそ9msとなります。
チラつき等を確認したい場合は、Hotaltimeを200にしたり87回をもう少し増やせば、LEDがチラつきます。ぜひ、実験してみてください
(2)光る速度を上げたり下げたりをしたい場合
//待たせるための雑処理 の部分をコメントアウトしていただくか、unsigned char time_data[5]={20,100,10,250,20}; を変更してもらえれば変わります。
もう一つの点灯の速度は、下記の原理より組み合わせによる実験が必要になります。下図にもありますが、i=1の部分に注目してもらうと、1マス光り、残り86マスは消灯になります。人の視覚をごまかしているため少し点灯し残りは消灯を繰り返しているだけなので、あまり数値を大きくしたり早くするとLEDのちらつきがわかるようになります。納得ができる組み合わせがあれば、実験で導き出してみてください。よければコメントをお願いいたします。
(3)オシロスコープ
細かな設定は、オシロスコープがあるともっとわかりやすくなります。しかしながら、今回のような波形をしっかり見えるようにしようと思うとPickit2のような、なんちゃってLogicToolでは対応できません。マイコンには欲しいですね。
ソースコード
/*
* File: flash.c
* Author: TM-kit
*
* Created on 2022/01/30, 23:19
*/
#include <xc.h>
// #pragma config statements should precede project file includes.
// Use project enums instead of #define for ON and OFF.
// 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 = OFF // Brown Out Detect (BOR disabled)
#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)
unsigned int g_cnt=0;
unsigned int g_hotal_flag=0;
unsigned int g_delayTime = 500;
int g_tCNT=0;
void firefly(char interval);
#define _XTAL_FREQ 4000000
//スイッチを定義して今後SW1とする
#define SW1 GP5
#define Led_ON 1
#define Led_OFF 0
#define Hotal 88
#define Hotaltime 100
static void interrupt time0int (void)
{
if(INTCONbits.T0IF == 1)
{
/*タイマーフラグをリセット*/
INTCONbits.T0IF = 0;
TMR0=252;/*10m秒ごと*/
g_cnt++;
g_tCNT++;
if(g_cnt>g_delayTime){
g_hotal_flag++;
g_cnt=0;//タイマーカウントのリセット
}
//WRITETIMER0(64536);
}
}
void main(void) {
//変数設定
unsigned char i = 0;
unsigned char j = 0;
unsigned char z = 0;
unsigned char time_data[5]={20,100,10,250,20};
/*手順1:内部クロックの設定:4MHz*/
/*データシート:P19,P20*/
OSCCON = 0b01101000;
/*ポートプルアップを使いたい場合は、このオプションレジスタを0で有効にしておく*/
/*プリスケーラ:256 */
OPTION_REG = 0b00000111;
/*手順2:ポートデジタルに設定*/
/*P33(P35)*/
/*設定全てデジタル & Fosc/64*/
ANSEL = 0b01100000;
/*手順2:ポートの入出力の設定 1;入力 0:出力*/
/*今回の設定:GP3は入力のみなので1を立てておく。また、今回は、反省を活かし入力をGP5とします*/
/*データシート:P36*/
TRISIO = 0b00001000;
/*ポートのデータは何が入っているかわからないので、消灯できるように0をポートに入れておく*/
GPIO = 0b00000100;
/*念のためにコンパレータを無効にしておく*/
CMCON0 = 0b00000111;
GP0 = 1;
TMR0 = 252;
INTCONbits.T0IE = 1;
INTCONbits.GIE = 1;
//プログラム開始前の初期設定
g_delayTime = 10;//割り込み時間設定 10ms
g_hotal_flag = 0;
//プログラムの動作開始
while(1){
for(i=1; i<Hotal; i++){
firefly(i);
}
for(i=Hotal; i>1; i--){
firefly(i);
g_hotal_flag = 0;
}
//待たせるための雑処理
for(i=1; i<time_data[j]; i++){
GPIO = 0;
__delay_ms(100);
}
j++;
if(j>5)j=0;
//出力
//GPIO = led_data;
}//プログラムここまで
return;
}
void firefly(char interval){
unsigned char i;
for(i=1; i<Hotal; i++){
if(i<interval){
GPIO = 0b00000110;
}else{
GPIO = 0b00000000;
}
__delay_us(Hotaltime);
}
}
まとめ
点灯方法を変更するには、下記を変えてみてください
define Hotal 88 横と縦の幅 プログラム解説を参照
define Hotaltime 100 点灯間隔
unsigned char time_data[5]={20,100,10,250,20};次の点灯までの待ち時間
大変参考にさせて戴いております
早速ですが当方全くの初心者でプログラミングについてチンプンカンプンのままLEDをホタル点灯したく彷徨ってたどり着きました
上記のプログラムを12F683に書込み点灯はしましたが点灯、消灯がランダムで私が思い描く一定の間隔でホタル点滅を繰り返すものとは違い困っています
(色々なサイトのプログラムを試してみましたがこちらのLEDの光り方が一番スムーズで綺麗で思い描くものと一致いたしました)
上記のプログラミングのどこをどういう風に変更すればよいのでしょうか?
ご教授ください
コメントありがとうございます。
こちらの説明ページにプログラム解説を追加させていただきました。参考にしてもらえると幸いです。
そちらの説明にも記載してありますが、なんちゃってPWMのため、大きく見え方の変更ができないようになっています。
御了承ください。
何か他の説明が必要でしたら、再度書き込みをお願いいたします。
ありがとうございました!!
お忙しい中ありがとうございます
「//待たせるための雑処理 の部分をコメントアウト」しましたら綺麗に光り出しました
プログラミングの入り口に立ったばかりなので「何がわからないのかがわからない」状態です
現在LEDが1秒ごとに点滅を繰り返していますが最終目標が「1秒で2回ホタル点滅し1秒消灯する」
を繰り返す事です
最後に「0b01101000」といったこのような羅列は16進法という事ははわかりますが簡単にどう解釈すればよいのでしょうか?
お時間のある時で結構ですので教えて頂けないでしょうか?
よろしくお願いいたします
お答えいたします。基本的な内容として下記があります
2進数 0b10101010
16進数 0xAA
で記載します。私はどちらの記載でも良いですが、2進数で記載したほうが理解しやすいです。
https://e-work-education.com/2021/12/05/pic12f683-2/ ←ここも参照してみてください
12F683は、出力として5つとれます。
例えば、下記のような記載をすると
0b00001010 GP0の部分が電源OFF(右から1桁目) GP1の部分が電源ON(右から2桁目) というような記載となります
2進数で記載すると、プログラムが少し長くなりますが直感的に理解しやすいので利用しています。
お世話になります
変数の箇所を何十回と変えてみて納得のいくものに近づきました
お手数お掛けしました
ありがとうございます