Страница 1 из 3

stm32 и работа со стуктурами и указателями

Добавлено: Сб окт 14, 2017 19:47:34
Novosib_3000
Пытаюсь разобраться как работают структуры, и наткнулся на один непонятный момент. Возьмем к примеру, вендор производителя (stm32f4xx.h), в нем есть, кусок кода, содержащий набор регистров для работы с портами ввода-вывода.

Здесь создается структура и она же объявляется как новый тип

Код: Выделить всё

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 uint16_t BSRRL;    /*!< GPIO port bit set/reset low register,  Address offset: 0x18      */
  __IO uint16_t BSRRH;    /*!< GPIO port bit set/reset high register, Address offset: 0x1A      */
  __IO uint32_t BSRR;
  __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;
Далее следует код:

Код: Выделить всё

#define GPIOA               ((GPIO_TypeDef *) GPIOA_BASE)
Этой строчкой мы создаем указатель GPIOA на структуру типа GPIO_TypeDef.

И дальше, я не могу разобраться.

Указатель тоже своего рода переменная и должен занимать место в памяти и где-то находится, но я так и не смог найти его место положение. Вопрос, так где же он лежит ?

Re: stm32 и работа со стуктурами и указателями

Добавлено: Сб окт 14, 2017 20:17:53
uk8amk
Вовсе не так.
Процесс компиляции Си-кода проходит 2 этапа:
1. препроцессор
2. компиляция

В данном случае имеет место только первый вариант.
Препроцессор сразу подставляет вместо GPIOA указанную строку, он не создаёт указателей или каких-либо иных объектов.

Re: stm32 и работа со стуктурами и указателями

Добавлено: Сб окт 14, 2017 21:00:54
arkhnchul
[uquote="Novosib_3000",url="/forum/viewtopic.php?p=3206857#p3206857"]Здесь создается структура и она же объявляется как новый тип[/uquote]
только объявляется как тип. Переменная не создается.
[uquote="Novosib_3000",url="/forum/viewtopic.php?p=3206857#p3206857"]Этой строчкой мы создаем указатель GPIOA на структуру типа GPIO_TypeDef.[/uquote]
не создаем. После такого объявления директивы препроцессор заменит в остальной программе "GPIOA" на "((GPIO_TypeDef *) 0x094566)" - цифирь от балды, это GPIOA_BASE - что, в свою очередь, означает "обратиться к области памяти по адресу 0x94566 как к содержащей переменную типа GPIO_TypeDef"

Re: stm32 и работа со стуктурами и указателями

Добавлено: Вс окт 15, 2017 11:19:19
dosikus
[uquote="Novosib_3000",url="/forum/viewtopic.php?p=3206857#p3206857"] вендор производителя[/uquote]

Вы когда решаете применить незнакомое вам слово, хоть погуглите что оно означает...

Re: stm32 и работа со стуктурами и указателями

Добавлено: Вс окт 15, 2017 16:25:50
Novosib_3000
[uquote="dosikus",url="/forum/viewtopic.php?p=3207215#p3207215"][uquote="Novosib_3000",url="/forum/viewtopic.php?p=3206857#p3206857"] вендор производителя[/uquote]

Вы когда решаете применить незнакомое вам слово, хоть погуглите что оно означает...[/uquote]

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

Добавлено after 15 minutes 6 seconds:
[uquote="uk8amk",url="/forum/viewtopic.php?p=3206884#p3206884"]Вовсе не так.
Процесс компиляции Си-кода проходит 2 этапа:
1. препроцессор
2. компиляция

В данном случае имеет место только первый вариант.
Препроцессор сразу подставляет вместо GPIOA указанную строку, он не создаёт указателей или каких-либо иных объектов.[/uquote]

Что-то, я не до конца понимаю, а что происходит дальше, после того, как он подставил.

Добавлено after 6 minutes 30 seconds:
arkhnchul писал(а): " - цифирь от балды, это GPIOA_BASE - что, в свою очередь, означает "обратиться к области памяти по адресу 0x94566 как к содержащей переменную типа GPIO_TypeDef"
Это я уже выучил на изусть, но как происходит это обращение, я что-то до конца не понимаю.

Re: stm32 и работа со стуктурами и указателями

Добавлено: Вс окт 15, 2017 16:38:38
Zhuk72
В данном случае stm32f4xx.h - заголовочный файл целой серии МК, и он может быть написан также создателем компилятора, поэтому связывать его с производителем не нужно.

Re: stm32 и работа со стуктурами и указателями

Добавлено: Вс окт 15, 2017 16:46:23
Myp3ik
Препроцессор заменит GPIOA на ((GPIO_TypeDef *)0x40020000)

когда вы напишете конструкцию

Код: Выделить всё

uint32_t idr = GPIOA->IDR;
препроцессор заменит её на

Код: Выделить всё

uint32_t idr = ((GPIO_TypeDef *)0x40020000)->IDR;
Далее компилятор возьмет смещение поля IDR в структуре (0x10) и прибавит его к адресу структуры и соорудит что-то типа

Код: Выделить всё

uint32_t idr = *((__IO uint32_t*)0x40020010);

Re: stm32 и работа со стуктурами и указателями

Добавлено: Вс окт 15, 2017 17:26:07
Novosib_3000
[uquote="Myp3ik",url="/forum/viewtopic.php?p=3207494#p3207494"]Препроцессор заменит GPIOA на ((GPIO_TypeDef *)0x40020000)

когда вы напишете конструкцию

Код: Выделить всё

uint32_t idr = GPIOA->IDR;
препроцессор заменит её на

Код: Выделить всё

uint32_t idr = ((GPIO_TypeDef *)0x40020000)->IDR;
Далее компилятор возьмет смещение поля IDR в структуре (0x10) и прибавит его к адресу структуры и соорудит что-то типа

Код: Выделить всё

uint32_t idr = *((__IO uint32_t*)0x40020010);
[/uquote]

Спасибо, теперь картина, вроде бы проясняется.

Re: stm32 и работа со стуктурами и указателями

Добавлено: Вс окт 15, 2017 18:45:03
YS
Этой строчкой мы создаем указатель GPIOA на структуру типа GPIO_TypeDef.
Нет. Этой строчкой мы приводим GPIOA_BASE (константа-дефайн, определенная где-то выше) к типу "указатель на GPIO_TypeDef".

Проще говоря, это один из способов наложить структуру GPIO_TypeDef на область памяти, начиная с ячейки номер GPIOA_BASE.

Таким образом, компилятор будет знать, что GPIOA - синоним указателя на структуру типа GPIO_TypeDef, расположенную в памяти начиная с GPIOA_BASE.

Кстати, если что, обращение

GPIOA->ODR

эквивалентно

(*GPIOA).ODR.

Так что можно было бы написать

#define GPIOA (*((GPIO_TypeDef *)GPIOA_BASE))

и дальше писать

GPIOA.ODR.

Re: stm32 и работа со стуктурами и указателями

Добавлено: Вс окт 15, 2017 19:00:47
dosikus
[uquote="Zhuk72",url="/forum/viewtopic.php?p=3207486#p3207486"]В данном случае stm32f4xx.h - заголовочный файл целой серии МК, и он может быть написан также создателем компилятора, поэтому связывать его с производителем не нужно.[/uquote]
Неа, хэдеры периферии пишет вендор.
Это требование CMSIS, читаем спецификацию на CMSIS...

Re: stm32 и работа со стуктурами и указателями

Добавлено: Вс окт 15, 2017 19:05:27
Novosib_3000
[uquote="YS",url="/forum/viewtopic.php?p=3207564#p3207564"]
Этой строчкой мы создаем указатель GPIOA на структуру типа GPIO_TypeDef.
Нет. Этой строчкой мы приводим GPIOA_BASE (константа-дефайн, определенная где-то выше) к типу "указатель на GPIO_TypeDef".

Проще говоря, это один из способов наложить структуру GPIO_TypeDef на область памяти, начиная с ячейки номер GPIOA_BASE.[/uquote]

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

Re: stm32 и работа со стуктурами и указателями

Добавлено: Вс окт 15, 2017 19:35:16
Myp3ik
Это скорее арифметика указателей, а не приведение константы к типу. На основе констант рассчитываются указатели. У нас есть адрес
в памяти и структура описывающая смещения относительно этого адреса.

Если посмотреть на комментарий структуры

Код: Выделить всё

__IO uint32_t MODER;    /*!< GPIO port mode register,               Address offset: 0x00      */
Address offset это и есть смещение каждого поля относительно адреса в памяти. Собственно чтоб предоставить удобную форму отображения смещения эта структура и нужна, чтобы вместо

Код: Выделить всё

uint32_t idr = *((__IO uint32_t*)(0x40020000+ 0x00000010));
использовать

Код: Выделить всё

uint32_t idr = GPIOA->IDR;

Re: stm32 и работа со стуктурами и указателями

Добавлено: Вс окт 15, 2017 19:40:44
YS
Я до сих пор, до конца не могу понять, как именно происходит приведение константы к типу.
Для начала требуется осознать, что на уровне железа типов нет. Есть только огромный линейный массив байт, каждый из которых пронумерован. Интерпретировать их можно как угодно.

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

"Приведение к типу" - переопределение правила, по которому компилятор будет обращаться к байтам по указанному адресу.

int32_t x; - "компилятор, выбери какой хочешь адрес в оперативной памяти, и начиная с него зарезервируй четыре байта, которые интерпретируй в дальнейшем как целое число со знаком; а называться эти четыре байта будут x".

Компилятор понимает это, и в дальнейшем будет обращаться с этими четырьмя байтами как с целым тридцатидвухибитным числом (big endian или little endian - зависит от соглашения), причем при вычислениях будет использовать дополнительный код, чтобы учитывать знак.

Начнем с простого.

temp1 = &x; - "компилятор, запиши в temp1 стартовый адрес той области, которая x". При этом компилятор знает, что к этой области надо обращаться по правилам, адекватным для целого числа со знаком. Теперь если мы напишем

(*temp) = 500;

компилятор запишет в x (потому что в temp лежит адрес x) число 500 (0x000001F4) с учетом установленных правил. То есть, в случае little endian, запишет 0xF4 в байт с самым младшим адресом, 0x01 в следующий и обнулит остальные два.

Теперь будем приводить типы.

y = *((uint8_t *)(&x)); - "компилятор, мы договаривались, что по адресу, содержимое которого мы называли x, лежит целое тридцатидвухбитное. Теперь сделай вид, что там целое беззнаковое восьмибитное, и прочти его по этому правилу."

Компилятор прочтет один байт с адреса, по которому лежит x. А там, с учетом присвоения выше, лежит 0xF4 (244 в десятичной системе счисления). Вот это число и будет записано в y.

z = ((uint8_t *)(&x))[1]; - "компилятор, интерпретируй стартовый адрес x как указатель на массив беззнаковых целых, и излеки из него первое значение."

В z окажется 1. Понимаете, почему? :)

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

((uint8_t *)0x100) - "компилятор, понимай 0x100 как номер ячейки, начиная с которой лежит беззнаковое целое восьмибитное число."

Re: stm32 и работа со стуктурами и указателями

Добавлено: Чт окт 19, 2017 22:14:18
philosoraptor
YS Браво, просто винрарнейший ликбез по работе с памятью. :beer: Схоронил для потомков.

Re: stm32 и работа со стуктурами и указателями

Добавлено: Пт окт 20, 2017 09:05:01
YS
Я рад, что вам понравилось. :)

Ну вот, мои труды уже входят в наследие для потомков. :)))

Re: stm32 и работа со стуктурами и указателями

Добавлено: Сб окт 21, 2017 20:09:24
Novosib_3000
Я тоже написал, что мне это помогло, но почему то это не отобразилось, только сейчас заметил. Хорошая работа.

Re: stm32 и работа со стуктурами и указателями

Добавлено: Сб окт 21, 2017 20:29:32
YS
А я-то думал, зачем вы цитируете мое сообщение целиком... :) Кстати странно, что модераторы еще не задались этим же вопросом. :wink:

Re: stm32 и работа со стуктурами и указателями

Добавлено: Пн апр 01, 2019 14:48:35
Sanchosd
[uquote="YS",url="/forum/viewtopic.php?p=3211060#p3211060"]Я рад, что вам понравилось. :)

Ну вот, мои труды уже входят в наследие для потомков. :)))[/uquote]


Мне тоже понравилось!)
Скину в txt и распечатаю.
До прочтения вашего поста у меня было мнение, что "ну не умеют программисты объяснять/преподавать, не умеют, сколько раз проверяли!!!!!"
Но нет, бывают исключения :)

PS: последнее, где массив [1]- непонятно(((

Re: stm32 и работа со стуктурами и указателями

Добавлено: Пн апр 01, 2019 22:08:47
YS
Воу, два года прошло, а это еще кто-то читает. :)

Кстати там опечатка, только заметил... Присваиваю я там в "temp1", а потом рассуждаю про "temp". Но, видимо, все эти два года и так было понятно... Даже не знаю, править теперь, или нет.
последнее, где массив [1]- непонятно(((
Скобочки - синтаксический сахар (красивая запись) для инкремента указателя с его последующим разыменованием.

То есть, записи

a[x] = N;

и

*(a+x) = N;

эквивалентны.

То есть,

z = ((uint8_t *)(&x))[1]

есть то же самое, что

z = *(((uint8_t *)(&x)) + 1);

а это извлечение второго байта начиная с адреса &x.
До прочтения вашего поста у меня было мнение, что "ну не умеют программисты объяснять/преподавать, не умеют, сколько раз проверяли!!!!!"
Это, наверное, оттого, что я не программист, а электронщик. :)

Всяко, я рад, что мой пост имеет такой успех. :)

Re: stm32 и работа со стуктурами и указателями

Добавлено: Пн апр 01, 2019 22:23:22
Аlex
[uquote="Sanchosd",url="/forum/viewtopic.php?p=3606419#p3606419"]PS: последнее, где массив [1]- непонятно((([/uquote]
Всё очень просто. Имя любого массива - указатель (константа). По этому любой указатель можно интерпретировать как имя массива.
Например :

Код: Выделить всё

int *p = bla-bla-bla...
Тогда будет уазывать на адрес 2-ого элемента по указателю. Т.е. со смещением на sizeof(int).