Где в даташите предупреждение о ненадежности чтения PINxn?

Я использую atmega32u4 (но думаю, что это относится ко всем моделям). Вот техническое описание.

Рассмотрите следующую программу C (я использую AVR-GCC 5.4.0):

#include <avr/io.h>
int main(void)
{
  PORTB |= 1 << PB5; /* pullup */
  DDRB |= 1 << PB0; /* LED */
  if (!(PINB & 1 << PB5))
    PORTB |= 1 << PB0; /* turn on LED */
  while (1) ;
}

Есть две ситуации:

  • подключите 10-сантиметровый провод к контакту PB5 (другой конец провода находится в воздухе): в результате иногда после включения питания светодиод горит, а иногда не горит.

  • не подключайте ничего к контакту PB5: в результате после включения светодиод никогда не загорится.

У меня стандартные настройки. Ничего сложного. Если это не работает надежно, эта ошибка ДОЛЖНА быть задокументирована в таблице данных. Эта установка должна иметь дело с глупой кнопкой. Но иногда он обнаруживает нажатие клавиши, когда ее нет.

Вопрос: где в даташите есть предупреждение об этом глюке? А кто-нибудь сталкивался с таким случаем, или я первый в мире?

EDIT См. также этот вопрос.

, 👍5

Обсуждение

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

@jsotola хорошо, но мне нужно знать, где это упоминается в техническом описании (помните: это стандартная кнопка, и я сомневаюсь, что 10-сантиметровый провод может иметь какое-либо влияние, даже если «в моем районе может быть слишком много радиопомех» ), @Igor Liferenko

Этого нет в техническом описании, потому что это основная характеристика *всех* КМОП-схем, и она должна быть описана в Digital Electronics 101., @Ignacio Vazquez-Abrams

@jsotola Хорошо, укажите это в ответе, пожалуйста. Теперь (как новичок) я знаю, что внутренние подтягивания никуда не годятся. Хорошо это знать., @Igor Liferenko

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


4 ответа


Лучший ответ:

6

кто-нибудь сталкивался с таким случаем, или я первый в мире?

Вы не первый. Недавно меня укусила та же самая проблема.

Однако, если вы не находитесь рядом с необычно сильным радиоисточником, я не думаю, что это имеет какое-либо отношение к электромагнитным помехам. В моем опыт, внутреннее подтягивание совершенно надежно для чтения переключатели и кнопки, даже на макетной плате с действующими перемычками как антенны. Если вы используете pinMode() и digitalRead() вместо прямой доступ к порту, вы также обнаружите, что внутренний подтягивающий хорошо.

Прежде чем я дам вам ответ (ожидание...), давайте внимательно посмотрим, что ваш код делает. Разборка выглядит так:

main:
    sbi  PORTB, PB5 ; turn on pullup on PB5
    sbi  DDRB,  PB0 ; set LED pin as output
    sbis PINB,  PB5 ; read PB5, if it is LOW then:
    sbi  PORTB, PB0 ;   turn on LED
0:  rjmp 0b         ; hang in an infinite loop

В ситуации, когда загорается светодиод, выполняется каждая инструкция за два такта ЦП, за исключением sbis, который занимает один цикл в этом случай. Теперь давайте посмотрим на тайминги и, в частности, на состояния подтягивание и входная схема во время каждого из первых семи ЦП циклы:

cycle   instruction   pullup latch PINB5
----------------------------------------
   1   sbi PORTB, PB5   off    X     X
   2      cont.         off    X     X
   3   sbi DDRB,  PB0   on     X     X
   4      cont.         on    LOW    X
   5   sbis PINB, PB5   on     X    LOW
   6   sbi PORTB, PB0   on     X     X
   7      cont.         on     X     X

В приведенной выше таблице "X" означает "все равно". Каждая инструкция занимает эффект в начале следующего цикла ЦП, когда часы увеличиваются edge фиксирует результат инструкции на затронутые триггеры. Поэтому подтяжка включается только в начале цикла 3. Теперь, поскольку на выводе действительно был НИЗКИЙ уровень, это означает, что триггер PB5 был НИЗКИМ. пока ЦП читал его, во время цикла 5. Это в свою очередь означает что синхронизирующая защелка перед этим триггером была НИЗКОЙ во время цикл 4.

Вы ожидали, что контакт будет иметь значение ВЫСОКОЕ. Итак, давайте посмотрим: как быстро должно ли напряжение на выводе повыситься, чтобы вывод действительно читался ВЫСОКИЙ? Поскольку входная защелка прозрачна в течение первой половины каждого цикла, это означает, что напряжение должно подняться выше порога Шмитта. порог срабатывания триггера во время цикла 3 и первой половины цикла 4. Таким образом, время нарастания должно быть менее 1,5 циклов. Или скорее 1,5 цикла минус комбинированная задержка распространения PORTB5 триггер и логика, которая управляет подтягиванием от этого триггера выход. Вероятно, около 90 ns. Это коротко!

Вы, наверное, уже догадались: причина не в электромагнитном поле. помехи, это паразитная емкость. Простой расчет покажет вам что около трех пикофарад достаточно, чтобы произвести эффект, который вы видеть. Это вид паразитной емкости, которую вы найдете повсюду, даже между дорожками печатной платы вашей платы Arduino. Обратите внимание, что электромагнитный шум может добавить некоторую непредсказуемость к тому, что вы видите, поскольку штифт восприимчив к нему, когда он полностью плавает, т.е. прежде чем вы включите подтяжку, которая создает начальное напряжение (при начало цикла 3) непредсказуемо. Но как только подтягивание включено и шпилька встала, вас больше не будет волновать шум, если только он не необычайно сильный.

Решение вашей проблемы — просто подождать, пока подтяжка зарядится. паразитная емкость. Микросекундной задержки должно быть достаточно, чтобы исправить проблема. В качестве альтернативы используйте функции Arduino вместо прямого порта. доступ: они настолько медленные, что вам не потребуется дополнительная задержка.

где в даташите есть предупреждение об этом глюке?

В разделе 18.2.4 – Чтение значения контакта: «[Синхронизирующая защелка] необходимо, чтобы избежать метастабильности, если физический контакт меняет значение вблизи фронт внутренних часов, но это также приводит к задержке». (выделено мной).

Изменить: в предыдущей версии этого ответа я неправильно указал что sbi — это инструкция с одним циклом, которая требует напряжения для рост за половину цикла вместо 1,5 циклов. Исправленный тайминг все еще достаточно короткий, чтобы паразитная емкость могла быть действительным объяснением наблюдаемое поведение.


Подводя итоги:

  • Блуждающая емкость, прикрепленная к контакту, вместе с подтягивающей резистор для RC-цепи.
  • Поскольку штифт изначально плавает, его начальное напряжение равно непредсказуемо.
  • Когда подтяжка включена, напряжение начинает расти от любой начальное значение у него было 5 В.
  • Через некоторое время, которое обычно может быть порядка времени RC постоянный, входной триггер Шмитта становится ВЫСОКИМ.
  • После некоторой дополнительной задержки, вызванной синхронизатором, PINxn триггер становится ВЫСОКИМ.
  • Если ЦП считывает регистр PINx до того, как это произойдет, он получает НИЗКИЙ уровень. чтение.
,

Не могли бы вы в двух словах объяснить, что это за "защелка" и "синхронизатор"? Или порекомендуйте ресурс, который объясняет это более понятно, чем техническое описание?, @Igor Liferenko

@IgorLiferenko: Это общепринятое знание электроники. Защелка представляет собой [D-защелку](https://en.wikipedia.org/wiki/Flip-flop_%28electronics%29#Gated_D_latch), аналогичную D-триггеру, но чувствительную к уровням тактовых импульсов, а не к фронтам тактовых импульсов. Синхронизатор синхронизирует входной сигнал с внутренними часами, чтобы предотвратить метастабильность, то есть «неопределенные» состояния между НИЗКИМ и ВЫСОКИМ. Это нетривиальная тема. Вы можете попробовать выполнить поиск в Интернете или воспользоваться этим [подробным руководством](http://webee.technion.ac.il/~ran/papers/Metastability-and-Synchronizers.IEEEDToct2011.pdf) (предупреждение: не легкое чтение)., @Edgar Bonet

Подводя итог: задержки от входной цепи синхронизации метастабильности иногда достаточно для паразитная емкость стабилизируется, а иногда и нет. И паразитная емкость имеет больше шансов (при прочих равных условиях) повлиять на входной вывод, когда считывание вывода задерживается на 1/2 цикла, чем когда на 1,5 цикла. Это правильно?, @Igor Liferenko

@IgorLiferenko: Я не понял твоего резюме, поэтому добавил свое., @Edgar Bonet


2

Вы сделали антенну из 10-сантиметрового провода и принимаете радиоволны, которые преобразуются в напряжения на входном контакте. Вот почему у вас нет такой проблемы, когда провод не подключен.

Согласно www.csgnetwork.com/freqwavelengthcalc.html, ваш 10-сантиметровый провод улавливает частоты 750 МГц на основе расчет четверти длины волны.

В техническом описании указано, что внутреннее сопротивление подтягивания составляет примерно от 20 000 до 50 000 Ом. Как упоминалось в комментариях, это довольно слабо.

Я вижу две вещи, которые вы можете сделать:

  • Добавьте более мощный подтягивающий резистор (достаточно от 1 до 5 кОм).
  • Добавьте развязывающий/шунтирующий конденсатор.

Причина, по которой этого нет в техническом описании, заключается в том, что это общие знания в области электроники/физики, а не характеристика самого микроконтроллера.

,

Есть ли какое-то объяснение тому факту, что этот эффект проявляется только сразу после включения подтягивания, а не после задержки (я проверял 10 мс)?, @Igor Liferenko

@IgorLiferenko, сколько тебе объяснений? Радиоволны отражаются металлами и могут быть непредсказуемыми. Возможно, воздух сухой и заряжен. Внутренний мультиплексор может иметь влияние. Насколько научным был ваш тест? Вы пробовали это с другим компьютером и другой платой Arduino в другом месте? Вы сделали свою собственную схему с atmega32u4, вы забыли развязать vcc или кварц и конденсаторы 22pf не близки к atmega? Возможно, слишком низкое напряжение или ошибка заземления. Я могу придумать около 20 объяснений, а другие, возможно, еще 80., @Jot

@Jot Если вам интересно, попробуйте простую настройку, как я описал в OP - я уверен, что вы получите такие же результаты. Я просто думал, что кто-то уже сталкивался с такой проблемой... Теперь я уверен, что единственный способ узнать, что происходит, это взять осциллограф..., @Igor Liferenko

@IgorLiferenko Да, смотрите мой ответ., @Jot


3

Тест с Arduino Uno.

Я использовал разъем USB для включения Arduino Uno. Светодиод находится на PB5, поэтому я поменял местами PB0 и PB5. Поэтому мой тест не такой.

#define LED_PB  PB5   // контакт 13, встроенный светодиод
#define INP_PB  PB0   // контакт 8, антенна

void setup(void)
{
  noInterrupts();
  PORTB |= 1 << INP_PB; /* pullup */
  DDRB |= 1 << LED_PB; /* LED */
  if (!(PINB & 1 << INP_PB))
    PORTB |= 1 << LED_PB; /* turn on LED */
  while (1) ;
}

void loop()
{
}

Используя его рядом с моим компьютером и телефоном, индикатор иногда остается включенным. Я даже могу найти место за монитором моего компьютера (с подсветкой CCFL), где всегда горит светодиод.

Затем я вышел на улицу, подальше от металлов, Wi-Fi и так далее. Влажность воздуха на улице в данный момент составляет 70%, что является высоким показателем. Ставлю на деревянный стол, так пластиком заряда нет. Я использовал аккумулятор для питания Arduino Uno. Затем светодиод никогда не загорается.

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

@IgorLiferenko, @sa_leinad дал правильный ответ. То, что вы заметили, это нормально. Это совершенно нормально. Такое поведение можно ожидать. Будет хуже, если ваша схема плохо спроектирована.
Осциллограф не поможет. Как только вы подключили осциллограф, вы что-то изменили. Также будет очень трудно обнаружить это очень короткое время (менее микросекунды).

На мой взгляд, влияние радиоволн — это только половина проблемы. Я думаю, что электрический заряд воздуха и емкостная связь провода с воздухом могут иметь больший эффект.

,

-1

На самом деле это задокументировано в таблице данных и проиллюстрировано логической схемой типичного GPIO. Чтобы увидеть, что на самом деле происходит за вашим скомпилированным кодом, вам нужно посмотреть на сгенерированный ассемблерный код и к каким регистрам осуществляется доступ:

Когда вы меняете один бит порта ввода-вывода, вы либо изменяете 8-битный регистр, на который этот бит отображается, с помощью сигнала WRx, либо только 1-битный регистр только для вывода, используя сигнал WPx. Когда вы используете сигнал WPx для установки или сброса бита, вам не нужно знать состояния других контактов, поэтому на этом обсуждение заканчивается. Однако, когда вы используете сигнал WRx для ИЗМЕНЕНИЯ одного бита, вам нужно знать состояния других 7 битов, что означает, что вам нужно прочитать 8-битный порт. Вы можете прочитать LATCH порта, используя сигнал RRx, или вы можете прочитать фактические физические выводы, используя сигнал RPx. Теперь при использовании сигнала RRx никакая нагрузка или шум на самих контактах не повлияют на то, что считывается с выхода защелки порта, но при использовании сигнала RPx, если за предыдущей записью в порт следует чтение порт, любые паразитные помехи или шум на выводах OUTPUT могут привести к тому, что вывод, который, как вы думаете, был только что установлен на единицу, внезапно станет нулем.

Причина, по которой это происходит, заключается в конвейерной обработке кодов операций. Запись в порты происходит в конце такта последней инструкции, но чтение происходит в начале, так что практически нет времени между установкой этого бита в 1 и изменением какого-либо другого бита (способ чтения-изменения-записи), поэтому этот бит по-прежнему равен нулю, когда он читается, а затем сразу же записывается как ноль.

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

,

Различие WRx и WPx не имеет ничего общего с тем, записываете ли вы один бит или весь регистр: WRx — это сигнал для записи триггера PORTxn, тогда как WPx — для записи PINxn (который фактически используется для переключения PORTxn). И обратите внимание, что OP действительно включал задержку в два цикла между записью и чтением., @Edgar Bonet

Вы, по-видимому, упустили весь смысл: когда чтение-модификация-запись считывает с контактов, а затем записывает защелку, шум или задержка на выходах могут быть перезаписаны на выход и, возможно, непреднамеренно изменять состояние выхода. Добавление одного цикла, двух циклов или тысячи циклов задержки может быть недостаточным, и понимание механизма этой неопределенности — единственный способ построить надежные платформы, которые работают предсказуемо при ЛЮБЫХ обстоятельствах., @John Taylor

Вы также упустили мою мысль. Я говорил вам о **фактической ошибке** в вашем ответе. Соответствующий ответ в этом случае - отредактировать ответ и исправить ошибку. Ваша точка зрения, которую я до сих пор не понимаю, касается того, что «когда чтение-модификация-запись читает с контактов, а затем записывает защелку [...]». Вы действительно имеете в виду «читает из PINx и записывает в PORTx»? Если да, то это неважно. Никто этого не делает. Конечно не ОП., @Edgar Bonet