Arduino әр секунд сайын үзіледі. Контактіні өңдеу

Міне, Arduino функциясын пайдаланудың мысалы attachInterrupt().

Үзіліс – процессорға дереу назар аударуды қажет ететін оқиғаның орын алғанын хабарлайтын сигнал. Процессор бұл сигналға ағымдағы нұсқаулардың орындалуын үзу және басқаруды үзу өңдеушісіне беру арқылы жауап беруі керек (ISR, үзіліс қызметінің тәртібі). Өңдеуіш - бұл біз өзіміз жазатын және оқиғаға жауап беретін кодты орналастыратын тұрақты функция.

ISR үзілуіне қызмет көрсеткеннен кейін функция өз жұмысын аяқтайды және процессор өзінің үзілген әрекеттеріне қуана оралады - ол кодты тоқтаған жерінен орындауды жалғастырады. Мұның бәрі автоматты түрде болады, сондықтан біздің міндетіміз - ештеңені бұзбай немесе процессорды біз жиі алаңдатпай, үзу өңдеушісін жазу ғана. Сізге тізбекті түсіну, қосылған құрылғылардың жұмыс істеу принциптері және үзілістің қаншалықты жиі туындауы мүмкін екендігі және оның пайда болу ерекшеліктері қандай екендігі туралы түсінік қажет. Мұның бәрі үзілістермен жұмыс істеудің негізгі қиындығын құрайды.

Аппараттық және бағдарламалық қамтамасыз ету үзілістері

Arduino-да үзілістерді бірнеше түрге бөлуге болады:

  • Аппараттық үзілістер. Микропроцессорлық архитектура деңгейіндегі үзіліс. Оқиғаның өзі өнімді сәтте сыртқы құрылғыдан пайда болуы мүмкін - мысалы, пернетақтадағы түймені басу, компьютер тінтуірін жылжыту және т.б.
  • Бағдарламалық құрал үзілістері. Олар арнайы нұсқауларды қолдану арқылы бағдарлама ішінде іске қосылады. Үзіліс өңдеушісін шақыру үшін қолданылады.
  • Ішкі (синхронды) үзілістер. Ішкі үзіліс бағдарламаның орындалуын өзгерту немесе бұзу нәтижесінде пайда болады (мысалы, жарамсыз адреске, жарамсыз операция кодына және т.б. қатынасу кезінде).

Неліктен аппараттық үзілістер қажет?

Аппараттық үзілістер сыртқы оқиғаға жауап ретінде пайда болады және сыртқы аппараттық құрылғыдан туындайды. Arduino аппараттық үзілістердің 4 түрін қамтамасыз етеді. Олардың барлығы үзу пиніндегі сигналда ерекшеленеді:

  • Контакт жерге тартылады. Үзіліс өңдегіші үзу пинінде LOW сигналы болғанша орындалады.
  • Контактідегі сигналды өзгерту. Бұл жағдайда Arduino үзу пинінде сигнал өзгерген кезде үзу өңдеушісін орындайды.
  • Сигналдың ТӨМЕН-ден ЖОҒАРҒЫ-ға пинде өзгеруі - төмен сигналдан жоғарыға ауысқанда, үзу өңдеушісі орындалады.
  • Сигналды пинде ЖОҒАРЫ күйден ТӨМЕНге өзгерту – жоғары сигналдан төмен сигналға ауысқанда үзу өңдеушісі орындалады.

Үзілістер Arduino бағдарламаларында пайдалы, өйткені олар уақыт мәселелерін шешуге көмектеседі. Мысалы, UART-пен жұмыс істегенде үзілістер әрбір таңбаның келуін қадағалаудың қажетін болдырмауға мүмкіндік береді. Сыртқы аппараттық құрылғы үзу сигналын береді, процессор дереу таңбаны уақытында түсіретін үзу өңдеушісін шақырады. Бұл процессордың уақытын үнемдейді, әйтпесе UART күйін үзусіз тексеруге жұмсалады, оның орнына барлық қажетті әрекеттер негізгі бағдарламаға әсер етпей орындалады. Аппараттық құрылғыдан арнайы мүмкіндіктер қажет емес.

Үзуді шақырудың негізгі себептері:

  • Шығыс күйінің өзгеруін анықтау;
  • Таймерді үзу;
  • SPI, I2C, USART арқылы деректерді үзу;
  • Аналогты-сандық түрлендіру;
  • EEPROM, флэш-жадты пайдалануға дайын болу.

Arduino-да үзілістер қалай жүзеге асырылады

Үзу сигналы қабылданған кезде жұмыс тоқтатылады. Үзілген кезде орындалады деп жарияланған функцияның орындалуы басталады. Жарияланған функция кіріс мәндерін қабылдай алмайды және ол шыққан кезде мәндерді қайтара алмайды. Үзіліс бағдарламаның негізгі цикліндегі кодтың өзіне әсер етпейді. Arduino-да үзілістермен жұмыс істеу үшін стандартты функция қолданылады attachInterrupt().

Әр түрлі Arduino тақталарында үзілістерді орындаудағы айырмашылықтар

Белгілі бір микроконтроллер моделінің аппараттық іске асыруына байланысты бірнеше үзілістер болады. Arduino Uno тақтасында екінші және үшінші түйреуіштерде 2 үзіліс бар, бірақ екіден көп шығыс қажет болса, тақта арнайы «пин-өзгерту» режимін қолдайды. Бұл режим барлық түйреуіштер үшін кірісті өзгерту арқылы жұмыс істейді. Кірісті өзгертудің үзіліс режимінің арасындағы айырмашылық сегіз түйреуіштің кез келгенінде үзілістерді жасауға болады. Бұл жағдайда өңдеу қиынырақ және ұзағырақ болады, өйткені сіз әрбір контактідегі соңғы күйді қадағалауыңыз керек.

Басқа тақталарда үзілістер саны көбірек. Мысалы, тақтада сыртқы үзулерді өңдей алатын 6 түйреуіш бар. Барлық Arduino тақталары үшін attachInterrupt функциясымен (үзу, функция, режим) жұмыс істегенде Inerrupt 0 аргументі 2 сандық пинмен байланысты.

Arduino тіліндегі үзілістер

Енді практикалық болайық және сіздің жобаларыңызда үзілістерді қалай пайдалану керектігі туралы сөйлесейік.

Синтаксис attachInterrupt()

attachInterrupt функциясы үзілістермен жұмыс істеу үшін қолданылады. Ол өңдеушіге сыртқы үзуді қосу үшін қызмет етеді.

Қоңырау синтаксисі: attachInterrupt(үзу, функция, режим)

Функция аргументтері:

  • үзіліс – шақырылатын үзіліс нөмірі (стандартты 0 – 2-ші түйреуіш үшін, Arduino Uno тақтасы үшін 1 – 3-ші түйреуіш үшін),
  • функция – үзілген кезде шақырылатын функцияның аты (маңызды – функция ешқандай мәндерді қабылдамауы немесе қайтармауы керек),
  • режим – үзуді іске қосу шарты.

Келесі іске қосу шарттарын орнатуға болады:

  • LOW – контакт нөлдік мәнге ие болғанда сигнал деңгейі төмен болғанда орындалады. Үзіліс циклді түрде қайталануы мүмкін - мысалы, түйме басылғанда.
  • ӨЗГЕРТУ – шетте сигнал жоғарыдан төменге немесе керісінше өзгерген кезде үзіліс орын алады. Кез келген сигналды өзгерту үшін бір рет орындалады.
  • RISING – сигнал ТӨМЕНден ЖОҒАРҒЫ күйге өзгерген кезде үзуді бір рет орындаңыз.
  • FALLING – сигнал ЖОҒАРЫ күйден ТӨМЕНге өзгерген кезде үзуді бір рет орындау.4

Маңызды ескертпелер

Үзілістермен жұмыс істеу кезінде келесі маңызды шектеулерді ескеру қажет:

  • Өңдеуші функциясы тым ұзақ жұмыс істемеуі керек. Мәселе мынада, Arduino бір уақытта бірнеше үзілістерді өңдей алмайды. Өңдеуші функциясы жұмыс істеп тұрғанда, барлық басқа үзулер еленбейді және маңызды оқиғаларды өткізіп жіберуіңіз мүмкін. Егер сізге үлкен бірдеңе жасау қажет болса, оқиғаны өңдеуді негізгі цикл() цикліне көшіріңіз. Өңдеушіде тек оқиға жалауын орнатуға болады, ал циклде жалаушаны тексеріп, оны өңдеуге болады.
  • Айнымалылармен өте сақ болу керек. Интеллектуалды C++ компиляторы сіздің бағдарламаңызды «қайта оңтайландыруы» мүмкін - оның пікірінше, қажет емес айнымалы мәндерді алып тастаңыз. Компилятор кейбір айнымалы мәндерді бір бөлікке орнатып, оларды басқа бөлігінде пайдаланғаныңызды көрмейді. Негізгі деректер түрлері жағдайында бұл мүмкіндікті жою үшін сіз өзгермелі кілт сөзін пайдалана аласыз, мысалы: ұшпа логикалық күй = 0. Бірақ бұл әдіс күрделі деректер құрылымдарымен жұмыс істемейді. Сондықтан сіз әрқашан сергек болуыңыз керек.
  • Үзілістердің көп санын пайдалану ұсынылмайды (6-8-ден артық қолданбауға тырысыңыз). Әртүрлі оқиғалардың үлкен саны кодтың күрделі күрделенуін талап етеді, демек, қателіктерге әкеледі. Бұған қоса, сіз үзілістердің көптігі бар жүйелерде орындаудың уақытша дәлдігі туралы сөз болмайтынын түсінуіңіз керек - сіз үшін маңызды командаларды шақыру арасындағы интервалдың дәл қандай екенін ешқашан түсіне алмайсыз.
  • Өңдеушілерде delay() функциясын қолдануға қатаң тыйым салынады. Кешігу аралығын анықтау механизмі таймерлерді пайдаланады және олар өңдеуші блоктайтын үзілістерде де жұмыс істейді. Нәтижесінде барлығы бәрін күтеді және бағдарлама қатып қалады. Дәл сол себепті үзуге негізделген байланыс протоколдарын (мысалы, i2c) пайдалану мүмкін емес.

attachInterrupt қолдану мысалдары

Жаттығуға кірісейік және үзілістерді пайдаланудың қарапайым мысалын қарастырайық. Мысалда Arduino Uno 2 түйреуішіндегі сигнал өзгерген кезде біз дәстүрлі түрде жарықдиодты қосатын 13 істікшелі күйін ауыстыратын өңдеуші функциясын анықтаймыз.

#define PIN_LED 13 ұшпа логикалық әрекетКүй = ТӨМЕН; void setup() ( pinMode(PIN_LED, OUTPUT); // Үзуді орнату // myEventListener функциясы // 2 пинде (үзу 0 2 пинге қосылған) болғанда шақырылады // сигнал өзгереді (қайсысына қарамастан direction) attachInterrupt (0, myEventListener, CHANGE void loop() ( // Біз цикл функциясында ештеңе жасамаймыз, өйткені барлық оқиғаларды өңдеу коды myEventListener функциясында болады) void myEventListener() ( actionState != actionState); ; // / / Басқа әрекеттерді орындаңыз, мысалы, диодты қосу немесе өшіру digitalWrite(PIN_LED, actionState )

Неғұрлым күрделі үзілістер мен олардың өңдегіштерінің кейбір мысалдарын қарастырайық: таймерлер мен түймелер үшін.

Секіруге қарсы түймені басу арқылы үзеді

Үзіліс болған кезде, ол пайда болады - түймені басқан кезде контактілер тығыз байланыста болғанға дейін, олар тербеліп, бірнеше операцияларды жасайды. Секірумен күресудің екі жолы бар: аппараттық құрал, яғни конденсаторды түймеге дәнекерлеу және бағдарламалық қамтамасыз ету.

Функцияны пайдалана отырып, сөйлесуден құтылуға болады - бұл түйменің бірінші әрекетінен бері өткен уақытты өлшеуге мүмкіндік береді.

If(digitalRead(2)==HIGH) ( //түйме басылғанда //Егер алдыңғы басылғаннан бері 100 миллисекундтан астам уақыт өтсе if (millis() - алдыңғыMillis >= 100) ( //Алғашқы уақыт операция есте сақталады алдыңғыMillis = millis(); if (led==oldled) ( //батырма күйінің өзгермегенін тексереді led=!led; )

Бұл код үзілістерді жоюға мүмкіндік береді және үзілістерде рұқсат етілмеген кідірту функциясы сияқты бағдарламаның орындалуын блоктамайды.

Таймер үзіледі

Таймер - 16 МГц процессордан алынған белгілі бір жиілікте санайтын есептегіш. Жиілік бөлгішті қалаған санау режимін алу үшін конфигурациялауға болады. Сондай-ақ, есептегішті белгіленген мәнге жеткенде үзілістерді жасау үшін теңшеуге болады.

Ал таймерді үзу миллисекунд сайын бір рет үзуге мүмкіндік береді. Arduino-да 3 таймер бар - Timer0, Timer1 және Timer2. Timer0 әр миллисекунд сайын үзілістерді жасау үшін пайдаланылады, ол есептегішті жаңартады және оны millis() функциясына береді. Бұл таймер сегіз разрядты және 0-ден 255-ке дейін санайды. Үзіліс мән 255-ке жеткенде жасалады. Әдепкі бойынша, 1 кГц-ке жақын жиілікті алу үшін 65 сағат бөлгіші пайдаланылады.

Салыстыру регистрлері таймердегі күйді және сақталған мәліметтерді салыстыру үшін қолданылады. Бұл мысалда санауыш 0xAF жеткенде код үзіліс жасайды.

TIMSK0 |= _BV(OCIE0A);

Таймерді үзу векторы үшін үзу өңдеушісін анықтау керек. Үзу векторы үзіліс шақырылған кезде орындалатын команданың орналасу мекенжайының көрсеткіші болып табылады. Бірнеше үзіліс векторлары үзіліс векторы кестесіне біріктірілген. Бұл жағдайда таймер TIMER0_COMPA_vect деп аталады. Бұл өңдеуші цикл() сияқты әрекеттерді орындайды.

СИГНАЛ(TIMER0_COMPA_vect) ( таңбаланбаған ұзын ағымдағыMillis = millis(); sweeper1.Update(currentMillis); if(digitalRead(2) == HIGH) ( sweeper2.Update(currentMillis); led1.Update(currentMillis); )date2 currentMillis); led3.Update(currentMillis); //Loop() функциясы бос қалады. жарамсыз цикл()( )

Қорытындылау

Arduino-дағы үзіліс - бұл өте күрделі тақырып, өйткені сіз бірден жобаның бүкіл архитектурасы туралы ойлануыңыз керек, кодтың қалай орындалатынын, қандай оқиғалар болуы мүмкін екенін, негізгі код үзілгенде не болатынын елестетіп көріңіз. Біз бұл тілдік құрылыммен жұмыс істеудің барлық ерекшеліктерін ашуды мақсат еткен жоқпыз; Болашақ мақалаларда үзілістер туралы әңгімені толығырақ жалғастырамыз.

Жоба барысында бірнеше үзілістер қажет болуы мүмкін, бірақ олардың әрқайсысында максималды басымдық болса, онда іс жүзінде функциялардың ешқайсысы оған ие болмайды. Дәл сол себепті оннан астам үзілістерді пайдалану ұсынылмайды.

Өңдеушілер тек уақыт аралықтарына ең жоғары сезімталдығы бар процестерге ғана қолданылуы керек. Бағдарлама үзу өңдегішінде болған кезде, барлық басқа үзулер өшірілетінін ұмытпаңыз. Үзілістердің көп саны олардың реакциясының нашарлауына әкеледі.

Бір үзіліс белсенді және басқалары өшірілген кезде, схема құрастырушы ескеруі керек екі маңызды нюанс пайда болады. Біріншіден, үзіліс уақыты мүмкіндігінше қысқа болуы керек.

Бұл басқа жоспарланған үзілістерді жіберіп алмауыңызды қамтамасыз етеді. Екіншіден, үзіліспен жұмыс істегенде, бағдарлама коды басқа үзулерден белсенділікті талап етпеуі керек. Егер бұл алдын алмаса, бағдарлама жай ғана қатып қалады.

Ұзақ мерзімді өңдеуді қолданбаңыз цикл() , ауыспалы мәнге орнатылған айнымалымен үзу өңдеушісінің кодын жасаған дұрыс. Ол бағдарламаға қосымша өңдеу қажет емес екенін айтады.

Егер функция шақырылса Жаңарту() әлі де қажет болса, алдымен күй айнымалысын тексеру керек. Бұл қосымша өңдеу қажет пе екенін анықтауға мүмкіндік береді.

Таймерді конфигурациялау алдында кодты тексеру керек. Anduino таймерлерін шектеулі ресурстарға жатқызу керек, өйткені олардың үшеуі ғана бар және олар әртүрлі функцияларды орындау үшін қолданылады. Егер сіз таймерлерді пайдаланумен шатассаңыз, онда бірқатар операциялар жұмысын тоқтатуы мүмкін.

Осы немесе басқа таймер қандай функцияларды орындайды?

Arduino Uno микроконтроллері үшін үш таймердің әрқайсысының өз әрекеттері бар.

Сонымен Таймер0 бесінші және алтыншы түйреуіштердегі PWM үшін жауапты, функциялар millis() , micros() , кешіктіру() .

Басқа таймер - Таймер1, тоғызыншы және оныншы түйреуіштердегі PWM, кітапханалармен бірге пайдаланылады WaveHC және Servo.

Таймер 2 11 және 13 түйреуіштердегі PWM-мен жұмыс істейді, сондай-ақ Тон.

Схема құрастырушы ортақ деректерді қауіпсіз пайдалануды қамтамасыз ету үшін қамқорлық жасауы керек. Өйткені, үзіліс процессордың барлық әрекеттерін миллисекундқа тоқтатады және олардың арасындағы деректер алмасуы цикл() және үзу өңдеушілері тұрақты болуы керек. Компилятор максималды өнімділікке жету үшін кодты оңтайландыруды бастағанда жағдай туындауы мүмкін.

Бұл процестің нәтижесі регистрде негізгі кодтық айнымалылардың көшірмесін сақтау болады, бұл оларға қол жеткізудің максималды жылдамдығын қамтамасыз етеді.

Бұл процестің кемшілігі нақты мәндердің сақталған көшірмелермен ауыстырылуы мүмкін, бұл функцияның жоғалуына әкелуі мүмкін.

Бұған жол бермеу үшін айнымалы мәнді пайдалану керек кернеулі , бұл қажетсіз оңтайландырулардың алдын алуға көмектеседі. Жаңартулар үшін циклдарды қажет ететін үлкен массивтерді пайдаланған кезде, осы жаңартулар кезінде үзілістерді өшіру керек.

Бұл бағдарламаны орнатқан кезде оның Arduino IDE-ге қаншалықты ұқсас екеніне таң қаласыз. Таң қалмаңыз, екі бағдарлама да бір қозғалтқышта жасалған.

Қолданбаның көптеген мүмкіндіктері бар, соның ішінде кітапхана Сериялық, осылайша біз тақта мен арасындағы деректерді тасымалдауды байланыстыра аламыз.

Arduino IDE іске қосып, деректерді шығарудың ең қарапайым мысалын таңдайық Сериялық порт:

Void setup() ( Serial.begin(9600); ) void loop() ( Serial.println("Hello Kitty!"); // келесі кешіктіруді жіберу алдында 500 миллисекунд күтіңіз(500); )

Мысалды іске қосып, код жұмыс істейтініне көз жеткізейік.

Деректерді қабылдау

Енді біз бірдей мәтінді алғымыз келеді. Жаңа жобаны бастаймыз және код жазайық.

Бірінші қадам - ​​кітапхананы импорттау. барайық Эскиз | Кітапхананы импорттау | Сериялық. Сызық эскизде пайда болады:

Import processing.serial.*; Сериялық серия; // сериялық порт нысанын құру Жол алынды; // сериялық порттан алынған деректер void setup() ( String port = Serial.list(); serial = new Serial(this, port, 9600); ) void draw() ( if (serial.available() > 0) ( // деректер болса, алынған = serial.readStringUntil("\n"); // деректерді оқу ) println(қабылданған // консольдегі деректерді көрсету )

Деректердің сериялық порттан алынуын қамтамасыз ету үшін бізге сыныптың нысаны қажет Сериялық. Біз Arduino көмегімен String түріндегі деректерді жібергендіктен, жолды өңдеуде алуымыз керек.

Әдісте орнату()қол жетімді сериялық портты алу керек. Әдетте бұл тізімдегі бірінші қол жетімді порт. Осыдан кейін біз нысанды конфигурациялай аламыз Сериялық, портты және деректерді беру жылдамдығын көрсетеді (жылдамдықтардың сәйкес болғаны жөн).

Тақтаны қайта қосу, өңдеуден эскизді іске қосу және қолданба консоліндегі кіріс деректерді байқау ғана қалады.

Өңдеу консольмен жұмыс істеуге ғана емес, сонымен қатар стандартты терезелерді жасауға мүмкіндік береді. Кодты қайта жазайық.

Import processing.serial.*; Сериялық серия; // сериялық порт нысанын құру Жол алынды; // сериялық порттан алынған деректер void setup() ( size(320, 120); String port = Serial.list(); serial = new Serial(this, port, 9600); ) void draw() ( if (serial) .available() > 0) ( // егер деректер болса, // оны оқыңыз және қабылданған айнымалыға жазыңыз = serial.readStringUntil("\n"); ) // Мәтіндік мәтіннің параметрлері(24); (алынған != null) ( мәтін(алынған, 10, 30); ) )

Мысалды қайтадан іске қосып, бір жерде қайта сызылған жазуы бар терезені көрейік.

Осылайша біз Arduino-дан деректерді алуды үйрендік. Бұл бізге әдемі графиктерді салуға немесе сенсордың көрсеткіштерін бақылау үшін бағдарламалар жасауға мүмкіндік береді.

Деректерді жіберу

Біз тақтадан деректерді қабылдап қана қоймай, оны компьютерден командаларды орындауға мәжбүрлей отырып, тақтаға деректерді жібере аламыз.

«1» таңбасын өңдеуден жібереміз делік. Тақта жіберілген таңбаны анықтағанда, 13-портта (кіріктірілген) жарық диодты қосыңыз.

Эскиз алдыңғыға ұқсас болады. Мысалы, шағын терезені жасайық. Терезе аймағында басқан кезде, біз «1» жібереміз және оны тексеру үшін консольде көшіреміз. Егер шертулер болмаса, онда «0» командасы жіберіледі.

Import processing.serial.*; Сериялық серия; // сериялық порт нысанын құру Жол алынды; // сериялық порттан алынған деректер void setup() ( size(320, 120); String port = Serial.list(); serial = new Serial(this, port, 9600); ) void draw() ( if (mousePressed) == true) (//терезеде тінтуірді шертсек serial.write("1"); //1 println("1" жіберу); ) басқа ( //егер басу болмаса serial.write ("0" ) //жіберу 0 ) )

Енді Arduino үшін эскиз жазайық.

Char командасының мәні; // сериялық порттан келетін деректер int ledPin = 13; // кірістірілген LED void setup() ( pinMode(ledPin, OUTPUT); // деректерді шығару режимі Serial.begin(9600); ) void loop() ( if (Serial.available()) ( commandValue = Serial.read ( ) if (commandValue == "1") ( digitalWrite(ledPin, HIGH); // LED қосу ) else ( digitalWrite(ledPin, LOW); // әйтпесе өшіру ) келесі деректер оқу)

Екі эскизді де орындайық. Терезенің ішіне шертіп, жарық диоды жанып тұрғанын байқаймыз. Сізге тіпті басу қажет емес, бірақ тінтуір түймесін басып тұрыңыз - жарық диоды үнемі жанады.

Деректер алмасу

Енді екі тәсілді біріктіріп, тақта мен қолданба арасында екі бағытта хабарлама алмасуға тырысайық.

Максималды тиімділік үшін логикалық айнымалыны қосайық. Нәтижесінде, біз бұдан былай Өңдеуден 1 немесе 0 жіберудің қажеті жоқ және сериялық порт жүктелмейді және қажетсіз ақпаратты жібермейді.

Тақта жіберілген бірлікті анықтаған кезде, логикалық мәнді ағымдағы күйге қатысты керісінше өзгертеміз ( ТӨМЕНқосулы ЖОҒАРЫжәне керісінше). IN басқаБіз «Hello Kity» жолын қолданамыз, оны «1» анықталмаған жағдайда ғана жібереміз.

Функция орнатуContact()Өңдеуде аламыз деп күткен жолды жібереді. Жауап келсе, өңдеу деректерді қабылдай алады.

Char командасының мәні; // сериялық порттан келетін деректер int ledPin = 13; логикалық ledState = ТӨМЕН; //жарық диодты void setup() күйін басқару ( pinMode(ledPin, OUTPUT); Serial.begin(9600); kurmakContact(); // ресивер жауап берген кезде контакт үшін байт жіберу) void loop() ( / / егер деректерді оқу мүмкін болса, егер (Serial.available() > 0) ( // деректерді оқу commandValue = Serial.read(); if (commandValue == "1") ( ledState = !ledState; digitalWrite(ledPin, ledState) ) delay(100) ) else ( // Serial.println("Hello Kitty"); ) delay(50 ) void establishContact() ( while (Serial.available());<= 0) { Serial.println("A"); // отсылает заглавную A delay(300); } }

Өңдеу эскизіне көшейік. әдісті қолданамыз serialEvent(), ол буферде белгілі бір таңба кездескен сайын шақырылатын болады.

Жаңа логикалық айнымалыны қосайық бірінші Байланыс, бұл Arduino-ға қосылым бар-жоғын анықтауға мүмкіндік береді.

Әдісте орнату()жолды қосыңыз serial.bufferUntil("\n");. Бұл бізге белгілі бір таңбаға тап болғанша кіріс деректерді буферде сақтауға мүмкіндік береді. Бұл жағдайда біз (\n) қайтарамыз, себебі біз жібереміз Serial.println() Arduino-дан. "\n"соңында жаңа жолды іске қосатынымызды білдіреді, яғни бұл біз көретін соңғы деректер болады.

Біз үнемі деректерді жіберіп жатқандықтан, әдіс serialEvent()цикл тапсырмаларын орындайды сызу(), содан кейін оны бос қалдыруға болады.

Енді негізгі әдісті қарастырайық serialEvent(). Біз жаңа жолды (\n) енгізген сайын бұл әдіс шақырылады. Әр жолы келесі әрекеттер тізбегі орындалады:

  • Кіріс деректер оқылады;
  • Оларда қандай да бір мәндердің бар-жоғы тексеріледі (яғни бізге бос деректер массиві немесе «нөл» берілді ме);
  • Бос орындарды алып тастаңыз;
  • Қажетті деректерді бірінші рет алған болсақ, логикалық айнымалының мәнін өзгертеміз бірінші Байланысжәне Arduino-ға жаңа деректерді қабылдауға дайын екенімізді айтыңыз;
  • Егер бұл қажетті деректер түрін бірінші қабылдау болмаса, біз оны консольде көрсетеміз және жасалған басу туралы микроконтроллер деректерін жібереміз;
  • Біз Arduino-ға жаңа деректер пакетін алуға дайын екенімізді хабарлаймыз.
импорт өңдеу.сериялық.*; Сериялық серия; // сериялық порт нысанын құру Жол алынды; // сериялық порттан алынған деректер // Arduino логикалық біріншіден алынған деректерді тексеруContact = false; void setup() (өлшем(320, 120); Жол порты = Serial.list(); сериялық = жаңа Сериялық(бұл, порт, 9600); serial.bufferUntil("\n"); ) void draw() ( ) void serialEvent(Serial myPort) ( //келетін деректерден жолды құру // "\n" - бөлгіш - алынған деректер пакетінің соңы = myPort.readStringUntil("\n"); //біздің деректеріміздің екеніне көз жеткізіңіз жалғастыру жолы алдында бос емес if (қабылданды != null) ( //алынған бос орындарды алып тастаңыз = қию(қабылданды); println(қабылданды); //қол алысуды бастау үшін «А» жолын іздеңіз //егер табылса, тазалаңыз буфер және деректерге сұрау жіберу if (firstContact == false) ( if (received.equals("A"))) ( serial.clear(); firstContact = true; myPort.write("A"); println("contact "); ) ) else ( //егер контакт орнатылған болса, біз деректерді қабылдаймыз және талдаймыз println(қабылданған); if (тінтуірдің басылуы == true) (//терезені бассақ serial.write("1) "); //1 println(" 1"); ) жіберу // барлық деректер болған кезде, жаңа бумаға сұраныс жасаңыз serial.write("A"); ) ) )

Қосылған және іске қосылған кезде консольде «Hello Kitty» тіркесі пайда болуы керек. Өңдеу терезесінде тінтуірді басқан кезде, 13 істікшедегі жарық диоды қосылады және өшеді.

Өңдеуден басқа, сіз PuTTy бағдарламаларын пайдалана аласыз немесе порттармен жұмыс істеуге арналған дайын сыныптарды пайдаланып C# тілінде өз бағдарламаңызды жаза аласыз.

04.Байланыс: Диммер

Мысал ЖШД жарықтығын басқару үшін деректерді компьютерден тақтаға қалай жіберуге болатынын көрсетеді. Деректер 0-ден 255-ке дейінгі жеке байттар түрінде келеді. Деректер өңдеуді қоса, сериялық портқа кіру мүмкіндігі бар компьютердегі кез келген бағдарламадан алынуы мүмкін.

Мысалы, сізге резисторы бар стандартты схема және 9 түйреуіште жарықдиодты қажет болады.

Arduino үшін эскиз.

Const int ledPin = 9; // 9 pinдегі ​​жарық диоды void setup() ( Serial.begin(9600); // режимді pinMode(ledPin, OUTPUT); ) void loop() (байт жарықтығы; // деректердің бар-жоғын тексеріңіз. компьютер егер (Serial.available()) ( // 0-ден 255-ке дейінгі соңғы қабылданған байтты оқу жарықтық = Serial.read(); // LED жарықтығын орнату analogWrite(ledPin, жарықтық; ) )

Өңдеу коды

Import processing.serial.*; Сериялық порт; void setup() ( size(256, 150); println("Қолжетімді сериялық порттар:"); println(Serial.list()); // Осы тізімдегі бірінші портты пайдаланады (0 саны). Оны таңдау үшін өзгертіңіз. порт // Arduino тақтасына сәйкес келетін соңғы параметр (мысалы, 9600) - бұл Arduino эскиз портындағы // Serial.begin() параметріне берілген мәнге сәйкес келуі керек. (this, Serial.list(), 9600); // Arduino тақтасы пайдаланатын порттың атын білсеңіз, //port = new Serial(this, "COM1", 9600 ) void draw() параметрін нақты көрсетіңіз. (// үшін қарадан аққа қарай градиент салу (int i = 0; i).

Оны іске қосыңыз және тінтуірді жасалған терезенің үстіне кез келген бағытта жылжытыңыз. Солға жылжытқанда жарық диоды азаяды, оңға жылжытқанда ол артады.

04.Байланыс: PhysicalPixel (Тінтуірдің көмегімен жарық диодты шамды жағыңыз)

Мәселені сәл өзгертейік. Біз тінтуірді шаршының үстіне жылжытамыз және тақтадағы жарық диодты жарықтандыру үшін «H» (Жоғары) белгісін жібереміз. Тінтуір төртбұрышты аймақтан шыққанда, жарық диодты өшіру үшін «L» (Төмен) белгісін жібереміз.

Arduino үшін код.

Const int ledPin = 13; // LED int incomingByte үшін pin 13; // деректерді қабылдауға арналған айнымалы void setup() ( Serial.begin(9600); pinMode(ledPin, OUTPUT); ) void loop() ( // егер деректер болса, егер (Serial.available() > 0) ( // буфердегі оқу байты incomingByte = Serial.read(); // егер бұл H таңбасы болса (ASCII 72), егер (incomingByte == "H") болса, жарық диодты қосыңыз (digitalWrite(ledPin, HIGH); ) / / егер бұл L таңбасы болса ( ASCII 76), егер (incomingByte == "L") ( digitalWrite(ledPin, LOW); ) ) ) жарық диодты өшіріңіз.

Өңдеу коды.

Import processing.serial.*; float boxX; float boxY; int boxSize = 20; логикалық mouseOverBox = жалған; Сериялық порт; void setup() (өлшем(200, 200); boxX = ені / 2.0; boxY = биіктік / 2.0; rectMode(RADIUS); println(Serial.list()); // Arduino тақтасы қосылған портты ашыңыз (бұл жағдайда #0) // Arduino пайдаланатын жылдамдықпен портты ашуды тексеріңіз (9600бит/с) порт = new Serial(this, Serial.list(), 9600 ) void draw() ( background(0); // Егер курсор шаршының үстінде болса, (mouseX > boxX - boxSize && mouseX boxY - boxSize && mouseY);

04. Коммуникация: График (график салу)

Алдыңғы мысалда мәліметтерді компьютерден тақтаға жіберсек, енді қарама-қарсы тапсырманы орындаймыз – потенциометрден мәліметтерді алып, оны график түрінде көрсетеміз.


Аппараттық үзілістер

Мен бұл сабақ үшін күлкілі сурет таба алмадым, мен тек бағдарламалау туралы лекция таптым, және осы дәрістің басы бізге нені жақсы түсіндіреді үзу. Arduino-дағы үзіліс дәл осылай сипатталуы мүмкін: микроконтроллер «бәрін тастайды», үзу өңдегішіндегі функциялар блогын орындауға ауысады, оларды орындайды, содан кейін ол тоқтаған негізгі кодтағы орынға дәл оралады.

Үзілістердің әртүрлі түрлері бар, яғни үзілістердің өзі емес, олардың себептері: үзіліс аналогты-цифрлық түрлендіргіштің, таймер-есептегіштің немесе сөзбе-сөз микроконтроллердің түйреуішінің әсерінен болуы мүмкін. Бұл үзілістер сыртқы үзілістер деп аталады. аппараттық құрал, және бұл біз бүгін сөйлесетін нәрсе.

Сыртқы аппараттық үзілісмикроконтроллер түйреуішіндегі кернеудің өзгеруінен туындаған үзіліс. Ең бастысы, микроконтроллер (есептеуіш ядро) пинге сауалнама жүргізбейдіЖәне оған уақыт жұмсамайды, түйреуіш басқа аппараттық құралмен өңделеді. Істікшедегі кернеу өзгерген бойда (сандық сигналды білдіреді, +5 қолданылған/+5 жойылды) микроконтроллер сигналды қабылдайды, бәрін тастайды, үзуді өңдейді және жұмысқа оралады. Бұл не үшін қажет? Көбінесе үзілістер негізгі кодты жүктемей-ақ қысқа оқиғаларды - импульстарды анықтау үшін немесе тіпті олардың санын санау үшін қолданылады. Аппараттық үзіліс күрделі ұзақ есептеулер немесе кодтың кешігуі кезінде қысқа түймені басу немесе сенсорлық триггерді ұстай алады, яғни. дөрекі айтқанда, түйреуіш сауалнама жүргізілуде негізгі кодқа параллель. Үзілістер сонымен қатар барлық дерлік перифериялық құрылғылар өшірілген кезде микроконтроллерді қуат үнемдеу режимдерінен оятуы мүмкін. Arduino IDE жүйесінде аппараттық үзілістермен қалай жұмыс істеу керектігін көрейік.

Arduino-да үзілістер

Барлық түйреуіштер үзіліс жасай алмайтындығынан бастайық. Иә, мұндай нәрсе бар pinChangeInterrupts, бірақ бұл туралы тереңдетілген сабақтарда айтатын боламыз. Енді біз аппараттық үзілістер белгілі бір түйреуіштерді ғана жасай алатынын түсінуіміз керек:

MK / үзу нөмірі INT 0 INT 1 INT 2 INT 3 INT 4 INT 5
ATmega 328/168 (Nano, UNO, Mini) D2 D3
ATmega 32U4 (Leonardo, Micro) D3 D2 D0 D1 D7
ATmega 2560 (Мега) D2 D3 D21 D20 D19 D18

Кестеден түсінгеніңіздей, үзілістердің пин нөмірінен ерекшеленетін өз нөмірі бар. Айтпақшы, ыңғайлы функция бар digitalPinToInterrupt(pin), ол PIN нөмірін алып, үзу нөмірін қайтарады. Бұл функцияны Arduino nano-ға 3 санын беру арқылы біз 1 аламыз. Барлығы жоғарыдағы кестеге сәйкес, жалқауларға арналған функция.

Үзіліс функциясы арқылы қосылады attachInterrupt (pin, өңдеуші, режим):

  • түйреуіш– үзу саны
  • өңдеуші– үзу өңдеушісі функциясының атауы (оны өзіңіз жасауыңыз керек)
  • режимі– үзу жұмысының «режимі»:
    • ТӨМЕН(төмен) – сигнал арқылы іске қосылады ТӨМЕНтүйреуіште
    • ЖОҒАРЫЛУ(өсу) – c пиніндегі сигнал өзгерген кезде іске қосылады ТӨМЕНқосулы ЖОҒАРЫ
    • ҚҰЛАУ(түсіру) – түйреуіштегі сигнал өзгерген кезде іске қосылады ЖОҒАРЫқосулы ТӨМЕН
    • ӨЗГЕРТУ(өзгерту) – сигнал өзгерген кезде іске қосылады (мен ТӨМЕНқосулы ЖОҒАРЫжәне керісінше)

Үзуді функцияны пайдаланып өшіруге де болады detachInterrupt(pin), мұндағы түйреуіш – қайтадан үзу саны.

Сондай-ақ, функцияны пайдаланып үзулерді жаһандық түрде өшіруге болады үзіліссіз()және оларды қайтадан шешу үзіліс(). Олармен абай болыңыз! үзіліссіз()сонымен қатар таймер үзілістерін тоқтатады және барлық уақыт функциялары мен PWM генерациясы үзіледі.

Түймені басу үзілісте есептелетін, бірақ негізгі циклде олар 1 секунд кідіріспен шығарылатын мысалды қарастырайық. Түймемен қалыпты режимде жұмыс істей отырып, мұндай өрескел шығысты кідіріспен біріктіру мүмкін емес:

Тұрақты инт санауышы = 0; // санауыш айнымалысы void setup() ( Serial.begin(9600); // байланыс үшін порт ашылды // D2 және GND pinMode(2, INPUT_PULLUP) бойынша түйме қосылды; \ // D2 үзіліс 0 // өңдеуші - функция buttonTick // FALLING - түйме басылғанда 0 сигналы болады, біз оны тіркеймізInterrupt(0, buttonTick, FALLING) void buttonTick() ( counter++; // + басу ) void loop() ( Serial.println (санауыш) // шығыс кідірісі (1000 // күту);

Сонымен, біздің код кешіктіру кезінде де пернелерді басуларды санайды! Тамаша. Бірақ не ұшпа? Біз жаһандық айнымалыны жарияладық есептегіш, ол түймені басу санын сақтайды. Егер үзілісте айнымалының мәні өзгерсе, бұл туралы микроконтроллерге спецификатор арқылы хабарлау керек. ұшпа, ол айнымалының деректер түрін көрсету алдында жазылады, әйтпесе жұмыс дұрыс емес болады. Бұл жай ғана есте сақтау керек нәрсе: егер айнымалы үзілісте өзгерсе, оны жасаңыз ұшпа.

Тағы бірнеше маңызды тармақтар:

  • Үзілісте өзгертілген айнымалылар ретінде жариялануы керек ұшпа
  • Мұндай кешігулер үзілістерде жұмыс істемейді кешіктіру()
  • Үзілісте оның мағынасын өзгертпейді millis()Және micros()
  • Үзілісте портқа шығу дұрыс жұмыс істемейді ( Serial.print()), сонымен қатар оны ол жерде қолданбау керек - ол ядроны жүктейді
  • Үзіліс кезінде сіз мүмкіндігінше аз есептеулерді және әдетте «ұзақ» әрекеттерді орындауға тырысуыңыз керек - бұл жиі үзілістермен МК жұмысын бәсеңдетеді! Енді не істеу керек? Төменде оқыңыз.

Егер үзіліс дереу өңдеуді қажет етпейтін оқиғаны ұстаса, онда үзумен жұмыс істеу үшін келесі алгоритмді қолданған дұрыс:

  • Үзуді өңдеуде біз жай ғана жалаушаны көтереміз
  • Бағдарламаның негізгі циклінде біз жалаушаны тексереміз, егер ол көтерілсе, біз оны қалпына келтіреміз және қажетті әрекеттерді орындаймыз
өзгермелі логикалық intFlag = жалған; // flag void setup() ( Serial.begin(9600); // байланыс үшін портты ашты // түймені D2 және GND pinMode(2, INPUT_PULLUP) қосты; // D2 үзіліс 0 // өңдеуші - функция түймешігін белгілеңіз // FALLING - түйме басылғанда 0 сигналы болады, біз оны тіркеймізInterrupt(0, buttonTick, FALLING) void buttonTick() ( intFlag = true; // үзу жалауын көтерді ) void loop() ( егер (intFlag) ( intFlag = жалған; // қалпына келтіру // кейбір әрекеттерді орындау Serial.println("Үзу!");

Бұл, негізінен, үзілістер туралы білуіңіз керек, біз қосымша сабақтарда нақты жағдайларды қарастырамыз.

Бейне

Таймер үзулерімен жұмыс істеуді үйренейік. Параллель процестері бар қарапайым программа жазайық.

Нақты бағдарламада көптеген әрекеттер бір уақытта орындалуы керек. Кіріспеде мен мысал келтірдім. Мен оның қандай әрекеттерді орындайтынын тізімдеймін:

Операция

Цикл уақыты
3 түймені сұрайды, секіруді жою үшін олардан сигналдарды өңдейді 2 мс
Жеті сегментті жарық диоды индикаторларынан деректерді қалпына келтіреді 2 мс
2 DS18B20 температура сенсорлары үшін басқару сигналдарын жасайды және олардан деректерді оқиды. Датчиктерде 1 сымды сериялық интерфейс бар. Әрбір бит үшін 100 мкс,
1 сек жалпы оқу циклі
Пельтиер элементіндегі ток пен кернеудің аналогтық мәндерін оқу, қоректендіру кернеуі 100 мкс
Ток пен кернеудің аналогтық мәндерін цифрлық фильтрлеу 10 мс
Peltier элементінде қуатты есептеу 10 мс
PID (пропорционалды интегралдық дифференциал) ток пен кернеуді тұрақтандыру контроллері 100 мкс
Қуат реттегіші 10 мс
Температура реттегіші 1 сек
Қауіпсіздік функциялары, деректер тұтастығын бақылау 1 сек
Басқару, жүйе жұмысының жалпы логикасы 10 мс

Бұл операциялардың барлығы циклдік түрде орындалады, олардың әрқайсысында әртүрлі цикл кезеңдері бар. Олардың ешқайсысын уақытша тоқтатуға болмайды. Жұмыс уақытының кез келген, тіпті қысқа мерзімді өзгеруі қиындықтарға әкеледі: өлшеудің елеулі қатесі, тұрақтандырғыштардың дұрыс жұмыс істемеуі, индикаторлардың жыпылықтауы, түймелерді басудың тұрақсыз реакциясы және т.б.

Тоңазытқыш контроллері бағдарламасында осы әрекеттердің барлығын орындайтын бірнеше параллель процестер бар, олардың әрқайсысы өз кезең уақыты бар циклде. Параллель процестер – әрекеттері бір уақытта орындалатын процестер.

Алдыңғы сабақтарда түйме нысаны үшін класс құрдық. Бұл параллельді процесте сигналды өңдеуге арналған класс екенін айттық. Оның қалыпты жұмыс істеуі үшін сигналды өңдеу функциясын (әдісін) қалыпты кезеңмен циклге шақыру қажет (біз 2 мс уақытты таңдадық). Содан кейін бағдарламаның кез келген жерінде түйменің немесе сигналдың ағымдағы күйін көрсететін белгілер бар.

Бір циклде біз түймелердің күйін өңдеуге және жарық диодтарын басқаруға арналған кодты орналастырдық. Ал цикл соңында delay(2) кешіктіру функциясын орнатамыз. Бірақ циклдегі бағдарламаны орындауға кететін уақыт циклдің жалпы уақытын өзгертеді. Ал цикл кезеңі анық 2 мс тең емес. Сонымен қатар, delay() функциясын орындау кезінде бағдарлама қатып қалады және басқа әрекеттерді орындай алмайды. Күрделі бағдарлама толық хаосқа әкеледі.

Шығу - аппараттық таймерден үзілген кезде түйме күйін өңдеуге арналған функцияны шақырыңыз. Әр 2 мс сайын негізгі бағдарлама циклі үзілуі керек, түйме сигналы өңделеді және басқару элементі негізгі циклге үзілген кодқа оралады. Түйме сигналын өңдеудің қысқа уақыты негізгі циклдің орындалуына айтарлықтай әсер етпейді. Анау. Түймелерді өңдеу негізгі бағдарламада байқалмай, параллель орындалады.

Аппараттық таймердің үзілуі.

Аппараттық үзіліс – бұл қандай да бір оқиғаны хабарлайтын сигнал. Ол келгеннен кейін бағдарламаның орындалуы тоқтатылады және басқару үзіліс өңдеушісіне өтеді. Өңдеуден кейін басқару үзілген бағдарлама кодына оралады.

Бағдарлама тұрғысынан үзіліс – программа кодына тікелей қатысы жоқ сыртқы оқиғаға байланысты функционалды шақыру.

Таймерді үзу сигналы белгілі бір уақыт кезеңімен циклдік түрде жасалады. Ол аппараттық таймер арқылы жасалады - белгілі бір мәнге жеткенде оның кодын қалпына келтіретін логикасы бар есептегіш. Қалпына келтіру логикасы үшін кодты бағдарламалық түрде орнату арқылы біз таймерді үзу кезеңінің уақытын орната аламыз.

Arduino таймер кезеңінің режимі мен уақытын орнату микроконтроллердің аппараттық регистрлері арқылы жүзеге асырылады. Қаласаңыз, мұның қалай жасалатынын анықтай аласыз. Бірақ мен қарапайым опцияны ұсынамын - MsTimer2 кітапханасын пайдалану. Сонымен қатар, таймер режимін орнату сирек кездеседі, яғни кітапхана функцияларын пайдалану бағдарламаны баяулатпайды.

Кітапхана MsTimer2.

Кітапхана микроконтроллердің 2-таймерінен аппараттық үзуді конфигурациялауға арналған. Оның тек үш функциясы бар:

  • MsTimer2::set(қолтаңбасыз ұзақ мс, жарамсыз (*f)())

Бұл функция үзу кезеңінің уақытын мс-мен белгілейді. Осы кезеңмен f үзіліс өңдеушісі шақырылады. Ол жарамсыз деп жариялануы керек (ештеңені қайтармайды) және дәлелдері жоқ. * f — функция көрсеткіші. Оның орнына функцияның атын жазу керек.

  • MsTimer2::start()

Функция таймер үзілістерін қосады.

  • MsTimer2::stop()

Функция таймер үзілістерін өшіреді.

Функциялар атауының алдында MsTimer2:: жазу керек, өйткені Кітапхана аттар кеңістігі директивасы арқылы жазылады.

Кітапхананы орнату үшін MsTimer2 каталогын Arduino IDE жұмыс қалтасындағы кітапханалар қалтасына көшіріңіз. Содан кейін Arduino IDE бағдарламасын іске қосыңыз, ашыңыз Эскиз -> Кітапхананы қосужәне MsTimer2 кітапханасының кітапханалар тізімінде бар екенін көріңіз.

MsTimer2 кітапханасын zip мұрағатында жүктеп алуға болады. Оны орнату үшін оны орауыштан шығару керек.

Түйме сигналын параллель өңдейтін қарапайым бағдарлама.

Енді 6-сабақтан бір түйме және жарық диоды бар қарапайым бағдарламаны жазайық. Бір түйме диаграмма бойынша Arduino тақтасына қосылған:

Бұл келесідей көрінеді:

Әрбір түймені басқан сайын Arduino тақтасындағы жарық диоды күйін өзгертеді. MsTimer2 және Button кітапханаларын орнату қажет:

MsTimer2

Және төлеңіз. Бар болғаны 40 руб. барлық сайт ресурстарына қол жеткізу үшін айына!

// эскиз_10_1 10-сабақ
// Түймені басу жарық диоды күйін өзгертеді

#қосу
#қосу

#LED_1_PIN 13 анықтау //
#BUTTON_1_PIN 12 анықтау // түйме 12 түйреуішке қосылған

Түйме түймесі1(BUTTON_1_PIN, 15); // объект құру – батырмасы

жарамсыз орнату() (

MsTimer2::set(2, timerInterrupt); // таймерді үзу кезеңін 2 мс етіп орнатыңыз
MsTimer2::start(); //
}

жарамсыз цикл() (

// Жарық диодты басқару
егер (button1.flagClick == шын) (
// түйме басылды



}
}

// үзу өңдеушісі
void timerInterrupt() (
button1.scanState(); // түйме үшін тұрақты күйді күту әдісін шақыру
}

setup() функциясында біз таймерді үзу циклінің уақытын 2 мс етіп орнаттық және үзіліс өңдеушісі timerInterrupt атауын көрсетеміз. Түйме сигналын өңдеу функциясы button1.scanState() таймерді үзу өңдегішінде 2 мс сайын шақырылады.

Осылайша, біз түйме күйін параллельді процесте өңдейміз. Ал бағдарламаның негізгі циклінде біз түймені басу белгісін тексереміз және жарық диоды күйін өзгертеміз.

Құбылмалы квалификация.

Алдыңғы бағдарламадағы loop() циклін өзгертейік.

жарамсыз цикл() (

while(шын) (
егер (button1.flagClick == true) үзу;
}

// түйме басылды
button1.flagClick= false; // белгіні қалпына келтіріңіз
digitalWrite(LED_1_PIN, ! digitalRead(LED_1_PIN)); // Жарықдиодты инверсия
}

Логикалық тұрғыдан ештеңе өзгерген жоқ.

  • Бірінші нұсқада бағдарлама циклды соңына дейін жүріп өтіп, ондағы button1.flagClick жалаушасын талдады.
  • Екінші нұсқада бағдарлама шексіз while циклінде button1.flagClick жалауын талдайды. Жалау белсенді болған кезде, үзіліс арқылы уақытша циклден шығып, жарық диоды күйін өзгертеді.

Жалғыз айырмашылық - бұл цикл немесе while бағдарламасының қай циклде орындалатынында.

Бірақ егер біз бағдарламаның соңғы нұсқасын іске қоссақ, жарық диоды түймелерді басуға жауап бермейтінін көреміз. Классты алып тастап, бағдарламаны жеңілдетейік.

#қосу
#LED_1_PIN 13 анықтау // Жарық диоды 13 істікшеге қосылған
int count=0;

жарамсыз орнату() (
pinMode(LED_1_PIN, OUTPUT); // шығыс ретінде жарық диодты істікшесін анықтаңыз
MsTimer2::set(500, timerInterrupt); // таймерді үзу кезеңін 500 мс етіп орнатыңыз
MsTimer2::start(); // таймер үзілісін қосыңыз
}

жарамсыз цикл() (

ал (шын) (
егер (санау != 0) үзілсе;
}

санау= 0;
digitalWrite(LED_1_PIN, ! digitalRead(LED_1_PIN)); // Жарық диодты күйдің инверсиясы
}

// үзу өңдеушісі
void timerInterrupt() (
санау++;
}

Бұл бағдарламада санауыш әр 500 мс сайын үзу өңдеуішінде 1-ге артады. while циклінде ол талданады, үзіліс кезінде біз циклден шығып, жарық диоды күйін өзгертеміз. Сіз қарапайым бағдарламаны елестете алмадыңыз, бірақ ол да жұмыс істемейді.

Өйткені, C++ тілінің компиляторы бағдарламаны өзінің интеллектіне сәйкес оңтайландырады. Кейде бұл жақсы нәтиже бермейді. Компилятор while циклінде count айнымалысы бойынша ешқандай операциялар орындалмайтынын көреді. Сондықтан ол санаудың күйін бір рет қана тексеру жеткілікті деп есептейді. Неліктен циклде ешқашан өзгермейтін нәрсені тексеріңіз. Компилятор кодты түзетеді, оны орындау уақытына оңтайландырады. Қарапайым сөзбен айтқанда, ол циклден айнымалыны тексеру кодын жояды. Компилятор count айнымалысы үзу өңдегішіндегі күйін өзгертетінін түсіне алмайды. Нәтижесінде біз while циклінде тұрып қаламыз.

Циклды соңына дейін орындайтын программа нұсқаларында компилятор барлық айнымалылар өзгеруі мүмкін деп есептейді және тексеру кодын қалдырады. Кез келген жүйе функциясына қоңырауды while цикліне енгізсеңіз, компилятор айнымалы мәндердің өзгеруі мүмкін екенін де шешеді.

Мысалы, уақытша циклге delay() функциясына қоңырау қоссаңыз, бағдарлама жұмыс істейді.

ал (шын) (
егер (санау != 0) үзілсе;
кешіктіру(1);
}

Жақсы стиль - цикл соңына дейін орындалатын және бағдарлама еш жерде қатып қалмайтын бағдарламаларды жасау. Келесі сабақта жалаушаларды шексіз while циклдерінде талдайтын жалғыз код болады. Әрі қарай, циклды барлық бағдарламаларда соңына дейін орындауды жоспарлап отырмын.

Кейде бұл оңай немесе тиімді емес. Содан кейін ұшпа квалификацияны пайдалану керек. Ол айнымалы жарияланғанда көрсетіледі және компиляторға оны пайдалануды оңтайландыруға әрекет жасамауды айтады. Ол компилятордың айнымалының мәні туралы жорамал жасауына жол бермейді, себебі айнымалы басқа бағдарлама бірлігінде, мысалы, параллель процессте өзгертілуі мүмкін. Сондай-ақ, компилятор айнымалы мәнді жалпы мақсаттағы регистрлерге емес, жедел жадқа орналастырады.

Бағдарламада санауды жариялағанда жазу жеткілікті

өзгермелі int саны=0;

және барлық опциялар жұмыс істейді.

Түймені басқаратын бағдарлама үшін Button класының данасы қасиеттерінің өзгеруі мүмкін екенін жариялау керек.

өзгермелі Түйме түймесі1(BUTTON_1_PIN, 15); // объект құру – батырмасы

Менің бақылауларым бойынша, ұшпа квалификацияны пайдалану бағдарлама кодының ұзақтығын ешқандай жолмен арттырмайды.

Түйме сигналын өңдеу әдісін Bounce кітапханасымен салыстыру.

Bounce түймелерін жоюға арналған дайын кітапхана бар. Түймешік күйі update() функциясы шақырылған кезде тексеріледі. Бұл функцияда:

  • түйме сигналы оқылады;
  • жаңартуға алдыңғы шақыру кезіндегі күймен салыстырғанда();
  • millis() функциясы арқылы алдыңғы қоңыраудан қанша уақыт өткенін тексереді;
  • түйме күйінің өзгерген-өзгермегені туралы шешім қабылданады.
  • Бірақ бұл параллель сигналды өңдеу емес. update() функциясы әдетте негізгі, асинхронды бағдарлама циклінде шақырылады. Белгілі бір уақыттан артық шақырылмаса, түйме сигналының ақпараты жоғалады. Тұрақты емес қоңыраулар алгоритмнің дұрыс жұмыс істемеуіне әкеледі.
  • Функцияның өзінде өте көп код бар және Button() кітапханасының функцияларына қарағанда орындалуы әлдеқайда көп уақытты алады.
  • Орташа мәнге негізделген сигналдарды сандық сүзгілеу мүлде жоқ.

Бұл кітапхананы күрделі бағдарламаларда қолданбаған дұрыс.

Келесі сабақта параллель процестері бар күрделірек программа жазамыз. Бір таймер үзуінен әртүрлі уақыт интервалдары бар циклдерде программа блоктарының орындалуын жүзеге асыруды үйренейік.

Санат: . Оны бетбелгілеуге болады.