что то с памятью моей стало
Память.
По сути, внутренняя память устройства - это те же регистры, но с несколько иной системой доступа. Мы не можем обращаться к ним напрямую как к рабочим регистрам. Если следовать аналогии с фирмой - то ОЗУ это некий архив, в который мы не можем пойти и поработать, но заказанную нами информацию из архива нам принесут, и если нужно - поместят туда нашу информацию.
То есть для работы, с какой либо ячейкой памяти нам необходимо сначала скопировать информацию из нее в рабочий регистр, затем выполнив необходимые действия скопировать информацию обратно в память.
В системе команд микропроцессора AVR есть различные варианты обращения к оперативной памяти.
- прямая адресация.
- косвенная адресация
- косвенная адресация со смещением.
- косвенная адресация с авто инкриментом/декриментом.
Самая простая - это прямая адресация. выполняется командами
lds reg,number
sts reg,number
Эти команды работают с регистром (reg) и ячейкой памяти с номером (number). Команда lds грузит (load) значение из памяти в регистр, а команда sts соответственно устанавливает (set) значение регистра в память.
Косвенная адресация - это адресация по указателю(адресу) записанному в рабочем регистре.
ld reg,longreg
st reg,longreg
Здесь адрес читаемой (записываемой) ячейки находится в регистре longreg. Так как размер ОЗУ чипа может быть больше 256 байт, то используется двойной регистр. это регистровые пары R26:R27(регистр X), R28:R29(регистр Y), R30:R31(регистр Z).
Косвенная адресация со смещением - это гибрид первых двух способов. когда при записи и чтении используется указатель longreg и смещение number. Такие форму адресации удобно применят, когда речь идет о полях структуры или о массиве. В первом случае регистровая пара указывает на начало структуры, а константа - на смещение поля структуры относительно начала. Во втором - начало таблицы - константа, а индекс - указатель.
С авто инкрементом/декрементом - это практически а же косвенная адресация, только позволяющая одновременно изменять указатель. Таким образом, экономится несколько команд и машинных циклов, что достаточно важно при решении задач реального времени.
Следует отметить, что некоторые команды применимы и в моделях микрочипов, где нет памяти. Как видно из рисунка и рабочие регистры, и регистры ввода вывода и память расположены в одном адресном пространстве. По этому, часть регистров общего назначения можно использовать как небольшой массив.
Стек.
Еще один вариант использования
памяти - это стек. Данные в стек (из стека) можно поместить командами PUSH /
POP. Кроме того данные помещаются в стек при вызове (выходе)
подпрограмм командами RCALL / RET.
Сам стек расположен в «нижней» части ОЗУ и «растет» в верх. Для работы процессора со стеком предусмотрен указатель в виде регистров ввода вывода SPH:SPL. Это указатель на вершину стека. То есть на байт следующий за «загнанным» в стек последним. Изначально он должен указывать на самый последний байт памяти (константа RAMEND)
ldi temp,Low(RamEnd)
out SPL,temp
ldi temp,HIGH(RamEnd)
out SPH,temp
Этот кусок кода должен быть выполнен при инициализации процессора, если мы хотим использовать вызовы подпрограмм и прерывания. Однако следует учесть, что в некоторых моделях, не имеющих ОЗУ, реализован аппаратный стек, не требующий инициализации.
передачи данных в подпрограммы и из них.
push R10
push R15
call Shift
pop R10
///
push R14
push R16
call Shift
pop R14
///
Shift:
pop temp1
pop temp2
pop Shifter
pop Shifted
(shifted<<shifter)
push Shifted
push temp2
push temp1
ret
Запомните приведенный выше пример и НИКОГДА так не делайте. Хотя данный кусок кода призван продемонстрировать возможности работы со стеком, он, по сути, является ярким примером «вирусного» стиля написания программ, хоть и эффективного, но весьма неряшливого.
пример.
Следующий пример тоже не отличается изысканностью кода, однако дает представление о работе с памятью. Эта программа зеркально отображает полученную через СОМ-порт строку.
;========================================================== ;Autor: MadMayDay 2008 ;Project: StepByStep ;Name: USART-USART ;========================================================== .device ATtiny2313 .include "tn2313def.inc" .def SlopReg=R16 .def ViewReg=R18 .def Char=R19 .def CharCount=R20 .def NextChar=R21 .equ String=SRAM_START .equ StringSize=SRAM_SIZE ;========================================================== Start: rjmp Init Init: ;---------------------------------------------------------- ; подготовка приемо передатчика USART ; кроме делителя ничего менять не надо все настроенно на работу с компьютером. ; прерывания не используются. ; вход RXD- PD0(pin2), выход TXD- PD1(pin3) ldi SlopReg,0 ; 19200 бод на частте кварца 8 мегагерц out UBRRh,SlopReg ; Делитель USART = 25 ldi SlopReg,25 ; заливаем в регистр UBRR out UBRRl,SlopReg ; не забывая, что его размер 2 байта ldi SlopReg,0b00000110 ; 8 бит. 1 стоп. без четности out UCSRC,SlopReg ; заливаем в регистр управления UCSRC ldi SlopReg,0b00011000 ; признаки прием и передача разрешена. out UCSRB,SlopReg ; заливаем в регистр управления UCSRB ;---------------------------------------------------------- ; Читаем строку из COM-порта и если она принят без ошибок ; передаем в COM-порт как зеркальное отражение. NewString: ;---------------------------------------------- ldi Zh,High(String) ;Подготавливаемся к приему строки. ldi Zl,Low(String) ;ставим указатль на начало строки ldi CharCount,0 ;Длинна строки равна нолю ;---------------------------------------------- NoData: in SlopReg,UCSRA ; Считываем регистр состояния USART bst SlopReg,RXC ; и запоминаем флаг принятости байта. brtc NoData ; если запомненный флаг не установлен ; то, ждем еще раз. ;---------------------------------------------- ; если символ принят, то bst SlopReg,FE ; запоминаем флаг ошибки. in NextChar,UDR ; и считываем принятый байт brts NoData ; если флаг ошибки установлен ; не отображаем символ, а ждем следующий ;---------------------------------------------- cpi NextChar,$0a ; введенный символ - <br> breq NoData ; не печатаем и не запоминаем. cpi NextChar,$0d ; введенный символ - <cr> breq SendData ; Необходимо вывести результат cpi CharCount,StringSize ; проверка на переполнение brsh NoData ; не печатаем и не запоминаем. ;---------------------------------------------- ;если пришел простой символ то st Z+,NextChar ;записываем введенный символ в строку inc CharCount ;увеличиваем счетчик символов в строке. rjmp NoData ;и ждем следующего символа. ;указатель при этом сдвигается ;и указывает на следующую ячейку памяти ;---------------------------------------------- ;если пришла пора напечатать строку SendData: cpi CharCount,0 ;пока длинна строки больше ноля breq Finish ; ld NextChar,-Z ;берем последний символ ;z-всегда указывает на хвост строки out UDR,NextChar ;и восылаем его в СОМ-порт SendDelay1: sbis UCSRA,UDRE ;пока символ не передан - rjmp SendDelay1 ;"курим бамбук" ;после передачи символа dec CharCount ;уменьшаем длину строки rjmp SendData ;и повторяем со следующим символом... Finish: ldi NextChar,$0d ;дополняем переданную последовательность out UDR,NextChar ;символами переноса строки SendDelay2: sbis UCSRA,UDRE; ;(разделив их паузой на передачу) rjmp SendDelay2 ; ldi NextChar,$0a ;и возврата каретки out UDR,NextChar ; rjmp NewString ; и снова ждем ввода символов End: ;========================================================== |
Таким образом можно к примеру шифровать- дешифровать сообщения, и ни один "самоучка с долбагером" не вскроет ваш алгоритм шифрования, так как он будет намертво зашит в железе. (если конечно сам алгоритм криптостойкий.). Скачать исходник и прошивку примера работы с памятью можно здесь.