如何改进我的 LED 显示代码,使其实时响应
How can I improve my LED display code so that it reacts in real-time
这是 my previous one 提出的一个问题,我想知道如何改进我的代码,目前它在将数字输出到显示器上有极大的延迟,而且也没有显示预期的输出在所有情况下,即某些段不亮。
请看下面的图片!
正确但延迟的输出 1
正确但延迟的输出 2
正确但延迟输出 3
输出不正确和延迟
从一个没有经验的角度来看,我认为这是一个时间问题。但是,目前我还不太清楚如何解决它。因此,非常感谢我可以使用的任何见解或建议的数字。
ADC 代码块的工作代码如下:
LIST p=16f1829 ;list directive to define processor
#INCLUDE <p16f1829.inc> ;processor specific variable definitions
__CONFIG _CONFIG1, (_FOSC_INTOSC & _WDTE_OFF & _PWRTE_OFF & _MCLRE_OFF & _CP_OFF & _CPD_OFF & _BOREN_ON & _CLKOUTEN_OFF & _IESO_OFF & _FCMEN_OFF);
__CONFIG _CONFIG2, (_WRT_OFF & _PLLEN_OFF & _STVREN_OFF & _LVP_OFF);
;-------------------------------------------------------------------------
; UDATA_SHR declares a section of shared (all across the banks) uninitialised data
INT_VAR UDATA_SHR ; INT_VAR is the section name in ACCESS RAM
TempC RES 1 ;
L0 RES 1 ;
w_temp RES 1 ; variable used for context saving
pclath_temp RES 1 ; variable used for context saving
status_temp RES 1 ; variable used for context saving
;-------------------------------------------------------------------------
LEDtrisA EQU TRISA
LEDtrisC EQU TRISC
LEDlatA EQU LATA
LEDlatC EQU LATC
;-------------------------------------------------------------------------
RESET_VECTOR CODE 0x0000
GOTO START ; When using debug header, ICD2 may not stop
; on instruction 0 during reset.
;-------------------------------------------------------------------------
INT_VECTOR CODE 0x0004 ; Interrupt vector location
ISR ; Relocatable Interrupt Service Routine
; Context saving for ISR
MOVWF w_temp ; save off current W register contents
MOVF STATUS, w ; move status register into W register
MOVWF status_temp ; save off contents of STATUS register
MOVF PCLATH, w ; Saves value in register PCLATH
MOVWF pclath_temp
;-------------------------------------------------------------------------
; If the interrupt came from the timer, execute the TMR0 interrupt
; service routine.
BANKSEL TMR0
MOVLW .96
MOVWF TMR0
BTFSC INTCON, TMR0IF
CALL Service_TMR0
BRA UpdateDisplay ; Refresh the display
UpdateDisplay
BANKSEL LATA ; Selects memory bank containing LATA register
MOVF LEDlatA, w ; display status -> w register
ANDLW 0x0f ; Separate the lower half byte
MOVWF TempC ; Save display status in TempC
BSF TempC, 4 ; Beginning status of LSD display
RRF TempC, F ; Set the status of the next display
BTFSS STATUS, C ; C = 1?
BCF TempC, 3 ; If not, turn off the LSD display
BTFSC TempC, 0
BRA UpdateMsd ; If it is turned on, display the MSD
; digit of the number
UpdateLsd
BCF TempC, 3
BSF TempC, 1
BTFSS STATUS, Z ; If it is, skip
MOVF L0, w ; Third LSD digit -> w
ANDLW 0x0f ; /
BRA DisplayOut ; Show it on the display
UpdateMsd
SWAPF L0, w ; MSD figure - >
ANDLW 0x0f ; /
BTFSC STATUS, Z ; MSD = 0?
MOVLW 0x0a ; If it is, skip
DisplayOut
CALL LedTable ; Take the mask for a digit
MOVWF LEDlatC ; Set the mask on port B
MOVF TempC, W ; Turn on displays
MOVWF LEDlatA
BRA ISR_END
;-------------------------------------------------------------------------
ISR_END
; Restore contents before returning from interrupt
MOVF pclath_temp,w ; PCLATH is given its original content
MOVWF PCLATH
MOVF status_temp,w ; STATUS is given its original content
MOVWF STATUS
SWAPF w_temp,f ; W is given its original content
SWAPF w_temp,w
BSF INTCON,GIE ; Global interrupt enabled
RETFIE ; Return from interrupt routine
;-------------------------------------------------------------------------
; LOOKUP TABLE
;-------------------------------------------------------------------------
LUT_VECTOR CODE 0x0030 ; Lookup Table location
LUT ; Lookup table is at the top of third page,
; but can be placed at some other place, it
; is important to have it all on one page
LedTable
ADDWF PCL, F
RETLW B'00111111' ; mask for digit 0
RETLW B'00000110' ; mask for digit 1
RETLW B'01011011' ; mask for digit 2
RETLW B'01001111' ; mask for digit 3
RETLW B'01100110' ; mask for digit 4
RETLW B'01101101' ; mask for digit 5
RETLW B'01111101' ; mask for digit 6
RETLW B'00000111' ; mask for digit 7
RETLW B'01111111' ; mask for digit 8
RETLW B'01101111' ; mask for digit 9
RETLW B'01110111'
RETLW B'01111100'
RETLW B'00111001'
RETLW B'01011110'
RETLW B'01111101'
RETLW B'01110001'
RETLW B'00000000' ; no digit ......
;-------------------------------------------------------------------------
MAIN_PROG CODE
START
;-------------------------------------------------------------------------
ERRORLEVEL -302 ; Disable warning accessing register not in bank 0
BANKSEL OSCTUNE ; Configure OPTION_REG and TMR0
MOVLW 0x00 ; Set oscillator to factory calibrated frequency
MOVWF OSCTUNE ;
BANKSEL STATUS
ERRORLEVEL +302 ; Enable warning accessing register not in bank 0
CLEAR_RAM ; code sequence initialises all GPR's to 0x00
MOVLW 0x70 ; initialise pointer
MOVWF FSR0 ; to RAM
CLRF FSR0H
NEXT
CLRF INDF0 ; Clear INDF0 register
INCF FSR0L, F ; Inc pointer
BTFSS FSR0L, 7 ; All done?
GOTO NEXT ; No, clear NEXT
CONTINUE ; Yes, CONTINUE
NOP
;-------------------------------------------------------------------------
; MAIN BODY OF PROGRAM
;-------------------------------------------------------------------------
; Setup main init
BANKSEL OSCCON ; Selects memory bank containing OSCCON register
MOVLW b'01011000' ; Set CPU clock speed of 500KHz -> correlates to (1/(500K/4)) for each instruction
MOVWF OSCCON ; OSCCON <- 0x38
; Configure the ADC/Potentimator
; Already in bank1
MOVLW b'00001101' ; Select RA4 as source of ADC and enable the module (careful, this is actually AN3)
MOVWF ADCON0
MOVLW b'00010000' ; Left justified - Fosc/8 speed - vref is Vdd
MOVWF ADCON1
BANKSEL ANSELA ; Selects memory bank containing ANSELA register
; Setup pins as digital I/O
MOVLW 0x10 ; Selects memory bank containing ANSELA register
ANDWF ANSELA ; All pins are digital
CLRF ANSELC
; Configure the input & output pins
BANKSEL TRISA ; Selects memory bank containing TRISA register
MOVLW b'11111100' ; RA0 and RA1 are configured as outputs and
; used for 7-segment display multiplexing
; RA2 is input push-button for initialization
MOVWF TRISA
CLRF LEDtrisC ; Port C is output
BANKSEL LATA ; Selects memory bank containing LATA register
CLRF LEDlatA ; Set all outputs to "0"
CLRF LEDlatC ;
BSF LEDlatA, 1 ; Turn on MSD display
; Setup Timer0 as the delay
BANKSEL OPTION_REG
MOVLW b'10000100' ; TMR0 is incremented each 32us (Fclk=8MHz)
MOVWF OPTION_REG ; ps = 32
BANKSEL TMR0 ; Selects memory bank containing TMR0 register
BSF INTCON, GIE ; Global interrupt enabled
BSF INTCON, TMR0IE ; Timer TMR0 interrupt enabled
MAINLOOP
BRA MAINLOOP ; Continue forever
A2D:
; Start the ADC
NOP ; Requried ADC delay of 8uS => (1/(Fosc/4)) = (1/(500KHz/4)) = 8uS
BANKSEL ADCON0 ; Selects memory bank containing ADCON0 register
BSF ADCON0, GO ; Start the ADC
BTFSC ADCON0, GO ; This bit will be cleared when the conversion is complete
GOTO $-1 ; Keep checking the above line until GO bit is clear
; Grab Results and write to the LEDs
SWAPF ADRESH, w ; Get the top 4 MSbs (remember that the ADC result is LEFT justified!)
MOVWF L0
RETURN
; TIMER 0 Interrupt routine clears the TMR0 interrupt flag.
Service_TMR0:
BANKSEL INTCON
BCF INTCON, TMR0IF ; MUST ALWAYS clear this in software or else stuck in the ISR forever
BTFSC LATA, 1 ; Check if ADC value already determined
CALL A2D ; Get the ADC result
RETURN
;-------------------------------------------------------------------------
; END OF PROGRAM
;-------------------------------------------------------------------------
END ; End of program
这里有一些问题,有些可能是不好的做法。
1- 此处调用 Timer0 ISR 中的 ADC 例程。
Service_TMR0:
BANKSEL INTCON
BCF INTCON, TMR0IF ; MUST ALWAYS clear this in software or else stuck in the ISR forever
BTFSC LATA, 1 ; Check if ADC value already determined
CALL A2D ; Get the ADC result
RETURN
然后 Timer0 isr 调用 ADC,程序在那里循环至少大约 20us,这可能导致错过另一个挂起的中断或挂起的输入事件。
A2D:
; Start the ADC
NOP ; Requried ADC delay of 8uS => (1/(Fosc/4)) = (1/(500KHz/4)) = 8uS
BANKSEL ADCON0 ; Selects memory bank containing ADCON0 register
BSF ADCON0, GO ; Start the ADC
BTFSC ADCON0, GO ; This bit will be cleared when the conversion is complete
GOTO $-1 ; Keep checking the above line until GO bit is clear
; Grab Results and write to the LEDs
SWAPF ADRESH, w ; Get the top 4 MSbs (remember that the ADC result is LEFT justified!)
MOVWF L0
RETURN
据我所知,您在计时器 isr 中执行所有操作,这是一种糟糕的编程习惯。您也更新显示。更新显示不会花费太多时间,因此可以在 isr 中忽略它。但是一些需要很多时间的例程不应该在 isr 中处理。因此,您最好处理可能需要更多时间的例程,或者可以在主循环中轮询一个标志。
2- 在本节中,您将对数字进行多路复用。您使用 RRF 指令将它们多路复用,就好像有 4 位数字一样,但您的应用程序中只有 2 位数字。
UpdateDisplay
BANKSEL LATA ; Selects memory bank containing LATA register
MOVF LEDlatA, w ; display status -> w register
ANDLW 0x0f ; Separate the lower half byte
MOVWF TempC ; Save display status in TempC
BSF TempC, 4 ; Beginning status of LSD display
RRF TempC, F ; Set the status of the next display
BTFSS STATUS, C ; C = 1?
BCF TempC, 3 ; If not, turn off the LSD display
BTFSC TempC, 0
我不知道哪些引脚用于多路复用数字,但您必须在临时寄存器中设置正确的位才能正确多路复用它们。例如,如果您的 digit1 和 digit0 分别连接到 A1 和 A0,则必须设置第二位 Tempc,2 才能正确复用它们。然后当设置的位到达进位时,你必须再次设置它等等。
aLoHa,
使用 PIC 控制器多路复用 7 段 LED 显示器的方法有很多种。
这是我最喜欢的方法的演示:
;
; File: main.asm
; Target: PIC16F1829
; Author: dan1138
; Assembler: MPASMWIN v5.81
; IDE: MPLABX v5.04
;
LIST p=16f1829 ; list directive to define processor
LIST n=0, c=250 ; make list file look nice
LIST r=dec ; use decimal as default radix for numbers
errorlevel -302 ; suppress warning for: Register in operand not in bank 0.
;
; PIC16F1829
; +-------------:_:-----------+
; 5v0 -> 1 : VDD VSS : 20 <- GND
; <> 2 : RA5/CPP2 PGD/AN0/RA0 : 19 <> ICD_PGD
; POT <> 3 : RA4/AN3 PGC/AN1/RA1 : 18 <> ICD_PGC
; ICD_MCLR -> 4 : RA3/MCLR CCP3/AN2/RA2 : 17 <> SW
; SEG_F <> 5 : RC5/CPP1 AN4/RC0 : 16 <> SEG_A
; SEG_E <> 6 : RC4 AN5/RC1 : 15 <> SEG_B
; SEG_D <> 7 : RC3/AN7 AN6/RC2 : 14 <> SEG_C
; SEG_G <> 8 : RC6/AN8 SDI/AN10/RB4 : 13 <> DIGIT1
; <> 9 : RC7/AN9/SDO RX/AN11/RB5 : 12 <>
; <> 10 : RB7/TX SCK/RB6 : 11 <> DIGIT2
; +---------------------------+
; DIP-20
;
; Description:
;
; This is code to demonstrate one method to multiplex
; two digits in two 7-segment LED displays.
;
; A segment is ON when the SEG(A-G) output is HIGH.
;
; A digit is on when the DIGIT(1,2) output is HIGH.
; Only one DIGIT output can be HIGH at a time.
;
#INCLUDE <p16f1829.inc> ;processor specific variable definitions
__CONFIG _CONFIG1, (_FOSC_INTOSC & _WDTE_OFF & _PWRTE_OFF & _MCLRE_OFF & _CP_OFF & _CPD_OFF & _BOREN_ON & _CLKOUTEN_OFF & _IESO_OFF & _FCMEN_OFF);
__CONFIG _CONFIG2, (_WRT_OFF & _PLLEN_OFF & _STVREN_OFF & _LVP_OFF);
RES_VECT CODE 0x0000 ; processor reset vector
pagesel START
GOTO START ; go to beginning of program
ISR_DATA UDATA_SHR
WREG_Save res 1
STATUS_Save res 1
PCLATH_Save res 1
Segments res 2
ISR CODE 0x0004 ; interrupt vector location
movwf WREG_Save
movf STATUS,W
movwf STATUS_Save
movf PCLATH,W
movwf PCLATH_Save
pagesel $
btfsc INTCON,TMR0IE
btfss INTCON,TMR0IF
goto ISR_EXIT
bcf INTCON,TMR0IF
banksel LATB
movlw 0x80
andwf LATC,F ; Turn off all LED segments
movlw (1<<LATB6)
btfsc LATB,LATB6
movlw (1<<LATB4)
btfsc LATB,LATB4
movlw (1<<LATB6)
xorwf LATB,W
andlw (1<<LATB4)|(1<<LATB6)
xorwf LATB,F ; Select LED display to be on
btfsc LATB,LATB6
movf Segments+0,W
btfsc LATB,LATB4
movf Segments+1,W
iorwf LATC,F ; Turn on LED segments for selected digit
ISR_EXIT:
movf PCLATH_Save,W
movwf PCLATH
movf STATUS_Save,W
movwf STATUS
swapf WREG_Save,F
swapf WREG_Save,W
RETFIE
MAIN_PROG CODE ; let linker place main program
;
; Setup system clock at 32MHz
;
START:
clrf INTCON
banksel PIE1
clrf PIE1
clrf PIE2
clrf PIE3
clrf PIE4
banksel OPTION_REG
movlw 0xFF
movwf OPTION_REG
banksel OSCCON ; Setup 32MHz system oscillator
movlw 0xF0
movwf OSCCON
;
; Setup GPIO ports
;
banksel LATA
clrf LATA
banksel TRISA
clrf TRISA
bsf TRISA,TRISA2
bsf TRISA,TRISA4
banksel ANSELA
movlw 0xFF
movwf ANSELA
bcf ANSELA,ANSA2 ; Make SWn a digital input
banksel LATB
clrf LATB
banksel TRISB
clrf TRISB
banksel ANSELB
clrf ANSELB
banksel LATC
clrf LATC
banksel TRISC
clrf TRISC
banksel ANSELC
clrf ANSELC
;
; Setup TIMER0 to interrupt every 1.024 milliseconds
; when the system oscillator is 32MHz.
;
banksel OPTION_REG
bcf OPTION_REG,TMR0CS
bcf OPTION_REG,PSA
bcf OPTION_REG,PS0
bcf OPTION_REG,PS1
banksel TMR0
clrf TMR0
bcf INTCON,TMR0IF
bsf INTCON,TMR0IE
;
; Turn on interrupts
;
bsf INTCON,GIE
;
; Setup LED displays to show '1' and '2'
;
movlw 1
call Hex2Segments
movwf Segments+0
movlw 2
call Hex2Segments
movwf Segments+1
;
; Application process loop
;
AppLoop:
GOTO AppLoop
;
; Function: Hex2Segments
;
; Input: WREG = LED segment table lookup index
; Range: 0x00 to 0x0F
;
; Output WREG = 7 low bits are LED segments gfedcba
;
Hex2Segments:
andlw 0x0F
xorlw HIGH(SegTable)
movwf PCLATH
xorlw HIGH(SegTable)
xorwf PCLATH,F
addlw LOW(SegTable)
btfsc STATUS,C
incf PCLATH,F
movwf PCL
SegTable:
RETLW B'00111111' ; mask for digit 0
RETLW B'00000110' ; mask for digit 1
RETLW B'01011011' ; mask for digit 2
RETLW B'01001111' ; mask for digit 3
RETLW B'01100110' ; mask for digit 4
RETLW B'01101101' ; mask for digit 5
RETLW B'01111101' ; mask for digit 6
RETLW B'00000111' ; mask for digit 7
RETLW B'01111111' ; mask for digit 8
RETLW B'01101111' ; mask for digit 9
RETLW B'01110111'
RETLW B'01111100'
RETLW B'00111001'
RETLW B'01011110'
RETLW B'01111001'
RETLW B'01110001'
END
此方法在 ISR 之外进行 HEX 分段查找。
这样可以缩短 ISR 的执行时间。
我没有包含读取 ADC 的函数,但有一个示例说明如何在 LED 显示屏上显示 1 和 2。
我不清楚您正在使用的 7 段显示器的类型以及它们的接线方式,所以我怀疑这段代码是否适用于您的面包板。
这是 my previous one 提出的一个问题,我想知道如何改进我的代码,目前它在将数字输出到显示器上有极大的延迟,而且也没有显示预期的输出在所有情况下,即某些段不亮。
请看下面的图片!
正确但延迟的输出 1
正确但延迟的输出 2
正确但延迟输出 3
输出不正确和延迟
从一个没有经验的角度来看,我认为这是一个时间问题。但是,目前我还不太清楚如何解决它。因此,非常感谢我可以使用的任何见解或建议的数字。
ADC 代码块的工作代码如下:
LIST p=16f1829 ;list directive to define processor
#INCLUDE <p16f1829.inc> ;processor specific variable definitions
__CONFIG _CONFIG1, (_FOSC_INTOSC & _WDTE_OFF & _PWRTE_OFF & _MCLRE_OFF & _CP_OFF & _CPD_OFF & _BOREN_ON & _CLKOUTEN_OFF & _IESO_OFF & _FCMEN_OFF);
__CONFIG _CONFIG2, (_WRT_OFF & _PLLEN_OFF & _STVREN_OFF & _LVP_OFF);
;-------------------------------------------------------------------------
; UDATA_SHR declares a section of shared (all across the banks) uninitialised data
INT_VAR UDATA_SHR ; INT_VAR is the section name in ACCESS RAM
TempC RES 1 ;
L0 RES 1 ;
w_temp RES 1 ; variable used for context saving
pclath_temp RES 1 ; variable used for context saving
status_temp RES 1 ; variable used for context saving
;-------------------------------------------------------------------------
LEDtrisA EQU TRISA
LEDtrisC EQU TRISC
LEDlatA EQU LATA
LEDlatC EQU LATC
;-------------------------------------------------------------------------
RESET_VECTOR CODE 0x0000
GOTO START ; When using debug header, ICD2 may not stop
; on instruction 0 during reset.
;-------------------------------------------------------------------------
INT_VECTOR CODE 0x0004 ; Interrupt vector location
ISR ; Relocatable Interrupt Service Routine
; Context saving for ISR
MOVWF w_temp ; save off current W register contents
MOVF STATUS, w ; move status register into W register
MOVWF status_temp ; save off contents of STATUS register
MOVF PCLATH, w ; Saves value in register PCLATH
MOVWF pclath_temp
;-------------------------------------------------------------------------
; If the interrupt came from the timer, execute the TMR0 interrupt
; service routine.
BANKSEL TMR0
MOVLW .96
MOVWF TMR0
BTFSC INTCON, TMR0IF
CALL Service_TMR0
BRA UpdateDisplay ; Refresh the display
UpdateDisplay
BANKSEL LATA ; Selects memory bank containing LATA register
MOVF LEDlatA, w ; display status -> w register
ANDLW 0x0f ; Separate the lower half byte
MOVWF TempC ; Save display status in TempC
BSF TempC, 4 ; Beginning status of LSD display
RRF TempC, F ; Set the status of the next display
BTFSS STATUS, C ; C = 1?
BCF TempC, 3 ; If not, turn off the LSD display
BTFSC TempC, 0
BRA UpdateMsd ; If it is turned on, display the MSD
; digit of the number
UpdateLsd
BCF TempC, 3
BSF TempC, 1
BTFSS STATUS, Z ; If it is, skip
MOVF L0, w ; Third LSD digit -> w
ANDLW 0x0f ; /
BRA DisplayOut ; Show it on the display
UpdateMsd
SWAPF L0, w ; MSD figure - >
ANDLW 0x0f ; /
BTFSC STATUS, Z ; MSD = 0?
MOVLW 0x0a ; If it is, skip
DisplayOut
CALL LedTable ; Take the mask for a digit
MOVWF LEDlatC ; Set the mask on port B
MOVF TempC, W ; Turn on displays
MOVWF LEDlatA
BRA ISR_END
;-------------------------------------------------------------------------
ISR_END
; Restore contents before returning from interrupt
MOVF pclath_temp,w ; PCLATH is given its original content
MOVWF PCLATH
MOVF status_temp,w ; STATUS is given its original content
MOVWF STATUS
SWAPF w_temp,f ; W is given its original content
SWAPF w_temp,w
BSF INTCON,GIE ; Global interrupt enabled
RETFIE ; Return from interrupt routine
;-------------------------------------------------------------------------
; LOOKUP TABLE
;-------------------------------------------------------------------------
LUT_VECTOR CODE 0x0030 ; Lookup Table location
LUT ; Lookup table is at the top of third page,
; but can be placed at some other place, it
; is important to have it all on one page
LedTable
ADDWF PCL, F
RETLW B'00111111' ; mask for digit 0
RETLW B'00000110' ; mask for digit 1
RETLW B'01011011' ; mask for digit 2
RETLW B'01001111' ; mask for digit 3
RETLW B'01100110' ; mask for digit 4
RETLW B'01101101' ; mask for digit 5
RETLW B'01111101' ; mask for digit 6
RETLW B'00000111' ; mask for digit 7
RETLW B'01111111' ; mask for digit 8
RETLW B'01101111' ; mask for digit 9
RETLW B'01110111'
RETLW B'01111100'
RETLW B'00111001'
RETLW B'01011110'
RETLW B'01111101'
RETLW B'01110001'
RETLW B'00000000' ; no digit ......
;-------------------------------------------------------------------------
MAIN_PROG CODE
START
;-------------------------------------------------------------------------
ERRORLEVEL -302 ; Disable warning accessing register not in bank 0
BANKSEL OSCTUNE ; Configure OPTION_REG and TMR0
MOVLW 0x00 ; Set oscillator to factory calibrated frequency
MOVWF OSCTUNE ;
BANKSEL STATUS
ERRORLEVEL +302 ; Enable warning accessing register not in bank 0
CLEAR_RAM ; code sequence initialises all GPR's to 0x00
MOVLW 0x70 ; initialise pointer
MOVWF FSR0 ; to RAM
CLRF FSR0H
NEXT
CLRF INDF0 ; Clear INDF0 register
INCF FSR0L, F ; Inc pointer
BTFSS FSR0L, 7 ; All done?
GOTO NEXT ; No, clear NEXT
CONTINUE ; Yes, CONTINUE
NOP
;-------------------------------------------------------------------------
; MAIN BODY OF PROGRAM
;-------------------------------------------------------------------------
; Setup main init
BANKSEL OSCCON ; Selects memory bank containing OSCCON register
MOVLW b'01011000' ; Set CPU clock speed of 500KHz -> correlates to (1/(500K/4)) for each instruction
MOVWF OSCCON ; OSCCON <- 0x38
; Configure the ADC/Potentimator
; Already in bank1
MOVLW b'00001101' ; Select RA4 as source of ADC and enable the module (careful, this is actually AN3)
MOVWF ADCON0
MOVLW b'00010000' ; Left justified - Fosc/8 speed - vref is Vdd
MOVWF ADCON1
BANKSEL ANSELA ; Selects memory bank containing ANSELA register
; Setup pins as digital I/O
MOVLW 0x10 ; Selects memory bank containing ANSELA register
ANDWF ANSELA ; All pins are digital
CLRF ANSELC
; Configure the input & output pins
BANKSEL TRISA ; Selects memory bank containing TRISA register
MOVLW b'11111100' ; RA0 and RA1 are configured as outputs and
; used for 7-segment display multiplexing
; RA2 is input push-button for initialization
MOVWF TRISA
CLRF LEDtrisC ; Port C is output
BANKSEL LATA ; Selects memory bank containing LATA register
CLRF LEDlatA ; Set all outputs to "0"
CLRF LEDlatC ;
BSF LEDlatA, 1 ; Turn on MSD display
; Setup Timer0 as the delay
BANKSEL OPTION_REG
MOVLW b'10000100' ; TMR0 is incremented each 32us (Fclk=8MHz)
MOVWF OPTION_REG ; ps = 32
BANKSEL TMR0 ; Selects memory bank containing TMR0 register
BSF INTCON, GIE ; Global interrupt enabled
BSF INTCON, TMR0IE ; Timer TMR0 interrupt enabled
MAINLOOP
BRA MAINLOOP ; Continue forever
A2D:
; Start the ADC
NOP ; Requried ADC delay of 8uS => (1/(Fosc/4)) = (1/(500KHz/4)) = 8uS
BANKSEL ADCON0 ; Selects memory bank containing ADCON0 register
BSF ADCON0, GO ; Start the ADC
BTFSC ADCON0, GO ; This bit will be cleared when the conversion is complete
GOTO $-1 ; Keep checking the above line until GO bit is clear
; Grab Results and write to the LEDs
SWAPF ADRESH, w ; Get the top 4 MSbs (remember that the ADC result is LEFT justified!)
MOVWF L0
RETURN
; TIMER 0 Interrupt routine clears the TMR0 interrupt flag.
Service_TMR0:
BANKSEL INTCON
BCF INTCON, TMR0IF ; MUST ALWAYS clear this in software or else stuck in the ISR forever
BTFSC LATA, 1 ; Check if ADC value already determined
CALL A2D ; Get the ADC result
RETURN
;-------------------------------------------------------------------------
; END OF PROGRAM
;-------------------------------------------------------------------------
END ; End of program
这里有一些问题,有些可能是不好的做法。
1- 此处调用 Timer0 ISR 中的 ADC 例程。
Service_TMR0:
BANKSEL INTCON
BCF INTCON, TMR0IF ; MUST ALWAYS clear this in software or else stuck in the ISR forever
BTFSC LATA, 1 ; Check if ADC value already determined
CALL A2D ; Get the ADC result
RETURN
然后 Timer0 isr 调用 ADC,程序在那里循环至少大约 20us,这可能导致错过另一个挂起的中断或挂起的输入事件。
A2D:
; Start the ADC
NOP ; Requried ADC delay of 8uS => (1/(Fosc/4)) = (1/(500KHz/4)) = 8uS
BANKSEL ADCON0 ; Selects memory bank containing ADCON0 register
BSF ADCON0, GO ; Start the ADC
BTFSC ADCON0, GO ; This bit will be cleared when the conversion is complete
GOTO $-1 ; Keep checking the above line until GO bit is clear
; Grab Results and write to the LEDs
SWAPF ADRESH, w ; Get the top 4 MSbs (remember that the ADC result is LEFT justified!)
MOVWF L0
RETURN
据我所知,您在计时器 isr 中执行所有操作,这是一种糟糕的编程习惯。您也更新显示。更新显示不会花费太多时间,因此可以在 isr 中忽略它。但是一些需要很多时间的例程不应该在 isr 中处理。因此,您最好处理可能需要更多时间的例程,或者可以在主循环中轮询一个标志。
2- 在本节中,您将对数字进行多路复用。您使用 RRF 指令将它们多路复用,就好像有 4 位数字一样,但您的应用程序中只有 2 位数字。
UpdateDisplay
BANKSEL LATA ; Selects memory bank containing LATA register
MOVF LEDlatA, w ; display status -> w register
ANDLW 0x0f ; Separate the lower half byte
MOVWF TempC ; Save display status in TempC
BSF TempC, 4 ; Beginning status of LSD display
RRF TempC, F ; Set the status of the next display
BTFSS STATUS, C ; C = 1?
BCF TempC, 3 ; If not, turn off the LSD display
BTFSC TempC, 0
我不知道哪些引脚用于多路复用数字,但您必须在临时寄存器中设置正确的位才能正确多路复用它们。例如,如果您的 digit1 和 digit0 分别连接到 A1 和 A0,则必须设置第二位 Tempc,2 才能正确复用它们。然后当设置的位到达进位时,你必须再次设置它等等。
aLoHa,
使用 PIC 控制器多路复用 7 段 LED 显示器的方法有很多种。
这是我最喜欢的方法的演示:
;
; File: main.asm
; Target: PIC16F1829
; Author: dan1138
; Assembler: MPASMWIN v5.81
; IDE: MPLABX v5.04
;
LIST p=16f1829 ; list directive to define processor
LIST n=0, c=250 ; make list file look nice
LIST r=dec ; use decimal as default radix for numbers
errorlevel -302 ; suppress warning for: Register in operand not in bank 0.
;
; PIC16F1829
; +-------------:_:-----------+
; 5v0 -> 1 : VDD VSS : 20 <- GND
; <> 2 : RA5/CPP2 PGD/AN0/RA0 : 19 <> ICD_PGD
; POT <> 3 : RA4/AN3 PGC/AN1/RA1 : 18 <> ICD_PGC
; ICD_MCLR -> 4 : RA3/MCLR CCP3/AN2/RA2 : 17 <> SW
; SEG_F <> 5 : RC5/CPP1 AN4/RC0 : 16 <> SEG_A
; SEG_E <> 6 : RC4 AN5/RC1 : 15 <> SEG_B
; SEG_D <> 7 : RC3/AN7 AN6/RC2 : 14 <> SEG_C
; SEG_G <> 8 : RC6/AN8 SDI/AN10/RB4 : 13 <> DIGIT1
; <> 9 : RC7/AN9/SDO RX/AN11/RB5 : 12 <>
; <> 10 : RB7/TX SCK/RB6 : 11 <> DIGIT2
; +---------------------------+
; DIP-20
;
; Description:
;
; This is code to demonstrate one method to multiplex
; two digits in two 7-segment LED displays.
;
; A segment is ON when the SEG(A-G) output is HIGH.
;
; A digit is on when the DIGIT(1,2) output is HIGH.
; Only one DIGIT output can be HIGH at a time.
;
#INCLUDE <p16f1829.inc> ;processor specific variable definitions
__CONFIG _CONFIG1, (_FOSC_INTOSC & _WDTE_OFF & _PWRTE_OFF & _MCLRE_OFF & _CP_OFF & _CPD_OFF & _BOREN_ON & _CLKOUTEN_OFF & _IESO_OFF & _FCMEN_OFF);
__CONFIG _CONFIG2, (_WRT_OFF & _PLLEN_OFF & _STVREN_OFF & _LVP_OFF);
RES_VECT CODE 0x0000 ; processor reset vector
pagesel START
GOTO START ; go to beginning of program
ISR_DATA UDATA_SHR
WREG_Save res 1
STATUS_Save res 1
PCLATH_Save res 1
Segments res 2
ISR CODE 0x0004 ; interrupt vector location
movwf WREG_Save
movf STATUS,W
movwf STATUS_Save
movf PCLATH,W
movwf PCLATH_Save
pagesel $
btfsc INTCON,TMR0IE
btfss INTCON,TMR0IF
goto ISR_EXIT
bcf INTCON,TMR0IF
banksel LATB
movlw 0x80
andwf LATC,F ; Turn off all LED segments
movlw (1<<LATB6)
btfsc LATB,LATB6
movlw (1<<LATB4)
btfsc LATB,LATB4
movlw (1<<LATB6)
xorwf LATB,W
andlw (1<<LATB4)|(1<<LATB6)
xorwf LATB,F ; Select LED display to be on
btfsc LATB,LATB6
movf Segments+0,W
btfsc LATB,LATB4
movf Segments+1,W
iorwf LATC,F ; Turn on LED segments for selected digit
ISR_EXIT:
movf PCLATH_Save,W
movwf PCLATH
movf STATUS_Save,W
movwf STATUS
swapf WREG_Save,F
swapf WREG_Save,W
RETFIE
MAIN_PROG CODE ; let linker place main program
;
; Setup system clock at 32MHz
;
START:
clrf INTCON
banksel PIE1
clrf PIE1
clrf PIE2
clrf PIE3
clrf PIE4
banksel OPTION_REG
movlw 0xFF
movwf OPTION_REG
banksel OSCCON ; Setup 32MHz system oscillator
movlw 0xF0
movwf OSCCON
;
; Setup GPIO ports
;
banksel LATA
clrf LATA
banksel TRISA
clrf TRISA
bsf TRISA,TRISA2
bsf TRISA,TRISA4
banksel ANSELA
movlw 0xFF
movwf ANSELA
bcf ANSELA,ANSA2 ; Make SWn a digital input
banksel LATB
clrf LATB
banksel TRISB
clrf TRISB
banksel ANSELB
clrf ANSELB
banksel LATC
clrf LATC
banksel TRISC
clrf TRISC
banksel ANSELC
clrf ANSELC
;
; Setup TIMER0 to interrupt every 1.024 milliseconds
; when the system oscillator is 32MHz.
;
banksel OPTION_REG
bcf OPTION_REG,TMR0CS
bcf OPTION_REG,PSA
bcf OPTION_REG,PS0
bcf OPTION_REG,PS1
banksel TMR0
clrf TMR0
bcf INTCON,TMR0IF
bsf INTCON,TMR0IE
;
; Turn on interrupts
;
bsf INTCON,GIE
;
; Setup LED displays to show '1' and '2'
;
movlw 1
call Hex2Segments
movwf Segments+0
movlw 2
call Hex2Segments
movwf Segments+1
;
; Application process loop
;
AppLoop:
GOTO AppLoop
;
; Function: Hex2Segments
;
; Input: WREG = LED segment table lookup index
; Range: 0x00 to 0x0F
;
; Output WREG = 7 low bits are LED segments gfedcba
;
Hex2Segments:
andlw 0x0F
xorlw HIGH(SegTable)
movwf PCLATH
xorlw HIGH(SegTable)
xorwf PCLATH,F
addlw LOW(SegTable)
btfsc STATUS,C
incf PCLATH,F
movwf PCL
SegTable:
RETLW B'00111111' ; mask for digit 0
RETLW B'00000110' ; mask for digit 1
RETLW B'01011011' ; mask for digit 2
RETLW B'01001111' ; mask for digit 3
RETLW B'01100110' ; mask for digit 4
RETLW B'01101101' ; mask for digit 5
RETLW B'01111101' ; mask for digit 6
RETLW B'00000111' ; mask for digit 7
RETLW B'01111111' ; mask for digit 8
RETLW B'01101111' ; mask for digit 9
RETLW B'01110111'
RETLW B'01111100'
RETLW B'00111001'
RETLW B'01011110'
RETLW B'01111001'
RETLW B'01110001'
END
此方法在 ISR 之外进行 HEX 分段查找。
这样可以缩短 ISR 的执行时间。
我没有包含读取 ADC 的函数,但有一个示例说明如何在 LED 显示屏上显示 1 和 2。
我不清楚您正在使用的 7 段显示器的类型以及它们的接线方式,所以我怀疑这段代码是否适用于您的面包板。