Нужно ли устанавливать функцию с атрибутом always_inline, когда функция будет работать, возможно, один раз?

c++ spi inline

Я просматриваю библиотеку для ILI9488, работающую под управлением SPI, и хочу скопировать эту библиотеку в свою версию.

В начале исходного файла для SPI_HAS_TRANSACTION есть предупреждение о том, что если библиотека будет работать в компиляторе Arduino, то она будет использовать функции SPI, в противном случае она должна запускаться из библиотек SPI другой платформы.

Ссылка на библиотеку: джарет буркетт / ILI9488

Это та часть кода, по поводу которой у меня есть вопросы:

// Если библиотека SPI имеет поддержку транзакций, эти функции
// установить настройки и защитить от помех со стороны других
// библиотеки.  В противном случае они просто ничего не делают.
#ifdef SPI_HAS_TRANSACTION
static inline void spi_begin(void) __attribute__((always_inline));
static inline void spi_begin(void) {
#if defined (ARDUINO_ARCH_ARC32)
  // максимальная скорость!
  SPI.beginTransaction(SPISettings(16000000, MSBFIRST, SPI_MODE0));
#else
    // максимальная скорость!
  SPI.beginTransaction(SPISettings(24000000, MSBFIRST, SPI_MODE0));
#endif
}
static inline void spi_end(void) __attribute__((always_inline));
static inline void spi_end(void) {
  SPI.endTransaction();
}
#else
#define spi_begin()
#define spi_end()
#endif

Мои вопросы:

  1. почему программист сначала объявляет функцию в исходном файле, а не в заголовочном файле?
  2. зачем использовать атрибуцию always_inline для функции, которая чаще всего используется один раз в файле программы и вряд ли будет использоваться много раз во время работы?
  3. что это определение означает ARDUINO_ARCH_ARC32 и где его найти? Я искал во всех файлах в папках arduino и не нашел его. Предназначен ли он для быстрых плат или для обычных плат arduino с частотой 16 МГц?

Редактировать:

Я хочу добавить еще один вопрос для той же библиотеки.

Содержание этой функции:

void ILI9488::spiwrite(uint8_t c) {

  //Serial.print("0x"); Serial.print(c, HEX); Serial.print(", ");

  if (hwSPI) {
#if defined (__AVR__)
  #ifndef SPI_HAS_TRANSACTION
    uint8_t backupSPCR = SPCR;
    SPCR = mySPCR;
  #endif
    SPDR = c;
    while(!(SPSR & _BV(SPIF)));
  #ifndef SPI_HAS_TRANSACTION
    SPCR = backupSPCR;
  #endif
#else
    SPI.transfer(c);
#endif
  } else {
#if defined(ESP8266) || defined (ARDUINO_ARCH_ARC32)
    for(uint8_t bit = 0x80; bit; bit >>= 1) {
      if(c & bit) {
    digitalWrite(_mosi, HIGH);
      } else {
    digitalWrite(_mosi, LOW);
      }
      digitalWrite(_sclk, HIGH);
      digitalWrite(_sclk, LOW);
    }
#else
    // Быстрый битбанг SPI, извлеченный из библиотеки LPD8806
    for(uint8_t bit = 0x80; bit; bit >>= 1) {
      if(c & bit) {
    //Цифровая запись (_mosi, ВЫСОКАЯ);
    *mosiport |=  mosipinmask;
      } else {
    //Цифровая запись (_mosi, НИЗКИЙ);
    *mosiport &= ~mosipinmask;
      }
      //Цифровая запись (_sclk, ВЫСОКАЯ);
      *clkport |=  clkpinmask;
      //Цифровая запись (_sclk, НИЗКИЙ);
      *clkport &= ~clkpinmask;
    }
#endif
  }
}

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

Мой вопрос здесь в том, проверяет ли #if defined (__AVR__) наличие AVR / Arduino в целом, а когда указывает SPI_HAS_TRANSACTION, означает ли это библиотеку Arduino SPI конкретно? верно ?

, 👍1


3 ответа


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

1
  1. почему программист сначала объявляет функцию в исходном файле, а не в заголовочном файле?

объявление предназначено для встроенного атрибута. функция должна использоваться только в этом cpp.

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

существует 14 случаев запуска транзакции, и она вызывается перед каждой командой связи SPI, установленной в библиотеке. смотрите мой ответ о beginTransaction

  1. что это определение означает ARDUINO_ARCH_ARC32 и где его найти? Я искал во всех файлах в папках arduino и не нашел его. Предназначен ли он для быстрых плат или для обычных плат arduino с частотой 16 МГц?

определение архитектуры создается разработчиком на основе имени папки пакета boards выбранной платы. Архитектура ARC32 - это пакет плат Intel Arduino 101.

РЕДАКТИРОВАТЬ (ответ на отредактированную часть вопроса):

Мой вопрос здесь заключается в том, проверяет ли #if defined (__AVR__) либо AVR / Arduino в целом и когда указывает SPI_HAS_TRANSACTION означает Конкретно библиотека Arduino SPI? верно ?

__AVR__ определяется в avrlibc, наборе инструментов для микроконтроллеров AVR. Arduino использует avrlibc для микроконтроллеров AVR. Не рекомендуется проверять __AVR__. Семейство микроконтроллеров AVR теперь велико с различными периферийными устройствами, поэтому проверка __AVR__ должна быть действительно ограничена вопросами процессора.

Каждая "архитектура" Arduino имеет свою собственную библиотеку SPI. Поддержка транзакций была добавлена позже, поэтому не все реализации библиотеки SPI поддерживают транзакции. Чтобы объявить доступность транзакций в библиотеке SPI, Arduino определил определение SPI_HAS_TRANSACTION.

,

Спасибо вам за ссылки ! Действительно полезно. Но у меня есть вопрос, где находятся определения? Также с какими скоростями это связано ? например, "ARC32" имеет 16 МГц, так какие другие чипы имеют 24 МГц?, @R1S8K

другие библиотеки SPI используют максимальную скорость SPI архитектуры, если требуется более высокая. архитектура (и плата) определяются как параметр -D в командной строке компилятора, созданный сборщиком arduino на основе platform.txt записи как -DARDUINO_{build.board} -DARDUINO_ARCH_{build.arch}, @Juraj

во-первых, где найти platform.txt ? во-вторых, я понял, что определения сохраняются в platform.txt файл и передается с параметром -D во время компиляции, так что компилятор собирает вещи воедино на этапе компоновки, верно?, @R1S8K

Я хочу добавить еще один вопрос в основной пост, надеюсь, вы его проверите., @R1S8K

Я уточнил ответ. platform.txt находится в папке пакета boards, поскольку она установлена Arduino IDE (пожалуйста, не задавайте дополнительных вопросов по этому поводу. это уже другая тема). вы можете прочитать документацию по платформе Arduino здесь https://arduino.github.io/arduino-cli/platform-specification/, @Juraj

спасибо за поддержку, @R1S8K


1
  1. Функция используется только в библиотеке, а не является частью "API" этой библиотеки. Что-то вроде частной функции.

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

  3. ARDUINO_ARCH_ARC32 предназначен для платы Arduino 101 (процессор Intel Curie).

,

Я отвечаю на ваши вопросы. 1. Да, программист определил защиту с двумя функциями; static inline void spi_begin (void) и static inline void spi_end (void), которые включают вызов библиотеки SPI. 2. Как сэкономить несколько байт с помощью inline? Я узнал, что это экономит тактовые циклы, но не пространство. 3. Значит, на самом деле мне не нужно использовать ARDUINO_ARCH_ARC32, когда я использую платы nano или uno? верно ?, @R1S8K

Прошло уже больше двух месяцев. Я не помню, о чем идет речь., @Gerben

Речь идет об использовании функции SPI begin, которая используется в библиотеке и переводится в встроенный режим., @R1S8K

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

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


1

Здесь я обращаюсь только к вопросу № 2. У остальных уже есть полные ответы.

Выбор того, должна ли функция быть встроенной или нет , зависит от соотношения скорости и размера. Вызов функции сопряжен с определенными затратами. Например, в AVR команда вызова занимает 4 цикла процессора, а ret - еще 4. Встраивание функции устраняет эту стоимость. С другой стороны, в итоге вы получаете столько копий функции, сколько мест, где она встроена, а это, в свою очередь, обходится во флэш-память.

Компилятор обычно достаточно умен, чтобы принять мудрое решение относительно встраивания функций. В этом случае не очевидно, какое решение он примет самостоятельно. С одной стороны, функция используется в коде несколько раз, что является веским аргументом против ее встраивания. С другой стороны, тело функции крошечное: только настройка некоторых регистров (два параметра SPIClass::beginTransaction(), включая неявное this) и переход к другой функции, поэтому стоимость встраивания невелика.

Для этой библиотеки у программиста оказалось твердое мнение по этому вопросу, и он подумал, что встраивание действительно было лучшим вариантом. Кроме того, он не подталкивал компилятор к принятию правильного решения. Именно по этой причине он использовал расширение gcc:

__attribute__((always_inline))

И нет, в этом нет необходимости, это просто выбор оптимизации.

,

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