Помощь с аппаратными таймерами.
- Войдите на сайт для отправки комментариев
Пнд, 12/06/2017 - 11:40
Добрый день господа форумчане, я пришел за помощью в вопросе таймеров.
// Обработчик переполнения счетчика. ISR(TIMER1_OVF_vect) { if(timerConf==true && timerRun==true) pop_task(); if( (MAX_ULONG-microsec)<MAXMICROTIMER){ //Если произойдет переполнение. microsec=MAXMICROTIMER-(MAX_ULONG-microsec); return; } microsec+=MAXMICROTIMER; } //---------------------------------------------------------------------------------------------------- // Совпадение A таймера-счётчика 1 ISR(TIMER2_COMPA_vect) //Регистр TCNT1 принял значение, равное регистру OCR1A { //run_funcTL(jobCOMPA); busyCOMPA=false; //Выставить триггер занятости на свободный timerCOMPAdisable(); //Отключить прерывания по сравнению. pop_task(); //Найти новую задачу. } //---------------------------------------------------------------------------------------------------- // Совпадение B таймера-счётчика 1 ISR(TIMER2_COMPB_vect) //Регистр TCNT1 принял значение, равное регистру OCR1B { //run_funcTL(jobCOMPB); busyCOMPB=false; //Выставить триггер занятости на свободный timerCOMPBdisable(); //Отключить прерывания по сравнению. pop_task(); //Найти новую задачу. } //**************************************************************************************************** // Тумблеры включения/отключения прерываний по сравнению OCR1 и OCR2 void timerCOMPAenable() { TIMSK1=(busyCOMPB==false) ? 0x03 : 0x07; } //---------------------------------------------------------------------------------------------------- void timerCOMPAdisable() { TIMSK1=(busyCOMPB==false) ? 0x01 : 0x05; } //---------------------------------------------------------------------------------------------------- void timerCOMPBenable() { TIMSK1=(busyCOMPA==false) ? 0x07 : 0x05; } //---------------------------------------------------------------------------------------------------- void timerCOMPBdisable() { TIMSK1=(busyCOMPA==false) ? 0x01 : 0x03; } //**************************************************************************************************** // Настройка пуск и стоп таймера int settingTimer() { TCCR1A=0x00; //настройка таймера TCNT1=0x00; //Обнуляем регистр счетчика. OCR1A=0x00; //обнуление регистра сравнения (записывается функциями) OCR1B=0x00; //тоже но второй регистр сравнения 2 TIMSK1=0x01; //Разрешаем прерывания по переполнению. init_tl(); // Инициализируем список. timerConf=true; } //---------------------------------------------------------------------------------------------------- int startTimer() { if(!timerConf){ return -1; //Вначале нужно вызвать settingTimer! } timerRun=true; TCCR1B=0x01; // Настраиваем прескалер на тактирования от системного кварца. } //---------------------------------------------------------------------------------------------------- int stopTimer() { if(timerRun){ TCCR1B=0x00; //Настраиваем регистр на отсутствие источника тактирования( таймер остановлен ) timerRun=false; microsec=0; } }
Вызов startTimer вызывает хаотичную перезагрузку микроконтроллера. Все перепробывал, тчетно. Возможно у гуру есть ответ в чем же я ошибься ? Благодарю.
в чем же я ошибься ?
1. в строках 14 и 23 указаны таймер2, а не 1. Следовательно может быть все что угодно;
2. Главная ошибка в создании очередной ОС для Ардуино. Для процессоров с малыми ресурсами она не применима, а для процессоров с достаточными ресурсами уже создана.
Благодарю за наведение на косяк. ОС я не создаю, нужен таймер со списком задач. который я сейчас г**кожу
gus21rus, https://github.com/DetSimen/Arduino- список таймеров. Может, поможет.
мой на микросекунды настроен.
Тогда надо быть очень осторожным, за микросекунды выполняется ограниченное число команд процессора, поэтому функция отклика должна быть как можно короче, иначе следующее прерывание можно пропустить. А если исполнять длинную функцию вне контекста прерывания, тогда можно исчерпать стек.
В коде нет реализации pop_task() .. она что делает? Выложите..
За микросекунду выполняется 16 однотактовых команд, но поскольку все команды работы с переменными (загрузка, запись в/из памяти, стека) выполняются за 2 такта, то надо ориентироваться "в смеси" будет примерно 10 команд. Байтовых. Поскольку часто пользуют int или вовсе long, то эту цифирьку надо уменьшить примерно в 2 раза. Да, ещё вызов подпрограммы без параметров занимает 10 тактов по 5 на "вход" и "выход", и каждый параметр съедает ещё 2-8 тактов в зависимости от разрядности и порядкового номера..
Функция pop_task() вызывается из под обработчика прерывания, соответственно время его исполнения увеличивается на время работы этой функции + 10тактов (вход-выход). Если за это время прилетит следующее прерывание оно запросто может быть "утерянным"..
Если эта функция "активирует" (вызывает) найденную задачу (из коммента), то ваще будет "труба" ибо прерывания тут - не открываются явно: потеря прерываний прогнозируется с большой вероятностью.
Если прерывания разрешаются внутри pop_task() И запускается задача из под неё - можно прогнозировать переполнение стека (зачистка стеком данных) и произвольное поведение программы в целом. Это если внутри pop_task() не принято специальных мер по защите.. (тут их не видно).
Ну и ещё: "тут микросекунды" - весьма опрометчиво, поскольку вход-выход в обработчик прерывания на "С/С++" очень прожолив и надо ожидать, что сам обработчик исполняется в среднем 40-50мксек. даже если он состоит из пары действий на ЯВУ.
Вот именно из тех соображений, которые изложил я и уважаемый Arhat109-2, я и выбрал для своего TTimerList минимальный интервал времени 1 миллисекунда. За это время вызываемая функция гарантированно успеет вызваться, положить сообщение в очередь, и выйти из контекста прерывания. Примерно так:
кусок из работающего проекта вентилятора в сортире
в сетапе настраиваются таймеры на разный интервал срабатывания
функция, которая вызывается таймером
А в loop() просто отслеживаются сообщения и передаются исполнителям
функция реагирования тоже только помещает сообщение в очередь
Вот именно из тех соображений, которые изложил я и уважаемый Arhat109-2, я и выбрал для своего TTimerList минимальный интервал времени 1 миллисекунда. За это время вызываемая функция гарантированно успеет вызваться, положить сообщение в очередь, и выйти из контекста прерывания.
Очень интересный код. Однако, как я понимаю, в плотно загруженном loop() своевременная обработка сообщений в очереди не гарантируется. Можно ли для самых критичных ко времени действий, вместо помещения сообщений в очередь, выполнять их прямо из контекста прерывания? Что можно успеть при периоде 1 мс? Помигать парой диодов успеем, чтобы осталось время и на работу остальной части программы?
примерно 10 тысяч инструкций в 1 мс.
Некоторые вещи, которые не требуют много времени, я делаю прямо в таймере.
Что характерно, часы на этом таймере отстают всего на полминуты в сутки ( при условии хорошего кварца и правильно подобранной константы в таймере). А потом синхронизируются в полночь по NRF24L01.
Единственное, операции, требующие millis() или упаси Боже, micros() в контексте прерывания неправильно работают. Нарвался так на чтении DHT11.
Единственное, операции, требующие millis() или упаси Боже, micros() в контексте прерывания неправильно работают. Нарвался так на чтении DHT11.
Я так понимаю, что в контексте прерываний не только millis() и micros() неверно работают, но и например обращение к Serial или SoftSerial. Думаю, в контексте прерываний вообще не стоит использовать никакие библиотеки или протоколы доступа к устройствам, так как все они или на прерываниях, или millis и micros
Поэтому, чтоб не думать об..., я и реализовал всю логику, через очередь сообщений.
Например, в функции отклика таймера (в прерывании) можно часами рулить. Что нибудь простое там запросто поместица.
Господа, спокойнее. я не говорю о том что мне нужна размерность строго в 1мкс. мне нужно чтобы условие мое срабатывало Чаще чем 1мс. Что там в pop_task ввиду самозащиты от шквала негативных комментариев по поводу связки г...о кода и костылей выкладывать не стал. Вопрос был с таймером, вопрос решен.
Все и так спокойны. Зря что не стали выкладывать, ибо это и есть ключ к работоспособности всей идеи. Вам просто перечислили небольшую кучку дерьма, в которую Вы стремитесь вступить обеими лапками .. и только.
А в целом, вполне можно из под прерывания работать и с длинным/долгим кодом и с временем и с сериалом .. только это надо делать грамотно, понимая в точности что, где, когда и как происходит.
Получилось .. и ладно. При возникновении следующих затыков - перечитайте внимательно тутошние комменты. Поможет на 146%. :)