AVR. Учебный Курс. Использование EEPROM
Автор DI HALT
Опубликовано 08 Сен 2008
Рубрики: AVR. Учебный курс
Метки: Assembler, AVR, EEPROM, От Автора, Память
Иногда нужно сохранить данные так, чтобы они восстановились после перезагрузки контроллера. В этом тебе поможет EEPROM, почти все контроллеры серии AVR имеют на борту некоторое количество этой памяти. Физически и логически эта память находится в отдельном адресном пространстве, а чтение из EEPROM и запись туда осуществляется через специальные порты.
Чтобы что-то записать в EEPROM нужно в регистры адреса EEARH и EEARL (EEPROM Address Register) положить адрес ячейки в которую мы хотим записать байт. После чего нужно дождаться готовности памяти к записи – EEPROM довольно медленная штука. О готовности к записи нам доложит флаг EEWE (EEPROM Write Enable) регистра управления состоянием EECR, когда он будет равен 0, то память готова к следующей записи. Сам байт, который нужно записать, помещается в регистр EEDR (EEPROM Data Register). После чего взводится предохранительный бит EEMWE (EEPROM Master Write Enable), а затем, в течении четырех тактов, нужно установить бит EEWE и байт будет записан. Если в течении четырех тактов не успеешь выставить бит EEWE то предохранительный бит EEMWE сбросится и его придется выставлять снова. Это сделано для защиты от случайной записи в EEPROM память.
Чтение происходит примерно аналогичным образом, вначале ждем готовности памяти, потом заносим в регистры нужный адрес, а затем выставляем бит чтения EERE (EEPROM Read Enable) и следующей командой забираем из регистра данных EEDR наше число, сохраняя его в любом регистре общего назначения. Чтобы было понятно, я тебе набросал две процедурки – на чтение и на запись. Чтобы записать байт, нужно в регистры R16 и R17 занести младший и старший байт адреса нужной ячейки, а в регистр R21 байт который мы хотим записать. После чего вызвать процедуру записи. Аналогично и с чтением – в регистра R16 и R17 адрес, а в регистре R21 будет считанное значение.
Вот так выглядит запись в память:
1 2 3 4 5 | … LDI R16,0 ; Загружаем адрес нулевой ячейки LDI R17,0 ; EEPROM LDI R21,45 ; и хотим записать в нее число 45 RCALL EEWrite ; вызываем процедуру записи. |
А так чтение:
1 2 3 4 | LDI R16,0 ; Загружаем адрес нулевой ячейки LDI R17,0 ; EEPROM из которой хотим прочитать байт RCALL EERead ; вызываем процедуру чтения. После которой ; в R21 будет считанный байт. |
Ну и, разумеется, сами процедуры чтения и записи
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | EEWrite: SBIC EECR,EEWE ; Ждем готовности памяти к записи. Крутимся в цикле RJMP EEWrite ; до тех пор пока не очистится флаг EEWE CLI ; Затем запрещаем прерывания. OUT EEARL,R16 ; Загружаем адрес нужной ячейки OUT EEARH,R17 ; старший и младший байт адреса OUT EEDR,R21 ; и сами данные, которые нам нужно загрузить SBI EECR,EEMWE ; взводим предохранитель SBI EECR,EEWE ; записываем байт SEI ; разрешаем прерывания RET ; возврат из процедуры EERead: SBIC EECR,EEWE ; Ждем пока будет завершена прошлая запись. RJMP EERead ; также крутимся в цикле. OUT EEARL, R16 ; загружаем адрес нужной ячейки OUT EEARH, R17 ; его старшие и младшие байты SBI EECR,EERE ; Выставляем бит чтения IN R21, EEDR ; Забираем из регистра данных результат RET |
Да, при работе с EEPROM нужно в цикле ожидания готовности не забывать командой WDR сбрасывать Watch Dog Timer - специальный сторожевой таймер, отслеживающий зависание процессора. Если его не сбрасывать с нужной периодичностью, то он сбрасывает контроллер. Это, конечно, если Watch Dog используется. По дефолту он вырублен. Но помнить надо, иначе огребете трудно отслеживаемый глюк.
Впрочем, у EEPROM тоже есть свои прерывания. Это:
1 2 | .ORG $01E RETI ; (EE_RDY) EEPROM Ready |
И никто не помешает выбросить цикл ожидания и сделать массовую запись в ЕЕПРОМ на прерываниях! Аналогично как это сделано для USART. А если надо что то сохранить очень быстро, то можно и буферизированную с пробросом через RAM таким же образом запись заюзать. Т.е. сначала быстро сожрали в оперативку, а потом, неспеша, по прерываниям, загнать в EEPROM.
Комментарии
61 комментариев на «AVR. Учебный Курс. Использование EEPROM»
Оставьте свой отзыв
Вы должны войти, чтобы оставлять комментарии.





Спасибо! Отличная статья! Как раз сейчас нужно будет использовать EEPROM. Конечно можно прочитать даташит, что я обычно и делаю, но там все довольно разбросано, а здесь раз прочитал и в голове сложилась четкая структура.
P.S. Еще интересно, может у тебя уже был опыт по использованию карт памяти SD/MMC? Там же вроде элементарно по SPI с ними общаться.
До SPI пока руки не доходили. А так да, в планах. С этими карточками проблема составляет не SPI доступ, а FAT :)
Впринципе туда можно любую файловую систему зарулить. Хоть Fat, хоть Ext2fs, хоть вобще BSD’шную файловую систему.
А описаний и документации, как работать с этими системами хоть отбавляй!
Хочу использовать SD катрочку в автомобильном логгере (запись показаний датчиков системы впрыска)
Забыл написать, что у атмеловских контроллеров есть так называемая “мертвая зона” EEPROM-a. У 64й атмеги, например, это все адреса от 0×00 - 0×100. Так же была замечена тенденция (по крайней мере на атмегах), чем круче модель (ATMeag32->64->128), тем больше у нее мертвая зона EEPROM. Причем в документации, про нее нифига не написано и подбирать придется в ручную. Ах да, чем же она такая мертвая эта зона. А тем, что запись и чтение в ней происходят через раз, а то и не происходят вообще. Дрочится конечно можно, но лучше оставить эти 100 адресов на советси разработчиков и работать со стабильным ПЗУ.
Может это контроллеры бракованные были? Или может запись производилась без проверки готовности EEPROM’а?
Нет, контроллеры н бракованные и запись проводилась так как нужно, будь уверен.
День добрый!
Ребята, вы все занимаетесь электроникой, помогите решить задачу, пожалуйста!!!
Для вас это не составит труда, а человеку поможете.
Задание:
Разработать систему измерения частоты и скважности сигнала с ТТЛ – совместимыми уровнями и вывод результата на индикатор МТ-16S2х (частота – верхняя строка, скважность - нижняя). Диапазон измеряемых частот - 1÷1000 Гц. Абсолютный шаг измерения скважности – не менее 0,1 мс. Период накопления данных при измерении частоты – 1 секунда.
ядро м/п системы:
ATMEGA-8, ATTINY2313 (AVR)
Нужны:
расчетные параметры устройств (адреса, режимы работы и.т.д.),
принципиальную электрическую схему системы,
блок-схему программы, программу на языке ассемблера.
Помогите пожалуйста, я не понимаю в этом ничего. А вам может будет интересно.
Зарание спасибо.
Не, нам это не интересно уже. Если будут _конкретные_ вопросы, на которые можно будет дать четкий ответ, то задавай.
Я бы с радостью задала вопрос, но я в этом ничего не понимаю.
Это в институт надо принести, я студентка.
Дали такое задание.
Помогите пожалуйста.
Я могу заплатить за работу.
Просто очень надо, мне больше не к кому обратится.
Я догадался.
Ну что я могу сказать. Вы, похоже, сильно ошиблись специальностью, мне вас искренне жаль. В таком случае ничем помочь не могу. Если кто из комментов откликнется, то может быть вам повезет.
Блин, парюсь уже часа 3 с этим EEPROM
В .ESEG объявил константы, так Студия не цапает их сама при эмуляции - пришлось вручную через меню указывать файл .epp. Ладно, вроде заработало.
Но!! В Протеусе никак не могу загрузить данные EEPROM. Уж и конвертер hex2bin скачал, .epp файл конвертнул в .bin, указал в настройках ATMega8 - Initial Contens of EEPROM этот файл. А нифига - смотрю в паузе на содержимое EEPROM - везде 0xFF
Помогите, плиз!
Хм… Помог сброс данных модели и последующий выбор .bin файла….
И все-таки протеус очень веселый….
З.Ы. Студия тоже не фонтан… Сменил в простеньком проекте мегу8 на мегу 32 - так студия перестала показывать состояние всех РВВ… Создал пустой проект, скопипастил код туда - все заработало :-) Чудеса просто…
А вот такой насущный вопрос:
Собираю схему электронного одометра. Надо будет хранить пробег включая сотни метров. Ясно, что возможность записи в EEPROM закончится очень быстро. Вопрос: 100,000 записей это для всей памяти либо для одной ячейки? Т.е. могу ли я записать в оду ячейку 100000 раз потом перейти к следующей и т.д. таким образом использовать EEPROM долгое время? Если нет, то какой выход в данной ситуации можно посоветовать?
Спасибо!
1 применить память типа FRAM от Ramtron у ней число циклов перезаписи такое, что скорей у твоей машины все молекулы сотрутся.
2 держать данные в памяти, а на епром сбрасывать раз в 10-15 минут.
И 100 000 это для каждой ячейки.
Почему фигня в еепром при включении питания пишется? бодлевел вроде поставил.
А боден?
тоже –всё согласно даташиту(тини2313).
Я программлю в CodeVision AVR так там в хэлпе вот такая тема:
Accessing the AVR internal EEPROM is accomplished using global variables, preceded by the keyword eeprom.
Example:
/* The value 1 is stored in the EEPROM during chip programming */
eeprom int alfa=1;
eeprom char beta;
eeprom long array1[5];
/* The string is stored in the EEPROM during chip programming */
eeprom char string[]=”Hello”;
void main(void) {
int i;
/* Pointer to EEPROM */
int eeprom *ptr_to_eeprom;
/* Write directly the value 0×55 to the EEPROM */
alfa=0×55;
/* or indirectly by using a pointer */
ptr_to_eeprom=&alfa;
*ptr_to_eeprom=0×55;
/* Read directly the value from the EEPROM */
i=alfa;
/* or indirectly by using a pointer */
i=*ptr_to_eeprom;
}
то есть доступ к еепрому понимается как доступ к обычной переменной. у меня мега8, я просто создал массив на 512 char-ов и пользуюсь. Отлично работает :)
1. Ошибка. В подпрограмме чтении байта проверяется флаг готовности записи.
2. В статье нет никакого упоминания о EECR (EPROM Control Register), он встречается только в тексте программы. Откуда будем брать флаги готовности?
1. Не ошибка. Флаг записи при чтении надо проверять чтобы быть уверены что читаем мы последние актуальные данные.
2. Добавил в текст, спасибо.
По пункту 1 последнего комментария.
А зачем проверять? Если только в контексте данного примера, учитывая, что идет сначала запись, а затем сразу же чтение и там где сейчас стоит многоточие нет кода с приличным количеством тактов? А если я просто читаю данные уже находящиеся в EEPROM, то тоже надо проверять EEWE? Получается не очень понятно из этого примера как считывать данные. imho надо было не выходить из подпрограммы записи пока не установится EEWE, т.е. убедиться что запись гарантированно завершена, тогда не понадобиться ожидать её завершения при чтении. Или я не прав? Очень неоднозначный пример получился.
В свете применения RTOS или подобной диспетчеризации проверять нужно всегда. Т.к. в разных процессах чтение и запись могут быть очень близко друг от друга по времени. А ждать окончания записи в процедуре записи еденичного байта это лишняя потеря времени. Лучше тогда отдать управление диспетчеру, а запись массива делать не на ожидании флага, а с использованием прерывания готовности — не будет потерь времени, запись пройдет фоном.
Так что пример очень однозначный и совершенно универсальный. В конце концов, так гласит даташит. =)
Не, всё равно пример не хороший или эту проверку надо в статье детально описать - зачем она нужна. Ведь в даташите конкретно расписано, что EEWE надо проверять перед чтением, т.к. возможен вариант, что запись ещё не завершена. А если я вообще ничего в EEPROM не пишу? У меня вот программа специфическая, использует подбираемые мной константы задержки расположенные в EEPROM, а сам EEPROM я дома программирую, не трогая саму программу во FLASH. Зачем мне тогда EEWE? Совершенно не зачем.
А если я использую такую подпрограмму записи без контроля того, закончена ли запись, то возможны проблемы. Вызвал подпрограмму, записал байт и решил после этого ресетнутся и всё, кирдык - последний байт не записался. Лучше уж в подпрограмме дождаться флага готовности и тогда выходить - данные целее будут.
Всех вариантов использования не опишешь.
Я указал самый очевидный и наиболее универсальный.
Тем более в комментах к проге указано с какой целью делаем проверку готовности при чтении. Собственно, имхо, тут более пояснения и не требуются.
А извраты вроде программного ресета после записи в епром это уже экзотика и если такое делаешь, то надо четко понимать последствия и и к чему это может привести. Ради такого события тупить в процедуре записи до окончания ее это нерационально с точки зрения расхода процессорного времени. Если уж хочется ресет по окончании — взвел флаг на необходимость ресета, а ресет сделается уже из прерывания по готовности епрома.
может невтему, но у меня возникла проблема: при выполнении программы заполняется пространство ОЗУ (начинает заполняться с конца), это видно в протеусе. Я думаю это из-за записи адресов команд в стек после прерываний, в некоторых случаях я не использую reti после выполнения обработки прерывания. Можно ли как-то очистить стек? И почему такое происходит?
микроконтроллер: MEGA16
Вот потому что не выходишь из прерывания по RETI у тебя стек и срывает. Выходи из прерывания правильно
а стек можно очистить?
Тебе надо восстановить стековый указатель. Такс. Адрес у нас два байта. Сделай два раза подряд POP куда нибудь в ненужный регистр. ДОлжно помочь.
Всем Привет. Я вот тут тоже парюсь с EEPROM, в студии подгрузил файл *.eep а вот в протеусе чет никак не получается.
Добавил в свойства контроллера в протеусе такую строку:
{MODDATAFILE=..\MotoController\MotoController.eep}
он стал подгружать память, но она не такая как должна быть, в студии совсем иначе. Ваще не понятно откуда протеус взял такое.
Раньше вроде в протеусе можно было выбрать не только файл прошивки но и файл епрома. Разве сейчас не так?
протеус вставляет в память тоже самое если открыть файл *.eep нотпадом.
я методом научного тыка пытался изменить ееп файл так как мне надо, так протеус на изменения забил и вставляет в память то что было изначально в ееп файле.
хм… возможно он ее грузит как бинарик, а еееп в хекс формате. Или наоборот, он жрет ее как хекс ,а ееп в бинаре. Я не помню формат ееп. Если что hex2bin и bin2hex утилиты тебе в помощь.
{MODDATAFILE=..\MotoController\MotoController.eep} это строка и означает вставку файла памяти, но как я написал делает это не так как надо
Ну, спасибо! знал бы как поставить пива, не пожалел бы. Буду сёдня экспериментировать.
Может вопрос не по теме, но все же. Разработал на базе меги 8535 тренажер, весь код залил в память программ, а нужные для работы данные в EEPROM. Т.е. получилось два
файла [имя].hex [имя].eep, которые были успешно залиты в кристалл. Таких кристалла 4 шт., и вот в чем фокус один работает как должен, а три нет. Проверил сами кристаллы -вроде живы, проверил залитые файлы - во всех кристаллах идентичные. В чем может быть косяк?
Классная статья только что ее применил в Atmega8L, все работает. После отключения питания и его восстановления сперва данные считываются из EEPROM если там 1 то моя плата входит в нужный мне режим работы если нет то и нет. Только заметил что в этой статье не описывается флаг EERIE. Флаг включения вектора прерывания после завершения процедуры записи в EEPROM.
А еще у меня возник вопрос. В статье в примере взят начальный адрес для EEPROM 0×00, а какой может быть конечный адрес если в Atmega8 512 байт EEPROM? В даташите не нашел адресной карты ЕЕПРОМА. Для ОЗУ и для флэш памяти нашел. Но если логично предположить что у первого байта адрес 0×00 то у 512 байта он равен 0×1FF то есть 511. Так наверное.
Ответ нашел таки в даташите, невнимательно смотрел. Так и есть у 512 байта конечный адрес 511. Получается адреса eeprom у atmega8 находятся в диапазоне от 0 до 511 или 0×00 до 0×1FF.
Добрый день уважаемый DI HALT!)
Помогите разобраться - задыхаюсь!(
Я новичок поэтому не судите строго…
Мне необходимо создать динамическую таблицу констант, ну т.е. записать массив из 55 чисел и затем работать с ними (пишу на ассемблере), а результат снова записывать в этот массив, ну вот это и будет динамическая таблица.
1. Куда записывать ? Насколько я понял нет директивы записи массива чисел в Рам, есть .db для записи байтов во флэш либо в EEPROM… Во флэш я так понимаю, не прокатит, так как надо обновлять данные, тогда EEPROM остаётся…
2.Облазил кучу ссылок - не могу найти пример записи в EEPROM массива на азме. В вашем примере
всё понятно, но он для записи отдельной константы. Можете помочь с куском кода для записи массива в EEPROM ?
А чем массив отличается от одного байта? Тем что там много байт. В ассемблере нет такого понятия как массив. Это все абстракция программиста. У тебя есть некоторая область в памяти. Это твой массив. У ней есть первая ячейка - начальный адрес, а адреса остальных ячеек вычисляются простым смещением относительно этой первой ячейки. Берешь и последовательно, побайтно записываешь все нужные тебе данные.
В ОЗУ можешь забабахать свой массив в виде:
mass: .byte 55
55 число элементов в твоем массиве. И все
Огромное спасибо за оперативность…
Однако вопросов ещё много…
Извините, я новичок:
Каким образом мне производить запись в озу (через какую команду, неужели последовательно через РОН)?
.include “m16def.inc” ; Присоединение файла описаний
.list ; Включение листинга
.dseg
.org 0×60
.def temp = r16 ; Определение главного рабочего регистра
ldi temp,77 ;инициализация регистра
array_55:
.byte 55
STS 0×61,temp
и так далее…???
Только почему-то у меня ошибка появляется: ” code in .dseg ” ???
Потому что у тебя код в дсег.
Для кода нужно определить ксег
Да через РОН, а как ты хотел? В Z кидаешь адрес начала, а дальше командами ST Z+,Rn или им подобными гоняешь данные туда сюда. По одному байту
Сперва об ошибке компилятора: Ясно что проблема с выбором памяти для размещения. Но не понимаю куда нужно воткнуть .cseg: орг-директиву выбрал чтоб запись была после РОН и РВВ, temp - эт и есть РОН из РАМ, директива .byte тоже резервирует именно в РАМ, а ругань идёт именно на выделение места под “массив”.
Вообще офигенно!
Сперва ошибка, что код:
array_55: .byte 55
STS 0×61,temp
недопустим в .сseg - ставлю .dseg - ошибка что код лежит в дсег. %)
Всё, с этой проблемой разобрался.)
Добрый Вечер.
Потыкался я в команды и записал таки последовательно эти 55 байт…
Однако как-то нерационально получилось, у меня 4 куска аналогичных приведённому ниже коду.Как вы думаете можно ли его записать по-проще?
Например, нельзя ли использовать что-нибудь кроме R16-29 чтобы оттуда кидать в РАМ?
=============================================================================
.include “m16def.inc” ; Присоединение файла описаний
.list ; Включение листинга
.cseg
ldi r16,28
ldi r17,69
ldi r18,66
ldi r19,39
ldi r20,75
ldi r21,35
ldi r22,18
ldi r23,87
ldi r24,21
ldi r25,32
ldi r26,123
ldi r27,38
ldi r28,86
ldi r29,89
.dseg
array_55: .byte 55
.cseg
clr r31 ldi r30, 96
st z, r16
std z+1,r17
std z+2,r18
std z+3,r19
std z+4,r20
std z+5,r21
std z+6,r22
std z+7,r23
std z+8,r24
std z+9,r25
std z+10,r26
std z+11,r27
std z+12,r28
std z+13,r29
================================================================================
А я не понял, ты пытаешься инициализировать свой массив начальными значениями? Или что? Или грузишь туда результаты расчета?
Нет)
Всё гораздо интереснее…
Ладно карты на стол:
1:
Нужно сделать генерацию случайных чисел и последовательное их
выплёвывание в порт мк.
Для этого я использую алгоритм Митчелла Мура, суть вот в чём:
Надо загнать в мк(использую мегу16) массив из 55 случайных чисел, соответственное 55 регистров а потом складывать
24й и 55й - результат в 55й (если переполнение то по ходу по фиг),
декрементируем (т.е. юзаем 23й и 54й) = результат
в 54й, и так надо гонять этот массив (в книге термин - циклическая
таблица).
Когда меньший индекс (ну т.е. 24й например) станет = 0, записать в него
55, когда старший - тоже записать(эт цикл) 0. вот…
2: И потом всё это дело отфильтровать при помощи Цифрового фильтра,
уравнение которого надо реализовать программно
P.S.: Если удобно можете написать и в аську 487422360.
1. Где ты возьмешь случайные числа? Сам наплодишь или заранее таблицу сделаешь? Если заранее таблицу, то тебе проще так:
Размещаешь ее в ПЗУ, в флешраме через .db 1,2,3 и так далее все твои 55 значений.
Но изменять их нельзя, т.к. они в ПЗУ.
Поэтому мы при старте через команду LPM копируем их в массив в ОЗУ, а дальше начинаем его гонять твоим методом. Получится намного компактней чем делать непосредственную загрузку из регистров.
А можно случайные числа и так нагенерить, если есть ацп. Включить ее и начать брать самый младший бит - он почти всегда случаен. 8 самых младших битов снятых с ацп дадут тебе совершенно случайный байт. Случайный от природы - :)
Мда…
Вопросы растут по экспоненте…
Вот код написал, но он не работает по-человечески (массив - из таблицы случайных чисел), можете помочь?:
———————————————————————
.cseg
.def temp = r16
.org 0xEF
Array: .db 28,69,66,39,75,31
.db 35,18,87,21,32,46
.db 123,38,86,89,93,75
.db 54,47,59,98,76, 6
.db 147,59,239,244,170,66
.db 92,162,1,7,28,89
.db 78,5,15,154,38,19
.db 84,7,33,8,77,94
.db 93,56,5,99,89,54
.db 3,54,63,6,65, 84
.db 213,90,83, 111,159,3
ldi ZH,High(Array*2) ;загрузка адреса метки в
ldi ZL,Low (Array*2) ;регистровую пару Z
lpm ;загрузка ячейки по адресу (Z)
mov temp,R0 ;копирование загруженного байта
——————————————————————–
1: Прогоняю пошагово, жёлтая стрелка доходит до 4й строчки массива и перескакивает на 1ю массива, до ldi даже не доходит. Однако во Флэш эти циферки лежат блин… (и ещё заметил какие-то махинации/изменения в SREG, регистры R20,R19,R17, тоже пляшут, может я что-то из периферии завёл?)
2: Не пойму по поводу команды lpm - она загружает из Флэш по адресу что в Z-слове в РАМ, а именно в RO ? Тогда почему RO ? RO-R15 же не юзают…
3: И ещё, не совсем понял Структурно Вашу идею, ну допустим получим мы в R0 последовательно, все 50-60 случайных чисел, а потом…
НЕ ВИЖУ компактного способа перегона этого всего в РАМ
Ну а какого хрена ты разместил массив вначале программы? Процессору все равно, он не различает данные и код и пытается твой массив случайных чисел выполнить аки команду. образуя при этом случайные команды процессора %)))
Поставь массив ПОСЛЕ кода.
2. А что сложного то?
В Z адрес твоего Array - это ты уже сделал.
В X адрес твоего массива который будет в памяти.
Дальше LPM и из R0 делаешь ST X+,R0 - перекладываешь в память. Если СТ не работает с R0 то либо ищи команду LPM Rn (есть в некоторых авр, может и в твоем есть) и сохраняй в любой другой регистр, либо из р0 перекладывай в р16 и сохраняй уже оттуда.
Потом тебе надо увеличить Z на 1 чтобы выбрать следующий элемент памяти в флеше. Сделать это можно через вычитание отрицательной величины
SUBI ZL,(-1)
SBCI ZH,(-1)
Это увеличит оба регистра зед.
И повторить это в цикле 50 или сколько там у тебя раз. Для этого надо будет предварительно загрузить счетчик значением 50 и уменьшать его после каждого прогона командой DEC, проверяя результат на ноль - BRNE - в начало цикла.
А ну еще бывают в мегах команды LPM Rn,Z+ тогда указатель зед увеличится сам
Привет!Спасибо за продуктивный ответ)
Начинаю прозревать что по чём…
Теперь хотел спросить вот что:
1.Компилирую в студио, ток чот я не понимаю, как заставить студию шоб прогнала прогу столько итераций, сколько надо (то бишь нужное количество случ.чисел).
Делаю пошагово, и за цикл число записывается в РАМ. Как сделать чтоб прогнала до конца и записала в РАМ все числа? (после нажатия ф5 никаких изменений не вижу)
2.Теперь другй большой косяк. Допустим я оставляю 4 строчки массива, в итоге эти числа есть в РАМ, а потом там ещё какие-то числа… ОТкуда? Я ж ничего не пишу больше туда…
3. Что происходит когда стрелка пошагово в каждом цикле доходит до .db ?
Происходит запись каждый раз массива во Флэш?
4. И на сладкое :( как только ставлю 5 и более строчек в массиве, прогоняю пошагово, а стрелка доходит до 4й строки, пару раз дрогнет и снова в начало цикла.
До SUBI даже не доходит. Причём снова эти махинации сo SREG и некоторыми РОН, что я не использую…
6. вопрос про аврСтудио, не пойму как поставить DataBreakpoint:
* курсор перед Loop
* захожу в Debug -> DataBreakpoint: ничего не меняю а лишь ставлю 18 в поле Break Execution After
* Syntax error: Symbol expected. ????????(
7. А ещё вопрос немного отвлечённый, что означает два знака неравенства подряд , не равно?
————————————————————————————-
.include “m16def.inc” ; Присоединение файла описаний
.list ; Включение листинга
.cseg
.org 0×2D
ldi ZH,High(Array*2) ;загрузка адреса метки в регистровую пару Z
ldi ZL,Low (Array*2) ;(а именно загрузка адреса первого элемента массива)
.dseg
.byte 55
.cseg
clr XH
ldi XL, 96 ;Загрузить в XL, адрес начального регистра РАМ
Loop:
ldi r17, 66
lpm ;загрузка в r0, ячейки, адрес которой в(Z)
mov r16,r0 ;копирование загруженного байта в r16
st X, r16
Array: .db 13, 69, 66, 39, 75, 31 ;
.db 35, 18, 87,21, 32, 46
.db 123, 38, 86, 89,93, 75
.db 54, 47, 59,98, 76, 6
.db 147, 59, 239, 244, 170, 66
.db 92,162, 1, 7, 28, 89
.db 78, 5, 15, 154, 38, 19
.db 84, 7, 33, 8, 77, 94
.db 93,56,5, 99, 89, 54
.db 3,54,63,6, 65, 84
.db 213, 90,83, 111, 159, 8
SUBI ZL,(-1)
SBCI ZH,(-1)
SUBI XL,(-1)
dec r17
brne Loop
nop
————————————————————————————-
Вот ,уже лучше. Только вместо SUBI XL,(-1) лучше юзай команду st X+,R16 при сохранении, тогда у тебя Х увеличится автоматом после выполнения и SUBI XL,(-1) не потребуется вовсе.
А не работает потому, что ты ОПЯТЬ ЗАПИХАЛ ДАННЫЕ В КОД!!!! И он опять пытается их выполнить словно команды.
3. Что происходит когда стрелка пошагово в каждом цикле доходит до .db ?
Происходит запись каждый раз массива во Флэш?
НЕТ! В этот момент процессор думает, что перед ней очредная инструкция и пытается ее выполнить. ОН НЕ Различает данные и код. Данные надо писать туда куда процессор не сможет добраться самовольно. Например в самом конце программы, а перед данными делать
RJMP Reset
чтобы данные не выполнились.
Спасибо за комментарии - очень ценные и всё по делу.
Код изменил - запись в РАМ прошла успешно:
cseg
.org 0×2D
ldi ZH,High(Array*2) ;загрузка адреса метки в регистровую пару Z
ldi ZL,Low (Array*2) ;(а именно загрузка адреса первого элемента массива)
.dseg
.byte 55
.cseg
clr XH
ldi XL, 96 ;Загрузить в XL, адрес начального регистра РАМ
ldi r17, 56
Loop:
lpm ;загрузка в r0, ячейки, адрес которой в(Z)
mov r16,r0 ;копирование загруженного байта в r16
st X+, r16
SUBI ZL,(-1)
SBCI ZH,(-1)
dec r17
brne Loop
nop
Array: .db 13, 69, 66, 39, 75, 31 ;
.db 35, 18, 87,21, 32, 46
.db 123, 38, 86, 89,93, 75
.db 54, 47, 59,98, 76, 6
.db 147, 59, 239, 244, 170, 66
.db 92,162, 1, 7, 28, 89
.db 78, 5, 15, 154, 38, 19
.db 84, 7, 33, 8, 77, 94
.db 93,56,5, 99, 89, 54
.db 3,54,63,6, 65, 84
.db 213, 90,83, 111, 159, 8
Теперь у меня другая задача, подскажите в какую сторону и “чем” копать:
Сейча мне нужно брать из этого массива элемент самый последний в этой куче, то бишь 56й, добавлять 25й сохранять сумму в 56м и результат выплёвывать в порт.
Короче если тупо в лоб:
==========================================================================
lds r18, 151 ; Загрузить в r18 содержимое SRAM по адресу 151
lds r19, 120 ; Загрузить в r19 содержимое SRAM по адресу 120
add r18, r19 ; Прибавить к r18 содержимое r19
sts 151, r18 ; Записать обратно
; выплёвывание в порт А микроконтроллера
==========================================================================
далее мне нужно декрементировать индекс этих двух элементов массива (теперь это уже 55й и 23й)и по аналогии суммировать и в порт.
Когда младший индекс станет равным 1 - то следующий элемент уже брать снова из 56го, соответственно когда старший - то ему 56й… и т.д.
Команды толком не знаю, поэтому не представляю через что это организовать,
Надо как-то последовательно (в цикле с проверкой на минимальный адрес 0х60) суммировать и перезаписывать элементы массива в РАМ.
Читал Вашу статью по работе с памятью, всё ясно, но чтобы с декрементацией адреса регистров РАМ работать не нашёл ничего полезного…
Что посоветуете?)
Ну почитай систему команд чтоль. Их там всего 130.
Тебе проще наверное ввести какой нибудь базисный указатель и от него плясать, при выборе чисел. А потом просто сдвигать этот указатель и все.
Салют DI HALT!
Вроде сделал я генерацию, отсимулировал тоже вроде, но не могу прошарить в
аврСтудио, не пойму как поставить DataBreakpoint для определённого цикла (задолбался тыкать пошагово):
* курсор после нужного Loop
* захожу в Debug -> DataBreakpoint: ничего не меняю а лишь ставлю 18 в поле Break Execution After
* Syntax error: Symbol expected. ????????(
———————————————————————————-
И ещё, что значит символы больше и меньше подряд без пробела (типа А B ) ? Это означает A не равно B ?
блин почему-то не пишутся знаки, короче что значит:
(А)МеньшеБольше(В) ???