AVR: Переносим строки в программную память

При программировании под микроконтроллеры приходится учитывать что объем ОЗУ(RAM) и программной памяти(flash) ограничен. По этому приходится искать способы что бы эту память сэкономить.

К примеру рассмотрим простую строку кода:

puts( "hello world" );

Под строку «hello world» выделяется место в ОЗУ, и при старте эта область памяти инициализируется из flash. Т.е получается что эта строка занимает память и в ОЗУ и во flash.

Пока программа небольшая, все хорошо, но в определенный момент ОЗУ кончается, и программа перестаёт работать.

Что можно сделать? Первое — это отказаться от использования строк в ОЗУ. И поместить все строковые константы в программной памяти.

Далее описаны примеры оптимизации кода для компилятора  avr-gcc (WinAvr).

Для работы с программной (flash) памятью надо  подключить соответствующий заголовочный файл:

#include <avr/pgmspace.h>

После чего заменяем

puts( "hello world" ); 
//заменяем на
puts_P( PSTR( "hello world" ) );
printf( "x=%d\r\n", x );
//заменяем на
printf_P( PSTR("x=%d\r\n"), x );

и т.д.

PSTR — это макрос, который определен как:

# define PSTR(s) (__extension__({static char __c[] PROGMEM = (s); &__c[0];}))

Но при использовании макроса PSTR есть один нюанс — память выделятся на каждую строку, даже если такая строка повторяется.
Например если написать:

puts_P( PSTR( "hello world" ) );
puts_P( PSTR( "hello world" ) );
puts_P( PSTR( "hello world" ) );

то в программной памяти будет лежать три строки «hello world».

Для того, что бы этого избежать лучше объявить строку заранее:

char hello_world[] PROGMEM = "hello world".
puts_P( hello_world );

В данном коде PROGMEM  — это  макрос, который определен в  /usr/lib/avr/include/avr/pgmspace.h как:

#define PROGMEM  __attribute__((__progmem__))

Так же при помощи PSTR не получиться проинициализировать массив строк:

char *strings[] PROGMEM = {
  PSTR("string1"),
  PSTR("string2"),
  PSTR("string3"),
};

При компиляции данного кода будет выдана ошибка «error: braced-group within expression allowed only inside a function».

Вместо этого надо объявить строки с атрибутом PROGMEM, а затем инициализировать массив указателями на эти строки.

char string1[] PROGMEM = "string1";
char string2[] PROGMEM = "string2";
char string3[] PROGMEM = "string3";
 
char *strings[] PROGMEM = {
  string1,
  string2,
  string3,
};

а для вывода массива строк можно использовать следующий код:

unsigned char i;
  for( i =0; i < 3; i ++) {
    PGM_P str = (PGM_P)pgm_read_word( &strings[i] );
    puts_P( str );
  }
Запись опубликована в рубрике Микроконтроллеры avr с метками , . Добавьте в закладки постоянную ссылку.

Один комментарий: AVR: Переносим строки в программную память

  1. Guest говорит:

    ни черта не понятно….
    Можно подробней. и с комментариями в коде.
    А то выходит «Вкрутим этот болт вот сюды и все работает», а на кой черт его именно сюда вкрутили, неясно

Добавить комментарий

Ваш e-mail не будет опубликован.

Можно использовать следующие HTML-теги и атрибуты: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>