Параллельное чтение нескольких выводов DI как одного байта/слова

digital-in

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

Я читал советы о том, что для этой цели можно объединить выводы в «порт», но я не могу найти никаких примеров. Эта возможность доступна только на определенных процессорах? Или есть какой-то способ зафиксировать входные контакты при сканировании их в пару байтов, чтобы они не менялись во время чтения?

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

Мне пришло в голову, что я могу синхронизировать показания с помощью ISR, запускаемого изменением LSB, но, увы, этот энкодер выдает код Грея. :(

, 👍-1

Обсуждение

Какой энкодер? Кажется странным, что вам нужно читать все биты в один и тот же момент. Вы не сказали, какой Arduino у вас есть, но большинство из них представляют собой 8-битные процессоры, поэтому максимум, который вы можете прочитать атомарно, составляет 8 бит., @Nick Gammon

@NickGammon Это не так уж и странно, это просто асинхронно. Я не указал, какой Arduino на случай, если есть только некоторые, которые это поддерживают, но я бы склонялся к Uno. Я понимаю, что с 8-битными портами мне придется сделать два чтения, но я могу справиться с возможностью смазывания данных только с двумя последовательными чтениями, а не с десятью., @Jim Mack

Какой энкодер? Ссылка на даташит?, @Nick Gammon

@NickGammon Извините, но я не вижу смысла. Я не прошу никого диагностировать на другом уровне или решать какие-то проблемы. Это довольно простой вопрос о параллельном чтении данных, который может иметь много применений. Может быть, мы могли бы забыть о кодировщике и думать о нем более широко...?, @Jim Mack


4 ответа


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

3

Если вы хотите прочитать несколько входных данных, вы можете получить прямой доступ к регистру порта. Для 8-битного AVR:

DDRA = 0;            // устанавливаем контакты A0-A7 в качестве входных, modeInpit() в Arduino
uint8_t data = PINA; // прочитать все входы A0-A7 (8 бит)

Для чтения более 8 бит вам потребуется 32-битный MCU/плата на базе ARM, например Arduino Due, другой Atmel SAM, MCU STM32Fxxx и т. д. Для STM32 доступна IDE, аналогичная Arduino, — http://www.stm32duino.com/

Даже на 32-битном микроконтроллере вы можете одновременно считывать только 16 GPIO (регистры GPIO 32-битные, но обрабатывают 16 GPIO).

,

Двигаемся сюда... причина, по которой я спрашиваю о примерах, заключается в том, что то, что я читал, непоследовательно. Некоторые документы, в том числе два, на которые вы ссылаетесь, используют разные имена для регистров порта. Есть ли «порт A» и инструкция PINA? Нет в большинстве документов, в которых говорится, что «порт C» предназначен для аналоговых контактов, и PINC их считывает. Кроме того, вы ссылаетесь на «обозначения GPIO_xx», которые я не могу найти в имеющихся у меня документах, чтобы ограничить двусмысленность. Любые документы для этого синтаксиса?, @Jim Mack

в мире AVR PINA и PORTA являются именами РЕГИСТРОВ; они относятся к одним и тем же физическим контактам GPIOA (ввод-вывод общего назначения), принадлежащим регистру A (контакт может обозначаться как PA или GPIOA); для чтения используется PINA, для записи используется регистр PORTA; так что они своего рода псевдонимы; то же самое касается PINB/PORTB, PINC/PORTC и т. д.; точный физический контакт и функция (в основном) разные для каждого MCU, поэтому здесь требуется таблица данных; На Atmega328 UNO PINC/PORTC (ПК) являются аналоговыми входами (в то время как на Atmega128 они являются цифровыми); GPIO - это своего рода универсальное наименование для физических контактов, ИМХО, оно предпочтительнее., @Flanker

Хорошие рисунки здесь для некоторых [Arduino GPIO](http://www.pighixxx.com/test/portfolio_category/atmega328/#prettyPhoto[gallery1368]/0/), @Flanker


2

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


8-битные процессоры AVR могут атомарно считывать только 8 бит. Это действительно называется портом. Atmega328P (используемый в Uno) организует свои порты ввода-вывода как порты B, C и D (я не знаю, куда делся A).

На плате открыты порты PORTB (биты с 0 по 5), PORT C (биты с 0 по 5) и PORT D (биты с 0 по 7) — всего 20 контактов.

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

Чтобы прочитать весь порт D, вы можете просто сделать это:

byte foo = PIND;

(Для ввода это PINx, для вывода это PORTx).


Что может помочь, так это расширитель ввода-вывода, такой как MCP23017.

Я надеюсь избежать дополнительного оборудования

Ну, жаль.


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

,

Это не нежелание раскрывать, это желание избежать отвлекающих факторов. Я знаю аппаратное обеспечение и то, как оно работает; Я вынужден использовать его; поэтому проблемная область - это «параллельное чтение данных». Спасибо за решение этого вопроса и за указание на расширитель ввода/вывода., @Jim Mack

Учитывая ответ @Flanker, можно ли объединить чтение ПОРТА D (который, как я понимаю, использует цифровые контакты) с чтением ПОРТА A (который, как я понимаю, использует аналоговые контакты), чтобы получить 16-битное значение. за два чтения?, @Jim Mack

*Я знаю оборудование и то, как оно работает;* - **ты** можешь, **я** не знаю., @Nick Gammon

*... со значением PORT A* - где вы нашли PORT A в техническом описании? Я не могу это заметить., @Nick Gammon

*это желание избежать отвлекающих факторов* - отвлекающих факторов, таких как люди, которые пытаются ответить на ваш вопрос, зная, о каком оборудовании вы говорите?, @Nick Gammon

Я не нашел ПОРТ А, он пришел из ответа @Flanker, если только я его неправильно не понял. Разве нельзя делать то, что он показывает?, @Jim Mack

*можно, а мне нет* Не надо раздеваться, это не оскорбление. Я слишком хорошо осведомлен о склонности благонамеренных и знающих людей хотеть «решить проблему» вместо «ответа на вопрос», исходя из (иногда разумного) предположения, что спрашивающий не знает, чего он действительно хочет. .или потребности., @Jim Mack

@ Джим, в моем ответе есть регистр PINA (ввод порта), а не PORTA (выход порта). Кстати, неправильное обозначение регистра от ATMEL, которое легко спутать с (физическими) значениями портов/выводов (для них лучше использовать обозначение GPIO_xx), @Flanker

@Flanker - Спасибо, я перепутал их, подумав об общем термине «порт». Если вы можете указать на какие-либо документы или пример кода, я был бы признателен., @Jim Mack

@JimMack Пожалуйста, проверьте [Прямое управление портом](https://hekilledmywire.wordpress.com/2011/02/23/direct-port-manipulation-using-the-digital-ports-tutorial-part-3/), также [ Как получить доступ к портам ввода-вывода](http://www.elecrom.com/avr-tutorial-2-avr-input-output/) и, наконец, ATmega328 [данные](http://www.atmel.com/Images/Atmel -42735-8-bit-AVR-Microcontroller-ATmega328-328P_Datasheet.pdf) :), @Flanker


0

Я имею в виду, что вы можете найти порт и вывод в порту и прочитать в portByte. Этот пример работает:

//учитывая два GPIO на порту (PortC), например: 12 (правая кнопка) & 13 (левая кнопка)
byte portByte = (*portInputRegister(digitalPinToPort(rightButtonGpio)));
rightButtonPinHigh = portByte & (digitalPinToBitMask(rightButtonGpio));
leftButtonPinHigh = portByte & (digitalPinToBitMask(leftButtonGpio));

Однако, по моему опыту, ваш вывод будет выглядеть почти так же, как если бы вы выполнили два последовательных digitalRead с благословением Arduino. Я предполагаю, что вы могли бы (в приведенном выше примере) получить инструкцию значения leftButtonHigh 1 раньше, но я не знаю, может ли регистр даже обновляться при таком разрешении.

,

digitalRead() может быть очень медленным, около 100 циклов процессора. Напротив, чтение входного регистра порта — это один цикл. И регистр обновляется с тактовой частотой процессора, то есть 16 миллионов раз в секунду на Uno., @Edgar Bonet


2

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

  1. Используемые контакты расположены последовательно как для MCU, так и для кодера (серое кодирование потребует некоторого размышления).
  2. Сгруппируйте как можно больше в одном регистре, и это будет наименьшее значение порядка.

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

uint16_t nEncoder = (PINB & 0x1F); // Пример: ваши 5 контактов являются младшими на PORTB

uint16_t nEncoder = ((PINB & 0x7C) >> 2); // Пример: если ваши 5 контактов имеют соотношение 6:2, вы извлекаете набор контактов из PORTB, а затем сдвигаете их вниз на нужное место.

затем вы читаете старшую часть

nEncoder |= ((PIND & 0x1F) << 5); // Пример: ваши 5 контактов являются младшими на PORTD. Как бы вы ни вытащили их из следующего порта, вам нужно будет убедиться, что вы сдвинули их в старшие 10 бит nEncoder, чтобы получить окончательное значение.

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

Кодирование серого цвета будет отличаться от двоичного. В приведенном выше примере предполагается двоичный код, в котором бит старшего разряда будет меняться меньше всего, а бит младшего разряда — больше всего. Кодирование Грея специально используется из-за того, насколько минимально биты изменяются между каждым изменением числа (т. е. предполагается, что они имеют только 1 изменение бита на каждое изменение числа, независимо от числа, а не 10 бит изменяются, когда двоичный счет переворачивается - т.е. минимизирует именно эту проблему). Так что для исходного постера большая часть этого не имеет значения (поскольку они сказали, что используют кодировщик кода Грея).

Но для тех, кто ищет оптимальное решение (для двоичных счетчиков или просто так), где невозможно подключить все выводы к одному порту. Разделение его между двумя регистрами с возможностью только двух операций чтения оптимизирует время чтения и минимизирует возможность смены выводов.

,