В первой части мы рассмотрели основные характерситики и параметры интерфейса внешней памяти микроконтроллера ATmega128, схему и интерфейс подключения модуля расширения.
Примечание. Выборки исходного кода, размещенные в тексте описания, использовать в своих проектах не рекомендуется. В конце описания имеется ссылка на архив с полным исходным кодом к проекту.
После сборки модуля необходимо протестировать его работу. Для этого напишем несколько программных функций реализующих алгоритм размещения памяти и чтения/записи данных.
Сперва необходимо настроить проект в среде разработки AVRStudio. При использовании автоматической генерации make-файла, нам необходимо добавить опции, связанные с параметрами внешней SRAM, таким образом, мы указываем компилятору адресное пространство для распределения ОЗУ. Для этого в настройках проекта (меню Project->Configuration Options) найдем закладку Custom Options и в параметрах компоновщика (Linker Options) впишем строку:
-Wl,--section-start,.data=0x801100,--defsym=__heap_end=0x8030ff
Это указывает компоновщику начальный и конечный адрес внешнего ОЗУ, а также указывает, что данная память используется для хранения переменных и хипа (Heap область – это область памяти, динамически распределяющаяся приложением)
В соответствии с такой конфигурацией мы имеем диапазон адресов: для внутреннего ОЗУ 0×800000 – 0x8010FF, для внешнего ОЗУ 0×801100 – 0x8030FF и поскольку мы используем 8 КБайт внешней памяти, то к концу адресного пространства внутреннего ОЗУ (0x8010FF) мы прибавляем 0×2000. Всего мы получаем 12 КБайт ОЗУ, причем внутренняя память используется под стек (STACK), а внешняя – для хранения данных (секция .data), глобальных и статических переменных (секция .bss) и хипа (heap).
Для проверки внешней памяти есть несколько способов. Мы будем использовать функцию malloc(), которая просто распределяет некоторый объем памяти и возвращает начальный адрес указателю. После конфигурирования проекта мы можем приступить к написанию кода.
Прежде, чем мы сможем использовать внешнюю память, нам необходимо разрешить ее использование установкой бита SRE в регистре управления MCUCR – это действие переключает специализированные выводы микроконтроллера на XMEM интерфейс. Также мы можем освободить неиcпользуемыме в интерфейсе XMEM выводы для других функций (выводы PC5, PC6, PC7)
void XMEM_init(void)
{
// External memory interface enable
MCUCR |= (1<SRE);
XMCRA = 0;
//PC7..PC5 released pins
XMCRB |= (1<XMM1)|(1<XMM0);
}
После инициализации мы можем приступить к написанию основной программы. Простой код распределяет 255 Байт в хипе (куче) во внешней памяти:
#define BUFFER_SIZE 255
uint8_t *mem;
mem = malloc(BUFFER_SIZE);
После распределения мы можем заполнить его некоторыми данными и затем считать их, чтобы удостовериться в правильности работы:
uint8_t index;
// Fill memory incrementing values
for(index = 0; index < BUFFER_SIZE; index++)
{
mem[index] = data++;
}
// Display memory block
for(index = 0; index < BUFFER_SIZE; index++)
{
PRINTF("%02X ",mem[index]);
if((index&0x0F) == 0x0F)
{
PRINTF("/n");
}
}
В окне терминальной программы мы получим следующее сообщение:
В этом случае начало heap-памяти находится по адресу 0×1221. Это означает, что секции .data и .bss находятся в адресном пространстве от 0×1100 до 0x121E. Проверить это можно, посмотрев .map файл. В нашем случае начальный адрес секции .data 0×1100, .bss секции - 0×1114. Указатель *mem указывает на начальный адрес heap 0x121E.
Рассмотрим другую ситуацию, когда вся область внешней памяти является областью, динамически распределяемой приложением. Для этого необходимо изменить конфигурацию компоновщика:
-Wl,--defsym=__heap_start=0x801100,--defsym=__heap_end=0x8030ff
Распределение памяти в этом случае будет такое:
После компиляции программы в окне терминальной программы мы увидим:
Как вы видите, область heap размещается с самого начала внешней памяти. Первые два байта используются для хранения размера распределенной области памяти. В .map файле мы видим:
.data 0x00800100
.bss 0x00800114
__heap_start = 0x801100
__heap_end = 0x8030ff
Сектор .data находится в начале внутреннего ОЗУ, затем следует сектор .bss, а область heap занимает весь объем внешнего ОЗУ.
Какую модель распределения памяти использовать – решает сам пользователь при разработке своего приложения. Еси вы рассчитываете использовать объемные буферы данных хранения графической информации для дальнейшего вывода на дисплей – определенно необходимо распределять внешнюю память только для heap. Если планируется реализация операционной системы реального времени с большим количеством задач – то, вероятно, лучше оставлять больше внутреннем ОЗУ для стека, а статические данные переместить во внешнее ОЗУ.
Загрузки
Исходный код к проекту - скачать.