Что здесь означает ((Порт *)0x41004400UL)?

Я работаю над платой разработки, на которой установлен 32-битный микротроллер на базе ARM (а именно плата Atmel SAM D21J18A). Я все еще нахожусь на этапе обучения, и мне еще многое предстоит сделать, но я действительно увлекаюсь встроенными системами.

У меня есть некоторый опыт в C. Однако этого явно недостаточно. Я просматривал коды примера проекта Atmel и не понял некоторых его частей. Вот один из них:

    #define PORT              ((Port     *)0x41004400UL) /**< \brief (PORT) APB Base Address */

Порт определяется как:

    typedef struct {
        PortGroup             Group[2];    /**< \brief Offset: 0x00 PortGroup groups [GROUPS] */
    } Port;

и PortGroup определяется как:

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 */
    RoReg8                         Reserved1[0x4];
    __IO PORT_PMUX_Type            PMUX[16];    /**< \brief Offset: 0x30 (R/W  8) Peripheral Multiplexing n */
    __IO PORT_PINCFG_Type          PINCFG[32];  /**< \brief Offset: 0x40 (R/W  8) Pin Configuration n */
    RoReg8                         Reserved2[0x20];
} PortGroup;

Итак, мы смотрим на адрес 0x41004400UL, получаем туда данные, а дальше что происходит?

Я искал это, но не нашел ничего полезного. Если у вас есть какие-либо предложения (учебники, книги и т. д.), пожалуйста, дайте мне услышать.


person Motun    schedule 07.07.2015    source источник
comment
Вы не нашли похожий вопрос. Я видел много на SO. Аналогичный вопрос задается здесь: заголовок stackoverflow.com/questions/2417195/   -  person Mohit Jain    schedule 07.07.2015
comment
and then what happens - смотрите инструкцию к вашему устройству. Для общего ознакомления вам может понравиться, например. superuser.com/q/703695/52365   -  person GSerg    schedule 07.07.2015
comment
@MohitJain Я не видел этого вопроса. Я посмотрел на это сейчас, но я до сих пор не понимаю, как работает это определение/назначение :(   -  person Motun    schedule 07.07.2015


Ответы (2)


Как правило, вы можете получить доступ к аппаратному регистру в C следующим образом:

#define PORT  (*(volatile uint8_t*)0x1234)
  • 0x1234 - адрес регистра
  • uint8_t — это тип регистра, в данном случае размером 1 байт.
  • volatile требуется для того, чтобы компилятор знал, что он не может оптимизировать такую ​​переменную, но что каждое чтение или запись в переменную, указанную в коде, должно выполняться на самом деле.
  • (volatile uint8_t*) приводит целочисленный литерал к адресу нужного типа.
  • Крайний левый * затем принимает содержимое этого адреса, так что макрос можно использовать так же, как если бы PORT был обычной переменной.

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

Используя тот же метод, вы также можете иметь другие типы данных C, чтобы напрямую соответствовать аппаратным регистрам. Например, используя удобную структуру, вы можете отобразить всю область регистров определенного периферийного устройства. Однако такой код немного опасен и вызывает сомнения, поскольку он должен учитывать такие вещи, как выравнивание/заполнение структуры и псевдонимы.


Что касается конкретного кода в вашем примере, это типичная ужасная карта регистров для конкретного аппаратного периферийного устройства (выглядит как обычный порт ввода-вывода общего назначения) на определенном микроконтроллере. Один такой зверь обычно поставляется с каждым компилятором, поддерживающим MCU.

Такие карты регистров, к сожалению, всегда написаны ужасно, совершенно не переносимыми способами. Например, два символа подчеркивания __ являются запрещенным идентификатором в C. Ни компилятору, ни программисту не разрешается объявлять такие идентификаторы (7.1.3).

Что действительно странно, так это то, что они опустили ключевое слово volatile. Это означает, что у вас есть один из этих сценариев здесь:

  • Ключевое слово volatile скрыто под определением Port. Скорее всего так и есть или
  • Карта регистров полна фатальных ошибок, или
  • Компилятор — такая ужасная хрень, что он вообще не оптимизирует переменные. Что устранит проблемы с volatile.

Я бы исследовал это дальше.

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

person Lundin    schedule 07.07.2015
comment
В заголовочном файле у них есть следующие определения: #define __I volatile, #define __O volatile, #define __IO volatile. Спасибо за ответ, сэр, я попытаюсь переварить это сопоставление регистров. Это все еще сбивает с толку новичка, хотя :) - person Motun; 07.07.2015

Ничего не происходит, потому что вы только представляете какие-то декларации. Я не совсем уверен, в чем вопрос на самом деле, но кратко объясните этот код:

  • 0x41004400UL, очевидно, является адресом в пространстве ввода-вывода (не в обычной памяти), где начинается порт (набор регистров ввода-вывода)

  • Этот Порт состоит из двух групп с аналогичным расположением одиночных регистров.

  • struct PortGroup моделирует эти регистры точно в макете, представленном на оборудовании.

Чтобы узнать значение регистров, обратитесь к документации по оборудованию.

person Community    schedule 07.07.2015