Задача для начинающих: коварная ошибка

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

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

Эта программа ждёт нажатия на кнопку, подключённую между пином D2 и землёй (дребезг аппаратно подавлен!) и, поймав таковое нажатие, выводит в Serial время этого события.

Код:

static volatile bool buttonState = false;

void processButton(void) {
   buttonState = true;
}

//
// Возвращаем текущее значение buttonState
// а саму её делаем false, чтобы не обработать 
// тоже самое нажатие дважды
//
static bool isPressed(void) {
   const bool result = buttonState;
   buttonState = false;
   return result;
}

void setup(void){
   Serial.begin(57600);
   Serial.println("Fun begins!");
   pinMode(2, INPUT_PULLUP);
   attachInterrupt(0, processButton, FALLING);
}

//
// Если клавиша была нажата, печатаем время,
// когда мы об этом узнали.
//
void loop(void) {
   if (isPressed()) {
      Serial.print("Button is pressed: ");
      Serial.println(millis());
   }
}

Результат:

Fun begins!
Button is pressed: 2385
Button is pressed: 2785
Button is pressed: 3184
Button is pressed: 3479
Button is pressed: 3829
Button is pressed: 4177
Button is pressed: 4579
Button is pressed: 4929

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

В чём дело? Нет, плата, конечно, китайская и Си нечестный, это понятно. Только ошибка здесь не в плате, а в голове программиста. Найдите и привыкайте видеть такие вещи сразу, при первом взгляде на программу.

Примечание: если будете собирать для пробы в протеусе, то проблем нет, а если в железе, то не забудьте подавить дребезг. Для данной задачи достаточно зашунтировать выводы кнопки конденсатором, скажем в 4,7µF и между кнопкой и пином воткнуть резистор на пару килоом – если кнопка не вдрызг убитая, то никакого дребезга не будет.

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

Пример был бы нагляднее (ИМХО), если бы время, когда событие может быть пропущено, имело бы большую долю в цикле.

 

-NMi-
Offline
Зарегистрирован: 20.08.2018

Может ISR Falling поменять на Down?  Ибо не всегда будет именно Falling, например при удержании кнопки или "неуспевания" зарядки конденсатора.

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

wdrakula пишет:

Пример был бы нагляднее (ИМХО), если бы время, когда событие может быть пропущено, имело бы большую долю в цикле.

А при чём тут цикл? Пришло прерывание, переменная buttonState стала true и будет таковой оставаться пока до неё isPressed не доберётся. Цикл (в смысле loop) здесь как раз и нужен очень короткий, чтобы невозможно было два раза нажать кнопку за время между обращениями к isPressed. Или я не так понял замечание?

-NMi- пишет:

Может ISR Falling поменять на Down?  Ибо не всегда будет именно Falling, например при удержании кнопки или "неуспевания" зарядки конденсатора.

Удержание не при делах - мы считаем количество нажатий и плюём на удержание. Так что FALLING - самое то. А время зарядки конденсатора - постоянная времени там менее 10мс - не думаю, что можно успеть дважды нажать кнопку за такое время.

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

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

А при чём тут цикл?...

Или я не так понял замечание?

;)) не так понял.

Сейчас так:

Это кусок компилированного кода задачи, имеющий отношение к самой задаче.
(прсстите за тафтологию!)
сама переменная buttonstate и наш loop(). Комментарии я выкинул, для компактности.
Спиенти сат и все эти вещи.

 0080013a l     O .bss	00000001 buttonState
..............................
 6b0:	80 91 3a 01 	lds	r24, 0x013A	; 0x80013a <__data_end>     2
 6b4:	10 92 3a 01 	sts	0x013A, r1	; 0x80013a <__data_end>     2
 6b8:	88 23       	and	r24, r24                                1
 6ba:	49 f1       	breq	.+82     	; 0x70e <main+0x1e2>    2
.......
 70e:	0e 94 88 01 	call	0x310	; 0x310 <serialEventRun()>  4
 712:	ce cf       	rjmp	.-100    	; 0x6b0 <main+0x184>    2
======================================
13 + вызов = 22 такта. Время, когда прерывание не будет обработано это время между 
первой и второй командами (lds и sts) - 2 такта. 2/22 = 1/11 = вероятность необработки.
Всего 9% приблизительно.


 310:	80 e0       	ldi	r24, 0x00	; 0 1
 312:	90 e0       	ldi	r25, 0x00	; 0 1
 314:	89 2b       	or	r24, r25        1
 316:	29 f0       	breq	.+10     	; 0x322 <serialEventRun()+0x12>  2
 ........
 322:	08 95       	ret 4
==========================
9 тактов

Вероятность "осечки" 9%.

Я имел ввиду, что можно было как-то ярче выделить ошибку, чтобы "осечки" были чаще... Но я не придумал - как. Так что мое замечание - чисто демагогическое.

--------------

Так напишу, штоп не портить празднег: между строчкой N и N+1 нужно было бы вставить какой-нибудь осмысленный(якобы) мусор. Тогда осечки были бы чаще.

-NMi-
Offline
Зарегистрирован: 20.08.2018

Странный у тебя ассемблер... ret 4  --- когда это у оператора возврата был параметр? Или может я чо не знаю...

А по поводу тактов в нахождении в процедуре обработчика, вот тут надо подумать. Флаг окончания обработки в регистре флагов от прерываний когда сбрасывается, при входе или при выходе (reti) ???  (я забыл, но по идее должен при выходе из обработчика сбросится на аппаратном уровне, в 328р это 100%) По идее если его (флаг) сбросить в начале обработчика вектора прерывания - будет возможность работы прерывания в прерывании, что, конечно же не жалательно. Так что 9% на "необработку" по причине "занятости" вполне себе может быть.

Komandir
Komandir аватар
Offline
Зарегистрирован: 18.08.2018

13, 14 - не обеспечивается атоммарность доступа

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

Komandir пишет:

13, 14 - не обеспечивается атоммарность доступа

Ну и зачем это было???

Не знаю как Женя, я планировал еще долго издеваться над умниками, а ты мне такую малину прогадил!!!

 

Ворота
Ворота аватар
Offline
Зарегистрирован: 10.01.2016

Мда, мля..., тема привлекла внимание исключительно новичков :(((

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

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

Не только новички, не только здесь. Любого начальника возьми: "Дыра заткнута? Перебрасываем ресурсы на другое направление".

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

-NMi- пишет:

Странный у тебя ассемблер... ret 4  --- когда это у оператора возврата был параметр? Или может я чо не знаю...

А по поводу тактов в нахождении в процедуре обработчика, вот тут надо подумать. Флаг окончания обработки в регистре флагов от прерываний когда сбрасывается, при входе или при выходе (reti) ???  (я забыл, но по идее должен при выходе из обработчика сбросится на аппаратном уровне, в 328р это 100%) По идее если его (флаг) сбросить в начале обработчика вектора прерывания - будет возможность работы прерывания в прерывании, что, конечно же не жалательно. Так что 9% на "необработку" по причине "занятости" вполне себе может быть.

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

;))))

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

Ворота пишет:
откуда-то же потом вырастают и нормальные люди :(

Хде?????!!!!!

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

Думаю, не все, всё-таки. А старожилы ... да, зря сразу всё раскололи - подумать людям не дали :(

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

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

Думаю, не все, всё-таки. А старожилы ... да, зря сразу всё раскололи - подумать людям не дали :(

ну я ж написал, что задачку упростить надо было, между... что уж теперь скрывать, 13 и 14 нужно было тебе какой-нить, якобы полезный, счет добавить. Тогда неатомарность сразу бы вылезла.

Не увидят наши дебилы неатомарность, если она в соседних строках, она и в изделии - (9% - помнишь?) будет редко давать глюки, которые наши кулибины спишут на - "платы - гафно китайское!"! ;))

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

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

Думаю, не все, всё-таки. А старожилы ... да, зря сразу всё раскололи - подумать людям не дали :(

та чё там думать...прыгать надо, день ВДВ на носу

void loop(void) {
   if (buttonState==true) {
      Serial.print("Button is pressed: ");
      Serial.println(millis());
       buttonState=false;
   }
}

 

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

wdrakula пишет:
будет редко давать глюки, которые наши кулибины спишут на - "платы - гафно китайское!"! ;))
Это да. А те, кто попродвинутее, те вспомнят, что С нечестный - "wiring-гафно итальянское".

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

А как нада?

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

ишё адин навичёк :-)))

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

Дак век живи - и дураком помрешь. Который не дальше песочницы. ¦(

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

Ну, за новичков! (тм)

nik182
Offline
Зарегистрирован: 04.05.2015

Я б третью строку со слова inline бы начал и пофиг все не атомарности. 

-NMi-
Offline
Зарегистрирован: 20.08.2018

wdrakula пишет:

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

И слава яйцам, шо я ещё раз убедился в "говняности" дурдуинокода и его компилятора. Как так "иногда" процессор может "потерять" значение переменной. Тем более, что на асме это бы заняло всё вместе не более 10 команд и никогда ничего не потерялось.

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

-NMi- пишет:

wdrakula пишет:

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

И слава яйцам, шо я ещё раз убедился в "говняности" дурдуинокода и его компилятора. Как так "иногда" процессор может "потерять" значение переменной. Тем более, что на асме это бы заняло всё вместе не более 10 команд и никогда ничего не потерялось.

Хы хочешь выглядеть еще глупее и еще смешнее? У тебя получается, возможно это талант.

VladimirTsibrov
Offline
Зарегистрирован: 05.03.2019

-NMi- пишет:

wdrakula пишет:

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

И слава яйцам, шо я ещё раз убедился в "говняности" дурдуинокода и его компилятора. Как так "иногда" процессор может "потерять" значение переменной. Тем более, что на асме это бы заняло всё вместе не более 10 команд и никогда ничего не потерялось.

Значения теряют программисты. Компилятор ни при чем.

VladimirTsibrov
Offline
Зарегистрирован: 05.03.2019

Можно отдельную тему создать: "найди ошибку в коде". Для интересных и неочевидных моментов.

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

Можно. Создай.

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

Пока ОН не запретил :)

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

nik182 пишет:

Я б третью строку со слова inline бы начал и пофиг все не атомарности. 

ну, пофиг, так пофиг, только нажатия всё равно бы терялись :)))

Кстати, не удивлюсь, если она и так inline (компилятор любит такие шутки).

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

VladimirTsibrov пишет:

Для интересных и неочевидных моментов.

Ну, здесь-то момент достаточно очевидный.

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

-NMi- пишет:
я ещё раз убедился в "говняности" дурдуинокода и его компилятора. Как так "иногда" процессор может "потерять" значение переменной.

wdrakula, а что я говорил!? :)))

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

wdrakula пишет:
будет редко давать глюки, которые наши кулибины спишут на - "платы - гафно китайское!"! ;))
Это да. А те, кто попродвинутее, те вспомнят, что С нечестный - "wiring-гафно итальянское".

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

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

PS. Про эмбеддоров с вертикальным счётчиком забыл. С ним по дефолту все ок будет.

Ворота
Ворота аватар
Offline
Зарегистрирован: 10.01.2016

Много раз слышал заявления типа: "говно ваш С, то ли дело асм" и всегда (100% - ни одного исключения!) такие заявления исходили от людей толком не знающих ни Си, ни ассемблера, и, по гамбургскому счёту, не умеющих программировать.

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

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

Кстати, не удивлюсь, если она и так inline (компилятор любит такие шутки).

Конечно так и есть. Я же привел код, на исключением всего добра по сериалу и прерываниям, что я убрал, это и есть весь код программы. Само прерывание - отдельно. Это особенность реализации attachInterrupt().

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

sadman41 пишет:

Ну, и там еще лямбды нет, как я заметил.

Чё, правда нет? Как же я мог так облажаться-то? Позор на мою лысину!

Приношу покаянные извинения и исправляюсь:

static volatile bool buttonState = false;

void processButton(void) {
   buttonState = true;
}

void setup(void){
   Serial.begin(57600);
   Serial.println("Fun begins!");
   pinMode(2, INPUT_PULLUP);
   attachInterrupt(0, [] { return processButton; }(), FALLING);
}

//
// Если клавиша была нажата, печатаем время,
// когда мы об этом узнали.
//
void loop(void) {
   if ([] { const bool result = buttonState; buttonState = false; return result; }()) {
      Serial.print("Button is pressed: ");
      Serial.println(millis());
   }
}

 

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

sadman41 пишет:

Скоро должны подойти представители секты СТМ32 и затянуть песню про то, что все проблемы от ногодрыга авровского.

Ну я люблю СТМ32 и ЕСП. Считаю на их фоне АВР - пустой тратой времени...  хотя хобби, по-любому - трата времени ;)).

Но можно я про "проблемы ногодрыга" не буду?

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

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

sadman41 пишет:

Ну, и там еще лямбды нет, как я заметил.

Чё, правда нет? Как же я мог так облажаться-то? Позор на мою лысину!

Приношу покаянные извинения и исправляюсь:

static volatile bool buttonState = false;

void processButton(void) {
   buttonState = true;
}

void setup(void){
   Serial.begin(57600);
   Serial.println("Fun begins!");
   pinMode(2, INPUT_PULLUP);
   attachInterrupt(0, [] { return processButton; }(), FALLING);
}

//
// Если клавиша была нажата, печатаем время,
// когда мы об этом узнали.
//
void loop(void) {
   if ([] { const bool result = buttonState; buttonState = false; return result; }()) {
      Serial.print("Button is pressed: ");
      Serial.println(millis());
   }
}

 

Супер! Хотя вынужден поправить: "The fun begins!"

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

Ворота пишет:

Много раз слышал заявления типа: "говно ваш С, то ли дело асм" и всегда (100% - ни одного исключения!) такие заявления исходили от людей толком не знающих ни Си, ни ассемблера, и, по гамбургскому счёту, не умеющих программировать.

Я историю этого знаю. Если интересно?

Вкратце это пошло от ПИКов. 20 (может больше, память уже подводит) лет назад компилятор для ПИК был очень слабым и без приличного оптимизатора. Простейшие алгоримы в ПИК не лезли и "тру ембеддеры" вынужденно пользовались ассемблером, который был приличный, с нормальными макросами, "шахматами и пионистками". ;)). Хотя "тру тру" писали в кодах. ;)))))

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

wdrakula пишет:
"The fun begins!"
Thank you sir!

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

wdrakula пишет:

Но можно я про "проблемы ногодрыга" не буду?

Можно. Я же не зверь какой, чтобы запрещать.

Кстати, вот удивительное дело - два бородатых мужика, работавших на серьёзную организацию, решили что на ассемблере писать утомительно и выковали Си. А сейчас любой водитель кобылы с безаппеляционной уверенностью заявляет, что на ассемблере писать в сто крат легче, чем на нечестном. Ну и далее докУментом машет, рассказывая о том, что он 20 лет руки на клавиатуре держал и кнопка 0 у него пробита до самого пола.

BOOM
BOOM аватар
Offline
Зарегистрирован: 14.11.2018

Очень интересная задача! Жалко сразу результат выдали.... ((

mixail844
Offline
Зарегистрирован: 30.04.2012

BOOM пишет:

Очень интересная задача! Жалко сразу результат выдали.... ((

Ну результат выдали , теперь вопрос "как этих пропусков нажатия избежать ?"



 

BOOM
BOOM аватар
Offline
Зарегистрирован: 14.11.2018

В данном примере, могу предположить - Возможно глупое предположение, но если запретить прерывание перед использованием if в loop и после него разрешить прерывания?

Но я как ученик лишь могу высказаться....

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

BOOM пишет:

если запретить прерывание перед использованием if в loop и после него разрешить прерывания?

Очень грубо! У Вас программа будет половину времени с закрытми прерываниями работать, если не больше. А они может ещё кому нужны. 

BOOM
BOOM аватар
Offline
Зарегистрирован: 14.11.2018

Согласно приведенной программы - несколько микросекунд, пока if выполняется. Но вас понял. Надо еще подумать....

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

Без лямбд.

static bool isPressed(void) {
   cli();
   const bool result = buttonState;
   buttonState = false;
   sei();
   return result;
}

 

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

Да, ну вас всех, блин. Вот все "новички" cj,hfkbcm и навалились :( Чё людЯм подумать-то не даёте? Да и щё и без лямбд :(

В следующий раз выложу задачу для гуру :-)

BOOM
BOOM аватар
Offline
Зарегистрирован: 14.11.2018

У меня такая мысль была самой первой, но я предположил, что такое невозможно. Получается такое возможно?....

BOOM
BOOM аватар
Offline
Зарегистрирован: 14.11.2018

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

Да, ну вас всех, блин. Вот все "новички" cj,hfkbcm и навалились :( Чё людЯм подумать-то не даёте? Да и щё и без лямбд :(

В следующий раз выложу задачу для гуру :-)

Не, надо честно - надо две задачи. Одна для гуру, другая для начинающих. Гуру в решение задачи начинающих "лезть" нельзя, а начинающим попробовать свои силы в задаче гуру можно. Вот так справедливее будет. Не? )

asam
asam аватар
Offline
Зарегистрирован: 12.12.2018

Green пишет:

Без лямбд.

static bool isPressed(void) {
   cli();
   const bool result = buttonState;
   buttonState = false;
   sei();
   return result;
}

 

 

Ну зачем _все_ прерывания запрещать? Достаточно INT0 запретить.

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

)) Лишние телодвижения.

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

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

Да, ну вас всех, блин. Вот все "новички" cj,hfkbcm и навалились :( Чё людЯм подумать-то не даёте? Да и щё и без лямбд :(

В следующий раз выложу задачу для гуру :-)

"А чё тут думать", после того как Komandir указал на атомарность в 13, 14!)