«AVR шаг за шагом», версия для печати. Исходный документ: https://avr.ru/beginer/avrsbs/step6

что то с памятью моей стало

Память.

По сути, внутренняя память устройства - это те же регистры, но с несколько иной системой доступа. Мы не можем обращаться к ним напрямую как к рабочим регистрам. Если следовать аналогии с фирмой - то ОЗУ это некий архив, в который мы не можем пойти и поработать, но заказанную нами информацию из архива нам принесут, и если нужно - поместят туда нашу информацию.

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

В системе команд микропроцессора 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:
;==========================================================

 

Таким образом можно к примеру шифровать- дешифровать сообщения, и ни один "самоучка с долбагером" не вскроет ваш алгоритм шифрования, так как он будет намертво зашит в железе. (если конечно сам алгоритм криптостойкий.). Скачать исходник и прошивку примера работы с памятью можно здесь (http://avr.ru/int/Files/Dounload/sbs/sbs_06.zip).

 Далее ---> (http://avr.ru/beginer/avrsbs/step7)

Все права защищены © AVR.RU, 2021.