Кому будет интересно - пользуйтесь. На АВРке подобного соорудить будет весьма напряжно даже под ассемблером - а как "прикладная микруха" ПИК вполне может пригодиться...
Только согласно специфике команд один и тот же алгоритм для разных МК прийдется реализовать на разных типах команд. Как пример - реализация варианта на attiny13a: viewtopic.php?p=2816604#p2816604
подниму старую тему. на днях занялся освоением энкодера. при "изучении" интернета понял, что практически существует только два алгоритма обработки: 1. через внешнее прерывание. канал А заводится на прерывание, например, INT0, а при обработке прерывания опрашивается канал В. 2. опрос по таймеру обоих сигналов энкодера. логика обработки через внешнее прерывание на первый взгляд выглядит "железобетонно", и такое впечатление, что сбоев давать не должна. хотя в различных статьях надежность работы через внешнее прерывание оценивается гораздо ниже, чем второй способ (алгоритм). соответственно кажущейся "железобетонности", я начал осваивать по внешнему прерыванию (INT0). в обработке прерывания я сразу же запрещал это прерывание и запускал таймер, которой через некоторое время опять разрешал это внешнее прерывание. прерывание по таймеру отключало этот таймер. в общем, по внешнему прерыванию у меня вообще ничего не получилось. при вращении в одну сторону параметр и увеличивался и уменьшался, то есть, "крутился" вокруг исходного значения. то же самое происходило при вращении энкодера в другую сторону. тогда я реализовал второй алгоритм. и, о чудо!, энкодер прекрасно заработал с первого раза, даже ничего не пришлось подправлять в тексте программы. для МК я пишу только на ассемблере. тексты на С я даже читать отказываюсь. даю фрагменты своего текста, определяющие работу с энкодером. Спойлер
;--- прерывание по Таймеру0 --- timer0: push R16 ; сохраним регистр, который использует прерывание in R16, SREG ; сохраним SREG push R16 ; сохраним SREG ; канал A энкодера - порт D0 ; канал B энкодера - порт D1 in new_state_enc, PinD andi new_state_enc, 3
cp old_state_enc, new_state_enc ; если состояние не поменялось, то выходим breq nothing
cpi new_state_enc, 0 ; если состояние поменялось, выбираем подходящий вариант breq case0 cpi new_state_enc, 1 breq case1 cpi new_state_enc, 2 breq case2 cpi new_state_enc, 3 breq case3 rjmp nothing ; блокировка на всякий случай, хотя до этой команды никогда не должно дойти
Iref_plus: mov old_state_enc, new_state_enc ; копируем в темп1 новое состояние выводов inc R14 brne no_0_R14 inc R15 no_0_R14: rjmp nothing
Iref_minus: mov old_state_enc, new_state_enc ; копируем в темп1 новое состояние выводов ldi R16, 1 sub R14, R16 brcc no_R14 sbc R15, R31 no_R14:
nothing: pop R16 ; восстановим SREG out SREG, R16 ; восстановим SREG pop R16 ; восстановим регистр, который использует прерывание reti
...
;--- это делается в основном цикле программы с интервалом 0,1 секунды --- ;--- обработка результата вращения энкодера --- ldi R16, par_Iref ; подставим номер параметра "задание тока" rcall get_parametr ; получим в R21:R20 параметр add R20, R14 adc R21, R15 cp R20, R31 cpc R21, R31 brpl no_Iref_min mov R20, R31 mov R21, R31 no_Iref_min: ldi R16, low(500) ldi R17, high(500) cp R16, R20 cpc R17, R21 brcc no_Iref_max mov R20, R16 mov R21, R17 no_Iref_max: ldi R30, spisok_par + (par_Iref<<1) st Z+, R20 st Z, R21 clr R14 ; очищаем регистры счетчика энкодера (приращение к параметру) clr R15
...
как можно видеть, признаком направления вращения служит само число, точнее, знак числа в счетчике энкодера. при вращении вправо получается положительное число, а при вращении влево - отрицательное. поэтому в основном цикле пропадает необходимость в анализе направления, и достаточно к параметру просто прибавить получившееся число энкодера. после суммирования я делаю проверку на минимум, на ноль (в регистре R31 находится ноль), и проверку на максимум (число 500). после чего я сохраняю новое значение параметра (или старое, если энкодер не вращали) и обнуляю счетчик энкодера.
_________________ Мудрость приходит вместе с импотенцией... Когда на русском форуме переходят на Вы, в реальной жизни начинают бить морду.
Обязательным условием долгой и стабильной работы Li-FePO4-аккумуляторов, в том числе и производства EVE Energy, является применение специализированных BMS-микросхем. Литий-железофосфатные АКБ отличаются такими характеристиками, как высокая многократность циклов заряда-разряда, безопасность, возможность быстрой зарядки, устойчивость к буферному режиму работы и приемлемая стоимость. Но для этих АКБ очень важен контроль процесса заряда и разряда для избегания воздействия внешнего зарядного напряжения после достижения 100% заряда. Инженеры КОМПЭЛ подготовили список таких решений от разных производителей.
Компания EVE выпустила новый аккумулятор серии PLM, сочетающий в себе высокую безопасность, длительный срок службы, широкий температурный диапазон и высокую токоотдачу даже при отрицательной температуре.
Эти аккумуляторы поддерживают заряд при температуре от -40/-20°С (сниженным значением тока), безопасны (не воспламеняются и не взрываются) при механическом повреждении (протыкание и сдавливание), устойчивы к вибрации. Они могут применяться как для автотранспорта (трекеры, маячки, сигнализация), так и для промышленных устройств мониторинга, IoT-устройств.
Отказ от си - банальный страх перед кажущейся сложностью. Я тоже когда-то был ярым приверженцем асма. Как съесть слона, ведь он большой. Кусочками. Ищете книжку по си, которая наиболее подходит вам по восприятию. Читаете понемножку. Перечитываете, что непонятно. И так каждый день. В определенный момент критическая масса достигнет предела и приходит понимание. Не всего, не сразу. Но приходит. Теперь начинаете пробовать. С практикой понимание еще больше наступает. Как-то так.
Заголовок сообщения: Re: Программная обработка Энкодера на AVR
Добавлено: Вт ноя 15, 2016 09:33:59
Собутыльник Кота
Карма: 29
Рейтинг сообщений: 645
Зарегистрирован: Сб май 14, 2011 21:16:04 Сообщений: 2694 Откуда: г. Чайковский
Рейтинг сообщения:0 Медали: 1
Starichok51 писал(а):
в обработке прерывания я сразу же запрещал это прерывание и запускал таймер, которой через некоторое время опять разрешал это внешнее прерывание. прерывание по таймеру отключало этот таймер. в общем, по внешнему прерыванию у меня вообще ничего не получилось.
Возможно Вы перед разрешением прерывания не сбрасывали флаг прерывания. Если Вы определяли направление вращения по фронту, то прерывание "дребезжания" спада следует игнорировать, чего Вы скорее всего не делали.
_________________ Добро всегда побеждает зло. Поэтому кто победил - тот и добрый.
очень давно, когда я игрался с энкодером (надо признать, что и с Си я тогда в основном игрался), я сделал такую реализацию... без прерываний и таймеров, т.к. и по сей день уверен, что для пользовательского интерфейса все эти "быстродействия" совершенно не нужны. то есть 300 мс - это не проблема для пользовательского интерфейса.
предлагаю всем поглядеть на моё творчество и, как обычно, раскритиковать его в пух и прах
encoder.h
Код:
#ifndef _ENCODER_ #define _ENCODER_ 1 /********************************************************************* Модуль программной поддержки контактного квадратурного энкодера типа РЕС16 и аналогичных (без использования прерываний)
Внимание! Вы можете использовать данный модуль по своему усмотрению в любых проектах. При любой публикации ваших проектов, использующих этот модуль, следует указать ссылку на сайт http://arv.radioliga.com
Требуется компилятор WinAVR **********************************************************************/
#define ENC_A_PIN 3 /* номер линии порта для сигнала А энкодера */ #define ENC_B_PIN 6 /* номер линии порта для сигнала B энкодера */ #define ENC_A_PORT PIND /* порт, к которому подключен сигнал А энкодера */ #define ENC_B_PORT PINB /* порт, к которому подключен сигнал B энкодера */
#define rotate_fast(x) (x > ENC_FAST_ROTATE) #define DDR(x) (*(&x + 1)) #define PORT(x) (*(&x + 2)) /* Использование модуля элементарно: в начале программы следует вызвать функцию enc_init(), которая настроит нужные порты ввода-вывода. Затем в основном цикле в нужном месте можно обратиться либо к функции enc_rotate(), которая вернет значение, соответствующее направлению вращения энкодера, либо функцию enc_delta(), которая кроме направления возвратит и условную величину скорости вращения. enc_rotate() позволяет использовать энкодер, как 2 кнопки "+1" и "-1" enc_delta() уже может заменить 4 кнопки "+1", "+100", "-1" и "-100". Функция способна отличать "рывки туда-сюда" от простого вращения в одном направлении.
Нажатие встроенной кнопки энкодера НЕ ОБРАБАТЫВАЕТСЯ */
// инициализирует порты, к которым подключен энкодер void enc_init(void);
/* возвращает направление и скорость вращения энкодера параметр speed - указатель на переменную, в которую возвращается условная скорость вращения (число от 0 до 100)
возвращает: -1 - вращение влево (против часовой стрелки) 0 - нет вращения 1 - вращение вправо (по часовой стрелке)
ВНИМАНИЕ! функция выполняется достаточно длительное время: максимально (при остановленном энкодере) ENC_MAX_SPEED миллисекунд, при вращении возвращает управление раньше */ signed char enc_rotate(unsigned char * speed);
/* возвращает приращение энкодера: 1 или -1 при медленном вращении, 0 - при остановленном ENC_BIG_STEP или -ENC_BIG_STEP при быстром вращении
ВНИМАНИЕ!!! Функция выполняется долго!!! при быстром вращении возвращает управление только ПОСЛЕ ОСТАНОВКИ энкодера! остановкой считается, если в течение 2*ENC_MAX_SPEED миллисекунд не было импульсов с энкодера. */ signed char enc_delta(void);
#endif
encoder.cСпойлер
Код:
/********************************************************************* Модуль программной поддержки контактного квадратурного энкодера типа РЕС16 и аналогичных (без использования прерываний)
Внимание! Вы можете использовать данный модуль по своему усмотрению в любых проектах. При любой публикации ваших проектов, использующих этот модуль, следует указать ссылку на сайт http://arv.radioliga.com
Требуется компилятор WinAVR **********************************************************************/ #include <avr/io.h> #include <util/delay.h> #include "encoder.h"
signed char enc_rotate(unsigned char *speed){ /* возвращает направление и скорость вращения энкодера параметр speed - указатель на переменную, в которую возвращается условная скорость вращения (число от 0 до 100)
возвращает: -1 - вращение влево (против часовой стрелки) 0 - нет вращения 1 - вращение вправо (по часовой стрелке)
ВНИМАНИЕ! функция выполняется достаточно длительное время: максимально (при остановленном энкодере) ENC_MAX_SPEED миллисекунд, при вращении возвращает управление раньше */ char stage = 0;
signed char enc_delta(void){ /* возвращает приращение энкодера: 1 или -1 при медленном вращении, 0 - при остановленном ENC_BIG_STEP или -ENC_BIG_STEP при быстром вращении
ВНИМАНИЕ!!! Функция выполняется долго!!! при быстром вращении возвращает управление только ПОСЛЕ ОСТАНОВКИ энкодера! остановкой считается, если в течение 2*ENC_MAX_SPEED миллисекунд не было импульсов с энкодера. */ signed char d; unsigned char w;
d = enc_rotate(&w); if(rotate_fast(w)){ if((d == enc_rotate(&w)) && rotate_fast(w)){ d *= ~ENC_BIG_STEP; while(enc_rotate(&w)) _delay_ms(1); } } return d; }
какие дефайны тебя интересуют? единственное, что я не дал, вот это .def old_state_enc = R24 ; предыдущее состояние энкодера .def new_state_enc = R25 ; новое состояние энкодера
Demiurg писал(а):
Отказ от си - банальный страх перед кажущейся сложностью.
страха перд С у меня нет. для компа я пишу на С++ (Мелкософтовская вижл студия). для любопытствующих - в разделе Питание (на верху списка тем) есть моя большая тема с большим числом моих программ для расчетов трансформаторов и дросселей. но для МК у меня ассемблер - дело принципа. ведь, я же не говорил, что я не понимаю и что я боюсь С...
Z_h_e писал(а):
Возможно Вы перед разрешением прерывания не сбрасывали флаг прерывания.
да, я про его сброс даже не подумал. видимо, прерывание запрещено, а флаг все равно устанавливается. но когда у меня уже всё прекрасно заработало, что-то не хочется возвращаться к экспериментам с внешним прерыванием. мне так понравилась четкая работа по второму алгоритму, что я вчера перед сном не удержался и включил плату, чтобы еще раз полюбоваться прекрасной работой энкодера с этим алгоритмом.
_________________ Мудрость приходит вместе с импотенцией... Когда на русском форуме переходят на Вы, в реальной жизни начинают бить морду.
Так вот вроде простенько, на ассемблере и для АВРки: download/file.php?id=257389 ни таймеров там ни прерываний. Исходник открытый - интересно "доработать" - вполне на Ваш вкус...
спасибо, но принять твои исправления не представляется возможным, так как регистры R18 и R19 уже заняты под другие цели и у меня не осталось свободных регистров, допускающих работу с непосредственным операндом. придется пока оставить ; R14, R15 - счетчик энкодера и всё остальное без изменений.
но еще не всё потеряно, похоже, именно в этом проекте я могу функции четырех регистров R20-R23 переложить на регистры с номерами ниже 16-ти. тогда у меня появится возможность сделать определения, например, .def new_state_enc = R22 .def old_state_enc = R23 ; R24, R25 - счетчик энкодера и применить команды ADIW и SBIW. хотя применение этих команд не даст существенного выигрыша в длине кода (в данном случае "экономия" составит 10 байт кода).
_________________ Мудрость приходит вместе с импотенцией... Когда на русском форуме переходят на Вы, в реальной жизни начинают бить морду.
да это понятно, что можно всё опустить ниже 16-го номера. но это приводит к лишним "телодвижениям". но этот второй твой вариант меня не устраивает. я уже переделал программу. функции регистров R20-R23 опустил на регистры R10-R13. и сделал такие определения:
Принципиальность - нужная вещь. Но не в угоду самой принципиальности. Посмотрите на последние сообщения. Речь о регистрах и экономии АЖ ЦЕЛЫХ 10-ти байтах. Абалдеть не встать. Все ваши мысли об этом. И ни слова об алгоритмах, решениях.
дело не в экономии байт, места во флеши вполне хватает. но подкупает "красивость" решения, когда одной строкой можно заменить 3 или 4 строки. а какие еще нужны слова об алгоритмах и решениях, если я привел полный текст алгоритма и своих решений? а также очень положительно высказался в пользу этого алгоритма. если ты считаешь, что я о чем-то умолчал, то лучше задай вопросы, а не устраивай подобные "наезды"...
_________________ Мудрость приходит вместе с импотенцией... Когда на русском форуме переходят на Вы, в реальной жизни начинают бить морду.
Это не наезд. Повторяю, я тоже когда-то был приверженцем асма. Но в определенный момент я честно признался себе, что асм не решает многих моих задач. Что мои проекты стали сложны, чтобы продолжать ковыряться в песочнице регистрах. Я стал изучать си. Когда стало получаться, я бросил асм и проекты до тех пор, пока боле-менее не освоил си. Перенес многие свои наработки на си. И спустя некоторое время я пожалел, что был таким упертым. Я забыл что такое регистры и всякую прочую ерунду. Если раньше мне приходилось держать в голове состояние регистров, указателей, стека и так далее, то сейчас я оперирую переменными, функциями, модулями. Вот что я пытаюсь вам сказать. Если вы цепляетесь за асм, то я могу сказать одно - ваши проекты относительно простые. Другого объяснения я не вижу. Иначе вы бы уже замучались. Лично я увидел предел в асме, когда начал делать проекты с меню. Когда в указателях указатели на указатели указателей, мой мозг не выдержал такого насилия. Вот тогда я понял, что асм себя исчерпал.
я могу сказать одно - ваши проекты относительно простые.
ты абсолютно прав. у меня нет сложных проектов. и для моих домашних нужд и не нужны сложные проекты. более того, мои потребности настолько просты, что в своих проектах я обхожусь всего тремя кнопками, так как сложной "навигации" у меня нет, и мне хватает трех кнопок, чтобы управлять всеми режимами работы и настраивать все используемые параметры. понятное дело, что при сложной иерархической структуре меню трех кнопок явно не хватит. и несмотря на свой возраст (скоро исполнится 66 лет), я еще способен держать в голове состояние всех регистров. а чтобы не мучиться, вспоминая где что "лежит", существуют имена и метки, которые гораздо легче помнить, чем просто адреса, по которым размещены используемые данные и переменные. а назначая осмысленные, целевые, имена (а метка - это тоже имя), вообще нет никаких проблем писать на ассемблере программы любой сложности. не стану на 100% утверждать, но я думаю, что смогу на ассемблере одолеть во много раз сложнее проекты, чем те, которые уже у меня сделаны. и еще раз скажу - не надо меня агитировать за Си. мне нравится ковыряться в регистрах. и мне нравится получать минимальную длину кода с помощью ассемблера. и мне НЕ нравится, чтобы Си генерировал код, в несколько раз длиннее, чем можно сделать на ассемблере. и еще. я живу на "голую" пенсию. я купил несколько штук АТМега8. и я НЕ ХОЧУ покупать АТМега16 или АТМега32, когда мне Си сгенерирует такую кучу своего мусора, который не поместится в АТМегу8...
но и тебя я могу понять. если у тебя постоянно идут сложные проекты, то делая на Си, ты потратишь гораздо меньше времени на разработку, чем делая на ассемблере. а мне спешить некуда...
_________________ Мудрость приходит вместе с импотенцией... Когда на русском форуме переходят на Вы, в реальной жизни начинают бить морду.
Чтож, ваша позиция и мотивы понятны. Все-таки напоследок скажу. Когда более-менее разобрался с си, у меня получилось писать программы так, чтобы компилятор выдавал более-менее приемлемый результат.
Последний раз редактировалось Demiurg Ср ноя 16, 2016 17:17:27, всего редактировалось 2 раз(а).
Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 34
Вы не можете начинать темы Вы не можете отвечать на сообщения Вы не можете редактировать свои сообщения Вы не можете удалять свои сообщения Вы не можете добавлять вложения