2009年7月12日日曜日

10進カウンタの試作


錠剤カウンタで使用する10進カウンタを、16F84Aで作ってみました。


友人から教えていただいた、7セグメントLEDをダイナミック点灯する方式の回路をそのまま流用することにし、入力にシュミットトリガを持つINT線の割込を使いたかったので、プログラムの部分は作りました。

RA0~3でPNPトランジスタをスイッチすることでセグメントを選択し、RB1-7で各セグメントa-gを点灯させます。RB0はINTの入力とし、信号の立ち上がりでカウンタをインクリメントします。74138あたりのデコーダを用いれば桁数はもっと増やせそうな気がします。

以下、プログラムの内容です。

;
; for 16F84A Decimal Counter
;
; Ver 0.01 6, Jul 2009
; Ver 0.02 10, Jul 2009 カウンタ各桁の表示はループを使わず展開して行う
;
;
;
;


LIST P=16F84A

INCLUDE P16F84A.INC
ERRORLEVEL -302,-306

;
; configration bit
;
__CONFIG _HS_OSC & _WDT_OFF & _PWRTE_ON & _CP_OFF

__IDLOCS H'FFFF'


;*********************************************************
; Label definition
;*********************************************************

;RAM領域のマッピング(0x20-0x4f)

VARIABLES equ 0x20 ;プログラム中で使用するレジスタ群
COUNT_DATA equ 0x30 ;BCDカウンタ 表示用RAM領域
SEG_DATA equ 0x40 ;7セグメント駆動用パターン領域


;-----------------------------------
; プログラムで使用する変数領域の確保
;-----------------------------------

cblock VARIABLES

;Disp_Seg ;
t1mc ; 1ms確保するサブルーチン内でのカウンタ

w_save ; INT 開始に伴うメインルーチンのWレジスタ値保管領域
s_save ; INT 開始に伴うメインルーチンのStatusレジスタ値保管領域

endc

;-----------------------------------
; 表示用10進数の保管領域(BCD値)の確保
;-----------------------------------

cblock COUNT_DATA

DEG_100 ;1の位
DEG_101 ;10の位
DEG_102 ;100の位
DEG_103 ;1000の位

endc

;-----------------------------------
; 7セグメント表示データの保管領域
;-----------------------------------

; 7セグの表示はテーブルを作成し、
; 先頭アドレス(SEG_DATA)+indexで読む
;
; SEG_DATA
; ↓
; 0123456789

; Anode Common にて駆動する
; 0:点灯 1:消灯

; b'gfedcba-'
SEG_0 equ b'10000001'
SEG_1 equ b'11110011'
SEG_2 equ b'01001001'
SEG_3 equ b'01100001'
SEG_4 equ b'00110011'
SEG_5 equ b'00100101'
SEG_6 equ b'00000101'
SEG_7 equ b'10110001'
SEG_8 equ b'00000001'
SEG_9 equ b'00100001'


; 16F84にはEEPROMが無いため、RAM領域へ作成
; Initial Process にて、LW→seg_xへ格納される

cblock SEG_DATA

seg_0
seg_1
seg_2
seg_3
seg_4
seg_5
seg_6
seg_7
seg_8
seg_9

endc

;*********************************************************
; EEPROM Data definition
;*********************************************************


; 16F84にはEEPROMは存在しない



;*********************************************************
; Program Start
;*********************************************************

ORG 0 ;Reset Vector
GOTO INIT
ORG 4 ;Interrupt Vector
GOTO INT

;*********************************************************
; Initial Process
;*********************************************************

INIT
;WPUを無効
bsf STATUS,RP0 ;change to bank 1
movlw b'11010111' ;PU OFF, PosEDG, INTCLK, NegEDG, TMR0, 1/256
movwf OPTION_REG ;set option registor

;Port の 設定
;
;Port A
;RA0 OUT: LED 1の位 選択
;RA1 OUT: LED 10の位 選択
;RA2 OUT: LED 100の位 選択
;RA3 OUT: LED 1000の位 選択
;RA4 IN: Counter Reset

bsf STATUS,RP0
movlw b'00010000'
movwf TRISA

;Port B
;RB0 IN: Counter Input(INT 立ち下がり)
;RB1 OUT: Seg A 選択
;RB2 OUT: Seg B 選択
;RB3 OUT: Seg C 選択
;RB4 OUT: Seg D 選択
;RB5 OUT: Seg E 選択
;RB6 OUT: Seg F 選択
;RB7 OUT: Seg G 選択

bsf STATUS,RP0
movlw b'00000001'
movwf TRISB

;Bank 0 に戻す
bcf STATUS,RP0

;GPIO 初期化
clrf PORTA
clrf PORTB


; test

movlw DEG_100
movlw seg_0


;7セグメントの表示パターンをRAM領域へ展開

movlw SEG_0
movwf seg_0
movlw SEG_1
movwf seg_1
movlw SEG_2
movwf seg_2
movlw SEG_3
movwf seg_3
movlw SEG_4
movwf seg_4
movlw SEG_5
movwf seg_5
movlw SEG_6
movwf seg_6
movlw SEG_7
movwf seg_7
movlw SEG_8
movwf seg_8
movlw SEG_9
movwf seg_9

;各桁を0にリセット
clrf DEG_100
clrf DEG_101
clrf DEG_102
clrf DEG_103


;割り込みの設定

bcf STATUS,RP0
movlw b'10010000' ;GIE ON, EEPROM OFF, TMR0 OFF, INTE ON, RBIE OFF, IF CLR
movwf INTCON

;Bank 0 に戻す
bcf STATUS,RP0



;*********************************************************
; Main Routine
;*********************************************************

MAIN
;-----------------------------------
; 数値表示部
;-----------------------------------

;千の位のセグメントを表示
movlw b'00001110' ; 千の位のA1015をONにする
movwf PORTA ; A1015 ON

movfw COUNT_DATA+3; W に 千の位の値をロード
addlw SEG_DATA ; W に 点灯パターンの先頭値にIndex値を加える
movwf FSR ; テーブルのアドレスをセット
movfw INDF ; テーブルの数値を得る
movwf PORTB ; 点灯パターンをPORT B に出力

call T1ms ;1msの表示ウェイト

;百の位のセグメントを表示
movlw b'00001101' ; 百の位のA1015をONにする
movwf PORTA ; A1015 ON

movfw COUNT_DATA+2; W に 百の位の値をロード
addlw SEG_DATA ; W に 点灯パターンの先頭値にIndex値を加える
movwf FSR ; テーブルのアドレスをセット
movfw INDF ; テーブルの数値を得る
movwf PORTB ; 点灯パターンをPORT B に出力

call T1ms ;1msの表示ウェイト

;十の位のセグメントを表示
movlw b'00001011' ; 十の位のA1015をONにする
movwf PORTA ; A1015 ON

movfw COUNT_DATA+1; W に 十の位の値をロード
addlw SEG_DATA ; W に 点灯パターンの先頭値にIndex値を加える
movwf FSR ; テーブルのアドレスをセット
movfw INDF ; テーブルの数値を得る
movwf PORTB ; 点灯パターンをPORT B に出力

call T1ms ;1msの表示ウェイト

;一の位のセグメントを表示
movlw b'00000111' ; 一の位のA1015をONにする
movwf PORTA ; A1015 ON

movfw COUNT_DATA+0; W に 一の位の値をロード
addlw SEG_DATA ; W に 点灯パターンの先頭値にIndex値を加える
movwf FSR ; テーブルのアドレスをセット
movfw INDF ; テーブルの数値を得る
movwf PORTB ; 点灯パターンをPORT B に出力

call T1ms ;1msの表示ウェイト

;-----------------------------------
; リセットスイッチ判定
;-----------------------------------

btfss PORTA, 4 ; RA4が1なら次の命令をスキップ
goto MAIN ; そうでないならMAINの継続
goto INIT ; 初期化へ


;*********************************************************
; 1ms wait routine
;*********************************************************

T1ms ;1000命令こなす Routine
movlw .249 ; 1
movwf t1mc ; 1
t1mlp nop ; 1*249
decfsz t1mc,f ; 1*249 + 1(skip)
goto t1mlp ; 2*248
nop ; 1
return ; 2 sum 1000

;*********************************************************
; Interrupt Routine
;*********************************************************

;-----------------------------------
; レジスタ情報保存 と 割り込み分岐
;-----------------------------------
INT
movwf w_save ;Wレジスタを保存
movfw STATUS
movwf s_save ;STATUSレジスタを保存
bcf STATUS,RP0
btfss INTCON,INTF ;GPIFの変化なら次の命令をスキップ
goto INIT ;不正終了は初期化へ

;-----------------------------------
; カウンタのインクリメント
;-----------------------------------

; カウンタは1ずつ増加

intmain
; 1の位の処理
incf DEG_100 ;1の位を+1
movfw DEG_100 ;比較のためワークレジスタへコピー
xorlw .10 ;10になったらゼロフラグ=1
btfss STATUS,Z ;ゼロフラグが0なら次の命令をスキップ
goto intend ;メインルーチンへ復帰するプロセスへ

; 10の位の処理
clrf DEG_100 ;1の位を0にする
incf DEG_101 ;10の位を+1
movfw DEG_101 ;比較のためワークレジスタへコピー
xorlw .10 ;10になったらゼロフラグ=1
btfss STATUS,Z ;ゼロフラグが0なら次の命令をスキップ
goto intend ;メインルーチンへ復帰するプロセスへ

; 100の位の処理
clrf DEG_100 ;1の位を0にする
clrf DEG_101 ;10の位を0にする
incf DEG_102 ;100の位を+1
movfw DEG_102 ;比較のためワークレジスタへコピー
xorlw .10 ;10になったらゼロフラグ=1
btfss STATUS,Z ;ゼロフラグが0なら次の命令をスキップ
goto intend ;メインルーチンへ復帰するプロセスへ

; 1000の位の処理
clrf DEG_100 ;1の位を0にする
clrf DEG_101 ;10の位を0にする
clrf DEG_102 ;100の位を0にする
incf DEG_103 ;1000の位を+1
movfw DEG_103 ;比較のためワークレジスタへコピー
xorlw .10 ;10になったらゼロフラグ=1
btfss STATUS,Z ;ゼロフラグが0なら次の命令をスキップ
goto intend ;メインルーチンへ復帰するプロセスへ

; 9999を超えた場合の処理
clrf DEG_100 ;1の位を0にする
clrf DEG_101 ;10の位を0にする
clrf DEG_102 ;100の位を0にする
clrf DEG_103 ;100の位を0にする

intend
bcf STATUS,RP0
bcf INTCON,INTF ;GPIFクリア
movfw s_save
movwf STATUS ;STATUSレジスタ復帰
swapf w_save,f ;Wレジスタの復帰
swapf w_save,w ;フラグを変化させないで戻す
retfie ;INT routineの終了

END


メモリにも余裕がありますし、わざと冗長に作ってあります。あとで改造したりするときに読みやすい方が好ましいからです。
MAIN部は永遠に10進数を保管しているレジスタを表示し続けるだけで、割込が入ったときに、そのレジスタをインクリする処理にしています。プログラムで立ち上がり/立ち下がりを検出するのは面倒だと思ったからです。

PICを焼き、可変周波数発生回路から信号となるパルスをいれてみました。同じ信号を以前に作成したステッピングモータドライバにも入れてみたので、回ります。



このカウンタ、FFFFまでカウントできる16進カウンタにもすぐに改造出来ると思うので、4桁表示のままでも60000回越えのカウントができますね。

0 件のコメント: