Энкодер. Вопросы 01 #3
Loading…
Reference in New Issue
Block a user
No description provided.
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Вынести из прерывания.
Значение next_state должны быть одним из 0x01, 0x02, 0x03, 0x04. Получить это значение на основании состояния контактов.
Добрый вечер Степан Андреевич, загрузил последнюю версию кода проекта согласно вашим замечаниям
Я совершенно не понял содержимое новой версии энкодера. Вопрос: ENC_CLK и ENC_DAT это что за контакты и какого интерфейса?
У нас используется плата ArduinoATmega 328p, контакт ENC_CLK отвечает за контакт CLK энкодера, контакт ENC_DAT отвечает за контакт DAT, они оба подключены к ардуине, программа считывает комбинацию сигналов 00, 01, 10, 11 и в зависимости от их значения либо увеличивает либо уменьшает и тп
Согласно какому даташиту или иной документации есть контакты
CLK
иDAT
?Эти контакты не по даташиту, это контакты ктр мы подключаем энкодер к плате ардуино
В старой версии так же отсутствовали эти контакты, а значение state так же увеличивалось либо уменьшалось в зависимости от значения ктр находилось на пинах порта
Старый код:
#include <avr/interrupt.h>
#include <avr/io.h>
/* Дефайны /
#define F_CPU 1000000UL
#define PIND_MASK 0b00001100
/ Переменные /
volatile uint8_t next_state, prev_state, up_state, down_state, state;
int sw34; // Передача
/ Прерывания */
ISR(TIMER1_COMPA_vect) {
next_state = PIND & PIND_MASK;
if (next_state != prev_state) {
switch (prev_state) {
case 8: {
if (next_state == 3) state++;
if (next_state == 0) state--;
break;
}
case 0: {
if (next_state == 2) state++;
if (next_state == 1) state--;
break;
}
case 4: {
if (next_state == 0) state++;
if (next_state == 3) state--;
break;
}
case 12: {
if (next_state == 1) state++;
if (next_state == 2) state--;
break;
}
default: {
break;
}
}
prev_state = next_state; // Присваиваем текущему состоянию предыдущее
}
TCNT1H = 0x00;
TCNT1L = 0x00; // Обнуляем счетчик(на всякий случай)
}
void timer1_init() {
TCCR1A = 0x00;
TCCR1B |= (1«CS11); // устанавливаем коэффициент деления 8
TCNT1H = 0x00; // Обнуляем старший и младший байты счетного регистра
TCNT1L = 0x00;
OCR1AH = 0x03; // Настройка регистра сравнения для старшего и младщего байта
OCR1AL = 0xE8;
// Разрешаем прерывание таймера по совпадению с OCR1A
TIMSK1 |= (1«OCIE1A);
}
int main(void) {
Serial.begin(9600);
int s;
DDRD = 0x00;
PORTD = 0x0C;
timer1_init(); // Инициализация таймера
sei(); // Разрешаем глобальные прерывания
while (1) {
if (state != s)
{
Serial.println(s);
s = state;
}
}
}
Контакты ктр подключены к ардуино мы заранее определяем, в самом начале программы а затем снимаем с них значения, если мы нажмем на кнопку энкодера клик, то она на пине ENC_CLK выдаст единицу в момент нажатия, аналогично и с DAT_CLK, и в зависимости от комбинации этих значений, наша переменная state будет либо увеличиваться либо уменьшаться, в живой сборке работает корректно и выводит правильные значения
Кнопка энкодера? Про какой энкодер речь идёт?
Ой не кнопка, используется инкрементальный энкодер KY-040, просто в симуляторе на wokwi для прокрута нужно нажать на стрелочку по часовой либо против часовой, на живой сборке для прокрута нужно покрутить, на живой сборке так же выдает корректные значения в зависимости от того куда крутишь
Если нужно могу приложить скриншоты работы из wokwi и живой сборки
На кнопках тоже работает корректно
Это если энкодер заменить на две кнопки
Как долго будет процессор будет выполнять функцию
int encoder()
изENC_CLK
будет притянут к земле всё время?Мы в Мэйне вызываем функцию энкодера в loop, то есть постоянно, если значение энкодера не менялось то он просто будет выводить старое значение бесконечно пока мы не завершим работу программы, был вариант реализации через считывание старого значения и его сравнения с новым поступившим и если они не одинаковые то выводить новое значения, но данный вариант при работе общей программы к сожалению почему-то не работает, но если вместо возвращаемого значения выводить через Serial.println, то работает как нужно и выводит только новые значения
А если говорить про конкретное время работы программы одного цикла, то это нужно засекать, либо в атмеле либо в симуляторе, но насколько я знаю происходит вывод очень быстро
Это вопрос на логику. Нужно посмотреть на свой собственный код и дать ответ на вопрос.
понятно, прошу прощения что сам вопрос не так понял
Он будет нам очень быстро возвращать значение state, но так как у нас ENC_CLK подключен к земле все время, то внутрь функции он не попадет, соответственно значение state будет выводиться с большой скоростью, так как задержка тоже не будет учитываться , то он просто будет нам постоянно выводит 0 с очень большой скоростью
Прошу написать результаты очной встречи в рамках этого разговора
Результатами очной встречи стали доработки по энкодера, нужно будет переписать старый код, чтобы next_state всегда было равно 0, 1, 2, 3 и загрузить измененную версию кода в GIT, данный момент был сделан и код в Git загружен, буду ждать ваших комментариев по его реализации, так же нужно будет переделать задержку у датчика температуры и влажности, чтобы код не виснул при его работе, сделать это через таймер и всё
Ну а если будет так что один сигнал энкодера будет поступать на конткт D2, а другой D6?
На этот случай тоже есть возможность реализации, можно сделать такое же смещение в зависимости от контактов, просто сами подключенные контакты а именно PIND_MASK разбить на два Пина, ктр будут отвечать за контакты энкодера, и так же их сдвигать в зависимости от полученное значения пинов, просто данный вариант показывает саму идею реализации, если она подойдет, то я ее доработаю до автоматизма
Именно так и сделать
Понял, в таком случае я автоматизирую полностью, то что я написал и после загружу обновленную версию кода в Git и сброшу вам ссылку
Вместо портянки с 14-й по 150-ю строку можно просто написать вверху
Пробовали? Работает?
Да в эмуляторе пробовал все работает, нужно только указать значения пинов в самом начале и все, все комбинации в ручную вводил и проверял, в живую буду сегодня проверять
Попробую сегодня и этот вариант, просто вариант реализации ктр я сейчас написал кажется более гибким, так как я конкретно под каждую комбинацию пинов могу задать нужное смещение для каждого пина
Прошу подумать хорошо над строкой кода. Опишите в комментариях каждую строчку кода так, чтобы понял человек, который не работал с энкодерами. Читаем состояние энкодера не является хорошим комментарием.
Ну ведь в дефайнах и задаётся однозначно нужное смещение для каждого пина. Больше ничего делать не надо. Ваши вычисления на 130 строк компилятор оптимизирует до этих же 4х констант.
Я понял, просто я хотел максимально автоматизировать работу чтобы в define менять как можно меньше переменных, поэтому реализовал это данным способом
Я могу изменить и убрать эти вычисления о ктр вы говорите и сделать все в дефайн, но тогда нужно будет каждый раз указывать нужное смещение и пины для работы программы, сейчас указываются только подключенные пины и на основании этих пинов программа автоматом выдает нужное смещение для них
Понял, сделаю более подробные комментарии для пользователя
Не для пользователя. Для себя и меня. Вам нужно самому понять, что тут написано.
Понял
Нужно ли ещё что-нибудь доработать по коду?
Вы проверяли? Работает? Подумайте!
что значит
volatile
?PIND_MASK, PIN_CLK_2, PIN_DT_2
В верхнем регистре как правило только дефайны прописываются. Переменные в нижнем регистре. Банальный Си-шный кодстайл.
Volatile это квалификатор переменной, ктр просит чтобы компилятор брал значение сразу из оперативной памяти, а не из регистра хранения, насколько я знаю это позволяет убрать неточность, которая может возникнуть в регистрах хранения переменной, насчет регистров и их объявления, поменяю, нужны ли будут какие-либо еще изменения по коду и доработки, или можно будет переписать пз по одобренному вами коду и сбросить вам?
Сам код проверял, все работает, в случае изменения подключения, мы меняем значения сдвига и пины подключения и всё работает как нужно, проверил на нескольких комбинациях и в живую и в симуляторе работает корректно
Скриншот эмулятора сюда
Разумеется, сейчас сброшу
Скриншоты с тремя разными комбинациями
Выдаёт значения корректно
Переделать схему на два переключателя. А то не ясно, что у вас там происходит.
имеете в виду поставить кнопки вместо энкодера?
Переключатели.
Ну даже на скрине смотрю на код и вижу что что-то не так.
Понял! Но это так не очевидно! Вы сразу сдвигаете на позицию в бит 1. Не надо так.
тут необходимо ещё раз посмотреть и подумать.
Аааааа, понял
Я тоже подумаю и посмотрю как это можно по другому реализовать
Просто у меня появилась идея как реализовать то о чем вы просили, это идея как раз таки и заключалась в смещении на нужное кол-во битов, чтобы мы всегда получали либо 0 либо 1 либо 2 либо 3, раз вы говорите что так нельзя делать, то я подумаю как еще можно этот момент реализовать, но пока что если честно конкретных идей в голову не приходит
И по итогу мы это и получаем с любой комбинации подключений у нас всегда будет либо 00, либо 10, либо 01, либо 11
Мы на прошедшей встрече договорились о том, что будет сделана таблица для переменных вот этого куска кода с 26й по 58ю строку кода
Столбцы
Подсказка: если нет желания устанавливать компилятор для хоста, то можно воспользоваться https://www.onlinegdb.com/online_c_compiler
Так я так и сделал и прогнал все значения и заметил что были перепутаны комбинации для поворота по часовой и против часовой стрелки и в результате выводились другие значения, и я исправил этот момент, сейчас сделаю таблицу со значения еще раз и после отпишу вам
Понял, обязательно воспользуюсь, спасибо большое)
Степан Андреевич, я так и сделал, прогнал все значения через прошлый код и записал их как в таблице в итоге вышло следующее: Начальное положение: encoder_state = 3 prev_encoder_state = 0 state(ож) = 1 state(пол) = 0
При повороте по часовой стрелке: encoder_state = 1 prev_encoder_state = 3 state(ож) = 2 state(пол) = 1
При повороте против часовой стрелке: encoder_state = 2 prev_encoder_state = 1 state(ож) = 1 state(пол) = 1
При повороте против часовой стрелке(2 раза): encoder_state = 2 prev_encoder_state = 1 state(ож) = 0 state(пол) = 1
При повороте по часовой стрелке(2 раза): encoder_state = 2 prev_encoder_state = 1 state(ож) = 2 state(пол) = 0, значения выводил через debug программы, после этого произвёл доработки чтобы исправить на нужные мне значения, по итогу получилось исправить их, а конкретнее была исправленна ошибка со значениями при начальным положением и с переключением при увеличении и при уменьшении, я проганяю дальше значения при нынешней версии кода и он выводит нужный мне state, тот ктр я ожидаю увидеть, я просто не понимаю что нужно исправить по коду, не могли бы вы подсказать, я и с осцилограммой сигнала смотрел, и значения 2 прогонял, если ошибка в том что я сдвигаю на 1 пин pin_clk2 и не сдвигаю pin_dt2, то это всё можно подкоректировать в зависимости от того какие пины подключены
#3 (comment)
Я более проверять не буду, пока не будет выполнена договорённость. Я не буду проверять работу чаще, чем раз в два дня. Игнорирование требований не приводит ни к чему хорошему.
И читайте теорию энкодеров. На экзамене буду досконально спрашивать помимо тестов.
Степан Андреевич, в данной версии кода отсутствует какая-либо ошибка влияющая на правильный вывод данных, сам код работает корректно и в живой сборке и в эмуляторе, а таблицу я вам сбрасывал значения из нее, вы сказали сделать таблицу я ее сделал, сбросил вам значения, но они остались без ответа
Хорошо
Вот скриншоты работы нынешней версии кода:
Прошу ссылку на таблицу
А лучше разместить её здесь
Переделал код согласно новой составленной таблице, в файле есть как новые значения из таблицы, так и те ктр были до изменения
Читайте и выполняйте. Берите карандаш и записывайте каждый проход по коду, буквально интерпретируйте свой код. Искренне не понимаю, в чём сложность выполнить такое простое задание.
Чего добились, сделав таблицу
Таблица истинности энкодера.xlsx
?Степан Андреевич добрый вечер, я сделал таблицу согласно вашим замечаниям, по старому коду и прогнал все значения через debug как показано в таблице, а именно: encoder_state, prev_encoder_state и state, не написал только state(ожидаемый), но он должен быть таким как в таблице сверху, я сделал ровным счетом то что вы сказали, а именно через debug прогнал все значения и записал их в таблицу и увидел что в старом коде значение state меняется один раз в то время когда при повороте должна меняться 4 раза и я переделал код согласно этому замечанию, добавив переменную total_state ктр увеличивается на 1 или уменьшается в зависимости от значения state, само значение state в конце цикла обнуляется, единственное что сделано некорректно так это начальное состояние энкодера, ктр должно быть 0 0, так как я ставлю сразу 1 в define, но этот момент я исправил присвоив начальное значение prev_encoder_state равное 3, и у меня отличается от таблицы только начальное состояние а именно оно будет не 0 0, а 3 3, но это не влияет никоим образом на работу энкодера, так как при повороте он так же проходит все 4 значения и либо делает state равным 4 либо делает его равным -4 и после в зависимости от этого значения к total_state либо прибавляется 1 либо вычетается
нет, вы не сделали.
Я не говорю, что код ТЕПЕРЬ не рабочий. Но я говорю, что надо бы разочек выполнить простые действия. И не писать много текста, а сделать таблицу на 5 столбцов.
И куда делся
Encoder.c
? отсюда вопрос: чем отличаются C(CPP) файлы от H(HPP) файлов?Просто спешили и сделали сразу заголовочные файлы чтобы показать вам в прошлую встречу что оба кода корректно работают в main при живой сборке, если говорить в чем отличие, то отличие между ними в том что в CPP файле хранится исходный код программы в то время как в H файле находиться ссылки на функции в файле CPP, если говорить примером, то в моем понимании CPP файл что-то вроде книги в которой все написано, а H файл является оглавлением для этой книги
Хорошо Степан Андреевич, я еще раз прогоню все значения кода через debug и составлю таблицу со значениями и приложу ее сюда
А саму структуру общего проекта мы переделаем, как только получим ваше одобрение, вернем CPP файлы и сделаем правильные заголовочный файлы для мейна
Зачем вот это я не понимаю, это не нужно, у вас есть переменная state, которая меняется каждый раз при изменении значений контактов. В остальном верно. Читайте теорию энкодеров, учите. В большинстве современных контроллерах блок, обрабатывающий сигналы с инкрементального энекодера реализованы аппаратно в таймере или в отдельном блоке, и на каждое изменение сигналов меняется значение счётчика.
Понял Степан Андреевич, подробнее прочитаю об этом, спасибо большое за помощь, а по нынешней версии кода, я реализовал полный цикл ктр проходит сигнал при повороте энкодера по часовой стрелке либо против часовой, и в зависимости от полученной комбинации мы получаем state равное 4 или -4 и в последствии от этого значения мы увеличиваем или уменьшаем на 1 наш результат
Всё ещё жду
Вот составленная таблица, по нынешнему коду, не могли бы вы сказать то ли я составил или нет, заранее спасибо за вашу помощь
Вот таблицы по старому коду и новому коду:
Степан Андреевич, сбросил табличку для всех значений энкодера по старому коду
Степан Андреевич, сбросил табличку для всех значений по нынешнему коду
всю дорогу прошу вот такую заполненную табличку
Степан Андреевич, добрый вечер, переделал табличку как вы показали, прошу прощения что не смог понять что вы хотели, в самой таблице пустые столбцы означают что мы туда не попадаем по коду, соответственно значений с этих комбинаций нету, сама таблица сделана по старому коду
Степан Андреевич, вот табличка по нынешнему коду