Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Keil. Не работает код
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > ARM
MySOL
Здравствуйте, товарищи! Прошу помочь разобраться в проблеме. Сам новичок и для написания программы решил разобраться в такой теме как "typedef struct", которая очень полезна при реализации различных интерфейсов (spi, ethernet и прочее). Написал простенькую программу, дабы протестировать и найти возможные подводные камни. Код следующий
Код
void f1(uint16_t *f1_var1);

typedef struct type1                          
{
    uint8_t var1;
    uint8_t var2;
} type1_t;

int __main(void)
{
        type1_t *main_var;                
    
        main_var->var1    =    0xFF;        
        main_var->var2    =    0xFF;

    f1((uint16_t*)main_var);

}

void f1(uint16_t *f1_var1)
{
    *f1_var1    =    *f1_var1>>1;
}


Суть программы заключается в формировании пакета данных, состоящего из двух наборов данных (в данном случае var1 и var2) формата uint8_t вызов функции, которая эти данные обрабатывает.
Проблема заключается в том, что данные 0xFF не записываются в var1 и var2. С чем это связанно не знаю. Тут значения переменных на этапах отладки
Нажмите для просмотра прикрепленного файла
Нажмите для просмотра прикрепленного файла
Нажмите для просмотра прикрепленного файла
Нажмите для просмотра прикрепленного файла
Нажмите для просмотра прикрепленного файла
Прошу помочь с проблемой.
Палыч
Вы забыли свою структуру где либо разместить (выделить под неё память).
kolobok0
Цитата(MySOL @ May 11 2014, 17:41) *
.. Прошу помочь...


ответ прозвучал уже, ну и ышо пять копеек: о выравнивании не забудьте. либо прямо в коде сохраняете старое, ставите прагмой новое,
после возвращаете старое. либо глобально на весь проект через ключики компиляции...
KnightIgor
Код
typedef struct type1                          
{
    uint8_t var1;
    uint8_t var2;
} type1_t;

int __main(void)
{
        type1_t main_var;                
    
        main_var.var1    =    0xFF;        
        main_var.var2    =    0xFF;

    f1(&main_var);

}

void f1(type1_t *f1_var1)
{
    f1_var1->var1    >>=    1;

}

MySOL
Цитата(Палыч @ May 11 2014, 17:55) *
Вы забыли свою структуру где либо разместить (выделить под неё память).

Уважаемый, вы не могли бы пояснить как это делается? Просто я впервые слышу про такое.


Цитата(kolobok0 @ May 11 2014, 18:00) *
ответ прозвучал уже, ну и ышо пять копеек: о выравнивании не забудьте. либо прямо в коде сохраняете старое, ставите прагмой новое,
после возвращаете старое. либо глобально на весь проект через ключики компиляции...

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

Цитата(KnightIgor @ May 11 2014, 18:03) *
Код
typedef struct type1                          
{
    uint8_t var1;
    uint8_t var2;
} type1_t;

int __main(void)
{
        type1_t main_var;                
    
        main_var.var1    =    0xFF;        
        main_var.var2    =    0xFF;

    f1(&main_var);

}

void f1(type1_t *f1_var1)
{
    f1_var1->var1    >>=    1;

}

По-моему вы ошиблись=)
Golikov A.
когда вы объявляется
my_str * str;

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

my_str str;

так вы создадите структуру и сможете обращаться к ее полям как str.field, то есть через точку.

далее можно сделать указатель my_str *str2 = &str;
и уже обращаться к полям через стрелку
str2->field, при это такое обращение будет к полю именно первой структуры str.


структуры периферии проца описаны через указатели так (условно)

GPIO_STR *PORT1 = (GPIO_STR *) 0x434002300;
потому что в памяти уже есть данные лежащие как поля в структуре, это собственно регистры управляющие периферией, и вы создаете явный указатель на эту область памяти

Цитата
Простите, но я вас немного не понял. Если можете, ткните носом в статейку, где можно про это почитать, что бы вас не утруждать


есть разное выравнивание памяти, и иногда структура вида
struct...
{
char field1;
char field2;
}

может быть в памяти представлена 2 байтами, лежащими друг за другом, а может 8 байтами, где в 0 лежит field1, потом 3 байта пропуска, и в 4 лежит filed2. Это сделано потому что некоторые процы умеют брать данные только 32 битными или 16 битными словами, и в принципе не могут обращаться в области памяти с адресами не кратными их размеру слова.

так вот есть специальные слова позволяющие подсказывать как создавать структуру, типа __packed




Цитата
По-моему вы ошиблись=)


он не ошибся, он заменил указатель структуры на экземпляр

type1_t main_var;
вместо ваших
type1_t *main_var;

и соответственно вызовы через стрелку на вызовы через точку.

а также добавил функцию обработки данных структуры через указатель...

И в целом это все не относиться к кейлу или арму, это вообще вопросы базового знания языка С....
MySOL
Цитата(Golikov A. @ May 11 2014, 18:27) *
....

Благодарю, что указали на ошибку при инициализации указателя!

Благодарю всех за помощь! Проблема решена
Golikov A.
Выравнивание - с этим ничего не сделаешь, с этим надо житьsm.gif..
вот на пальцах смысл.

берем проц типа кортекс м0, если не ошибаюсь этот процессор не умеет обращаться к памяти без выравнивания.

делаем в нем структуру
struct str
{
int8_t A;
int8_t B;
}
он выделяет 8 байт памяти и кладет данные
A
0
0
0
B
0
0
0

работаем внутри проца и все счастливы. Теперь мы добавляем какой-то интерфейс с внешним миром, например с PC или делаем сохранение параметров в память. Радостно делаем функцию которая посылает данные, с указателем на данные и размером данных, вызываем SaveSendData(MY_STR, 2) и что? облом, структура то не 2 а 8 байт, половина не сохранилась.

вызываем SaveSendData(MY_STR, sizeof(MY_STR)), все хорошо, но в памяти отъели 8 байт если мы про это не знаем может затереть данные.

Теперь на РС делаем ответную структуру
struct str{char A; char B} и что дальше? в компе то нет выравнивания, структура 2 байта, прямое копирования и пересылка - опять облом...


казалось, ок, если такая структура плохо, делаем ее

struct __packed str
{
int8_t A;
int8_t B;
}

пересылка - сохранение в память - прием на компе - супер! вот оно счастье, но!

Проц то не умеет обращаться в не выровненые адреса, и обращение my_str.B будет всегда вести в my_str.A, хоть убейся об него, пишите В, меняете А, читаете В, получаете А. И один способ записать или изменить В, это считать 32 бита A,B,0,0, вырезать маской нужную часть, сдвинуть и считать данные, для изменения обратно, считать 32 бита, масками изменить нужный кусок, записать обратно...

То есть либо теряем скорость, либо удобство, и об этом надо думать и понимать что у вас происходит....
jcxz
Не очень понял как мешает выравнивание структуре
struct str {
int8_t A;
int8_t B;
}
в ней-же все элементы - байт. На любом CPU, который умеет работать с байтами (и PC и Cortex-M0), с такой структурой никаких проблем выравнивания не будет.
Цитата(Golikov A. @ May 11 2014, 23:02) *
в компе то нет выравнивания

В компе-то - нет, но в си-компиляторах есть. Вне зависимости от CPU.
Насчёт пакованных структур - тоже не понял. Компиляторы (IAR во всяком случае) при работе с пакованными структурами на CPU не умеющем
невыровненный доступ, знают это прекрасно и выполняют доступ к членам структуры в соответствии с величиной пакованности (1,2,...) -
разбивают обращения на несколько 8-и или 16-и-битных (загляните в листинг!). И си-программисту эта кухня невидима. Скорость конечно будет ниже.
Но на удобство почти никак не повлияет. Единственно - будет проблема с указателями на такие структуры.
Golikov A.
да..., вот потому и хорошо даже для себя много раз прописывать даже очевидные вещи, я правда был не корректен и не точен

в структуре
struct
{
char A;
char B;
}
наверное, правда, проблем не будет, а в структуре
struct
{
char A;
char B;
int C;
}
проблемы точно должны быть. Также должны быть проблемы между 2 соседними структурами, в массиве, например, если думать что начало второй через sizeof(str) после начала первой....

Цитата
В компе-то - нет, но в си-компиляторах есть. Вне зависимости от CPU.

Тут я опять не совсем корректен. Я имел ввиду глобально, что обычно на компьютере в силу архитектуры их процов (блин сейчас правда и для компа процов уже не 1, но будем мыслить шире) обычно (по умолчанию) память пакована до байтовой границы. Опять же более строго для все обще принятых компиляторов и языков.

Естественно спец компиляторы под платформу с С язык скрывают всю эту кухню, и никогда написав в коде str.B, вы не измените и не получите соседнего поля, но за этим обращением может быть скрыто много действий, то есть как минимум оно может стать не атомарным. Сейчас процы все быстрее, и пару тактов туда суда погоды не делают, но не атомарность может здорово что-то загадить при наличии прерываний.
А в 32 битном микроблайзе попытка копирования или печати массива с 1, 2, 3 адреса всегда копировала или печатала его с 0, это не относится к упаковке, но относиться к выравниванию.
Иногда разбирая или наоборот заполняю структуру удобнее копировать ее не по полям, а большими кусками из других кусков, данных и памяти, и при таких операциях надо всегда четко знать что как лежит.

Мне кажется что кто пишет под процы, должен очень хорошо знать эту кухню изнутри, мы особая каста это не в виндус thread-ов наделать, а потом писать в форумах что программ без ошибок не бывает. И я пытался на примерах (возможно не очень корректно изложенных) показать с чем мы имеем дело.
jcxz
Цитата(Golikov A. @ May 12 2014, 00:49) *
struct
{
char A;
char B;
int C;
}
проблемы точно должны быть. Также должны быть проблемы между 2 соседними структурами, в массиве, например, если думать что начало второй через sizeof(str) после начала первой....

В чём-же? rolleyes.gif
Ну разве, что если использовать её на разных системах, с разным размером int....
Вам известно, что в структурах (классах) си по умолчанию выполняется выравнивание (вне зависимости от CPU; это правило языка си)?
Ну только если конечно не предпринять спец. мер (pack).
Также и начало второй в массиве - всегда после sizeof() от начала первой как ни странно... rolleyes.gif
Размер (sizeof()) также выравнивается на размер выравнивания структуры (который в свою очередь равен размеру максимального члена).
Golikov A.
о том вроде и беседуем...

Вы уверены что все проекты что вы создаете в кейле или других средах, под разные процы по умолчанию упакованы до байта, перефразирую, уверены что нигде не задается метод пакования отличный от побайтно?

А вы уверены что упакованные 6 байтные структуры идут в памяти через 6 байт на всех типах процов?

jcxz
Хм... Я разве где-то подобное говорил??
Я вообще-то говорил почти противоположное:
1. Структуры в си по умолчанию не упакованные.
2. Выравнивание структуры равно выравниванию максимального члена структуры.
3. Максимальное выравнивание для встроенных типов данных как правило не превышает основной разрядности CPU (хотя это не могу утверждать с полной уверенностью).
(т.е. - если CPU 32-разрядный, то выравнивание 64-битного встроенного типа будет всё равно ==32).

Про упакованность - тоже Вы что-то не поняли. Я как раз писал, что пакование может выполняться не обязательно до уровня байт, может быть до уровня 16-бит или др.
см. #pragma pack(push, N) в IAR к примеру. Пример IAR:
#pragma pack(push, 2)
struct T {
u32 a;
u8 b;
};
#pragma pack(pop)
В данной структуре будет sizeof(T)==6. И выравнивание её будет равно ==16бит.

И вообще - упаковка не входит в язык си и может выполняться по-разному на разных компиляторах.

Цитата(Golikov A. @ May 12 2014, 02:55) *
А вы уверены что упакованные 6 байтные структуры идут в памяти через 6 байт на всех типах процов?

Очевидно, что в си, если имеется структура (не важно - пакованная или нет), то в массиве элементов этой структуры начало каждого следующего элемента будет
находиться по смещению sizeof() от предыдущего. А вот уже величина этого sizeof(T) будет зависеть от наличия pack() в её атрибутах и величины пакования этого самого pack().
Golikov A.
Что-то я совсем потерялся....

Что в процах с 32 битным выравниванием данных прямые бездумные обращения к памяти чреваты - это точно. В таких процах создание массивов элементов размера не кратного 32 битам и бездумное обращение с ними как с памятью - тоже чревато.

А вот объяснения и примера я чет хорошего придумать не могу%(.... вы все врем меня стандартами языка придавливаетеsad.gif....
Сергей Борщ
Цитата(Golikov A. @ May 12 2014, 07:40) *
А вот объяснения и примера я чет хорошего придумать не могу%(.... вы все врем меня стандартами языка придавливаетеsad.gif....
Объяснения чего? Чреватости? Пожалуйста: MSP430, 16-битный, доступ к 16-битному значению по невыровненному адресу игнорирует младший бит адреса, результат - идет обращение к одному байту переменной и одному сосенднему. ARM7TDMI - обычно аналогично. В Cortex-M0 - исключение HardFault. Cortex-M3 - Исключение UsageFault или нормальный доступ для некоторых инструкций в зависимости от бита UNALIGN_TRP, для остальных инструкций - UsageFault. Пример в первом же сообщении этой темы:

Код
void f1(uint16_t *f1_var1);

typedef struct type1                          
{
    uint8_t var1;
    uint8_t var2;
} type1_t;

...
    type1_t *main_var;                
    .....
    f1((uint16_t*)main_var);
jcxz
Цитата(Golikov A. @ May 12 2014, 10:40) *
Что-то я совсем потерялся....

Что в процах с 32 битным выравниванием данных прямые бездумные обращения к памяти чреваты - это точно. В таких процах создание массивов элементов размера не кратного 32 битам и бездумное обращение с ними как с памятью - тоже чревато.

А вот объяснения и примера я чет хорошего придумать не могу%(.... вы все врем меня стандартами языка придавливаетеsad.gif....

Вы почему-то путаете невыровненный доступ CPU и работу си-компиляторов со структурами.
Это две не относящиеся друг к другу вещи.
Во всех примерах, что я привел нигде нет невыровненного доступа. Даже в случаях с пакованными структурами. sm.gif
В случае с пакованной структурой, вы указываете (величиной паковки в pack()) инструкциями какой максимальной длины
следует обращаться к элементам структуры.
Таким образом, чтение элемента T::a
#pragma pack(push, 1)
struct T { u32 a; };
#pragma pack(pop)
на ARM7/9 будет осуществляться 4-мя байтовыми LDRB с последующей склейкой байт в u32.
Для:
#pragma pack(push, 2)
struct T { u32 a; };
#pragma pack(pop)
на ARM7/9 сгенерит две LDRH с последующей склейкой в u32.

Для Cortex-M3 компилятор имеет право сгенерить в обоих случаях одну невыровненную LDR (если, как указал уважаемый Сергей Борщ,
соответствующий бит разрешает невыровненный доступ) (не помню уж - есть-ли такая настройка в опциях компилёра, чтобы указать ему состояние этого бита).
А может он просто - поступает так же, как для ARM7/9. Не помню.

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

PS: Я кстати стараюсь не пользоваться пакованными структурами. А там, где это нужно (например - для описания форматов кадров протоколов обмена),
предпочитаю в качестве членов структур, размер которых превышает выравнивание структуры, использовать свои типы данных, реализованные классами с
соответствующими методами доступа к ним.
В таком случае не возникает проблем с указателями на пакованные структуры в компиляторе.
И что ещё более важно - такие описания структур легко переносятся между разными компиляторами, так как не привязаны к прагмам, которые в разных компиляторах = разные.
Golikov A.
Цитата
Вы почему-то путаете не выровненный доступ CPU и работу си-компиляторов со структурами

Очевидно вы правы, я смешал 2 независимые вещи...

Что-то мне казалось что в зависимости от типа проца (от величины его выравнивания), среда задает такой же параметр паковки структур по умолчанию. Вероятно это скорее исключение чем правило, но мне сейчас кажется что я пару раз на такое нарывался...

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

Вот и теперь я понял откуда растут проблемы которые я пытался описать:
Когда с обоих концов стоит проц и язык С, проблемы реально мной надуманы. А вот когда у вас посылки делает проц с языком С, а приемник у вас ПЛИС с автоматом разбора входящего сообщения, вот тут проблемы не пакованных структур и вопросы понимания что как лежит в памяти встают в полный рост!




Также вот такой пример мне вспомнился

Код
struct __packed str
{
   char A;
   char B;
}

если есть функция передачи 1 поля структуры, по UART, например, то попытки разбора аля
Код
memcpy((void *)&my_str.B, data, 1);

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

jcxz
Цитата(Golikov A. @ May 12 2014, 13:56) *
Что-то мне казалось что в зависимости от типа проца (от величины его выравнивания), среда задает такой же параметр паковки структур по умолчанию. Вероятно это скорее исключение чем правило, но мне сейчас кажется что я пару раз на такое нарывался...

От CPU зависит только максимальное выравнивание. Т.е. - struct T { u32 a; }; для 32-битных CPU будет выровнена на 32, а для 16-битных CPU - на 16.
В соответствии с основной разрядностью CPU.

Цитата(Golikov A. @ May 12 2014, 13:56) *
Код
struct __packed str
{
   char A;
   char B;
}

если есть функция передачи 1 поля структуры, по UART, например, то попытки разбора аля
Код
memcpy((void *)&my_str.B, data, 1);

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

А в чём тут проблема? В поле my_str.B будет скопирован 1 char из data. С любым выравниванием.
Ну кроме процов, где char - не байт, а другой размер. Но там тоже всё ок, только будет скопирован не байт, а char wink.gif
Golikov A.
Код
struct __packed str
{
   char A;
   char B;
}


если экземпляр этой структуры my_str лежит, допустим, по адресу 0, и у нас проц типа кортекса М0, с 32 битным выравниванием памяти.

какой адрес будет иметь my_str.B? числовое значение? что передастся в memcpy? чем закончится вызов? и где окажутся в итоге данные?




jcxz
Да какое тут 32-битное выравнивание???
Кроме 32-битных инструкций (LDR/STR), у ARM-ов есть и 8-и (LDRB/STRB) и 16-битные (LDRH/STRH) инструкции доступа к памяти.
И выравнивание зависит от разрядности инструкции.
Для байтовых переменных будет конечно-же использованы LDRB/STRB (или LDRSB/STRSB).
и ваш пример будет выполнен корректно.

Иначе - следуя вашей логике, когда вы объявляете переменную размером байт, у вас всегда бы выделялось 32 бита памяти под неё.
Но это-же бред.
Сергей Борщ
Цитата(Golikov A. @ May 12 2014, 12:28) *
что передастся в memcpy? чем закончится вызов? и где окажутся в итоге данные?
Для ответа на этот вопрос достаточно прочитать (нагуглить) описание memcpy(). Она работает с любыми адресами и любым выравниванием. Снаружи это выглядит, как будто она копирует побайтно (и при таком копировании проблем возникнуть не может). Внутри же она может вызывать различные ветки своего алгоритма в зависимости от фактических аргументов (т.е. может скопировать 3 байта до невыровненного адреса побайтно а потом перейти на копирование 4-байтных выровненных слов, потом аналогично побайтно скопировать оставшиеся 3..1 байт, может делать так независимо для источника и приемника), а может подставить одну-две-три команды в точку вызова, если все параметры известны на момент компиляции - это уже забота компилятора и его оптимизатора. В общем с memcpy работать будет всегда.
Golikov A.
Ладно, сдаюсь... все всегда будет работать хорошо, уговорили....

Belikov_lp
Алгоритмы не пробовал добавлять?
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.