чтение бита по прерыванию
- Войдите на сайт для отправки комментариев
нужно отслеживать появление сигнала после которого прочитать биты. для этого логично воспользоваться прерыванием. для этого я использовал функцию attachInterrupt(0, impuls, LOW);
приведу полный код -
int readbit[8]={}; bool flag; void setup() { Serial.begin(115200); attachInterrupt(0, impuls, LOW); } void loop() { if (flag){ if(readBit()){ for (int i = 0; i < 8; i++) { Serial.print(readbit[i]); if (i == 7) Serial.println(); } } flag= false; } } void impuls(){ flag = true; } bool readBit() { for (int i = 0; i < 8; i++) { uint32_t tmout_guard = 300; while( (PIND & (1<<2)) != LOW){
if(!--tmout_guard) return false; } delayMicroseconds(5); readbit[i]= (PIND & (1<<2)) != LOW; } return true; }
сами биты выглядят так-
как видно стартовый бит имеет низкий уровень в течении 14 мкс и высокий в течении 4 мкс
в итоге ничего не работает.
по моей логике это работает так -
1. на линии появляется стартовый бит низкого уровня.
2. прерывание вызывает установку флага и запускается readBit()
3. в цикле while( (PIND & (1<<2)) != LOW){} ждем рост уровня или выходим по таймауту
4. как только уровень стартового бита вырос ждем 5 мкс (для попадания в середину бита)
5. читаем состояние порта и записываем в переменную
Камменты к коду напишите (для себя).
Пока я вижу только одно: после срабатывания прерывания по любой помехе производится жуткая эквилибристика. Что это за start bit 18ms, и почему остальные короче?
помех нет. я анализатором смотрю. старт бит дает понять ардуине что нужно начать чтение бит. а вообще мне кажется что вот эта идея с задержкой для попадания в середину бита для чтения не корректна. хотя так и делают многие библиотеки.
может есть способ чтения длительности импульсов а по ней определять что там - старт бит, ноль или единица.
примерно так -
1. прерывание по изменению
2. читаем уровень и длительность до следующего изменения.
3. записываем (в моем случае LOW - 14 мкс)
4. когда изменения прекратились ждем новых
итого получаем -
LOW - 14,
HIGH - 4,
//старт бит
LOW - 7,
HIGH - 3,
//0
LOW - 7,
HIGH - 3,
//0
LOW - 3,
HIGH - 7,
//1
...
LOW - 7,
HIGH - 3
//0
Смотреть-то вы смотрите, что читаете - вот вопрос? Я минут 10 пялился, так и не увидел логики.
Возьмите простой "блинк без дилэй", запустите его по окончанию "стартового бита" с периодом, соответствующим продолжительности стандартного бита со сдвигом в пол-периода. Снимите 8 сэмплов, получите полный байт.
совсем не пойму как такое сделать
1. прерывание по изменению
Чего? Вы уверены? В коде я вижу совсем другое
что означает "прерывание по LOW уровню".
1. прерывание по изменению
Чего? Вы уверены. В коде я вижу совсем другое
что означает "прерывание по LOW уровню".
это я для примера привел. конечно же в коде у меня LOW т. к я заведомо знаю что высокий уровень смениться на низкий. а для универсальности можно по CHANGE сделать
сейчас пытаюсь измерить время между состояниями. подскажите пожалуйста - функция attachInterrupt срабатывает при изменении один раз с каким то таймаутом или же постоянно при изменении уровня? (хоть каждую микросекунду)
Без таймаута, но насчет "каждой микросекунды" вы где то рядом с граблями ходите - прерывание вызывается не мгновенно, несколько тактов пройдет, потом ваш код, потом выход еще несколько тактов. Сколько микросекунд у вас минимум между фронтами (вот там где 0 в 1 переходит) - 1 или 2 ?
это я для примера привел.
Т.е. попытавшись Вам помочь, я потратил своё время абсолютно зря потому, что вместо нормального технического описания, Вы привели "цену на прошлогодний овёс" "для примера"?
Я называю такое поведение хамством.
конечно же в коде у меня LOW т. к я заведомо знаю что высокий уровень смениться на низкий.
Если "высокий уровень смениться на низкий", то должно быть FALLING, а не LOW!
P.S. Полностью решение Вашей проблемы описано вот здесь. Ссылку я "для примера привёл", конечно же на самом деле там нет описания Вашей проблемы.
Я бы попробовал как sadman41 предложил. Заменил бы чтение бита на запись, подобрал бы задержку по анализатору чтобы попасть "в середину бита" (каждого), потом заменил бы обратно на чтение, если будет глючить - попробовать загнать весь прием пакета в прерывание.
извините пожалуйста.
в первом сообщении абсолютно точное описание моей задачи. в сообщении под номером 2 мои мысли как я считаю это должно работать. более того я привел графические данные где четко видно что изначально линия в высоком состоянии (резистор на плюсе питания)
Это не отменяет того, что должно быть FALLING, а не LOW!
да. изменил-
attachInterrupt(0, impuls, FALLING); //теперь при изменении уровня с высокого на низкий вызовется функция impuls
Без таймаута, но насчет "каждой микросекунды" вы где то рядом с граблями ходите - прерывание вызывается не мгновенно, несколько тактов пройдет, потом ваш код, потом выход еще несколько тактов. Сколько микросекунд у вас минимум между фронтами (вот там где 0 в 1 переходит) - 1 или 2 ?
если я правильно понял то минимум 2 мкс
Вам нужно считать длительность низких уровней. В прерывании по FALLING фиксируете начало импульса, в лупе ожидаете RISING и получаете длительность. >= 10 us - стартовый, >= 5 us низкий, < 5 высокий. Когда добьётесь нормального приёма и, если необходимо, можете и RISING получать в прерывании (перенастроив). Тогда руки будут развязаны и можно будет заниматься не только приёмом.
что то не пойму. это как осуществить?
Нет. Я советую сначала сделать только с прерыванием по FALLING. А потом уже будете играться и другим, если захотите.
если можно пример а то вообще не догоню.
Тогда делайте вообще без прерываний.
iopq, в дополнение к совету из предыдущего сообщения:
вход в прерывания и выход из него - достаточно затратные операции, производящие сохранение регистров в стеке (как рабочих, так и служебных) с последующим восстановлением. Что происходит далеко не мгновенно. По моим прикидкам это время составляет единицы микросекунд, порядка 6-8. Т.е. если Вы оперируете сигналами длительностью порядка 4 мс, то весьма вероятно, что прерывание просто не успевает.
В частности, у SoftwareSerial практически такая же задача - считать бит по прерыванию. И максимальная достижимая скорость для AVR составляет 57600, что соответствует 17 мкс.
.
Человек же хочет по прерыванию.) Так пусть убедится успеет или нет. А тогда можно свой обработчик придумать.
На 328 с ардуино-прерываниями ты 4 мкс не поймаешь точно. Бери плату побыстрее и читай даташит. Такие мелкие импульсы измеряются таймерами.
Да ладно! Смотря что будет в обработчике. Если один флаг только, то поймаешь и меньше, потому как никаких прологов/эпилогов.
Зачем ты пишешь про вариант "один флаг только", когда задача совсем другая? И код настоящего прерывания ты специально забыл посчитать, да?
Да, позабывал я уже всё.) Вот код настоящего, который можно сократить, при желании:
Ну а в цикле не много. Проверил флаг, сбросил, сохранил микрос и всё. Дальше дожидаешься изменения сигнала и получаешь длительность. Думаю, 3 мкс поймаешь при 16 мгц.
Думаю, 3 мкс поймаешь при 16 мгц.
Вы не забыли, что каждые 4 микросекунды происходит прерывание для службы времени Ардуино? И оно совсем не только "флаг взвести". Ваше прерывание будет постоянно конфликтовать с ним и ожидать его. Так что немного Вы там наловите.
3 мкс теоретически ловить, наверное, можно, но это нужно забыть про Ардуино, всё нахрен выключить (миллис, сериал и т.п.) и писать всё ручками, что явно не для ТС с его сегодняшними знаниями и умениями.
Да, тут согласен. Поэтому и советовал ТС начать писать без прерываний.
ЕвгенийП, я дико извиняюсь за прошлую белиберду.) Прям, как в классике: "мы по разному видим мир").
чем отличается _delay_us от delayMicroseconds?
чем отличается _delay_us от delayMicroseconds?
Можете считать, что ничем. Различия настолько тонкие, что на Ваши проблемы не влияют.
Вы не забыли, что каждые 4 микросекунды происходит прерывание для службы времени Ардуино?
А раз в 4 микросекунды - это "тикает" таймер. Но без прерывания считает до 256.
Да, миллис при таких измерениях придётся выключать в любом случае, ну и сериал не использовать.
что то не пойму
вот эта запись
DDRD &= ~(1 << 2);
ведь аналогична pinMode(2, INPUT); ?
но при этом первая не работает
сами биты выглядят так-
как видно стартовый бит имеет низкий уровень в течении 14 мкс и высокий в течении 4 мкс
в итоге ничего не работает.
если не работает, то откуда берётся график? - подозреваю, что работает и было бы полезно озвучить название девайса, с которого снят лог.
но при этом первая не работает
Откуда информация, что не работает?
но при этом первая не работает
Откуда информация, что не работает?
при первом старте порты настроены на вход если не указано другое. если мы используем пин D2 на вход, то в сетапе указывать pinMode(2, INPUT); не нужно. правильно?
затем мы принимаем байт и хотим ответить. для этого нужно настроить порт на выход -
pinMode(2, OUTPUT); или DDRD |= 1<<2;
правильно?
а теперь для принятия нужно перестроить на вход -
pinMode(2, INPUT); или DDRD &= ~ 1<<2;
правильно? так вот - последнее перестроение в случае pinMode(2, INPUT); работает а в случае DDRD &= ~ 1<<2; нет.
и еще -
последнее перестроение в случае pinMode(2, INPUT); работает а в случае DDRD &= ~ 1<<2; нет.
Странно. Или Вы не умеете читать или не понимаете прочитанного. Русский-то родной? Я Вас спросил
Откуда информация, что не работает?
Попробую поподробнее: как именно, и из каких тестов Вы поняли, что оно не работает?
====================
Теперь по поводу собственно Вашего вопроса. Вы пишете
теперь для принятия нужно перестроить на вход -
pinMode(2, INPUT); или DDRD &= ~ 1<<2;
правильно?
Нет, конечно, неправильно.
Тут есть вопрос к Вам. Вы считаете, что операции "pinMode(2, INPUT);" и "DDRD &= ~ 1<<2;" эквиваленты (логически). Это, конечно, не так, но Вы в этом уверены. При этом язык Вы знаете слабенько. Так зачем выпендриваться и писать напрямую в порты, если Вы этого не умеете и лишь приблизительно представляете как это делается? Хотите научиться? Или есть другие причины?
Если хотите научиться, так нужно не лезть с каждым чихом на форум, а брать учебник и учиться.
Могу дать подсказку, даже две, но то, что я сейчас напишу, Вы должны делать сами.
Подсказка №1. Включите в IDE предупреждения компилятора и читайте их! Разработчики IDE скрыли предупреждения, чтобы не напрягать блондинок и беременных доярок всякими глупостЯми. Включите! Компилятор честно предупредил Вас, что в строке "DDRD &= ~ 1<<2;" что-то не сходится, но Вы его предупреждение проигнорировали.
Подсказка №2. Вы почему-то уверены, что ~ 1<<2 даст байт в котором все 1 кроме бита №2. Так? Но оно не заработало! Ваше первое действие - не бежать на форум, а просто запустить вот такой "проще некуда" скетч и убедиться, что Вы правы в своей уверенности. Или не правы - тогда надо исправлять ошибку.
Запустите и посмотрите действительно ли там все 1-цы, кроме второго бита или Вы таки где-то лажанулись. А когда убедитесь, что лажанулись, подумайте почему так. Ошибка у Вас простая, если увидите результат, прочитаете предупреждение компилятора, то разберётесь.
В любом случае, сообщения компилятора читать всегда и при любой проблеме запускать тестовые скетчи. Так научитесь, а воплями "памагити" на форуме ничему научиться нельзя.
Право на жизнь имеет даже коронавирус - тоже божья тварь как ни крути.
Другое дело, что написанная Вам конструкция - это как приготовление "The Russin cocktail": взять 50% водки, добавить ещё 50% водки, тщательно перемешать, и перед употреблением добавить водки по вкусу.
Внутри обработчика прерывания и без Вас запрещены, так что Ваша cli - в пользу бедных. В при выходе из обработчика они разрешаются, так что на Вашу cli плевать хотели.
спасибо вам кажется начинаю понимать.
DDRD = 0b00000100; //настроили порт D2 на выход. все остальные - вход. тут уж точно верно?
как это не работает -
тут я заменил ардуиновскую функцию attachInterrupt согласно даташита 328 меги. она даже работает.
при этом если изменить DDRD = 0b00000000; на pinMode(2, INPUT); то все работает как прежде. вопрос в том что бы понять как это так
да - включил подробный вывод при компиляции, но ошибок нету
ТС, а можно узнать, зачем подобный мазохизм - читать входной сигнал и писать выходной в один и тот же выход?
Ну и потом, как бы DDRD = 0b00000000; и pinMode(2, INPUT); - опять совсем не одно и тоже. Сколько вы будете играть в ромашку, переделывая методом тыка одну элементарную строчку - может уже сесть за учебник?
ТС, а можно узнать, зачем подобный мазохизм - читать входной сигнал и писать выходной в один и тот же выход?
Ну и потом, как бы DDRD = 0b00000000; и pinMode(2, INPUT); - опять совсем не одно и тоже. Сколько вы будете играть в ромашку, переделывая методом тыка одну элементарную строчку - может уже сесть за учебник?
видимо мазохисты придумали ту же 1-Wire
нашел на таком же форуме только домен сс вот такую запись -
DDRD &= ~_BV (2); // pinMode (2, INPUT);
короче я вообще не понимаю уже ничего. вот из даташита атмеги -
я вижу это так - регистр DDRD в котором 8 флажков - портов. если нужно на выход - ставим 1.
если на вход то 0.
если мне нужно установить порт D2 на вход, то если я запишу DDRD = 0b11111011 то именно D2 должен
стать входом. остальные порты пусть хоть вход, хоть выход, хоть просто сгорят мне не важны они
сейчас. или не правильно и надо еще что то сделать?
iopq - из-за того, что вы знаете предмет нетвердо - вы даже не понимаете, о каких ошибках вам пишут и ищете ошибки не там. Никто не сомневается в том, что регистр DDRD управляет направлением ввода-вывода соответвующих пинов. Проблема в том. что вы неверно используете битовые операции.
Вы проделали пример Евгения из сообщения #36? - суда по ответам - нет. иначе вы бы исправили ошибку, а не меняли код
естественно проделал и увидел что я полную чушь выставлял. потому решил пока просто в двоичном коде выставить что бы без ошибок но не работает все равно. вот эта запись DDRD &= ~_BV (2);
а именно ~_BV (2) == 251 or 0b11111011
пойду проще исходники долбанной pinMode гляну...
естественно проделал и увидел что я полную чушь выставлял. потому решил пока просто в двоичном коде выставить что бы без ошибок но не работает все равно. вот эта запись DDRD &= ~_BV (2);
а именно ~_BV (2) == 251 or 0b11111011
пойду проще исходники долбанной pinMode гляну...
Зачем? - думаю, вам исходник не поможет
У вас уже Евгений спрашивал - ну если вы не догоняете. как этим пользоваться. что мешает не выпендриваться и просто написать pinMode(2, INPUT) ?
естественно проделал и увидел что я полную чушь выставлял. потому решил пока просто в двоичном коде выставить что бы без ошибок но не работает все равно. вот эта запись DDRD &= ~_BV (2);
а именно ~_BV (2) == 251 or 0b11111011
пойду проще исходники долбанной pinMode гляну...
Зачем? - думаю, вам исходник не поможет
У вас уже Евгений спрашивал - ну если вы не догоняете. как этим пользоваться. что мешает не выпендриваться и просто написать pinMode(2, INPUT) ?
если бы никто ничего не хотел и не пытался понять - до сих пор с палкой зайцев по лесу гоняли и ждали грозы что бы его пожарить
я бы на вашем месте сосредочился на главном - на приеме и отправке импульсов по прерыванию. И чтобы уменьшить число возможных ошибок, для начала попробовал с самым простым и дубовым кодом. А когда начало бы хоть что-то получаться - перешел бы к оптимизации. постепенно заменяя ардуино-функции на прямой доступ к регистрам
я бы на вашем месте сосредочился на главном - на приеме и отправке импульсов по прерыванию. И чтобы уменьшить число возможных ошибок, для начала попробовал с самым простым и дубовым кодом. А когда начало бы хоть что-то получаться - перешел бы к оптимизации. постепенно заменяя ардуино-функции на прямой доступ к регистрам
так уже получается. я как раз и занимаюсь оптимизацией и пытаюсь понять как это все работает на самом низшем уровне.
так уже получается. я как раз и занимаюсь оптимизацией и пытаюсь понять как это все работает на самом низшем уровне.
тогда не понятно, нафига это переливание из пустого в порожнее в форуме.
берите главу из учебника про битовые операции, пишите простенький тест типа примера Евгения из #36 и экспериментируйте, пока на выходе не начнет получаться тот же результат. что и у pinMode()
с уверенностью заявляю что запись pinMode(2, INPUT); для 328 атмеги полностью соответствует -
а с этим разобрались:
DDRD &= ~ 1<<2;
да с этим я еще в 36 сообщении разобрался. кто же знал что еще порт в низкий уровень перевести нужно