Понимание синтаксиса макроса директивы препроцессора #define - PullRequest
0 голосов
/ 03 июля 2019

Следующий код взят из заголовочного файла LPC54618.h:

typedef struct {
     //...structure elements
     __IO uint32_t SDIOCLKSEL;
     //...more elements

} SYSCON_Type;

#define SYSCON_BASE         (0x40000000u)
#define SYSCON              ((SYSCON_Type *)SYSCON_BASE)
  1. Насколько я могу догадаться, смысл за строкой

    #define SYSCON ((SYSCON_Type *)SYSCON_BASE)

Я бы предположил, что он создает указатель с именем SYSCON, который указывает на переменную типа SYSCON_Type, которая хранится по адресу 0x40000000u. Это действительно то, что происходит? И есть ли какой-либо ресурс, объясняющий используемый здесь синтаксис (то есть определение указателей внутри макросов)?

  1. Когда я пытаюсь изменить значение SDIOCLKSEL напрямую, т. Е.

    SYSCON->SDIOCLKSEL = some value;

    Я получаю ошибку:

    error: expected ')' error: expected parameter declarator error: expected ')' error: expected function body after function declarator

но если я использую его внутри функции, например ::1010 *

void foo(void)
{
   SYSCON->SDIOCLKSEL = some value;  
}

ошибки нет. Это почему? Почему я не могу написать прямо в структуру?

Любой ответ с благодарностью!

Ответы [ 2 ]

0 голосов
/ 03 июля 2019
#define SYSCON_BASE         (0x40000000u)

Это просто указывает, что по физическому адресу 0x40000000.

#define SYSCON ((SYSCON_Type *)SYSCON_BASE)

Это преобразует целочисленную константу 0x40000000u в указатель на структуру с помощью приведения.Он на самом деле ничего не выделяет - фактические регистры уже выделены как аппаратные средства отображения памяти.

Проще говоря, он говорит: «по адресу 0x40000000 есть аппаратное периферийное устройство SYSCON» (что бы это ни было, какой-нибудь таймер?).Это распространенный сценарий, когда у вас есть несколько аппаратных периферийных устройств одного и того же типа внутри MCU (много SPI, ADC и т. Д.), Каждый с одинаковым расположением регистров, но найденный по разным адресам.Мы можем использовать один и тот же тип структуры для каждого такого периферийного устройства, а также один и тот же код драйвера.

Сама структура будет иметь карту памяти, которая на 100% соответствует макету регистра.Здесь важно убедиться, что заполнение / выравнивание не приводит к ошибкам, но, надеюсь, производитель микроконтроллеров подумал об этом (хотя и не принимайте это как должное).

Предполагая, что SDIOCLKSEL имеет регистрсмещение 0x10, затем, когда вы набираете SYSCON->SDIOCLKSEL = some value;, вы получаете машинный код, подобный этому (псевдо-ассемблерный код):

LOAD 0x40000000 into index register X  
LOAD 0x10 into register A
ADD A to X
MOVE some value into the address of X

(ARM получил специальные инструкции, которые могут перемещаться и т.д. на основе смещения, поэтомув действительном машинном коде может быть меньше инструкций. Последующие обращения к регистру могут оставить «X» нетронутым и использовать этот базовый адрес повторно для эффективного кода.)

Спецификатор __IO - это просто скрытие раздувания кода volatile.

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

0 голосов
/ 03 июля 2019

это очень просто.

, что он создает указатель с именем SYSCON, который указывает на переменную типа SYSCON_Type, которая хранится по адресу 0x40000000u.Это действительно то, что происходит?

Да и нет.При использовании макроса SYSCON

void foo(uint32_t value)
{
   SYSCON->SDIOCLKSEL = value;
}

препроцессор преобразуется в:

void foo(uint32_t value)
{
  ((SYSCON_Type *)0x40000000u)->SDIOCLKSEL = value;
}

, который записывает 32-разрядное без знака value в ячейку памяти по адресу 0x40000000u + смещение структурыmember.

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

Это необходимо сделать внутри функции (как и весь код на языке C)

...