Arduino Due - непонятки при попытках ускорить digitalWrite

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

По измерениям функция digitalWrite на Arduino Due выполняется около 2 мкс, т.е. более полутораста тактов при том, что в среднем инструкция выполняется в среднем за такт (есть данные, что за такт выполняется в среднем 1.2-1.25 инструкции). 

В исходнике digitalWrite рабобраться непросто, не зная архитектуры Cortex-M3, наименования регистров и констант, но у меня сложилось впечатление, что туда запихнули все, что не лень, например, эмуляция режима AVR, когда для подтяжки в режиме INPUT используют digitalWrite(pin, HIGH), хотя в SAM3 подтяжка делается по-другому.

Так вот, возникло желание сделать что-то существенно более быстрое наподобие библиотеки Arhat.

В отличие от AVR в ARM можно установить нужный вывод в нужное состояние не единственнм способом, а, минимум, тремя:

1. Регистр ODSR программируется практически так же, как и в случае AVR, только ширина его 32 разряда.

2. Пара регистров SODR и CODR служат для установки или сброса выделенных разрядов по маске: в тех позициях, где установлены 1, биты изменяются на 1 при записи в SODR или 0 при записи в CODR, а в тех, что установлены в 0, состояние не меняется.

3. У этих регистров есть алиасы - у каждого из битов указанных регистров есть свой адрес. Т.е. по сути часть адресного пространства заполнена не 32-разрядными, а 1-разрядными данными. Т.е. на 1 бит отводится 4 байта адресного пространства (выравнивание на 4).

Для проверки всего этого было выяснено, что 13-й пин, на котором висит светодиод, является 27-м битом в регистре B. Соответствующие адреса нужных регистров были аккуратно вычислены и задекларированы в скетче.

Сам скетч представляет собой простейшую реализацию блинка.

#define PB_SODR ( *(unsigned long*)(0x400E1030))
#define PB_CODR ( *(unsigned long*)(0x400E1034))
#define PB_ODSR ( *(unsigned long*)(0x400E1038))
#define BB_PB_ODSR_27 (*(unsigned long*)(0x43C2076C))

void setup() {
  Serial.begin(115200);
  pinMode(13,OUTPUT);
  long t0 = micros();
  for (int i = 0; i < 1000; i++)
    delayMicroseconds(1000);
  long t1 = micros();
  Serial.println(t1-t0);
  
}

void loop() {
  unsigned long j;
  while(1) {
//    digitalWrite(13, HIGH);  // 1-st variant 240 kHz (160 kHz) ==226kHz
//    PB_SODR = (1 << 27);     // 2-nd variant                 ==1.9 MHz
    PB_ODSR    |= (1 << 27); // 3-rd variant                   ==1.45 MHz
//    BB_PB_ODSR_27 = 1;       // 4-th variant
//     delayMicroseconds(1);
//    delay(1);
     j++;
     if((j % 1000000) == 0) Serial.println(millis());
//    digitalWrite(13, LOW);   // 1-st variant
//    PB_CODR = (1 << 27);     // 2-nd variant
    PB_ODSR &= (~(1 << 27)); // 3-rd variant
//    BB_PB_ODSR_27 = 0;       // 4-th variant
//    delay(1);
//     delayMicroseconds(1);
  }
}

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

При попытке повториь должна быть раскомментирована только одна пара строк, относящихся к одному варианту, а остальные - закомментированы.

Наблюдалось следующее:

- При использовании delay или delayMicroseconds все варианты продемонстрировали работоспособность.

- При удалении из цикла delay работоспособность сохранили только 3 варианта:

а) 1-й (digitalWrite) частота сигнала примерно 0.24 МГц,

б) 2-й (SODR/CODR) частота сигнала примерно 1.9 МГц,

в) 3-й (ODSR) частота сигнала примерно 1.4 МГц,

г) 4-й вариант признаков работоспособности не обрнаружил.

При комментировании строк 25-26 (вывод счетчика в консоль) 2-й и 3-й варианты также теряют работоспособность.

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

Другой странный вывод: оказывается, запсь в регисиры SODR/CODR происходит медленнее, чем последовательность считывание-наложение_маски-запись регистра ODSR.

В общем, хотелось бы две вещи:

1. Объяснения причин всех обнаруженных странностей.

2. Советов, как добиться устойчивой работоспособности всех имеющихся вариантов.

dimax
dimax аватар
Онлайн
Зарегистрирован: 25.12.2013

andriano, если вы пишите в arduino IDE с аддоном DUE то можно не дефайнить регистры, -они все уже продефайнены в CMSIS просто немножко в другом виде, к даташитовкому названию нужно прибавить REG_ , будет вот так:

REG_PIOB_SODR|=1<<27;

-Про комментирование строк не понял -ничего не должно меняться. Всё правильно.

-Вариант "б" у вас кажется входит в противоречие с "другой странный вывод".

К слову я тоже делал подобные замеры, скорости идентичны кроме варианта с инверсией бита ODSR:

void setup() {
 __disable_irq();
REG_PIOB_PER|=1<<27; //PIO enable
REG_PIOB_OER|=1<<27; //output enable
while(1) {

// Variant 1 4,19997MHz
REG_PIOB_SODR|=1<<27; //set 1
REG_PIOB_CODR|=1<<27; //set 0

// Variant 2  4,19997MHz
//REG_PIOB_ODSR|=1<<27; 
//REG_PIOB_ODSR&=~(1<<27); 

// Variant 3  3,81815MHz
//REG_PIOB_ODSR^=1<<27;
 
}
}

void loop() {}

Про алиас битов PIO ничего не припоминаю, так что логично что вар4 не работает. Где вы прочли об этом инфу?

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

0. Как всегда издеожки: в И-нете работаю с одного компьютера, а Arduino IDE установлена на другом. Поэтому внимательно проанализирую Ваше сообщение и подробно отвечу позже.

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

2. Интересная аргументация: "я об этом ничего не знаю, поэтому логично, что не работает". Кстати, работает, но только вместе с delay().

По документу: "SAM3X / SAM3A [DATASHEET], Atmel-11057C-ATARM-SAM3X-SAM3A-Datasheet_23-Mar-15" страницы 66-68, искать bit-banding.

dimax
dimax аватар
Онлайн
Зарегистрирован: 25.12.2013

andriano, да, спасибо, нашёл. Попробовал -работает.

#define BB_PB_ODSR_27 (*(RwReg*)(0x43C2076C))
void setup() {
 __disable_irq();
REG_PIOB_PER|=1<<27; //PIO enable
REG_PIOB_OER|=1<<27; //output enable

while(1) {

//var_4 5.24997MHz
BB_PB_ODSR_27=0;
BB_PB_ODSR_27=1;

//var_5 2.79998MHz
//BB_PB_ODSR_27^=1;

}
}
void loop() {}

Итого максимальная скорость ногодрыга в Дуе -5,25Мгц,  что точно соответсвует 4 тактам при  перефирии 21МГц :)

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

Выяснил, почему иногда не работали фрагменты кода в моем исходном сообщении - в дефайнах не хватало volatile.

Попутно обнаружил еще одну странность - один и тот же код (цикл while(1)), помещенный в setup, работает быстрее (4.2МГц), чем в loop (2.7 МГц). Видать, оптимизатор по-разному обрабатывает эти функции. Хотя, с другой стороеы, мой четвертый вариант, оказавшийся самым быстрым, в обоих случаях работает одинаково (5.25МГц).

mag155
Offline
Зарегистрирован: 21.12.2017

Не могли бы вы прокоментировать эту строку 0x43C2076C. Как я понял это установка регистров. И так как на одном порту их больше 8 то по какому алгоритму их указывать?

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

Не могли бы Вы как-то переформулировать Ваш вопрос, чтобы я смог понять, чего Вы хотите?

mag155
Offline
Зарегистрирован: 21.12.2017

Вот это 0x43C2076C. Установка регистров порта?

mag155
Offline
Зарегистрирован: 21.12.2017

шестнадцатеричной системе ?

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

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

Учитесь самостоятельно добывать информацию. Тем более, что здесь - даже в Гугле искать не нужно, все и так известно, - осталось лишь прочитать и [постараться] понять. Честное слово, лучше, чем в дэйташите, я это описать не смогу (тем более, я так и не понял, что Вам нужно).

 

PS. Собственно в дэйташите тоже искать не нужно, в посте №2 уже указаны и номер страницы, и ключевые слова.

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

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

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

sadman41 пишет:

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

Уахахахахах :) :) :)

mag155
Offline
Зарегистрирован: 21.12.2017

Ок.

mag155
Offline
Зарегистрирован: 21.12.2017

Такой вопрос . На меге все понятно 

PORTB 

PORT это регистр управления пина HIGH или LOW , B Какой именно порт.

А вот в DUO Вот эта запись что значит  PIOB_CODR . Или подскажите где прочитать по этому поводу?

b707
Offline
Зарегистрирован: 26.05.2017

sadman41 пишет:

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

нет, МАГ155 не такой. Он просто будет тупо повторять этот вопрос разными словами в разных ветках, чаще всего не имеющих никакого отношения к теме :)

b707
Offline
Зарегистрирован: 26.05.2017

mag155 пишет:

Вот эта запись что значит  PIOB_CODR . Или подскажите где прочитать по этому поводу?

в гугле? :)

mag155
Offline
Зарегистрирован: 21.12.2017

Да как же не имеющих единственная ветка в которой обсуждается ускорение именно DUO. 

mag155
Offline
Зарегистрирован: 21.12.2017

Вот что в гугле нашел ?

Jeka_M
Jeka_M аватар
Offline
Зарегистрирован: 06.07.2014

mag155 пишет:

Или подскажите где прочитать по этому поводу?

В даташите на на микроконтроллер.

Страница 623, раздел 31.5.4 Output Control, раздел 31.5.5 Synchronous Data Output, раздел 31.5.7 Output Line Timings

Страница 643, описание регистра 31.7.11 PIO Controller Clear Output Data Register

Там же (в даташите) и по всем остальным регистрам.

 

mag155
Offline
Зарегистрирован: 21.12.2017

Спасибо!!! Буду разбираться.

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

mag155 пишет:

А вот в DUO Вот эта запись что значит  PIOB_CODR . Или подскажите где прочитать по этому поводу?

Вы не поверите - в исходном сообщении этой темы!

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

Не совсем понял, а почему тактовая периферии только 21Мгц .. он жеж вроде может существенно больше или я что-то спутал с другим камнем?

P.S. Пошукал даташит .. и опять какое-то уродство: из 103 рабочих ног на плате развели только 50+ .. разьемов жалко им что ли.. зато впендюрен 16U2 ценой как самолет - не знают кому всучить что ли? Не, камень интересный, на уровне 103-го, но разводить под него свою плату не буду. :)

mag155
Offline
Зарегистрирован: 21.12.2017

Было бы 103 пина Было бы оч круто.

mag155
Offline
Зарегистрирован: 21.12.2017

Да вы правы. Как то пропустил этот момент.

mag155
Offline
Зарегистрирован: 21.12.2017

Разобрался PIOB здесь именно B указывает какой порт . То есть если вот так PIOD то будет порт D. Я правильно понял ?

dimax
dimax аватар
Онлайн
Зарегистрирован: 25.12.2013

Arhat109-2 пишет:

Не совсем понял, а почему тактовая периферии только 21Мгц .. он жеж вроде может существенно больше или я

Если это про то, что я писал в #3, то это было просто наивное предположение. Судя по разбору ассемблерных команд в теме про stm32 всё оказалось всё гораздо сложнее :) По идее тут, так же как и там, самая быстаря скорость будет с командами, пишущими не в бит, а в весь порт.

 

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

Да, мне вот тоже показалось "маловато будет" .. тут оно применимо точно также, и даже ишо сильней, ибо у Атмелов кеша поболе должны быть.. Для себя пока понял только одно: или применять 8-и битники, без кешей с фиксю тактированием, или применять не ниже М4 с встроенным float и DSP, если акромя управления ещё надо арифметику (ну там, гиперболические синусы, арктангенсы, преобразования координат, БПФ и т.д. "на лету" для определения угла перехвата цели к примеру). Всё промежуточное - от лукавого. :)

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

Arhat109-2 пишет:

Для себя пока понял только одно: или применять 8-и битники, без кешей с фиксю тактированием, или применять не ниже М4 с встроенным float и DSP

Зачем мелочиться: либо Intel 4004, либо Blue Gene, все промежуточное - от лукавого.

 

PS. По собственному месячному опыту: f103 - очень симпатичный камень. Но для экспериментов заказал и f407, жду, пока прибудет.

mag155
Offline
Зарегистрирован: 21.12.2017

Всем привет!!! Подскажите какой командой считать состояние входа ? В даташиде смотрел не совсем понял .

mag155
Offline
Зарегистрирован: 21.12.2017

PIO_OSR. Как то так или я не туда копаю ?

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

Ответ зависит от того, что именно Вы подразумеваете под термином "состояние".

Если режим работы, то CRL или CRH. 

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

mag155
Offline
Зарегистрирован: 21.12.2017

Состояние я имею в виду HIGH или LOW .  В даташиде есть таблицы но не совсем пойму как ими пользоватся ?

Например условие 

if (digitalRead(3) == HIGH)

как будет выглядет ?

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

mag155 пишет:

Состояние я имею в виду HIGH или LOW .  В даташиде есть таблицы но не совсем пойму как ими пользоватся ?

Например условие 

if (digitalRead(3) == HIGH)

как будет выглядет ?

ПРавильно оно будет выглядеть так:

if (digitalRead(3))

Не нужно в коде лишних операций.

Если хотите через регистры, читайте IDR. Лучше - битбандингом.

mag155
Offline
Зарегистрирован: 21.12.2017

if( !digitalRiad(3)); А вот так тогда будет если LOW.?

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

Да.