Регистры ввода-вывода SAM3X8E (Arduino Due)

Как работают регистры ввода-вывода Arduino Due? На Arduino Uno просто установите DDRx, затем PINx для чтения, PORTx для записи, я хотел бы сделать то же самое с Arduino Due, но у него есть еще много регистров, таких как PIO_OWER, PIO_OSER, PIO_CODR, PIO_SODRи т. Д. Я не нахожу соответствия между регистрами Arduino Uno и Arduino Due.

Есть также некоторые полезные функции, такие как pio_clear, pio_set, pio_getи другие, все они объяснены здесь:

http://asf.atmel.com/docs/3.19.0/sam3x/html/group__sam__drivers__pio__group.html

Теперь я думаю, что понял, что делают три упомянутые функции, но не другие, например:

pio_configure (Pio *p_pio, const pio_type_t ul_type, const uint32_t ul_mask, const uint32_t ul_attribute)

Я не могу понять, что такое ul_attribute и ul_type.

, 👍11

Обсуждение

Вот класс GPIO, который реализован для AVR и SAM. Может дать подсказку о том, как использовать регистры: https://github.com/mikaelpatel/Arduino-GPIO/blob/master/src/Hardware/SAM/GPIO.h, @Mikael Patel


4 ответа


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

7

Если вы прочитали раздел 31 Таблицы данных, доступный здесь, все может стать немного яснее для вас.

Вот краткое изложение того, что я знаю:

PIO расшифровывается как параллельный ввод/вывод и предлагает функциональность для чтения и записи нескольких регистровых портов одновременно. Там, где в таблице данных упоминается регистр, например PIO_OWER, библиотека Arduino имеет макросы для доступа к ним в этом формате REG_PIO?_OWER где ? это либо A, B, C, либо D для различных доступных портов.

Я по-прежнему использую медленную функцию Arduino pinMode() для установки ввода/вывода на выводах, поскольку она делает код более читабельным, чем вызовы регистров на основе аббревиатур, такие как REG_PIOC_OWER = 0xdeadbeef, но затем использует прямые регистры для установки выводов для производительности/синхронизации. До сих пор я ничего не делал с вводом, поэтому все мои примеры основаны на выводе.

Для базового использования вы бы использовали REG_PIO?_SODR для установки высоких выходных строк и REG_PIO?_CODR для установки низких. Например, REG_PIOC_SODR = 0x00000002 установит бит 1 (пронумерованный от нуля) на PORTC (это связано с цифровым выводом 33). Все остальные контакты на PORTC остаются неизменными. REG_POIC_CODR = 0x00000002 установит бит 1 на низком уровне PORTC. Опять же, все остальные выводы останутся неизменными.

Поскольку это все еще не оптимально или синхронизировано, если вы работаете с параллельными данными, существует регистр, который позволяет записывать все 32 бита порта одним вызовом. Это REG_PIO?_ODSR, поэтому REG_PIOC_ODSR = 0x00000002 теперь установит бит 1 на высоком уровне PORTC, а все остальные биты на PORTC будут установлены на низком уровне мгновенно в одной инструкции процессора.

Поскольку маловероятно, что вы когда-нибудь окажетесь в ситуации, когда вам нужно будет установить все 32 бита порта одновременно, вам нужно будет сохранить текущее значение выводов, выполнить операцию AND, чтобы замаскировать те, которые вы хотите изменить, выполнить операцию OR, чтобы установить те, которые вы хотите установить высоко, затем выполняют запись и снова, и это не оптимально. Чтобы преодолеть это, процессор сам выполнит маскировку за вас. Существует регистр OWSR (output write status register), который маскирует любые биты, записываемые в ODSR, которые не соответствуют битам, установленным в OWSR.

Итак, теперь, если мы вызовем REG_PIOC_OWER = 0x00000002 (это устанавливает бит 1 высокого значения OWSR) и REG_PIOC_OWDR = 0xfffffffd (это очищает все биты, кроме бита 1 OWSR), а затем снова вызовем REG_PIOC_ODSR = 0x00000002, на этот раз он изменит только бит 1 PORTC, а все остальные биты останутся неизменными. Обратите внимание на то, что OWER включает любые биты, установленные в 1 в записываемом значении, и OWDR отключает любые биты, установленные в 1 в записываемом значении. Несмотря на то, что я понял это, когда прочитал его, мне все же удалось сделать ошибку кода при написании моего первого тестового кода, думая, что OWDR отключил биты, которые не были установлены в 1 в значении, которое я написал.

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

Редактировать: И еще кое-что...

Как узнать, какие биты портов соответствуют каким цифровым строкам Должного? Проверьте это: Due Pinout

,

3

Существует довольно простая эквивалентность для базового прямого доступа к выводу. Ниже приведен пример кода, который показывает, как установить цифровой вывод высоко, а затем низко. Первый - для Arduino Due, второй - для Arduino Uno/Mega/etc.

const unsigned int imThePin = 10; //e.g. digital Pin 10

#ifdef _LIB_SAM_

    //Сначала давайте получим контакт и битовую маску-это можно сделать один раз в начале, а затем использовать позже в коде (до тех пор, пока переменные находятся в области видимости
    Pio* imThePort = g_APinDescription[imThePin].pPort; 
    unsigned int imTheMask = g_APinDescription[imThePin].ulPin; 

    //Lets set the pin high
    imThePort->PIO_SODR = imTheMask;
    //And then low
    imThePort->PIO_CODR = imTheMask;

#else

    //First lets get the pin and bit mask - this can be done once at the start and then used later in the code (as long as the variables are in scope
    volatile unsigned char* imThePort = portOutputRegister(digitalPinToPort(imThePin)); 
    unsigned char imTheMask = digitalPinToBitMask(imThePin);

    //Lets set the pin high
    *imThePort |= imTheMask;
    //Now low
    *imThePort &= ~imTheMask;

#endif

Все, что необходимо для этого, должно быть включено по умолчанию - и если нет, то #include <Arduino.h> должно быть достаточно, чтобы получить его там.

На самом деле существуют функции, которые можно вызвать, как только у вас есть указатель Pio, чтобы выполнить настройку/очистку/подтягивание резисторов и т. Д., Используя немного более чистые вызовы функций. Полный список можно найти в заголовочном файле.

,

2

Это пример кода, который мигает светодиодом на выводе 33. Код позаимствован выше - большое спасибо за очень полезные объяснения :) Это начало проекта по общению с TFT-сенсорным дисплеем с сбросом 16-битных цветных пиксельных данных, которые нуждаются в быстром доступе к портам. Я думаю, что у меня есть правильный код - особенно линия, которая устанавливает низкий вывод. Светодиод радостно мигает.

void setup() 
{
  pinMode(33, OUTPUT); 
  REG_PIOC_OWER = 0x00000002; 
  REG_PIOC_OWDR = 0xfffffffd; 
}

void loop() 
{
  REG_PIOC_ODSR = 0x00000002; 
  delay(1000);             
  REG_PIOC_ODSR = 0x00000000;    
  delay(1000);   
}
,

2

Я действительно следовал приведенным выше примерам и провел много тестов, которые, как я уверен, помогут любому, кто ищет прямую адресацию регистров.

   void setup() {
  pinMode(25, OUTPUT); 
  pinMode(26, OUTPUT);
  pinMode(27, OUTPUT);
  pinMode(28, OUTPUT); 
  pinMode(29, OUTPUT);
  //REG_PIOD_SODR = 0b00000000000000000000000001001111; // written in binary form
  REG_PIOD_SODR = 0x0000004F; //written in Hexadecimal form. Note the differences. Thw two, binary and Hexadecimal are the same.
  delayMicroseconds (250000);
  //REG_PIOD_CODR = 0b00000000000000000000000001001111;
  REG_PIOD_CODR = 0x0000004F;
  delayMicroseconds (2000000);

}

void loop() 
{
  //REG_PIOD_ODSR = 0b00000000000000000000000001001111; // serts all the bits set to 1 HIGH and the rest set to 0 LOW
  REG_PIOD_SODR = 0b00000000000000000000000001001111; // sets all the bits set to 1 HIGH and leaves the rest untouched
  delay(500);             
  REG_PIOD_CODR = 0b00000000000000000000000000000100;//1
  //REG_PIOD_CODR = 0x00000004; //identical to the line above only that it is in Hexadecimal format
  //REG_PIOD_SODR = 0b00000000000000000000000000000100; // will disable the rest bits and leave only one HIGH
  delay(500);
  REG_PIOD_CODR = 0b00000000000000000000000000000010;//2
  delay(500);
  REG_PIOD_CODR = 0b00000000000000000000000001000000;//3
  delay(500);
  REG_PIOD_CODR = 0b00000000000000000000000000001000; //4
  delay(500);
  REG_PIOD_CODR = 0b00000000000000000000000000000001; //5 
  delay(500);

}

Чтобы лучше объяснить, все контакты, которые должны быть выходами, устанавливаются через pinMode, а затем очень необязательно использовать их при настройке. В этом случае я просто хотел, чтобы контакты были сделаны ВЫСОКИМИ только один раз во время НАСТРОЙКИ, а затем НИЗКИМИ перед началом цикла. Я использовал REG_PIO?_SODR (в данном случае? - это порт D, так что это REG_PIOD_SODR), который устанавливает только 1 ВЫСОКИЙ, а остальные остаются нетронутыми. Я использовал как двоичный, так и шестнадцатеричный формат. Двоичный код облегчает определение того, какие контакты следует включить. Например, бит PD0-это самый правый бит в двоичном числе. Она начинается справа налево. Например, 0b00000000000000000000000000000001, бит 1 равен PD0, PD1 будет 0b00000000000000000000000000000010 и так далее. Вы видели примеры, представленные в этом формате, 0xfffffffd x означает, что он находится в шестнадцатеричном формате, а b-в двоичном формате. Чтобы преобразовать двоичный код в шестнадцатеричный, вы можете использовать самый простой маршрут по этой ссылке

Чтобы объяснить, любое шестнадцатеричное число представляет собой 4 двоичных числа, например, Hex 0 - двоичное 0000, Hex F-двоичное 1111, Hex 4 - двоичное 0100, Hex A-двоичное 1010.

Теперь, когда это понятно, переходя к основному циклу, я использовал REG_PIOD_SODR, потому что он дает человеку свободу обращаться к нужным контактам, не касаясь других контактов на этом порту. Вы можете использовать REG_PIOD_ODSR ТОЛЬКО тогда, когда хотите установить все биты сразу. Это идеально подходит, когда вы хотите, чтобы сразу было установлено много контактов ВЫСОКО и/или НИЗКО. Это не самое лучшее, если вы хотите установить только несколько пинов ВЫСОКО/НИЗКО, а остальные оставить нетронутыми. REG_PIOD_SODR идеально подходит, когда некоторые пины нужно оставить нетронутыми. Вы не хотите устанавливать один бит ВЫСОКО, тогда все контакты будут ВЫСОКИМИ. Эта команда REG_PIOD_CODR устанавливает биты, установленные на 1, и оставляет все остальные, установленные на 0, нетронутыми.

Этот код просто делает биты, выбранные при настройке, ВЫСОКИМИ для указанного времени задержки, а затем НИЗКИМИ для данного времени. В цикле все биты, выбранные как 1, делаются ВЫСОКИМИ, а остальные нетронутыми. В следующих строках просто уменьшите выбранные биты по одному, пока все они не будут выключены, а затем процесс повторится.

Надеюсь, это упрощенное объяснение кому-то помогло.

,