#c #avr #cmsis
#c #avr #cmsis
Вопрос:
Итак, я просматривал Интернет в поисках ответа и ничего не получил.
Я хочу знать, используют ли определения регистров в структурном стиле для микроконтроллеров (ARM mcu, AVR mcu) RAM. Я знаю, что если создается экземпляр объекта структуры, он будет потреблять оперативную память (либо в стеке, либо иным образом).
Но как насчет определений регистров, подобных приведенным ниже, которые используются ARM в их CMSI и похожи на то, что новая серия ATtiny использует для своих определений регистров, насколько я знаю.
Используют ли они RAM-память конкретно. Я совершенно уверен, что они будут в некоторой степени потреблять пространство flash / program, но RAM?
#define PORT ((Port *)0x41008000UL) /**< brief (PORT) APB Base Address */
typedef struct {
PortGroup Group[4]; /**< brief Offset: 0x00 PortGroup groups [GROUPS] */
} Port;
typedef struct {
__IO PORT_DIR_Type DIR; /**< brief Offset: 0x00 (R/W 32) Data Direction */
__IO PORT_DIRCLR_Type DIRCLR; /**< brief Offset: 0x04 (R/W 32) Data Direction Clear */
__IO PORT_DIRSET_Type DIRSET; /**< brief Offset: 0x08 (R/W 32) Data Direction Set */
__IO PORT_DIRTGL_Type DIRTGL; /**< brief Offset: 0x0C (R/W 32) Data Direction Toggle */
__IO PORT_OUT_Type OUT; /**< brief Offset: 0x10 (R/W 32) Data Output Value */
__IO PORT_OUTCLR_Type OUTCLR; /**< brief Offset: 0x14 (R/W 32) Data Output Value Clear */
__IO PORT_OUTSET_Type OUTSET; /**< brief Offset: 0x18 (R/W 32) Data Output Value Set */
__IO PORT_OUTTGL_Type OUTTGL; /**< brief Offset: 0x1C (R/W 32) Data Output Value Toggle */
__I PORT_IN_Type IN; /**< brief Offset: 0x20 (R/ 32) Data Input Value */
__IO PORT_CTRL_Type CTRL; /**< brief Offset: 0x24 (R/W 32) Control */
__O PORT_WRCONFIG_Type WRCONFIG; /**< brief Offset: 0x28 ( /W 32) Write Configuration */
__IO PORT_EVCTRL_Type EVCTRL; /**< brief Offset: 0x2C (R/W 32) Event Input Control */
__IO PORT_PMUX_Type PMUX[16]; /**< brief Offset: 0x30 (R/W 8) Peripheral Multiplexing */
__IO PORT_PINCFG_Type PINCFG[32]; /**< brief Offset: 0x40 (R/W 8) Pin Configuration */
RoReg8 Reserved1[0x20];
} PortGroup;
ПРИМЕЧАНИЕ: Весь код, приведенный в приведенных выше блоках кода, является прямыми цитатами пользователя из EEVBlog. Это касается определений регистров, предоставляемых в CMSIS. Ссылка здесь.
РЕДАКТИРОВАТЬ: я понимаю, что микроконтроллеры имеют регистры, и доступ к этим регистрам не потребляет ОЗУ. Но моя путаница заключается в том, как ссылаются на эти регистры. Например :
// if a register address is 0x50
#define address 0x50 // This consumes no RAM as it is resolved during compilation
uint8_t addr = 0x50; // This consumes RAM because it is now a variable
// So what about this??
typedef struct {
uint8_t addr = 0x50;
} address_group;
Таким образом, доступ к самим регистрам не будет использовать пространство ОЗУ, но меня смущает метод, который мы используем для простого обращения к этим адресам, в данном случае с использованием структур.
Все регистры могут быть #define’d один за другим, которые не будут потреблять RAM, но выбор делать это в формате struct …?
Комментарии:
1. Нет, они не потребляют никакой оперативной памяти
2. Они также не потребляют flash.
3. ОЗУ не требуется.
4. Вау, 3 человека говорят, что они не потребляют ОЗУ. Итак, все ли они разрешены во время компиляции? Или на этапе предварительного процессора?
5. Обычно вы определяете указатель на этот тип данных и присваиваете указателю адрес блока регистров, отображенного в памяти. Разыменовывая указатель, вы можете напрямую обращаться к аппаратному регистру. Это то, что первая строка делает с
PORT
макросом, который даже опускает саму переменную указателя.
Ответ №1:
Для управления периферийными устройствами, такими как GPIO и UART, периферийные устройства предоставляют регистры, которые отображаются в адресное пространство памяти MCU.
Типичный диапазон адресов для таких регистров на микроконтроллерах STM32 составляет от 0x40000000 и выше. Это отдельно как от flash, так и от RAM.
В качестве примера: регистр скорости передачи данных в бодах для USART1 может находиться по адресу 0x41006008. Таким образом, считывая слово (32 бита) с этого адреса, можно считывать скорость передачи данных в бодах. И, написав на адрес, его можно изменить.
В языке Си это может выглядеть следующим образом:
*(volatile uint32_t*)0x41006008 = 115200;
Однако он гораздо более удобочитаем и эффективен, если выглядит так:
USART1.BRR = 115200;
Все определения и определения типов, показанные в вашем вопросе, объявляют типы данных (например, PortGroup
) и псевдопеременные (например, PORT
). Я называю их псевдо, потому что они не являются обычными переменными, размещенными в оперативной памяти, а скорее структурами, отображенными в память, встроенными в аппаратное обеспечение. (И объявление типа данных в любом случае никогда не потребляет память.)
Преимущество этого подхода заключается в том, что код легче писать и читать, и при этом он имеет тот же размер, что и зашифрованный код. Это решается во время компиляции, поскольку компилятор может вычислить абсолютный адрес USART1.BRR
.
Ответ №2:
Строго говоря, регистры, отображенные в память, в некотором смысле являются ОЗУ, хотя они могут обновляться как аппаратными, так и программными средствами. Они расположены на карте памяти в месте, где компоновщику не разрешено возиться. Компилятор вообще не знает о существовании области регистров.
Используя методы, подобные описанным здесь: Как получить доступ к аппаратному регистру из встроенного ПО? или, используя предварительно созданную «карту регистров» от поставщика, мы указываем компилятору на доступ к области памяти, где он понятия не имеет, что хранится.
Вы можете сообщить компилятору, например, « PortGroup
в этом месте есть структура, начиная с адреса 0x41008000UL
и далее». Затем компилятор слепо доверяет программисту и обращается к этой области через предоставленный указатель, используя объявленные типы структуры. Если эти типы соответствуют аппаратным регистрам, то все будет работать так же, как если бы вы выделили там переменные. Но на самом деле вам не нужно заставлять компоновщик выделять память в этой области, потому что все уже предусмотрено в аппаратном обеспечении.
Доступ к регистрам или доступ к любой другой форме переменной, если на то пошло, не обязательно требует ОЗУ. Учитывая любую случайную переменную int x;
, then x
сама по себе выделяется где-то, но процесс доступа к ней с x=0;
помощью etc вряд ли займет какую-либо оперативную память.
И, наконец, некоторые заблуждения: #define address 0x50
не потребляет никакой оперативной памяти не потому, что она разрешена во время компиляции, а потому, что вместо этого она потребляет ROM. Все числа, используемые программой, будут выделены где-то в исполняемом файле.
Если у вас есть uint8_t addr = 0x50;
… if(addr == something)
, оптимизирующему компилятору не обязательно выделять пространство RAM для addr
. Это может оптимизировать всю переменную. Смотрите Этот пример для x86: https://godbolt.org/z/KroKhq . Не имело никакого смысла выделять переменную, поэтому компилятор сохранил магическое число 0x50 (80 dec) в машинном коде как часть cmp
инструкции. Вы получите идентичный машинный код, если будете использовать #define
в том же примере.
Комментарии:
1.
#define address 0x50
не потребляет ПЗУ. Само объявление define не генерирует никакого кода и ничего не выделяет. Это имеет эффект только в том случае, если оно используется какx.y = address;
, тогда оно будет генерировать код (вкл. константы).2. @Codo Ну, я как бы предположил, что он будет использоваться в программе, и доверяю читателю иметь некоторый здравый смысл. Код не потребляет никакого ПЗУ, если вы также не загружаете программу в MCU…
3. Обычно программа включает заголовочные файлы с тысячами определений и будет использовать только несколько из них. Важно понимать, если и когда define генерирует код.
Ответ №3:
Нет, эти структуры не занимают оперативной памяти. Оперативная память потребляется, когда вы фактически определяете переменную с помощью кода, подобного этому
unsigned int myInteger; // takes RAM
struct foo myStruct; // takes RAM
Строки, подобные приведенным выше, сообщают вашему компилятору / компоновщику вызывать символы myInteger
myStruct
и выделять место для этих символов в области оперативной памяти вашего микроконтроллера (возможно, в вашем стеке).
С другой стороны, когда вы определяете тип структуры или создаете a typedef
, это отличается от определения переменной и не занимает никакой оперативной памяти:
struct foo { // Does not take RAM, just defines a type.
...
};
Когда вы определяете указатель на структуру, используя приведенный ниже код, вы просто определяете выражение, которое вычисляет указатель на структуру, которая уже существует в аппаратном обеспечении. Вы не делаете ничего, что должно потреблять оперативную память:
// Does not take up RAM, just defines an expression.
#define PORTA ((struct GPIO *)0x1230)
Когда вы используете PORTA в своем коде, строки кода, которые его используют, займут некоторое пространство кода. Эти строки могут занимать некоторое место в стеке при выполнении, но они не должны занимать оперативную память постоянно.