Авг 302012
 

Этот таймер не описан подробно в документации на конкретное семейство микроконтроллеров, например в том же Reference manual про него присутствует всего лишь несколько строк. Дело в том, что эти документы в основном объясняют порядок работы с периферией. А таймер SysTick расположен в ядре микроконтроллера и для всех устройств на базе ядра Cortex является стандартным.  Этот таймер предназначен для формирования временных интервалов операционной системы реального времени – RTOS. Но и для других целей можно использовать периодические прерывания, формируемые этим таймером. В микроконтроллерах на ядре Cortex время перехода к обработчику прерывания строго детерминировано, что является огромным плюсом этого ядра. В этой статье будет вкратце описан системный таймер SysTick (вкратце ввиду его простоты) и приведены практические примеры работы с ним.

Таймер представляет собой 24-разрядный декрементирующий счетчик. Источником тактирования является системная тактовая частота SYSCLK, либо та же частота, но поделенная на 8 – SYSCLK/8.
В составе таймера имеются следующие регистры:

  •  SYSTICK Current Value Register – счетный регистр, с каждым тактом уменьшающий содержимое на 1. При достижении нулевого значения в счетном регистре генерируется прерывание.
  • SYSTICK Reload Value Register – в этом регистре хранится значение для перезагрузки счетного регистра после обнуления.
  • SYSTICK Control and Status Register — регистр управления и статуса,  здесь выбирается тактовая частота, запускается счетчик, разрешается генерация прерывания.

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

Для конфигурации таймера в файле core_cm3.h есть функция SysTick_Config(uint32_t ticks), в качестве аргумента которой передается коэффициент деления тактовой частоты для получения необходимой временной задержки.

static __INLINE uint32_t SysTick_Config(uint32_t ticks)
{ 
  if (ticks > SysTick_LOAD_RELOAD_Msk)  return (1);            /* Reload value impossible */
                                                               
  SysTick->LOAD  = (ticks & SysTick_LOAD_RELOAD_Msk) - 1;      /* set reload register */
  NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1);  /* set Priority for Cortex-M0 System Interrupts */
  SysTick->VAL   = 0;                                          /* Load the SysTick Counter Value */
  SysTick->CTRL  = SysTick_CTRL_CLKSOURCE_Msk | 
                   SysTick_CTRL_TICKINT_Msk   | 
                   SysTick_CTRL_ENABLE_Msk;                    /* Enable SysTick IRQ and SysTick Timer */
  return (0);                                                  /* Function successful */
}

Это значение заносится в регистр перезагрузки. В самом начале выполняется проверка на то, что данная величина не выходит за максимальный предел, поскольку счетчик 24-разрядный, а передаваемый аргумент функции является 32-разрядным числом и необходимо об этом помнить.

Далее в функции конфигурации SysTick_Config() задается уровень приоритета прерывания, обнуляется счетный регистр, разрешается генерация прерывания, задается источник тактирования и разрешается работа таймера – запускается счет.

По умолчанию тактовая частота этого таймера будет равна системной тактовой частоте SYSCLK. Если же нужно задать частоту тактирования таймера как SYSCLK/8, то уже после этой функции инициализации можно вызвать функцию

SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8),

которая находится в файле misc.c. Для этого к проекту необходимо подключить файлы misc.c и misc.h.

Как видите, здесь производится минимум настроек, и далее можно посмотреть на примеры программ, которые были проверены на двух отладочных платах: STM32VL-DISCOVERY (контроллер STM32F100) и STM32L-DISCOVERY (контроллер STM32L152). Отличия в кодах только в инициализации нужных выводов портов, а настройка системного таймера одинакова, поскольку в обоих случаях он является частью одного и того же ядра Cortex-M3.

В данных примерах таймер настраивается на генерацию прерывания через “базовый” промежуток времени, равный 1 миллисекунде. Затем, уже с помощью программного цикла формируется задержка, равная 1 секунде. Через каждую секунду происходит попеременное зажигание/гашение светодиодов платы, здесь я не стал особо мудрить, поскольку основной задачей была настройка системного таймера для генерации прерываний через определенный интервал.

Для задания длительности между двумя последующими прерываниями от таймера, в регистр перезагрузки необходимо записать значение, которое рассчитывается по простой формуле:

Reload Value = SysTick Counter Clock (Hz) x  Desired Time base (s), где

  • Reload Value – величина перезагружаемого значения для таймера
  • SysTick Counter Clock (Hz) – тактовая частота таймера
  • Desired Time base (s) – желаемое время формируемой временной задержки между прерываниями

Системная тактовая частота задается при начальной инициализации микроконтроллера.

На плате STM32VL-DISCOVERY микроконтроллер конфигурируется на работу с внешним кварцем частотой 8 МГц, при этом его системная тактовая частота после начальной инициализации будет равна 24 МГц.

На плате STM32L-DISCOVERY внешнего кварца нет и после начальной инициализации системная тактовая частота формируется внутренним источником MSI и равна 2,097 МГц.

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

Код программы для платы STM32VL-DISCOVERY (контроллер STM32F100)

#include "stm32f10x.h"

static __IO uint32_t TimingDelay;

void Delay_ms(__IO uint32_t nTime);

GPIO_InitTypeDef    GPIO_InitStruct;

int main()
{ 
  /*Вызов функции конфигурации системного таймера SysTick.
  Эта функция находится в файле core_cm3.h и:
  --Задает источник тактирования системного таймера (по умолчанию это SYSCLK = 24 МГц, 
    другой возможный вариант  - SysTick тактируется от SYSCLK/8);
  --Задает уровень приоритета прерывания;
  --Сбрасывает флаг ожидания прерывания, если он выставлен;
  --Заносит в нужный регистр перезагружаемое значение для декрементирующего счетчика,
    которое вычисляется по формуле:
        Reload Value = SysTick Counter Clock (Hz) x  Desired Time base (s),
        для базовой задержки длительностью 1 мс получим величину
        Reload Value = 24 000 000 Гц х 0,001 с = 24 000 
    (Необходимо самостоятельно посчитать эту величину и вставить в качестве
    параметра при вызове функции);
  --Обнуляет счетчик
  --Запускает счет системного таймера*/
  SysTick_Config(24000);
  
  //Включаем тактирование порта GPIOC  
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
  //Конфигурируем выводы, к которым подключены светодиоды платы
  GPIO_InitStruct.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9; //Выбираем выводы PC8, PC9
  GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; //Максимальная скорость работы 
  GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP; //Выход Push-Pull 
  GPIO_Init(GPIOC, &GPIO_InitStruct); //Заносим заданные настройки в регистры порта
  
  while(1)
  {
    GPIO_ResetBits(GPIOC, GPIO_Pin_9); //Гасим зеленый LED
    GPIO_SetBits(GPIOC, GPIO_Pin_8);   //Зажигаем синий LED
    Delay_ms(1000); //Временная задержка на 1 с
    GPIO_ResetBits(GPIOC, GPIO_Pin_8); //Гасим синий LED
    GPIO_SetBits(GPIOC, GPIO_Pin_9);   //Зажигаем зеленый LED
    Delay_ms(1000); //Временная задержка на 1 с
  }  
}

//Функция временной задержки
void Delay_ms(__IO uint32_t nTime)
{ 
  TimingDelay = nTime;
  while(TimingDelay != 0);
}

void TimingDelay_Decrement(void)
{
  if (TimingDelay != 0x00)
  { 
    TimingDelay--;
  }
}

//Обработчик прерывания системного таймера
void SysTick_Handler(void)
{
  TimingDelay_Decrement();
}

Код программы для платы STM32L-DISCOVERY (контроллер STM32L152)

#include "stm32l1xx.h"

static __IO uint32_t TimingDelay;

void Delay_ms(__IO uint32_t nTime);

GPIO_InitTypeDef    GPIO_InitStruct;

int main()
{
    /*Вызов функции конфигурации системного таймера SysTick.
  Эта функция находится в файле core_cm3.h и:
  --Задает источник тактирования системного таймера (по умолчанию это SYSCLK = 2.097 МГц, 
    другой возможный вариант  - SysTick тактируется от SYSCLK/8);
  --Задает уровень приоритета прерывания;
  --Сбрасывает флаг ожидания прерывания, если он выставлен;
  --Заносит в нужный регистр перезагружаемое значение для декрементирующего счетчика,
    которое вычисляется по формуле:
        Reload Value = SysTick Counter Clock (Hz) x  Desired Time base (s),
        для базовой задержки длительностью 1 мс получим величину
        Reload Value = 2 097 000 Гц х 0,001 с = 2097
    (Необходимо самостоятельно посчитать эту величину и вставить в качестве
    параметра при вызове функции);
  --Обнуляет счетчик
  --Запускает счет системного таймера*/
  SysTick_Config(2097);
  
  RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE); //Включаем тактирование GPIOB
  
  //Конфигурируем выводы, к которым подключены светодиоды платы
  GPIO_InitStruct.GPIO_Pin = (GPIO_Pin_6 | GPIO_Pin_7); //Выбираем выводы PB6, PB7
  GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT; //Выводы порта в режиме выхода
  GPIO_InitStruct.GPIO_OType = GPIO_OType_PP; //Выход Push-Pull
  GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL; //Выход без подтягивающих резисторов
  GPIO_InitStruct.GPIO_Speed = GPIO_Speed_40MHz; //Скорость порта максимальная
  GPIO_Init(GPIOB, &GPIO_InitStruct); //Заданные настройки сохраняем в регистрах GPIOB
  
  while(1)
  {
    GPIO_ResetBits(GPIOB, GPIO_Pin_7); //Гасим зеленый LED
    GPIO_SetBits(GPIOB, GPIO_Pin_6);   //Зажигаем синий LED
    Delay_ms(1000); //Временная задержка на 1 с
    GPIO_ResetBits(GPIOB, GPIO_Pin_6); //Гасим синий LED
    GPIO_SetBits(GPIOB, GPIO_Pin_7);   //Зажигаем зеленый LED
    Delay_ms(1000); //Временная задержка на 1 с
  }
}

//Функция временной задержки
void Delay_ms(__IO uint32_t nTime)
{ 
  TimingDelay = nTime;
  while(TimingDelay != 0);
}

void TimingDelay_Decrement(void)
{
  if (TimingDelay != 0x00)
  { 
    TimingDelay--;
  }
}

//Обработчик прерывания системного таймера
void SysTick_Handler(void)
{
  TimingDelay_Decrement();
}

Архивы с проектами можно загрузить по следующим ссылкам:

Проект для STM32VL-DISCOVERY (STM32F100)

Проект для STM32L-DISCOVERY (STM32L152)

Другие статьи:

 

  24 Responses to “STM32. Системный таймер SysTick.”

  1. Здравствуйте!
    Запускал проект с таймером SysTick. Пользуюсь IAR EWARM 6.4, скачал откуда-то…
    Создавал свой проект, потом проект «SysTick_Example» из STM32F4xx_DSP_StdPeriph_Lib_V1.0.1,
    потом взял текущий из статьи. Всё компилируется, но прерывание по этому таймеру не происходит.
    Конкретно — не меняется значение счётчика CURRENT в SysTick current value register (STK_VAL).
    В EWARM значение CURRENT смотрю в регистре NVIC > SYSTICKCVR > CURRENT.
    Поставил IAR EWARM 6.3 — то же самое… «Простой» таймер однако…

  2. Отладку делаю в симуляторе…

    • Вполне возможно, симулятор тут так себе, периферию не поддерживает. Я им не пользуюсь практически, в «железе» всегда проверяю. Посмотрю то же самое завтра для интереса.
      Таймер простой в том смысле, что только одну функцию выполняет — счет.

  3. // ——- почитал HELP EWARM !
    Засада оказывается в симуляторе: см. help тему «Simulating a simple interrupt»…
    Чтобы запустить прерывание в симуляторе: выбрать через меню Simulator>Interrupt Setup,
    потом добавить и настроить требуемое прерывание.
    Значение счётчика CURRENT не меняется по-прежнему (всегда значение 0), но прерывание работает.
    В железе меняется CURRENT?

  4. Проверил на STM32VL-DISCOVERY. При отладке в «железе» регистр SYSTIKCVR (Current Value) загружается значением 0x00005DBF (23999), затем нормально декрементируется как и положено. Начальное значение 0x00005DBF также отображается в регистре SYSTICKRVR (Reload Value). При проходе в симуляторе эти регистры пустые и не изменяются. Так что все нормально, симулятор не поддерживает все «от и до», нет у него такого сверхмощного функционала. Работа ядра ЦПУ и все, больше никаких функций.

  5. хороший счетчик. попробовал поменять частоту в файле конфигурации на 32 МГц, и диод начал быстрее моргать- т.е. система берет именно частоту SYSCLK.
    Одно по коду- может и не имеет смысл из прерывания ф-ю вызывать, а уменьшать переменную TimingDelay прямо в прерывании? По крайней мере, так тоже работает.

    void SysTick_Handler(void)
    {
    if (TimingDelay != 0x00)
    {
    TimingDelay—;
    }
    }

  6. а что происходит, если я не вызываю ф-ю SysTick_Config(2097); ?
    если я не использую в программе у себя этот таймер, он все равно прерывается постоянно?

  7. Привет! Спасибо Вам за супер хорошую статью, интересную и полезную.
    Сделал все по Вашему примеру, для задержек в миллисекунды (mS) работаю отлично!
    Но мне нужно сделать задержку в микросекундах (uS) я загрузил в пред делитель SysTick_Config(31) при частоте кварца 32Мгц (проц stm32l152), т.е. каждое прерывание черзе 1us, Вызываю Функцию загружаю к примеру 1 т.е. должна получиться 1 uS, а по факту 1.8us по осциллографу. А вот если я хочу задержку 10uS то все получается запросто, и на осциллографе 10uS.
    Подскажите в чем может быть дело, может скорости процессора не хвает для обработки микросекундных задержек? или я неправильно что-то делаю. Кстате побывал делать на таймерах, та же проблема, 1us задержку неполучается сделать, все начинает работать после 10uS.

    C Уважением, Григорий

    • Для таких коротких интервалов здесь слишком долго выполняются вызовы функций и проверки, например вызов TimingDelay_Decrement() из обработчика прерывания и проверка условия while(TimingDelay != 0). Не проверял, но скорее всего так. Надо попробовать выполнять какое-либо короткое действие прямо в обработчике прерывания, например инвертировать вывод порта. И больше ничего, тогда должно успевать.

      • Спасибо за ответ, ну, а все таки каким образом мне реализовать задержки от 1 до 10 uS. Мне эти задержки нужны для работы с внешними интерфейсами. В PIC18 и dsPIC33 я использовал стандартные библиотеки для задержек и просто вызывал функцию Delay_us(1) и т.д. Сейчас я сделал задержку с помощью NOP();, но это как то не очень хорошо, потому что мне нужно 1,2,4, и 6uS и для каждой задержки я пишу функцию с соответствующим кол-вом NOP`в. Как сделать грамотно задержку а рамках 1-10uS?

  8. Тут библиотеки иногда тормозные, в плане временных параметров. А вообще вектор я указал… Пойми, я не могу дать ответы на все случаи. Я стараюсь подробно описать устройство модуля и инициализацию, а также дать доступный пример. А реализаций может быть много.

  9. «Мне эти задержки нужны для работы с внешними интерфейсами.»
    А что за внешний интерфейс так шустро работает на частоте 1МГц по прерыванию? И какие действия в обработчике прерывания планируются? Тут бы подробней, может я и помог бы.

    • Протокол похож на SPI только «дефективный» мне нужно передавать по 13 и 17 бит, т.е. обычный модуль SPI использовать не получиться. К примеру мне нужно установить линию CS и через 1-2uS передать данные, не позднее 1-2us начинать формировать сигнал синхронизации, потом выдержать в течении 4us сигнал и сделать его нулевым, через 1us нужно установить в ноль линию данные и т.д.
      Задача простая на микроконтроллерах PIC я это делал без проблем с более низкой производительностью и частотой, а тут удивился что такая проблема, при частоте 32Мгц, один шаг микроконтроллера выполняется за 31.25 nS т.е. за 1uS контроллер будет успевать выполнять 32 инструкции.
      Я вот думаю такой крутой контроллер, наверняка должны быть способы реализовать соответствующую задержку.

  10. За 1uS при частоте 32МГц можно выполнить 32 «атомарные» инструкции, т.е. на которые тратится ровно один такт. Передача данных через обычную посылку в порт идет по циклу «чтение-модификация-запись». Может причина в этом, может в чем-то другом. Советую начать с малого, в обработчике прерывания инвертировать вывод порта и посмотреть осциллографом частоту. Поискать в доках сколько тактов тратится на вход в прерывание, где-то есть, цифры не помню. 32 такта — довольно короткий интервал на самом деле, там еще вход в процедуру кушает несколько тактов. Вот.

  11. так и не понял одного- в программе мы пишем:
    SystemCoreClock/1000
    а ведь systick по документации делится еще на 8 от SystemCoreClock. Почему тут мы не учитываем эту восьмерку?

    • Давно это было, надо почитать доки. Но я тут вроде в тексте программы такой комментарий разместил:
      /*Вызов функции конфигурации системного таймера SysTick.
      Эта функция находится в файле core_cm3.h и:
      —Задает источник тактирования системного таймера (по умолчанию это SYSCLK = 2.097 МГц,
      другой возможный вариант — SysTick тактируется от SYSCLK/8);

      Можно же проверить, измерить задержку, должна быть 1 сек. Я в свое время все параметры временные с часами измерял сидел :)

      • настроил переключение на 1 минуту при записи
        SystemCoreClock/1000
        все четко. Но, ведь systick тактируется не напрямую от SystemCoreClock, судя по даташите.

        • А по факту напрямую. И как то все неоднозначно в разных источниках расписано, видимо зависит от производителя. Вот, например:
          «Источник тактирования для таймера SysTick настраивается с помощью регистра SysTick Control и регистра статуса в области управления Cortex-M3 System. При установленном бите CLKSOURSE, таймер SysTick будет работать на частоте ЦПУ. Если этот бит сброшен, таймер будет работать на частоте в 8 раз меньшей частоты ЦПУ.»
          Другой вариант — данным битом выбирается внешний или внутренний источник тактовой частоты для таймера:
          «You must set CLKSOURCE (bit 2) to 1 to select the core clock and zero select the external reference clock.» (цитата с infocenter.arm.com)
          «The Cortex-M3 processor includes a simple timer. Since all Cortex-M3 chips have the same
          timer, porting software between different Cortex-M3 products is simplifi ed. The timer is a
          24-bit down counter. It can use the internal clock (FCLK, the free running clock signal on
          the Cortex-M3 processor) or external clock (the STCLK signal on the Cortex-M3 processor).
          However, the source of the STCLK will be decided by chip designers, so the clock frequency
          might vary between products. You should check the chip’s datasheet carefully when selecting
          a clock source».
          «Возможность использования внешнеrо тактового сигнала определяется разработчиком микросхемы и в некоторых моделях может отсутствовать». Цитаты из «Ядро Cortex-M3 компании ARM. Полное руководство. Джозеф Ю.»
          По ссылке схема на стр.61 https://www.google.ru/url?sa=t&rct=j&q=&esrc=s&source=web&cd=1&ved=0CB4QFjAA&url=https%3A%2F%2Fmy.st.com%2Fpublic%2FSTe2ecommunities%2Fmcu%2FLists%2Fcortex_mx_stm32%2FAttachments%2F22412%2F2012_STM32%2520Technical%2520Updates%2520-%2520Issue%25202.pdf&ei=62cSVMbYOMbiywPSwYCAAw&usg=AFQjCNEIe5Hbx-s-vqFGBPR0XtkvukfJOQ&bvm=bv.75097201,d.bGQ&cad=rjt.
          По ней видно, что у таймера один источник тактирования — внутренний с делителем на 8. Значит надо проверить что дает установка бита CLKSOURSE.

  12. Fantomas

    Спасибо большое, статья полезная. Есть пара вопросов:
    Не слишком ли возможность формирования задержки таким способом загрузит МК, если в этот момент на нём будет сложная программа. Ведь прерывания вызываются независимо от того, вызвали ли мы задержку. Не лучше ли тогда отключать прерывания, когда эта задержка не нужна?
    И ещё какова логика тактирования STM? Если я подключаю внешний кварц, мне надо на него переключиться или она сама? То есть по умолчанию от MSI всегда? А может она сама может найти самый быстрый кварц в системе и от него затактироваться?

    • Fantomas

      Позволю себе выложить мой быдлокод, вдруг кому полезно будет. Плата у меня stm32l — discovery, МК — stm32l152rbt6. Тут я сделал что бы на фоне мигалка была, а кнопкой её можно было затормозить, запретив прерывание от системного таймера.
      #include «stm32l1xx.h»
      #include «stm32l1xx_rtc.h»
      #include «stm32l1xx_rcc.h»
      #include «stm32l1xx_pwr.h»
      #include «stm32l1xx_exti.h»
      #include «system_stm32l1xx.h»
      #include «misc.h»
      #include «stm32l1xx_gpio.h»
      #include «stm32l1xx_tim.h»

      uint16_t delay_count=0; // переменная для задержки
      char i = 0; // переменная — флаг того, что прерывание от системного таймера разрешено
      //—————————
      void PerSvet(char green, char blue) // функция для удобного переключения светодиодов
      {
      if(blue==1) GPIOB->ODR ^= GPIO_ODR_ODR_6; // надо синий — инвертируем синий
      if(green==1) GPIOB->ODR ^= GPIO_ODR_ODR_7; // надо зелёный — инвертируем зелёный
      }
      void SysTick_Handler(void)//1ms
      {
      if (delay_count > 0)
      {
      delay_count—;
      }
      }

      void delay_ms(uint16_t delay_temp)
      {
      delay_count = delay_temp;

      while(delay_count){}
      }
      void InitPeref()
      {
      RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA|RCC_AHBPeriph_GPIOB,ENABLE); // включаем тактирование портов
      // Инициализируем светодиоды
      GPIO_InitTypeDef init;
      init.GPIO_Mode = GPIO_Mode_OUT;
      init.GPIO_OType = GPIO_OType_PP;
      init.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7;
      init.GPIO_Speed = GPIO_Speed_10MHz;
      GPIO_Init(GPIOB,&init);
      // Инициализируем кнопку
      init.GPIO_Mode = GPIO_Mode_IN;
      init.GPIO_PuPd = GPIO_PuPd_DOWN;
      init.GPIO_Pin = GPIO_Pin_0;
      init.GPIO_Speed = GPIO_Speed_10MHz;
      GPIO_Init(GPIOA,&init);
      // конфигурируем прерывание от кнопки (PA0):
      RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN; //настройка внешнего прерывания на int0
      SYSCFG->EXTICR[0] |= SYSCFG_EXTICR1_EXTI0_PA; // подключаем линию прерываний EXTI0 к PA0
      EXTI->IMR |= EXTI_IMR_MR0; // какую линию выбираем из 23-х (у нас 0-я)
      EXTI->RTSR |= EXTI_RTSR_TR0; // прерывание по переднему фронту импульса (когда нажал кнопку)
      // EXTI->FTSR |= EXTI_FTSR_TR0; // прерывание по заднему фронту импульса (когда отпустил кнопку)
      NVIC_EnableIRQ(EXTI0_IRQn); //разрешение прерывания EXTI0
      NVIC_SetPriority(EXTI0_IRQn, 0); //задаем приоритет прерывания
      }
      void EXTI0_IRQHandler(void)
      {
      SysTick->CTRL^=0x0002; // запрещаем или разрешаем прерывание от системного таймра
      EXTI->PR |= EXTI_PR_PR0;//сбросили бит прерывания
      }
      int main(void)
      {
      InitPeref();
      // частота тактирования МК по умлочанию после запуска от MSI 2,097 МГц
      // интервал между прерываниями от системного таймера 2097/2097000=1 ms
      SysTick_Config(2097);
      i=1; //
      // В цикле инвертируем зелёный светодиод через каждую секунду
      while(1)
      {
      PerSvet(1,0);
      delay_ms(1000);
      PerSvet(1,0);
      delay_ms(1000);
      }
      }

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