Форум РадиоКот https://radiokot.ru/forum/ |
|
stm32 и работа со стуктурами и указателями https://radiokot.ru/forum/viewtopic.php?f=59&t=149115 |
Страница 1 из 3 |
Автор: | Novosib_3000 [ Сб окт 14, 2017 19:47:34 ] |
Заголовок сообщения: | stm32 и работа со стуктурами и указателями |
Пытаюсь разобраться как работают структуры, и наткнулся на один непонятный момент. Возьмем к примеру, вендор производителя (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. И дальше, я не могу разобраться. Указатель тоже своего рода переменная и должен занимать место в памяти и где-то находится, но я так и не смог найти его место положение. Вопрос, так где же он лежит ? |
Автор: | uk8amk [ Сб окт 14, 2017 20:17:53 ] |
Заголовок сообщения: | Re: stm32 и работа со стуктурами и указателями |
Вовсе не так. Процесс компиляции Си-кода проходит 2 этапа: 1. препроцессор 2. компиляция В данном случае имеет место только первый вариант. Препроцессор сразу подставляет вместо GPIOA указанную строку, он не создаёт указателей или каких-либо иных объектов. |
Автор: | arkhnchul [ Сб окт 14, 2017 21:00:54 ] |
Заголовок сообщения: | Re: stm32 и работа со стуктурами и указателями |
Здесь создается структура и она же объявляется как новый тип только объявляется как тип. Переменная не создается. Этой строчкой мы создаем указатель GPIOA на структуру типа GPIO_TypeDef. не создаем. После такого объявления директивы препроцессор заменит в остальной программе "GPIOA" на "((GPIO_TypeDef *) 0x094566)" - цифирь от балды, это GPIOA_BASE - что, в свою очередь, означает "обратиться к области памяти по адресу 0x94566 как к содержащей переменную типа GPIO_TypeDef" |
Автор: | dosikus [ Вс окт 15, 2017 11:19:19 ] |
Заголовок сообщения: | Re: stm32 и работа со стуктурами и указателями |
вендор производителя Вы когда решаете применить незнакомое вам слово, хоть погуглите что оно означает... |
Автор: | Novosib_3000 [ Вс окт 15, 2017 16:25:50 ] |
Заголовок сообщения: | Re: stm32 и работа со стуктурами и указателями |
вендор производителя Вы когда решаете применить незнакомое вам слово, хоть погуглите что оно означает... Верное замечание, вендор и есть производитель. Писал, когда время уже было позднее, поэтому может быть и ошибся. И признаюсь честно, узнал это слово из ваших топиков, где вы кому то объясняли структуру проекта или что-то схожее с этим, но найти это сообщение снова не смог. А вообще нужно было написать "заголовочный файл вендора", поправте меня, если я снова ошибаюсь. Добавлено after 15 minutes 6 seconds: Вовсе не так. Процесс компиляции Си-кода проходит 2 этапа: 1. препроцессор 2. компиляция В данном случае имеет место только первый вариант. Препроцессор сразу подставляет вместо GPIOA указанную строку, он не создаёт указателей или каких-либо иных объектов. Что-то, я не до конца понимаю, а что происходит дальше, после того, как он подставил. Добавлено after 6 minutes 30 seconds: arkhnchul писал(а): " - цифирь от балды, это GPIOA_BASE - что, в свою очередь, означает "обратиться к области памяти по адресу 0x94566 как к содержащей переменную типа GPIO_TypeDef" Это я уже выучил на изусть, но как происходит это обращение, я что-то до конца не понимаю. |
Автор: | Zhuk72 [ Вс окт 15, 2017 16:38:38 ] |
Заголовок сообщения: | Re: stm32 и работа со стуктурами и указателями |
В данном случае stm32f4xx.h - заголовочный файл целой серии МК, и он может быть написан также создателем компилятора, поэтому связывать его с производителем не нужно. |
Автор: | Myp3ik [ Вс окт 15, 2017 16:46:23 ] |
Заголовок сообщения: | Re: stm32 и работа со стуктурами и указателями |
Препроцессор заменит 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);
|
Автор: | Novosib_3000 [ Вс окт 15, 2017 17:26:07 ] |
Заголовок сообщения: | Re: stm32 и работа со стуктурами и указателями |
Препроцессор заменит 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); Спасибо, теперь картина, вроде бы проясняется. |
Автор: | YS [ Вс окт 15, 2017 18:45:03 ] |
Заголовок сообщения: | Re: stm32 и работа со стуктурами и указателями |
Цитата: Этой строчкой мы создаем указатель 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. |
Автор: | dosikus [ Вс окт 15, 2017 19:00:47 ] |
Заголовок сообщения: | Re: stm32 и работа со стуктурами и указателями |
В данном случае stm32f4xx.h - заголовочный файл целой серии МК, и он может быть написан также создателем компилятора, поэтому связывать его с производителем не нужно. Неа, хэдеры периферии пишет вендор. Это требование CMSIS, читаем спецификацию на CMSIS... |
Автор: | Novosib_3000 [ Вс окт 15, 2017 19:05:27 ] |
Заголовок сообщения: | Re: stm32 и работа со стуктурами и указателями |
Цитата: Этой строчкой мы создаем указатель GPIOA на структуру типа GPIO_TypeDef. Нет. Этой строчкой мы приводим GPIOA_BASE (константа-дефайн, определенная где-то выше) к типу "указатель на GPIO_TypeDef". Проще говоря, это один из способов наложить структуру GPIO_TypeDef на область памяти, начиная с ячейки номер GPIOA_BASE. Я до сих пор, до конца не могу понять, как именно происходит приведение константы к типу. Как, с вашей точки зрения, происходит это на белее низком уровне. |
Автор: | Myp3ik [ Вс окт 15, 2017 19:35:16 ] |
Заголовок сообщения: | Re: stm32 и работа со стуктурами и указателями |
Это скорее арифметика указателей, а не приведение константы к типу. На основе констант рассчитываются указатели. У нас есть адрес в памяти и структура описывающая смещения относительно этого адреса. Если посмотреть на комментарий структуры Код: __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;
|
Автор: | YS [ Вс окт 15, 2017 19:40:44 ] |
Заголовок сообщения: | Re: stm32 и работа со стуктурами и указателями |
Цитата: Я до сих пор, до конца не могу понять, как именно происходит приведение константы к типу. Для начала требуется осознать, что на уровне железа типов нет. Есть только огромный линейный массив байт, каждый из которых пронумерован. Интерпретировать их можно как угодно. Для компилятора тип - это правило, по которому он будет интерпретировать один или несколько байт, расположенных начиная с заданного адреса. "Приведение к типу" - переопределение правила, по которому компилятор будет обращаться к байтам по указанному адресу. 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 как номер ячейки, начиная с которой лежит беззнаковое целое восьмибитное число." |
Автор: | philosoraptor [ Чт окт 19, 2017 22:14:18 ] |
Заголовок сообщения: | Re: stm32 и работа со стуктурами и указателями |
YS Браво, просто винрарнейший ликбез по работе с памятью. Схоронил для потомков. |
Автор: | YS [ Пт окт 20, 2017 09:05:01 ] |
Заголовок сообщения: | Re: stm32 и работа со стуктурами и указателями |
Я рад, что вам понравилось. Ну вот, мои труды уже входят в наследие для потомков. |
Автор: | Novosib_3000 [ Сб окт 21, 2017 20:09:24 ] |
Заголовок сообщения: | Re: stm32 и работа со стуктурами и указателями |
Я тоже написал, что мне это помогло, но почему то это не отобразилось, только сейчас заметил. Хорошая работа. |
Автор: | YS [ Сб окт 21, 2017 20:29:32 ] |
Заголовок сообщения: | Re: stm32 и работа со стуктурами и указателями |
А я-то думал, зачем вы цитируете мое сообщение целиком... Кстати странно, что модераторы еще не задались этим же вопросом. |
Автор: | Sanchosd [ Пн апр 01, 2019 14:48:35 ] |
Заголовок сообщения: | Re: stm32 и работа со стуктурами и указателями |
Я рад, что вам понравилось. Ну вот, мои труды уже входят в наследие для потомков. Мне тоже понравилось!) Скину в txt и распечатаю. До прочтения вашего поста у меня было мнение, что "ну не умеют программисты объяснять/преподавать, не умеют, сколько раз проверяли!!!!!" Но нет, бывают исключения PS: последнее, где массив [1]- непонятно((( |
Автор: | YS [ Пн апр 01, 2019 22:08:47 ] |
Заголовок сообщения: | Re: stm32 и работа со стуктурами и указателями |
Воу, два года прошло, а это еще кто-то читает. Кстати там опечатка, только заметил... Присваиваю я там в "temp1", а потом рассуждаю про "temp". Но, видимо, все эти два года и так было понятно... Даже не знаю, править теперь, или нет. Цитата: последнее, где массив [1]- непонятно((( Скобочки - синтаксический сахар (красивая запись) для инкремента указателя с его последующим разыменованием. То есть, записи a[x] = N; и *(a+x) = N; эквивалентны. То есть, z = ((uint8_t *)(&x))[1] есть то же самое, что z = *(((uint8_t *)(&x)) + 1); а это извлечение второго байта начиная с адреса &x. Цитата: До прочтения вашего поста у меня было мнение, что "ну не умеют программисты объяснять/преподавать, не умеют, сколько раз проверяли!!!!!" Это, наверное, оттого, что я не программист, а электронщик. Всяко, я рад, что мой пост имеет такой успех. |
Автор: | Аlex [ Пн апр 01, 2019 22:23:22 ] |
Заголовок сообщения: | Re: stm32 и работа со стуктурами и указателями |
PS: последнее, где массив [1]- непонятно((( Всё очень просто. Имя любого массива - указатель (константа). По этому любой указатель можно интерпретировать как имя массива. Например : Код: int *p = bla-bla-bla... Тогда Код: p[1] будет уазывать на адрес 2-ого элемента по указателю. Т.е. со смещением на sizeof(int).
|
Страница 1 из 3 | Часовой пояс: UTC + 3 часа |
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group http://www.phpbb.com/ |