Какова цель использования смещения для получения полного адреса регистра в микроконтроллере?

Я новичок в программировании встроенных систем, и пытаюсь пробиться. Использование оценочной платы LaunchPad Stellaris LM4F120 с таблицей данных LM4F120H5QR Microcontroller Я нашел, чтобы получить полный адрес некоторых регистров, вы всегда должны добавлять смещение! что я не понимаю важность этого, поскольку вместо этого мы можем использовать полный адрес напрямую!

Например, чтобы настроить порт F (который начинается с 0x4002.5000 в 0x4002.5FFF) и его контакты (используя шину APB)

  1. Активируйте clk для этого порта, установив (бит 5) значение 1 в RCGCGPIOзарегистрировать его базовый адрес 0x400F.E000 со смещением 0x608 так что полный адрес 0x400FE608
  2. Настройте GPIODIR рег, это его базовый адрес 0x4002.5000со смещением 0x400 так что полный адрес 0x4002.5400
  3. Настройте GPIODEN рег, это его базовый адрес 0x4002.5000со смещением 0x51C так что полный адрес 0x4002.551C
  4. Настройте GPIODATA рег, это его базовый адрес 0x4002.5000с 0x3FC так что полный адрес 0x4002.50x3FC

Если я могу предположить, что это смещение здесь используется, чтобы сделать его менее подверженным ошибкам, как мы можем написать это так:

#define GPIO_PORTF_BASE 0x40025000
#define GPIO_PORTF_DATA (*((volatile unsigned long *)(GPIO_PORTF_BASE + 0x3FC)))
#define GPIO_PORTF_DIR (*((volatile unsigned long *)(GPIO_PORTF_BASE + 0x400)))
#define GPIO_PORTF_DEN (*((volatile unsigned long *)(GPIO_PORTF_BASE + 0x51C)))

Увеличивает ли использование смещения удобочитаемость и делает его более простым и неприхотливым, поскольку нам нужно только записать смещение, чтобы получить нужный регистр?


Обновить

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

например: GPIODATA управления 0-7 pins и он имеет 255 регистров, которые могут позволить нам настраивать каждый вывод отдельно и даже их комбинацию, просто добавляя смещение к базовому адресу, например, если мы хотим настроить красный светодиод, который включен Port F мы пишем по адресу base address 0x4002.5000 + offset 0x008 непосредственно.

4 ответа

Решение

Это потому, что заголовок, из которого вы скопировали эти определения, автоматически генерируется из формата описания представления системы CMSIS. Этот формат используется производителями микросхем для стандартного описания основных и периферийных элементов их микропроцессоров. Обычно вы можете скачать эти так называемые файлы.svd в каком-либо хранилище или на домашней странице производителя.

Одной из описанных периферийных устройств LM4F120H5QR будет IO-порт общего назначения F (GPIOF). Файл.svd будет содержать элемент для порта с некоторым базовым адресом, а затем подэлемент для каждого регистра, который имеет периферийное устройство с некоторым смещением.

Вы могли бы написать #define GPIO_PORTF_DATA 0x400253FC который дает вам абсолютный адрес регистра данных порта F. Это всего лишь макрос, и вам, как программисту, легче знать, что вы говорите о регистре данных какого-либо порта.

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

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

Конкретный код, который вы разместили, не имеет особого смысла. Но в общем случае вы должны сделать что-то вроде этого, чтобы обрабатывать несколько аппаратных периферийных устройств на одном чипе:

#define PORTF 0x40025000ul
...
#define GPIO_PORT_DATA(base) (*((volatile unsigned long *)(base + 0x3FCul)))
#define GPIO_PORT_DIR(base)  (*((volatile unsigned long *)(base + 0x400ul)))
#define GPIO_PORT_DEN(base)  (*((volatile unsigned long *)(base + 0x51Cul)))

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

void gpio_set (volatile unsigned long* port, uint8_t pin);

...

gpio_set (PORTF, 5);

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

Это распространенный способ написания драйверов для таких вещей, как SPI, UART, CAN, ADC и т. Д., Где вы, вероятно, имеете несколько идентичных периферийных устройств на кристалле и хотите, чтобы один и тот же код обрабатывал их все, без повторения кода.

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

  1. Потому что так написана документация.

  1. Но на самом деле никто (кроме изобретателей колеса) так не делает. Файлы STM CMSIS определяют структуры, а компилятор сам вычисляет смещения:
      #define GPIOA               ((GPIO_TypeDef *) GPIOA_BASE)

    typedef struct
    {
      __IO uint32_t MODER;    /*!< GPIO port mode register,               Address offset: 0x00      */
      __IO uint32_t OTYPER;   /*!< GPIO port output type register,        Address offset: 0x04      */
      __IO uint32_t OSPEEDR;  /*!< GPIO port output speed register,       Address offset: 0x08      */
      __IO uint32_t PUPDR;    /*!< GPIO port pull-up/pull-down register,  Address offset: 0x0C      */
      __IO uint32_t IDR;      /*!< GPIO port input data register,         Address offset: 0x10      */
      __IO uint32_t ODR;      /*!< GPIO port output data register,        Address offset: 0x14      */
      __IO uint32_t BSRR;     /*!< GPIO port bit set/reset register,      Address offset: 0x18      */
      __IO uint32_t LCKR;     /*!< GPIO port configuration lock register, Address offset: 0x1C      */
      __IO uint32_t AFR[2];   /*!< GPIO alternate function registers,     Address offset: 0x20-0x24 */
    } GPIO_TypeDef;
Другие вопросы по тегам