如何改进我的 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 段显示器的类型以及它们的接线方式,所以我怀疑这段代码是否适用于您的面包板。