前回のマイコン動作を実際のプログラムで確認してみましょう。
●ヘッダ・ファイル
1行目と末行はデバイス設定に必須な記述です。
●グローバル変数
timecはメイン・プログラムで割り込み回数をカウントして指定数のウェイトを作成するのに使います。nightcountは一日のうち午後五時から十時までの間点灯させるための定時起動タイマ変数です。
●Timer0割り込み
●乱数計算ルーチン
●定時起動タイマ定数の宣言
●メイン・ループ
ループの初めではnightcountを調べて、午後十時以降・翌日五時までならば全部のLEDを消灯させます。それ以外は、8個のオブジェを制御します。forループで0~7までを前回の説明に沿った方法で計算し、led[]に格納します。格納された値は、先の割り込みプログラムの中でPWM点灯処理します。
●メイン・プログラム
すでに、おわりのこととは思いますが、定時起動タイマとは時間を計数しますが、時計ではありませんので、最初に電源を投入する時間で決まってしまいます。つまり五時~十時にしたければ、午後五時に電源を投入してください。時間精度は内蔵RCで数分/日、セラロックで数十秒/日、水晶発振子では数分/月です。
したがって、内蔵RCでは1週間に1回程度、決まった時間に電源を入れなおす必要が出てきます。また、ずれる割合がある程度解った時は、プログラムソースのSTOP_TIMEやHOUR24を調整してしまうという手もあります。
次回は応用編です。
#include <16F88.h> // Clock Select //#define INT8M //#define EXT10M #define EXT20M #ifdef INT8M #fuses NOWDT,INT_RC, PUT, NOMCLR, BROWNOUT, NOLVP, CCPB0, NOCPD, NOWRT, NODEBUG, NOPROTECT, FCMEN, IESO #else #fuses NOWDT,HS, PUT, NOMCLR, BROWNOUT, NOLVP, CCPB0, NOCPD, NOWRT, NODEBUG, NOPROTECT, FCMEN, IESO #endif #use fast_io(B) #use fast_io(A)3行目以下の部分、ClockSelectは、使うクロック・ソースのコメントを外します。内蔵8MHz、外部10MHz、外部20MHzの三種類を想定しており、上記の例では外部20MHzを設定しています。この部分は、後で説明する定時起動タイマの時間計算に影響します。
1行目と末行はデバイス設定に必須な記述です。
●グローバル変数
unsigned int8 led[8]; // PWM作成用バッファ(明るさ設定) unsigned int16 cond[8]; // 点灯時のカウント unsigned int16 term[8]; // 消灯時間カウント unsigned int8 scanc; // PWMカウント(32カウント) unsigned int8 timec; // ウェイト作成用カウンタ unsigned int32 nightcount; // 夜間タイマ計算カウンタ変数led、cond、termは前回の説明にありましたが、オブジェ8個分確保するために8個の配列にしています。
timecはメイン・プログラムで割り込み回数をカウントして指定数のウェイトを作成するのに使います。nightcountは一日のうち午後五時から十時までの間点灯させるための定時起動タイマ変数です。
●Timer0割り込み
#int_RTCC
RTCC_isr() // 256us(204.8us:10/20MHz) timer
{
if(timec!=0) timec--; // ウェイト・カウンタ-1
if(++scanc>=32) { // PWMカウンタ+1
scanc = 0;
}
// 各オブジェの点灯制御
if (scanc>=led[0]) output_bit(PIN_A0,0); else output_bit(PIN_A0,1);
if (scanc>=led[1]) output_bit(PIN_A1,0); else output_bit(PIN_A1,1);
if (scanc>=led[2]) output_bit(PIN_B0,0); else output_bit(PIN_B0,1);
if (scanc>=led[3]) output_bit(PIN_B1,0); else output_bit(PIN_B1,1);
if (scanc>=led[4]) output_bit(PIN_B2,0); else output_bit(PIN_B2,1);
if (scanc>=led[5]) output_bit(PIN_B3,0); else output_bit(PIN_B3,1);
if (scanc>=led[6]) output_bit(PIN_B4,0); else output_bit(PIN_B4,1);
if (scanc>=led[7]) output_bit(PIN_B5,0); else output_bit(PIN_B5,1);
}
RTCCとはPICのTimer0の特別な呼び方です。#int_TIMER0としても同じことです。この中では前半はtimec、scancのカウント、後半の8行は、scancと明るさ変数ledと比較してPWMの作成を行っています。●乱数計算ルーチン
// 8ビット・ランダム作成(ビルトイン関数rand()でも可)
unsigned int8 rand()
{
static unsigned int32 ival;
ival1 = ival1 * 214013 + 2531011;
ival1 = (ival1 >> 16);
return((int8)(ival1 & 0xff));
}
// 2.62s(256)-5.24s(511)の間の乱数に変換
int16 calc_rand()
{
int16 i;
i=(int16)rand();
i=i+256;
return i;
} term用の乱数を作成します。まず、8ビットの乱数を作成してから16ビットに展開し、512~1024の範囲に拡張します。rand()関数は某コンパイラで使われていた簡易乱数作成サブルーチンです。CCS-Cにもビルトイン乱数発生rand()が存在するので、どちらを使ってもかまいません。
●定時起動タイマ定数の宣言
#define START_TIME 0 #ifdef INT8M #define STOP_TIME 1757812L // 1/(256 * 40)us * 3600 * 5H #define HOUR24 8437500L // 1/(256 * 40)us * 3600 * 24H #else #define STOP_TIME 2197266L // 1/(204.8 * 40)us * 3600 * 5H #define HOUR24 10546875L // 1/(204.8 * 40)us * 3600 * 24H #endif五時から十時までの五時間分:STOP_TIME、24時間分:HOUR24が定義されています。単位は割り込み周期を40倍した約10mSですが、ヘッダ・ファイルで設定したとおりクロック・ソースの違いで内容が異なることになるので、それを吸収するためにクロック別に宣言します。
●メイン・ループ
// メイン・ループ
void mloop()
{
int8 i;
for(;;) {
timec = 40; // 10.24ms
if (nightcount<STOP_TIME) { // 5時~10時?
for(i=0;i<8;i++) {
cond[i]++;
if (cond[i]<128) { // 点灯中
led[i]=(int8)(32-(cond[i]>>2)); // 32段階で暗くなる
} else { // 消灯中
led[i]=0;
if (cond[i]>term[i]) {
cond[i]=0;
term[i]=calc_rand(); // 消灯終了直前に次の消灯時間を計算しておく
}
}
}
} else { // 10時から翌日5時は強制消灯
for(i=0;i<8;i++) led[i]=0;
if (nightcount > HOUR24) {
nightcount = 0;
}
}
while(timec!=0); // ゼロになるまで待つ
nightcount++;
}
}
メイン・ループ内はfor(;;)の無限ループです。1回のループは割り込み周期×40で、timec変数で作成します。ループの初めではnightcountを調べて、午後十時以降・翌日五時までならば全部のLEDを消灯させます。それ以外は、8個のオブジェを制御します。forループで0~7までを前回の説明に沿った方法で計算し、led[]に格納します。格納された値は、先の割り込みプログラムの中でPWM点灯処理します。
●メイン・プログラム
void main()
{
int i;
scanc = 0;
for(i=0;i<8;i++) cond[i]=0;
for(i=0;i<8;i++) term[i]=calc_rand();
nightcount=0;
output_a(0);
output_b(0);
setup_oscillator(OSC_8MHz); // 内蔵RC発振のみ必要
setup_adc_ports(NO_ANALOGS|VSS_VDD);
setup_adc(ADC_OFF);
setup_spi(FALSE);
set_tris_a(0x00); // RA all out
set_tris_b(0x00); // RB2:in other out
#ifndef EXT20M
setup_timer_0(RTCC_INTERNAL|RTCC_DIV_2); // 8/10MHz
#else
setup_timer_0(RTCC_INTERNAL|RTCC_DIV_4); // 20MHz only
#endif
setup_timer_1(T1_DISABLED);
setup_comparator(NC_NC_NC_NC);
setup_vref(FALSE);
enable_interrupts(INT_RTCC);
enable_interrupts(GLOBAL);
id = read_eeprom(0);
mloop();
}
変数と内蔵モジュールの初期設定を行っています。ここでもクロック・ソースによって割り込み周期の設定が変わっています。
すでに、おわりのこととは思いますが、定時起動タイマとは時間を計数しますが、時計ではありませんので、最初に電源を投入する時間で決まってしまいます。つまり五時~十時にしたければ、午後五時に電源を投入してください。時間精度は内蔵RCで数分/日、セラロックで数十秒/日、水晶発振子では数分/月です。
したがって、内蔵RCでは1週間に1回程度、決まった時間に電源を入れなおす必要が出てきます。また、ずれる割合がある程度解った時は、プログラムソースのSTOP_TIMEやHOUR24を調整してしまうという手もあります。
次回は応用編です。
<高野慶一>
(編集部注) リストの右側が切れて表示されていますが、データは入っています。全選択で本文をコピーしていただき、メモ帳などに張り込んでいただければ、リストを取り出すことができます。(2008/08/18 メインループプログラムの一部が欠けていたので修正。)
