Как правило, было бы полезно, если бы вы могли предоставить MCVE , чтобы другие могли легко скомпилировать ваш код, посмотреть, как он работает, и попробовать его настроить.
Вам не нужно определять вещикак DDRA
и PA1
в вашем коде.Просто передайте соответствующую опцию компилятору, чтобы указать, какой AVR вы используете (например, -mmcu=atmega1284p
), а затем добавьте #include <avr/io.h>
вверху вашей программы, чтобы получить эти определения.И обычно нет особого смысла копировать эти определения из io.h
в вопросы о StackOverflow, поскольку они довольно стандартны.Эти определения взяты из avr-libc, поэтому, если вы действительно хотите предоставить эти подробности, вы можете просто сказать, какую версию avr-libc вы используете.
Одним из основных положений вашего вопроса является то, что кодВы отправили с массивами и макросами эквивалентно DDRA |= (1 << PA1);
.К сожалению, эта предпосылка неверна.Когда GCC видит DDRA |= (1 << PA1);
, он может фактически скомпилировать это до одной атомарной инструкции AVR, которая устанавливает бит 1 регистра DDRA.Когда GCC видит ваш код, он делает что-то намного более сложное, что заканчивается чтением, записью и изменением регистра.Таким образом, код массива тратит впустую циклы процессора и небезопасен для использования, если прерывания могут изменить регистр DDRA.
Если вы мне не верите, вы можете взглянуть на эту ссылку godbolt.org, которая сравнивает сборкудля двух методов:
https://godbolt.org/g/jddzpK
Похоже, что вы действительно можете решить эту проблему, просто добавив квалификаторы const
в свои массивы.Тогда компилятор будет знать, какие значения хранятся в ваших массивах во время компиляции, и может генерировать хороший код.
volatile uint8_t * const FOO_DDR[] = {&DDRA, &DDRA};
uint8_t const FOO[] = {PA1, PA2};
Теперь перейдем к вашему основному вопросу - как избавиться от избыточных массивов.Я не думаю, что есть простой способ сделать это, и наличие двух константных массивов в вашей программе не имеет большого значения, и в любом случае они, вероятно, будут оптимизированы во время компиляции.Что вы можете сделать, так это расширить эти массивы, чтобы они содержали записи для каждого вывода на вашем чипе.Затем, когда вы хотите записать в вывод, вы просто используете номер вывода, который является индексом для массивов (вместо того, чтобы определять новые массивы).Подавляющее большинство вашего кода будет иметь дело с этими номерами выводов и не будет беспокоиться о массивах.Вот как я бы это написал:
#include <avr/io.h>
// Here is a general GPIO library for your chip.
// TODO: expand these arrays to cover every pin on the chip
#define setAsOutput(i) { *pin_dir[i] |= (1 << pin_bit[i]); }
#define setHigh(i) { *pin_value[i] |= (1 << pin_bit[i]); }
static volatile uint8_t * const pin_dir[] = {
&DDRA, // Pin 0
&DDRA, // Pin 1
};
static volatile uint8_t * const pin_value[] = {
&PORTA, // Pin 0
&PORTA, // Pin 1
};
static const uint8_t pin_bit[] = {
PA1, // Pin 0
PA2, // Pin 1
};
// Pin definitions for your particular project.
// (e.g. pin 0 is connected to a green LED)
#define GREEN_LED_PIN 0
void nice()
{
setAsOutput(GREEN_LED_PIN);
setHigh(GREEN_LED_PIN);
}
Каждый приведенный выше вызов функции GPIO в конечном итоге компилируется в одну инструкцию по сборке.
Если вы покопаетесь в коде Arduino Core, вы будетенайти массивы так же, как это.(Но люди Arduino совершают ошибку, получая расточительный доступ к этим массивам в своих pinMode
и digitalWrite
функциях.)
Обратите внимание, что с кодом, который я предоставил выше, существует большой риск, что выслучайно пропустит пин-код, который не является константой времени компиляции, и, следовательно, компилятор не сможет его оптимизировать и произведет расточительный / небезопасный код.Это одна из причин, почему было бы лучше использовать встроенную сборку и шаблоны C ++, как это делает библиотека FastGPIO .