Top.Mail.Ru

STM32 и таймеры. STM32CubeMx. Настройка и использование.

Всем доброго времени суток, сегодня мы продолжим исследовать возможности STM32CubeMx и рассмотрим таймеры. Вот предыдущие статьи цикла:

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

Выполняем привычную последовательность действий - создаем и настраиваем новый проект в STM32CubeMx. И начнем с уже знакомого - активируем один из выводов для работы в качестве выхода, чтобы управлять светодиодом. Пусть будет PD12, не буду даже скриншот приводить, все это мы уже осуществляли в предыдущей статье курса 👍

Вторым этапом в данном случае будет активация и настройка непосредственно таймера, возьму TIM3 для этого примера, существенной разницы, какой именно модуль задействовать, нет:

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

Выбрав TIM3 на левой панели осуществляем последовательно следующее:

  • собственно, активируем его, задав Clock Source
  • задаем предделитель частоты таймера через поле Prescaler
  • и, наконец, задаем период в поле Counter Period.

Сегодня у нас базовый пример, поэтому остальные настройки не трогаем, оставим пока на будущее. Итак, разбираемся с частотами и временными интервалами.

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

Активация прерываний

Генерируем код и получаем готовый проект для выбранной IDE.

Теперь осталось совсем немного - запустить наш таймер (STM32CubeMx занимается только инициализацией, все активные действия мы должны совершать сами). Кроме того, в прерывании по переполнению таймера мы будем менять состояние светодиода. Cам обработчик прерывания CubeMx создал за нас, и найти его совсем несложно - для всех прерываний генерируется отдельный файл stm32f4xx_it.c (либо для другого контроллера аналогичный, например, stm32f1xx_it.c). В принципе, мы можем поместить наш код непосредственно в обработчик. Но HAL предлагает альтернативный способ с использованием callback-функций. Пара слов об этой концепции...

В библиотеке реализованы различные функции, объявленные с атрибутом weak, которые может использовать пользователь для добавления своего кода. Атрибут weak используется для функций-заглушек, которые могут быть переопределены. Таким образом, при наступлении определенного события в библиотеке происходит вызов такой callback-функции, тело которой является по умолчанию пустым. Но если программист переопределил эту функцию, то будет вызван этот новый вариант.

Для наглядности и понимания процессов рассмотрим на нашем конкретном примере. Механизм выглядит следующим образом:

  • Для таймера (как и для любой другой периферии) есть ряд callback-функций. Их список можно найти в описании HAL, нас же сейчас интересует вполне конкретная, а именно HAL_TIM_PeriodElapsedCallback(). Именно она соответствует событию переполнения таймера.
  • Эту функцию мы определяем, поместив в нее необходимый функционал.
  • И по итогу она будет вызвана автоматически из библиотеки при наступлении соответствующего события.

Давайте все это поэтапно осуществим. Для начала идем в 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 */

	/* Infinite loop */
	/* USER CODE BEGIN WHILE */
	while (1)
	{
		/* USER CODE END WHILE */

		/* USER CODE BEGIN 3 */
	}
	/* USER CODE END 3 */
}

Поскольку мы будем использовать прерывания, то нам нужна функция HAL_TIM_Base_Start_IT(). А теперь в этом же файле main.c добавим callback-функцию. При этом обязательно размещаем внутри секций вида:

/* USER CODE BEGIN 4 */

/* USER CODE END 4 */

Это нужно для того, чтобы после того как файлы будут перегенерированы (при изменении проекта в CubeMx), данный код был также автоматически перенесен в новые файлы:

/* USER CODE BEGIN 4 */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
	HAL_GPIO_TogglePin(GPIOD, GPIO_PIN_12);
}
/* USER CODE END 4 */

Здесь при помощи HAL_GPIO_TogglePin() мы меняем состояние светодиода.

В итоге функция будет вызываться каждый раз при переполнении таймера (аналогично соответствующему прерыванию). И, поскольку тут в качестве аргумента наличествует структура TIM_HandleTypeDef, которая является базовой при работе с таймерами, то мы можем ее использовать, чтобы убедиться, что прерывание вызвано именно TIM3, а не другим модулем:

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
	if (htim->Instance == TIM3)
	{
		HAL_GPIO_TogglePin(GPIOD, GPIO_PIN_12);
	}
}

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

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

Подписаться
Уведомить о
guest

70 комментариев
Старые
Новые
Межтекстовые Отзывы
Посмотреть все комментарии
Alexey
Alexey
9 лет назад

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

Alexey
Alexey
Ответ на комментарий  Aveal
9 лет назад

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

Sergey
Sergey
9 лет назад

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

Алексей
Алексей
9 лет назад

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

Алексей
Алексей
9 лет назад

а также строка HAL_TIM_IRQHandler(&htim3); меняется на HAL_TIM_IRQHandler(&htim3);

Алексей
Алексей
9 лет назад

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

Алексей
Алексей
9 лет назад

также извиняюсь HAL_TIM_IRQHandler(&htim3); на HAL_TIM_IRQHandler(&htim3);

Алексей
Алексей
9 лет назад

хмхм чето не ладное с ответами((( HAL_TIM_Base_Start_IT(&htim3) строка

Алексей
Алексей
9 лет назад

пишется без &amp

Алексей
Алексей
9 лет назад

И насчет точности времени: приходит на 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 мс., верно ли я полагаю???

Алексей
Алексей
9 лет назад

И еще, прескаллер 16000-1 для повышения точности.

Андрей
Андрей
9 лет назад

Подскажите, break interrupt - это прерывания чем вызываются.

Никита
Никита
9 лет назад

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

Андрей
Андрей
9 лет назад

Друзья, недавно в 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__)))

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

Андрей
Андрей
9 лет назад

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

Андрей
Андрей
9 лет назад

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

Александр
Александр
9 лет назад

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

Викор
Викор
9 лет назад

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

Lexa
Lexa
8 лет назад

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

Алексей
Алексей
8 лет назад

Здравствуйте.
Столкнулся с проблемой разного времени выхода на вектор прерывания. В 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. Все болтается такое ощущение что что-то выполняет какие то операции и не дает сразу перейти на вектор но что не понятно. Подскажите что делать

Ефим
Ефим
8 лет назад

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

Ефим
Ефим
8 лет назад

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

Voha
Ответ на комментарий  Aveal
8 лет назад

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

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

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

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

Владислав
Владислав
Ответ на комментарий  Aveal
7 лет назад

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

Владислав
Владислав
Ответ на комментарий  Voha
7 лет назад

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

Владислав
Владислав
Ответ на комментарий  Владислав
7 лет назад

Поясню на примере. Допустим, мы пишем код для приемника и передатчика и хотим, чтобы это была одна и та же программа, которая, в зависимости от 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);

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

Владислав
Владислав
Ответ на комментарий  Владислав
7 лет назад

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

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

Владислав
Владислав
Ответ на комментарий  Владислав
7 лет назад

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

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

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

Владислав
Владислав
Ответ на комментарий  Владислав
7 лет назад

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

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

Владислав
Владислав
Ответ на комментарий  Владислав
7 лет назад

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

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

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

Владислав
Владислав
Ответ на комментарий  Владислав
7 лет назад

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

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

Владислав
Владислав
Ответ на комментарий  Владислав
7 лет назад

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

Владислав
Владислав
Ответ на комментарий  Voha
7 лет назад

Вот кусок кода из 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;

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

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

Владислав
Владислав
Ответ на комментарий  Ефим
7 лет назад

завтра ответить попробую, не сегодня.

а завтра - предъявлю владение CubeMX в тех вопросах, где малость разобрался.

Оки?

Ефим
Ефим
8 лет назад

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

Василий Теркин
Василий Теркин
7 лет назад

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

Адлан
Адлан
Ответ на комментарий  Aveal
6 лет назад

Здравствуйте!
Настраиваю в Кубе таймер на энкодер. Куб позволяет подтягивать входы вниз, вверх или оставить болтаться. Однако в созданном коде (IAR EWARM) нигде не нашел, чтобы эти входы инициализировались. Между тем, таймер и не считает данные с энкодера. Может, надо было вручную?

Василий Теркин
Василий Теркин
7 лет назад

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

Василий Теркин
Василий Теркин
Ответ на комментарий  Aveal
7 лет назад

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

Василий Теркин
Василий Теркин
Ответ на комментарий  Aveal
7 лет назад

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

Адлан
Адлан
Ответ на комментарий  Aveal
7 лет назад

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

Сергей
Сергей
7 лет назад

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

LEV
LEV
7 лет назад

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

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

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

Адлан
Адлан
Ответ на комментарий  Aveal
6 лет назад

Здравствуйте!
Я так и делаю: TIM4->ARR = "расчетное значение" - для генерации импульсов управления частотником - от 20 до 5000 Гц. Почему-то при росте частоты генерация срывается и возобновляется через (примерно) 4 секунды, а при уменьшении все работает непрерывно. Если бы контроллер сбивали импульсы от энкодера или собственного выхода, это было бы и при падении частоты (кажется). Если бы МК зависал от внешних помех, то перестал бы работать весь, а не только таймер TIM4. Если бы не хватало быстродействия (чтобы переключать выход с частотой в сотню - другую герц?), его не хватало бы и после паузы в несколько секунд... В общем, не знаю, что и предполагать. Ваши опыт и знания что-нибудь подсказывают?
Спасибо

Адлан
Адлан
6 лет назад

Править комментарий здесь, похоже, нельзя, но Вы можете - если слишком длинно - сократить его до разумных пределов, или удалить совсем

Адлан
Адлан
Ответ на комментарий  Aveal
6 лет назад

Спасибо, я об этом не подумал. Завтра проверю и отпишусь

Адлан
Адлан
Ответ на комментарий  Адлан
6 лет назад

Здравствуйте! Сегодня попробовал записывать в прерывании в регистр ARR значение, вычисленное в цикле. Но переменная в обработчике получает нулевое значение независимо от результатов расчета. Может, что то опять не так делаю? Сообщение с проектом на Ваш адрес почему-то не ушло, сейчас попробую сообщение без вложения. Спасибо

Адлан
Адлан
Ответ на комментарий  Aveal
6 лет назад

Письмо без вложения ушло. Значит, дело было не в адресе, а во вложении (проект IAR, завернутый в RAR) - в списке запрещенных такого файла нет, не знаю, почему

70
0
Оставьте комментарий! Напишите, что думаете по поводу статьи.x