PIC16F887 红外接收器,汇编语言

IR receiver with PIC16F887, assembly language

我一直在为学校做一个项目。涉及到PIC汇编编程。我使用 44 针演示板,PIC16F887

我必须做一个红外接收器,它的输出可以改变 LED 亮度。我知道到目前为止我必须为此进行 PWM 控制。但是我仍在努力解码按钮。 我正在使用具有 NEC 编码的发射器。我的输入设置是数字的,通过内部电阻 PORTB 上拉,0。我正在尝试使用 TMR0 中断轮询输入。

系统设置为 4MHz 振荡器,TMR0 每 Osc/4 递增一次。预分频器为 1:2,因此定时器每 2μs 递增 1。Timer0 预加载为 D'206',因此中断 50*2μs = 100μs。根据 NEC 协议,逻辑 0 有 562.5μs 低信号,逻辑 1 有 1687.5μs 低信号,在 562.5 高信号之后。

所以 1687.5μs /100μs = 16,562.5μs / 100μs = 5。我试图从 8 中减去它们。所以我可以检查什么是状态,零位是活动的还是低的。

我不知道我哪里出错了。我将在下面留下我的代码。此代码有一个 LED 闪烁,而如果按下音量 + 按钮,它应该将 PORTD,0 LED 设置为亮起。所以通过这种方式我会知道我是否可以检测到按钮,并在 PWM 上工作。

感谢每个回答。

ISR:                        ;IF ISR GLOBAL INT 0  
    btfss   INTCON,T0IF   
    retfie                  ;if there is no interrupt
    banksel 0               ;ISR occur in Bank0
    movwf   W_save          ;save WORK register's value
    movf    STATUS,W       
    movwf   STATUS_save     ;save STATUS register's value

    call    IR              ;call IR
    goto    ISR_EXIT

ISR_EXIT:
    bcf     INTCON,T0IF     ;TMR0 interrupt flag clear
    movlw   b'01100110'     ;preload 206
    movwf   TMR0

    movf    STATUS_save,W
    movwf   STATUS          ;STATUS register original value reload
    swapf   W_save,f        ;WORK register original value reload
    swapf   W_save,W
    retfie                  ;retfie -> global int = 1

IR:
    btfss   PORTB,0         ;testing IR input
    bsf     ir_reg,0        ;button was pressed

    btfss   ir_reg,0        ;button was pressed?
    goto    NO_BUTTON

    btfsc   PORTB,0         ;HIGH signal?
    goto    HIGH_P

    btfss   ir_reg,1        ;previous was HIGH?
    goto    HIGH_TO_LOW

    incf    time,f          ;increment time
    goto    ISR_EXIT

HIGH_P:
    btfsc   ir_reg,1        ;was previous LOW?
    goto    LOW_TO_HIGH

    incf    time,f          ;increment time

    goto    ISR_EXIT

HIGH_TO_LOW:                ;transition between HIGH to LOW pulses
    bsf     PORTD,3
    movf    time,W     
    movwf   high_pulse      ;saving HIGH pulse's time
    clrf    time            ;time variable clear
    bcf     ir_reg,1        ;previous pulse was HIGH
    goto    CALC

CALC:
    movf    high_pulse,W    ;high pulse's time into Work
    bcf     STATUS,Z        ;STATUS ZERO CLEAR

    sublw   D'10'           ;LOW_P -> 5-10 = -5,HIGH_P - > 16-10 = 6
    btfsc   STATUS,Z        ;if subtraction = +
    bsf     ir_reg,3        ;subtraction ended positive -> LOGIC 1
    bcf     ir_reg,3        ;subtraction ended negative -> LOGIC 0
    goto    ADD_BITS

ADD_BITS:
    bsf     PORTD,2
    btfsc   ir_reg,3        ;if LOGIC 1
    bsf     STATUS,C        ;carry bit 1
    bcf     STATUS,C        ;carry bit 0

    goto    ROTATE

ROTATE:
    bsf     PORTD,1
    rlf     naddress        ;Carry is rotated to naddress LSB
    rlf     address         ;naddress MSB rotated to address LSB through Carry
    rlf     ncommand        ;address MSB rotated to ncommand LSB through Carry
    rlf     command         ;ncommand MSB rotated to command LSB through Carry

    incf    pulses          ;every time we have a rotation increment variable
    movf    pulses,W
    bcf     STATUS,Z        ;status zero clear
    sublw   D'32'           ;33-pulses,we have a decoded signal
    btfss   STATUS,Z        ;if Zero set
    goto    ISR_EXIT        ;goto NO_button
    goto    LED_FLASH

LED_FLASH:
    movf    command,W
    bcf     STATUS,Z
    sublw   b'10101000'     ;+ button command: b'10101000'
    btfss   STATUS,Z
    goto    NO_BUTTON
    bsf     PORTD,0
    goto    ISR_EXIT

LOW_TO_HIGH:                ;transition between LOW to HIGH pulses
    movf    time,W
    movwf   low_pulse       ;saving LOW pulse's time
    clrf    time            ;time variable clear
    bsf     ir_reg,1        ;previous pulse was LOW
    goto    ISR_EXIT

NO_BUTTON:
    btfsc   PORTB,0
    goto    ISR_EXIT

    clrf    pulses          ;clearing variables
    clrf    ir_reg
    clrf    time
    clrf    address
    clrf    naddress
    clrf    address
    clrf    ncommand
    clrf    command
    goto    ISR_EXIT

INIT:
;OSCCON INIT
    banksel OSCCON
    movlw   b'01100000'     ;4Mhz oscillator
    movwf   OSCCON

;OUTPUT INIT
    banksel TRISD
    clrf    TRISD           ;TRISD OUTPUT
    banksel PORTD
    clrf    PORTD           ;PORTD LOW

;INPUT INIT
    banksel TRISB
    bsf     TRISB,RB0       ;RB0 INPUT
    bsf     WPUB,RB0

    movlw   0x00
    banksel ANSELH
    movwf   ANSELH          ;RB0 DIGITAL

    call Delay

;OPTION REG INIT / TMR0
    banksel OPTION_REG
    movlw   b'00000000'     ;TMR0 prescale 1:2 increment every 2us
    movwf   OPTION_REG
    movlw   b'01100110'     ;preload 206
    movwf   TMR0            ;50 tick until overflow 50*2us = 100us

;INTCON INIT
    banksel INTCON
    bcf     INTCON,T0IF     ;TMR0 overflow flag clear
    bsf     INTCON,T0IE     ;TMR0 overflow enable
    bsf     INTCON,GIE      ;global interrupt enable

    return

MAIN:

    call INIT
    call FLASH              ;LED FLASH
    goto $-1
    END

在我的傲慢中,我认为你的任务简单明了。

我大错特错了。我花了三天时间编写和测试一个可以解码 NEC 红外遥控协议的应用程序。

仅仅试图理解在各种网站上发现的所谓文档就存在重大挑战。所有这些都是误导和不完整的。光是整理那堆乱七八糟的东西就花了一整天。

这就是我的工作:

    list n=0,c=255,r=dec    ; Make .LST file look nice
    errorlevel -302         ; Suppress Register in operand not in bank 0 warning.
#define MAIN_ASM
;
; File:     main.asm
; Date:     2020-05-23
; Target:   PIC16F887
; Author:   dan1138
;
; Description:
;   Decoder for NEC Infrared Remote control protocol.
;
;   Physical transport:
;       Long flash  (> 8ms)
;       Pause       (COMMAND when pause is more than 4ms), 
;                   (REPEAT when pause is less than 4ms but greater than 2ms)
;       Short flash (0.5 to 0.6ms)
;     Repeats 32 times:
;       Pause       DATA is one when pause is more than 1ms, else DATA is zero.
;       Short flash (0.5 to 0.6ms)
;
;
;                         PIC16F887
;                 +----------:_:----------+
;       VPP ->  1 : RE3/MCLR/VPP  PGD/RB7 : 40 <> PGD
;           <>  2 : RA0/AN0       PGC/RB6 : 39 <> PGC
;           <>  3 : RA1/AN1      AN13/RB5 : 38 <>
;           <>  4 : RA2/AN2      AN11/RB4 : 37 <>
;           <>  5 : RA3/AN3   PGM/AN9/RB3 : 36 <> 
;           <>  6 : RA4/T0CKI     AN8/RB2 : 35 <> 
;           <>  7 : RA5/AN4      AN10/RB1 : 34 <> 
;           <>  8 : RE0/AN5  INT/AN12/RB0 : 33 <> IR_RECEIVERn
;           <>  9 : RE1/AN6           VDD : 32 <- 5v0
;           <> 10 : RE2/AN7           VSS : 31 <- GND
;       PWR -> 11 : VDD               RD7 : 30 -> LCD_ON
;       GND -> 12 : VSS               RD6 : 29 -> LCD_E
;           -> 13 : RA7/OSC1          RD5 : 28 -> LCD_RW
;           <- 14 : RA6/OSC2          RD4 : 27 -> LCD_RS
;           <> 15 : RC0/SOSCO   RX/DT/RC7 : 26 <>    
;           <> 16 : RC1/SOSCI   TX/CK/RC6 : 25 <>    
;           <> 17 : RC2/CCP1          RC5 : 24 <>
;           <> 18 : RC3/SCL       SDA/RC4 : 23 <>    
;    LCD_D4 <> 19 : RD0               RD3 : 22 <> LCD_D7
;    LCD_D5 <> 20 : RD1               RD2 : 21 <> LCD_D6
;                 +-----------------------:
;                          DIP-40
;
; Include Special Function Register definitions
;
#include "p16f887.inc"
#include "main.inc"
#include "lcd.inc"
;
; PIC16F887 Configuration Bit Settings
; Assembly source line config statements
;
 __CONFIG _CONFIG1, _FOSC_INTRC_NOCLKOUT & _WDTE_OFF & _PWRTE_OFF & _MCLRE_ON & _CP_OFF & _CPD_OFF & _BOREN_OFF & _IESO_ON & _FCMEN_OFF & _LVP_OFF
 __CONFIG _CONFIG2, _BOR4V_BOR21V & _WRT_OFF
;
; Power on reset vector
;
RES_VECT    CODE    0x0000      ; processor reset vector
    pagesel START
    GOTO    START               ; go to beginning of program
;
; Interrupt context save area
;
ISR_DATA    UDATA_SHR
WREG_SAVE   res     1
STATUS_SAVE res     1
PCLATH_SAVE res     1
NEC_IR_State        res 1
NEC_IR_StartFlash   res 1
NEC_IR_CdPause      res 1
;
; Data area for protocol decoder
;
NEC_IR_DATA   UDATA
NEC_IR_RawData      res 4
NEC_IR_Address      res 1
NEC_IR_Command      res 1
NEC_IR_Flags        res 1
#define BIT_NEC_IR_Flags_COMMAND NEC_IR_Flags,0
#define BIT_NEC_IR_Flags_REPEAT  NEC_IR_Flags,1
;
; Interrupt Service Routine
;
ISR_VECT    CODE    0x0004      ; interrgot vector
ISR:
    movwf   WREG_SAVE           ; 
    movf    STATUS,W            ; These register: WREG, STATUS, PCLATH
    movwf   STATUS_SAVE         ; are what, at the minimum, must be saved 
    movf    PCLATH,W            ; and restored on an interrupt.
    movwf   PCLATH_SAVE         ;
    clrf    STATUS              ; Force to memory bank 0
    clrf    PCLATH              ; Force to code page 0
;
; Handle external INT interrupt request
;
    btfsc   INTCON,INTE
    btfss   INTCON,INTF
    goto    INT_End
    bcf     INTCON,INTF
;
; Block flash detection until application loop is done
;
    btfss   BIT_NEC_IR_Flags_COMMAND
    btfsc   BIT_NEC_IR_Flags_REPEAT
    goto    INT_End

    movf    NEC_IR_State,F
    skpz    
    goto    NEC_IR_NextState
;
; Look for initial long flash
;
    clrf    NEC_IR_StartFlash
    clrf    TMR0
    bcf     INTCON,T0IF
MeasureStartFlash:
    btfsc   PORTB,0             ; Skip if flash still on
    goto    EndOfFlash
    btfss   INTCON,T0IF
    goto    MeasureStartFlash
    bcf     INTCON,T0IF
    incfsz  NEC_IR_StartFlash,W
    movwf   NEC_IR_StartFlash
    goto    MeasureStartFlash
EndOfFlash:
    clrf    TMR0
    bcf     INTCON,T0IF
    clrf    NEC_IR_CdPause
    movlw   8
    subwf   NEC_IR_StartFlash,W
    btfss   STATUS,C            ; Skip if count equal or greater than 8 T0IF ticks
    goto    INT_End
;
; Measure pause after flash
;
MeasurePause:
    btfss   PORTB,0             ; Skip if flash still off
    goto    EndOfPause
    btfss   INTCON,T0IF
    goto    MeasurePause
    bcf     INTCON,T0IF
    incfsz  NEC_IR_CdPause,W
    movwf   NEC_IR_CdPause
    goto    MeasurePause
EndOfPause:
    btfss   PORTB,0             ; Skip when flash goes off
    goto    EndOfPause
    clrf    TMR0
    bcf     INTCON,T0IF
    bcf     INTCON,INTF
    movlw   4
    subwf   NEC_IR_CdPause,W
    btfsc   STATUS,C            ; Skip if count less than 4 T0IF ticks
    goto    ReceiveCommandState
    banksel NEC_IR_Flags
    bsf     BIT_NEC_IR_Flags_REPEAT ; Assert this is a REPEAT event
    goto    INT_End
ReceiveCommandState:
    movlw   d'32'
    movwf   NEC_IR_State      ; Advnace to state 32 when we expect ADDRESS/COMMAND data
INT_End:
;
    movf    PCLATH_SAVE,W       ;
    movwf   PCLATH              ; Restore the saved context of the
    movf    STATUS_SAVE,W       ; interrupted execution.
    movwf   STATUS              ;
    swapf   WREG_SAVE,F         ;
    swapf   WREG_SAVE,W         ;
    retfie                      ; Exit ISR and enable the interrupts.
;
; Receive COMMAND or REPEAT event
;
NEC_IR_NextState:
    banksel PORTB
    bcf     STATUS,C
    btfsc   INTCON,T0IF
    bsf     STATUS,C
    banksel NEC_IR_RawData
    rlf     NEC_IR_RawData,F
    rlf     NEC_IR_RawData+1,F
    rlf     NEC_IR_RawData+2,F
    rlf     NEC_IR_RawData+3,F
EndOfBit:
    banksel PORTB
    btfss   PORTB,0             ; Skip when flash goes off
    goto    EndOfBit
    clrf    TMR0
    bcf     INTCON,T0IF
    decf    NEC_IR_State,F
    btfss   STATUS,Z
    goto    INT_End
;
; Validate ADDRESS and COMMAND
;
    banksel NEC_IR_RawData
    comf    NEC_IR_RawData,W
    xorwf   NEC_IR_RawData+1,W
    btfss   STATUS,Z
    goto    INT_End
    comf    NEC_IR_RawData+2,W
    xorwf   NEC_IR_RawData+3,W
    btfss   STATUS,Z
    goto    INT_End

    movf    NEC_IR_RawData+1,W
    movwf   NEC_IR_Command
    movf    NEC_IR_RawData+3,W
    movwf   NEC_IR_Address
    bsf     BIT_NEC_IR_Flags_COMMAND
    goto    INT_End
;
; Initialize the PIC hardware
;
START:
    clrf    INTCON              ; Disable all interrupt sources
    banksel BANK1
    clrf    PIE1
    clrf    PIE2

    movlw   b'01100000'
    movwf   OSCCON              ; Set internal oscillator at 4MHz

    movlw   b'10000001'         ; Pull-ups off, INT edge high to low, WDT prescale 1:1
    movwf   OPTION_REG          ; TMR0 clock edge low to high, TMR0 clock = FCY, TMR0 prescale 1:4
                                ; TIMER0 will assert the overflow flag every 256*4 (1024)
                                ; instruction cycles, with a 4MHz oscilator this ia 1.024 milliseconds.

    movlw   b'11111111'         ;
    movwf   TRISA

    movlw   b'11111111'         ;
    movwf   TRISB

    movlw   b'11111111'         ;
    movwf   TRISC

    movlw   b'11111111'         ;
    movwf   TRISD

    ; Set all ADC inputs for digital I/O
    banksel BANK3
    movlw   b'00000000'
    movwf   ANSEL
    movlw   b'00000000'
    movwf   ANSELH
    banksel BANK2
    clrf    CM1CON0             ; turn off comparator
    clrf    CM2CON0             ; turn off comparator
    banksel BANK1
    movlw   b'00000000'
    movwf   ADCON1
    clrf    VRCON               ; turn off voltage reference
    banksel BANK0
    movlw   b'10000000'
    movwf   ADCON0

    pagesel main
    goto    main
;
; Main data
;
MAIN_DATA   UDATA
RepeatCount res     1
;
; Main application code
;
MAIN_PROG   CODE
; 
; Main application initialization
;
main:
    lcall   OpenXLCD

    movlw   LINE_ONE
    lcall   SetDDRamAddr

    movlw   LOW(LCD_message1)
    movwf   pszLCD_RomStr
    movlw   HIGH(LCD_message1)
    movwf   pszLCD_RomStr+1
    lcall   putrsXLCD

    banksel NEC_IR_Flags
    clrf    NEC_IR_Flags
    clrf    NEC_IR_State
    bcf     BIT_NEC_IR_Flags_COMMAND
    bcf     BIT_NEC_IR_Flags_REPEAT
    bcf     INTCON,INTF
    bsf     INTCON,INTE
    bsf     INTCON,GIE
;
; Application process loop
;
AppLoop:
    movf    NEC_IR_Flags,F      ; Check for event
    btfsc   STATUS,Z            ; Skip if any event bit set
    GOTO    AppLoop             ;

    banksel NEC_IR_Flags
    btfsc   BIT_NEC_IR_Flags_REPEAT ; skip of not a REPEAT event
    goto    IncrementCount
    banksel RepeatCount
    clrf    RepeatCount
;
; Increment repeat count
;
IncrementCount:
    banksel RepeatCount
    incfsz  RepeatCount,W
    movwf   RepeatCount

;
; Show measurement for Start Of Transmission (SOT) flash
;
    movlw   LINE_TWO
    lcall   SetDDRamAddr
    movf    NEC_IR_StartFlash,W
    lcall   PutDecXLCD
;
; Show measurement for pause after SOT flash
;
    movlw   ' '
    lcall   WriteDataXLCD
    movf    NEC_IR_CdPause,W
    lcall   PutDecXLCD
;
; Show decoded ADDRESS and COMMAND
;
    movlw   ' '
    lcall   WriteDataXLCD
    banksel NEC_IR_Address
    movf    NEC_IR_Address,W
    lcall   PutHexXLCD
    banksel NEC_IR_Command
    movf    NEC_IR_Command,W
    lcall   PutHexXLCD
;
; Show REPEAT count
;
    movlw   ' '
    lcall   WriteDataXLCD
    banksel RepeatCount
    movf    RepeatCount,W
    lcall   PutHexXLCD
;
; Clear event flags to enable capture of next event
;
    banksel NEC_IR_Flags
    clrf    NEC_IR_Flags

    lgoto   AppLoop
;
; LCD messages
;
MAIN_CONST   code
LCD_message1:
    dt  "NEC IR Decode v0",0
    END

我已经用汇编语言为 8 位 PIC 控制器编写了很多代码。该应用程序包含一些非常微妙的技巧。其中没有足够的评论来解释它是如何工作的以及为什么工作。你只需要自己分析一下。

NEC IR 协议的诀窍在于它完全是关于 IR 闪光之间的停顿。如果网上有关于NEC IR 控制协议的准确、完整和易懂的描述我找不到了。

我原以为可以教您如何编写此代码,但我认为我还不够好,无法做到这一点。我只希望这可以作为你的例子。