Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Как передать sprintf() в качестве параметра строку из flash
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > Cредства разработки для МК > IAR
Konstantin Ilichev
Добрый день всем!

Перехожу с CodeVisionAVR на IAR EWAVR 5.30.
При переносе проекта столкнулся с тем, что привычная конструкция компилируется, но не работает:
Код
sprintf_P(str,"Socket type=%p",(SocketType)?("TCP"):("UDP"));

В строке str получается что-то типа такого: "Socket type=3CD".
Причина - CodeVision рассматривает спецификатор %p как указатель на строку во флеш, а IAR - как *void.
Включена опция "--string_literals_in_flash".

В проекте очень много таких конструкций, переделать на %s, мне кажется, нереально.
Мне видится, что легче внести изменения в библиотечную функцию sprintf_P, которая разбирает спецификаторы.

Посоветуйте, как быть. Может, эта проблема решается совсем просто?
zhevak
Проблема в том, что Си изначально писался для Фон-Неймановских архитектур, у которых единое адресное пространство для данных и для команд процессора. AVR -же имеет Гарвардскую архитектуру с тремя разными пространствами (RAM, flash, EEPROM).

Поэтому, когда Вы используете стандартную (из стандартной библиотеки Си) функцию, например sprintf, и передаете ей в качестве аргументов адреса строк, то функция предполагает, что работает с пространством RAM. Иначе говоря, Ваши строки "Socket type=%p", "TCP" и "UDP" должны располагаться в RAM.

Туда они могут попасть только в двух случаях:

1. Автоматически. Такое имеет место быть, когда Вы понятия не имеет о предмете и тупо их прописываете в своей программе(то есть не указываете модификаторы __flash или PROGMEM, в зависимости от компилятора (IAR или gcc)). Так делают все начинающие. У каждого Си-шного проекта по умолчанию имеется файл начальной загрузки или startup-файл. Называется он по разному. Одной из множества его задач является как раз перенос вот таких строк из flash в RAM. Этот способ программирования удобен тем, что не надо думать вообще на эту тему. Недостаток способа -- повышенный расход RAM. Так можно делать, когда у вас проект небольшой, и строковых переменных немного. Перед тем как передать управление в вашу функцию main() загрузчик из всех разом перенесет в RAM. Понятно, что при этом придется отрезать для них нехилый кусок RAM и забыть про него.

2. Ручками. Каждый раз, когда Вам требуется строковая (или любая другая) переменная, которую вы прописали во flash, вы в функции, где она используется, ручками переносите ее в RAM. После того как функция отработает и переменная будет не нужна. Соответствующий объем RAM, временно занимаемый под эту переменную, снова станет доступен для размещения других переменных в других функциях. Иначе говоря, Вы используете RAM по мере необходимости. Вы контролируете процесс. Это и есть преимущество этого метода. Недостаток -- больше возни с исходным кодом, больше заботы, больше нужно уделить внимания техническим деталям -- как это сделать, а не бизнес-задаче, ради которой создавался проект, повышенный расход flash.

Когда Вы пишите проект с LCD и меню на какой-нибудь Меге-16, у которой 16 кБ flash и всего 1 кБ RAM, то иногда выгоднее "помучатся", чем вешать дополнительный корпус внешней памяти или устанавливать другой проц, у которого оперативы побольше. Это кому что нравится -- есть профи и есть быдлокодеры. И те, и другие успешно зарабатывают себе на хлеб с икрой.

Вот тут я написал, как работать "ручкам" -- http://forum.e-lug.ru/viewtopic.php?id=484 . Описание идет в контексте gcc, но IAR придерживается этих же принципов, поэтому Вы можете смело переносить рекомендации в свой IAR-вский проект. Конечно, Вам придется вместо PROGMEM писать __flash, и ставить модификатор не в конце определения, а в начале. Но принцип работы со строковыми переменными, которые находятся в flash одни и те же. Даже include-файл называется аналогично -- pgmspace.h

Главное понять сам принцип, по ходу сориентируетесь сами. Если что -- форумы рулят.
Удачи!
KRS
Цитата(Konstantin Ilichev @ Aug 31 2011, 19:54) *
В проекте очень много таких конструкций, переделать на %s, мне кажется, нереально.
Мне видится, что легче внести изменения в библиотечную функцию sprintf_P, которая разбирает спецификаторы.

раз надо перенести готовый проект - то логичнее конечно изменить sprintf, но не в самой библиотеке (хотя это тоже можно), а написать свою функцию, за основу конечно можно взять библиотечную - благо исходники все есть.
А вообще надо документацию посмотреть может быть у IAR и есть модификатор или тип для указателя на flash (я уже давно с АВР ничего нового не делал)

Цитата(zhevak @ Aug 31 2011, 20:50) *
Проблема в том, что Си изначально писался для Фон-Неймановских архитектур, у которых единое адресное пространство для данных и для команд процессора.

С прекрасно подходит и для Гарвардской архитектуры!
И кстати у GCC есть %S для строк во флеше.
Сергей Борщ
QUOTE (Konstantin Ilichev @ Aug 31 2011, 18:54) *
Причина - CodeVision рассматривает спецификатор %p как указатель на строку во флеш, а IAR - как *void.
Причина - CodeVision компилятор "языка, похожего на С". В стандарте языка C спецификатор %p предназначен для отображения указателей, а %s - строк. "Строки фо флеш" - вообще чудное расширение архитектуры AVR. В имеющейся у меня документации на ИАР версии 4.хх спецификаторов для строк из флеш не описано.
Konstantin Ilichev
Всем спасибо за быстрые ответы!

Модифицировал функцию, занимающуюся разбором спецификаторов. Получилось просто, всё работает. Далее описываю подробно.

1. Цель - сделать так, чтобы под IAR работал следующий код, написанный для CodeVisionAVR:
Код
sprintf(str,"Socket type=%p",(SocketType)?("TCP"):("UDP"));

Требуется, чтобы в IAR EWAVR код минимально отличался, например, выглядел так:
Код
sprintf_P(str,"Socket type=%p",(SocketType)?("TCP"):("UDP"));

2. Открываем EWAVR_CompilerReference.pdf, раздел "Overriding library modules", подраздел "Overriding library modules using IAR Embedded Workbench" и далее выполняем пункты из этого подраздела.

3. Создаем в своем проекте папку "mysprintf", в нее копируем файл "frmwri_p.c" (это модуль с функцией _formatted_write_P(), выполняющей разбор спецификаторов и собственно формированием форматированной строки) из папки "src\lib\dlib" (там, где установлен IAR). Модифицируем файл из библиотеки DLIB, так как компилироваться будет именно с ней. Аналогично можно модифицировать файл библиотеки CLIB.

4. В начале файла добавляем строчку, чтобы компилилось с поддержкой float (можно другой дефайн, см. frmwri_p.c).
Код
#define FLOAT_SUPPORT

5. Вносим требуемые изменения - комментируем стандартную обработку спецификатора 'p' и добавляем свой код:
Код
    case 'p':
      if ( !(flash_pointer = VAPTR(__flash char)) )
              {
                flash_pointer = null_pointer_str;
                ptr = buf_pointer=0;
                ptr += 13;
                break;
              }
      if (precision < 0)
        precision = 10000;
      for (n=0; *flash_pointer++ && n < precision; n++)
       ;
      buf_pointer = (char *)(--flash_pointer);
      ptr = buf_pointer;
      buf_pointer -= n;
      flash_pointer -= n;
      break;
Этот код сделан на базе обработчика спецификатора 's' (см. frmwri_p.c).

6. Далее изменения в коде для REDUCED_SUPPORT:
Код
    case 'p':
      ptr_flash = VAPTR(__flash char);
      while (format_flag = *ptr_flash++)
      {
        put_one_char(format_flag, secret_pointer);
        nr_of_chars++;
      }
      continue;
Тоже сделано на базе обработчика спецификатора 's'.

7. Больше ничего изменять не требуется, новые переменные не добавляются (используются имеющиеся).

8. Добавляем в свой проект модифицированный файл "mysprintf\frmwri_p.c", имя файла обязательно должно остаться таким же, какое оно в исходной библиотеке.

9. Компилируем, пьем кофе черный, последнее время чай.

10. Хорошая мысль использовать 'S' вместо 'p'. Можно и так сделать. Мне 'p' ни к чему, поэтому оставлю, как в CV.
zhevak
Цитата(KRS @ Sep 1 2011, 00:50) *
С прекрасно подходит и для Гарвардской архитектуры!

Абсолютно с Вами согласен, но с одной оговоркой!

Сам по себе язык С от архитектуры вообще не зависит. Однако, от нее зависит набор тех или иных библиотек. Стандартная библиотека С -- сильно зависит от архитектуры. (Я не захотел делать на этом акцент. Думал, что люди отличают сам язык от его библиотек. А зря наверно!) Как я уже говорил, эта библиотека разрабатывалась применительно к Фон-Неймановской архитектуре. По прошествию нескольких десятков лет с момента появления языка С и его стандартных библиотек (подчеркиваю!) в мире появилось очень много разных архитектур. В том числе и AVR архитектура.

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

CodeVision изначально развивался по своему сценарию и всегда клал болт на стандарты С. Не берусь судить -- хорошо-ли это, плохо ли. Но одно могу сказать -- для новичков он подходит как нельзя лучше. А то, что люди работают с этим (не отвечающим стандартам) компилятором и потом, когда вырастают из этих "коротких штанишек", начинают воспринимать реальные стандарты как отклонение от своего "стандарта CV" -- это так, побочный эффект. Это как с утками -- что первое увидел, которое движется, -- то и мама.
ReAl
У AVR-GCC проще, там сразу идет %s -- строка в ОЗУ, %S -- строка во флеш
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.