LINUX.ORG.RU
решено ФорумTalks

энкодер плохо работает

 , , , ,


0

1

Вот что интересно. Раньше использовал atmega328 и у меня энкодер работал без всякого debouncing. Работало тупо: смотрел на состояние второго пина при falling edge на первом (энкодер коротит pullUp пины на землю). Ну, всё как у всех.

А вот stm32F411re с тем же энкодером тупо не работает. Значения скачут очень рандомно (т.е. повернул ручку на одно деление, а в программе счётчик увеличивается сразу на 2-30). Причём, в лучшем случае удаётся читать показания при вращении в одну сторону, но не другую.

С чем может быть связано такое различие? Я вот подумал что питание и наводки вряд ли могут оказать какое-то влияение. Может, разница в частоте сказывается? Атмега на 8Mhz работала, а эта плата на 100MHz.

★★★★★

Последнее исправление: true_admin (всего исправлений: 1)

Ты, наверное. понимаешь, что это вопрос вопрос для экстрасенсов, а они в отпуске. Как догадаться, как ты подключил энкодер к этой STM32, как сконфигурировал, правильно ли. А без всего этого ответ такой: барабашка завелся, вызывай батюшку.

Zubok ★★★★★
()

верни атмегу назад :)

Harald ★★★★★
()

Может, разница в частоте сказывается? Атмега на 8Mhz работала, а эта плата на 100MHz.

ну так заведи ее на пониженной частоте, чо как маленький

registrant ★★★★★
()

Дребезг контактов энкодера, т.к. STM32 быстрее AVR, успевает обработать несколько срабатываний. Может быть из-за этого. Лечить программно или повесить кондер небольшой на этот вывод.

pitman
()

С чем может быть связано такое различие?

Тактовая выше.

Eddy_Em ☆☆☆☆☆
()

Поправьте метки

Поставьте в метки cortex-m вместо arm. Хочу чтобы у меня в уведомлениях всплывали темы с процессорами ARM, а не с контроллерами.

Camel ★★★★★
()
Ответ на: комментарий от aiqu6Ait

Сорри за кресты, взял готовое с mbed.org :) Код «либы» с небольшими сокращениями для простоты просмотра.

#include "mbed.h"                                       
#include "ILI9340_Driver.h"
#include "CRotaryEncoder.h"

volatile int enc1_val = 0;
#define CHBUF 512
char chbuf[CHBUF];

int main() {
    
    //               (mosi, miso, sck, cs, reset, dc)
    ILI9340_Display tft(D11, D12, D13, D8, D9, D10);
    tft.DispInit();
    tft.SetRotation(3);
    CRotaryEncoder enc1(D7, D6);
    while(1) {
        enc1_val = enc1.Get();
        snprintf(chbuf, CHBUF-1, "enc1: %d", enc1_val);
        tft.FillScreen(ILI9340_BLACK);
        tft.DrawString(chbuf, 1, 120, 4, ILI9340_WHITE);
        }
}
#include "mbed.h"
#include "CRotaryEncoder.h"
#include "PinDetect.h"

CRotaryEncoder::CRotaryEncoder(PinName pinA, PinName pinB)
{
    m_pinA = new InterruptIn(pinA);
    m_pinB = new DigitalIn(pinB);

// не понимаю зачем rize и fall смотреть одновременно,
// отрубил rise
//    m_pinA->rise(this, &CRotaryEncoder::rise);
    m_pinA->fall(this, &CRotaryEncoder::fall);

// pull up я добавил, не понимаю почему этого не было в оригинале    
    m_pinA->mode(PullUp);
    m_pinB->mode(PullUp);

    m_position = 0;
}

int CRotaryEncoder::Get(void)
{
    return m_position;
}
    
void CRotaryEncoder::fall(void)
{
        if(*m_pinB == 1)
        {
            m_position++;
        }
        else
        {
            m_position--;
        }
}

void CRotaryEncoder::rise(void)
{
        if(*m_pinB == 1)
        {
            m_position--;
        }
        else
        {
            m_position++;
        }
}

cast Zubok

true_admin ★★★★★
() автор топика
Ответ на: комментарий от ramon13666

pitman, ramon13666 с кондёрами на 104 паралельно выводам энкодера оно стало понимать в какую сторону я вращаю ручку, но инкремент всё равно по 10-30. Странно...

Надо бы осциллографом посмотреть, но в этом году я до него вряд ли дойду.

true_admin ★★★★★
() автор топика
Ответ на: комментарий от true_admin

А, это, оказывается, только для PWM input, но ты почитай даташит по поводу jitter в режиме counter:

Figure 127 gives an example of counter operation, showing count signal generation and direction control. It also shows how input jitter is compensated where both edges are selected. This might occur if the sensor is positioned near to one of the switching points. For this example we assume that the configuration is the following:
• CC1S=’01’ (TIMx_CCMR1 register, TI1FP1 mapped on TI1).
• CC2S=’01’ (TIMx_CCMR2 register, TI1FP2 mapped on TI2).
• CC1P=’0’, CC1NP=’0’, and IC1F = ‘0000’ (TIMx_CCER register, TI1FP1 non-inverted, TI1FP1=TI1).
• CC2P=’0’, CC2NP=’0’, and IC2F = ‘0000’ (TIMx_CCER register, TI1FP2 non-inverted, TI1FP2= TI2).
• SMS=’011’ (TIMx_SMCR register, both inputs are active on both rising and falling edges).
• CEN=’1’ (TIMx_CR1 register, Counter enabled).

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

Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от true_admin

Вот, посмотри осциллом или хотя бы шустрым логическим анализатором.

Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от true_admin

// pull up я добавил, не понимаю почему этого не было в оригинале

А зачем? У тебя же энкодер сам уровни TTL держит на выходах. Поэтому и не сделано. Или у твоего выход open collector? И какое у него по паспорту питание и как его питаешь ты?

Zubok ★★★★★
()
Ответ на: комментарий от Zubok

Хм, я под «энкодер» понимал такую мелкую фигню которая при вращении ручки замыкает контакты. Т.е. никакой электроники внутри. Типа такого: http://www.hobbytronics.co.uk/rotary-encoder . Я среднюю ногу на землю кидаю, остальные две к выводам МК.

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

Кстати, а может быть такое что я свой МК уже поджарил и ему тупо плохо? А то я тут на днях дисплей неправильно подсоединил...

true_admin ★★★★★
() автор топика
Ответ на: комментарий от true_admin

Хм, я под «энкодер» понимал такую мелкую фигню которая при вращении ручки замыкает контакты. Т.е. никакой электроники внутри.

Я думал, что у тебя оптический с питанием.

Zubok ★★★★★
()
Ответ на: комментарий от Eddy_Em

ОМГ, спасибо, это интересно. Вечером попробую.

true_admin ★★★★★
() автор топика
Ответ на: комментарий от true_admin

Кстати, а может быть такое что я свой МК уже поджарил и ему тупо плохо?

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

Eddy_Em ☆☆☆☆☆
()

Причём, в лучшем случае удаётся читать показания при вращении в одну сторону, но не другую.

Что это значит? Что значения при вращении в другую сторону не меняются? Объясни вот это место. Не очень-то похоже на последствия дребезга. Энкодер точно не убитый? Ты бы его проверил на всякий случай. Может, он там внутри развалился. Надо исключить.

Zubok ★★★★★
()
Последнее исправление: Zubok (всего исправлений: 1)
Ответ на: комментарий от true_admin

// не понимаю зачем rize и fall смотреть одновременно,
// отрубил rise

А вот это как раз и есть обработка дребезга. Представь себе модель дребезга, когда у тебя дребезг заднего фронта идет. Вопрос: сколько у тебя срабатываний? Три! А если ты оба фронта обрабатываешь, то у тебя будет m_position +1-1+1-1+1 - итого +1. Но это слишком наивная обработка, так как счетчик принимает неправильные значения в процессе обработки и есть вероятность считать неправильное значение.

                        здесь дребезг

                  --------+ ++ ++
 pinA (interrupt)         | || ||
                          +-++-++----------------
                      +--------------------+     
 pinB                 |                    |     
                  ----+                    +-----

Zubok ★★★★★
()
Последнее исправление: Zubok (всего исправлений: 4)
Ответ на: комментарий от Zubok

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

ilovewindows ★★★★★
()
Ответ на: комментарий от Zubok

Спасибо за развёрнутый ответ. Я этот момент упустил.

Правда, я и так пробовал, у меня не заработало (счётчик быстро увеличивался). Что сейчас наводит меня на мысли что тут ещё какая-то проблема. Например, по каким-то причинам пины D1-D7 ещё как-то используются (скажем, либо от mbed туда вешает debug serial). Надо на другой порт попробовать подключиться, просто не хватило вчера времени, из инструментов дома только мультиметр за 5 баксов :(.

Я уже думаю сделать из второй отладочной борды осцилоскоп, благо экран на ней есть...

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

true_admin ★★★★★
() автор топика
Ответ на: комментарий от ilovewindows

А эта штука точно срабатывает на каждый перепад

Да, это настраивается.

true_admin ★★★★★
() автор топика
Ответ на: комментарий от ilovewindows

А эта штука точно срабатывает на каждый перепад, по прерываниям работает?

А хрен его знает. Он делает на базе mbed и использует его функции при программировании входов, а как этот mbed работает... Может, это там проблемы. В любом случае нужны таймауты или время невосприимчивости, чтобы программно исключать дребезг. Думаю, что 10 мс хватит. Или простейшую схему устранения дребезга делать.

Zubok ★★★★★
()
Ответ на: комментарий от true_admin

Например, по каким-то причинам пины D1-D7 ещё как-то используются (скажем, либо от mbed туда вешает debug serial).

Проще всего просто с нуля написать без mbed. Ручками прямо. Это же простой код. Конфигурирование периферии, прерывание и простой обработчик. Так исключишь mbed.

Zubok ★★★★★
()
Ответ на: комментарий от true_admin

а m_position - это что такое?

вообще если у тебя ПО не сильно медленно обрабатывается, отключись от прерываний и обрабатывай состояния в основном цикле, с периодичностью например 100 опросов вывода в секунду, + добавь программное подавление дребезга контактов, например как здесь (второй вариант) http://home.roboticlab.eu/ru/examples/digi/switch_debounce

aiqu6Ait ★★★★
()

Работало тупо: смотрел на состояние второго пина при falling edge на первом (энкодер коротит pullUp пины на землю). Ну, всё как у всех.

Сделай нормальный конечный автомат, чтобы счётчик менялся сначала по спаду на первом канале, потом по спаду на втором при лежащем первом, потом по нарастанию на первом при лежащем втором и так далее. Ну и в обратную сторону аналогично.

prischeyadro ★★★☆☆
()
Ответ на: комментарий от prischeyadro

Не уверен, как оно правильно выглядит на крестах, но смысл такой:

CRotaryEncoder::CRotaryEncoder(PinName pinA, PinName pinB)
{
    m_irqA = new InterruptIn(pinA);
    m_pinA = new DigitalIn(pinA);
    m_irqB = new InterruptIn(pinB);
    m_pinB = new DigitalIn(pinB);

    m_irqA->rise(this, &CRotaryEncoder::arise);
    m_irqA->fall(this, &CRotaryEncoder::afall);
    m_irqB->rise(this, &CRotaryEncoder::brise);
    m_irqB->fall(this, &CRotaryEncoder::bfall);
 
    m_pinA->mode(PullUp);
    m_pinB->mode(PullUp);

    m_position = 0;
    state = 0;
}

void CRotaryEncoder::afall(void)
{
        if(*m_pinB == 1 && state == 1)
        {
            m_position++;
            state = 1;
        }
        else if (*m_pinB == 0 && state == 0)
        {
            m_position--;
            state = 3;
        }
}

void CRotaryEncoder::arise(void)
{
        if(*m_pinB == 0 && state == 3)
        {
            m_position++;
            state = 0;
        }
        else if (*m_pinB == 1 && state == 2)
        {
            m_position--;
            state = 1;
        }
}
void CRotaryEncoder::bfall(void)
{
        if(*m_pinA == 0 && state == 2)
        {
            m_position++;
            state = 3;
        }
        else if (*m_pinA == 1 && state == 1)
        {
            m_position--;
            state = 0;
        }
}

void CRotaryEncoder::brise(void)
{
        if(*m_pinA == 1 && state == 0)
        {
            m_position++;
            state = 1;
        }
        else if (*m_pinA == 0 && state == 3)
        {
            m_position--;
            state = 2;
        }
}
prischeyadro ★★★☆☆
()
Ответ на: комментарий от true_admin

Проверить поджаренность элементарно. Подкинь кодер на другой порт (D,E,B...). Выше тебе отписали за триггер Шмитта, это хорошое решение.

ramon13666 ★★★
()
Ответ на: комментарий от prischeyadro

Опаньки, косяк. В функции CRotaryEncoder::afall(void) должно быть

        if(*m_pinB == 1 && state == 1)
        {
            m_position++;
            state = 2; // вот здесь
        }

prischeyadro ★★★☆☆
()
Ответ на: комментарий от Opxocc

Ну я надеюсь что авторе делает небольшую макетку по быстрому для тестов и прочего и что когда он будет делать нормальную схему - изолирует или защитит от статики все органы управления.

/me представил себе как чел. в шерстяном свитере подходит к БП подносит руку к ручке энкодера, красивый 3-х мм разряд проскакивает в МК выжигая входные порты и включая БП на максимум. Горит и плавится планета... :-)

pitman
()
Ответ на: комментарий от true_admin

Осциллограф или логический анализатор чтобы посмотреть. Без этого совсем никак. Фантазировать можно долго, надо смотреть что реально на железе происходит. Можно поиграть величиной кондера, увеличение его сделает управление более тупым, но уменьшит дребезг. Так же енкодер подключить к МК через резистор 22-100 Ом.

pitman
()
Ответ на: комментарий от prischeyadro

В принципе, хорошая идея, да — после первого срабатывания по фронту (любому) канала A начинается зона нечувствительности, из которой выходим по фронту уже второго канала B (т. е. где-то посередине полупериода A). То есть дребезг просто будет проигнорирован. Тогда можно обойтись без таймера нечувствительности. По сути получаем тоже самое, что и таймер, но уже нет зависимости от того, как быстро крутишь.

Я правильно изобразил?

                

m_position                 +1  +1  +1  +1
                            
 state                 -1- -2- -3- -0- -1- -2-
                   -------+       +-------+
 pinA                     |       |       |
                          +-------+       +---------
                      +-------+       +-------+
 pinB                 |       |       |       |
                  ----+       +-------+       +-----

2 - зона нечувствительности после заднего фронта A; ождание заднего фронта B
3 - зона ожидания переднего фронта A; зона нечувствительности B
0 - зона нечувствительности после переднего фронта A; ождание переднего фронта B
1 - зона ожидания заднего фронта A; зона нечувствительности B

Если правильно, то вот хочу понять, что будет, если начнется дребезг, например, по заднему фронту A. Ты вошел в состояние 2, у тебя B = 1. Из-за дребезга начинает щелкать по A, пойдут прерывания afall, arise попеременно, а это приведет к тому, что несколько раз декремент m_position у тебя будет из-за этого и переход в другой state.

        else if (*m_pinB == 1 && state == 2)
        {
            m_position--;
            state = 1;
        }

Или я не осознал что-то? (поздно уже, пока это все печатал, уже лень переосознавать).

Zubok ★★★★★
()
Последнее исправление: Zubok (всего исправлений: 1)
Ответ на: комментарий от Zubok

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

P.S Как же хорошо работать с оптическими энекодерами. :)

Zubok ★★★★★
()
Ответ на: комментарий от Zubok

Да, примерно так. Идея эта рабочая, на практике проверенная. Если начнется дребезг, то он будет происходить между двумя соседними состояниями, то есть накопленная ошибка равна нулю. Если этот дребезг m_position в пределах одного LSB мешает (но для лабораторного источника питания это вряд ли, в отличие от накопленной ошибки), то можно добавить цифровой гистерезис в 1 LSB (благо, тут он точно известен), причем даже на уровне архитектуры FSM. Основное преимущество - мгновенная реакция и меньший расход ресурсов по сравнению с противодребезговым фильтром.

prischeyadro ★★★☆☆
()
Ответ на: комментарий от Zubok

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

prischeyadro ★★★☆☆
()
Ответ на: комментарий от prischeyadro

Если этот дребезг m_position в пределах одного LSB мешает

Вот это как раз может быть существенно, потому что вот тот энкодер, который выше по ссылке, у него 12 позиций всего. Поэтому гистерезис в 1LSB - это много. Все равно таймер надо будет вводить, чтобы дребезг от недребезга отличать. Да и считывания счетчика в программе происходят в основном цикле. Есть вероятность считать промежуточное значение. То есть по сути в методе с четырьмя состояниями нет подавления дребезга. Он уже расчитан на то, что дребезг как-то подавляется.

Я подумал, ведь можно же после срабатывания по переднему фронту A, вообще не трогать счетчик (то есть никаких ++ и --) до тех пор, пока по фронту B не придет сигнал. Тогда дребезг как бы подавляется в диапазоне половины полупериода, что вполне достаточно, а после прихода фронта B разрешить счетчик. Но я подробно не рассматривал этот вариант. Сразу вижу проблему, когда в зоне нечувствительности резко развернулись и пошли назад. В общем, это уже детали, которые вряд ли имеют к проблеме ТС. У него, похоже, какая-то иная проблема.

Zubok ★★★★★
()
Ответ на: комментарий от Zubok

Неважно, сколько позиций у энкодера, он же не за один оборот весь диапазон регулируемого напряжения проходит (а иначе точность регулирования +/- 1/12 от диапазона) . Важно, какое изменение напряжения приходится на 1LSB.

Проблема автора - даже не дребезг, а накопленная ошибка. («повернул ручку на одно деление, а в программе счётчик увеличивается сразу на 2-30»), её-то конечный автомат и устраняет. И никакого дополнительного подавления дребезга ему не нужно.

prischeyadro ★★★☆☆
()
Последнее исправление: prischeyadro (всего исправлений: 1)
Ответ на: комментарий от prischeyadro

Важно, какое изменение напряжения приходится на 1LSB.

Дык ведь задача вообще ошибок не получать. Зачем нужны компромиссы? Если нет дребезга, то и ошибки никакой не будет. Зачем с ней соглашаться?

Проблема автора - даже не дребезг, а накопленная ошибка. («повернул ручку на одно деление, а в программе счётчик увеличивается сразу на 2-30»),

У него явно ошибка в чем-то другом:

Значения скачут очень рандомно (т.е. повернул ручку на одно деление, а в программе счётчик увеличивается сразу на 2-30). Причём, в лучшем случае удаётся читать показания при вращении в одну сторону, но не другую.

Ну никак на проблему дребезга не тянет. И даже его алгоритм должен выдавать вменяемые значения. Здесь у него, скорее всего, либо энкодер сдох, либо что-то не так сконфигурировано. Но так как он использует mbed как платформу, то не ясно. что именно.

Zubok ★★★★★
()
Последнее исправление: Zubok (всего исправлений: 1)
Ответ на: комментарий от Zubok

Если нет дребезга, то и ошибки никакой не будет. Зачем с ней соглашаться?

Как это не будет? Ведь фильтр вносит задержку на обработку, поэтому его значение отстаёт от реального. Это ошибка на 1LSB. А если считывание значения в цикле произойдёт чуть раньше реального изменения, то ещё целый период оно будет ошибочным на 1LSB. Вот у тебя такого же уровня претензии. Причём всё это только во время вращения, в статике никакого отклонения на 1LSB нет.

Ну никак на проблему дребезга не тянет.

Дребезг там явно есть, вот эта рандомная накопленная ошибка на него 100% указывает. Алгоритм в плане дребезга надо переделать на нормальный и потом смотреть дальше.

prischeyadro ★★★☆☆
()
Ответ на: комментарий от prischeyadro

Дребезг там явно есть, вот эта рандомная накопленная ошибка на него 100% указывает. Алгоритм в плане дребезга надо переделать на нормальный и потом смотреть дальше.

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

А вот то, что у него в обратную сторону не работает, очень странно. По алгоритму все должно работать в любую сторону.

UPD. Лучше давить дребезг до завода в МК, а алгоритм уже можно тогда любой оставлять, считая, что дребезга нет.

Zubok ★★★★★
()
Последнее исправление: Zubok (всего исправлений: 1)
Ответ на: комментарий от Zubok

Если раскомментировать, то накопления счетчика на паразитке не будет
>А если ты оба фронта обрабатываешь, то у тебя будет m_position +1-1+1-1+1 - итого +1

Да, это будет то же самое, с таким же дребезгом (который, я настаиваю, в данном случае страшен не больше, чем задержка противодребезгового фильтра или расхождение на период чтения в цикле). Единственное - я не знаю, возможно ли, что при быстром дребезге не все прерывания обработаются как надо и сложатся в +1.

prischeyadro ★★★☆☆
()
Ответ на: комментарий от Zubok

Лучше давить дребезг до завода в МК

Ни в коем случае. Только цифровая обработка сигналов, только светлое будущее.

prischeyadro ★★★☆☆
()
Ответ на: комментарий от Zubok

Завтра попробую сделать. Вчера уже не в состоянии код был править.

PS кондёры просто так не помогли. Наверно, нужны ещё резисторы чтобы вышел полноценный low-pass фильтр.

PPS а вот смена ног отчасти помогла. Видимо, те ноги по-дефолту ещё для чего-то используются.

true_admin ★★★★★
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.