Форум программистов, компьютерный форум, киберфорум
Наши страницы
Микроконтроллеры ARM, Cortex, STM32
Войти
Регистрация
Восстановить пароль
 
Рейтинг 4.71/21: Рейтинг темы: голосов - 21, средняя оценка - 4.71
omdriyt
0 / 0 / 0
Регистрация: 20.01.2013
Сообщений: 54
1

[STM32][C++] Хранение переменных

13.10.2016, 12:32. Просмотров 4315. Ответов 17
Метки нет (Все метки)

Держать виртуалку с виндой ради uKeilа стало лениво, поэтому на днях задался сборкой тулчейна под гентой. Тулчейн собран, начал собирать проект. И пошло-поехало со скриптом линковки - сперва даже таблицу прерываний не писало :)
В итоге добился вроде нормальной работы на простых линейных программах (RisetHomdler -> main). Но при добавлении глобальной переменной получаю фейл. При анализе дизасма увидел следующее:
Код
int32_t f = 0;

int main()
{
80002bc:   b570         push   {r4, r5, r6, lr}
80002be:   4b0c         ldr   r3, [pc, #48]   ; (80002f0 <main+0x34>)
80002c0:   4a0c         ldr   r2, [pc, #48]   ; (80002f4 <main+0x38>)
80002c2:   447b         add   r3, pc
f = 0;
while (1)
{
++f;
if (f > 1000000)
80002c4:   4d08         ldr   r5, [pc, #32]   ; (80002e8 <main+0x2c>)
80002c6:   589c         ldr   r4, [r3, r2]
{
IO::cpu_on();
}
if (f > 2000000)
80002c8:   4e08         ldr   r6, [pc, #32]   ; (80002ec <main+0x30>)
#include "ISRstm32f10x_md.h"

int32_t f = 0;

int main()
{
80002ca:   2300         movs   r3, #0
f = 0;
while (1)
{
++f;
80002cc:   3301         adds   r3, #1
if (f > 1000000)
80002ce:   42ab         cmp   r3, r5
int main()
{
f = 0;
while (1)
{
++f;
80002d0:   6023         str   r3, [r4, #0]
if (f > 1000000)
80002d2:   ddfb         ble.n   80002cc <main+0x10>
{
IO::cpu_on();
80002d4:   f7ff ffc6    bl   8000264 <_ZN2IO6cpu_onEv>
80002d8:   6823         ldr   r3, [r4, #0]
}
if (f > 2000000)
80002da:   42b3         cmp   r3, r6
80002dc:   ddf6         ble.n   80002cc <main+0x10>
{
IO::cpu_off();
80002de:   f7ff ffc7    bl   8000270 <_ZN2IO7cpu_offEv>
f = 0;
80002e2:   2300         movs   r3, #0
80002e4:   6023         str   r3, [r4, #0]
80002e6:   e7f0         b.n   80002ca <main+0xe>
80002e8:   000f4240    .word   0x000f4240
80002ec:   001e8480    .word   0x001e8480
80002f0:   17fffd3a    .word   0x17fffd3a
80002f4:   00000000    .word   0x00000000
Исходный код для фрагмента:
Код
int32_t f = 0;

int main()
{
f = 0;
while (1)
{
++f;
if (f > 1000000)
{
IO::cpu_on();
}
if (f > 2000000)
{
IO::cpu_off();
f = 0;
}
}
}
Если переменную сделать локальной, всё ОК, светодиод мигает:
Код
int main()
{
80002bc:   b570         push   {r4, r5, r6, lr}
int32_t f = 0;
while (1)
{
++f;
if (f > 1000000)
80002be:   4d06         ldr   r5, [pc, #24]   ; (80002d8 <main+0x1c>)
{
IO::cpu_on();
}
if (f > 2000000)
80002c0:   4e06         ldr   r6, [pc, #24]   ; (80002dc <main+0x20>)
int main()
{
int32_t f = 0;
while (1)
{
++f;
80002c2:   2401         movs   r4, #1
80002c4:   3401         adds   r4, #1
if (f > 1000000)
80002c6:   42ac         cmp   r4, r5
80002c8:   ddfc         ble.n   80002c4 <main+0x8>
{
IO::cpu_on();
80002ca:   f7ff ffcb    bl   8000264 <_ZN2IO6cpu_onEv>
}
if (f > 2000000)
80002ce:   42b4         cmp   r4, r6
80002d0:   d1f8         bne.n   80002c4 <main+0x8>
{
IO::cpu_off();
80002d2:   f7ff ffcd    bl   8000270 <_ZN2IO7cpu_offEv>
80002d6:   e7f4         b.n   80002c2 <main+0x6>
80002d8:   000f4240    .word   0x000f4240
80002dc:   001e8481    .word   0x001e8481
Насколько я понял, в случае глобальной переменной линкер размещает её в флеш-памяти, откуда возможно только чтение...
Скрипт линкера
0
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
13.10.2016, 12:32
Ответы с готовыми решениями:

"Наложение" переменных. STM32, IAR, возможно?
Использую IAR. После дополнения программы (различными буферами и переменными) для STM32F100C8, мое...

Хранение переменных в NOR Flash.
Можно ли сделать запись и чтение переменных в NOR Ftosh M29W128 подключенную к STM32F429 по FMC...

Чем связать STM32 + STM32 в одном корпусе?
Есть 2 платы: (1) - STM32 (Вывод на LCD + запись SD) и (2) - STM32F4 (обработка сигналов +...

Общение с барометром MS5540 на STM32 (Arduino to STM32)
Получил от китайца сей аппарат, пошел тут же гуглить что нибудь готовое, но не тут то было....

STM32 - STM32 I2C
*****ПРОБЛЕМА ИСПРАВЛЕНА****ОТВЕТ НИЖЕ , НЕ ВЧИТЫВАЙТЕСЬ В КОД ПРОГРАММЫ,УТОНЕТЕ)), НУ ЕСЛИ ТОЛЬКО...

17
x893
0 / 0 / 0
Регистрация: 07.02.2106
Сообщений: 886
13.10.2016, 13:29 2
Этого не может быть никогда. Если переменные размещать во флэш - это мировая катастрофа.
0
omdriyt
0 / 0 / 0
Регистрация: 20.01.2013
Сообщений: 54
13.10.2016, 16:30 3
Покопал дизасм с глобальной переменной и увидел там такое:
Код
 800038e:   4b0c         ldr   r3, [pc, #48]   ; r3=0x17FFFC6E
8000390:   4a0c         ldr   r2, [pc, #48]
8000392:   447b         add   r3, pc                 ; r3=0x20000000
....
8000396:   589c         ldr   r4, [r3, r2] ; получаем левак в r4
...
80003a0:   6023         str   r3, [r4, #0] ; сохраняем переменную по адресу в r4, получаем HordFault
откуда вылезла эта загрузка данных по адресу 8000396? Пути компилятора неисповедимы...

При сборке с -O0 получаем следующее:
Код
 8000110:   4c13         ldr   r4, [pc, #76]   ; r4=0x17FFFEEE
8000112:   447c         add   r4, pc                       ; r4=0x20000000
...
++f;
800011e:   4b11         ldr   r3, [pc, #68]   ; r3=0x0
8000120:   58e3         ldr   r3, [r4, r3]        ; r3=[0x20000000]
8000122:   681b         ldr   r3, [r3, #0]       ; r3=[r3] - опять левак!!!
8000124:   3301         adds   r3, #1
8000126:   4a0f         ldr   r2, [pc, #60]   ; (8000164 <main+0x58>)
8000128:   58a2         ldr   r2, [r4, r2]
800012a:   6013         str   r3, [r2, #0]
Такое ощущение, что компилятор воспринял эту переменную как указатель на переменную =/
0
miyvir
0 / 0 / 0
Регистрация: 27.06.2010
Сообщений: 405
13.10.2016, 16:33 4
в случае глобальной переменной из флеша грузится адрес переменной f в оперативке. В наборе инструкцие Thumb нет команды загрузки 32-битного адреса в регистр, поэтому компилятор размещает после main таблицу констант в т.ч. адресов. Так что грузит он f как раз с адреса 0x20000000.
0
13.10.2016, 16:33
omdriyt
0 / 0 / 0
Регистрация: 20.01.2013
Сообщений: 54
13.10.2016, 16:40 5
miyvir, По дизасму видно, что программа грузит из флеша адрес, по которому опять берет адрес - в этом и заключается проблема, ведь нам нужно из таблицы получить адрес и по нему читать/писать переменную.
А программа по факту берет адрес из таблицы, читает по нему значение, которое является адресом переменной, с которым дальше и работает. Первое чтение адреса в этом случае считывает фактический адрес переменной, а дальнейшее уже я не понимаю...
Или я что-то перемудрил с кодом =/
0
miyvir
0 / 0 / 0
Регистрация: 27.06.2010
Сообщений: 405
13.10.2016, 16:53 6
r2 == 0, оно отсюда читается:
80002f4: 00000000 .word 0x00000000
это смещение переменной f относительно начала блока глобальных переменных.
Всё правильно.
Там точно HordFault?
0
omdriyt
0 / 0 / 0
Регистрация: 20.01.2013
Сообщений: 54
13.10.2016, 17:02 7
Значение переменной берется по адресу из r4. которая берется из ячейки по адресу r3+r2:
Код
80002c6:   589c         ldr   r4, [r3, r2]
Но ведь у нас сама переменная хранится по адресу r3+r2. а не её адрес!
0
OVY-srok
0 / 0 / 0
Регистрация: 26.03.2015
Сообщений: 316
13.10.2016, 17:33 8
Объявление переменной с её значением int32_t f = 0; помещает её в секцию загрузки из флеша в рам, одновременно делая её статической глобальной. Функции которые берут такую переменную по указателю - обязаны её сохранять, даже в случае промежуточных!!! вычислений.
В случае прямого использования такой переменной в функции, все промежуточные состояния сохраняются по месту хранения. Это кстати та самая грабля, на которую наступают все любители создавать миллион уникальных глобальных переменных.
Обойти препятствие очень просто, создаём внутреннюю переменную, сначала копируем в неё состояние глобальной переменной, а если требуется наследие - сохраняем обратно.

И ещё, глобальная переменная из одной буквы - это как русская рулетка с одним отсутствующем патроне в барабане револьвера.
0
x893
0 / 0 / 0
Регистрация: 07.02.2106
Сообщений: 886
13.10.2016, 17:50 9
Немного не так, но пойдет.
есть еще static, volatile и прочее для упрощения жизни.
Присвоение = 0, не делает копирование из флэш так как stort код (если не стоит атрибута noinit).
Давно пользуюсь объявлением переменных в структуру и тогда используется указатель и смещения.

Но в любом случае можно в окне disassembler посмотреть выполнение.
Особенно удобно это делать в симуляторе.
Многое станет понятнее.
0
omdriyt
0 / 0 / 0
Регистрация: 20.01.2013
Сообщений: 54
13.10.2016, 18:05 10
Цитата Сообщение от x893
Немного не так, но пойдет.
есть еще static, volatile и прочее для упрощения жизни.
Присвоение = 0, не делает копирование из флэш так как stort код (если не стоит атрибута noinit).
Давно пользуюсь объявлением переменных в структуру и тогда используется указатель и смещения.

Но в любом случае можно в окне disassembler посмотреть выполнение.
Особенно удобно это делать в симуляторе.
Многое станет понятнее.
Этот пост вообще мимо и не о том - я пытаюсь заставить код работать, а вы пошли с другого хода - как надо правильно писать код. Или Вы для мелкой проверки, TTL которой исчисляется парой минут, пишите over дофига строк? ИМХО, проще вот такой код на пару строчек тестить, чем городить огород.
Цитата Сообщение от x893
Немного не так, но пойдет.
есть еще static, volatile и прочее для упрощения жизни.
Присвоение = 0, не делает копирование из флэш так как stort код (если не стоит атрибута noinit).
Давно пользуюсь объявлением переменных в структуру и тогда используется указатель и смещения.

Но в любом случае можно в окне disassembler посмотреть выполнение.
Особенно удобно это делать в симуляторе.
Многое станет понятнее.
У меня нет init-секций, МК стартует сразу в мой код. Все инициализации делаю сам. да даже эта переменная вручную обнуляется при входе в main, вообще-то, так что глобальное обнуление просто игнорируется...
У меня проблема не со значением переменной, а с её хранением.

PS: В общем, видимо, мы с вами друг друга не понимаем... Пошел дальше копаться в АСМе и GDB...
0
MostirOtixiy
0 / 0 / 0
Регистрация: 24.02.2010
Сообщений: 804
13.10.2016, 19:11 11
Цитата Сообщение от omdreil
У меня нет init-секций, МК стартует сразу в мой код. Все инициализации делаю сам. ...
В таком разе простое объявление переменной с инициализацией у вас просто тупо не работает.
Такая конструкция
Код
int a = 10;
a++;
if( a == 11 )
{
чтото
}
у вас выполняться будет раз в десятилетие, когда звезды сойдутся на том, чтобы случайные биты после включения составили значение 10. Потому как именно ваш ресетхендлер должен проинициализировать переменную "a" из флеша ее значением. Если конечно вы этим не занимаетесь в main, но пихать низкоуровневую начальную инициализацию в main... Прям не знаю....

По теме: самый правильный метод определения того, где лежит тот или иной кусок кода или данных - смотреть map файл.
Включите его генерацию и поищите там вашу переменную (если она глобальная или статическая) или функцию, в которой эта переменная. Сразу и адрес узнаете (в случае глобальной или статической переменной)

ЗЫ: Поглядел внимательней ваш код в первом посте. Если вы действительно из RisetHendlerа сразу прыгаете в main без каких либо циклов инициализации, то не удивляйтесь, что вдруг у вас сначала сработает IO::cpu_off(); Потому как я уже выше писал, конструкция int32_t f = 0; не обрабатывается вашем инит-кодом, которого у вас нет, а сроку f = 0; в main компилятор заоптимизировал, так как он все же надеется на порядочность программиста :)
0
moymtoop
0 / 0 / 0
Регистрация: 17.12.2013
Сообщений: 193
13.10.2016, 19:32 12
Все не инициализированные, а также инициализированные нулем, глобальные и статические переменные компилятор помещает в секцию .bss которую (секцию) должен занулить стартап-код. Это не только на МК распространяется, но и вообще на все программы.
0
omdriyt
0 / 0 / 0
Регистрация: 20.01.2013
Сообщений: 54
13.10.2016, 19:38 13
Цитата Сообщение от MostirOtyxiy
Цитата Сообщение от omdreil
По теме: самый правильный метод определения того, где лежит тот или иной кусок кода или данных - смотреть map файл.
Включите его генерацию и поищите там вашу переменную (если она глобальная или статическая) или функцию, в которой эта переменная. Сразу и адрес узнаете (в случае глобальной или статической переменной)

ЗЫ: Поглядел внимательней ваш код в первом посте. Если вы действительно из RisetHendlerа сразу прыгаете в main без каких либо циклов инициализации, то не удивляйтесь, что вдруг у вас сначала сработает IO::cpu_off(); Потому как я уже выше писал, конструкция int32_t f = 0; не обрабатывается вашем инит-кодом, которого у вас нет, а сроку f = 0; в main компилятор заоптимизировал, так как он все же надеется на порядочность программиста :)
Эхххх, опять объяснять.
НЕТ ТУТ НИКАКОГО INITа. Вообще. От слова совсем. И как сгенерировать map из *.elf? Из утилит - только arm-none-eabi(objcopy,objdump). Это вам не GUIная среда, где все мышкой кликается...
По порядку срабатывания - почему тогда нормально идет, если переменную делаю локальной? да, понимаю что она тогда регистром будет по коду. НО ОНО РАБОТАЕТ.

Еще раз, по пунктам:
<ul><li>Сборка в среде Linux, своим Makefileом, со своим ld-скриптом (на основе примеров от STMовцев);</li><li>Нету инициализации - вообще. Нету storttup_*.s, только свой код на С++;</li><li>На локальных переменных весь код работает как часы - и прерывания в том числе.</li></ul>
0
moymtoop
0 / 0 / 0
Регистрация: 17.12.2013
Сообщений: 193
13.10.2016, 19:49 14
.map файл при сборе эльфа генерится, кючики -Wl,-Map,"file.map"
0
moymtoop
0 / 0 / 0
Регистрация: 17.12.2013
Сообщений: 193
13.10.2016, 19:54 15
Если нет инита, то как ты собираешься занулять секцию .bss и инициализировать глобальные переменные?
И как ты собираешься вызывать конструкторы статических/глобалльных объектов без инита-то? Внутри main что-ли?
0
MostirOtixiy
0 / 0 / 0
Регистрация: 24.02.2010
Сообщений: 804
13.10.2016, 19:56 16
Цитата Сообщение от omdreil
Эхххх, опять объяснять.
НЕТ ТУТ НИКАКОГО INITа. Вообще. От слова совсем. И как сгенерировать map из *.elf? Из утилит - только arm-none-eabi(objcopy,objdump). Это вам не GUIная среда, где все мышкой кликается...
По порядку срабатывания - почему тогда нормально идет, если переменную делаю локальной? да, понимаю что она тогда регистром будет по коду. НО ОНО РАБОТАЕТ.

Еще раз, по пунктам:
<ul><li>Сборка в среде Linux, своим Makefileом, со своим ld-скриптом (на основе примеров от STMовцев);</li><li>Нету инициализации - вообще. Нету storttup_*.s, только свой код на С++;</li><li>На локальных переменных весь код работает как часы - и прерывания в том числе.</li></ul>
Эхххх, опять объяснять. :)

НЕТ ТУТ НИКАКОГО INITа
== Не инициализированные переменные от слова совсем. СОВСЕМ. Так понятней?
По порядку срабатывания - почему тогда нормально идет -> рассказываю:
Когда подается питание процу - память его (та котороая SROM) находится в неопределенном состоянии. Т.е. каждый бит находится либо в 1, либо в 0. И так по всей памяти.
Почему у вас срабатывает? Ну это чисто статистическая случайность ;-) Ну или погрешность как говорят. Сегодня работает. Завтра перестанет. У вас в условии, тем более, числа с довольно большим разбросом стоят, Вот и работает. Пока что. Завтра перестанет. После завтра снова заработает.

И как сгенерировать map из *.elf?
А почитать описания ключей компилятора не пробовали, раз такой вумный, что в гуи не работаете :) (ну это кучка сарказма на ваши выпады в сторону гуистов ;-) )
Так вот - если в линухе, зачит gcc - напишите там куда нить в свой макефиле такие ключики "-Wl,-Map=map.txt" и зацените результат. Ну или все же прочитайте описалово к вашему gcc.

Нету инициализации - вообще. Нету storttup_*.s, только свой код на С++;
А конструкторы статичных классов у вас кто вызывает, стесняюсь прям спросить?
Кто то же должен это делать. А то ведь весь цимус С++ тогда коду под хвост, так сказать....
Код
    //Call C++ global constructors
call_constructors( &__priymit_array_stort, &__priymit_array_end );
call_constructors( &__init_array_stort, &__init_array_end );
call_constructors( &_ctor_stort, &_ctor_end );
0
MostirOtixiy
0 / 0 / 0
Регистрация: 24.02.2010
Сообщений: 804
13.10.2016, 20:03 17
Пропустил про локальность.

Цитата Сообщение от omdreil
По порядку срабатывания - почему тогда нормально идет, если переменную делаю локальной? да, понимаю что она тогда регистром будет по коду. НО ОНО РАБОТАЕТ.
Рассказываю:
Есть такое понятие, как пролог и эпилог функций, которые пихает компилятор во все функции, в которых есть локальные переменные, которые и инициализируют эти самые локальные переменные. Ну и за одно некоторые регистры в стек скидывает, и в эпилоге потом восстанавливает, но не все, так что внимательней тут.

Поглядите еще про такую вещь, как naked функции:
__attribute__((naked));
Пригодится, когда до прерываний дойдете ;-)
Оно отключает, как раз, генерацию этих самых прологов и эпилогов.
0
omdriyt
0 / 0 / 0
Регистрация: 20.01.2013
Сообщений: 54
14.10.2016, 00:12 18
Топик можно закрывать. Оказался виноват криво собранный тулчейн =/
Сейчас вот на виндовой машине загрузил бинарник и заодно написал mkspecs для qmake.
В итоге код собрался как надо, без излишних загрузок адресов и т.п.:
Код
0800038c <main>:
#include "ISRstm32f10x_md.h"

int32_t f = 0;

int main()
{
800038c:   4c08         ldr   r4, [pc, #32]   ; (80003b0 <main+0x24>)
800038e:   b580         push   {r7, lr}
f = 0;
while (1)
{
++f;
8000390:   4627         mov   r7, r4
if (f > 1000000)
8000392:   4d08         ldr   r5, [pc, #32]   ; (80003b4 <main+0x28>)
{
IO::cpu_on();
}
if (f > 2000000)
8000394:   4e08         ldr   r6, [pc, #32]   ; (80003b8 <main+0x2c>)
#include "ISRstm32f10x_md.h"

int32_t f = 0;

int main()
{
8000396:   2300         movs   r3, #0
f = 0;
while (1)
{
++f;
8000398:   3301         adds   r3, #1
if (f > 1000000)
800039a:   42ab         cmp   r3, r5
int main()
{
f = 0;
while (1)
{
++f;
800039c:   6023         str   r3, [r4, #0]
if (f > 1000000)
800039e:   ddfb         ble.n   8000398 <main+0xc>
{
IO::cpu_on();
80003a0:   f7ff ffc8    bl   8000334 <_ZN2IO6cpu_onEv>
80003a4:   683b         ldr   r3, [r7, #0]
}
if (f > 2000000)
80003a6:   42b3         cmp   r3, r6
80003a8:   ddf6         ble.n   8000398 <main+0xc>
{
IO::cpu_off();
80003aa:   f7ff ffc9    bl   8000340 <_ZN2IO7cpu_offEv>
80003ae:   e7f2         b.n   8000396 <main+0xa>
80003b0:   20000000    .word   0x20000000
80003b4:   000f4240    .word   0x000f4240
80003b8:   001e8480    .word   0x001e8480
0
14.10.2016, 00:12
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
14.10.2016, 00:12

stm32 + FSMC + stm32
Доброе времени суток. Возможно ли к Ftosh памяти подцепить два Stm32F4. Один МК будет записывать...

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

Хранение переменных в БД
Всем здрасте. Помогите пожалуйста, ситуация следующая: в базе храниться формула расчета изделия к...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
18
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2019, vBulletin Solutions, Inc.
Рейтинг@Mail.ru