Периферийные регистры отображения памяти с использованием массива указателей

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

Я немного запутался в отображении памяти

Из этого сообщения, если 8-битная память отображается на 0X123, тогда я могу использовать следующее

uint8_t volatile * p_reg = (uint8_t volatile *) 0x1234;

or

#define PORT 0X1234
uint8_t volatile * p_reg = (uint8_t volatile *) PORT;

или в случае массива указателей

#define PORT 0X1234
uint8_t volatile * const portsout[NUM_PORTS] =
    {
        (uint8_t*)PORTB, ....,
    };

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

uint8_t volatile * const portsout[NUM_PORTS] =
{
    (uint8_t*)&PORTB, (uint8_t*)&PORTC, (uint8_t*)&PORTD,
};

PORTB определен в заголовочном файле "avr/io.h", как это

#define PORTB   _SFR_IO8 (0x05)

Чего я не понимаю, так это необходимости & в массиве указателей??

Когда у меня возникла ошибка компиляции при использовании lpc2148, я отправил письмо автору, и в его ответном письме он упомянул

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

(uint32_t*)&IOPIN0, (uint32_t*)&IOPIN1,

может быть на самом деле

(uint32_t*)IOPIN0, (uint32_t*)IOPIN1,

в зависимости от того, как IOPIN0 и IOPIN1 определены для вашей части

Макрос IOPIN0 для lpc2148

#define IOPIN0          (*((volatile unsigned long *) 0xE0028000))

У меня нет большого опыта в C. Я знаю, что если макрос относится к памяти, то мне не нужно использовать & при определении массивов указателей. Как узнать, ссылается ли макрос (например: PORTB, IOPIN0) на адрес или значение??


person Athul    schedule 04.10.2018    source источник
comment
Это много зависит от того, что такое SFR_IO8 и что он делает со значением 0x05.   -  person Some programmer dude    schedule 04.10.2018


Ответы (2)


При написании карт регистров обычно используется метод, который оставляет регистры такими, как если бы они были переменными:

#define REGISTER (*(volatile uint8_t*)0x1234)

где левый * разыменовывает указанный адрес, что означает, что теперь вы можете использовать REGISTER точно так же, как любую простую переменную. Вот почему вам нужно написать &PORTB, который после расширения макроса заканчивается как &*pointer. И это гарантированно эквивалентно pointer на C (c17 6.5.3.2):

Унарный оператор & возвращает адрес своего операнда. /--/
Если операнд является результатом унарного оператора *, ни этот оператор, ни оператор & не оцениваются, и результат будет таким, как если бы оба они были опущены

Что касается того, нужен ли & или нет, это действительно зависит от того, как регистры определены в карте регистров. Который должен быть доступен в виде заголовочного файла, который вы можете проверить, независимо от системы.

Кстати, ваши забросы подозрительны. (uint8_t*)&PORTB не нужно. Это предполагает некоторое несоответствие квалификатора с volatile и const. В общем, всегда можно перейти от указателя к квалифицированному указателю, но не наоборот.

На основе вашего примера с IOPIN0 код должен выглядеть примерно так:

volatile uint32_t*const portsout [NUM_PORTS] =
{
  &IOPIN0, ...
};
person Lundin    schedule 04.10.2018
comment
Не могли бы вы немного объяснить несоответствие квалификаторов. Может быть, с небольшим примером - person Athul; 05.10.2018
comment
@Athul Предположим, например, что исходный указатель определен как const volatile uint8_t*, но вы хотите сохранить его в массиве volatile uint8_t*. Вы получите несовместимые указатели: вы всегда можете перейти от меньшего количества квалификаторов к большему, но не наоборот. Ошибки такого рода часто возникают, когда есть более серьезные ошибки проектирования изображения: не удалось заранее правильно определить, какие указатели указывают на аппаратные регистры (volatile*), какие указывают на данные только для чтения (const*), а какие должны быть сохранены. в ПЗУ (*const). - person Lundin; 08.10.2018
comment
const volatile uint8_t* так что, если у меня есть что-то подобное, я могу сохранить только в const volatile uint8_t* Верно?? Также volatile uint8_t* можно хранить в const volatile uint8_t* Верно??. У меня есть такая функция, как эта, void Adc_RegisterWrite(uint8_t const Address, uint8_t const Value) { uint8_t * const RegisterPointer = (uint8_t *) Address; *RegisterPointer = Value; } И компилятор выдает мне предупреждение cast to pointer from integer of different size Как правильно здесь выполнить приведение типов? - person Athul; 10.10.2018
comment
@Атул Да и да. Таким образом, броски не нужны, поэтому они подозрительны. Однако ваша функция здесь не имеет никакого смысла, поскольку это простые uint8_t, а не указатели. Возможно, вы могли бы опубликовать эту часть как отдельный вопрос (с копированием/вставкой фактического кода). - person Lundin; 10.10.2018
comment
Функция принимает переменную типа uint8_t, и в теле функции это значение, не являющееся указателем, приводится к типу указателя, верно?? Что, если переменная Address, переданная при вызове функции, на самом деле является адресом, подобным 0x28 или что-то в этом роде. - person Athul; 17.10.2018

определения для C выглядят примерно так:

#if __AVR_ARCH__ >= 100
#    define __SFR_OFFSET 0x00
#else
#    define __SFR_OFFSET 0x20
#endif

#define _SFR_IO8(io_addr) _MMIO_BYTE((io_addr) + __SFR_OFFSET)
#define _MMIO_BYTE(mem_addr) (*(volatile uint8_t *)(mem_addr))
#define PORTB   _SFR_IO8(0x05)

Это означает, что PORTB расширяется до чего-то вроде:

// presuming __AVR_ARCH__ >= 100
(*(volatile uint8_t *)(0x05 + 0x00))

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

volatile uint8_t * p = &PORTB;
person Groo    schedule 04.10.2018
comment
Вот что я сначала подумал. (uinit8_t*)&(_SFR_IO8 (0x05)) это работает как надо, но когда я пишу (uinit8_t*)0x05 код не работает. Почему?? Согласно вашему объяснению, так и должно быть, верно?? Или мне попробовать (uinit8_t*)0x0C - person Athul; 05.10.2018
comment
@Athul: как именно выглядит твоя линия? т.е. volatile uint8_t * p = (volatile uint8_t*)0x05; должно работать (если __AVR_ARCH__ не меньше 100). В зависимости от вашего компилятора вы сможете получить предварительно обработанный вывод (например, параметр -E в GCC). Это покажет вам, как выглядит результирующий код после замены всех макросов, непосредственно перед компиляцией. - person Groo; 09.10.2018
comment
uint8_t volatile * const portsout[NUM_PORTS] = { (uint8_t*)0x05, (uint8_t*)&PORTC, (uint8_t*)&PORTD, }; Я пробовал так, но не сработало, но uint8_t volatile * const portsout[NUM_PORTS] = { (uint8_t*)&(_SFR_IO8 (0x05)), (uint8_t*)&PORTC, (uint8_t*)&PORTD, }; сработало - person Athul; 09.10.2018
comment
Странно, я проверил это здесь, и предупреждений нет. Вы обязательно должны использовать -E и проверить, как на самом деле определены ваши макросы. - person Groo; 09.10.2018