//此程序實現計時秒表功能,時鐘顯示范圍00.00~99.99秒,分辨度:0.01秒
#include "p18f458.h"
unsigned char s[4]; //定義0.01 秒、0.1 秒、1秒、10秒計時器
unsigned char k,data,sreg;
unsigned int i;
const table[11]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0XD8,0x80,0x90};
//不帶小數點的顯示段碼表
const table0[10]={0X40,0X79,0X24,0X30,0X19,0X12,0X02,0X78,0X00,0X10};
//帶小數點的顯示段碼表
void clkint(void);
//TMR0初始化子程序
void tmint()
{
T0CON=0XCF; //設定TMR0L工作于8位定時器方式
//內部時鐘,TMR0不用分頻
INTCON=0X20; //總中斷禁止,TMR0中斷允許,清除TMR0的中斷標志
INTCON2bits.TMR0IP=1; //TMR0中斷高優先級
RCONbits.IPEN=1; //使能中斷優先級
}
//系統其它部分初始化子程序
void initial()
{
TRISA=0x00; //A口設置為輸出
TRISB=0XF0; //RB1輸出,RB4輸入
TRISC=0x00; //SDO引腳為輸出,SCK引腳為輸出
TRISE=0x00; //E口設置為輸出
SSPCON1=0x30; //SSPEN=1;CKP=1,FOSC/4
SSPSTAT=0xC0; //時鐘下降沿發送數據
PIR1=0; //清除SSPIF標志
data=0X00; //待顯示的寄存器賦初值
PORTBbits.RB1=0;
PORTAbits.RA3=0;
PORTE=0; //將K1,K2,K3,K4四條列線置0
}
//SPI傳輸數據子程序
void SPILED(char data)
{
SSPBUF=data; //啟動發送
do
{
;
}while(PIR1bits.SSPIF==0);
PIR1bits.SSPIF=0;
}
//顯示子程序,顯示4位數
void dispaly()
{
PORTAbits.RA5=0; //準備鎖存
for(k=0;k<4;k++)
{
data=s[k];
if(k==2) data=table0[data]; //個位需要顯示小數點
else data=table[data];
SPILED(data); //發送顯示段碼
}
for(k=0;k<4;k++)
{
data=0xFF;
SPILED(data); //連續發送4個DARK,使顯示好看一些
}
PORTAbits.RA5=1; //最后給鎖存信號,代表顯示任務完成
}
//軟件延時子程序
void DELAY()
{
for(i = 3553; --i ;)
continue;
}
//鍵掃描子程序
void KEYSCAN()
{
while(1)
{
dispaly(); //調用一次顯示子程序
while(PORTBbits.RB4==0)
{
DELAY(); //若有鍵按下,則軟件延時
break;
}
if (PORTBbits.RB4==0) break; //若還有鍵按下,則終止循環掃描,返回
}
}
//等鍵松開子程序
void keyrelax()
{
while(1)
{
dispaly(); //調用一次顯示子程序
if (PORTBbits.RB4==1) break; //為防止按鍵過于靈敏,每次等鍵松開才返回
}
}
/*高優先級中斷向量*/
#pragma code InterruptVectorHigh=0x08
void InterruptVectorHigh (void)
{
_asm
goto clkint //跳到中斷程序
_endasm
}
//中斷服務程序
#pragma code
#pragma interrupt clkint
void clkint()
{
TMR0=0X13; //對TMR0寫入一個調整值。因為寫入TMR0后接著的
//兩個周期不能增量,中斷需要3個周期的響應時間,
//以及C語言自動進行現場保護要消耗周期
INTCONbits.T0IF=0; //清除中斷標志
sreg=sreg+1; //中斷計數器加1
if(sreg==40) //中斷次數為40后,才對S0,S1,S2,S3 操作
{
sreg=0;
s[0]=s[0]+1;
if(s[0]==10)
{
s[0]=0 ;
s[1]=s[1]+1;
if(s[1]==10)
{
s[1]=0 ;
s[2]=s[2]+1;
if(s[2]==10)
{
s[2]=0;
s[3]=s[3]+1;
if(s[3]==10) s[3]=0;
}
}
}
}
}
//主程序
//http://www.zg4o1577.cn 提供此單片機程序有問題請聯系
main()
{
tmint(); //TMR0初始化
initial(); //spi顯示初始化及系統其它部分初始化
INTCONbits.GIE=0; //總中斷禁止
while(1)
{
for(k=0;k<4;k++)
{
s[k]=0;
}
sreg=0; //賦顯示初值
dispaly(); //調用一次顯示子程序
KEYSCAN(); //鍵掃描,直到開始鍵按下
keyrelax(); //等鍵松開
INTCONbits.GIE=1; //總中斷允許
KEYSCAN(); //鍵掃描直到停止鍵按下,在鍵掃描時有顯示
keyrelax(); //等鍵松開
INTCONbits.GIE=0; //總中斷禁止
KEYSCAN(); //鍵掃描到清0鍵按下,在鍵掃描時有顯示
keyrelax(); //等鍵松開
}
}
---------------------匯編語言版本的pic單片機計時秒表程序設計--------------------------------------;此程序實現計時秒表功能
;時鐘顯示范圍:00.00秒-99.99秒,分辨度:0.01秒
;通過按鍵來啟動計時、停止計時及清0
;即第一次按下任意鍵開始計時,第二次按下任意鍵停止計時 ;第三次按下任意鍵使LED清0,等待下一次計時開始
LIST P=18F458
INCLUDE "P18F458.INC"
S0 EQU 0X20 ;0.01 秒計時器
S1 EQU S0+1 ;0.1 秒計時器
S2 EQU S0+2 ;1 秒計時器
S3 EQU S0+3 ;10 秒計時器
SREG EQU S0+4 ;軟計數器
LEDF EQU S0+5 ;顯示的LED的位置指示寄存器
XW_TEMP EQU S0+6 ;用于中斷中保護W的值
XHOSTS EQU S0+7 ;用于中斷中保護STATUS的值
DEYH EQU S0+8
DEYL EQU S0+9 ;以上兩個寄存器用于軟件延時
HOSTF EQU S0+0A ;用于中斷中保護FSR0L的值
TEMP EQU S0+0B ;用于存放臨時值
ORG 0X00
GOTO MAIN
ORG 0X08
GOTO CLKINT ;轉向中斷服務程序
ORG 0X30
;數字到段碼的轉換子程序(最好放在程序開頭,避免超過2K的空間發生跨頁讀表)
;************不帶小數點的共陽極碼表**************
CONVERT ADDWF PCL,1
RETLW 0XC0 ;0,顯示段碼與具體的硬件連接有關
RETLW 0XF9 ;1
RETLW 0XA4 ;2
RETLW 0XB0 ;3
RETLW 0X99 ;4
RETLW 0X92 ;5
RETLW 0X82 ;6
RETLW 0XD8 ;7
RETLW 0X80 ;8
RETLW 0X90 ;9
RETLW 0X88 ;A
RETLW 0X83 ;B
RETLW 0XC6 ;C
RETLW 0XA1 ;D
RETLW 0X86 ;E
RETLW 0X8E ;F
RETLW 0X7F ;"."
RETLW 0XBF ;"-"
RETLW 0X89 ;H
RETLW 0XFF ;DARK
RETURN
;*********帶小數點的共陽極碼表***********
CONVERT2 ADDWF PCL,1
RETLW 0X40 ;0
RETLW 0X79 ;1
RETLW 0X24 ;2
RETLW 0X30 ;3
RETLW 0X19 ;4
RETLW 0X12 ;5
RETLW 0X02 ;6
RETLW 0X78 ;7
RETLW 0X00 ;8
RETLW 0X10 ;9
RETURN
;***********SPI發送顯示子模塊************
TRANSMIT
CLRF PORTA ;LACK送低電平,為鎖存做準備
MOVWF SSPBUF ;啟動發送
WAIT NOP
BTFSS PIR1,SSPIF
GOTO WAIT ;等待發送結束
BCF PIR1,SSPIF ;清除中斷標志
RETURN
;***********顯示子程序模塊************
DISPLAY
MOVLW 0X01
MOVWF LEDF
MOVLW 0XA0
MOVWF FSR0L
AGAINXIAN
MOVF LEDF,W
SUBLW 0X03
BTFSS STATUS,Z
GOTO XIANB
MOVF INDF0,W
CALL CONVERT2 ;若是個位,則查帶小數點的碼表
GOTO TRAN
XIANB MOVF INDF0,W
CALL CONVERT
TRAN CALL TRANSMIT ;發送一個顯示數據
INCF FSR0L
INCF LEDF
MOVF LEDF,W
SUBLW 0X05
BTFSS STATUS,Z
GOTO AGAINXIAN
MOVLW 0X01
MOVWF LEDF
XIANDARK
MOVLW 0X27
CALL CONVERT
CALL TRANSMIT
INCF LEDF
MOVF LEDF,W
SUBLW 0X05
BTFSS STATUS,Z
GOTO XIANDARK ;顯示4個"DARK"
BSF PORTA,5 ;最后給一個鎖存信號,代表一次顯示任務完成
RETURN
;********** S0計數溢出處理子程序***************
CS0
CLRF S0
INCF S1
RETURN
;********** S1計數溢出處理子程序***************
CS1
CLRF S1
INCF S2
RETURN
;********** S2計數溢出處理子程序***************
CS2
CLRF S2
INCF S3
RETURN
;********** S3計數溢出處理子程序***************
CS3
CLRF S3
RETURN
;***********時鐘中斷服務程序*******************
CLKINT
MOVWF XW_TEMP ;保存W的值
SWAPF STATUS,W
MOVWF XHOSTS ;暫存STATUS的值
MOVF FSR0L,W
MOVWF HOSTF ;保存FSR0L的值。以上程序為中斷現場保護
MOVLW 0X13
MOVWF TMR0L ;對TMR0L寫入一個調整值,因為寫入
;TMR0L后接著的兩個周期不能增量
BCF INTCON,T0IF ;清除中斷標志
INCF SREG
MOVF SREG,W
SUBLW 0X28 ;TMR0L 每250us中斷一次
BTFSS STATUS,Z ;其中斷40次后才執行對S0 S1 S2 S3 的操作
GOTO TIFAN
CLRF SREG
INCF S0
MOVF S0,W
SUBLW 0X0A
BTFSC STATUS,Z
CALL CS0
MOVF S1,W
SUBLW 0X0A
BTFSC STATUS,Z
CALL CS1
MOVF S2,W
SUBLW 0X0A
BTFSC STATUS,Z
CALL CS2
MOVF S3,W
SUBLW 0X0A
BTFSC STATUS,Z
CALL CS3
TIFAN
MOVF HOSTF,W ;以下為中斷現場恢復
MOVWF FSR0L ;恢復間接尋址指針FSR0L的值
SWAPF XHOSTS,W
MOVWF STATUS ;恢復STATUS的值
SWAPF XW_TEMP,1
SWAPF XW_TEMP,W ;恢復W的值
RETFIE
;*********** TMR0初始化子程序***************
CLKINSUB
MOVLW 0XCF
MOVWF T0CON ;設定TMR0L工作于8位定時器方式
;內部時鐘,TMR0不用分頻
BCF INTCON,TMR0IF ;清除TMR0的中斷標志
BCF INTCON,GIE ;總中斷禁止
BSF INTCON,TMR0IE ;TMR0中斷允許
BSF INTCON2,2 ;TMR0中斷高優先級
BSF RCON,7 ;使能中斷優先級
RETURN
;***************系統初始化子程序*****************
MAINSUB
BCF TRISA,5 ;置RA5為輸出方式,以輸出鎖存信號
BCF TRISB,1
BCF TRISA,3
BCF TRISE,0
BCF TRISE,1
BSF TRISB,4 ;設置與鍵盤有關的各口的輸入輸出方式
BCF TRISC,5
BCF TRISC,3 ;設置SCK與SDO為輸出方式
BCF INTCON,GIE ;關閉所有中斷
MOVLW 0XC0
MOVWF SSPSTAT ;設置SSPSTAT寄存器
MOVLW 0X30
MOVWF SSPCON1 ;設置SPI的控制方式,允許SSP方式,
;并且時鐘下降沿發送,與"74HC595當
;其SCLK從低到高平跳變時,串行輸入數據
;(DI)移入寄存器"的特點相對應
RETURN ;返回
;***********鍵掃描子程序********************
KEYSCAN
BCF PORTB,1
BCF PORTA,3
BCF PORTE,0
BCF PORTE,1 ;送低電平至K1,K2,K3,K4
RETURN
;*********鍵盤去抖子程序(8ms的延時)******************
KEYDELAY
MOVLW 0X0A
MOVWF DEYH
AGAIN2 MOVLW 0XFF
MOVWF DEYL
AGAIN1 DECFSZ DEYL,1
GOTO AGAIN1
DECFSZ DEYH,1
GOTO AGAIN2 ;具體程序語句參考3. 2節
RETURN
;*************顯示緩沖區處理子程序****************
XIANHUAN
MOVLW 0XA0
MOVWF FSR0L
RLNCF S0,0
MOVWF TEMP
INCF TEMP,0
MOVWF INDF0
INCF FSR0L
RLNCF S1,0
MOVWF TEMP
INCF TEMP,0
MOVWF INDF0
INCF FSR0L
RLNCF S2,0
MOVWF TEMP
INCF TEMP,0
MOVWF INDF0
INCF FSR0L
RLNCF S3,0
MOVWF TEMP
INCF TEMP,0
MOVWF INDF0
RETURN
;**************主程序******************
MAIN NOP
CALL MAINSUB ;系統初始化
CALL CLKINSUB ;調用時鐘初始化子程序
DENJIAN BCF INTCON,GIE
CLRF S0
CLRF S1
CLRF S2
CLRF S3
CLRF SREG
CLRF LEDF ;S0=S1=S2=S3=0
CALL XIANHUAN ;把S3、S2、S1、S0的值裝入顯示緩沖區
CALL DISPLAY ;顯示
DENAN BCF INTCON,GIE ;關中斷
CALL KEYSCAN ;進行鍵掃描
BTFSS PORTB,4
GOTO XIAODOU1 ;如起始鍵按下,則消抖動
GOTO DENAN ;如起始鍵沒按下,則繼續等待
XIAODOU1
CALL KEYDELAY ;延時消抖動
CALL KEYSCAN ;再次進行鍵掃描
BTFSC PORTB,4
GOTO DENAN ;若為干擾,則再次等待鍵按下
CLRF S0
CLRF S1
CLRF S2
CLRF S3
CLRF SREG ;設置新一次計時的初始條件
WAITS CALL KEYSCAN
BTFSS PORTB,4
GOTO WAITS ;為了防止按鍵過于靈敏,等鍵松開后再進行
;下面的操作
BSF INTCON,GIE ;開總中斷
HERE CALL XIANHUAN ;顯示緩沖區處理
CALL DISPLAY ;即時更新顯示內容
CALL KEYSCAN
BTFSS PORTB,4
GOTO XIAODOU ;若停止計時鍵按下,則消抖
GOTO HERE ;若停止計時鍵沒按下,則繼續等待中斷計時
XIAODOU
CALL DISPLAY ;利用消抖動時間調用顯示程序
CALL DISPLAY
CALL DISPLAY
CALL DISPLAY
CALL DISPLAY
CALL DISPLAY
CALL DISPLAY
CALL DISPLAY
CALL DISPLAY
CALL KEYSCAN
BTFSC PORTB,4
GOTO HERE ;若為干擾,則繼續等待停止鍵按下
BCF INTCON,GIE ;若停止鍵按下,則禁止中斷
CALL DISPLAY ;若不為干擾,則禁止中斷,更新顯示內容
WAITR CALL KEYSCAN
BTFSS PORTB,4
GOTO WAITR ;為了防止按鍵過于靈敏,等鍵松開后再進行
;下面的操作
DENDAI CALL KEYSCAN
BTFSC PORTB,4
GOTO DENDAI ;循環進行鍵掃描,等待清0鍵按下
CALL KEYDELAY ;延時消抖
CALL KEYSCAN
BTFSC PORTB,4
GOTO DENDAI
WAIT19 CALL KEYSCAN
BTFSS PORTB,4
GOTO WAIT19 ;等鍵松開
GOTO DENJIAN ;新一次計時開始
END