首页 > 解决方案 > 如何改进我的 LED 显示屏代码,使其实时反应

问题描述

这是我上一个问题的一个继续问题,我想知道如何改进我的代码,目前在将数字输出到显示器方面存在极大延迟,并且在所有情况下也没有显示预期的输出,即一些段不亮。

请看下面的图片!

正确但延迟的输出 1

正确但延迟的输出 1

正确但延迟的输出 2

正确但延迟的输出 2

正确但延迟的输出 3

正确但延迟的输出 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 

标签: performanceassemblymicrocontrollerpicmicrochip

解决方案


以下是一些可能是不好的做法的问题。

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

我不知道哪些引脚用于多路复用数字,但是您必须在 Temp 寄存器中设置正确的位才能正确地多路复用它们。例如,如果您的 digit1 和 digit0 分别连接到 A1 和 A0,您必须设置第二位 Tempc,2 才能正确复用它们。然后当设置位达到进位时,您必须再次设置它,依此类推。


推荐阅读