STM32CubeIDE (вопросы и ответы)

Могу только предположить подачу 32768 Гц от в высокоточного источника считать интервалы. А так, как правильно заметили это таймер счётчик и внешний вход скорее нужен для счётчика.

Высокую частоту измерять, 10мгц, например.

Хм.
В моей (когда-то кудрявой) голове начинает всё складываться в единую картину. Огромное спасибо всем ответившим по существу!
Очень интересные эти микроконтроллеры, оказывается. А я на них смотрел только как на «авр с большей памятью и переферии». Ошибка моя, признаю.

На «пиках» пишу, но у меня «блохи» только разные, использую их как «аналог тини13» всего лишь (и детально не разбирался). Но надо будет приглядеться ))

Аппаратный антидребезг вроде можно настроить.
Завтра если интересно для f103 могу кинуть код на cmsis.
Мне понравилось, оно само там че то считает, остаётся толко кнопки анализировать.

2 лайка

У меня энкодер на установке неидеально работает, или дребезг или ограничения по скорости вращения. Пришлось ограничить скорость и сделать инкремент на несколько щелчков. Хочу попробовать алгоритм с 3я состояниями, тут находил. А проблема как раз что я свой антидребезг делал, он тормозит. Имхо надо вообще без него.

Не помешает. Думаю на таймере скорость энкодер будет огромную держать.

Чуда на дешевом энкодере не произойдёт, проверено. Но в любом случае работает стабильнее, чем программная обработка.

Спойлер
/*
 * f103encoder.h
 *
 *  Created on: Apr 11, 2024
 *      Author: seleznev_a
 */

#ifndef F103ENCODER_H_
#define F103ENCODER_H_

enum typeEncoderResult {
	encNone = 0, encLeft, encRight, encShort, encOption, encLong
};

static constexpr unsigned char countStepToChange = 3;

void initKeyEncoder(void);
void loopEncoder(void);
typeEncoderResult getEncoder(void);

#endif /* F103ENCODER_H_ */
1 лайк
Спойлер
/*
 * f103encoder.cpp
 *
 *  Created on: Apr 11, 2024
 *      Author: seleznev_a
 */

#include "f103encoder.h"
#include "stm32f10x.h"
#include "f103c8t6.h"

typeEncoderResult currentEncoderResult = encNone;
unsigned char lastKeyLevel = 1;
unsigned short lastCNT = 0;

void initKeyEncoder(void) {
	// TIM1 encoder mode
	RCC->APB2ENR |= RCC_APB2ENR_TIM1EN;
	TIM1->CR1 &= ~TIM_CR1_CEN;
	TIM1->SR = 0;
	TIM1->CNT = 0;
	TIM1->PSC = 0;
	TIM1->ARR = 65535;
	TIM1->SMCR &= ~TIM_SMCR_SMS; // 011: Encoder mode 3 - Counter counts up/down on both TI1FP1 and TI2FP2 edges depending on the level of the other input.
	TIM1->SMCR |= TIM_SMCR_SMS_0; // 011: Encoder mode 3 - Counter counts up/down on both TI1FP1 and TI2FP2 edges depending on the level of the other input.
	TIM1->SMCR |= TIM_SMCR_SMS_1; // 011: Encoder mode 3 - Counter counts up/down on both TI1FP1 and TI2FP2 edges depending on the level of the other input.
	TIM1->CCMR1 &= ~TIM_CCMR1_CC1S; // 01: CC1 channel is configured as input, IC1 is mapped on TI1
	TIM1->CCMR1 |= TIM_CCMR1_CC1S_0; // 01: CC1 channel is configured as input, IC1 is mapped on TI1
	TIM1->CCMR1 &= ~TIM_CCMR1_CC2S; // 01: CC2 channel is configured as input, IC2 is mapped on TI2
	TIM1->CCMR1 |= TIM_CCMR1_CC2S_0; // 01: CC2 channel is configured as input, IC2 is mapped on TI2
	// PA8 alt func TIM1_CH1
	RCC->APB2ENR |= RCC_APB2ENR_IOPAEN; // GPIO port A
	GPIOA->CRH &= ~GPIO_CRH_MODE8; // 00: Input mode (reset state)
	GPIOA->CRH &= ~GPIO_CRH_CNF8; // 10: Input with pull-up / pull-down
	GPIOA->CRH |= GPIO_CRH_CNF8_1; // 10: Input with pull-up / pull-down
	GPIOA->ODR |= GPIO_ODR_ODR8; // Input with pull-up
	// PA9 alt func TIM1_CH2
	GPIOA->CRH &= ~GPIO_CRH_MODE9; // 00: Input mode (reset state)
	GPIOA->CRH &= ~GPIO_CRH_CNF9; // 10: Input with pull-up / pull-down
	GPIOA->CRH |= GPIO_CRH_CNF9_1; // 10: Input with pull-up / pull-down
	GPIOA->ODR |= GPIO_ODR_ODR9; // Input with pull-up
	// PA10 key press
	GPIOA->CRH &= ~GPIO_CRH_MODE10; // 00: Input mode (reset state)
	GPIOA->CRH &= ~GPIO_CRH_CNF10; // 10: Input with pull-up / pull-down
	GPIOA->CRH |= GPIO_CRH_CNF10_1; // 10: Input with pull-up / pull-down
	GPIOA->ODR |= GPIO_ODR_ODR10; // Input with pull-up
	// start
	TIM1->CR1 |= TIM_CR1_CEN;
	// save current CNT
	lastCNT = TIM1->CNT;
	// save current key
	if (GPIOA->IDR & GPIO_IDR_IDR10) lastKeyLevel = 1; else lastKeyLevel = 0;
}

void loopEncoder(void) {
	  static unsigned char flagKeysDown = 0;
	  static unsigned long startTimeDown = 0UL;
	  unsigned char currentLevel;
		if (GPIOA->IDR & GPIO_IDR_IDR10) currentLevel = 1; else currentLevel = 0;
	  if (currentLevel != lastKeyLevel) {  // change level
	    if (currentLevel == 0) {        // key down
	      if (flagKeysDown == 0) {      // no timer
	        flagKeysDown = 1;           // begin timer
	        startTimeDown = millis();
	      }
	    } else {                    // key up
	      if (flagKeysDown != 0) {  // timer started
	        if ((millis() - startTimeDown) >= 2000UL) {
	          flagKeysDown = 0;
	          currentEncoderResult = encOption;
	        } else {
	          if ((millis() - startTimeDown) >= 500) {          // error press
	            flagKeysDown = 0;                               // stop timer
	          } else if ((millis() - startTimeDown) >= 50UL) {  // end timer
	            flagKeysDown = 0;                               // stop timer - short press
	            currentEncoderResult = encShort;
	          }
	        }
	      }
	    }
	  } else {                                             // level not change
	    if ((flagKeysDown != 0) && (currentLevel == 0)) {  // timer started and key down
	      if ((millis() - startTimeDown) >= 5000UL) {     // end timer
	        flagKeysDown = 0;                              // stop timer - long press
	        currentEncoderResult = encLong;
	      }
	    } else if (currentLevel != 0) {  // key up
	      flagKeysDown = 0;              // reset timer
	    }
	  }
	  lastKeyLevel = currentLevel;
	// find move encoder
	unsigned short currCNT = TIM1->CNT;
	if (currCNT != lastCNT) {
	    static bool lastLeft = false;
	    static unsigned char countLeftRight = 0;
	    if (currCNT > lastCNT) {
	      if (!lastLeft) {
	        if ((++countLeftRight) >= countStepToChange) {
	          currentEncoderResult = encRight;
	          countLeftRight = 0;
	        }
	      } else {
	        countLeftRight = 1;
	      }
	      lastLeft = false;
	    } else {
	      if (lastLeft) {
	        if ((++countLeftRight) >= countStepToChange) {
	          currentEncoderResult = encLeft;
	          countLeftRight = 0;
	        }
	      } else {
	        countLeftRight = 1;
	      }
	      lastLeft = true;
	    }
	    lastCNT = currCNT;
	}
}

typeEncoderResult getEncoder(void) {
	typeEncoderResult cR = currentEncoderResult;
  	currentEncoderResult = encNone;
	return cR;
}

Я сейчас с карандашом немного посидел и каааак понял! :slight_smile:
Может не всё, но что хотел - так точно!
Ещё раз всем спасибо!!!

карандаш с диаметром грифеля 0.5 был или 0.7?
это важно :rofl: :handshake:

1 лайк

В двух словах - что именно? Логику работы? Или последовательность действий правильную?

И то и другое, и можно без хлеба” (с) Винни Пух :smiley:
Все участники дискуссии помогли по немного, а больше всех - @Arduman (отдельное спасибо).

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

1 лайк

Я вообще так понимаю, что не существует никакого “тактирования таймера”, он в любом случае будет работать только как счетчик. А источник подсчитываемых импульсов может быть как внутренний, так и внешний и различные “события”. И сам тоже может генерировать различные “события”, взаимодействовать с другими таймерами и периферией… Вот это классно и очень удобно! :slight_smile:

Кстати, на github есть несколько, от простых до навороченных. Работают через прерывания таймера, обещают минимум обработки процессором. Некоторые, судя по коду, практичечески полностью сгенерены а кубе.

Как будет время - ссылками не поделишься? :slight_smile:

Ну это слишком смелое утверждение :smiley: Как таковое тактирование необходимо во многих случаях. Т.е. нужен независимый равномерный частотный сигнал относительно которого или на основании котрого фиксируются или генерятся события. У stm32 вариантов таких событий задумано особенно много и доступ к управлению ими прозрачен и логичен. И кроме того таймер может выступать как счётчик внешних событий без привязки ко времени. В этом случае необходимость тактировамия отсутствует.

Последний из рекомендаций ардуины
https://docs.arduino.cc/libraries/stm32encoder/

1 лайк

Тут конечно просто счетный вход используется.

“тактирование” тут вообще какой-то левый термин имхо. Непонятно кто кого тактирует; есть синхронизация с тактом процессора, внешней она никак не может быть вроде.