Jump to content
Sign in to follow this  
Guest Lewin

Про Прерывания Таймера Мк

Recommended Posts

Guest Lewin

Не так давно программирую МК какие подвернуться. Никак не могу взять в толк одну вещь: Если мы тактируем какую нибудь периферию от таймера с определенной частотой, то имеем примерно следующее: инициализация генератора тактовых импульсов, инициализация периферии, инициализация таймера. Далее таймер считает, считает, считает -> досчитывает и генерирует прерывание. И мы что-то делаем. Например моргаем диодом. Часики продолжают тикать, пока обрабатывается прерывание, верно? А если надо опросить много кнопок, АЦП, послать посылку,...? Пройдет куча тактов (причем не всегда одинаковое количество) и уже нельзя будет сказать, например, что мы опрашиваем АЦП каждые xxx наносекунд, ведь частота прерываний поплыла. Верно?

Share this post


Link to post
Share on other sites

Нет. "имеем примерно следующее: инициализация генератора тактовых импульсов, инициализация периферии, инициализация таймера". Далее заходите в основной цикл, где выполняете какие-то функции (опрашиваете кнопки, посылаете посылки и многое другое). Цикл постоянно повторяется (все операции идут по кругу, многократно). А таймер в это время "считает, считает, считает -> досчитывает и генерирует прерывание". Тогда основной цикл останавливается и МК переходит к выполнению программы обработки прерывания, адрес которой находится в векторе данного прерывания (а таймер "считает, считает, считает..." ). Закончив обработку прерывания, МК возвращается в основной цикл в то месте, откуда он "ушел" (на следующую инструкцию) и продолжает "крутиться" в главном цикле. Как это происходит более подробно, почитайте в соответствующей литературе.

Edited by Геннадий

Share this post


Link to post
Share on other sites

Изготовление 2-х слойных плат от 2$, а 4-х слойных от 5$!

Быстрое изготовление прототипа платы всего за 24 часа! Прямая доставка с нашей фабрики!

Смотрите видео о фабрике JLCPCB: https://youtu.be/_XCznQFV-Mw

Посетите первую электронную выставку JLCPCB https://jlcpcb.com/E-exhibition чтобы получить купоны и выиграть iPhone 12, 3D-принтер и так далее...

Guest

Команды ветвления: условный и безусловный переходы.

Программа для процессора состоит из команд, которые выполняются последовательно. Нарушить последовательность выполнения программы можно, используя команды условного и безусловного переходов.

Команда безусловного перехода выглядит так: ПЕРЕЙТИ по «адресу». Данная команда заставляет процессор изменить порядок последовательного выполнения программы и продолжить выполнение программы по указанному в команде «адресу». Эта команда часто используется при отладке программ, когда необходимо пропустить часть команд и продолжить выполнение программы с иного места. Принцип работы команды показан на рис. безусловн-переход.

Рис. безусловн-переход. Принцип работы команды безусловного перехода

Команда условного перехода сложнее: при ее выполнении проверяется выполнения какого-либо логического действия. В результате выполнения логического действия программа разветвляется на две ветви: «Да» и «Нет». По завершению необходимых действий в любой из ветвей («команда А» и «команда В») происходит переход к общему участку. Принцип работы команды показан на рис. условн-переход.

Рис. условн-переход. Принцип работы команды условного перехода

А) Полный вид команды условного перехода

B) и С) Сокращенный вид команды условного перехода

Рассмотрим работу этой команды подробнее. Например, необходимо сравнить два значения. Если первое значение больше, то выполнить команды ветви «Да», иначе — выполнить команды ветви «Нет». Если при любом результате логического действия происходит выполнение одной из групп команд («команда А» или «команда В»), то такой вид команды называют полным (см. рис. условн-переход А).

Если же только одна из ветвей («Да» или «Нет») имеет команды, то такой вид команды называют неполным (см. рис. условн-переход В и С).

Как эти команды выполняет процессор.

Для того чтобы понять методы выполнения команд ветвления, необходимо объяснить такое понятие как «указатель адреса выполняемой команды». У процессора имеется специальный регистр — «счетчик команд», в котором хранится адрес ячейки памяти, в которой хранится выполняемая в текущий момент команда. При подаче питания в этот регистр записывается стартовый адрес. Во многих микроконтроллерах стартовый адрес равен нулю. Далее процессор начинает выполнять команды, начиная с адреса, загруженного в регистр указателя адреса выполняемой команды. Выполнив команду, значение этого регистра увеличивается — инкриминируется. Процессор считывает следующую команду из памяти по адресу, указанную в указателе.

Прерывания и их типы. Приоритеты прерываний.

Имеется еще один способ «заставить» процессор прекратить последовательное исполнение программы и перейти к выполнению программы по определенному адресу — вызвать «прерывание». Это понятие появилось вместе с первыми процессорами. Все дело в том, что чаще всего процессор управляет менее скоростными, чем он, устройствами.

Приведем простой пример. Процессор выполняет программу подсчета количества импульсов, поступивших на один из разрядов порта. При нажатии кнопки процессор должен прервать выполнение этой программы и выполнить другую программу: подать на один из разрядов порта логическую единицу, тем самым включить какое-либо устройство. Как решить эту задачу? Можно в самой программе постоянно опрашивать необходимый разряд порта, к которому подключена кнопка. Но при этом часть ресурсов (скорости) процессора будет практически впустую тратиться на опрос порта — ведь кнопка будет нажиматься редко. Второй способ — использование прерываний. У микроконтроллера имеется специальный вывод, обычно его обозначают как «Int» (англ. «Interrupt» — прерывание). При подаче сигнала на вывод «Int» происходят следующие действия:

- остановка выполнения основной программы;

- в оперативной памяти сохраняется значение регистра «счетчик команд» (место прерывания выполнения программы);

- после чего в этот же регистр загружается новый адрес (зависит от желания производителя процессора);

- в ячейке памяти с указанным адресом расположена команда безусловного перехода: «перейти по адресу хх»;

- в памяти, начиная с ячейки с адресом хх, расположена еще одна программа, назовем ее служебной программой.

В нашем случае служебная программа должна выдать в порт логическую единицу, тем самым, включив необходимое устройство. А вот тут начинается самое интересное действо: последней командой служебной программы является команда «выход из прерывания». Получив эту команду, процессор считывает из памяти ранее сохраненное значение регистра «счетчик команд» и загружает его в этот регистр. Следовательно, процессор продолжает выполнение основной программы с прерванного прерыванием адреса.

@! Программа обслуживания прерывания должна иметь только одну точку входа, и всегда заканчиваться командой возврата в основную программу.

Но прерывание может быть вызвано не только внешними сигналами, но и внутренними устройствами самого микроконтроллера: таймерами, счетчиками, последовательными портами и даже энергонезависимой памятью. Опять-таки, делается это в основном для того, чтобы уменьшить количество выполняемых команд по анализу состояния этих периферийных устройств.

Приведем пример: в ходе выполнения программы необходимо опрашивать клавиатуру. Но нажатия кнопок редки, и процессору придется впустую тратить время на опрос. Поэтому применяют следующий метод:

- включается (разрешается) прерывание по изменению состояния разряда порта, который ранее настроили как вход;

- процессор выполняет основную программу;

- как только на клавиатуре нажмут любую кнопку, схемы управления формируют соответствующее прерывание;

- выполнение основной программы приостанавливается, выполняется анализ нажатой кнопки и сопоставленной с этим событием действие.

Данный способ выполнения каких-либо действий вне основной программы называют «фоновый режим работы программы». Также часто говорят: «эта часть программы выполняется в фоновом режиме».

При работе с прерываниями необходимо быть осторожным: возможна ситуация, при которой выполнение программы и работа всего устройства может быть нарушена. Дело в том, что микроконтроллер может иметь несколько прерываний. Для управления режимами их работы имеется регистр управления прерываниями. В программе вы разрешили работу нескольких прерываний — это нормальная ситуация. Но, получив внешний или внутренний сигнал прерывания и перейдя к выполнению программы обработки прерывания, вы не выполнили отключение (запрет) прерывания. Процессор выполняет уже служебную программу и в этот момент он получает еще один сигнал прерывания. Процессор прерывает выполнение служебной программы и переходит к выполнению служебной программы обработки нового прерывания. Несложно догадаться, к чему это может привести.

Для решения этой проблемы был разработан метод присвоения каждому из прерываний степени важности, или «приоритета прерывания». В зависимости от модели микроконтроллера приоритет прерываний может быть задан жестко (а программист лишь разрешает или запрещает обработку того или оного прерывания), или быть реализован программистом программно (приоритет прерывания зависит от предпочтений программиста и алгоритма реализации конкретной задачи).

Share this post


Link to post
Share on other sites

Высокая надежность SiC! Как они этого добились?

За несколько лет кропотливых исследований и совершенствования технологии компания Infineon смогла довести показатели надежности и стабильности параметров высоковольтных и быстродействующих карбид-кремниевых транзисторов линейки CoolSiC практически до уровня их кремниевых собратьев.

Подробнее

Guest Lewin

Всем спасибо, кажется я догнал! Если взять например прерывание, генерируемое переполнением таймера: Сгенерировалось прерывание -> МК его обрабатывет, таймер в это же время сбрасывается и продолжает считать заново. Т.е. заполнение таймера для нового прерывания и обработка только что сгенерированного происходят одновременно. Не важно сколько тактов займет обработка прерывания, главное чтобы МК успел все обработать до следующего переполнения таймера. Верно?

Share this post


Link to post
Share on other sites

Вебинар «Практическое использование TrustZone в STM32L5»(10.12.2020)

Приглашаем на вебинар, посвященный экосистеме безопасности и возможностях, которые появились у разработчиков благодаря новой технологии TrustZone в микроконтроллерах STM32L5. Программа рассчитана на технических специалистов и тех, кто уже знаком с основами защиты ПО в STM32.

Подробнее

Снижена цена на AC/DC и DC/DC преобразователи Mornsun в Компэл!

Компэл и компания Mornsun снизили цены на преобразователи AC/DC-преобразователи семейств LS и LDE. По привлекательной цене также предлагаются DC/DC-преобразователи изолированных семейств поколений R2 и R3 различного конструктивного исполнения.

Подробнее

Join the conversation

You are posting as a guest. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
Sign in to follow this  

  • Сообщения

    • неизвестная переменная - это, например,- ёмкость, чтоб её узнать, нужно измерить дельты напряжения и заряда, подставить их в формулу и узнать ёмкость.
    • Морпехов с праздником!
    • Интересно, какая каша получится на нагрузке, если модулировать "несглаженную выпрямленную синусоиду" частотой 1 кГц? И что будет твориться в сети и эфире рядом с установкой.
    • Где ты видел в формуле - неизвестную переменку?  Напряжение порогового напряжения известно, минимального тоже известна(потому что мы их задаём  .  Их разница  и есть известная переменка.  Поэтому меняя соотношения макс. и мин. напр. мы меняем ёмкость.  И таким образом любой напряжение влияет на любую ёмкость.
    • по этой формуле нельзя, и не только потому что ЭПС на ёмкость не влияет, а потому, что это формула понятия - ёмкость, и она служит для расчёта неизвестной переменной, по остальным известным. например, неизвестной ёмкости, по известным дельтам заряда и напряжения, или нахождение дельты напряжения, по известным ёмкости и дельте заряда... ёмкости будут одинаковые, мы же это уже обсуждали, перечитай всё что я писал ещё раз.
    • Да есть взаимосвязь.  Поэтому я говорю ничего не менять кроме напряжения и результата.  2*2=4.  Что не понятно? Я же тебе сказал ничего не меняем, кроме ёмкости и напряжения.  И ты увидишь прямую зависимость их на друга.  2 конденсатора 100 мкф 50 и 100 вольт, подаём 50 вольт на обои.  Разные только максимальные пределы.  Ёмкости будут разные.Это видно из формулы.
    • они взаимно зависимы, если изменяешь порог, изменяется время его достижения и ёмкость получается та же. Это как в законе Ома, если изменишь сопротивление, то изменится ток - есть взаимосвязь. Вот смотри, ты набираешь ведро воды, до 1 литра (10 сантиметров уровня ведра), со скоростью 0,1 литра в секунду, получается 1 литр будет набран за 10 секунд, теперь ты изменил порог до 2 литров, порог теперь достигается за 20 секунд, видишь, за 10 секунд всё равно поступает литр воды и уровень повышается  на 10 сантиметров, я тебе уже давал аналогию ёмкости 3 страницы назад (с этой аналогии мы и начали), от изменения порога, ёмкость ведра не изменилась.

  • Повышающий преобразователь напряжения, 5А на основе XL4015

  • Similar Content

    • By Сергей Фомин
      Изучаю прерывания на attiny13. Пока остановился на прерываниях по переполнению. Сделал тестовый код в Atmel Studio и сразу через программатор заливаю на тиньку со светодиодом. Проблема в том что гореть он должен 10 секунд и выключаться, а горит примерно 20-23 секунды. Прошу помощи в правильном расчёте. Код ниже (пока учусь сильно не ругайтесь) :
      #define F_CPU 1000000 #define LED PB2 #include <avr/io.h> //#include <util/delay.h> #include <avr/interrupt.h> unsigned char work_time =384;    //  1000000/1024/256=3.8  (0.026 сек)    10/0.026=384 volatile unsigned char temp =0; ISR (TIM0_OVF_vect) {      TCNT0=0x00;     temp ++;     if (temp>=work_time)     {         PORTB &=~(1<<LED);  //Инвертируем состояние         TCCR0B=0x00    // остановка таймера         cli (); //общее запрещение прерываний     } } int main (void) {     init();               while (1)     {              } } void init () {          DDRB |= (1<<LED); // выход     PORTB =(1<<LED); //включен     TCCR0B =0x05; // установка делителя на 1024     TIMSK0 |= _BV(TOIE0);     sei();   // Либо SREG |= (1<<SREG_I); //Разрешаем прерывания глобально     TCNT0 = 0X00;        //Обнулить счётный регистр }  
    • By katet
      Добрый день. Может быть,кто-нибудь уже сталкивался с таким. 
      Занимаюсь доработкой чужого проекта в среде STM32CubeMX, первый раз работаю с библиотекой HAL.
      В этом проекте осуществлялся прием байт по USART1 из ComMon. Проект был открыт в STM32CubeMX, где мной дополнительно были активированы новые модули - RTC, SD, USART2. Настройки USART1 не менялись. 
      Часы реального времени RTC: питание от батарейки, тактирование – от LSE. 
      При приеме байт по USART1 в новом проекте (активны  USART1, RTC, SD, USART2) было выявлено, что после приема 2 байт по USART1 перестает инкрементироваться значение переменной uwTick, отвечающей за прерывания Systick, в результате чего дальнейшая отладка невозможна. Не удается выяснить, в каком месте и почему перестает увеличиваться значение uwTick. При работе со старым проектом (где активен только USART1) uwTick инкрементируется после приема 2 байт.
      Смены приоритета прерываний не происходит, в  функцию HAL_Delay() отладчик не попадает. При вызове функции HAL_ResumeTick(), возобновляющей прерывания Systick, ничего не меняется, значение uwTick остается неизменным.
      Остановка прерываний была обнаружена при попадании в функцию:
      static HAL_StatusTypeDef UART_WaitOnFlagUntilTimeout(UART_HandleTypeDef *huart, uint32_t Flag, FlagStatus Status, uint32_t Tickstart, uint32_t Timeout)
      {
        /* Wait until flag is set */
        while((__HAL_UART_GET_FLAG(huart, Flag) ? SET : RESET) == Status) 
        {
          /* Check for the Timeout */
          if(Timeout != HAL_MAX_DELAY)
          {            
            if((Timeout == 0U)||((HAL_GetTick() - Tickstart ) > Timeout))
            {
              /* Disable TXE, RXNE, PE and ERR (Frame error, noise error, overrun error) interrupts for the interrupt process */
              CLEAR_BIT(huart->Instance->CR1, (USART_CR1_RXNEIE | USART_CR1_PEIE | USART_CR1_TXEIE));
              CLEAR_BIT(huart->Instance->CR3, USART_CR3_EIE);
              
              huart->gState  = HAL_UART_STATE_READY;
              huart->RxState = HAL_UART_STATE_READY;
              
              /* Process Unlocked */
              __HAL_UNLOCK(huart);
              
              return HAL_TIMEOUT;
            }
          }
        }
        
        return HAL_OK;
      }
      В новом проекте текущее значение uwTick, возвращаемое функцией HAL_GetTick(), всегда равно значению Tickstart. В старом проекте сначала также, но затем uwTick начинает увеличиваться. Выяснить, в каком именно месте кода значение uwTick должно начать увеличиваться, не удается.
      Может быть, у кого-нибудь есть хоть какие-то идеи, с чем может быть связана остановка увеличения значений uwTick, помогите пожалуйста)
      Распиновка и конфигурация обоих проектов: верхняя часть рисунков - первоначальный, работающий вариант,  нижняя - сбой прерываний Systick.


    • By IgnatiusF
      Не могу настроить таймер 0 на работу, и даже не получается понять в чем проблема. Делаю в Proteus, так как это быстрее и нагляднее.
      Пробовал и просто по переполнению делать прерывание (WGM[1..0]  00) и по совпадению (WGM[1..0]  10; OCR0A = 0 - 255), однако прерываний нет. Перед циклом ставлю TCNT0 = 0;
      Тактирование выставляю TCCR0B (CS[2..0] 001, 100, 101).
      Прерывания TIMSK0 (OCIE0A, TOIE0) выставляю, не вызываются. Даже принудительным заносом значения в регистр TIFR0 (OCF0A, TOV0).
      Прерывание пытаюсь выполнить таким образом:
      #include <avr/interrupt.h> volatile unsigned int A = 0; ISR(TIMER0_OVF_vect) { A = 1; } ... while(1) { if (A == 1) PORTB |= (1<<0); } Proteus показывает, что вывод настроен как выход, но всегда 0;
      Конкретный код привести не могу, так как у меня не заработало совсем ничего.
      Внизу я сделал вырезку из даташита на ATMEGA328 по 0 таймеру 8-бит, и занес в один PDF файл.
      ATmega328-106-112.pdf
      Прошу помочь разобраться с таймером и прерываниями для него.
    • Guest Максим
      By Guest Максим
      Всем светлым и умным головам привет!

      Никак не могу найти информацию о данном прерывании TIM1_BRK_UP_TRG_COM.
      Вопрос 1: Что это за стек или система прерываний? 
      Вопрос 2: Когда будет вызываться обработчик прерывания TIM1_BRK_UP_TRG_COM_IRQHandler, если также есть обработчик прерывания TIM1_CC_IRQHandler?
      Вопрос 3: период переполнения таймера равен 100 мкс. Сколько раз будет вызываться обработчик прерывания TIM1_BRK_UP_TRG_COM_IRQHandler до обработчика прерывания TIM1_CC_IRQHandler? По логике вещей, предполагаю, что 100 раз?
      Заранее благодарен!

      Всем радости))
    • By LukaS_St
      Всем привет. Столкнулся с такой проблемой: Пишу программу для устройства мониторинга оборотов двух валов используя датчик холла. Настроил прерывания,  перевод в RPM, все проверил, в .порт отправляются верные значения, и после настройки индикации столкнулся с проблемой неадекватного поведения переменных считающих millis, а после и индикации. В Arduino я не спец, Гляньте на код, может чем помогите. Заранее благодарен.
      #include <EEPROM.h> #define CLOCK 13 //SH_CP #define DATA 11  //DS #define LATCH 10  //ST_CP int numbers[5]; boolean ee = 0; int mh; int hh; int et_mah; int et_shl; int ot_mah; int ot_shl; volatile  int rpm_mah = 0; volatile  int rpm_shl = 0;  int rpmmah_count = 0;  int rpmshl_count = 0; unsigned long lastmillis_mah = 0; unsigned long lastmillis_shl = 0; unsigned long lastmillis_show = 0; unsigned char number[] = {   0b01111110, //0   0b00110000, //1   0b01101101, //2   0b01111001, //3   0b00110011, //4   0b01011011, //5   0b01011111, //6   0b01110000, //7   0b01111111, //8   0b01111011, //9   0b00000001, //-   0b00000000  //тушим индикатор }; void setup() {   Serial.begin(9600);   attachInterrupt(0, rpm_mahovik, FALLING);   attachInterrupt(1, rpm_shluz, FALLING);   pinMode(CLOCK, OUTPUT);   pinMode(DATA, OUTPUT);   pinMode(LATCH, OUTPUT); } void rpm_mahovik() { /* this code will be executed every time the interrupt 0 (pin2) gets low.*/   rpmmah_count++; } void rpm_shluz() { /* this code will be executed every time the interrupt 0 (pin2) gets low.*/   rpmshl_count++; } // чтение //int EEPROM_int_read(int addr) { //  byte raw[2];  // for (byte i = 0; i < 2; i++) raw[i] = EEPROM.read(addr + i);  // int &num = (int&)raw; //  return num; //} // запись //void EEPROM_int_write(int addr, int num) { //  byte raw[2];  // (int&)raw = num;  // for (byte i = 0; i < 2; i++) EEPROM.write(addr + i, raw[i]); //} void show () {   int x;   for (x = 5; x >= 0; x--) {    Serial.print("x= "); Serial.println(x);     //включаем LATCH (Начинаем общение)     digitalWrite(LATCH, LOW);     shiftOut(DATA, CLOCK, LSBFIRST, number[numbers[x]]);     //выключаем LATCH     digitalWrite(LATCH, HIGH);     //отключаем LATCH (чтобы регистр не ждал данных)     digitalWrite(LATCH, HIGH);   } } //void ee_write () {  // EEPROM_int_write(0, mh); //  EEPROM_int_write(2, hh); //  EEPROM_int_write(4, et_mah); //  EEPROM_int_write(6, et_shl); //  EEPROM_int_write(8, ot_mah); //  EEPROM_int_write(10, ot_shl); //} void loop() { //  if ((millis() <= 100) & (ee == 0)) {   //  mh = EEPROM_int_read(0);  //   hh = EEPROM_int_read(2); //    et_mah = EEPROM_int_read(4);  //   et_shl = EEPROM_int_read(6);  //   ot_mah = EEPROM_int_read(8);  //   ot_shl = EEPROM_int_read(10);  //   ee = 1;  // }   if (millis() - lastmillis_mah == 1000) {     detachInterrupt(0);     rpm_mah = rpmmah_count * 60;     rpmmah_count = 0;     lastmillis_mah = millis();     attachInterrupt(0, rpm_mahovik, FALLING);   }   if (millis() - lastmillis_shl == 5000) {     detachInterrupt(1);     rpm_shl = rpmshl_count * 12;     rpmshl_count = 0;     lastmillis_shl = millis();     attachInterrupt(1, rpm_shluz, FALLING);   }   if (millis() - lastmillis_show == 1000) {     numbers[0] = rpm_mah / 1000;     numbers[1] = (rpm_mah % 1000) / 100;     numbers[2] = ((rpm_mah % 1000) % 100) / 10;     numbers[3] = ((rpm_mah % 1000) % 100) % 10;     numbers[4] = rpm_shl / 10;     numbers[5] = rpm_shl % 10;     lastmillis_show = millis();   show(); Serial.print("RPM mah= "); Serial.println (rpm_mah);  Serial.print("RPM shl= "); Serial.println (rpm_shl);   Serial.print("Show millis= "); Serial.println(lastmillis_show); Serial.print("Show mah= "); Serial.println(lastmillis_mah); Serial.print("Show shl= "); Serial.println(lastmillis_shl);   } }
×
×
  • Create New...