TL; DR:
इंटरप्ट सर्विस रूटीन (ISR) लिखते समय:
- इसे छोटा रखें
- उपयोग न करें
delay ()
- सीरियल प्रिंट न करें
- मुख्य कोड अस्थिर के साथ साझा चर बनाते हैं
- मुख्य कोड के साथ साझा किए गए चर को "महत्वपूर्ण वर्गों" द्वारा संरक्षित किया जा सकता है (नीचे देखें)
- इंटरप्ट को बंद या चालू करने का प्रयास न करें
व्यवधान क्या हैं?
अधिकांश प्रोसेसर में रुकावट होती है। व्यवधान आपको कुछ और करते समय "बाहरी" घटनाओं का जवाब देते हैं। उदाहरण के लिए, यदि आप रात का खाना पका रहे हैं तो आप आलू को 20 मिनट तक पकाने के लिए रख सकते हैं। 20 मिनट तक घड़ी में घूरने के बजाय आप टाइमर सेट कर सकते हैं, और फिर टीवी देख सकते हैं। जब टाइमर बजता है तो आप आलू के साथ कुछ करने के लिए अपने टीवी को "बाधित" करते हैं।
बीच का उदाहरण
const byte LED = 13;
const byte SWITCH = 2;
// Interrupt Service Routine (ISR)
void switchPressed ()
{
if (digitalRead (SWITCH) == HIGH)
digitalWrite (LED, HIGH);
else
digitalWrite (LED, LOW);
} // end of switchPressed
void setup ()
{
pinMode (LED, OUTPUT); // so we can update the LED
pinMode (SWITCH, INPUT_PULLUP);
attachInterrupt (digitalPinToInterrupt (SWITCH), switchPressed, CHANGE); // attach interrupt handler
} // end of setup
void loop ()
{
// loop doing nothing
}
यह उदाहरण दिखाता है कि कैसे, भले ही मुख्य लूप कुछ भी नहीं कर रहा है, आप पिन 13 को स्विच को चालू या बंद कर सकते हैं, अगर पिन डी 2 पर स्विच दबाया जाता है।
इसका परीक्षण करने के लिए, बस D2 और ग्राउंड के बीच एक तार (या स्विच) कनेक्ट करें। आंतरिक पुलअप (सेटअप में सक्षम) सामान्य रूप से पिन को उच्च बनाता है। ग्राउंडेड होने पर यह LOW हो जाता है। पिन में परिवर्तन एक CHANGE इंटरप्ट द्वारा पता लगाया जाता है, जिसके कारण इंटरप्ट सर्विस सर्विस (ISR) कहा जाता है।
अधिक जटिल उदाहरण में, मुख्य लूप कुछ उपयोगी हो सकता है, जैसे तापमान रीडिंग लेना, और धक्का देने वाले बटन का पता लगाने के लिए इंटरप्ट हैंडलर की अनुमति देना।
संख्याओं को बाधित करने के लिए पिन संख्याओं को परिवर्तित करना
पिन नंबर को पास करने के लिए आप रुकावट सदिश संख्याओं को पिन नंबर में परिवर्तित करने के लिए सरल कर सकते हैं digitalPinToInterrupt()
। यह उचित रुकावट संख्या, या NOT_AN_INTERRUPT
(-1) देता है।
उदाहरण के लिए, यूनो पर, पिन D2 बोर्ड पर इंटरप्ट 0 (नीचे दी गई तालिका से INT0_vect) है।
इस प्रकार इन दो पंक्तियों का समान प्रभाव है:
attachInterrupt (0, switchPressed, CHANGE); // that is, for pin D2
attachInterrupt (digitalPinToInterrupt (2), switchPressed, CHANGE);
हालाँकि दूसरा पढ़ना आसान है और विभिन्न Arduino प्रकारों के लिए अधिक पोर्टेबल है।
उपलब्ध व्यवधान
नीचे Atmega328 के लिए प्राथमिकता क्रम में, व्यवधान की एक सूची दी गई है:
1 Reset
2 External Interrupt Request 0 (pin D2) (INT0_vect)
3 External Interrupt Request 1 (pin D3) (INT1_vect)
4 Pin Change Interrupt Request 0 (pins D8 to D13) (PCINT0_vect)
5 Pin Change Interrupt Request 1 (pins A0 to A5) (PCINT1_vect)
6 Pin Change Interrupt Request 2 (pins D0 to D7) (PCINT2_vect)
7 Watchdog Time-out Interrupt (WDT_vect)
8 Timer/Counter2 Compare Match A (TIMER2_COMPA_vect)
9 Timer/Counter2 Compare Match B (TIMER2_COMPB_vect)
10 Timer/Counter2 Overflow (TIMER2_OVF_vect)
11 Timer/Counter1 Capture Event (TIMER1_CAPT_vect)
12 Timer/Counter1 Compare Match A (TIMER1_COMPA_vect)
13 Timer/Counter1 Compare Match B (TIMER1_COMPB_vect)
14 Timer/Counter1 Overflow (TIMER1_OVF_vect)
15 Timer/Counter0 Compare Match A (TIMER0_COMPA_vect)
16 Timer/Counter0 Compare Match B (TIMER0_COMPB_vect)
17 Timer/Counter0 Overflow (TIMER0_OVF_vect)
18 SPI Serial Transfer Complete (SPI_STC_vect)
19 USART Rx Complete (USART_RX_vect)
20 USART, Data Register Empty (USART_UDRE_vect)
21 USART, Tx Complete (USART_TX_vect)
22 ADC Conversion Complete (ADC_vect)
23 EEPROM Ready (EE_READY_vect)
24 Analog Comparator (ANALOG_COMP_vect)
25 2-wire Serial Interface (I2C) (TWI_vect)
26 Store Program Memory Ready (SPM_READY_vect)
आंतरिक नाम (जो आप ISR कॉलबैक सेट करने के लिए उपयोग कर सकते हैं) कोष्ठक में हैं।
चेतावनी: यदि आप रुकावट वेक्टर नाम को गलत ठहराते हैं, तो भी केवल कैपिटलाइज़ेशन गलत होने से (एक आसान बात) रुकावट रूटीन को नहीं कहा जाएगा , और आपको एक कंपाइलर त्रुटि नहीं मिलेगी।
बीच में उपयोग करने के कारण
आपके द्वारा व्यवधान का उपयोग करने के मुख्य कारण हैं:
- पिन परिवर्तनों का पता लगाने के लिए (उदाहरण के लिए रोटरी एनकोडर, बटन प्रेस)
- वॉचडॉग टाइमर (जैसे कि अगर 8 सेकंड के बाद भी कुछ नहीं होता है, तो मुझे बाधित करें)
- टाइमर में व्यवधान - टाइमर की तुलना / अतिप्रवाह के लिए उपयोग किया जाता है
- एसपीआई डेटा स्थानांतरित करता है
- I2C डेटा स्थानान्तरण
- USART डेटा स्थानांतरित करता है
- एडीसी रूपांतरण (डिजिटल के अनुरूप)
- EEPROM उपयोग के लिए तैयार है
- फ्लैश मेमोरी तैयार है
"डेटा ट्रांसफ़र" का उपयोग प्रोग्राम को कुछ और करने के लिए किया जा सकता है जबकि डेटा को सीरियल पोर्ट, SPI पोर्ट या I2C पोर्ट पर भेजा या प्राप्त किया जा रहा है।
प्रोसेसर को जगाएं
बाहरी इंटरप्ट, पिन-चेंज इंटरप्ट, और वॉचडॉग टाइमर इंटरप्ट, का उपयोग प्रोसेसर को जगाने के लिए भी किया जा सकता है। यह बहुत आसान हो सकता है, क्योंकि स्लीप मोड में प्रोसेसर को बहुत कम बिजली (जैसे लगभग 10 माइक्रोएम्प) का उपयोग करने के लिए कॉन्फ़िगर किया जा सकता है। एक उठने, गिरने या कम-स्तर की रुकावट का उपयोग गैजेट को जगाने के लिए किया जा सकता है (जैसे। यदि आप इस पर एक बटन दबाते हैं), या "वॉचडॉग टाइमर" रुकावट इसे समय-समय पर जगा सकती है (उदाहरण के लिए समय की जांच करने के लिए)। तापमान)।
यदि कोई की-पैड पर या किसी समान को दबाया जाता है, तो प्रोसेसर को जगाने के लिए पिन-चेंज इंटरप्ट का उपयोग किया जा सकता है।
प्रोसेसर को एक टाइमर इंटरप्ट (जैसे कि एक निश्चित मूल्य तक पहुँचने वाला टाइमर, या अतिप्रवाह) और कुछ अन्य घटनाओं, जैसे कि एक आने वाले I2C संदेश द्वारा जागृत किया जा सकता है।
सक्षम / अक्षम करना बाधित करता है
"रीसेट" रुकावट को अक्षम नहीं किया जा सकता है। हालाँकि अन्य रुकावटों को वैश्विक रुकावट के झंडे को साफ करके अस्थायी रूप से अक्षम किया जा सकता है।
बीच में सक्षम करें
आप फ़ंक्शन कॉल को "इस तरह से बाधित" या "सेई" से बाधित कर सकते हैं:
interrupts (); // or ...
sei (); // set interrupts flag
व्यवधान उत्पन्न करता है
यदि आपको निष्क्रिय करने की आवश्यकता है, तो आप इस तरह से वैश्विक रुकावट ध्वज को "साफ़" कर सकते हैं:
noInterrupts (); // or ...
cli (); // clear interrupts flag
किसी भी विधि का समान प्रभाव होता है, जिसका उपयोग करना interrupts
/ noInterrupts
याद रखना थोड़ा आसान होता है कि वे किस तरीके से हैं।
Arduino में डिफ़ॉल्ट को बाधित करने में सक्षम होने के लिए है। लंबी अवधि के लिए उन्हें अक्षम न करें या टाइमर जैसी चीजें ठीक से काम नहीं करेंगी।
क्यों बाधित होता है?
कोड के समय-महत्वपूर्ण टुकड़े हो सकते हैं जिन्हें आप बाधित नहीं करना चाहते हैं, उदाहरण के लिए एक टाइमर बाधा।
इसके अलावा यदि मल्टी-बाइट फ़ील्ड ISR द्वारा अपडेट किए जा रहे हैं तो आपको इंटरप्ट को अक्षम करने की आवश्यकता हो सकती है ताकि आपको "परमाणु" डेटा प्राप्त हो सके। अन्यथा ISR द्वारा एक बाइट को अपडेट किया जा सकता है जबकि आप दूसरे को पढ़ रहे हैं।
उदाहरण के लिए:
noInterrupts ();
long myCounter = isrCounter; // get value set by ISR
interrupts ();
अस्थायी रूप से रुकावटों को बंद करना यह सुनिश्चित करता है कि isrCounter (ISR के अंदर एक काउंटर सेट) नहीं बदलता है जबकि हम इसका मूल्य प्राप्त कर रहे हैं।
चेतावनी: यदि आप सुनिश्चित नहीं हैं कि अगर पहले से ही हस्तक्षेप हो रहा है या नहीं, तो आपको वर्तमान स्थिति को बचाने और बाद में इसे पुनर्स्थापित करने की आवश्यकता है। उदाहरण के लिए, मिलिस से कोड () फ़ंक्शन यह करता है:
unsigned long millis()
{
unsigned long m;
uint8_t oldSREG = SREG; // <--------- save status register
// disable interrupts while we read timer0_millis or we might get an
// inconsistent value (e.g. in the middle of a write to timer0_millis)
cli();
m = timer0_millis;
SREG = oldSREG; // <---------- restore status register including interrupt flag
return m;
}
ध्यान दें कि संकेतित लाइनें वर्तमान SREG (स्थिति रजिस्टर) को बचाती हैं जिसमें व्यवधान ध्वज शामिल है। जब हमने टाइमर मान प्राप्त कर लिया है (जो 4 बाइट लंबा है) हमने स्टेटस रजिस्टर को वापस डाल दिया कि यह कैसा था।
टिप्स
फ़ंक्शन नाम
एवीआर प्रोसेसर के लिए फ़ंक्शन cli
/ sei
और रजिस्टर SREG विशिष्ट हैं। यदि आप अन्य प्रोसेसर का उपयोग कर रहे हैं जैसे कि एआरएम वाले कार्य थोड़े अलग हो सकते हैं।
वैश्विक रूप से अक्षम करना बनाम एक व्यवधान को अक्षम करना
यदि आप उपयोग cli()
करते हैं तो आप सभी इंटरप्ट को अक्षम करते हैं (टाइमर इंटरप्ट, सीरियल इंटरप्ट, आदि सहित)।
हालाँकि यदि आप किसी विशेष व्यवधान को निष्क्रिय करना चाहते हैं तो आपको उस विशेष व्यवधान स्रोत के लिए अवरोध-सक्षम ध्वज को साफ़ करना चाहिए। उदाहरण के लिए, बाहरी व्यवधान के लिए, कॉल करें detachInterrupt()
।
रुकावट प्राथमिकता क्या है?
चूंकि 25 इंटरप्ट हैं (रीसेट के अलावा) यह संभव है कि एक से अधिक रुकावट घटना एक बार में हो सकती है, या कम से कम, पिछले एक के संसाधित होने से पहले हो सकती है। व्यवधान अक्षम होने पर भी एक बाधा घटना हो सकती है।
प्राथमिकता क्रम वह क्रम है जिसमें प्रोसेसर बाधित घटनाओं के लिए जाँच करता है। सूची जितनी ऊंची होगी, प्राथमिकता उतनी ही अधिक होगी। इसलिए, उदाहरण के लिए, एक बाहरी बाधा अनुरोध 0 (पिन डी 2) बाहरी बाधा अनुरोध 1 (पिन डी 3) से पहले सेवित किया जाएगा।
क्या रुकावटों को निष्क्रिय करते समय व्यवधान उत्पन्न हो सकता है?
इंटरप्ट इवेंट्स (जो कि इवेंट को नोट कर रहा है) किसी भी समय हो सकता है, और प्रोसेसर के अंदर "इंटरप्ट इवेंट" फ्लैग सेट करके सबसे ज्यादा याद किया जाता है। यदि इंटरप्ट को अक्षम किया जाता है, तो उस बाधा को तब हैंडल किया जाएगा जब वे प्राथमिकता क्रम में फिर से सक्षम हो जाएं।
आप इंटरप्ट का उपयोग कैसे करते हैं?
- आप ISR (व्यवधान सेवा दिनचर्या) लिखते हैं। यह तब होता है जब व्यवधान उत्पन्न होता है।
- जब आप चाहते हैं कि आग लगने पर आप प्रोसेसर को बताएं।
ISR लिखना
इंटरप्ट सर्विस रूटिन बिना किसी तर्क के कार्य हैं। कुछ Arduino पुस्तकालयों को आपके स्वयं के कार्यों को कॉल करने के लिए डिज़ाइन किया गया है, इसलिए आप बस एक साधारण फ़ंक्शन (ऊपर दिए गए उदाहरणों में) की आपूर्ति करते हैं, जैसे।
// Interrupt Service Routine (ISR)
void switchPressed ()
{
flag = true;
} // end of switchPressed
हालाँकि अगर किसी पुस्तकालय ने पहले से ही ISR को "हुक" प्रदान नहीं किया है, तो आप अपना स्वयं का बना सकते हैं, जैसे:
volatile char buf [100];
volatile byte pos;
// SPI interrupt routine
ISR (SPI_STC_vect)
{
byte c = SPDR; // grab byte from SPI Data Register
// add to buffer if room
if (pos < sizeof buf)
{
buf [pos++] = c;
} // end of room available
} // end of interrupt routine SPI_STC_vect
इस मामले में आप "ISR" मैक्रो का उपयोग करते हैं, और संबंधित व्यवधान वेक्टर (पहले तालिका से) के नाम की आपूर्ति करते हैं। इस मामले में ISR एक SPI हस्तांतरण पूरा करने का काम संभाल रहा है। (ध्यान दें, कुछ पुराने कोड ISR के बजाय SIGNAL का उपयोग करते हैं, हालाँकि SIGNAL को पदावनत कर दिया जाता है)।
ISR को किसी व्यवधान से जोड़ना
पुस्तकालयों द्वारा पहले से ही बाधित किए गए व्यवधानों के लिए, आप बस प्रलेखित इंटरफ़ेस का उपयोग करते हैं। उदाहरण के लिए:
void receiveEvent (int howMany)
{
while (Wire.available () > 0)
{
char c = Wire.receive ();
// do something with the incoming byte
}
} // end of receiveEvent
void setup ()
{
Wire.onReceive(receiveEvent);
}
इस स्थिति में I2C लाइब्रेरी को आंतरिक रूप से आने वाले I2C बाइट्स को संभालने के लिए डिज़ाइन किया गया है, और फिर आने वाले समय स्ट्रीम के अंत में अपने आपूर्ति किए गए फ़ंक्शन को कॉल करें। इस मामले में getEvent कड़ाई से ISR नहीं है (इसका एक तर्क है) लेकिन इसे इनबिल्ट ISR कहा जाता है।
एक अन्य उदाहरण "बाहरी पिन" रुकावट है।
// Interrupt Service Routine (ISR)
void switchPressed ()
{
// handle pin change here
} // end of switchPressed
void setup ()
{
attachInterrupt (digitalPinToInterrupt (2), switchPressed, CHANGE); // attach interrupt handler for D2
} // end of setup
इस स्थिति में संलग्नक फ़ंक्शन फ़ंक्शन को आंतरिक तालिका में स्विच करता है, और इसके अलावा प्रोसेसर में उपयुक्त इंटरप्ट फ्लैग को कॉन्फ़िगर करता है।
एक बाधा को संभालने के लिए प्रोसेसर को कॉन्फ़िगर करना
अगला चरण, एक बार जब आपके पास ISR होता है, तो प्रोसेसर को बताना होगा कि आप चाहते हैं कि यह विशेष स्थिति एक रुकावट पैदा करे।
एक उदाहरण के रूप में, बाहरी बाधा 0 (D2 रुकावट) के लिए आप कुछ इस तरह से कर सकते हैं:
EICRA &= ~3; // clear existing flags
EICRA |= 2; // set wanted flags (falling level interrupt)
EIMSK |= 1; // enable it
इस तरह परिभाषित नामों का उपयोग करना अधिक पठनीय होगा:
EICRA &= ~(bit(ISC00) | bit (ISC01)); // clear existing flags
EICRA |= bit (ISC01); // set wanted flags (falling level interrupt)
EIMSK |= bit (INT0); // enable it
एटमगा 328 डेटशीट से EICRA (एक्सटर्नल इंटरप्ट कंट्रोल रजिस्टर A) इस टेबल के अनुसार सेट किया जाएगा। आप चाहते हैं कि सटीक प्रकार को परिभाषित करता है:
- 0: INT0 का निम्न स्तर एक बाधा अनुरोध (LOW व्यवधान) उत्पन्न करता है।
- 1: INT0 पर कोई भी तार्किक परिवर्तन एक व्यवधान अनुरोध (CHANGE व्यवधान) उत्पन्न करता है।
- 2: INT0 का गिरता हुआ किनारा एक अवरोध अनुरोध (FALLING इंटरप्ट) उत्पन्न करता है।
- 3: INT0 की बढ़ती बढ़त एक बाधा अनुरोध (RISING व्यवधान) उत्पन्न करती है।
EIMSK (एक्सटर्नल इंटरप्ट मास्क मास्क) वास्तव में इंटरप्ट को सक्षम करता है।
सौभाग्य से आपको उन नंबरों को याद रखने की आवश्यकता नहीं है क्योंकि संलग्नक आपके लिए ऐसा करता है। हालाँकि, यह वही है जो वास्तव में हो रहा है, और अन्य व्यवधानों के लिए आपको "मैन्युअल रूप से" बाधित झंडे लगाने पड़ सकते हैं।
निम्न-स्तरीय ISRs बनाम पुस्तकालय ISRs
अपने जीवन को सरल बनाने के लिए कुछ सामान्य व्यवधान हैंडलर वास्तव में लाइब्रेरी कोड (उदाहरण के लिए INT0_vect और INT1_vect) के अंदर होते हैं और फिर एक अधिक उपयोगकर्ता-अनुकूल इंटरफ़ेस प्रदान किया जाता है (जैसे। संलग्नक)। वास्तव में क्या करता है संलग्नक आपके वांछित बाधित हैंडलर के पते को एक चर में सहेजता है, और फिर जरूरत पड़ने पर INT0_vect / INT1_vect से कॉल करता है। यह आवश्यक होने पर हैंडलर को कॉल करने के लिए उपयुक्त रजिस्टर झंडे भी सेट करता है।
क्या ISR को बाधित किया जा सकता है?
संक्षेप में, नहीं, जब तक आप उन्हें नहीं चाहते हैं।
जब एक ISR दर्ज किया जाता है, तो व्यवधान अक्षम होते हैं । स्वाभाविक रूप से उन्हें पहली बार में सक्षम होना चाहिए, अन्यथा ISR दर्ज नहीं किया जाएगा। हालाँकि ISR के बाधित होने से बचने के लिए, प्रोसेसर इंटरप्ट को बंद कर देता है।
जब एक ISR बाहर निकलता है, तो फिर से व्यवधान सक्षम होते हैं । कंपाइलर भी रजिस्टर और स्टेटस फ्लैग को बचाने के लिए एक ISR के अंदर कोड जनरेट करता है, ताकि जब भी इंटरप्ट हुआ तो आप जो भी कर रहे हैं वह प्रभावित न हो।
हालाँकि आप ISR के अंदर रुकावट को चालू कर सकते हैं यदि आप बिल्कुल, जैसे।
// Interrupt Service Routine (ISR)
void switchPressed ()
{
// handle pin change here
interrupts (); // allow more interrupts
} // end of switchPressed
आम तौर पर आपको ऐसा करने के लिए एक बहुत अच्छे कारण की आवश्यकता होगी, क्योंकि एक और रुकावट के परिणामस्वरूप पिनकेंज में एक पुनरावर्ती कॉल हो सकता है, संभवतः बहुत अवांछनीय परिणाम के साथ।
ISR को निष्पादित करने में कितना समय लगता है?
डेटाशीट के अनुसार, एक सेवा को बाधित करने के लिए समय की न्यूनतम राशि 4 घड़ी चक्र (स्टैक पर वर्तमान प्रोग्राम काउंटर को धकेलने के लिए) है जिसके बाद कोड अब बाधित वेक्टर स्थान पर निष्पादित होता है। इसमें सामान्य रूप से एक कूद शामिल होता है जहां रुकावट दिनचर्या वास्तव में होती है, जो एक और 3 चक्र है। संकलक द्वारा निर्मित कोड की जांच से पता चलता है कि "ISR" घोषणा के साथ बनाया गया एक ISR को निष्पादित करने में लगभग 2.625, का समय लग सकता है, साथ ही कोड जो भी करता है। सटीक राशि इस बात पर निर्भर करती है कि कितने रजिस्टरों को सहेजने और पुनर्स्थापित करने की आवश्यकता है। न्यूनतम राशि 1.1875 1. होगी।
बाहरी व्यवधान (जहां आप संलग्नक का उपयोग करते हैं) थोड़ा और करते हैं और कुल मिलाकर लगभग 5.125 (लेते हैं (एक 16 मेगाहर्ट्ज घड़ी के साथ चल रहा है)।
कब तक प्रोसेसर ISR में प्रवेश करना शुरू कर देता है?
यह कुछ हद तक भिन्न होता है। ऊपर दिए गए आंकड़े आदर्श हैं, जहां बाधा तुरंत संसाधित होती है। कुछ कारकों में देरी हो सकती है:
यदि प्रोसेसर सो रहा है, तो "वेक-अप" बार निर्दिष्ट किया जाता है, जो कि कुछ मिलीसेकंड हो सकता है, जबकि घड़ी की गति के लिए वापस स्पूल किया जाता है। यह समय फ्यूज सेटिंग्स पर निर्भर करेगा, और नींद कितनी गहरी है।
यदि एक बाधा सेवा दिनचर्या पहले से ही निष्पादित हो रही है, तो आगे की रुकावटों को तब तक दर्ज नहीं किया जा सकता है जब तक कि यह या तो खत्म नहीं होता है, या स्वयं को बाधित करने में सक्षम बनाता है। यही कारण है कि आपको प्रत्येक रुकावट सेवा को नियमित रखना चाहिए, क्योंकि प्रत्येक माइक्रोसेकंड जो आप एक में खर्च करते हैं, आप संभावित रूप से दूसरे के निष्पादन में देरी कर रहे हैं।
कुछ कोड में रुकावट आती है। उदाहरण के लिए, कॉलिंग मिलिस () संक्षेप में बंद हो जाता है। इसलिए सेवा बाधित होने के समय को लंबे समय तक बाधित किया जाएगा।
व्यवधान को केवल एक निर्देश के अंत में सेवित किया जा सकता है, इसलिए यदि कोई विशेष निर्देश तीन घड़ी चक्र लेता है, और अभी शुरू हुआ है, तो रुकावट कम से कम एक दो चक्र चक्र में देरी होगी।
एक ईवेंट जो वापस चालू हो जाता है (उदाहरण के लिए एक रुकावट सेवा दिनचर्या से लौटकर) कम से कम एक और निर्देश निष्पादित करने की गारंटी है। इसलिए भले ही एक ISR समाप्त हो जाए, और आपकी रुकावट लंबित है, फिर भी इसे सेवित करने से पहले एक और निर्देश की प्रतीक्षा करनी होगी।
चूंकि व्यवधानों की प्राथमिकता होती है, इसलिए आपकी रुचि में आने वाली रुकावट से पहले एक उच्च प्राथमिकता वाले व्यवधान की सेवा ली जा सकती है।
प्रदर्शन के विचार
व्यवधान कई स्थितियों में प्रदर्शन को बढ़ा सकते हैं क्योंकि आप अपने कार्यक्रम के "मुख्य कार्य" के साथ मिल सकते हैं बिना यह देखने के लिए लगातार परीक्षण किए बिना कि क्या स्विच दबाए गए हैं। बाद में, जैसा कि ऊपर चर्चा की गई है, एक बाधा की सर्विसिंग का ओवरहेड वास्तव में एक एकल इनपुट पोर्ट को "तंग पाश" करने से अधिक होगा। आप मुश्किल से एक घटना के भीतर जवाब दे सकते हैं, कहते हैं, एक माइक्रोसेकंड। उस स्थिति में आप व्यवधान (जैसे समय) को निष्क्रिय कर सकते हैं और सिर्फ लूप को बदल सकते हैं।
कैसे बाधित होती है कतार?
व्यवधान के दो प्रकार हैं:
कुछ ने एक झंडा लगाया और उन्हें प्राथमिकता क्रम में संभाला गया, भले ही उनके कारण होने वाली घटना रुक गई हो। उदाहरण के लिए, पिन D2 पर एक बढ़ती, गिरती या बदलते स्तर की बाधा।
दूसरों का केवल तभी परीक्षण किया जाता है यदि वे "अभी" हो रहे हैं। उदाहरण के लिए, पिन D2 पर एक निम्न-स्तरीय व्यवधान।
किसी ध्वज को सेट करने वाले को पंक्तिबद्ध माना जा सकता है, क्योंकि बीच में आने के बाद जब तक बाधा नियमित रूप से प्रवेश नहीं करती है, तब तक झंडा ध्वज सेट रहता है, जिस समय प्रोसेसर ध्वज को साफ करता है। बेशक, चूंकि केवल एक ही झंडा है, अगर एक ही रुकावट की स्थिति पहले एक के संसाधित होने से पहले फिर से होती है, तो इसे दो बार सेवित नहीं किया जाएगा।
कुछ के बारे में पता होना चाहिए कि इन झंडे को बाधित हैंडलर संलग्न करने से पहले सेट किया जा सकता है। उदाहरण के लिए, पिन D2 पर "फ्लैग्ड" होने के लिए बढ़ते या गिरते स्तर की रुकावट के लिए संभव है, और फिर जैसे ही आप एक संलग्न करते हैं, बाधा तुरंत आग लग जाती है, भले ही घटना एक घंटे पहले हुई हो। इससे बचने के लिए आप ध्वज को मैन्युअल रूप से साफ़ कर सकते हैं। उदाहरण के लिए:
EIFR = bit (INTF0); // clear flag for interrupt 0
EIFR = bit (INTF1); // clear flag for interrupt 1
हालाँकि "निम्न स्तर" की रुकावटों की लगातार जाँच की जाती है, इसलिए यदि आप सावधान नहीं हैं तो वे फायरिंग जारी रखेंगे, भले ही व्यवधान कहा गया हो। यही है, आईएसआर बाहर निकल जाएगा, और फिर बाधा तुरंत फिर से आग लग जाएगी। इससे बचने के लिए, आपको पता होना चाहिए कि रुकावट निकाल दिया गया था, इसके तुरंत बाद आपको एक अलग करना चाहिए।
आईएसआर लिखने के लिए संकेत
संक्षेप में, उन्हें छोटा रखें! जबकि एक ISR निष्पादित कर रहा है अन्य व्यवधानों को संसाधित नहीं किया जा सकता है। यदि आप बहुत अधिक करने की कोशिश करते हैं, तो आप आसानी से बटन प्रेस, या आने वाले सीरियल संचार को याद कर सकते हैं। विशेष रूप से, आपको आईएसआर के अंदर "प्रिंट" डिबगिंग करने की कोशिश नहीं करनी चाहिए। उन्हें करने में लगने वाला समय हल होने की तुलना में अधिक समस्याएं पैदा करने की संभावना है।
एक उचित बात यह है कि एकल-बाइट ध्वज सेट किया गया है, और फिर मुख्य लूप फ़ंक्शन में उस ध्वज का परीक्षण करें। या, सीरियल पोर्ट से आने वाली बाइट को बफर में स्टोर करें। इनबिल्ट टाइमर इंटरप्ट होने से आंतरिक टाइमर के ओवरफ्लो होने पर हर बार फायरिंग से गुजरे हुए समय का पता चलता रहता है, और इस प्रकार आप यह जानकर कि कितने समय में ओवरफ्लो हुआ है, समय बीतने पर काम कर सकते हैं।
याद रखें, एक ISR इंटरप्ट के अंदर अक्षम हैं। इस प्रकार यह उम्मीद करना कि मिलिस () फ़ंक्शन कॉल द्वारा वापस किया गया समय बदल जाएगा, निराशा पैदा होगी। यह उस तरह से समय प्राप्त करने के लिए वैध है , बस इस बात का ध्यान रखें कि टाइमर बढ़ाना नहीं है। और यदि आप आईएसआर में बहुत लंबा समय बिताते हैं तो टाइमर एक अतिप्रवाह घटना को याद कर सकता है, जिससे मिलिस () द्वारा गलत समय पर वापस लौटा जा रहा है।
एक परीक्षण से पता चलता है कि, 16 मेगाहर्ट्ज Atmega328 प्रोसेसर पर, micros () को कॉल करने में 3.5625 on लगते हैं। मिलिस () को एक कॉल में 1.9375 (लगते हैं। रिकॉर्डिंग (बचत) वर्तमान टाइमर मूल्य एक ISR में करने के लिए एक उचित बात है। बीता हुआ मिलीसेकंड ढूंढना बीते माइक्रोसेकंड से अधिक तेज़ है (मिलीसेकंड की गणना केवल एक चर से पुनर्प्राप्त की जाती है)। हालाँकि, माइक्रोसेकंड की गिनती टाइमर 0 टाइमर (जो बढ़ाकर रखेगा) के वर्तमान मूल्य को एक सहेजे गए "टाइमर 0 ओवरफ़्लो काउंट" में जोड़कर प्राप्त की जाती है।
चेतावनी: चूंकि एक ISR के अंदर इंटरप्ट्स अक्षम हैं, और चूंकि Arduino IDE का नवीनतम संस्करण सीरियल रीडिंग और राइटिंग के लिए इंटरप्ट का उपयोग करता है, और "मिलिस" और "देरी" द्वारा उपयोग किए गए काउंटर को बढ़ाने के लिए भी आप उन कार्यों का उपयोग करने का प्रयास नहीं करेंगे। एक ISR के अंदर। दूसरे तरीके से रखने के लिए:
- विलंब करने का प्रयास न करें, उदा:
delay (100);
- आप एक कॉल से मिलिस तक का समय प्राप्त कर सकते हैं, हालांकि यह वृद्धि नहीं होगी, इसलिए इसे बढ़ाने के लिए इंतजार करके देरी न करें।
- सीरियल प्रिंट न करें (उदाहरण के लिए
Serial.println ("ISR entered");
)
- धारावाहिक पढ़ने की कोशिश मत करो।
पिन परिवर्तन में रुकावट आती है
ऐसे दो तरीके हैं जिनसे आप पिंस पर बाहरी घटनाओं का पता लगा सकते हैं। पहला विशेष "बाहरी व्यवधान" पिन, डी 2 और डी 3 है। ये सामान्य असतत व्यवधान घटनाएँ, एक प्रति पिन। आप प्रत्येक पिन के लिए संलग्नक का उपयोग करके उन तक पहुँच सकते हैं। आप रुकावट के लिए बढ़ती, गिरती, बदलती या निम्न-स्तरीय स्थिति को निर्दिष्ट कर सकते हैं।
हालांकि सभी पिनों के लिए "पिन चेंज" इंटरप्ट भी है (एटमेगा 328 पर, जरूरी नहीं कि सभी प्रोसेसर पर सभी पिन हों)। ये पिंस के समूह (D0 से D7, D8 से D13, और A0 से A5) पर कार्य करते हैं। वे बाहरी घटना में बाधा डालने से भी कम प्राथमिकता देते हैं। हालाँकि, वे बाहरी व्यवधानों की तुलना में उपयोग करने के लिए थोड़े अधिक काल्पनिक हैं क्योंकि उन्हें बैचों में बांटा गया है। इसलिए अगर इंटरफायर में आग लग जाती है तो आपको अपने कोड में काम करना पड़ता है जिससे पिन बाधित हो जाता है।
उदाहरण कोड:
ISR (PCINT0_vect)
{
// handle pin change interrupt for D8 to D13 here
} // end of PCINT0_vect
ISR (PCINT1_vect)
{
// handle pin change interrupt for A0 to A5 here
} // end of PCINT1_vect
ISR (PCINT2_vect)
{
// handle pin change interrupt for D0 to D7 here
} // end of PCINT2_vect
void setup ()
{
// pin change interrupt (example for D9)
PCMSK0 |= bit (PCINT1); // want pin 9
PCIFR |= bit (PCIF0); // clear any outstanding interrupts
PCICR |= bit (PCIE0); // enable pin change interrupts for D8 to D13
}
एक पिन परिवर्तन बाधा को संभालने के लिए आपको निम्न की आवश्यकता है:
- निर्दिष्ट करें कि समूह में कौन सा पिन है। यह PCMSKn वैरिएबल है (जहां n नीचे दी गई तालिका से 0, 1 या 2 है)। आप एक से अधिक पिन पर व्यवधान डाल सकते हैं।
- इंटरप्ट के उपयुक्त समूह को सक्षम करें (0, 1 या 2)
- जैसा कि ऊपर दिखाया गया है, एक बाधा हैंडलर की आपूर्ति करें
पिन की तालिका -> नाम / मुखौटे को पिन बदलें
D0 PCINT16 (PCMSK2 / PCIF2 / PCIE2)
D1 PCINT17 (PCMSK2 / PCIF2 / PCIE2)
D2 PCINT18 (PCMSK2 / PCIF2 / PCIE2)
D3 PCINT19 (PCMSK2 / PCIF2 / PCIE2)
D4 PCINT20 (PCMSK2 / PCIF2 / PCIE2)
D5 PCINT21 (PCMSK2 / PCIF2 / PCIE2)
D6 PCINT22 (PCMSK2 / PCIF2 / PCIE2)
D7 PCINT23 (PCMSK2 / PCIF2 / PCIE2)
D8 PCINT0 (PCMSK0 / PCIF0 / PCIE0)
D9 PCINT1 (PCMSK0 / PCIF0 / PCIE0)
D10 PCINT2 (PCMSK0 / PCIF0 / PCIE0)
D11 PCINT3 (PCMSK0 / PCIF0 / PCIE0)
D12 PCINT4 (PCMSK0 / PCIF0 / PCIE0)
D13 PCINT5 (PCMSK0 / PCIF0 / PCIE0)
A0 PCINT8 (PCMSK1 / PCIF1 / PCIE1)
A1 PCINT9 (PCMSK1 / PCIF1 / PCIE1)
A2 PCINT10 (PCMSK1 / PCIF1 / PCIE1)
A3 PCINT11 (PCMSK1 / PCIF1 / PCIE1)
A4 PCINT12 (PCMSK1 / PCIF1 / PCIE1)
A5 PCINT13 (PCMSK1 / PCIF1 / PCIE1)
बाधा हैंडलर प्रसंस्करण
इंटरप्ट हैंडलर को वर्क आउट करने की आवश्यकता होती है, जो पिन में रुकावट पैदा करता है यदि मास्क एक से अधिक निर्दिष्ट करता है (जैसे। यदि आप D8 / D9 / D10 पर हस्तक्षेप करना चाहते हैं)। ऐसा करने के लिए आपको उस पिन की पिछली स्थिति को संग्रहीत करने की आवश्यकता होगी, और यदि यह विशेष पिन बदल गया था, तो एक digitalRead (इसी तरह) करके काम करें।
आप शायद वैसे भी व्यवधान का उपयोग कर रहे हैं ...
एक "सामान्य" Arduino वातावरण पहले से ही व्यवधान का उपयोग कर रहा है, भले ही आप व्यक्तिगत रूप से प्रयास न करें। मिलिस () और माइक्रो () फ़ंक्शन कॉल "टाइमर ओवरफ्लो" सुविधा का उपयोग करते हैं। आंतरिक टाइमर (टाइमर 0) में से एक को लगभग 1000 बार एक सेकंड में बाधित करने के लिए स्थापित किया जाता है, और एक आंतरिक काउंटर बढ़ाता है जो प्रभावी रूप से मिलिस () काउंटर बन जाता है। इसके मुकाबले थोड़ा अधिक है, क्योंकि सटीक घड़ी की गति के लिए समायोजन किया जाता है।
इसके अलावा हार्डवेयर सीरियल लाइब्रेरी आने वाले और बाहर जाने वाले सीरियल डेटा को संभालने के लिए इंटरप्ट का उपयोग करता है। यह बहुत उपयोगी है क्योंकि आपका प्रोग्राम अन्य चीजें कर सकता है जबकि इंटरप्टर्स फायरिंग कर रहे हैं, और एक आंतरिक बफर भर रहे हैं। फिर जब आप Serial.available () की जांच करते हैं तो आप पता लगा सकते हैं कि क्या, अगर कुछ भी, उस बफर में रखा गया है।
व्यवधान को सक्षम करने के बाद अगले निर्देश को निष्पादित करना
Arduino फ़ोरम पर थोड़ी सी चर्चा और शोध के बाद, हमने यह स्पष्ट किया है कि इंटरप्ट को सक्षम करने के बाद क्या होता है। तीन मुख्य तरीके हैं जो मैं सोच सकता हूं कि आप इंटरप्ट को सक्षम कर सकते हैं, जो पहले सक्षम नहीं थे:
sei (); // set interrupt enable flag
SREG |= 0x80; // set the high-order bit in the status register
reti ; // assembler instruction "return from interrupt"
सभी मामलों में, प्रोसेसर गारंटी देता है कि व्यवधान के बाद अगला निर्देश सक्षम किया गया है (यदि वे पहले अक्षम थे) तो हमेशा निष्पादित किया जाएगा, भले ही एक बाधा घटना लंबित हो। ("अगले" से मेरा मतलब है कि कार्यक्रम अनुक्रम में अगला एक है, जरूरी नहीं कि शारीरिक रूप से निम्नलिखित एक हो। उदाहरण के लिए, एक आरईटीआई निर्देश वापस कूदता है जहां रुकावट आई थी, और फिर एक और निर्देश निष्पादित करता है)।
इससे आप इस तरह कोड लिख सकते हैं:
sei ();
sleep_cpu ();
यदि इस गारंटी के लिए नहीं है , तो प्रोसेसर के सो जाने से पहले व्यवधान उत्पन्न हो सकता है , और फिर यह कभी भी जागृत नहीं हो सकता है।
खाली करने में बाधा आती है
यदि आप प्रोसेसर को जगाने के लिए केवल एक रुकावट चाहते हैं, लेकिन विशेष रूप से कुछ भी नहीं करते हैं, तो आप EMPTY_INTERRUPT परिभाषित, उदा।
EMPTY_INTERRUPT (PCINT1_vect);
यह बस एक "रेटी" (व्यवधान से वापसी) निर्देश उत्पन्न करता है। चूंकि यह रजिस्टर को बचाने या पुनर्स्थापित करने का प्रयास नहीं करता है, इसलिए इसे जगाने के लिए एक बाधा पाने का यह सबसे तेज़ तरीका होगा।
महत्वपूर्ण खंड (परमाणु चर पहुंच)
चरों के संबंध में कुछ सूक्ष्म मुद्दे हैं जो बीच में आने वाली सेवा दिनचर्या (ISR) और मुख्य कोड (यानी ISR में कोड नहीं) के बीच साझा किए जाते हैं।
चूंकि ISR किसी भी समय फायर कर सकता है जब इंटरप्ट को सक्षम किया जाता है, आपको इस तरह के साझा किए गए चर तक पहुंचने के बारे में सतर्क रहने की आवश्यकता है, क्योंकि वे उसी क्षण अपडेट हो सकते हैं जब आप उन्हें एक्सेस करते हैं।
पहला ... आप "अस्थिर" चर का उपयोग कब करते हैं?
एक वैरिएबल को केवल अस्थिर ही चिह्नित किया जाना चाहिए, यदि यह एक ISR के अंदर और एक के बाहर दोनों का उपयोग किया जाता है।
- केवल ISR के बाहर उपयोग किए जाने वाले चर अस्थिर नहीं होने चाहिए ।
- केवल एक ISR के अंदर उपयोग किए जाने वाले चर अस्थिर नहीं होने चाहिए ।
- एक ISR के अंदर और बाहर दोनों का उपयोग किया जाने वाला चर अस्थिर होना चाहिए।
जैसे।
volatile int counter;
एक चर को अस्थिर के रूप में चिह्नित करना संकलक को बताता है कि चर सामग्री को प्रोसेसर रजिस्टर में "कैश" नहीं करना चाहिए, लेकिन हमेशा आवश्यकता होने पर इसे मेमोरी से पढ़ें। यह प्रसंस्करण को धीमा कर सकता है, यही कारण है कि आप हर चर को अस्थिर नहीं करते हैं, जब जरूरत नहीं होती है।
एक अस्थिर चर का उपयोग करते हुए इंटरप्ट को बंद करें
उदाहरण के लिए, तुलना करने के लिए count
कुछ संख्या के लिए, में से एक बाइट मामले में तुलना के दौरान बंद बीच में आता है बारी count
ISR और नहीं अन्य बाइट के द्वारा अद्यतन किया गया है।
volatile unsigned int count;
ISR (TIMER1_OVF_vect)
{
count++;
} // end of TIMER1_OVF_vect
void setup ()
{
pinMode (13, OUTPUT);
} // end of setup
void loop ()
{
noInterrupts (); // <------ critical section
if (count > 20)
digitalWrite (13, HIGH);
interrupts (); // <------ end critical section
} // end of loop
डेटा शीट पढ़ें!
प्रोसेसर के लिए डेटा शीट से इंटरप्ट, टाइमर आदि के बारे में अधिक जानकारी प्राप्त की जा सकती है।
http://www.atmel.com/images/Atmel-8271-8-bit-AVR-Microcontroller-ATmega48A-48PA-88A-88PA-168A-168PA-328-328P_datasheet_Complete.pdf
आगे के उदाहरण
अंतरिक्ष के विचार (पोस्ट आकार की सीमा) मेरी लिस्टिंग को अधिक उदाहरण कोड से रोकते हैं। अधिक उदाहरण कोड के लिए मेरे पेज को इंटरप्ट के बारे में देखें ।