STM32Cube. Таймер и прерывания.

Всем доброго времени суток!

Сегодня мы снова будем исследовать возможности STM32Cube. Вот предыдущие статьи мини-цикла )

Создание проекта в STM32Cube

Настройка GPIO в STM32Cube

Таймер в микроконтроллерах

Без лишних слов, перейдем сразу к делу 😉

В предыдущей статье мы разобрались как настраивать и использовать порты ввода-вывода в STM32Cube, а сегодня рассмотрим конфигурацию таймера и прерываний. Пусть будет такая задача — задействуем таймер таким образом, чтобы он генерировал прерывания, допустим, каждые 500 мс, и в прерывании помигаем диодиком для наглядной демонстрации работы. В принципе, ничего сложного, так что приступаем!

Выполняем привычную нам последовательность действий — создаем и настраиваем новый проект в STM32Cube. И для начала нам нужно активировать таймер. Пусть в нашем примере будет использоваться TIM3:

Настройка таймера

Для активации таймера в разделе TIM3 нам нужно в поле Clock Source выбрать источник тактирования. Кроме того, я тут настроил вывод PD12 на работу в режиме выхода, чтобы управлять светодиодом на плате для демонстрации работы программы. Вроде бы все понятно, но с таймером не все так просто. Его нужно как минимум еще и настроить ) Для этого переходим на вкладку Configuration:

Настройки таймера

Дважды тыкаем на TIM3 и открывается новое окно, в котором мы уже можем более гибко настроить параметры таймера:

STM32Cube

Таймер сидит на шине APB1, частота которой составляет 16 МГц (это настройка по умолчанию — как изменить тактовые частоты при помощи Cube я расскажу  в следующей статье =) ). Выбираем делитель частоты равным 16000. В итоге получаем (16 МГц / 16000) = 1000 Гц. То есть один тик таймера будет соответствовать 1 мс. Установив период 499 (это значение в Cube надо ставить на единицу меньше, чем целевое значение, аналогично и значение делителя) получим прерывания по переполнению таймера каждые 500 мс — то, что нам и надо. В этом же окне во вкладке NVIC Settings нужно, собственно, включить нужное нам прерывание, и на этом настройки заканчиваются. Генерируем код и получаем готовый проект для IAR.

Теперь осталось совсем немного — запустить наш таймер (STM32Cube занимается только инициализацией, все активные действия мы должны совершать сами). Кроме того, в прерывании по переполнению таймера мы будем менять состояние светодиода. Кстати, сам обработчик прерывания Cube создал за нас и найти его совсем несложно — для всех прерываний генерируется отдельный файл stm32f4xx_it.c.

Давайте все это поэтапно осуществим. Для начала идем в main() и запускаем таймер:

int main(void)
{
 
  /* USER CODE BEGIN 1 */
 
  /* USER CODE END 1 */
 
  /* MCU Configuration----------------------------------------------------------*/
 
  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();
 
  /* Configure the system clock */
  SystemClock_Config();
 
  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_TIM3_Init();
 
  /* USER CODE BEGIN 2 */
 
  HAL_TIM_Base_Start_IT(&htim3);
 
  /* USER CODE END 2 */
 
  /* USER CODE BEGIN 3 */
  /* Infinite loop */
  while (1)
  {
 
  }
  /* USER CODE END 3 */
 
}

Поскольку мы будем использовать прерывания, то нам нужна функция HAL_TIM_Base_Start_IT().

Переходим к следующему этапу — а именно к изменению состояния светодиода в прерывании. Для этого даже есть специальная функция HAL_GPIO_TogglePin():

void TIM3_IRQHandler(void)
{
  /* USER CODE BEGIN TIM3_IRQn 0 */
 
  /* USER CODE END TIM3_IRQn 0 */
  HAL_TIM_IRQHandler(&htim3);
  /* USER CODE BEGIN TIM3_IRQn 1 */
 
  HAL_GPIO_TogglePin(GPIOD, GPIO_PIN_12);
 
  /* USER CODE END TIM3_IRQn 1 */
}

Теперь мы можем скомпилировать код, собрать проект и прошить микроконтроллер. Сразу же увидим мигающий диод, причем мигает он каждые 500 мс 😉 Задача успешно реализована!

Как видите, STM32Cube оказывается довольно-таки удобным инструментом для создания своих проектов и это заметно даже в небольших проектах, поэтому в следующих статьях мы продолжим его изучение, оставайтесь на связи!

Понравилась статья? Поделись с друзьями!

STM32Cube. Таймер и прерывания.: 61 комментарий
  1. Спасибо за материал! Ответьте, пожалуйста, на вопрос! Можно ли при работе с АЦП устанавливать частоту дискретизации, т.е. количество выборок в секунду? Или нужно использовать прерывание по таймеру и делать однократное преобразование?

    • Ну АЦП у тебя будет обрабатывать сигнал с высокой частотой (1 мвыб/ с, например), а уж сколько раз считывать значение — твое дело. Можно спокойно включить АЦП в непрерывном режиме и считывать значение по таймеру, допустим, 5 раз в секунду.

      • Спасибо за ответ! А в каком регистре задается значение, указанное вами 1 мвыборка/с? Наверное, слишком бегло я просмотрел даташит и не увидел. Могли вы привести просто пару строчек кода с примером! Спасибо!

  2. STM32Cube вещь интересная.
    Но! Сделав простой пример, поразился размером прошивки даже с оптимизацией. На SPL в 3 раза меньше получается, уж не говоря про проект на чисто регистрах.
    И мало хелпа, я так и не смог запустить ADC.

    • С размером кода — это, конечно, минус HAL_Driver, но в то же время это вполне ожидаемый минус, то есть, в принципе, ничего неожиданного в этом нет =)

  3. В Версии иара 7.3 строка HAL_TIM_Base_Start_IT(&htim3); работает в таком виде только: HAL_TIM_Base_Start_IT(htim3); , на описанный вид ругается. И еще, чтобы сделать, например 10 мс период, необходимо например при 1 кГц предделителя ставить 9 (а не 10) в число периода.

  4. *извиняюсь: В Версии иара 7.3 строка HAL_TIM_Base_Start_IT(&htim3); работает в таком виде только: HAL_TIM_Base_Start_IT(&htim3);

  5. И насчет точности времени: приходит на APB1 Timer Clocks 2 МГц, выставляю такие параметры (используя таймер 12)
    htim12.Instance = TIM12;
    htim12.Init.Prescaler = 2000;
    htim12.Init.CounterMode = TIM_COUNTERMODE_UP;
    htim12.Init.Period = 3;
    htim12.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
    HAL_TIM_Base_Init(&htim12);

    получаю на осцилле период в 8 мс (частотой 125 Гц), то есть таймер меняется через каждые 4 мс. То есть получается, что для получения нужного периода необходимо убавлять единицу, возможно это происходит, что счет начинается с 0, и по факту при занесении константы 3, у нас будет период 4 мс., верно ли я полагаю???

  6. Здравствуйте. Подскажите пожалуйста. Вот строка HAL_TIM_Base_Start_IT(&htim3); запускает таймер? То есть таймер начинает считать только после этой строки. Правильно ли я понял?
    И в связи с этим вопрос: а при настройке таймера в режим аппаратного ШИМ эта строка так же необходима?

  7. Друзья, недавно в stm. Сразу начал осваивать hal. Требуется помощь.
    Подскажите, пожалуйста, как правильно обрабатывать прерывания DMA? Я настроил прерывания от DMA. Настроил таймеры. Обработчик находится в _it.c. я так понимаю.
    У меня алгоритм следующий. 1) Таймер по захвату передает данные посредством DMA. DMA по окончанию приема должно выработать прерывание. Но так как перывания от DMA объединены, нам надо убедиться, что это «прерывание по завершению» прочитав соответствующий флаг.
    Верная ли последовательность?
    Как это сделать (прочитать флаг)?
    Нужно ли потом очищать флаги?
    2) Нужно определить по спаду или нарастанию на таймере-счетчике произошел запрос от DMA.
    Как прочитать регистр флагов прерывания?
    3) подскажите как пользоваться библиотекой, иногда получается (с выводами, с USART, а иногда нет).
    Вот например очистка флагов DMA. В dma.h есть такой define:

    #define __HAL_DMA_CLEAR_FLAG(__HANDLE__, __FLAG__) \
    (((uint32_t)((__HANDLE__)->Instance) > (uint32_t)DMA2_Stream3)? (DMA2->HIFCR = (__FLAG__)) :\
    ((uint32_t)((__HANDLE__)->Instance) > (uint32_t)DMA1_Stream7)? (DMA2->LIFCR = (__FLAG__)) :\
    ((uint32_t)((__HANDLE__)->Instance) > (uint32_t)DMA1_Stream3)? (DMA1->HIFCR = (__FLAG__)) : (DMA1->LIFCR = (__FLAG__)))

    #define __HAL_DMA_GET_FLAG(__HANDLE__, __FLAG__)\
    (((uint32_t)((__HANDLE__)->Instance) > (uint32_t)DMA2_Stream3)? (DMA2->HISR & (__FLAG__)) :\
    ((uint32_t)((__HANDLE__)->Instance) > (uint32_t)DMA1_Stream7)? (DMA2->LISR & (__FLAG__)) :\
    ((uint32_t)((__HANDLE__)->Instance) > (uint32_t)DMA1_Stream3)? (DMA1->HISR & (__FLAG__)) : (DMA1->LISR & (__FLAG__)))

    Как им воспользоваться, как с помощью него считывать?

  8. И еще один вопрос. После создания проекта в Cube в keil у меня на некоторых строках в файлах .h следующая надпись unknow type name uint32_t , в частности напротив этой строки #include «stm32f4xx_hal_def.h» . Это ненормально же? Как это исправить можно?

  9. Keil5. Сразу после генерации проекта в Cube, еще ничего не пишу, просто проект собираю и в .h файлах такая штука появляется

  10. TIM1 расширенный режим. Генерация комплементарных сигналов PWM с deadtime.
    Запуск HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_1);
    Вопрос: сигнал генерится только на одном выходе TIM1_CH1N (PA7), на другом выходе TIM1_CH1 (PA8) фиксированный уровень. В чем может быть проблема?

  11. Если ВЫ уж используете HAL, то советую не в прерывании писать а использовать Callback, например HAL_TIM_PeriodElapsedCallback(прерывание по периоду).

  12. Здравствуйте.
    Подскажите пожалуйста как пользоваться Callback-ми для обработки данных? Вот допустим получил данные по SPI, мне необходимо обработать их. Если не писать обработку в обработчике прерываний, то как это сделать с помощью callback?

  13. Здравствуйте.
    Столкнулся с проблемой разного времени выхода на вектор прерывания. В cube установил высокий приоритет HAL_NVIC_SetPriority(TIM3_IRQn, 0, 0); у всех остальных HAL_NVIC_SetPriority(x, 0, 1);
    Вот код void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim){
    counter_time ++;
    switch (counter_time)
    {
    case 1:
    HAL_GPIO_WritePin(GPIOE, GPIO_PIN_3, GPIO_PIN_SET);
    HAL_GPIO_WritePin(GPIOE, GPIO_PIN_3, GPIO_PIN_RESET);
    break;

    case 2: HAL_GPIO_WritePin(GPIOE, GPIO_PIN_2, GPIO_PIN_SET); flag_1 = 1; break;

    case 3: HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_SET); break;

    case 4: HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_RESET); counter_time =0;break;

    }
    }
    Смотрел осциллографом pin stm32f429. Все болтается такое ощущение что что-то выполняет какие то операции и не дает сразу перейти на вектор но что не понятно. Подскажите что делать

  14. Здравствуйте!
    Я начинающий, поэтому не судите строго, но данное занятие я проделать не смог.
    Не объясняется что , как и почему, почему именно эти значения в инфекциях и откуда взяли. Также куда вставили последний кусок
    В общем приходится додумывать самому

  15. в общем разобрался откуда ноги растут
    https://www.youtube.com/watch?v=GrF0Kto5c48
    вот не плохое видео. которое хорошо дополняет этот урок
    сам все делаю в воркбенхе код после куба автоматом разбит на файлы
    править нужно майн и stm32f1xx_it.c
    единственное пока не понял у меня 103 контроллер от кварца на 72 мегагерца. таймер 2 на 1APB частота 36М делил на 36к и потом умножал на 499 получалось 2 секунды. пока с определением времени вопрос открытый

    • Добрый день!

      На самом деле вот в этом абзаце полностью объясняется откуда берутся два числа, которые используются в примере:
      «Выбираем делитель частоты равным 16000. В итоге получаем (16 МГц / 16000) = 1000 Гц. То есть один тик таймера будет соответствовать 1 мс. Установив период 499 (это значение в Cube надо ставить на единицу меньше, чем целевое значение, аналогично и значение делителя) получим прерывания по переполнению таймера каждые 500 мс — то, что нам и надо.»

      Ну а последний кусок — это же прерывание, из названия функции это видно…

      По поводу частоты — видимо тактирование неверно настроено

      • Добрый день Aveal. Спасибо за такие классные материалы по HAL, всё очень просто и понятно.

        Есть у меня небольшой вопрос. Подразумевает ли Cube возможность изменение периода таймера во время работы контроллера? Я хочу изменять период таймера на работающем контроллере, без перепрошивки.

        Я предполагаю нужно в этой строке «htim3.Init.Period = 499» вместо числового значения поставить переменную «htim3.Init.Period = a», а потом перезапускать таймер после изменения значения этой переменной. Но боюсь Cube сотрёт эту переменную, т.к. она будет находится вне блока User Code.

        Может есть ещё способы?
        А может ткнёте носом в мануал\ссылку, где это всё расписано 🙂

        • Можно просто в регистр таймера напрямую писать значения — регистр TIMx->ARR, насколько я помню.

          • Нет, при такой схеме мы просто альтернативно инициализируем таймер, в зависимости от некоего условия на входе.
            Могу кусок простого кода добавить для ясности.
            Проблема в том, что пишу с Мака, а реальный код — на работе.
            Но суть-то в другом: в Кубе мы МОЖЕМ указывать некие ПЕРЕМЕННЫЕ, а вот где и в каком блоке кода их можно использовать — я про это говорил.
            На вкус и цвет.

        • В самом Cube надо в настройках писать не числа, а имена переменных или даже формулы с переменными (надо только там галку тыкнуть, чтоб не проверял на корректность) — тогда в коде, который он генерит, будет ровно то, что вы напишете в строке параметра.

          • Поясню на примере. Допустим, мы пишем код для приемника и передатчика и хотим, чтобы это была одна и та же программа, которая, в зависимости от 0 или 1 на некоем пине работала бы либо как приемник, либо как передатчик, но при этом, моргала бы разной частотой светодиода — штоб отличать одну от другой.
            Пусть за отличие приемника от передатчика морганием светодиода будет отвечать шестой таймер, как самый простой.
            Пусть у нас на ARB частота равна 36МГц.
            Пусть мы хотим на приемнике мигать лениво, а на передатчике — в 2 раза чаще.
            1. До функции main() в пользовательском блоке объявляем свою переменную — MyTIM6_Cnt (название — произвольное).
            2. В CubeMX указываем в прескалере «36000 — 1»
            3. В CubeMX указываем значение счетчика «MyTIM6_Cnt -1» (справа указывая, что данное поле проверять не надо, а надо вставлять в код так, как написано).
            4. В main(), после инициализаций, добавляем в юзерской части проверку порта, который по-умолчанию Pull-Up.
            (А если хотим сделать девайс обратным — просто замыкаем вывод на землю)
            5. А дальше — просто: после всех инициализаций в юзерском блоке проверяем значение порта, присваиваем MyTIM6_Cnt нужное значение и еще раз переинициализируем и запускаем таймер:
            MX_TIM6_Init();
            HAL_Base_Start_IT(&htim6);

            Вот и всё — получаем кубом код, который не зависит от того, что написано там в полях настройки не будет испорчен при изменении настроек куба.

          • Одна особенность.
            По умолчанию, все инициализации всего в Keil делаются в файле main.c
            Когда девайсов становится много — удобнее тыкнуть галочку в настройках проекта, штоб инициализации для каждого устройства и их «.h»-файлы были различными (чтоб не мешали в main.c)
            Так вот — тогда у вас эта ваша переменная типа MyTIIM6_Cnt — не опознается в отдельных файлах инициализаций.

            Просто добавьте там строчку, типа
            extern MyTIM6_Cnt.
            Но не

          • но не оставляйте так — указывайте между словом extern и именем переменной — тот самый тип, который нужен.

            если у вас было в main.c указано
            unsigned short MyTIM6_Cnt
            то и в файле инициализации нужно будет сказать полно:
            extern unsigned short MyTIM6_Cnt;

            Впрочем, это уже знания Си, а не МК.

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

          • Нет, при такой схеме мы просто альтернативно инициализируем таймер, в зависимости от некоего условия на входе.
            Могу кусок простого кода добавить для ясности.
            Проблема в том, что пишу с Мака, а реальный код — на работе.
            Но суть-то в другом: в Кубе мы МОЖЕМ указывать некие ПЕРЕМЕННЫЕ, а вот где и в каком блоке кода их можно использовать — я про это говорил.
            На вкус и цвет.

            P.S. У вас не туда и не так привязываются ответы.
            Что-то с базой не так.

          • А, понял.
            Нет, после повторной инициализации таймера и его запуска (а это все происходит по коду после системных инициализаций) — получаем результат.

            Тут ведь как — изначальная настройка таймера — была в системном блоке.

            А вот в юзерском — опрашиваем пин и еще раз таймер перенастраиваем на запуск.

          • Ну, короче, если вы поймете, как в настройках CubeMX указывать не числа, а переменные (и где и как в коде эти переменные располагать и где их использовать) — вы малаццы.

            Мне, так-то, добавить уже нечего.

          • Давайте я завтра просто пример кода сюда скопирую?

        • Вот кусок кода из main.c
          (на ibyt ARB1 для таймера задана частота 36МГц)

          /* USER CODE BEGIN PV */
          /* Private variables ———————————————————*/

          unsigned short MyTIM6_Cnt=0; /* Здесь будет храниться значение счетчика таймера */

          #define MODE_RECEIVER 0
          #define MODE_TRANSMITTER 1

          /* Т.к. пин режима «подтянут» к единице, то по умолчанию — мы передатчик */
          unsigned short RxTxMode = MODE_TRANSMITTER;

          /* USER CODE END PV */

          /* Private function prototypes ————————————————*/
          void SystemClock_Config(void);
          void Error_Handler(void);
          static void MX_NVIC_Init(void);

          /* USER CODE BEGIN PFP */
          /* Private function prototypes ————————————————*/

          /* USER CODE END PFP */

          /* USER CODE BEGIN 0 */

          /* USER CODE END 0 */

          int main(void)
          {

          /* USER CODE BEGIN 1 */

          /* USER CODE END 1 */

          /* MCU Configuration———————————————————-*/

          /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
          HAL_Init();

          /* Configure the system clock */
          SystemClock_Config();

          /* Initialize all configured peripherals */
          MX_GPIO_Init();
          MX_USART1_UART_Init();
          MX_TIM6_Init();

          /* Initialize interrupts */
          MX_NVIC_Init();

          /* USER CODE BEGIN 2 */

          /* TEST HAL_GPIO_WritePin(Light_1_GPIO_Port, Light_1_Pin, GPIO_PIN_SET);*/

          /* Опрашиваем пин режима и принимаем решение кто мы сейчас */
          RxTxMode = (HAL_GPIO_ReadPin(RxTxMode_GPIO_Port, RxTxMode_Pin) == GPIO_PIN_RESET)
          ? MODE_RECEIVER
          : MODE_TRANSMITTER;

          /* Пусть приемник будет мигать раз в секунду, передатчик — 2 раза в секунду */
          MyTIM6_Cnt = (RxTxMode == MODE_RECEIVER) ? (500-1) : (250-1);

          /* А теперь еще раз инициализируем таймер и запускаем его */
          MX_TIM6_Init();
          HAL_TIM_Base_Start_IT(&htim6);

          if (RxTxMode == MODE_RECEIVER)
          {
          while (1)
          {
          /* Здесь код приемника */
          }
          }
          else /* MODE_TRANSMITTER */
          {
          while (1)
          {
          /* Здесь код передатчика */
          }
          }

          /* USER CODE END 2 */

          А вот то, что генерит Куб в инициализации таймера:

          /* TIM6 init function */
          void MX_TIM6_Init(void)
          {
          TIM_MasterConfigTypeDef sMasterConfig;

          htim6.Instance = TIM6;
          htim6.Init.Prescaler = 36000 — 1;
          htim6.Init.CounterMode = TIM_COUNTERMODE_UP;
          htim6.Init.Period = MyTIM6_Cnt;
          if (HAL_TIM_Base_Init(&htim6) != HAL_OK)
          {
          Error_Handler();
          }

          sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;
          sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
          if (HAL_TIMEx_MasterConfigSynchronization(&htim6, &sMasterConfig) != HAL_OK)
          {
          Error_Handler();
          }

          }

          В строчках

          htim6.Init.Prescaler = 36000 — 1;
          htim6.Init.Period = MyTIM6_Cnt;

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

          Итак, это пример того, что вовсе необязательно в настройках Куба указывать абсолютные числа — можно (и нужно иногда) указывать свои переменные.

  16. Ув. Aveal насчет частот все понятно.
    вы не указали что надо поставить галочку NVIC Setting ну отсюда и естественно куда вкладывать код моргания диода (без галочки он не генерируется), так же нет объяснения откуда взялся параметр (&htim3) — по логике вещей понятно, но на видео по ссылке, что вкладывал автор все показывает и становится понятно, т.к. хотелось, что бы инструкция была относительно универсальна. (т.е. к разным МК и таймерам)
    для своего Мк выставил все верно — таймер2 частота 36МГЦ (по кубу и это максимальная по даташиту) при делении на 36к и умножении на 499 почему то получилось 2сек, в итоге сделал умножение на 249 и вроде стала 1сек., но почему не понял

  17. (c)»вы не указали что надо поставить галочку NVIC Setting»
    А я два часа голову себе ломал — что я не так делаю. Досадная неточность автора. Спасибо Ефим.

    • Не совсем понял…В тексте же написано — «В этом же окне во вкладке NVIC Settings нужно, собственно, включить нужное нам прерывание, и на этом настройки заканчиваются.»

    • Ну и, в общем-то, сказано, куда поместить код моргания диодом — «Кстати, сам обработчик прерывания Cube создал за нас и найти его совсем несложно — для всех прерываний генерируется отдельный файл stm32f4xx_it.c.»

  18. Спасибо. Разобрался, в общем. Есть вопрос — как посчитать количество импульсов за единицу времени. Как я понял — нужно считать «тики» между спадом импульса и фронтом следующего. Нет ли у Вас подобного примера? Может, будет желание статью написать?

    • Наверно удобнее всего будет внешние прерывания использовать — http://microtechnics.ru/stm32f3-ispolzovanie-vneshnix-preryvanij/ . Тоже через Cube можно легко настроить. Тогда при приходе переднего (к примеру) фронта будет срабатывать прерывание и в нем просто можно инкрементировать счетчик.

      • Метод надежен, но есть слабое место. При низкой частоте импульсов — очень большая погрешность. Я собираюсь считывать сигнал с импульсного водомера. Частота (средняя), пусть, 10 имп./сек. При опросе счетчика, по прерыванию таймера, например, каждую секунду — может быть полная лажа. 8-11…а это более 10%
        Я думал, что ловить нужно спад импульса, начинать считать «тики» и по фронту следующего — считывать счетчик. Частота «тиков» известна, все остальное — уже детали. Но вот именно этот кусок — мне непонятно, как реализовать. Спад — подсчет — остановка по фронту… Если подскажете примером — буду очень признателен 🙂

        • Ну я и имел ввиду не прерывание таймера, а прерывания внешней линии EXTI — ловим в прерывании передний или задний фронт импульса (или и то, и другое). Параллельно работает таймер (можно без прерывания). И попав в прерывание EXTI считываем значение счетчика таймера. Затем для другого импульса — и рассчитываем период.

          Другой вариант — без таймера отдельного — например, на SysTick (он по умолчанию включен) отсчитывать секунду. А через EXTI считать число импульсов за секунду.

          • Спасибо. Метод с обработкой значения счетчика в прерывании по входу — похоже, то, что надо. Но там, видимо, есть нюанс — с учетом переполнения таймера, оно может попасть в период между импульсами на входе. Возможно, таймер нужно сбрасывать в 0, в обработчике прерывания?
            Нет ли у Вас интереса на эту тему сделать урок? Например, урок в DISCOVERY — как считать количество нажатий на кнопку за 1…10 сек?

        • Ну да, таймер можем сбросить в прерывании.

          Вообще хорошая тема для статьи, но до Нового Года со временем тяжело, если только после получится =)

          • Здравствуйте!
            Вот, как раз ищу, как сбросить таймер, когда он еще не «досчитал» до сброса?
            Спасибо

    • Ну и соответственно количество за единицу времени если знаем, то рассчитываем период. Можно и не считать количество, а настроить внешнее прерывание на передний и задний фронт и по значению таймера рассчитать период.

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

  19. при вхождении в процедуру обработки прерывания надо сбрасывать флаг прерывания или он автоматически сбрасывается?

  20. Здравствуйте! Есть необходимость менять htim3.Init.Period прямо в прерывании. Я пытаюсь сделать так:

    В прерывании
    htim3.Init.Period = 10000; // или др. значение

    ошибку не выдает но и период прерываний не меняется… подскажите пожалуйста как это исправить

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *