目次
前
次
reduce circuit
ディスクリート回路を小さな基板でも
使えるように、PICを利用してICの数を
減らしてみました。
元の回路は、以下。
2個のプッシュスイッチのチャタリング除去のため
ヒステリシスインバータを使っています。
スイッチ入力の'H'を、論理和回路に入れて出力の'H'を
反転し、NE555のトリガーとします。
NE555は、規定時間だけ'H'を出力。
タイミングチャートで見ると、以下。
この回路をPIC12F1501を利用して、エミュレートする
ならば、以下でよいでしょう。
ディスクリートICを減らすのと同時に、次の部品を
使わないで済ませられます。
抵抗とキャパシタの数を減らし、抵抗3本だけに。
抵抗を減らしたので、PIC12F1501の該当ピンに
内部で、プルアップ抵抗を接続。
WPUA = 0x30 ;
初期化を考えると、次の関数でまとまるでしょう。
void setup(void)
{
/* select 2MHz */
OSCCON = (0x0d << 3) | 0x03 ;
/* I/O value */
LATA = 0x00 ;
/* I/O directions */
TRISA = 0x38 ; /* 0011 1000 */
/* pull-up */
WPUA = 0x30 ;
/* all pin are digital */
ANSELA = 0 ;
/* disable A/D converter */
ADCON0.ADON = OFF ;
ADCON2 = 0 ;
/* disable D/A converter */
DACCON0.DACEN = OFF ;
/* disable compare module */
CM1CON0.C1ON = OFF ;
CM1CON0.C1OE = OFF ;
/* initialize Timer 0 */
{
/*
2MHz/4 = 500kHz -> 500kHz/4 = 125kHz prescaler = 1:4
*/
OPTION_REG = 0x01 ;
/* 256 - 6 = 250 (2ms) */
TMR0 = CNTBEGIN ;
/* enable timer0 overflow interrupt */
INTCON.TMR0IE = ON ;
}
/* enable general interrupt */
INTCON.GIE = ON ;
/* initialize Timer 1
2MHz / 4 = 500kHz -> 500kHz / 8 = 62.5kHz
50000 / 62.5 = 800 ms
*/
{
T1CON = 0x30 ;
T1GCON = 0 ;
}
/* clear flag */
eflag = OFF ;
tflag = OFF ;
/* clear variables */
state = 0 ;
psw_sft = 0 ;
wsw_sft = 0 ;
}
すべてのI/Oピンを、デジタルで扱い
A/D変換、D/A変換、比較処理を
使わない仕様でまとめておきます。
内蔵タイマーは、タイマー0とタイマー1を使い
タイマー1は、出力の'H'パルスの持続時間を扱う
ことにして、タイマー0はスイッチのチャタリング
除去に利用。
1個のスイッチのチャタリング除去は、次の回路と
等価にしてあります。
プログラムでみれば、以下。
void get_sw(void)
{
/* skip */
if ( tflag == OFF ) return ;
/* clear interrupt flag */
tflag = OFF ;
/* shift */
psw_sft <= 1 ;
wsw_sft <= 1 ;
/* mask */
psw_sft &= MASK0F ;
wsw_sft &= MASK0F ;
/* judge logical level */
ptmp = PORTA ;
if ( ptmp.F4 == 0 ) { psw_sft |= ON ; }
if ( ptmp.F5 == 0 ) { wsw_sft |= ON ; }
/* judge */
eflag = OFF ;
if ( psw_sft == MASK07 ) { eflag = ON ; }
if ( wsw_sft == MASK07 ) { eflag = ON ; }
時間経過は、割込みを利用して通知。
割込み処理は、次の関数で実現。
void interrupt(void)
{
/* generate 1Hz */
if ( INTCON.T0IF == ON ) {
/* clear flag */
INTCON.T0IF = OFF ;
/* initialize */
TMR0 = CNTBEGIN ;
/* set flag */
tflag = ON ;
}
}
パルス出力は、フラグで通知されるので
ステートマシンで実現します。
ステートマシンは、以下のよう組んで見ました。
プログラムコードに落とすと、以下。
void perform(void)
{
switch( state ) {
/* wait trigger */
case 0 : state = 0 ;
if ( eflag == ON ) {
eflag = OFF ;
state = 1 ;
}
break ;
/* clear counter */
case 1 : state = 2 ;
TMR1H = 0 ;
TMR1L = 0 ;
break ;
/* enable timer */
case 2 : state = 3 ;
T1CON.TMR1ON = ON ;
LATA = ON ;
break ;
/* delay */
case 3 : state = 3 ;
if ( (TMR1H == VDH) && (TMR1L >= VDL) ) {
state = 4 ;
}
break ;
/* disable timer */
case 4 : state = 5 ;
T1CON.TMR1ON = OFF ;
LATA = OFF ;
break ;
/* return first state */
case 5 : state = 0 ;
break ;
/* others */
default : state = 0 ;
break ;
}
}
スイッチの状態でイベント通知する関数(get_sw)と
パルスを出力する関数(perform)を並べていけばよい
ので、ラッパー関数を用意して、考えやすくします。
void loop(void)
{
get_sw();
perform(void);
}
関数loopは、Arduinoの関数に似せて命名してます。
ここまでをまとめて、次のようにできます。
/*
internal 2MHz system clock
GP0(7 pin) pulse output
GP1(6 pin) output
GP2(5 pin) output
GP3(4 pin) reset
GP4(3 pin) push switch (negative edge)
GP5(2 pin) wireless switch (negative edge)
*/
typedef unsigned char UBYTE ;
typedef unsigned short UWORD ;
/*
#define POUT PORTA.F0
#define PSW PORTA.F4
#define WSW PORTA.F5
*/
#define OFF 0
#define ON OFF+1
#define MASK0F 0x0F
#define MASK07 0x07
#define VDH 195 // 50000 / 256
#define VDL 80 // 50000 % 256
#define CNTBEGIN 6
volatile UBYTE state ;
volatile UBYTE eflag ;
volatile UBYTE tflag ;
volatile UBYTE psw_sft ;
volatile UBYTE wsw_sft ;
volatile UBYTE ptmp ;
/* interrupt handler */
void interrupt(void)
{
/* generate 1Hz */
if ( INTCON.T0IF == ON ) {
/* clear flag */
INTCON.T0IF = OFF ;
/* initialize */
TMR0 = CNTBEGIN ;
/* set flag */
tflag = ON ;
}
}
/* function prototype */
void setup(void);
void loop(void);
void get_sw(void);
void perform(void);
void main(void)
{
/* initialize */
setup();
/* endless loop */
while (ON) {
loop();
}
}
/* define function body */
void setup(void)
{
/* select 2MHz */
OSCCON = (0x0d << 3) | 0x03 ;
/* I/O value */
LATA = 0x00 ;
/* I/O directions */
TRISA = 0x38 ; /* 0011 1000 */
/* pull-up */
WPUA = 0x30 ;
/* all pin are digital */
ANSELA = 0 ;
/* disable A/D converter */
ADCON0.ADON = OFF ;
ADCON2 = 0 ;
/* disable D/A converter */
DACCON0.DACEN = OFF ;
/* disable compare module */
CM1CON0.C1ON = OFF ;
CM1CON0.C1OE = OFF ;
/* initialize Timer 0 */
{
/*
2MHz/4 = 500kHz -> 500kHz/4 = 125kHz prescaler = 1:4
*/
OPTION_REG = 0x01 ;
/* 256 - 6 = 250 (2ms) */
TMR0 = CNTBEGIN ;
/* enable timer0 overflow interrupt */
INTCON.TMR0IE = ON ;
}
/* enable general interrupt */
INTCON.GIE = ON ;
/* initialize Timer 1
2MHz / 4 = 500kHz -> 500kHz / 8 = 62.5kHz
50000 / 62.5 = 800 ms
*/
{
T1CON = 0x30 ;
T1GCON = 0 ;
}
/* clear flag */
eflag = OFF ;
tflag = OFF ;
/* clear variables */
state = 0 ;
psw_sft = 0 ;
wsw_sft = 0 ;
}
void loop(void)
{
get_sw();
perform();
}
void get_sw(void)
{
/* skip */
if ( tflag == OFF ) return ;
/* clear interrupt flag */
tflag = OFF ;
/* shift */
psw_sft <= 1 ;
wsw_sft <= 1 ;
/* mask */
psw_sft &= MASK0F ;
wsw_sft &= MASK0F ;
/* judge logical level */
ptmp = PORTA ;
if ( ptmp.F4 == 0 ) { psw_sft |= ON ; }
if ( ptmp.F5 == 0 ) { wsw_sft |= ON ; }
/* judge */
eflag = OFF ;
if ( psw_sft == MASK07 ) { eflag = ON ; }
if ( wsw_sft == MASK07 ) { eflag = ON ; }
}
void perform(void)
{
switch( state ) {
/* wait trigger */
case 0 : state = 0 ;
if ( eflag == ON ) {
eflag = OFF ;
state = 1 ;
}
break ;
/* clear counter */
case 1 : state = 2 ;
TMR1H = 0 ;
TMR1L = 0 ;
break ;
/* enable timer */
case 2 : state = 3 ;
T1CON.TMR1ON = ON ;
LATA = ON ;
break ;
/* delay */
case 3 : state = 3 ;
if ( (TMR1H == VDH) && (TMR1L >= VDL) ) {
state = 4 ;
}
break ;
/* disable timer */
case 4 : state = 5 ;
T1CON.TMR1ON = OFF ;
LATA = OFF ;
break ;
/* return first state */
case 5 : state = 0 ;
break ;
/* others */
default : state = 0 ;
break ;
}
}
「mikroC PRO for PIC」の評価版を使い
コンパイル、リンクして動作を確認して
あります。
半田付けした基板は、以下。
目次
前
次