Могу только предположить подачу 32768 Гц от в высокоточного источника считать интервалы. А так, как правильно заметили это таймер счётчик и внешний вход скорее нужен для счётчика.
Высокую частоту измерять, 10мгц, например.
Хм.
В моей (когда-то кудрявой) голове начинает всё складываться в единую картину. Огромное спасибо всем ответившим по существу!
Очень интересные эти микроконтроллеры, оказывается. А я на них смотрел только как на «авр с большей памятью и переферии». Ошибка моя, признаю.
На «пиках» пишу, но у меня «блохи» только разные, использую их как «аналог тини13» всего лишь (и детально не разбирался). Но надо будет приглядеться ))
Аппаратный антидребезг вроде можно настроить.
Завтра если интересно для f103 могу кинуть код на cmsis.
Мне понравилось, оно само там че то считает, остаётся толко кнопки анализировать.
У меня энкодер на установке неидеально работает, или дребезг или ограничения по скорости вращения. Пришлось ограничить скорость и сделать инкремент на несколько щелчков. Хочу попробовать алгоритм с 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_ */
Спойлер
/*
* 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;
}
Я сейчас с карандашом немного посидел и каааак понял! ![]()
Может не всё, но что хотел - так точно!
Ещё раз всем спасибо!!!
карандаш с диаметром грифеля 0.5 был или 0.7?
это важно
![]()
В двух словах - что именно? Логику работы? Или последовательность действий правильную?
“И то и другое, и можно без хлеба” (с) Винни Пух ![]()
Все участники дискуссии помогли по немного, а больше всех - @Arduman (отдельное спасибо).
Вот как раз замечательный пример, где внешнее тактирование как раз не требуется, а используется свойство счётчика.
Я вообще так понимаю, что не существует никакого “тактирования таймера”, он в любом случае будет работать только как счетчик. А источник подсчитываемых импульсов может быть как внутренний, так и внешний и различные “события”. И сам тоже может генерировать различные “события”, взаимодействовать с другими таймерами и периферией… Вот это классно и очень удобно! ![]()
Кстати, на github есть несколько, от простых до навороченных. Работают через прерывания таймера, обещают минимум обработки процессором. Некоторые, судя по коду, практичечески полностью сгенерены а кубе.
Как будет время - ссылками не поделишься? ![]()
Ну это слишком смелое утверждение
Как таковое тактирование необходимо во многих случаях. Т.е. нужен независимый равномерный частотный сигнал относительно которого или на основании котрого фиксируются или генерятся события. У stm32 вариантов таких событий задумано особенно много и доступ к управлению ими прозрачен и логичен. И кроме того таймер может выступать как счётчик внешних событий без привязки ко времени. В этом случае необходимость тактировамия отсутствует.
Последний из рекомендаций ардуины
https://docs.arduino.cc/libraries/stm32encoder/
Тут конечно просто счетный вход используется.
“тактирование” тут вообще какой-то левый термин имхо. Непонятно кто кого тактирует; есть синхронизация с тактом процессора, внешней она никак не может быть вроде.