Přerušení od RB4-7 na 16F877A (reakce na impulsy z enkodéru)

Ahoj,

mám trochu trable s přerušením od pinů portu B při změně. Nejsem si jistý, jestli to mám špatně napsané, nebo jestli je to špatně přeložené, každopádně to nefunguje.

Část kódu, který chci použiít ve svém programu přikládám (je to jen obsluha přerušení a vypsání na LCD + deklarace a nastavení). Mělo by to v přerušení načítat impulsy z kvadraturního enkodéru a vypsat výsledek na displej:

[code]Define CONF_WORD = 0x3f72
Define CLOCK_FREQUENCY = 12

Define LCD_BITS = 4
Define LCD_DREG = PORTD
Define LCD_DBIT = 4
Define LCD_RSREG = PORTC
Define LCD_RSBIT = 0
Define LCD_EREG = PORTC
Define LCD_EBIT = 2
Define LCD_RWREG = PORTC
Define LCD_RWBIT = 1
Define LCD_COMMANDUS = 5000
Define LCD_DATAUS = 100
Define LCD_INITMS = 100
'Define LCD_READ_BUSY_FLAG = 1

OPTION_REG.7 = 1

TRISD = 0x00

AllDigital

Symbol ra_a = PORTB.5
Symbol ra_b = PORTB.2

Dim ra_a_last As Bit
Dim ra_b_last As Bit

Dim ra_cw As Bit

Dim ra_enc As Word
ra_enc = 7100

ra_a_last = ra_a
ra_b_last = ra_b

INTCON.GIE = 1 'enable all un-masked interrupts
INTCON.RBIE = 1 'enable RB0/INT interrupts

Lcdinit

loop:

Lcdcmdout LcdClear
Lcdout “X”, #ra_enc
WaitMs 500

Goto loop

End

On Interrupt 'Rutina preruseni

If PORTB.5 <> ra_a_last Then
If PORTB.5 = PORTB.2 Then
ra_enc = ra_enc - 1
ra_cw = 0
Else
ra_enc = ra_enc + 1
ra_cw = 1

Endif

Endif

ra_a_last = PORTB.5
ra_b_last = PORTB.2

INTCON.RBIF = 0

Resume [/code]

Přeložené (a vlastně celé psané) je to v PIC Simulator IDE:

; Compiled with: PIC Simulator IDE v6.86 ; Microcontroller model: PIC16F877A ; Clock frequency: 12.0 MHz ; ; The address of 'ra_a' (bit) (global) is 0x6,5 ; The address of 'ra_b' (bit) (global) is 0x6,2 ; The address of 'ra_a_last' (bit) (global) is 0x2C,0 ; The address of 'ra_b_last' (bit) (global) is 0x2C,1 ; The address of 'ra_cw' (bit) (global) is 0x2C,2 ; The address of 'ra_enc' (word) (global) is 0x2D ra_enc EQU 0x2D ; Begin R0L EQU 0x20 R0H EQU 0x21 R1L EQU 0x22 R1H EQU 0x23 R2L EQU 0x24 R2H EQU 0x25 R3L EQU 0x26 R3H EQU 0x27 R4L EQU 0x28 R4H EQU 0x29 R5L EQU 0x2A R5H EQU 0x2B W_TEMP EQU 0x7F STATUS_TEMP EQU 0x7E ORG 0x0000 BCF PCLATH,3 BCF PCLATH,4 GOTO L0002 ORG 0x0004 MOVWF W_TEMP SWAPF STATUS,W CLRF STATUS MOVWF STATUS_TEMP CALL L0003 SWAPF STATUS_TEMP,W MOVWF STATUS SWAPF W_TEMP,F SWAPF W_TEMP,W RETFIE ; Begin of program L0002: ; 1: Define CONF_WORD = 0x3f72 ; 2: Define CLOCK_FREQUENCY = 12 ; 3: ; 4: Define LCD_BITS = 4 ; 5: Define LCD_DREG = PORTD ; 6: Define LCD_DBIT = 4 ; 7: Define LCD_RSREG = PORTC ; 8: Define LCD_RSBIT = 0 ; 9: Define LCD_EREG = PORTC ; 10: Define LCD_EBIT = 2 ; 11: Define LCD_RWREG = PORTC ; 12: Define LCD_RWBIT = 1 ; 13: Define LCD_COMMANDUS = 5000 ; 14: Define LCD_DATAUS = 100 ; 15: Define LCD_INITMS = 100 ; 16: 'Define LCD_READ_BUSY_FLAG = 1 ; 17: ; 18: OPTION_REG.7 = 1 BSF STATUS,RP0 BSF 0x01,7 ; 19: ; 20: TRISD = 0x00 CLRF 0x08 ; 21: ; 22: AllDigital MOVLW 0x06 MOVWF 0x1F MOVLW 0x07 MOVWF 0x1C BCF STATUS,RP0 ; 23: ; 24: Symbol ra_a = PORTB.5 ; 25: Symbol ra_b = PORTB.2 ; 26: ; 27: Dim ra_a_last As Bit ; 28: Dim ra_b_last As Bit ; 29: ; 30: Dim ra_cw As Bit ; 31: ; 32: Dim ra_enc As Word ; 33: ra_enc = 7100 MOVLW 0xBC MOVWF 0x2D MOVLW 0x1B MOVWF 0x2E ; 34: ; 35: ; 36: ra_a_last = ra_a BTFSC 0x06,5 BSF 0x2C,0 BTFSS 0x06,5 BCF 0x2C,0 ; 37: ra_b_last = ra_b BTFSC 0x06,2 BSF 0x2C,1 BTFSS 0x06,2 BCF 0x2C,1 ; 38: ; 39: INTCON.GIE = 1 'enable all un-masked interrupts BSF 0x0B,7 ; 40: INTCON.RBIE = 1 'enable RB0/INT interrupts BSF 0x0B,3 ; 41: ; 42: Lcdinit BCF 0x07,2 BCF 0x07,0 BCF 0x07,1 BSF STATUS,RP0 BCF 0x07,2 BCF 0x07,0 BCF 0x07,1 MOVLW 0x0F ANDWF 0x08,F BCF STATUS,RP0 MOVLW 0x64 MOVWF R0L MOVLW 0x00 MOVWF R0H CALL W001 MOVLW 0x33 CALL LC02 MOVLW 0x33 CALL LC02 MOVLW 0x33 CALL LC02 MOVLW 0x22 CALL LC02 MOVLW 0x28 CALL LC02 MOVLW 0x0C CALL LC02 MOVLW 0x01 CALL LC02 ; 43: ; 44: loop: L0001: ; 45: ; 46: Lcdcmdout LcdClear MOVLW 0x01 CALL LC02 ; 47: Lcdout "X", #ra_enc MOVLW 0x58 CALL LC01 MOVF 0x2D,W MOVWF R2L MOVF 0x2E,W MOVWF R2H CALL LC21 ; 48: WaitMs 500 MOVLW 0xF4 MOVWF R0L MOVLW 0x01 MOVWF R0H CALL W001 ; 49: ; 50: Goto loop GOTO L0001 ; 51: ; 52: End L0004: GOTO L0004 ; 53: ; 54: On Interrupt 'Rutina preruseni L0003: ; 55: ; 56: If PORTB.5 <> ra_a_last Then CLRW BTFSC 0x06,5 ADDLW 0x01 BTFSC 0x2C,0 SUBLW 0x01 BTFSC STATUS,Z GOTO L0005 ; 57: If PORTB.5 = PORTB.2 Then CLRW BTFSC 0x06,5 ADDLW 0x01 BTFSC 0x06,2 SUBLW 0x01 BTFSS STATUS,Z GOTO L0006 ; 58: ra_enc = ra_enc - 1 MOVLW 0x01 SUBWF 0x2D,W MOVWF 0x2D MOVLW 0x00 BTFSS STATUS,C ADDLW 0x01 SUBWF 0x2E,W MOVWF 0x2E ; 59: ra_cw = 0 BCF 0x2C,2 ; 60: Else GOTO L0007 L0006: ; 61: ra_enc = ra_enc + 1 MOVF 0x2D,W ADDLW 0x01 MOVWF 0x2D MOVF 0x2E,W BTFSC STATUS,C ADDLW 0x01 ADDLW 0x00 MOVWF 0x2E ; 62: ra_cw = 1 BSF 0x2C,2 ; 63: ; 64: Endif L0007: ; 65: Endif L0005: ; 66: ; 67: ra_a_last = PORTB.5 BTFSC 0x06,5 BSF 0x2C,0 BTFSS 0x06,5 BCF 0x2C,0 ; 68: ra_b_last = PORTB.2 BTFSC 0x06,2 BSF 0x2C,1 BTFSS 0x06,2 BCF 0x2C,1 ; 69: ; 70: INTCON.RBIF = 0 BCF 0x0B,0 ; 71: ; 72: Resume RETURN ; End of program L0008: GOTO L0008 ; Division Routine D001: MOVLW 0x10 MOVWF R3L CLRF R2H CLRF R2L D002: RLF R0H,W RLF R2L,F RLF R2H,F MOVF R1L,W SUBWF R2L,F MOVF R1H,W BTFSS STATUS,C INCFSZ R1H,W SUBWF R2H,F BTFSC STATUS,C GOTO D003 MOVF R1L,W ADDWF R2L,F MOVF R1H,W BTFSC STATUS,C INCFSZ R1H,W ADDWF R2H,F BCF STATUS,C D003: RLF R0L,F RLF R0H,F DECFSZ R3L,F GOTO D002 MOVF R0L,W RETURN ; Waitms Routine W001: MOVF R0L,F BTFSC STATUS,Z GOTO W002 CALL W003 DECF R0L,F NOP NOP NOP NOP NOP GOTO W001 W002: MOVF R0H,F BTFSC STATUS,Z RETURN CALL W003 DECF R0H,F DECF R0L,F GOTO W001 W003: MOVLW 0x0C MOVWF R2H W004: DECFSZ R2H,F GOTO W004 NOP NOP MOVLW 0x3A MOVWF R1L W005: DECFSZ R1L,F GOTO W006 CALL W007 CALL W007 NOP NOP RETURN W006: CALL W007 GOTO W005 W007: MOVLW 0x0D MOVWF R2L W008: DECFSZ R2L,F GOTO W008 NOP RETURN ; Waitus Routine - Byte Argument X001: MOVLW 0x04 SUBWF R4L,F BTFSS STATUS,C RETURN GOTO X002 X002: MOVLW 0x02 SUBWF R4L,F BTFSS STATUS,C RETURN GOTO X002 ; Waitus Routine - Word Argument Y001: MOVLW 0x06 SUBWF R4L,F CLRW BTFSS STATUS,C ADDLW 0x01 SUBWF R4H,F BTFSS STATUS,C RETURN GOTO Y002 Y002: MOVLW 0x04 SUBWF R4L,F CLRW BTFSS STATUS,C ADDLW 0x01 SUBWF R4H,F NOP NOP BTFSS STATUS,C RETURN GOTO Y002 ; Lcdout Routine LC01: MOVWF R4L BSF 0x07,0 BCF 0x07,1 MOVLW 0x0F ANDWF 0x08,F MOVF R4L,W ANDLW 0xF0 IORWF 0x08,F CALL LCX1 SWAPF R4L,F MOVLW 0x0F ANDWF 0x08,F MOVF R4L,W ANDLW 0xF0 IORWF 0x08,F CALL LCX1 MOVLW 0x64 MOVWF R4L CALL X001 RETURN LCX1: BSF 0x07,2 NOP NOP NOP BCF 0x07,2 NOP NOP NOP RETURN ; Lcdcmdout Routine LC02: MOVWF R4L BCF 0x07,0 BCF 0x07,1 MOVLW 0x0F ANDWF 0x08,F MOVF R4L,W ANDLW 0xF0 IORWF 0x08,F CALL LCX1 SWAPF R4L,F MOVLW 0x0F ANDWF 0x08,F MOVF R4L,W ANDLW 0xF0 IORWF 0x08,F CALL LCX1 MOVLW 0x88 MOVWF R4L MOVLW 0x13 MOVWF R4H CALL Y001 RETURN ; Lcdout Decimal Conversion Routine LC21: BSF R3H,7 MOVLW 0x27 MOVWF R1H MOVLW 0x10 CALL LC22 MOVLW 0x03 MOVWF R1H MOVLW 0xE8 CALL LC22 CLRF R1H MOVLW 0x64 CALL LC22 CLRF R1H MOVLW 0x0A CALL LC22 MOVF R2L,W GOTO LC23 LC22: MOVWF R1L MOVF R2H,W MOVWF R0H MOVF R2L,W MOVWF R0L CALL D001 MOVF R0L,W BTFSS STATUS,Z BCF R3H,7 BTFSC R3H,7 RETURN LC23: ADDLW 0x30 CALL LC01 RETURN ; Configuration word settings ORG 0x2007 DW 0x3F72 ; End of listing END
Vím, že to není ideální, ale jako věčnému začátečníkovi vyhovuje možnost rovnou to odsimulovat… A v Basicu se mi píše dobře, nic jiného neznám…

Netušil by někdo, co v tom mám za botu? Můžu ukroutit osičku enkodéru a ani se to nehne. Zkoušel jsem to všelijak trápit, ale výsledkem bylo, že se buď nic nedělo, nebo že přerušení uvízlo ve smyčce…
Moc díky, i za nakopnutí správným směrem.
Martin

:arrow_right: administrator: přejmenováno z "Přerušení od RB4-7 na 16F877A"

Sice se v tom programu moc nevyznám, ale při přerušení od PORTuB musíš přečíst PORTB. Většinou bývá problém v tomto.

A nemáš chybu tady? INTCON.RBIE = 1 'enable RB0/INT interrupts

INTCON.RBIE by měl povolit přerušení při změně stabu portu B (bity 4-7). To je myslím v pořádku (enkodér mám pinem A připojený na PORTB.5, pin B zatím neřeším).
Co se týká čtení portu B v přerušení - něco jsem tušil a vyčetl, proto jsem zkusil zadat přímo If PORTB.5 <> ra_a_last Then… (místo symbolů - myslel jsem, že tím bude port B přečtený…)
Stačilo by načíst stav portu B třeba do nějaké, i nepoužité proměnné? Např.

Dim b As Byte
.
.
.
OnInt…

b = PORTB
.
.
Díky

Port přečteš jedině MOVF PORTB,w
Musíš ho přečíst, ale nemusíš ho nikam ukládat. Až ho přečteš, vynuluj příznak od přerušení.

Tím enkoderem myslíš něco takového? Pro tohle mám napsanej program.

Jj, v podstate je to stejne, vystupy A a B, o 90° posunuta faze. Ten muj ma teda 1000 impulsu na otacku a navic vystup Z (index), ale princip stejny - s tim, ze ja nemusim osetrovat prekmity, protoze jsou osetreny elektronikou enkoderu. Je to OMRON E6C2-CWZ6C.
Pokud mas neco napsaneho, myslis, ze by jsi mi mohl poskytnout kousek kodu? (preruseni, nastaveni registru preruseni, pripadne i dekodovani A a B vystupu, pro kontrolu, jestli to mam dobre)
Diky

Zákmity jsem vyřešil kondikama 100n. O zbytek se stará program.[code];preruseni od RB
btfsc INTCON,RBIF ; preruseni od RB4-7
goto IF_RB ; ANO
;preruseni od TMR0
btfsc INTCON,T0IF ; preruseni od TMR0
goto IF_TMR0 ; ANO
;ne,tak je tu vir
goto INT_END
;****************************************************************************
;Preruseni od TMR0
;****************************************************************************
;preruseni a vykonani od TMR0
IF_TMR0 ; ANO
bcf INTCON,T0IF ;nulovani priznaku preruseni

; TMR1_ROT1
; TMR2_ROT1
;test rotacaku
IF_TMR0_ROT1
;#DEFINE R_A1 PORTB,4 ; Vstup A2
;#DEFINE R_B1 PORTB,3 ; Vstup B2
IF_TMR0_ROT1_TEST
btfss UZ1
bra IF_TMR0_END
btfss R_B1
incf Speed_1
btfsc R_B1
decf Speed_1
;test na <0
movlw .255 ; test na 255, mensi jak 0?
xorwf Speed_1,w
btfsc STATUS,Z
clrf Speed_1 ; ano, vynuluj
;test na >100
movlw .101 ; test na 101, vetsi jak 100
xorwf Speed_1,w ; porovnej s registrem
btfss STATUS,Z
bra $+6
movlw .100 ; ano, dej 100
movwf Speed_1 ; porovnej s registrem
bcf T0CON,TMR0ON
bcf UZ1
bra IF_TMR0_END

IF_TMR0_END
goto INT_END

;****************************************************************************
;Preruseni od RBIF - RB4 - 7
; Cteni rotacaku, otocny rotacni cosi
;****************************************************************************
IF_RB
movf PORTB,w ; precist PORTB, jinak nejde vynulaovat RBIF
movwf PORTB_Flag
bcf INTCON,RBIF ; vynulovat priznak preruseni (pretekl casovac TMR)
btfsc R_A1
bra IF_RB_END
movlw .100 ;prednastaveni
movwf TMR0L ;na 2.5
bcf INTCON,T0IF ;nulovani priznaku preruseni
bsf T0CON,TMR0ON
bsf UZ1
bra IF_RB_END

IF_RB_END

bra		INT_END[/code]

Ale je to psané pro 18F, tak místo BRA si to přepiš na GOTO.A $+xy vyděl 2. Nastav si přerušení tak, aby bylo co 2,5ms.

Snad jsem na nic nezapoměl :slight_smile:

Tak, tomuhle vubec nerozumim… ASM mi do hlavy vubec nejde a jsem dost tupej se to nekdy naucit. Je to na mne moc logicke. Ten Basic je preci jen pochopitelnejsi.
Pokusim se tim trochu prokousat, ale uz ted tusim, ze preruseni casovou smyckou nebude pro muj ucel to prave.
Ve finale budu totiz potrebovat obsluhovat ty enkodery dva a trochu se bojim, ze takhle by to PIC nestihal obsluhovat a ztracel by impulsy. Potrebuju hlidat presnou polohu a ztrata impulsu by mne hodne mrzela. I proto jsem to resil prerusenim od encoderu, kdy vsechno ostatni potlacim a necham pocitat pulsy. Displej, vypocty a seriovy port si proste pockaji, az se dopocitaji pulsy.
No, hodne jsem toho pocetl na netu (prokousavam se tim uz 2 dny, nez ze sebe udelam vola tady), tak jeste budu hodne cist dal…

Ono ty rotáčaky tam původně byly 2, ale jeden jsem smazal, aby tě to nepletlo. A čeká na impulz na PORTB, pak spustí TMR0 a odpočíta 2,5ms. A jako celej program stíhá obsluhovat LCD, jestě krmit posuvné registry atd.

Trochu me plete, ze v simulatoru to jede tak, jak ma a po nahrati do PICu je to zase mrtve. Za chvili asi prekrocim povoleny pocet prepsani programu :frowning:
Trochu jsem to zprehazel, abych se priblizil tomu tvemu kodu, ale nic…

[code]; 56: On Interrupt 'Rutina preruseni
L0003:
; 57:
; 58: 'If INTCON.RBIF = 1 Then
; 59: b = PORTB
MOVF 0x06,W
MOVWF 0x2F
; 60: INTCON.RBIF = 0
BCF 0x0B,0
; 61:
; 62: If PORTB.5 <> ra_a_last Then
CLRW
BTFSC 0x06,5
ADDLW 0x01
BTFSC 0x2C,0
SUBLW 0x01
BTFSC STATUS,Z
GOTO L0005
; 63: If PORTB.5 = PORTB.2 Then
CLRW
BTFSC 0x06,5
ADDLW 0x01
BTFSC 0x06,2
SUBLW 0x01
BTFSS STATUS,Z
GOTO L0006
; 64: ra_enc = ra_enc - 1
MOVLW 0x01
SUBWF 0x2D,W
MOVWF 0x2D
MOVLW 0x00
BTFSS STATUS,C
ADDLW 0x01
SUBWF 0x2E,W
MOVWF 0x2E
; 65: Else
GOTO L0007
L0006:
; 66: ra_enc = ra_enc + 1
MOVF 0x2D,W
ADDLW 0x01
MOVWF 0x2D
MOVF 0x2E,W
BTFSC STATUS,C
ADDLW 0x01
ADDLW 0x00
MOVWF 0x2E
; 67: Endif
L0007:
; 68: Endif
L0005:
; 69:
; 70: ra_a_last = PORTB.5
BTFSC 0x06,5
BSF 0x2C,0
BTFSS 0x06,5
BCF 0x2C,0
; 71: ra_b_last = PORTB.2
BTFSC 0x06,2
BSF 0x2C,1
BTFSS 0x06,2
BCF 0x2C,1
; 72:
; 73:
; 74: Resume
RETURN

[/code]

Tak v tomhle se zase nevyznám já :slight_smile:

v Basicu je to takhle: (jeste jsem odebral nepodstatne a zmenil i ASM nahore)

[code]On Interrupt 'Rutina preruseni

'If INTCON.RBIF = 1 Then
b = PORTB
INTCON.RBIF = 0

If PORTB.5 <> ra_a_last Then
If PORTB.5 = PORTB.2 Then
ra_enc = ra_enc - 1
Else
ra_enc = ra_enc + 1
Endif
Endif

ra_a_last = PORTB.5
ra_b_last = PORTB.2

Resume [/code]

Honzo,
moc diky za Tvoje rady, ale priste mi pls hned ze zacatku napis, ze jsem vul a mam to blbe zapojene!!!
Chjo, se s tim vocasim 2 dny a nakonec zjistim, ze interni pull-up odpory asi stacit nebudou a 2x 10 kilo vyresi vse.
Jezis, to jsem si zase nabehnul… Aspon, ze existujou ty fora, ofi Microchip me nakoplo.

Edit: Nerad bych, aby to vyznelo jako nejaka nespokojenost s Tvou pomoci, pomohl jsi mi hodne moc. Donutil jsi me premyslet a zamerit se na spravnou vec. Velky dik :slight_smile: Jeste odstranit blikani displeje pri nacitani enkoderu a par drobnosti ve vypoctech a je hotovo.