чтение бита по прерыванию

iopq
Offline
Зарегистрирован: 05.07.2016

нужно отслеживать появление сигнала после которого прочитать биты. для этого логично воспользоваться прерыванием. для этого я использовал функцию 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. читаем состояние порта и записываем в переменную

 

sadman41
Offline
Зарегистрирован: 19.10.2016

Камменты к коду напишите (для себя).

Пока я вижу только одно: после срабатывания прерывания по любой помехе производится жуткая эквилибристика. Что это за start bit 18ms, и почему остальные короче?

iopq
Offline
Зарегистрирован: 05.07.2016

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

может есть способ чтения длительности импульсов а по ней определять что там - старт бит, ноль или единица. 

примерно так -

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

sadman41
Offline
Зарегистрирован: 19.10.2016

Смотреть-то вы смотрите, что читаете - вот вопрос? Я минут 10 пялился, так и не увидел логики.

Возьмите простой "блинк без дилэй", запустите его по окончанию "стартового бита" с периодом, соответствующим продолжительности стандартного бита со сдвигом в пол-периода. Снимите 8 сэмплов, получите полный байт.

iopq
Offline
Зарегистрирован: 05.07.2016

совсем не пойму как такое сделать

ЕвгенийП
ЕвгенийП аватар
Онлайн
Зарегистрирован: 25.05.2015

iopq пишет:

1. прерывание по изменению

Чего? Вы уверены? В коде я вижу совсем другое

iopq пишет:

 attachInterrupt(0, impuls, LOW);

что означает "прерывание по LOW уровню".

iopq
Offline
Зарегистрирован: 05.07.2016

ЕвгенийП пишет:

iopq пишет:

1. прерывание по изменению

Чего? Вы уверены. В коде я вижу совсем другое

iopq пишет:

 attachInterrupt(0, impuls, LOW);

что означает "прерывание по LOW уровню".

 

это я для примера привел. конечно же в коде у меня LOW т. к я заведомо знаю что высокий уровень смениться на низкий. а для универсальности можно по CHANGE сделать

сейчас пытаюсь измерить время между состояниями. подскажите пожалуйста - функция attachInterrupt срабатывает при изменении один раз с каким то таймаутом или же постоянно при изменении уровня? (хоть каждую микросекунду)

Morroc
Offline
Зарегистрирован: 24.10.2016

Без таймаута, но насчет "каждой микросекунды" вы где то рядом с граблями ходите - прерывание вызывается не мгновенно, несколько тактов пройдет, потом ваш код, потом выход еще несколько тактов. Сколько микросекунд у вас минимум между фронтами (вот там где 0 в 1 переходит) - 1 или 2 ?

ЕвгенийП
ЕвгенийП аватар
Онлайн
Зарегистрирован: 25.05.2015

iopq пишет:

это я для примера привел. 

Т.е. попытавшись Вам помочь, я потратил своё время абсолютно зря потому, что вместо нормального технического описания, Вы привели "цену на прошлогодний овёс" "для примера"?

Я называю такое поведение хамством.

iopq пишет:

конечно же в коде у меня LOW т. к я заведомо знаю что высокий уровень смениться на низкий.

Если "высокий уровень смениться на низкий", то должно быть FALLING, а не LOW!

P.S. Полностью решение Вашей проблемы описано вот здесь. Ссылку я "для примера привёл", конечно же на самом деле там нет описания Вашей проблемы.

Morroc
Offline
Зарегистрирован: 24.10.2016

Я бы попробовал как sadman41 предложил. Заменил бы чтение бита на запись, подобрал бы задержку по анализатору чтобы попасть "в середину бита" (каждого), потом заменил бы обратно на чтение, если будет глючить - попробовать загнать весь прием пакета в прерывание.

iopq
Offline
Зарегистрирован: 05.07.2016

извините пожалуйста. 

в первом сообщении абсолютно точное описание моей задачи. в сообщении под номером 2 мои мысли как я считаю это должно работать. более того я привел графические данные где четко видно что изначально линия в высоком состоянии (резистор на плюсе питания)

ЕвгенийП
ЕвгенийП аватар
Онлайн
Зарегистрирован: 25.05.2015

Это не отменяет того, что должно быть FALLING, а не LOW!

 

iopq
Offline
Зарегистрирован: 05.07.2016

да. изменил-

attachInterrupt(0, impuls, FALLING); //теперь при изменении уровня с высокого на низкий вызовется функция impuls

Без таймаута, но насчет "каждой микросекунды" вы где то рядом с граблями ходите - прерывание вызывается не мгновенно, несколько тактов пройдет, потом ваш код, потом выход еще несколько тактов. Сколько микросекунд у вас минимум между фронтами (вот там где 0 в 1 переходит) - 1 или 2 ?

 

если я правильно понял то минимум 2 мкс

Green
Offline
Зарегистрирован: 01.10.2015

Вам нужно считать длительность низких уровней. В прерывании по FALLING фиксируете начало импульса, в лупе ожидаете RISING и получаете длительность. >= 10 us - стартовый, >= 5 us низкий, < 5 высокий. Когда добьётесь нормального приёма и, если необходимо, можете и RISING получать в прерывании (перенастроив). Тогда руки будут развязаны и можно будет заниматься не только приёмом.

iopq
Offline
Зарегистрирован: 05.07.2016

что то не пойму. это как осуществить? 

void setup() {
  attachInterrupt(0, impuls, FALLING);   
  Serial.begin(115200);
}

void loop() {
attachInterrupt(0, impuls2, RISING);  
}
так что ли?

 

Green
Offline
Зарегистрирован: 01.10.2015

Нет. Я советую сначала сделать только с прерыванием по FALLING. А потом уже будете играться и другим, если захотите.

iopq
Offline
Зарегистрирован: 05.07.2016

если можно пример а то вообще не догоню. 

Green
Offline
Зарегистрирован: 01.10.2015

Тогда делайте вообще без прерываний.

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

iopq, в дополнение к совету из предыдущего сообщения:

вход в прерывания и выход из него - достаточно затратные операции, производящие сохранение регистров в стеке (как рабочих, так и служебных) с последующим восстановлением. Что происходит далеко не мгновенно. По моим прикидкам это время составляет единицы микросекунд, порядка 6-8. Т.е. если Вы оперируете сигналами длительностью порядка 4 мс, то весьма вероятно, что прерывание просто не успевает.

В частности, у SoftwareSerial практически такая же задача - считать бит по прерыванию. И максимальная достижимая скорость для AVR составляет 57600, что соответствует 17 мкс.

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

.

Green
Offline
Зарегистрирован: 01.10.2015

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

rkit
Онлайн
Зарегистрирован: 23.11.2016

На 328 с ардуино-прерываниями ты 4 мкс не поймаешь точно. Бери плату побыстрее и читай даташит. Такие мелкие импульсы измеряются таймерами.

Green
Offline
Зарегистрирован: 01.10.2015

Да ладно! Смотря что будет в обработчике. Если один флаг только, то поймаешь и меньше, потому как никаких прологов/эпилогов.

void setup() {
  attachInterrupt(0, interrupt, RISING);
}
 
volatile bool rising;

void interrupt() {
  rising = true;
}
 

void loop() {}
 
000000be <interrupt()>:
be: 81 e0 ldi r24, 0x01 ; 1
c0: 80 93 32 01 sts 0x0132, r24 ; 0x800132 <__data_end>
c4: 08 95 ret

 

rkit
Онлайн
Зарегистрирован: 23.11.2016

Зачем ты пишешь про вариант "один флаг только", когда задача совсем другая? И код настоящего прерывания ты специально забыл посчитать, да?

 

Green
Offline
Зарегистрирован: 01.10.2015

Да, позабывал я уже всё.) Вот код настоящего, который можно сократить, при желании:

0000041c <__vector_1>:
 41c:	1f 92       	push	r1
 41e:	0f 92       	push	r0
 420:	0f b6       	in	r0, 0x3f	; 63
 422:	0f 92       	push	r0
 424:	11 24       	eor	r1, r1
 426:	8f 93       	push	r24
 428:	81 e0       	ldi	r24, 0x01	; 1
 42a:	80 93 30 01 	sts	0x0130, r24	; 0x800130 <rising>
 42e:	8f 91       	pop	r24
 430:	0f 90       	pop	r0
 432:	0f be       	out	0x3f, r0	; 63
 434:	0f 90       	pop	r0
 436:	1f 90       	pop	r1
 438:	18 95       	reti

Ну а в цикле не много. Проверил флаг, сбросил, сохранил микрос и всё. Дальше дожидаешься изменения сигнала и получаешь длительность. Думаю, 3 мкс поймаешь при 16 мгц.

ЕвгенийП
ЕвгенийП аватар
Онлайн
Зарегистрирован: 25.05.2015

Green пишет:

Думаю, 3 мкс поймаешь при 16 мгц.

Вы не забыли, что каждые 4 микросекунды происходит прерывание для службы времени Ардуино? И оно совсем не только "флаг взвести". Ваше прерывание будет постоянно конфликтовать с ним и ожидать его. Так что немного Вы там наловите.

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

Green
Offline
Зарегистрирован: 01.10.2015

Да, тут согласен. Поэтому и советовал ТС начать писать без прерываний.
ЕвгенийП, я дико извиняюсь за прошлую белиберду.) Прям, как в классике: "мы по разному видим мир").

iopq
Offline
Зарегистрирован: 05.07.2016

чем отличается _delay_us от delayMicroseconds?

ЕвгенийП
ЕвгенийП аватар
Онлайн
Зарегистрирован: 25.05.2015

iopq пишет:

чем отличается _delay_us от delayMicroseconds?

Можете считать, что ничем. Различия настолько тонкие, что на Ваши проблемы не влияют.

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

ЕвгенийП пишет:

Вы не забыли, что каждые 4 микросекунды происходит прерывание для службы времени Ардуино?

Вообще-то почти каждую миллисекунду (если AVR). И точно каждую, если SAM3/STM32.

А раз в 4 микросекунды - это "тикает" таймер. Но без прерывания считает до 256.

Green
Offline
Зарегистрирован: 01.10.2015

Да, миллис при таких измерениях придётся выключать в любом случае, ну и сериал не использовать.

iopq
Offline
Зарегистрирован: 05.07.2016

что то не пойму 

вот эта запись

DDRD &= ~(1 << 2);

ведь аналогична pinMode(2, INPUT);  ?

но при этом первая не работает

Клапауций 12345
Offline
Зарегистрирован: 17.05.2020

iopq пишет:

сами биты выглядят так-

как видно стартовый бит имеет низкий уровень в течении 14 мкс и высокий в течении 4 мкс

в итоге ничего не работает. 

если не работает, то откуда берётся график? - подозреваю, что работает и было бы полезно озвучить название девайса, с которого снят лог. 

ЕвгенийП
ЕвгенийП аватар
Онлайн
Зарегистрирован: 25.05.2015

andriano пишет:
Вообще-то почти каждую миллисекунду
Таки, да :-)

ЕвгенийП
ЕвгенийП аватар
Онлайн
Зарегистрирован: 25.05.2015

iopq пишет:

но при этом первая не работает

Откуда информация, что не работает?

iopq
Offline
Зарегистрирован: 05.07.2016

ЕвгенийП пишет:

iopq пишет:

но при этом первая не работает

Откуда информация, что не работает?

при первом старте порты настроены на вход если не указано другое. если мы используем пин D2 на вход, то в сетапе указывать pinMode(2, INPUT); не нужно. правильно?

затем мы принимаем байт и хотим ответить. для этого нужно настроить порт на выход -

pinMode(2, OUTPUT); или DDRD |= 1<<2;

правильно?

а теперь для принятия нужно перестроить на вход - 

pinMode(2, INPUT); или DDRD &= ~ 1<<2;

правильно? так вот - последнее перестроение в случае pinMode(2, INPUT); работает а в случае DDRD &= ~ 1<<2; нет. 

и еще -

 ISR(INT0_vect){
  cli();
}
 
вот такое имеет право на жизнь? хочу при прерывании запретить дальнейшие прерывания т. к подозреваю что при обработке первого лезут еще и не дают работать. но это тоже не работает. если cli(); указать в loop, то работает
ЕвгенийП
ЕвгенийП аватар
Онлайн
Зарегистрирован: 25.05.2015

iopq пишет:

последнее перестроение в случае pinMode(2, INPUT); работает а в случае DDRD &= ~ 1<<2; нет. 

Странно. Или Вы не умеете читать или не понимаете прочитанного. Русский-то родной? Я Вас спросил

ЕвгенийП пишет:

Откуда информация, что не работает?

Попробую поподробнее: как именно, и из каких тестов Вы поняли, что оно не работает?

====================

Теперь по поводу собственно Вашего вопроса. Вы пишете

iopq пишет:

теперь для принятия нужно перестроить на вход - 

pinMode(2, INPUT); или DDRD &= ~ 1<<2;

правильно?

Нет, конечно, неправильно.

Тут есть вопрос к Вам. Вы считаете, что операции "pinMode(2, INPUT);" и "DDRD &= ~ 1<<2;" эквиваленты (логически). Это, конечно, не так, но Вы в этом уверены. При этом язык Вы знаете слабенько. Так зачем выпендриваться и писать напрямую в порты, если Вы этого не умеете и лишь приблизительно представляете как это делается? Хотите научиться? Или есть другие причины?

Если хотите научиться, так нужно не лезть с каждым чихом на форум, а брать учебник и учиться.

Могу дать подсказку, даже две, но то, что я сейчас напишу, Вы должны делать сами.

Подсказка №1. Включите в IDE предупреждения компилятора и читайте их! Разработчики IDE скрыли предупреждения, чтобы не напрягать блондинок и беременных доярок всякими глупостЯми. Включите! Компилятор честно предупредил Вас, что в строке "DDRD &= ~ 1<<2;" что-то не сходится, но Вы его предупреждение проигнорировали.

Подсказка №2. Вы почему-то уверены, что ~ 1<<2 даст байт в котором все 1 кроме бита №2. Так? Но оно не заработало! Ваше первое действие - не бежать на форум, а просто запустить вот такой "проще некуда" скетч и убедиться, что Вы правы в своей уверенности. Или не правы - тогда надо исправлять ошибку.

void setup(void) {
	Serial.begin(57600);
	byte kaka = ~ 1 << 2;
	Serial.print("kaka=");
	Serial.println(kaka,BIN);
}

void loop(void) {}

Запустите и посмотрите действительно ли там все 1-цы, кроме второго бита или Вы таки где-то лажанулись. А когда убедитесь, что лажанулись, подумайте почему так. Ошибка у Вас простая, если увидите результат, прочитаете предупреждение компилятора, то разберётесь.

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

ЕвгенийП
ЕвгенийП аватар
Онлайн
Зарегистрирован: 25.05.2015

iopq пишет:

 ISR(INT0_vect){
  cli();
}
 
вот такое имеет право на жизнь? 

Право на жизнь имеет даже коронавирус - тоже божья тварь как ни крути.

Другое дело, что написанная Вам конструкция - это как приготовление "The Russin cocktail": взять 50% водки, добавить ещё 50% водки, тщательно перемешать, и перед употреблением добавить водки по вкусу.

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

iopq
Offline
Зарегистрирован: 05.07.2016

спасибо вам кажется начинаю понимать. 

DDRD = 0b00000100; //настроили порт D2 на выход. все остальные - вход.  тут уж точно верно?

 

как это не работает -

тут я заменил ардуиновскую функцию attachInterrupt согласно даташита 328 меги. она даже работает. 

ISR(INT0_vect){

//сюда попали по прерыванию на INT0

  readbyte(); //читаем то, что вызвало прерывание

  if ( temp[0] == 147 ){ 
    
   DDRD = 0b00000100; //установили порт D2 на ВЫход
    
    writeByte(123); //отправили байт
   
   DDRD = 0b00000000;//pinMode(2, INPUT); все порты D2 на Вход, но после уже ничего прочитать не можем
  
}
}

при этом если изменить DDRD = 0b00000000; на pinMode(2, INPUT); то все работает как прежде. вопрос в том что бы понять как это так

 

да - включил подробный вывод при компиляции, но ошибок нету

b707
Онлайн
Зарегистрирован: 26.05.2017

ТС, а можно узнать, зачем подобный мазохизм - читать входной сигнал и писать выходной в один и тот же выход?

Ну и потом, как бы DDRD = 0b00000000; и pinMode(2, INPUT); - опять совсем не одно и тоже. Сколько вы будете играть в ромашку, переделывая методом тыка одну элементарную строчку - может уже сесть за учебник?

iopq
Offline
Зарегистрирован: 05.07.2016

b707 пишет:

ТС, а можно узнать, зачем подобный мазохизм - читать входной сигнал и писать выходной в один и тот же выход?

Ну и потом, как бы DDRD = 0b00000000; и pinMode(2, INPUT); - опять совсем не одно и тоже. Сколько вы будете играть в ромашку, переделывая методом тыка одну элементарную строчку - может уже сесть за учебник?

видимо мазохисты придумали ту же 1-Wire

нашел на таком же форуме только домен сс вот такую запись -

DDRD &= ~_BV (2); // pinMode (2, INPUT);

короче я вообще не понимаю уже ничего. вот из даташита атмеги -

я вижу это так - регистр DDRD в котором 8 флажков - портов. если нужно на выход - ставим 1.

если на вход то 0. 

если мне нужно установить порт D2 на вход, то если я запишу DDRD = 0b11111011 то именно D2 должен 

стать входом. остальные порты пусть хоть вход, хоть выход, хоть просто сгорят мне не важны они 

сейчас. или не правильно и надо еще что то сделать?

b707
Онлайн
Зарегистрирован: 26.05.2017

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

Вы проделали пример Евгения из сообщения #36? - суда по ответам - нет. иначе вы бы исправили ошибку, а не меняли код

iopq
Offline
Зарегистрирован: 05.07.2016

естественно проделал и увидел что я полную чушь выставлял. потому решил пока просто в двоичном коде выставить что бы без ошибок но не работает все равно. вот эта запись DDRD &= ~_BV (2);

а именно  ~_BV (2) == 251 or 0b11111011

пойду проще исходники долбанной pinMode гляну...

b707
Онлайн
Зарегистрирован: 26.05.2017

iopq пишет:

естественно проделал и увидел что я полную чушь выставлял. потому решил пока просто в двоичном коде выставить что бы без ошибок но не работает все равно. вот эта запись DDRD &= ~_BV (2);

а именно  ~_BV (2) == 251 or 0b11111011

пойду проще исходники долбанной pinMode гляну...

Зачем? - думаю, вам исходник не поможет

У вас уже Евгений спрашивал - ну если вы не догоняете. как этим пользоваться. что мешает не выпендриваться и просто написать pinMode(2, INPUT) ?

iopq
Offline
Зарегистрирован: 05.07.2016

b707 пишет:

iopq пишет:

естественно проделал и увидел что я полную чушь выставлял. потому решил пока просто в двоичном коде выставить что бы без ошибок но не работает все равно. вот эта запись DDRD &= ~_BV (2);

а именно  ~_BV (2) == 251 or 0b11111011

пойду проще исходники долбанной pinMode гляну...

Зачем? - думаю, вам исходник не поможет

У вас уже Евгений спрашивал - ну если вы не догоняете. как этим пользоваться. что мешает не выпендриваться и просто написать pinMode(2, INPUT) ?

 

если бы никто ничего не хотел и не пытался понять - до сих пор с палкой зайцев по лесу гоняли и ждали грозы что бы его пожарить

b707
Онлайн
Зарегистрирован: 26.05.2017

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

iopq
Offline
Зарегистрирован: 05.07.2016

b707 пишет:

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

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

b707
Онлайн
Зарегистрирован: 26.05.2017

iopq пишет:

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

тогда не понятно, нафига это переливание из пустого в порожнее в форуме.

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

iopq
Offline
Зарегистрирован: 05.07.2016

с уверенностью заявляю что запись pinMode(2, INPUT); для 328 атмеги полностью соответствует -

    DDRD   &= ~_BV (2);
    PORTD  &= ~_BV (2);

 

b707
Онлайн
Зарегистрирован: 26.05.2017

а с этим разобрались:

DDRD &= ~ 1<<2;

iopq
Offline
Зарегистрирован: 05.07.2016

да с этим я еще в 36 сообщении разобрался. кто же знал что еще порт в низкий уровень перевести нужно