Od pewnego czasu pojawiają się ulepszone wersje µC. Nowa bryza przyniosła też nowe funkcjonalności. Jedną z nich są asynchroniczne przerwania zewnętrzne pod nazwą PCINT. Fajna sprawa gdy µC nie posiada wielu wyprowadzeń z funkcjonalnością INT, ale …
Funkcjonalność PCINT posiada jedynie cechy asynchronicznego przerwania INT, znaczy to – działa tylko przy zmianie stanu z wysokiego na niski i odwrotnie. Zasadniczo w wielu przypadkach będzie to wystarczające a wręcz możliwość zastosowania takiej akcji zbawienna. I fajnie, ale … jak środowisko Bascom AVR wspiera nową funkcjonalność? Doszły jakieś nowe dyrektywy? coś się zmieniło w składni?
Otóż niespecjalnie.
Trochę ględzenia.
Środowisko Bascom AVR samo w sobie umożliwia dokonanie konfiguracji peryferiów µC samodzielnie. Jeśli ktoś posiada stosowną wiedzę i chęć wcale nie musi używać funkcji wbudowanych temu służących.
W temacie tym po trosze jest podobnie. A to dlatego, że sama konfiguracja nowych przerwań µC nie wymaga specjalnych zabiegów. Postępujemy znaną drogą. uczynniamy przerwania globalne (modyfikacja SREG – Status Register), uczynniamy wybrane przerwanie PCINT (modyfikacja GIMSK – General Interrupt Mask Register), i w tym momencie trafiamy na coś nowego 😛
Jeżeli chcemy by przerwanie wyzwalane było jedynie przez zmianę stanu logicznego na wybranym pinie, potrzebny jest dodatkowy zabieg. W przeciwnym razie zmiana stanu dowolnego pinu, przyporządkowanemu wybranemu przerwaniu nie spowoduje dokładnie NIC 😉 . I tak, selekcja aktywnych pinów dla przerwania, polega na ustawieniu odpowiednich wartości w rejestrze PCMSK. Inaczej mówiąc – założeniu maski na wybrane piny. Tym samym przerwanie może być wzbudzane przez większą ilość pinów niż jeden, jeżeli tego oczekujemy. Super funkcjonalność
Suche fakty.
Funkcjonalność ciekawa i nie skomplikowana w używaniu. Ale by się nią cieszyć trzeba się trochę napocić. Sytuacja taka występuje ponieważ dane potrzebne do uczynnienia są potraktowane trochę po macoszemu przez producenta, i pochowane w notkach katalogowych. Najczęściej najbardziej wymowny jest rysunek z opisem pinów, jeśli naniesiono przy nim jawnie przyporządkowane znaczniki PCINTx. W przeciwnym wypadku pomocna może być tabela I/O Multiplexing z noty katalogowej wybranego µC.
Skąd taka rozbieżność w podawaniu informacji … licho wie. Powyżej wycinek z nowszej wersji notki katalogowej, a nad nim ze starszej, dla różnych µC. W dodatku ten nowszy wzór wydaje się być niepełny – zawierać niekompletne informacje – brak opisu dla INT1. 😯
Dla nas istotne są informacje:
• µC może posiadać do 3 przerwań PCINTx dla wybranych portów IO, przy czym numerowane są one rosnąco dla kolejnych liter przyporządkowanych portom. Np Port B zasadniczo wyzwala przerwanie PCINT0. W mikrokontrolerach z dużą liczbą wyprowadzeń warto dobrze pozaglądać w czym rzecz by nie popełnić błędu, gdyż kolejne porty nie muszą posiadać omawianej funkcjonalności, w MCU ATmega640/1280/1281/2560/2561 są to porty PB, PJ, PK,
• piny przyporządkowane do danego portu, występować mogą w różnej ilości, zależnie od typu µC, tym samym ilość znaczników w rejestrze PCMSK nie zawsze musi być równa 8,
• znaczniki PCINTy numerowane są globalnie bez względu na przynależność do portu, oczywiście z zachowaniem chronologii. To znaczy, startujemy od liczby zero i od portu oznaczonego literą najbliższą początkowi alfabetu, następnie zwiększamy licznik w miarę zliczania kolejnych pinów portu, przechodząc do kolejnego portu kontynuujemy liczenie dalej itd … W przypadku pinu nie wyprowadzonego fizycznie w porcie, licznik zwiększamy licząc nie występującą pozycję.
Ufff chyba nie zawikłałem. Sytuacja może wydawać się zagmatwana z tytułu identycznego nazewnictwa dla przerwań i znaczników, np przerwanie PCINT0, PCINT1, PCINT2, znacznik PCINT0, PCINT1, PCINT2, PCINT3 … PCINT22, PCINT23. A wszystko przez Atmel-a 😉
Jeśli będziemy o pamiętać o tych kilku rzeczach, będzie łatwiej i być może unikniemy błędu.
.
PCINT Bascom w praktyce.
Dla zobrazowania, gdy zaglądniemy do pliku definicji dowolnego µC ze środowiska Bascom AVR, zobaczymy w czym rzecz. Ja wybrałem sobie ATtiny13A i dla porównania ATmega328.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
PCINT5 = 5 ;PCMSK PCINT4 = 4 PCINT3 = 3 PCINT2 = 2 PCINT1 = 1 PCINT0 = 0 [INTS] INT0 = $001 ;External Interrupt0 Vector Address PCINT0= $002 ;PIN change interrupt OVF0 = $003 ;timer/counter overflow ERDY = $004 ;EEPROM Write Complete Handle ACI = $005 ;Analog Comparator Interrupt Vector Address OC0A = $006 ;Output Compare0A Interrupt Vector Address OC0B = $007 ;Output Compare0B Interrupt Vector Address WDT = $008 ;watchdog ADCC = $009 ;ADC Conversion Complete Handle ; LAINTS:INT0 [INTLIST] count=9 INTname1=INT0,$001,GIMSK.INT0,GIFR.INTF0 INTname2=PCINT0,$002,GIMSK.PCIE,GIFR.PCIF INTname3=OVF0@TIMER0,$003,TIMSK0.TOIE0,TIFR0.TOV0 INTname4=ERDY,$004,EECR.EERIE INTname5=ACI,$005,ACSR.ACIE,ACSR.ACI INTname6=OC0A@COMPARE0A,$006,TIMSK0.OCIE0A,TIFR0.OCF0A INTname7=OC0B@COMPARE0B,$007,TIMSK0.OCIE0B,TIFR0.OCF0B INTname8=WDT@WATCHDOG,$008,WDTCR.WDTIE,WDTCSR.WDIF INTname9=ADCC@ADC,$009,ADCSRA.ADIE,ADCSR.ADIF |
powyżej wycinki dla ATtiny13A, plik ATtiny13A.DAT
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 |
PCINT23 = 7 ; PCMSK2 PCINT22 = 6 PCINT21 = 5 PCINT20 = 4 PCINT19 = 3 PCINT18 = 2 PCINT17 = 1 PCINT16 = 0 PCINT14 = 6 ; PCMSK1 PCINT13 = 5 PCINT12 = 4 PCINT11 = 3 PCINT10 = 2 PCINT9 = 1 PCINT8 = 0 PCINT7 = 7 ; PCMSK0 PCINT6 = 6 PCINT5 = 5 PCINT4 = 4 PCINT3 = 3 PCINT2 = 2 PCINT1 = 1 PCINT0 = 0 [INTS] INT0=$002 ; External Interrupt0 Vector Address INT1=$004 ; External Interrupt1 Vector Address PCINT0=$006 ;Pin Change Interrupt0 PCINT1=$008 ;Pin Change Interrupt1 PCINT2=$00A ;Pin Change Interrupt2 WDT=$00C ;Watchdog Timeout OC2A =$00E ; Output Compare2 Interrupt Vector Address OC2B =$010 ; Output Compare2 Interrupt Vector Address OVF2=$012; Overflow2 Interrupt Vector Address ICP1=$014 ; Input Capture1 Interrupt Vector Address OC1A=$016 ; Output Compare1A Interrupt Vector Address OC1B=$018 ; Output Compare1B Interrupt Vector Address OVF1=$01A ; Overflow1 Interrupt Vector Address OC0A=$01C ; Output Compare2 Interrupt Vector Address OC0B=$01E ; Output Compare2 Interrupt Vector Address OVF0=$020 ; Overflow0 Interrupt Vector Address SPI =$022 ; SPI Interrupt Vector Address URXC=$024 ; USART Receive Complete Interrupt Vector Address UDRE=$026 ; USART Data Register Empty Interrupt Vector Address UTXC=$028 ; USART Transmit Complete Interrupt Vector Address ADCC=$02A ; ADC Interrupt Vector Address ERDY=$02C ; EEPROM Interrupt Vector Address ACI =$02E ; Analog Comparator Interrupt Vector Address TWI =$030 ; Irq. vector address for Two-Wire Interface SPM =$032 ; SPM complete Interrupt Vector Address ; LAINTS:INT0,INT1 [INTLIST] count=25 INTname1=INT0,$002,EIMSK.INT0,EIFR.INTF0 INTname2=INT1,$004,EIMSK.INT1,EIFR.INTF1 INTname3=PCINT0,$006,PCICR.PCIE0,PCIFR.PCIF0 INTname4=PCINT1,$008,PCICR.PCIE1,PCIFR.PCIF1 INTname5=PCINT2,$00A,PCICR.PCIE2,PCIFR.PCIF2 INTname6=WDT,$00C,WDTCSR.WDIE,WDTCSR.WDIF INTname7=OC2A@COMPARE2A,$00E,TIMSK2.OCIE2A,TIFR2.OCF2A INTname8=OC2B@COMPARE2B,$010,TIMSK2.OCIE2B,TIFR2.OCF2B INTname9=OVF2@TIMER2,$012,TIMSK2.TOIE2,TIFR2.TOV2 INTname10=ICP1@CAPTURE1,$014,TIMSK1.TICIE1,TIFR1.ICF1 INTname11=OC1A@COMPARE1A,$016,TIMSK1.OCIE1A,TIFR1.OCF1A INTname12=OC1B@COMPARE1B,$018,TIMSK1.OCIE1B,TIFR1.OCF1B INTname13=OVF1@TIMER1,$01A,TIMSK1.TOIE1,TIFR1.TOV1 INTname14=OC0A@COMPARE0A,$01C,TIMSK0.OCIE0A,TIFR0.OCF0A INTname15=OC0B@COMPARE0B,$01E,TIMSK0.OCIE0B,TIFR0.OCF0B INTname16=OVF0@TIMER0,$020,TIMSK0.TOIE0,TIFR0.TOV0 INTname17=SPI,$022,SPCR.SPIE,SPSR.SPIF INTname18=URXC@SERIAL,$024,UCSR0B.RXCIE0,UCSR0A.RXC0 INTname19=UDRE,$026,UCSR0B.UDRIE0,UCSR0A.UDRE0 INTname20=UTXC,$028,UCSR0B.TXCIE0,UCSR0A.TXC0 INTname21=ADCC@ADC,$02A,ADCSRA.ADIE,ADCSRA.ADIF INTname22=ERDY,$02C,EECR.EERIE INTname23=ACI,$02E,ACSR.ACIE,ACSR.ACI INTname24=TWI,$030,TWCR.TWIE,TWCR.TWINT INTname25=SPM,$032,SPMCSR.SPMIE |
powyżej wycinki z pliku definicji µC ATmega328, plik M328DEF.DAT.
Podsumowując. Przerwanie PCINT0 może być wzbudzane pinem ze znacznikiem PCINT0…7, obsługuje je rejestr PCMSK0,
Przerwanie PCINT1 może być wzbudzane pinem ze znacznikiem PCINT8…15, obsługuje je wektor PCMSK1,
Przerwanie PCINT2 może być wzbudzane pinem ze znacznikiem PCINT16…23, obsługuje je wektor PCMSK2.
A który to pin i znacznik jest przyporządkowany w którym porcie, to już trzeba sobie sprawdzić indywidualnie, bo w każdym MCU może to być wykonane odmiennie.
W załączonych wycinkach widać, że przerwania zewnętrzne mają najwyższy priorytet 😉 z pośród pozostałych.
Z omówionej funkcjonalności – PCINT Bascom AVR, można korzystać np w poniższy sposób:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
Config Pinc.0 = Input 'enable port as input Config Pinb.7 = Input 'enable port as input Enable Interrupts 'enable global Interrupts Enable Pcint0 'we enable pcint0 as this has pcint0-pcint7 On Pcint0 Isr_pcint0 'we jump to this label when one of the pins is changed Pcmsk0 = &B10000000 '76543210--pcint7--pcint0 pcint7 is on Enable Pcint1 'we enable pcint1 as this has pcint8-pcint15 On Pcint1 Isr_pcint1 'we jump to this label when one of the pins is changed Pcmsk1 = &B00000001 'enable pcint8 (portc.0) '15.14.13.12.11.10.9.8--pcint8 is on With pcmsk you individualy select which pins must react on a logic level 'When you write a 1, the change in logic level will be detected. |
powyżej przykładowy sposób deklaracji.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
Isr_pcint0: If Pinb.7 = 0 Then Print "Pin is low" End If Return Isr_pcint1: If Pinc.0 = 1 Then Print "Pin change " End If Return End |
i sposób obsługi przerwania.
Na zakończenie przypomnę – obsługą przerwań PCINT bezpośrednio związane są rejestry:
• SREG – Status Register,
• GIMSK – General Interrupt Mask Register,
• GIFR – General Interrupt Flag Register,
• PCMSK – Pin Change Mask Register.
Z perspektywy czasu dodać można. iż starsze typy µC mogą nie posiadać rejestru PCMSK. A maskowanie linii IO może występować automatycznie z tytułu uruchomienia innego bloku funkcjonalnego. W związku z czym nie jest dostępne użytkownikowi. Dlatego wykrywanie zmiany stanu na wybranym pinie należy wykonać indywidualnie.
Opis zacząłem tworzyć we wrześniu 2014 (2014-09-10) i stąd taki początek. Wycinki kodu pochodzą z forum MCS.
Co można zrobić jeżeli przez wykorzystanie pcint0 i podpięcie switcha do masy i wejścia pb0 do Vcc, układ z fragmentem kodu jak poniżej po każdorazowym pobudzeniu switcha dolicza dodatkowe wartości (więcej jak jedna dla każdej zmiany stanu switcha) do zmiennej „liczba”? Dodam, że próbowałem dodać bezskutecznie kondensator przeciwzakłóceniowy do wejścia pb0. Dodatkowo w układzie wykorzystuję PWM i ADC (wszystko na płytce testowej i pajęczynce).
On Pcint0 Przeplyw
Enable Pcint0
Enable Interrupts
Pcmsk0 = &B00000001
Przeplyw:
Liczba = Liczba + 1
Return
Kondensatorek blokujący daj, niech pracuje. Atmel zaleca dodatkowy rezystor szeregowy ograniczający prąd przy zwarciu switcha. Nie rozwiąże on problemu ale zrobi dobrze µC.
Problem może być ze zbyt częstym wywołaniem przerwania, a niżeli byś się spodziewał. Piszesz „po każdorazowym pobudzeniu switcha dolicza dodatkowe wartości (więcej jak jedna dla każdej zmiany stanu switcha) do zmiennej „liczba””, więc zapytam kiedy wystąpi wyzwolenie przerwania? ;-D
W opisie jasno to przedstawiłem :-p
No właśnie, nawet jeżeli tylko raz przyciśnie się switcha i puści, to powinien naliczyć dwa, a nalicza różnie nawet do kilkudziesięciu.
Spróbuj kliknąć mocno, zdecydowanie i obczaić efekt.
Już jest ok. Nie było dostatecznie dobrze eliminowane drganie styków. W celu sprawdzenia dopisałem fragment kodu który cyklicznie zmieniał stan wyjścia uC i ten sygnał podałem przez opór do interesującego mnie PCINTa. Dzięki temu wyeliminowałem drganie styków i tak oto mogę się cieszyć kolejnymi zewnętrznymi przerwaniami w procku. Szkoda tylko że PCINT reaguje na zmianę stanu.
Pozdrawiam i dzięki za naprowadzenie.
Jak to się mówi experience rośnie ;-D
Dla mnie zaletą jest, że mogę dowolnie oprogramować czy program ma reagować na wystąpienie stanu niskiego czy też wysokiego.
Od biedy można zrobić reakcję na zbocze opadające lub narastające. Z tym że trzeba kilka linijek dopisać. Ale dzięki za podpowiedź.
W celu reakcji na stan dołożyłem warunek w przerwaniu:
if pin.x=0/1 then ….