Домашняя страница библиотеки_c AVR

Синтаксис языка C

Библиотека языка C GNU glibc

 Страницы развития библиотеки_с AVR

Главная страница

Инструкция пользователя

Содержание библиотеки_c

Часто задаваемые вопросы

Содержание по алфавиту

Демонстрационные проекты


Данные в программном пространстве

Введение

Допустим имеются некоторые постоянные данные, и участок памяти для их хранения исчерпывается? Многие AVRы для хранения данных имеют ограниченный размер RAM, но могут иметь больше перепрограммируемого пространства (Flash). МК AVR имеют Гарвардскую архитектуру, где Flash используется для программ, а RAM для данных, и каждая из них имеют отдельное адресное пространство. Это - вызов, чтобы заставить постоянные данные быть сохраненными в программном пространстве, и находить эти данные, чтобы использовать их в приложении AVR.

Проблема усилена тем фактом, что Язык C не был разработан для Гарвардской архитектуры, он был разработан для архитектуры Von Neumann, в которой код и данные находятся в одном и том же адресном пространстве. Это означает, что любой компилятор для процессора Гарвардской архитектуры, подобный AVR, должен использовать другие средства? работать с отдельными адресными пространствами.

Некоторые компиляторы используют ненормативные ключевые слова Языка C, или они расширяют стандартный синтаксис способами, которые являются ненормативными. Комплект инструментальных средств AVR имеет отличный подход.

GCC имеет специальное ключевое слово, __attribute__ оно используется для того, чтобы прикрепить различные атрибуты к вещам типа объявлений функций, переменных, и печати. Это ключевое слово сопровождается спецификацией атрибута в двойных круглых скобках. В AVR GCC, есть специальный вызваемый атрибут progmem. Этот атрибут используется при объявлении данных, и говорит компилятору размещать данные в памяти программ (Flash).

AVR-Libc обеспечивает простую макрокоманду PROGMEM она определена как синтаксис атрибута GCC с атрибутом progmem. Эта макрокоманда была создана как удобство конечному пользователю, поскольку мы будем видеть ниже.  PROGMEM макрокоманда определена в <avr/pgmspace.h>.

Трудно изменить GCC, чтобы создать новые расширения к синтаксису Языка C, так вместо этого, avr-libc создал макрос для поиска данных в программном пространстве. Этот макрос также находится в <avr/pgmspace.h>.

Примечание Относительно констант

Многие пользователи поднимают идею использования ключевого слова const как средства объявления данных помещаемых в пространство программы. Но это было бы не правильно.

const используется для того, чтобы указать компилятору, что данные предназначены "только для чтения". Оно используется для того, чтобы помочь  компилятору делать некоторые преобразования, или помогать компилятору проверять неправильное использование этех переменных.

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

const предназначен для использований этого типа, а не как средство определения места хранения данных. Если это использовалось как средство для определения хранения данных, то это теряет его правильное значение (изменяет его семантику) в других ситуациях типа в функциональном примере параметра.

Сохранение и поиск данных в программном пространстве

Скажем, Вы имеете некоторые глобальные данные:

unsigned char mydata[11][10] =
{
        {0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09},
        {0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,0x10,0x11,0x12,0x13},
        {0x14,0x15,0x16,0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D},
        {0x1E,0x1F,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27},
        {0x28,0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F,0x30,0x31},
        {0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x3B},
        {0x3C,0x3D,0x3E,0x3F,0x40,0x41,0x42,0x43,0x44,0x45},
        {0x46,0x47,0x48,0x49,0x4A,0x4B,0x4C,0x4D,0x4E,0x4F},
        {0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59},
        {0x5A,0x5B,0x5C,0x5D,0x5E,0x5F,0x60,0x61,0x62,0x63},
        {0x64,0x65,0x66,0x67,0x68,0x69,0x6A,0x6B,0x6C,0x6D}
};

и позже, в вашем коде, обращаетесь к этим данным в функции, и сохраняете единственный байт в переменную, например так:

byte = mydata[i][j];

Теперь Вы хотите сохранить ваши данные в Памяти Программы. Использование PROGMEM макрокоманды, находящейся в <avr/pgmspace.h> и поместите это после объявления переменной, но перед инициализацией, например так:

#include <avr/pgmspace.h>
.
.
.
unsigned char mydata[11][10] PROGMEM =
{
        {0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09},
        {0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,0x10,0x11,0x12,0x13},
        {0x14,0x15,0x16,0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D},
        {0x1E,0x1F,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27},
        {0x28,0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F,0x30,0x31},
        {0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x3B},
        {0x3C,0x3D,0x3E,0x3F,0x40,0x41,0x42,0x43,0x44,0x45},
        {0x46,0x47,0x48,0x49,0x4A,0x4B,0x4C,0x4D,0x4E,0x4F},
        {0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59},
        {0x5A,0x5B,0x5C,0x5D,0x5E,0x5F,0x60,0x61,0x62,0x63},
        {0x64,0x65,0x66,0x67,0x68,0x69,0x6A,0x6B,0x6C,0x6D}
};

Вот теперь ваши данные находятся в Пространстве Программы! Вы можете компилировать, связать, и проверить файл карты, чтобы проверить это - mydata помещен в правильный раздел.

Теперь, когда ваши данные постоянно находятся в Пространстве Программы, и ваш код при чтении данных больше не будет работать. Код, который сгенерирован, отыщет данные, которые расположены в адресе массива mydata, плюс смещения, индексированные i и j переменные. Адрес, который рассчитан для отыскания данных указывает на Пространство Данных! А не на Пространство Программы, где данные расположены на самом деле. Вероятно, что Вы найдёте там некоторый мусор. Проблема – в том, что AVR GCC не знает, что данные находятся в Пространстве Программы.

Решение довольно просто. "Правило бегунка" - чтобы обращаться к данным, сохраненным в Пространстве Программы состоит в том, что для того чтобы обратиться к данным, поскольку Вы обычно были бы (как будто переменная сохранена в Пространстве Данных), подобно так:

byte = mydata[i][j];

тогда возьмите адрес данных:

byte = &(mydata[i][j]);

тогда используйте соответствующий pgm_read_* макрокоманда, и адрес ваших данных становятся параметром для той макрокоманды:

byte = pgm_read_byte(&(mydata[i][j]));

pgm_read_* макрос берет адрес, который указывает на Пространство Программы, и отыскивает данные, которые сохранены в том адресе. Это - то, почему Вы берете адрес смещения в массив. Этот адрес становится параметром для макрокоманды, так что это может генерировать правильный код, чтобы отыскать данные в Пространстве Программы. Там отличны pgm_read_* макрос, чтобы читать отличные размеры данных в данном адресе.

Сохранение и поиск строк в пространстве программ

Now that you can successfully store and retrieve simple data from Program Space you want to store and retrive strings from Program Space. And specifically you want to store and array of strings to Program Space. So you start off with your array, like so:

char *string_table[] = 
{
    "String 1",
    "String 2",
    "String 3",
    "String 4",
    "String 5"
};

and then you add your PROGMEM macro to the end of the declaration:

char *string_table[] PROGMEM = 
{
    "String 1",
    "String 2",
    "String 3",
    "String 4",
    "String 5"
};

Right? WRONG!

Unfortunately, with GCC attributes, they affect only the declaration that they are attached to. So in this case, we successfully put the string_table variable, the array itself, in the Program Space. This DOES NOT put the actual strings themselves into Program Space. At this point, the strings are still in the Data Space, which is probably not what you want.

In order to put the strings in Program Space, you have to have explicit declarations for each string, and put each string in Program Space:

char string_1[] PROGMEM = "String 1";
char string_2[] PROGMEM = "String 2";
char string_3[] PROGMEM = "String 3";
char string_4[] PROGMEM = "String 4";
char string_5[] PROGMEM = "String 5";

Then use the new symbols in your table, like so:

PGM_P string_table[] PROGMEM = 
{
    string_1,
    string_2,
    string_3,
    string_4,
    string_5
};

Now this has the effect of putting string_table in Program Space, where string_table is an array of pointers to characters (strings), where each pointer is a pointer to the Program Space, where each string is also stored.

The PGM_P type above is also a macro that defined as a pointer to a character in the Program Space.

Retrieving the strings are a different matter. You probably don't want to pull the string out of Program Space, byte by byte, using the pgm_read_byte() macro. There are other functions declared in the <avr/pgmspace.h> header file that work with strings that are stored in the Program Space.

For example if you want to copy the string from Program Space to a buffer in RAM (like an automatic variable inside a function, that is allocated on the stack), you can do this:

void foo(void)
{
    char buffer[10];
    
    for (unsigned char i = 0; i < 5; i++)
    {
        strcpy_P(buffer, (PGM_P)pgm_read_word(&(string_table[i])));
        
        // Display buffer on LCD.
    }
    return;
}

Here, the string_table array is stored in Program Space, so we access it normally, as if were stored in Data Space, then take the address of the location we want to access, and use the address as a parameter to pgm_read_word. We use the pgm_read_word macro to read the string pointer out of the string_table array. Remember that a pointer is 16-bits, or word size. The pgm_read_word macro will return a 16-bit unsigned integer. We then have to typecast it as a true pointer to program memory,PGM_P. This pointer is an address in Program Space pointing to the string that we want to copy. This pointer is then used as a parameter to the function strcpy_P. The function strcpy_P is just like the regular strcpy function, except that it copies a string from Program Space (the second parameter) to a buffer in the Data Space (the first parameter).

There are many string functions available that work with strings located in Program Space. All of these special string functions have a suffix of _P in the function name, and are declared in the <avr/pgmspace.h> header file.

Протесты

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

 

Hosted by uCoz