Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: pgm_read_xxxx и PROGMEM в winavr20071221
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > Cредства разработки для МК > GNU/OpenSource средства разработки
Steel_monkey
Пишу простейшую програмку. Хочу запихать небольшой массив во флэш, и читать оттуда данные без записи всего массива в ОЗУ.

Мои действия?
1. Присоединяю библиотеку avr/pgmspace.h
2. Обьявляю массив:
uint16_t left[] PROGMEM = {60,0x93,45,70};
3. В программе читаю из массива:
REQUIRED_LEFT=pgm_read_word(&left[1]);
Переменная, куда читаю, точно такого же типа, что и в массиве.
Смотрю в файл main.lst

254:main.c **** REQUIRED_LEFT=pgm_read_word(&left[1]);
905 .LM96:
906 03b0 80E0 /* #NOAPP */
907 03b2 90E0 mov r18,r26
908 03b4 FC01 subi r18,lo8(-(1))
909 sts point.1941,r18
911 03b8 5491 .LM97:
912 cpi r18,lo8(4)
913 brlo .L50
915 03ba 5093 0000 .LM98:
916 03be 4093 0000 sts point.1941,__zero_reg__


В файле нет инструкции LPM.

4. Убираем закорючку:
REQUIRED_LEFT=pgm_read_word(left[1]);

Тогда это превращается в

254:main.c **** REQUIRED_LEFT=pgm_read_word(left[1]);
905 .LM96:
906 03b0 E3E9 /* #NOAPP */
907 03b2 F0E0 mov r18,r26
908 subi r18,lo8(-(1))
909 03b4 8591 sts point.1941,r18
911 .LM97:
912 cpi r18,lo8(4)
913 brlo .L50
915 03bc 8093 0000 .LM98:


Инструкция LPM в коде так и не появилась.
Сергей Борщ
Цитата(Steel_monkey @ Mar 3 2008, 19:18) *
3. В программе читаю из массива:
REQUIRED_LEFT=pgm_read_word(&left[1]);
Мы все надеемся, что REQUIRED_LEFT объявлена как глобальная переменная, или как локальная с квалификатором volatile, или что REQUIRED_LEFT используется в выражении, являющимся параметром какой-либо (не-инлайн) функции, или что REQUIRED_LEFT в дальнейшем используется в выражении, результатом которого является обновление какой-нибудь глобальной или volatile переменной. В противном случае оптимизатор выкидывает это чтение, ибо его результат не нужен и это чтение есть просто ненужная трата времени.
Steel_monkey
Обьявлена как глобальная volotile static, щас убрал и то, и другое, то есть осталась только глобальной. Тут еще разные чудеса происходили, типа как если добавить произвольный массив PRGMEM, и ничего с ним не делать, то LPM есть, а если убрать, то нет.
В конце концов, заработало. Не знаю, где был косяк, но опять смоделировать пропадение LPM не смог. В общем вывод делаю такой, до самого Си надо изучать компилятор и его причуды, причем не меньше, чем сам Си.
А за направление в правильное русло спасибо.
_Pasha
Цитата(Steel_monkey @ Mar 3 2008, 22:44) *
Не знаю, где был косяк, но опять смоделировать пропадение LPM не смог. В общем вывод делаю такой, до самого Си надо изучать компилятор и его причуды, причем не меньше, чем сам Си.


1. Как только компилер увидит константное выражение, он выдаст его результат - нафига ему LPM ?
2. Это не причуды, а кривоватая концепция, не позволяющая автоматизировать обращение к флешу и еепрому. То есть он (компилер) достаточно умный, но соответствия стандартам порождают некоторые казусы. К примеру(из другой области) - если следовать идеологии:
Код
uint8_t j;
for(j=0;j<256;j++){do_something();}


Или не мудрствовать лукаво
Код
char j;
for(j=0;j<256;j++){do_something();}


то в последнем случае код будет короче
Сергей Борщ
Цитата(_Pasha @ Mar 4 2008, 09:40) *
то в последнем случае код будет короче
Я думаю, в обоих случаях j будет выкинуто, а цикл заменен на бесконечный.

Если бы было написано j < 255, то бесконечный цикл получился бы только если в опциях компилятора задано "char по умолчанию знаковый". В противном случае оба цикла вроде как должны бы получиться одинаковыми. Или я что-то упустил?
Steel_monkey
Цитата(_Pasha @ Mar 4 2008, 10:40) *
1. Как только компилер увидит константное выражение, он выдаст его результат - нафига ему LPM ?

Дык в том и проблема, что мне нужна не отдельная константа, которую он может загрузить скажем через ldi и которая хранится априори в памяти программ, а массив констант, который хранится изначально и в ОЗУ и в ПЗУ. И тут уже как я понимаю только LPM.
А вообще смотря по англоязычным форумам, PROGMEM вызывает довольно много вопросов по работоспособности.
aesok
Цитата(Steel_monkey @ Mar 4 2008, 14:07) *
Дык в том и проблема, что мне нужна не отдельная константа, которую он может загрузить скажем через ldi и которая хранится априори в памяти программ, а массив констант, который хранится изначально и в ОЗУ и в ПЗУ. И тут уже как я понимаю только LPM.

Данные размещеют в памяти пограм как раз для того чтобы чтобы они не занимали место в RAM.
Цитата
А вообще смотря по англоязычным форумам, PROGMEM вызывает довольно много вопросов по работоспособности.

Не по работоспособности а по пониманию.

Посути вашенго вопроса. Ассемблерный код который вы привели не похож на код макроса pgm_read_word, возможно сбилось соответствие между С и ассемблерным кодом в файле с лстингом, таеое бывает. Поищите в листинге инструкции LPM/ELPM. Если нет, шлите минимальный неработающий пример.


PS:
Цитата
1. Присоединяю библиотеку avr/pgmspace.h

И давайте разберемся с терминологией: avr/pgmspace.h - это не билиотека а заголовычный файл, и он не присоединяеться а включаеться.

Анатолий.
_Pasha
Цитата(Сергей Борщ @ Mar 4 2008, 12:32) *
Я думаю, в обоих случаях j будет выкинуто, а цикл заменен на бесконечный.

Если бы было написано j < 255, то бесконечный цикл получился бы только если в опциях компилятора задано "char по умолчанию знаковый". В противном случае оба цикла вроде как должны бы получиться одинаковыми. Или я что-то упустил?


1. Ошибся я, j<254
2. uint8_t обрабатывается как 16-битовое, отсюда увеличение кода.

3. По поводу Lpm/Elpm, данный факт может сослужить плохую службу только в граничных ситациях, когда надо оценивать объем кода, а тут вдруг массив то есть / то нету. Это плохо. Имхо, единственный выход - избежать константных выражений.
aesok
Цитата(_Pasha @ Mar 4 2008, 18:01) *
1. Ошибся я, j<254
2. uint8_t обрабатывается как 16-битовое, отсюда увеличение кода.


Не верю. AVR-GCC 4.3

main.c
Код
void do_something(void)  __attribute__((noinline));
void do_something(void)
{
  asm("");
}

int
main (void)
{
  unsigned char j;
  for(j=0;j<100;j++){do_something();}


  signed char jj;
  for(jj=0;jj<100;jj++){do_something();}

  return 0;
}


main.lst
Код
...
  d2:    10 e0           ldi    r17, 0x00; 0
  unsigned char j;
  for(j=0;j<100;j++){do_something();}
  d4:    0e 94 67 00     call    0xce; 0xce <do_something>
  d8:    1f 5f           subi    r17, 0xFF; 255
  da:    14 36           cpi    r17, 0x64; 100
  dc:    d9 f7           brne    .-10     ; 0xd4 <main+0x4>
  de:    10 e0           ldi    r17, 0x00; 0


  signed char jj;
  for(jj=0;jj<100;jj++){do_something();}
  e0:    0e 94 67 00     call    0xce; 0xce <do_something>
  e4:    1f 5f           subi    r17, 0xFF; 255
  e6:    14 36           cpi    r17, 0x64; 100
  e8:    d9 f7           brne    .-10     ; 0xe0 <main+0x10>
...


Анатолий.
Сергей Борщ
Цитата(_Pasha @ Mar 4 2008, 17:01) *
1. Ошибся я, j<254
Попытка не защитана wink.gif signed char всегда меньше 254 (за исключением платформ, где char имеет размер более 8 битов).
Цитата(_Pasha @ Mar 4 2008, 17:01) *
2. uint8_t обрабатывается как 16-битовое, отсюда увеличение кода.
Вообще-то uint8_t - это синоним unsigned char на платформах, где char 8 -битный и не определен для платформ, где 8-битные данные не поддерживаются. Ну а дальше действуют правила языка - в арифметических операциях unsigned/signed char приводится (минимум) к int, а потом оптимизатор может выкинуть лишние операции.
singlskv
Цитата(Сергей Борщ @ Mar 4 2008, 19:44) *
Вообще-то uint8_t - это синоним unsigned char на платформах, где char 8 -битный и не определен для платформ, где 8-битные данные не поддерживаются.

небольшой OFF:
а кто-нить знает как адекватно заставить Gcc воспринимать значения
case(...) в switch как восмибитные ?
_Pasha
Цитата(aesok @ Mar 4 2008, 18:29) *
Не верю. AVR-GCC 4.3


И правильно делаете
Пытался смоделировать ту ситуацию повторно - не получилось. Теперь совсем ничего не понимаю crying.gif

Цитата
Попытка не защитана signed char всегда меньше 254 (за исключением платформ, где char имеет размер более 8 битов).


В командной строке задавал чтобы unsigned chars было
ReAl
Цитата(_Pasha @ Mar 4 2008, 22:37) *
В командной строке задавал чтобы unsigned chars было
А вот к этому привыкать не рекомендую.
Я расматриваю такой ключ как "аварийный", когда надо скомпилировать чей-то чужой код, в котором был написан просто char, подразумевая, что он unsigned.

Свой код лучше сразу писать с явным указанием. Чем потом глюки ловить, лучше сразу явно указать знаковость. Просто char я использую нечасто и только тогда (но не всегда "тогда"), когда в нём должны храниться именно символы.
Когда-то давно я использовал и uchar/schar, и u08/i08, теперь окончательно устаканился на stdint.h, введённом в С99 - uint8_t и компания. Настолько окончательно, что когда недавно поднимал небольшой старый проект для мелких изменений и перевода под avr-gcc - просто взял и прошёся по всему тексту заменой на новые типы smile.gif, иначе глаза постоянно спотыкались.

Рано или поздно захочется перейти на другйо кристалл/компилятор и можно просто забыть, что давно отлаженные куски писались с рассчётом на беззнаковый char, а у новой платформы он знаковый.
Хорошо ещё если компилятор сможет предупреждения выдать (condition always true для j < 255), но есть места, где для него неочевидно, что имел ввиду автор кода (например, p[j]).
_Pasha
Цитата(ReAl @ Mar 6 2008, 00:17) *
Когда-то давно я использовал и uchar/schar, и u08/i08, теперь окончательно устаканился на stdint.h, введённом в С99 - uint8_t и компания.


beer.gif Спасибо за совет.
Вот что интересно. Я постоянно с пиками пересекаюсь. И ихний "С18" использую. Дык там в аппликухах "uint8_t и компания" никто не пользует. Бардак, однако...
mdmitry
Цитата(_Pasha @ Mar 6 2008, 10:18) *
Дык там в аппликухах "uint8_t и компания" никто не пользует.

Так уши от gcc растут.
поддерживаю стиль, пропагандируемый ReAl. Переносимость улучшается, со временем очень привыкаешь к этим int_t
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.