Работа с оперативной памятью
Описание: Описание принципа взаимодействия с памятью в контроллерах avr
Итак, наши знания контроллеров расширяются, наши потребности возрастают, и шестнадцати отведенных нам судьбой регистров общего назначения становится уже недостаточно. Недостаточно??? Ну тогда вот вам еще: 128 байт оперативки.
Слышу нервные смешки в аудитории… Опять кто-то вспомнил свой 3-х гиговый Пентиум? Понимаю, случается. Но я уже где-то говорил: контроллер не стоит сравнивать с современными компьютерами ни по каким параметрам. У контроллера несколько иная сфера деятельности, и имеющихся у него параметров вполне достаточно для выполнения большинства задач в этой сфере.
Итак, мы имеем 128 байт оперативной памяти (ОЗУ). Смотрим рисунок из даташита:
Рисунок 1 – Вся оперативная память, имеющаяся в контроллере. Она занимает диапазон значений $00…$DF , то есть – 224 байта.
Младшие 96 байт отведены для РОН (32 байта) и регистров ввода вывода (64 байта). К регистрам ввода-вывода относятся регистры конфигурации таймеров, портов, прочего железа, статус-регистр SREG , контрольный регистр MCUCR и прочие. Короче, все, которые мы можем записать командой out или прочитать командой in . Вот они, полюбуйтесь:
Диапазон $60…$ DF занимает «оперативка» - ячейки памяти, с которыми мы можем делать все, что душе угодно.
А что может быть угодно душе:
sts - Store Direct to SRAM - прямая запись в ОЗУ. ds - Load Direct from SRAM - прямая загрузка из памяти Пример: sts 0x62, Temp ;ячейка памяти задана адресом sts MemoryCell,Temp ;ячейка памяти задана именем lds Temp1, 0x71 lds Temp1, MemoryCell_2
Пока что пусть этого душе будет достаточно.
Итак, самый простой способ – это взять бумажку, выписать на нее все переменные, которые хотим хранить в памяти, и присвоить им адреса из диапазона $60..$ DF .
Еще один момент: если мы юзаем стек, то лучше ничего не писать в старшие несколько байт оперативки, потому что именно старшая область ОЗУ отведена для стека.
Однако, бумажка имеет свойство теряться. Кроме того – это лишняя головная боль: назначать адреса ячейкам памяти. Да и работать с программой, в которой все переменные обозваны шестнадцатеричными числами, довольно затруднительно.
Выход есть! Мы можем взвалить эту неблагодарную работу на плечи компилятора! Ура товарищи, радостно выдохнули и читаем дальше.
Оказывается, компилятор сам в состоянии решить подобные проблемы. Нам даже не придется заморачиваться с адресами – он сам раскидает переменные по адресам, как ему покажется удобно. Все что нам нужно – это прописать имена переменных и указать, сколько ячеек памяти необходимо выделить каждой переменной. Мы уже называли страшные слова CSEG и DSEG .
CSEG – это программный сегмент – в нем пишется непосредственно, программулина. DSEG – это сегмент данных. В нем выделяется оперативная память. Сегмент данных прописывается в тексте раньше программного сегмента.
.include "d:avravrasmappnotes2313def.inc" .def Temp1=R16 .def Temp2=R17 .def Temp3=R18 .def Temp4=R19 .def Temp=R20 .dseg Digit: .byte 4 Input: .byte 2 Status: .byte 1
Таким образом, мы только что выделили 7 ячеек памяти:
4 ячейки под переменную Digit ,
2 ячейки под переменную Input ,
1 ячейку под переменную Status.
1-байтные переменные пишутся непосредственно командой sts и читаются командой lds .
Если переменная состоит из нескольких ячеек, то при операции следует указывать относительный адрес той, к которой мы обращаемся. При этом, имя переменной всегда указывает на нулевую ячейку.
Для того чтобы переместиться на n -ю ячейку, нужно обратиться к адресу, на n большему нулевой ячейки.
Обращение происходит так:
lds Temp, Digit ;загружаем в Temp ;0-ю ячейку переменной Digit lds Temp1,Digit+1 ;в Temp1 - 1-ю ячейку Digit lds Temp2,Digit+2 ;в Temp2 - 2-ю ячейку lds Temp3,Digit+3 ;в Temp3 - 3-ю ячейку
Вот так все хитро и запущено в этом несовершенном мире. Дальше будет еще хуже.