Arduino-да тоқтатуларды қолдану (басы)
Arduino Interrupts төлтума мақаланы, аудару
Көп ретте микроконтроллерде жобалармен жұмыс кезінде фондық функцияны бірдей аралықтағы уақыт сайын іске қосып тұру қажет. Бұл көп ретте тоқтатуларды болдыру үшін, таймер аппараттық құрылғысымен жүзеге асырылады. Бұл тоқтатулар, кезеңдік тоқтатуларды басқару үшін, тоқтатуларды өңдеу бағдарламасын іске қосады (Interrupt Service Routine, ISR). Осы мақалада мен, ATMega168 Arduino микроконтроллеріндегі тоқтатуларды өңдеуге арналған 8-биттік 2 таймерінің орнатылуын сипаттаймын. Мен, оның өзінің ішіндегі және тоқтатуларды өңдеу бағдарламасын орнатуға қажет болатын кезеңдер бойынша өтемін.
«Роботты техника». 5ші деңгей. 9 сабақ.
Сабақтың тақырыбы: Тоқтатулар 3.
Сабақтың ұзақтығы: 2 сағ.
Таймер2 ISR
Таймер 2 қайта толуы бойынша үзіліске арналған ISR төменде көрсетілген.
#define TOGGLE_IO 9 //ISR таймері бойынша қайта қосу үшін Arduino
қорытындысы
//Timer2 толып кету жөніндегі үзіліс векторының көрсеткіші
ISR(TIMER2_OVF_vect) {
//IOқорытындысының басқа күйге қайта қосылуы.
digitalWrite(TOGGLE_IO,!digitalRead(TOGGLE_IO));
//Таймердің ағымдағы мәнін қамту. Бұл қатенің көлемі
//осы қызметтің үзілісін және жұмысын өңдеудің кідіруінің салдарынан
latency=TCNT2;
//Таймердің қайта жүктелуі және кідіру жөніндегі түзету
TCNT2=latency+timerLoadValue;
}
Бұл қызмет қысқа және оның негізгі міндеті енгізу шығару портын
ауыстырып қосу. Ауыстырыпқосқаннан кейін ол таймердің ағымдағы мәнін
қамтиды және оны таймерді қайта жүктеуге түзету үшін пайдаланады.
Кідіртудің мәні ғаламдық, оны жоғарыда аталған жүктеу өлшемдері үшін
басты бағдарлама қадағалайды. Бұл такттердің саны 2 МГЦ жиілігінде, ол
өзінің қызметтерін орындау үшін ISR алады.
ISR қысқа болу керектігін естен шығармаңыз, себебі ол 50 кГц бапталған
таймер кезінде әр 20 мкс шақырылады. Сіз көп ISR жасай аласыз, бірақ сізге
үзілістің аралығы мен ISR орындалатын операциялардың санының арасында
теңгерімді табу қажет болады. Кідіртудің мәні төменде сипатталғандай
осыған көмектеседі.Менің сынақтарым шамамен 20 тактті құрайтын, орташа кідірісті көрсетті,
бұл процессорды жүктеудің 45% дейін құрайды. Бұл дегеніміз ISR үшін
бағдарламаның шамамен орта есеппен 45% баяу орындалатындығын білдіреді.
Аса да қорқынышты жағдай емес, бірақ байқасаңыз, ISR орындалатын барлық
жұмыс енгізушығару портын ауыстырыпқосудан құралған. Бұл барлық
үдерістегі уақыттың шамамен жартысын құрады! Бұл көлем үзіліске қызмет
көрсетуге, сандық жазбаның рәсімін орындауға / сандық оқуға және таймердің
қайта жүктелуінің толық үдерісіне кеткен уақыттан құралады.
Егер портты тіркеудің көмегімен шығаруға тікелей әрекет жасағанда ISR
әлдеқайда тезірек болар еді. Бірақ жалпы кітапханалық қызметтерді
пайдалану әлдеқайда жеңіл болды, және егер маған шынымен енгізушығару
портының күйін ауыстырыпқосу ғана қажет болса, маған 45% жоғалуы
жеткілікті болар еді.
Басты бағдарлама. Setup() функциясы
Setup() қызметі бағдарламаны іске қосқан кезде бір рет Arduino жүйелік
бағдарламасының көмегімен шақырылады. Ол енгізушығару мен таймердің
порттарын жүктейді. Ол сондайақ ретті портқа бағдарламаның іске
асырылғандығын көрсететін шығаруды жүзеге асырады.
void setup(void) {
//ISR ауыстырыпқосу қажет портты шығарушы етіп орнатады.
pinMode(TOGGLE_IO,OUTPUT);
//Ретті портты іске қосады
Serial.begin(9600);
//Бағдарламаның іске қосылуы туралы хабарлама
Serial.println("Timer2 Test");
//Таймерді іске қосады және таймердің жүктелетін мәнін алады.
timerLoadValue=SetupTimer2(44100);
//Таймердің жүктелетін мәнін шығарады
Serial.print("Timer2 Load:");Serial.println(timerLoadValue,HEX);
}
Setup() ауыстырып қосатын портты шығатын портқа орнатудан басталады,
сондықтан біз оны ISR ауыстырып қоса аламыз. Сосын ол ретті портты іске
қосады және бағдарламаның әрекет ететіндігін көрсету үшін жазбаша
хабарламаны шығарады.
Одан әрі дыбыстың дискреттелуінің жалпы жиілігімен орнатылған 44100 Гц
жиілікпен қызметі шақырылады. Қайтарылатын мән ISR кейіннен қолдану
үшін ғаламдық ауыспалы timerLoadValue сақталады.
Сонымен, Setup() timerLoadValue шығарады, сондықтан біз оның саналы шекте
екендігіне көз жеткізе аламыз.
Осы кезеңде таймер іске қосылған және біздің ISR рәсіміміз берілген
жиілікпен шақырылады. Егер осциллограф қоссақ, сіз таймер аралығының ½
тең шығудың ауыстырылыпқосылуын көресіз. ½ болатын себебі, біз ISR
біреуінің төменгі деңгейіне портты орнатамыз және оған екіншісінің жоғары
деңгейін жазамыз.
Басты бағдарлама, Loop() қызметі
Кезеңнің қызметі бағдарлама іске қосылған кезде қайтақайта шақырылады.
Кезеңнен қайтарылған әр кезде ол қайтадан шақырылады. Бағдарламаның
мәтіні күрделі болып көрінеді, бірақ шындығында, біздің жасағандарымыздың
барлығы Таймер2 ISR кідірісінің көлемін орташа ландырамыз және 100
өлшемді алғаннан кейін өлшемнің нәтижесін шығарамыз.
Кезеңнің қызметі енгізушығару желісін ауыстырып қосуға қатысты
ешнәрсенің жасамауының қажеттілігін атап өтеміз. Бұның бәрі ISR
басқарылады, ол кезеңнің қызметіне басқа заттармен айналысуына мүмкіндік
береді, ISR болып жатқан үдерістерге назар аудармайды. Ретті порттың
кезеңінде сіз дайын болған кезде, UART келесі белгісінің жүктелуі туралы
қам жемеуіңіз керек. Сіз өз істеріңізбен айналысасыз, ал ретті порт фондық
режимде басқарылады. Бұл кідіріспен басқарылатын бағдарламаның
ерекшеліктерінің бірі. Қызметтер жағдайдың пайда болуы кезінде
шақырылады және сіздің қосалқы бағдарламаңыздан бөлек болып табылады.void loop(void) {
//Әр 10 мс сайын ISR кідірісін жинайды.
delay(10);
// ISR кідірістің ағымдағы мәнін жинайды және есептегішті арттырады
//the sample counter
latencySum+=latency;
sampleCount++;
//10 өлшем жиналғаннан кейін есептейді және өлшемнің нәтижесін шығарады
if(sampleCount>99) {
float latencyAverage;
float loadPercent;
//Орташа кідірісті есептейді
latencyAverage=latencySum/100.0;
//сомманың мәнін нөлге теңестіреді
sampleCount=0;
latencySum=0;
//Процессорды жүктеудің күтілетін пайызын есептейді
loadPercent=latencyAverage/(float)timerLoadValue;
loadPercent*=100; //Үлестерді пайызға ауыстырады;
//Орташа кідірісті шығарады
Serial.print("Latency Average:");
Serial.print((int)latencyAverage);
Serial.print(".");
latencyAverage=(int)latencyAverage;
Serial.print((int)(latencyAverage*100));
//Жүктеменің күтілетін пайызын шығарады
Serial.print(" Load:");
Serial.print((int)loadPercent);
Serial.println("%");
}}
Кезеңнің қызметі 10 мс кідірісінен басталады. (Егер үздіксіз жоғары жиілікпен
енгізушығарудың портын ауыстырыпқосу қажет болса мұндай кідірістерді
пайдалана алмайтындығымызды есте сақтаңыз). 10 мс кідірісі кідірістің
өлшемін және өлшемнің қорытындысын біздің қаншалықты жиі
жасайтындығымызды реттейді. Біз олардың әрқайсысы 10 мс алатын 100
өлшемді алғаннан кейін, нәтиже әр секунд сайын шығарылады.
Одан әрі ISR кідірістің мәні latencySum ғаламдық ауыспалылығына жиналады.
Біз кідірістің ағымдағы мәнін қамтимыз және оны жиналғандарға қосамыз.
Сонымен қатар, өлшемдердің жиналған санының есептегішін бірлікке
арттырамыз.
Енді біз 100 өлшемнің жиналғандығын тексереміз. Егер болмаса, онда қалған
кодты қалдырамыз және қайтамыз. Егер 100 өлшем жиналса, онда
өлшемдердің санына жиналған кідірісті бөле отырып орташа мәнді аламыз
және latencyAverage нәтижесін сақтаймыз. Сосын барлығын қайта бастау үшін
аккумуляторды және өлшемдердің есептегішін тазартамыз.
Енді, кідірістің жақсы өлшемін ала отырып біз процессордың күтілетін
жүктелімін есептей аламыз. Бізге белгілісі таймер 0xFF дейін және кейін 0×00
дейін біздің қайта жүктелетін мәннен санап шыққанда таймердің толып
кететіндігі белгілі, бұл толып кетудің көрсеткіші. Жүктеу пайызы кідіріс/ ISR
такті ретінде анықталатын болады. Бұл мәні есептеледі және өлшемнің
нәтижесі ретінде шығарылады, сондықтан біз ISR бағдарламасының әсерін
бағалай аламыз.
Нәтижелер шығарылды, және кезеңнің қызметі қайтадан шақырылу үшін және
10 мс қайтарылады. Мен сіздің назарыңызды басты бағдарламаның портты
ауыстырыпқосу бойынша ешқандай жұмысты атқармайтындығына
аударамын. Ол кідірістерді пайдалануда шектелмеген, уақыттың өзінің
аралықтарымен шақырылады және оған оның тез орындалатындығы әсерін
тигізеді. Ол алатын уақыт бұл барлық фондық ISR орындаудың, осы
таймердің ISR және үнемі белсенді басқаларының қалған уақыты, мысалы,
ретті порт пен Таймер 0.Бағдарламаның мысалында таймер он алты мәнді D4 немесе он мәнді 212
жүктелген. Ол дегеніміз, 44 тактті санағаннан кейін, ол үзіле беретін болады.
Біз білеміз, процессор ISR кодын орындап тұрғанда, таймер шамамен 20
шақты тактті санайды, яғни ол ISR қайтып оралғанға дейін барлығы шамамен
24 қалады. Бұл 24 такт біздің басты бағдарлама орындауға алатын барлық
уақыт. Сондықтан жалпы уақыттан үзілістердің арасындағы 44 тактті біз 20
тактін ISR жұмсаймыз, қосалқы бағдарламаға шамамен 24 тактті қалдырамыз.
Бұл ISR жіберілген процессордың шамамен 45% құрайды.
Сонымен қатар, бізге белгілісі, ISR барлық орындалатын кодына шамамен 1
такт жұмсалады және кідірістің мәнінде өлшенбейді. Сондықтан жоғарыда
көрсетілген бағдарламада түзету үшін маған уақытша аралыққа бірлікті
қосуға тура келді. Нақты қосымшаға 44тен тек 23 такт қана беріледі. Мен
қорытындының ауыстырыпқосуына пікір айтып сол бағдарламаны іске қосқан
кезде, кідіріс орта есеппен шамамен 3 тактті немесе жүктеменің 6% құрады.
ISR қандай да бір пайдалы тапсырмаларсыз қызмет көрсетпей шамамен
қолжетімді ресурстардың шамамен 6% пайдаланады. Қалған уақытты сандық
оқудың қызметтері мен жазба жеп қояды.
Түйіндеме
Егер сіз осы орынға дейін оқып болсаңыз, онда сізде кідірістер туралы,
олардың неліктен өте маңызды екендігі туралы және олардың басты
бағдарламаға қалай әсер ететіндігі туралы түсінік қалыптасты. Сонымен
қатар, сізде өз жобаңызды құру үшін бірнеше мысал бар. ISR тез таймерімен
жұмыс жасаған уақытта өте мұқият болыңыз. Егер сіз жылдамдығы аз
таймерді іске қосаңыз, мәселелер, әрине, азаяды. Мен мұнда нені күтуге
болатындығы туралы түсінік беру үшін шектен тыс жағдайларды
қарастырдым.