Энкодер на stm32f405 для новичка

Кто любит RISC в жизни, заходим, не стесняемся.
Ответить
Встал на лапы
Сообщения: 136
Зарегистрирован: Пт май 20, 2022 22:19:02
Откуда: Ижевск

Сообщение Ифторик »

Приобрел я пару месяцев назад платку stm32f405 от WeActStudio в качестве игрушки, а полгода назад набор ардуино от фриновы, но быстро мне стал не интересен, в этом наборе был игрушечный (квадратурный) энкодер.
Мне хочется получить от этого энкодера импульсы и посчитать, но возникла фундаментальная проблема, если подключить только первый канал энкодера - счетчик считает только вверх, если второй канал - только вниз, это означает энкодер не работает, он не должен реагировать на один из каналов, только на оба вместе или никак.
Два канала вместе считают в обоих направлениях, но возникает много ошибок, около 20-30% шагов пропускаются или считают лишнего, фильтры есть программные и физические, информацию вывожу на дисплей ST7796 320x480, перечитал несколько раз Reference manual раздел Advanced-control timers (TIM1 and TIM8), пока больше ничего не читал, мануал крайне сложен для понимания. Использую кубы MX и IDE.
Опыта в программировании у меня нет, совсем, ни в микроконтроллерах, ни в языках Си и прочих, но я почти понимаю что я делаю. Ткните носом почему режим энкодера не работает, перещелкал все возможные настройки, до которых дотянулся мой мозг, моя не понимать где искать.
Основной код прикладываю под спойлер, я смотрю только на него.
код: рабочий цикл while (1)
Спойлер

Код: Выделить всё

  HAL_TIM_Encoder_Start(&htim1, TIM_CHANNEL_ALL);
  while (1)
  {
	if (TIM1->SR & TIM_SR_CC1IF) //Если бит первого канала 1
		{
		PulseDIR1 = TIM1->CNT;								//читаем значение
		snprintf(DIRi, sizeof(DIRi), "%d", PulseDIR1);				//конвертируем PulseDIR1 в массив символов DIRi[16]
		str_dot_out(10, 100, DIRi, strlen(DIRi), 0, 0, SystemFont);	//выводим на экран
		TIM1->SR &= ~TIM_SR_CC1IF;							//обнуляем
		szDIRi0 = strlen(DIRi);								//считаем количество символов
		}
	if (TIM1->SR & TIM_SR_CC2IF) //Если бит второго канала 1
		{
		PulseDIR2 = TIM1->CNT;
		snprintf(DIRi, sizeof(DIRi), "%d", PulseDIR2);
		str_dot_out(10, 100, DIRi, strlen(DIRi), 0, 0, SystemFont);
		TIM1->SR &= ~TIM_SR_CC2IF;
		szDIRi1 = strlen(DIRi);
		}
	if (szDIRi1!=szDIRi0)   //если количество символов меняется,
		{
		lcd_fill_rect(10, 99, 70, 114, 0x001F); //...очищаем область, иначе цифры накладываются друг на друга
		str_dot_out(10, 100, DIRi, strlen(DIRi), 0, 0, SystemFont);
		szDIRi0=0;
		szDIRi1=0;
		}
  }
код: инициализация TIM1
Спойлер

Код: Выделить всё

  htim1.Instance = TIM1;
  htim1.Init.Prescaler = 5;								
  htim1.Init.CounterMode = TIM_COUNTERMODE_UP;		// регистр CR1 бит CMS=00 - в какую сторону считает счетчик
  htim1.Init.Period = 1000;							// регистр ARR
  htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;	// регистр CR1 бит CKD - коэф. деления
  htim1.Init.RepetitionCounter = 0;					// регистр RCR - указывает сколько раз пропустить обновление регистров (UEV)
  htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
  sConfig.EncoderMode = TIM_ENCODERMODE_TI12;		// регистр SMCR бит SMS
  sConfig.IC1Polarity = TIM_ICPOLARITY_RISING;			// регистр CCER бит CC1P
  sConfig.IC1Selection = TIM_ICSELECTION_DIRECTTI;		// регистр CCMR1 бит CC1S
  sConfig.IC1Prescaler = TIM_ICPSC_DIV1;
  sConfig.IC1Filter = 0xf;
  sConfig.IC2Polarity = TIM_ICPOLARITY_RISING;
  sConfig.IC2Selection = TIM_ICSELECTION_DIRECTTI;
  sConfig.IC2Prescaler = TIM_ICPSC_DIV1;
  sConfig.IC2Filter = 0xf;
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
Вложения
STM32-Timer-Encoder-Mode.jpg
картинка из Reference manual глава 17.3.16
(28.42 КБ) 18 скачиваний
Реклама
Мучитель микросхем
Сообщения: 494
Зарегистрирован: Пн фев 16, 2026 17:30:02

Сообщение Rapra »

Ифторик писал(а): Вт июн 16, 2026 19:59:48 мануал крайне сложен для понимания. ... ни в микроконтроллерах, ни в языках Си и прочих, но я почти понимаю что я делаю.
Зачем себя мучить? Займите свою жизнь чем-то другим. Есть и нормальные развлечения - блохерство, тиктохерство, бухло и бабы. Серьезно, без шуток.
Программирование - обязательно требует способность читать и понимать, что делаешь.

При настройке таймера в режим энкодера не требуется никаких прерываний или флагов CCxIF. В счетчике CNT напрямую отображается число посчитанных шагов.
Реклама
Мудрый кот
Сообщения: 1743
Зарегистрирован: Вт авг 15, 2017 10:51:13

Сообщение jcxz »

Ифторик писал(а): Вт июн 16, 2026 19:59:48

Код: Выделить всё

...
		snprintf(DIRi, sizeof(DIRi), "%d", PulseDIR1);				//конвертируем PulseDIR1 в массив символов DIRi[16]
		str_dot_out(10, 100, DIRi, strlen(DIRi), 0, 0, SystemFont);	//выводим на экран
		TIM1->SR &= ~TIM_SR_CC1IF;							//обнуляем
		szDIRi0 = strlen(DIRi);								//считаем количество символов

...

		snprintf(DIRi, sizeof(DIRi), "%d", PulseDIR2);
		str_dot_out(10, 100, DIRi, strlen(DIRi), 0, 0, SystemFont);
		TIM1->SR &= ~TIM_SR_CC2IF;
		szDIRi1 = strlen(DIRi);

...

		lcd_fill_rect(10, 99, 70, 114, 0x001F); //...очищаем область, иначе цифры накладываются друг на друга
		str_dot_out(10, 100, DIRi, strlen(DIRi), 0, 0, SystemFont);
...
Это не "код", это ужас!
Погуглите в инете учебники на тему программирования микроконтроллеров и почитайте что такое "обработчики прерываний" (ISR) и почему в них нельзя делать то, что вы тут наворотили. Это объясняется уже в основах.
ISR должен быть минимальным. И тем более не должен содержать каких-то выводов куда-то, куда вы сами не знаете.
Про любую функцию из какого-либо Куба или любой другой мурзилки - нужно сперва почитать что она такое, и где её можно вызывать. И не пользоваться чужим кодом, работы которого вы не понимаете.
Ифторик писал(а): Вт июн 16, 2026 19:59:48я почти понимаю что я делаю.
По "коду" видно, что совершенно не понимаете.
Мучитель микросхем
Сообщения: 494
Зарегистрирован: Пн фев 16, 2026 17:30:02

Сообщение Rapra »

Там нет обработчика прерываний :) Это написано внутри цикла while(1). Но проблема непонимания работы, тем не менее, есть в самом ярком виде. Автору вообще не следует заниматься программированием. Либо выбрать для себя самый минимальный уровень, как для младших школьников.
Реклама
Эиком - электронные компоненты и радиодетали
Встал на лапы
Сообщения: 140
Зарегистрирован: Вс авг 23, 2015 08:14:05

Сообщение Denis82 »

jcxz писал(а): Ср июн 17, 2026 13:58:19 Погуглите в инете учебники на тему программирования микроконтроллеров и почитайте что такое "обработчики прерываний" (ISR) и почему в них нельзя делать то, что вы тут наворотили. Это объясняется уже в основах.
а где вы увидели что тс на прерываниях работает ? я вижу while(1){ } :)
Реклама
Мудрый кот
Сообщения: 1743
Зарегистрирован: Вт авг 15, 2017 10:51:13

Сообщение jcxz »

Denis82 писал(а): Ср июн 17, 2026 14:55:04а где вы увидели что тс на прерываниях работает ? я вижу while(1){ } :)
Да, точно. Чё-то не доглядел. Но один фиг - он обрабатывает такие быстрые процессы, а вызывает тяжёлые функции при этом.
Да и работать с энкодером нужно именно в прерываниях. А не в суперлупе main().
Особенно глядя на это:
Ифторик писал(а): Вт июн 16, 2026 19:59:48 TIM1->SR &= ~TIM_SR_CC1IF; //обнуляем
Это само по себе неправильно (ТС - читать мануал!), так ещё и делается с большой и непредсказуемой задержкой относительно события. Что тоже неправильно.
Потому и глючит оно безбожно.

PS: Так что совет остаётся прежним - изучать что такое прерывания и делать работу на них.
Реклама
Мучитель микросхем
Сообщения: 494
Зарегистрирован: Пн фев 16, 2026 17:30:02

Сообщение Rapra »

Вообще, всё зависит от соотношения частот - системной МК и скорости поворота энкодера.
Ну а режим энкодера у таймера вообще не предполагает обязательного использования флагов CC1IF. Счетчик увеличивается или уменьшается на единицу при распознавании последовательности фронтов от энкодера. Значение счетчика отражает число зафиксированных шагов энкодера и прочитать его можно в любое время.
Флаг CC1IF в рассматриваемом режиме работы будет возникать лишь при достижении счетчиком импульсов установленного в CCR1 значения. А на направление вращения энкодера будет указывать бит TIM_CR1_DIR.
Последний раз редактировалось Rapra Ср июн 17, 2026 15:55:16, всего редактировалось 1 раз.
Вымогатель припоя
Аватара пользователя
Сообщения: 681
Зарегистрирован: Пн сен 15, 2025 08:43:23
Откуда: Маленький СССР посреди шариатской республики

Сообщение linux_rulezz »

jcxz писал(а): Ср июн 17, 2026 15:13:16 Да и работать с энкодером нужно именно в прерываниях.
Не нужно!
Просто когда надо - проверять значение регистра CNT, а в прерываниях нужно лишь счетчик переполнений инкрементировать/декрементировать…
Например, вот так:

Код: Выделить всё

void tim1_brk_up_trg_com_isr(){
    encoders_UPD(0);
}
void tim2_isr(){
    encoders_UPD(1);
}
void tim3_isr(){
    encoders_UPD(2);
}
...
void encoders_UPD(uint8_t i){
    if(enctimers[i]->SR & TIM_SR_UIF){
        int8_t d = 1; // positive (-1 - negative)
        if(enctimers[i]->CR1 & TIM_CR1_DIR) d = -d; // negative
        if(the_conf.motflags[i].encreverse) d = -d;
        if(d == 1) encpos[i] += the_conf.encrev[i];
        else encpos[i] -= the_conf.encrev[i];
    }
    enctimers[i]->SR = 0;
}
Windows must die!
Контактная информация:
Мучитель микросхем
Сообщения: 494
Зарегистрирован: Пн фев 16, 2026 17:30:02

Сообщение Rapra »

Таймер в режиме энкодера автоматически считает число шагов от энкодера и отражает их в регистре CNT! Направление вращения энкодера отражается битом TIM_CR1_DIR.
Таймер в режиме энкодера получает счетные импульсы от входов канала 1 и канала 2, распознавая последовательность фронтов.
Кто-нить вообще читал в мануале параграф Encoder interface mode?


14.3.16 Режим интерфейса энкодера
Для выбора режима интерфейса энкодера запишите SMS=‘001’ в регистр TIMx_SMCR, если счетчик считает только по фронтам TI2, SMS=’010’, если он считает только по фронтам TI1, и SMS=’011’, если он считает как по фронтам TI1, так и по фронтам TI2.

Выберите полярность TI1 и TI2, запрограммировав биты CC1P и CC2P в регистре TIMx_CCER. При необходимости пользователь также может запрограммировать входной фильтр.
Два входа TI1 и TI2 используются для подключения к инкрементальному энкодеру. См. Таблицу 81. Счетчик тактируется каждым допустимым переходом на TI1FP1 или TI2FP2 (TI1 и TI2 после входного фильтра и выбора полярности, TI1FP1=TI1, если фильтр не отфильтрован и не инвертирован, TI2FP2=TI2, если фильтр не отфильтрован и не инвертирован), при условии, что он включен (бит CEN в
регистре TIMx_CR1 записан в «1»). Последовательность переходов двух входов оценивается и генерирует импульсы счета, а также сигнал направления. В зависимости от последовательности счета счетчика вверх или вниз, бит DIR в регистре TIMx_CR1 изменяется аппаратно соответственно. Бит DIR вычисляется при каждом переходе на любом входе (TI1 или TI2), независимо от того, считает ли счетчик только на TI1, только на TI2 или на обоих TI1 и TI2.

Режим интерфейса энкодера работает просто как внешний тактовый сигнал с выбором направления. Это означает, что счетчик непрерывно считает от 0 до значения автоперезагрузки в регистре TIMx_ARR (от 0 до ARR или от ARR до 0 в зависимости от направления). Поэтому пользователь должен настроить TIMx_ARR перед началом работы. Аналогичным образом, функции захвата, сравнения, делителя частоты, счетчика повторений и вывода триггера продолжают работать в обычном режиме. Режим энкодера и режим внешнего тактового сигнала 2 несовместимы и не должны выбираться одновременно.
В этом режиме счетчик автоматически изменяется в соответствии со скоростью и направлением инкрементного энкодера, и его содержимое, следовательно, всегда представляет положение энкодера. Направление счета соответствует направлению вращения подключенного датчика.

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

Таймер, настроенный в режиме интерфейса энкодера, предоставляет информацию о текущем положении датчика. Пользователь может получать динамическую информацию (скорость, ускорение, замедление), измеряя период между двумя событиями энкодера с помощью второго таймера, настроенного в режиме захвата. Для этой цели можно использовать выходной сигнал энкодера, указывающий на механический ноль. В зависимости от времени между двумя событиями, показания счетчика также можно считывать через равные промежутки времени. Это можно сделать, зафиксировав значение счетчика в третьем входном регистре захвата, если он доступен (в этом случае сигнал захвата должен быть периодическим и может генерироваться другим таймером). При наличии также можно считывать его значение через запрос DMA, генерируемый часами реального времени.
Последний раз редактировалось Rapra Ср июн 17, 2026 16:13:45, всего редактировалось 1 раз.
Встал на лапы
Сообщения: 136
Зарегистрирован: Пт май 20, 2022 22:19:02
Откуда: Ижевск

Сообщение Ифторик »

Да, вы правы, я только начинаю изучать алфавит, до прерываний я еще не дошел, понятия не имею что это. Первые буквы, которые я напечатал, сильно не ругайтесь))
Цифирки сами бегают, мне уже радостно. Почему вы решили что чужой код использую? Библиотеку на дисплей от кейла в сети нашел, это да, да и то там только функция очистки дисплея и отправки текста, остальное не работает, в кейле библиотека ХАЛ чуть другая, я замучался ее переписывать и обрезать под куб, понятно что для вас это дело двух с половиной минут, а мне аж кажется что пентагон взломал, но перевод переменной в массив помог гугл, мне аж стыдно что я нашел такую функцию с помощью ИИ.
Мучитель микросхем
Сообщения: 494
Зарегистрирован: Пн фев 16, 2026 17:30:02

Сообщение Rapra »

Да не нужны здесь никакие прерывания, мало ли чего там jcxz напишет, он сам не понял, как работает таймер в режиме энкодера. Правильно настроенный таймер будет считать шаги энкодера и без прерываний. Достаточно через какие-то промежутки времени читать счетный регистр CNT таймера и всё.
Выше я вставил в сообщение гуглоперевод параграфа из мануала, там всё написано.
Вымогатель припоя
Аватара пользователя
Сообщения: 681
Зарегистрирован: Пн сен 15, 2025 08:43:23
Откуда: Маленький СССР посреди шариатской республики

Сообщение linux_rulezz »

Ифторик писал(а): Ср июн 17, 2026 16:11:55ХАЛ
Самая грубая ошибка - начинать со всякого кала!
Начинать нужно с полного нуля! Разве что, чтобы время не тратить, не самому заполнять все эти заголовочные файлы CMSIS с регистрами и флагами, а взять готовое (например, здесь).
Стартап, линкер-скрипт и т.п. лучше всего самому написать, чтобы понимание пришло. Ну и, естественно, весь базовый код писать самому...
Чужие "библиотеки" - это лишняя возможность добавить в свой код непонятных ошибок. Код для МК пишется достаточно быстро - вообще нет нужды привлекать что-то чужое.

P.S. У некоторых таймеров есть возможность быть "связанными" друг с другом. В таком случае два 16-битных таймера можно аппаратно "превратить" в один 32-битный, и не нужно будет вообще прерывания дергать, чтобы считать переполнения CNT. А в некоторых задачах и 16-битного счетчика шагов за глаза хватит: например, если диапазон перемещения контролируемой каретки меньше, чем 65535 отсчетов энкодера.
Последний раз редактировалось linux_rulezz Ср июн 17, 2026 16:22:41, всего редактировалось 1 раз.
Windows must die!
Контактная информация:
Мучитель микросхем
Сообщения: 494
Зарегистрирован: Пн фев 16, 2026 17:30:02

Сообщение Rapra »

Грубая ошибка - не читать мануалов. А ХАЛ - использовать можно. По крайней мере, для новичков это быстрый способ. Тем более, что топикстартер, как он сам признался, не блещет скилами.

Стартап и линкер-скрипт самому писать - тоже излишне для такого простого и стандартного случая. Просто нет смысла в этом.

Про 32-битный таймер. В F405 - это TIM2 и TIM5, они аппаратно 32-битные.
Вымогатель припоя
Аватара пользователя
Сообщения: 681
Зарегистрирован: Пн сен 15, 2025 08:43:23
Откуда: Маленький СССР посреди шариатской республики

Сообщение linux_rulezz »

Rapra писал(а): Ср июн 17, 2026 16:22:10топикстартер, как он сам признался, не блещет скилами.
Ну так ТСу и нужно начинать с чтения документации и написания "blink". А никак не с таймеров в режиме энкодера!
Естественно, еще и английский надо до нужного уровня подтянуть, если его еще нет.

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

А то привыкли, паимашь, что за них "ИИ" думает. А после третьей мировой как они жить будут? Тоже им "ИИ" еду добудет или огонь разожжет?
Windows must die!
Контактная информация:
Мучитель микросхем
Сообщения: 494
Зарегистрирован: Пн фев 16, 2026 17:30:02

Сообщение Rapra »

Есть мнение, что третью мировую развяжет сам ИИ, и в ней он сам и победит :)) В целом, показанный в "Терминахтере" 40 лет назад сюжет может иметь место быть, учитывая возможность саморазвития ИИ без участия человека. Уже сейчас ИИ может самомодифицировать собственный программный код. Есть мнение, что темпы развития ИИ уже сейчас начинают опережать способность человека понимать механизмы ИИ.
Это не хвост, это антенна
Сообщения: 1330
Зарегистрирован: Вт ноя 19, 2019 06:10:18

Сообщение tonyk »

linux_rulezz писал(а):нельзя к компьютеру подпускать человека, пока он не освоит линукс
Хм, а как осваивать Линукс, когда тебя не подпускают к нему? :))
Мы изучали компьютеры, когда Линукс ещё не существовало. Нам надо было подождать, пока он появиться, чтобы, так сказать, осваивать комьютер по фэншую? :))

И вообще, Линукс не лучшая реализация *nix, чтобы зацикиваться на ней.
Встал на лапы
Сообщения: 140
Зарегистрирован: Вс авг 23, 2015 08:14:05

Сообщение Denis82 »

tonyk писал(а): Чт июн 18, 2026 09:44:26
linux_rulezz писал(а):нельзя к компьютеру подпускать человека, пока он не освоит линукс
Хм, а как осваивать Линукс, когда тебя не подпускают к нему? :))
Мы изучали компьютеры, когда Линукс ещё не существовало. Нам надо было подождать, пока он появиться, чтобы, так сказать, осваивать комьютер по фэншую? :))

И вообще, Линукс не лучшая реализация *nix, чтобы зацикиваться на ней.
да слушайте его больше, давно известно что воинствующий красноглазик :)) нахер винду, только линукс, нахер ИД, только командная строка, нахер библиотеки, только регистры и никак иначе, тот кто этому не следует лузер и чмо :))
Прорезались зубы
Сообщения: 223
Зарегистрирован: Чт май 07, 2026 00:30:38

Сообщение Zapolyarny »

Rapra писал(а): Ср июн 17, 2026 05:08:54 Зачем себя мучить? Займите свою жизнь чем-то другим... Программирование - обязательно требует способность читать и понимать, что делаешь.
Только для профессионального уровня. А в хобби можно всё. И этим оно и прекрасно: можно открывать для себя новое, добиваться каких-то побед, не смотря на то, что для опытного всё это крайне просто и элементарно, и получать положительные эмоции. К тому же, способность понимать не требует понимания с первого раза. Можно с любого. В хобби сроки несколько иные.
Ответить

Вернуться в «ARM»