目次
前
次
充電用タイマー
DIPスイッチ、アナログスイッチを利用して
1時間単位で1から15時間まで計時する
タイマーを作りました。
写真では、8ビットのDIPスイッチ、アナログスイッチを
利用していますが、4ビットで充分です。
以下が、ブロック図。
手持ちのPIC12F629を使うため、次のようにピン割当。
クロックは内蔵4MHzを利用します。
タイマーは、時間を計測するのでクロックから
1kHzを生成し、内部カウンタをインクリメント
しながら、経過時間を1分単位で管理します。
システムクロックがfであっても、CPUコア、タイマーの
動作クロックはf/4になります。1命令が、4クロックで
構成されているので、高速処理には向かないと言えます。
トリガーボタンを押したときの動作シーケンスを
設定し、必要な処理を洗い出します。
- DIPスイッチの4ビットを入力
- 2進数の値から時間を分に換算
- 制御出力をHにする
- 1分ごとに通知されるフラグで分カウンタをデクリメント
- 1分カウンタが0なら、制御出力をLにする
動作シーケンスをCのコードで記述します。
/* switch handling */
if ( sflag == ON ) {
/* clear flag */
sflag = OFF ;
/* get pin state */
trg_sft <<= 1 ;
trg_sft &= MASK03 ;
if ( GPIO.B4 == OFF ) { trg_sft |= ON ; }
/* judge */
if ( trg_sft == 0x01 ) {
/* get last count */
lastcnt = 60 * get_tcnt();
/* enable */
GPIO.B5 = ON ;
}
}
/* 60 seconds pass handling */
if ( tflag == ON ) {
/* clear flag */
tflag = OFF ;
/* decrement */
lastcnt-- ;
/* judge */
if ( lastcnt == 0 ) { GPIO.B5 = OFF ; }
}
トリガーボタンは、チャタリング除去のため
シフトレジスタでエッジを捉えます。
4ビットの2進数を入力するには、専用関数
get_tcntを定義して使います。
関数get_tcntは、次のように定義しました。
UBYTE get_tcnt(void)
{
UBYTE loop ;
UBYTE result ;
UBYTE xgpio ;
/* clear */
result = 0 ;
/* loop */
for ( loop = 0 ; loop < 4 ; loop++ ) {
/* address */
xgpio = GPIO ;
xgpio &= ~MASK03 ;
xgpio |= ((3-loop) & MASK03);
GPIO = xgpio ;
/* shift */
result <<= 1 ;
/* get target bit state */
if ( GPIO.B2 == ON ) { result |= ON ; }
}
return result ;
}
タイマー割込みで、10msと6000msを生成すれば
よいので、割込みハンドラを次のように定義
します。
void interrupt(void)
{
/* generate 1ms */
if ( INTCON.T0IF == ON ) {
/* clear flag */
INTCON.T0IF = OFF ;
/* initialize */
TMR0 = 6 ;
/* increment */
timcnt++ ;
/* get lower 8 bits */
timv = timcnt & MASKFF ;
/* judge */
if ( (timv & 15) == 10 ) {
/* set flag */
sflag = ON ;
}
if ( timv == 250 ) {
/* increment */
scnt++ ;
/* past 60 seconds */
if ( scnt == 240 ) {
scnt = 0 ;
tflag = ON ;
}
}
}
}
システムタイマーカウンタを用意し、1msごとに
インクリメントします。オーバーフロー割込みで
使わなければならないので、250分周になるよう
カウンタの初期値を調整して対応します。
10msと6000msの経過は、sflag、tflagで通知します。
大まかなカラクリを考えたので、I/O関係を初期化します。
PICは電源ONで、アナログコンパレータ、A/Dコンバータが
使える仕様なので、これらを停止し単純なI/Oで利用可能
とします。
レジスタを見ると、次のようになっています。
アナログコンパレータを使わないときは、レジスタに
0x07を設定すればよいとわかります。
CMCON = 0x07 ;
利用するPICには、A/Dコンバータがないので
このモジュールの動作を停止させないでOK。
外付け部品を減らすため、入力ピンの内部に
プルアップ抵抗を接続します。
GP2、GP4にプルアップ抵抗を設定するため
関連するレジスタを調べます。
プルアップ抵抗を接続するには、該当ビットに1を
指定すればよいので、レジスタ設定は以下となります。
WPU = 0x14 ;
但し書きで、GPPUビットをイネーブル(0設定)にします。
GPPUビットは、OPTION_REGの中にありますが
電源オンで0になるので、レジスタの該当
ビットはそのままとします。
I/Oピンの入出力方向と初期値を設定するため
該当するレジスタの内容を把握します。
初期値は、GPIOレジスタに格納し、入出力方向は
TRISIOに設定します。
/* I/O state */
GPIO = 0x00 ;
/* I/O directions */
TRISIO = 0x1C ; /* bit0,1,5 as output , others as input */
タイマー0の動作を設定するため、ブロック図をみます。
タイマー0のプリスケーラには、4MHzの1/4である1MHzが
入り、プリスケーラの出力で内蔵タイマーインクリメント
が発生します。また、割込みは、オーバーフローになると
記されています。
1kHzをタイマーから出力されるクロックだとすると
250分周すればよいことに。
さらに、WatchDogTimerを使わないでタイマー利用
とする設定が必要となります。
割込みを、オーバーフローで利用するとなれば、カウンタ
タイマーには、250を設定したのと等価になるように、6を
代入します。
初期化は、次のように値を設定しました。
/*
4MHz/4 = 1MHz -> 1MHz/4 = 250kHz prescaler = 1:4
all inputs are pull-up .
*/
OPTION_REG = 0x01 ;
/* 256 - 250 = 6 */
TMR0 = 6 ;
/* enable timer0 overflow interrupt */
INTCON.T0IE = ON ;
PICでは、全体割込みをイネーブルにしないと個々の
割込みが使えないので、次の一文が必要に。
INTCON.GIE = ON ;
ソースコードにまとめます。
/* redefine data type */
typedef unsigned char UBYTE ;
typedef unsigned int UWORD ;
typedef unsigned long ULONG ;
#define OFF 0
#define ON OFF+1
#define MASKFF 0xff
#define MASK30 0x30
#define MASK0F 0x0f
#define MASK03 0x03
volatile UBYTE tflag ;
volatile UBYTE sflag ;
volatile ULONG timcnt ;
volatile UBYTE timv ;
volatile UBYTE scnt ;
volatile UWORD lastcnt ;
volatile UBYTE trg_sft ;
/* function prototype */
void init_usr(void);
UBYTE get_tcnt(void);
/* interrupt handler */
void interrupt(void) {
/* generate 1ms */
if ( INTCON.T0IF == ON ) {
/* clear flag */
INTCON.T0IF = OFF ;
/* initialize */
TMR0 = 6 ;
/* increment */
timcnt++ ;
/* get lower 8 bits */
timv = timcnt & MASKFF ;
/* judge */
if ( (timv & 15) == 10 ) {
/* set flag */
sflag = ON ;
}
if ( timv == 250 ) {
/* increment */
scnt++ ;
/* past 60 seconds */
if ( scnt == 240 ) {
scnt = 0 ;
tflag = ON ;
}
}
}
}
void main(void) {
/* user initialize */
init_usr();
/* endless loop */
while ( ON ) {
/* switch handling */
if ( sflag == ON ) {
/* clear flag */
sflag = OFF ;
/* get pin state */
trg_sft <<= 1 ;
trg_sft &= MASK03 ;
if ( GPIO.B4 == OFF ) { trg_sft |= ON ; }
/* judge */
if ( trg_sft == 0x01 ) {
/* get last count */
lastcnt = 60 * get_tcnt();
/* enable */
GPIO.B5 = ON ;
}
}
/* 60 seconds pass handling */
if ( tflag == ON ) {
/* clear flag */
tflag = OFF ;
/* decrement */
lastcnt-- ;
/* judge */
if ( lastcnt == 0 ) { GPIO.B5 = OFF ; }
}
}
}
/* define function body */
void init_usr(void)
{
/* I/O state */
GPIO = 0x00 ;
/* I/O directions */
TRISIO = 0x1C ; /* bit0,1,5 as output , others as input */
/* disable compare module */
CMCON = 0x07 ;
/* pull-up */
WPU = 0x14 ;
/* initialize Timer 0 */
{
trg_sft = 0 ;
/*
4MHz/4 = 1MHz -> 1MHz/4 = 250kHz prescaler = 1:4
all inputs are pull-up .
*/
OPTION_REG = 0x01 ;
/* 256 - 250 = 6 */
TMR0 = 6 ;
/* enable timer0 overflow interrupt */
INTCON.T0IE = ON ;
}
/* enable general interrupt */
INTCON.GIE = ON ;
/* clear flags */
tflag = OFF ;
sflag = OFF ;
/* clear */
timcnt = 0 ;
scnt = 0 ;
lastcnt = 10 ;
}
UBYTE get_tcnt(void)
{
UBYTE loop ;
UBYTE result ;
UBYTE xgpio ;
/* clear */
result = 0 ;
/* loop */
for ( loop = 0 ; loop < 4 ; loop++ ) {
/* address */
xgpio = GPIO ;
xgpio &= ~MASK03 ;
xgpio |= ((3-loop) & MASK03);
GPIO = xgpio ;
/* shift */
result <<= 1 ;
/* get target bit state */
if ( GPIO.B2 == ON ) { result |= ON ; }
}
return result ;
}
回路図は、以下です。
目次
前
次