目次
前
次
7セグメントLED表示器(割込み活用)
7セグメントLEDは表示器なので、次のように用途を
考えることができます。
- 時計
- 入場者退場者カウンタ
- 駐車場にある車両台数表示
これらは、7セグメントLED表示器を複数利用する
ことになるので、各桁の値をダイナミック点灯して
対応します。
4桁の7セグメントLED表示器があるとすれば、次の
シーケンスを繰返します。
- 0けた目の表示パターン出力
- 0けた目をイネーブル、他はディセーブル
- 1けた目の表示パターン出力
- 1けた目をイネーブル、他はディセーブル
- 2けた目の表示パターン出力
- 2けた目をイネーブル、他はディセーブル
- 3けた目の表示パターン出力
- 3けた目をイネーブル、他はディセーブル
- 1に戻る
表示のタイミングは、タイマー割込みで通知します。
if ( LTRG == ON ) {
/* clear flag */
LTRG = OFF ;
/* get target digit */
tmp = *(digit+state);
/* convert bit pattern */
ltmp = conv7segment( tmp );
/* impress */
PORTB = ltmp ;
/* enable target digit */
PORTA = state & 0x03 ;
/* update state */
state++ ;
/* judge */
if ( state == 4 ) { state = 0 ; }
}
タイマー割込みは、20msより小さい周期を利用します。
20ms程度にするとチラツキが出るので、20msを桁数で
割った周期を利用します。ここでは1msとします。
7セグメントLED表示器のダイナミック点灯では、ポートの
ピン数を減らすために、デコーダICを利用した方が楽になり
ます。
4511のような消費電力が少ないCMOSタイプを使います。
デコードICを利用することで、数字をビットパターンに
変換する処理を省くことができます。
if ( LTRG == ON ) {
/* clear flag */
LTRG = OFF ;
/* get target digit */
tmp = *(digit+state);
/* clear lower nibble */
PORTB &= 0xf0 ;
/* impress */
PORTB |= tmp ;
/* enable target digit */
PORTA = state & 0x03 ;
/* update state */
state++ ;
/* judge */
if ( state == 4 ) { state = 0 ; }
}
ポートの中で、使わなくなったビットをスイッチや
通信に振り分けられます。
プッシュスイッチでは、チャタリングを除去する処理が
必要になります。チャタリング除去にはタイマー割込み
とシフトレジスタを利用すれば簡単になります。
チャタリング除去は、次のコードで対応できます。
if ( TFLAG == ON ) {
/* clear flag */
TFLAG = OFF ;
/* get current switch state */
tmp = PORTB ;
/* shift */
sreg0 <<= 1 ;
sreg1 <<= 1 ;
sreg2 <<= 1 ;
sreg3 <<= 1 ;
/* mask */
sreg0 &= 0x03 ;
sreg1 &= 0x03 ;
sreg2 &= 0x03 ;
sreg3 &= 0x03 ;
/* update LSB */
if ( tmp & 0x10 ) { sreg0 |= 1 ; }
if ( tmp & 0x20 ) { sreg1 |= 1 ; }
if ( tmp & 0x40 ) { sreg2 |= 1 ; }
if ( tmp & 0x80 ) { sreg3 |= 1 ; }
/* judge */
if ( sreg0 == ON ) { SFLAG0 = ON ; }
if ( sreg1 == ON ) { SFLAG1 = ON ; }
if ( sreg2 == ON ) { SFLAG2 = ON ; }
if ( sreg3 == ON ) { SFLAG3 = ON ; }
}
4つのシフトレジスタを用意し、チャタリング除去
しつつ0→1の変化をとらえます。変化があった時
各通知フラグを設定して、通知フラグを待っている
処理に操作を一任します。
対応処理を記述すると、いろいろなことができます。
if ( SFLAG0 == ON ) {
/* clear flag */
SFLAG0 = OFF ;
/* perform */
}
if ( SFLAG1 == ON ) {
/* clear flag */
SFLAG1 = OFF ;
/* perform */
}
if ( SFLAG2 == ON ) {
/* clear flag */
SFLAG2 = OFF ;
/* perform */
}
if ( SFLAG3 == ON ) {
/* clear flag */
SFLAG3 = OFF ;
/* perform */
}
割込みを使うと、イベントに対応した処理を簡単に
記述できるようになります。
Windowsアプリケーションは、割込みによるイベント通知を
OSが担当するので、イベントに対応した処理だけを記述して
いきます。ファームウエアでWindowsアプリケーションに近い
プログラムを作成するとき、割込みをどう扱うのかをユーザ
が指定し、そのコードを作成します。
娯楽施設での入場者、退場者を表示するアプリケーションを
作成してみます。入場者は最大999人とします。
仕様を決めていきます。
表示
6桁の7セグメントLEDの上位3桁を入場者数
下位3桁を退場者数にします。
ハードウエアが決まれば、内部処理はある程度は確定します。
word in_passengers ;
word out_passengers ;
if ( LTRG == ON ) {
/* clear flag */
LTRG = OFF ;
/* in passengers */
*(digit+0) = in_passengers / 100 ;
*(digit+1) = (in_passengers % 100) / 10 ;
*(digit+2) = in_passengers % 10 ;
/* out passengers */
*(digit+2) = out_passengers / 100 ;
*(digit+4) = (out_passengers % 100) / 10 ;
*(digit+5) = out_passengers % 10 ;
/* get target digit */
tmp = *(digit+state);
/* clear nibble */
PORTB = 0x00 ;
/* impress */
PORTB |= tmp ;
/* enable target digit */
if ( state < 5) {
PORTA = 0 ;
PORTA |= (1 << state & 0x07) ;
PORTB &= ~(1 << 4);
} else {
PORTB |= (1 << 4);
}
/* update state */
state++ ;
/* judge */
if ( state == 6 ) { state = 0 ; }
}
入場者、退場者カウント表示
入口、出口のマット下に、マイクロスイッチを入れて
1→0の変化で人が通過したと判断します。
チャタリングを除去しながら、入退場者の数を更新すれば
よいので、次のコードで対応できます。
if ( TFLAG == ON ) {
/* clear flag */
TFLAG = OFF ;
/* get current switch state */
tmp = PORTB ;
/* shift */
sreg0 <<= 1 ;
sreg1 <<= 1 ;
/* mask */
sreg0 &= 0x03 ;
sreg1 &= 0x03 ;
/* update LSB */
if ( tmp & 0x40 ) { sreg0 |= 1 ; }
if ( tmp & 0x80 ) { sreg1 |= 1 ; }
/* judge */
if ( sreg0 == ON ) { SFLAG0 = ON ; }
if ( sreg1 == ON ) { SFLAG1 = ON ; }
}
/* update out passengers */
if ( SFLAG1 == ON ) {
/* clear flag */
SFLAG1 = OFF ;
/* increment */
out_passengers++ ;
}
/* update in passengers */
if ( SFLAG0 == ON ) {
/* clear flag */
SFLAG0 = OFF ;
/* increment */
in_passengers++ ;
}
1日の入場者、退場者を逐次集計して、表示する場合の
ファームウエアは、以下。
#define OFF 0
#define ON OFF+1
#define STARTV 3036
#define XDIV 10
#define XXDIV 100
typedef unsigned char UBYTE ;
typedef unsigned short UWORD ;
typedef union {
struct {
unsigned B7:1;
unsigned B6:1;
unsigned B5:1;
unsigned B4:1;
unsigned B3:1;
unsigned B2:1;
unsigned B1:1;
unsigned B0:1;
} BIT ;
unsigned char DR ;
} FLAGSP ;
volatile FLAGSP xflags ;
#define LTRG xflags.BIT.B0
#define TFLAG xflags.BIT.B1
#define SFLAG0 xflags.BIT.B2
#define SFLAG1 xflags.BIT.B3
volatile UWORD xcnt ;
volatile UBYTE sreg0 ;
volatile UBYTE sreg1 ;
volatile UWORD in_passengers ;
volatile UWORD out_passengers ;
volatile UBYTE state ;
volatile UBYTE digit[6] ;
void interrupt(void)
{
/* timer0 overflow interrupt */
if ( INTCON.T0IF == ON ) {
/* clear flag */
INTCON.T0IF = OFF ;
/* initialize */
TMR0 = 6 ;
/* set flag */
TFLAG = ON ;
}
/* timer1 overflow interrupt */
if ( PIR1.TMR1IF == ON ) {
/* clear flag */
PIR1.TMR1IF = OFF ;
/* initialize */
TMR1L = STARTV % 256 ;
TMR1H = STARTV / 256 ;
/* set flag */
LTRG = ON ;
}
}
/* prototype */
void usr_init(void);
void main(void)
{
UBYTE tmp ;
/* initialize */
usr_init();
/* endless loop */
while (ON) {
if ( TFLAG == ON ) {
/* clear flag */
TFLAG = OFF ;
/* get current switch state */
tmp = PORTB ;
/* shift */
sreg0 <<= 1 ;
sreg1 <<= 1 ;
/* mask */
sreg0 &= 0x03 ;
sreg1 &= 0x03 ;
/* update LSB */
if ( tmp & 0x40 ) { sreg0 |= 1 ; }
if ( tmp & 0x80 ) { sreg1 |= 1 ; }
/* judge */
if ( sreg0 == ON ) { SFLAG0 = ON ; }
if ( sreg1 == ON ) { SFLAG1 = ON ; }
}
/* update out passengers */
if ( SFLAG1 == ON ) {
/* clear flag */
SFLAG1 = OFF ;
/* increment */
out_passengers++ ;
/* calculate counter */
*(digit+2) = out_passengers / XXDIV ;
*(digit+4) = (out_passengers % XXDIV) / XDIV ;
*(digit+5) = out_passengers % XDIV ;
}
/* update in passengers */
if ( SFLAG0 == ON ) {
/* clear flag */
SFLAG0 = OFF ;
/* increment */
in_passengers++ ;
/* calculate counter */
*(digit+0) = in_passengers / XXDIV ;
*(digit+1) = (in_passengers % XXDIV) / XDIV ;
*(digit+2) = in_passengers % XDIV ;
}
if ( LTRG == ON ) {
/* clear flag */
LTRG = OFF ;
/* get target digit */
tmp = *(digit+state);
/* clear nibble */
PORTB = 0x00 ;
/* impress */
PORTB |= tmp ;
/* enable target digit */
if ( state < 5) {
PORTA = 0 ;
PORTA |= (1 << state & 0x07) ;
PORTB &= ~(1 << 4);
} else {
PORTB |= (1 << 4);
}
/* update state */
state++ ;
/* judge */
if ( state == 6 ) { state = 0 ; }
}
}
}
void usr_init(void)
{
/* port values */
PORTA = 0x00 ;
PORTB = 0x00 ;
/* port directions */
TRISA = 0xff ;
TRISB = 0x3f ;
/* clear flags */
xflags.DR = 0 ;
/* disable analog comparator */
CMCON = 0x07 ;
/* initialize timer0 */
{
/*
10MHz/4 = 2.5MHz -> 2.5MHz/16 = 156250Hz prescaler = 1:16
156250Hz / 250 = 625 Hz
*/
OPTION_REG = 0x01 ;
/* 256 - 250 = 6 */
TMR0 = 6 ;
}
/* initialize Timer 1 */
{
/*
10MHz/4 = 2.5MHz -> 2.5MHz/8 = 312.5kHz prescaler = 1:8
*/
T1CON = (3 << 4) | 1 ;
/*
65536 - 62500
*/
TMR1L = STARTV % 256 ;
TMR1H = STARTV / 256 ;
/* enable timer1 overflow interrupt */
PIE1.TMR1IE = ON ;
}
/* enable global interrupt */
INTCON.GIE = ON ;
/* clear counter */
TMR1H = 0 ;
TMR1L = 0 ;
/* others */
state = 0 ;
in_passengers = 0 ;
out_passengers = 0 ;
}
生産現場では、材料入庫と製品出庫の逐次集計に使えます。
目次
前
次